Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b46912afcb | ||
|
|
ed3d4823b2 | ||
|
|
b8a360917e | ||
|
|
26dbe36a15 | ||
|
|
396447519d | ||
|
|
83e436bbd2 | ||
|
|
d307eff71f | ||
|
|
b935725096 | ||
|
|
12248acd7b | ||
|
|
07eb0f5e8e | ||
|
|
cfb5fc25c3 | ||
|
|
3816e1c5bd | ||
|
|
c9d551cd39 | ||
|
|
95bf0473f3 | ||
|
|
a9d92b78a2 | ||
|
|
b8ebace039 | ||
|
|
9223957931 | ||
|
|
5cddb7f2b6 | ||
|
|
08e7a3af16 | ||
|
|
1416cd432d | ||
|
|
2d6cf07a65 | ||
|
|
0b73d6112a | ||
|
|
b742ac0591 | ||
|
|
4546fd44ff | ||
|
|
216fa9341b | ||
|
|
1d6d5dcc94 | ||
|
|
61f451694f | ||
|
|
c681f0bd82 | ||
|
|
cbc0b2707e | ||
|
|
36d0f8a2a0 | ||
|
|
301f65ce16 | ||
|
|
f1b95fe45e | ||
|
|
3091d747e6 | ||
|
|
f4062e5ebb | ||
|
|
a8807f1ef0 | ||
|
|
10945927c1 | ||
|
|
ab25f698da | ||
|
|
b30e03ffb3 | ||
|
|
82157c7bd1 | ||
|
|
eb10f02ac3 | ||
|
|
b1ef94ef60 | ||
|
|
f89392b476 |
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Cosmic JAR creation stage
|
||||
#
|
||||
FROM maven:3.8.4-openjdk-17 AS jar
|
||||
FROM maven:3.9.1-eclipse-temurin-17 AS jar
|
||||
|
||||
# Build in a separated location which won't have permissions issues.
|
||||
WORKDIR /opt/cosmic
|
||||
@@ -21,7 +21,7 @@ RUN mvn -f ./pom.xml clean package -Dmaven.test.skip -T 1C
|
||||
#
|
||||
# Server creation stage
|
||||
#
|
||||
FROM openjdk:17.0.2
|
||||
FROM eclipse-temurin:17.0.6_10-jre
|
||||
|
||||
# Host the server in a location that won't have permissions issues.
|
||||
WORKDIR /opt/server
|
||||
|
||||
10
config.yaml
10
config.yaml
@@ -159,8 +159,8 @@ worlds:
|
||||
|
||||
server:
|
||||
#Database Configuration
|
||||
DB_URL_FORMAT: "jdbc:mysql://%s:3306/cosmic"
|
||||
DB_HOST: "localhost"
|
||||
DB_URL_FORMAT: "jdbc:mysql://%s:3306/cosmic" # If the docker ENV for DB_HOST is anything but "db", this string format should be changed from 3306 to 3307 (or whichever port it was changed to in docker)
|
||||
DB_HOST: "localhost"
|
||||
DB_USER: "cosmic_server"
|
||||
DB_PASS: "snailshell"
|
||||
INIT_CONNECTION_POOL_TIMEOUT: 90 # Seconds
|
||||
@@ -461,6 +461,12 @@ server:
|
||||
#Event End Timestamp
|
||||
EVENT_END_TIMESTAMP: 1428897600000
|
||||
|
||||
# GM Security Configuration
|
||||
MINIMUM_GM_LEVEL_TO_TRADE: 4
|
||||
MINIMUM_GM_LEVEL_TO_USE_STORAGE: 4
|
||||
MINIMUM_GM_LEVEL_TO_USE_DUEY: 4
|
||||
MINIMUM_GM_LEVEL_TO_DROP: 4
|
||||
|
||||
#Any NPC ids that should search for a js override script (useful if they already have wz entries since otherwise they're ignored).
|
||||
NPCS_SCRIPTABLE:
|
||||
#9200000: Talk to Cody # Cody
|
||||
|
||||
@@ -335,7 +335,7 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `pitch`, `position`) VALU
|
||||
(1301000, 2000006, 620, 0, 156),
|
||||
(1301000, 2000003, 200, 0, 160),
|
||||
(1301000, 2000002, 320, 0, 164),
|
||||
(1301000, 2000015, 160, 0, 168),
|
||||
(1301000, 2000001, 160, 0, 168),
|
||||
(1301000, 2000000, 50, 0, 172);
|
||||
|
||||
# adding missing pirate items at Singapore npc's
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
- ./scripts:/opt/server/scripts
|
||||
- ./wz:/opt/server/wz
|
||||
environment:
|
||||
DB_HOST: "db"
|
||||
DB_HOST: "db" ## Remember if this is present it will OVERRIDE the host in the config.yaml, if you put here anything other than db, you'll need to change the config.yaml jdbc string to port 3307, and not port 3306
|
||||
|
||||
db:
|
||||
image: mysql:8.0.23
|
||||
@@ -27,6 +27,8 @@ services:
|
||||
MYSQL_DATABASE: "cosmic"
|
||||
MYSQL_USER: "cosmic_server"
|
||||
MYSQL_PASSWORD: "snailshell"
|
||||
ports:
|
||||
- "3307:3306"
|
||||
volumes:
|
||||
- ./database/docker-db-data:/var/lib/mysql
|
||||
- ./database/sql:/docker-entrypoint-initdb.d
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
5160006 - Sparkling Eyes - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display sparkling eyes.
|
||||
5160007 - Flaming - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of rage.
|
||||
5160008 - Ray - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display beaming eyes.
|
||||
5160009 - Goo Goo - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of “awesome!!!”
|
||||
5160009 - Goo Goo - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of <EFBFBD>awesome!!!<EFBFBD>
|
||||
5160010 - Whoa Whoa - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the look of being flustered.
|
||||
5160011 - Constant Sigh - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character display the thinking look.
|
||||
5160012 - Drool - On the KeyConfig, configure this expression on a button of your choice. Press the button and watch your character act like falling asleep.
|
||||
@@ -479,9 +479,9 @@
|
||||
5610001 - Vega's Spell(60%) - This winning spell from Vega enables a 90% success rate on a 60% scroll. Please check the scroll description to confirm that Vega's Spell is available for the scroll you choose.
|
||||
5590000 - High-Five Stamp - Can equip items that are 5 levels above your current level.
|
||||
5021026 - Gift Box Throwing Stars - A gift box that can be freely thrown around. Using the #cThrowing Star# will create an orbital effect.\n\nThis item cannot be deleted.
|
||||
5010073 - Miss Popular - Well, lookie here. Someone’s certainly become popular with the guys. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
5010073 - Miss Popular - Well, lookie here. Someone<EFBFBD>s certainly become popular with the guys. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
|
||||
5010074 - Mr. Popular - Well, lookie here. Someone’s certainly become popular with the girls. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
5010074 - Mr. Popular - Well, lookie here. Someone<EFBFBD>s certainly become popular with the girls. Turn this effect on and off by assigning it to a shortcut key from the keyboard settings menu.
|
||||
|
||||
5240027 - Golden Drumstick - A drumstick that can be consumed only by #cBaby Tiger#. It recovers hunger and #cincreases Closeness by 100.#
|
||||
5390005 - Cute Tiger Messenger - Shout to everyone in the world your character is on with this megaphone. Now available with your avatar on the top of everyone's screen! Comes with a tiger background for your avatar.
|
||||
|
||||
@@ -1779,8 +1779,8 @@
|
||||
4032103 - The Lost Treasure - The lost treasure that was stolen by the Master of Disguise who brazenly entered Ereve. Let's take it back to Irena.
|
||||
4032104 - The Lost Treasure - The lost treasure that was stolen by the Master of Disguise who brazenly entered Ereve. Let's take it back to Eckhart.
|
||||
4032105 - The Lost Treasure - The lost treasure that was stolen by the Master of Disguise who brazenly entered Ereve. Let's take it back to Hawkeye.
|
||||
4000209 - Co-ke Slime’s Bell - The bell of Co-ke Slime that has been taken off.
|
||||
4000210 - Co-ke Pig’s Ribbon - A piece of Co-ke Pig’s ribbon.
|
||||
4000209 - Co-ke Slime<EFBFBD>s Bell - The bell of Co-ke Slime that has been taken off.
|
||||
4000210 - Co-ke Pig<EFBFBD>s Ribbon - A piece of Co-ke Pig<EFBFBD>s ribbon.
|
||||
4000211 - Coca-Cola Cube - A cube with a drawing of a Coca-Cola bottle on it.
|
||||
4000212 - CokePLAY Cube - A cube with the CokePLAY symbol.
|
||||
4000213 - Coca-Cola Card - A card with a drawing of Coca-Cola.
|
||||
@@ -1802,10 +1802,10 @@
|
||||
4001151 - Happy Valley - Happy Valley
|
||||
4001152 - Ariant - Ariant
|
||||
4001153 - Magatia - Magatia
|
||||
4031832 - Sophelia’s Portrait - A portrait of Sophelia.
|
||||
4031832 - Sophelia<EFBFBD>s Portrait - A portrait of Sophelia.
|
||||
4031833 - Pumpkin Juice - A juice made with pumpkin. It is very fragrant.
|
||||
4031834 - Perfect Tool - The most perfect tool for making dolls.
|
||||
4031835 - Lyudmilla’s Earring - The earrings that Lyudmilla lost. They shine with brilliance.
|
||||
4031835 - Lyudmilla<EFBFBD>s Earring - The earrings that Lyudmilla lost. They shine with brilliance.
|
||||
4031836 - Score - The score that Lyudmilla asked for. No one knows what kind of music is in it.
|
||||
4031837 - Dumped Doll - A doll that Sophelia used to cherish a long time ago. She threw it away when she got a new doll.
|
||||
4031838 - Piece of Cloth - A small piece of cloth. If you drag and drop it onto the rag doll, the doll will be completed bit by bit.
|
||||
@@ -1893,26 +1893,26 @@
|
||||
4032127 - Proof of Ability - The Proof of Ability that is required to learn a new skill. This proof signifies that you are ready to learn a new skill.
|
||||
4032128 - Proof of Ability - The Proof of Ability that is required to learn a new skill. This proof signifies that you are ready to learn a new skill.
|
||||
4032129 - Proof of Ability - The Proof of Ability that is required to learn a new skill. This proof signifies that you are ready to learn a new skill.
|
||||
4032130 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right…
|
||||
4032131 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right…
|
||||
4032130 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right<EFBFBD>
|
||||
4032131 - Pig Doll - An adorable doll that resembles a pig. However, something doesn't seem right<EFBFBD>
|
||||
4032132 - Roca's Mission Report - A mission report written by Roca, the Agent for Henesys. This compiles a list of events that have taken place recently.
|
||||
4032136 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. The way it smiles just seems a bit off, as if it has hidden agenda.
|
||||
4032137 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. The way it smiles just seems a bit off, as if it has hidden agenda.
|
||||
4032138 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in…tears.
|
||||
4032138 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in<EFBFBD>tears.
|
||||
4032139 - Old Key - An old, rusty key that Mr. Pickall has been looking for.
|
||||
4032140 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in…tears.
|
||||
4032140 - Bubbling Doll - An adorable doll that was designed based on Bubbling, but something doesn't seem right. It's in<EFBFBD>tears.
|
||||
4032141 - Mattias' Mission Report - A mission report written by Roca, the Agent for Kerning City. This compiles a list of events that have taken place recently.
|
||||
4032142 - Clear Tree Sap - A clear tree sap that can only be found at the very top of the skyscraping trees. Accompanied by the freshest green scent.
|
||||
4032143 - Fruit - A fruit picked from plants near Henesys.
|
||||
4032144 - Hersha's Mission Report - A mission report written by Hersha, the Agent for Ellinia. This compiles a list of events that have taken place recently.
|
||||
4032145 - Detector - A detector made to detect any forces of darkness. Take this to the 10 Boogies.
|
||||
4032146 - Wooden Mask Doll - An adorable doll modeled after the Wooden Mask, but something doesn't seem right…
|
||||
4032147 - Rocky Mask Doll - An adorable doll modeled after the Stone Mask, but something doesn't seem right…
|
||||
4032146 - Wooden Mask Doll - An adorable doll modeled after the Wooden Mask, but something doesn't seem right<EFBFBD>
|
||||
4032147 - Rocky Mask Doll - An adorable doll modeled after the Stone Mask, but something doesn't seem right<EFBFBD>
|
||||
4032148 - Detector - A detector created with the sole purpose of detecting the Puppeteer's movements. We put it to good use, so let's return it to Neinheart.
|
||||
4032149 - 10 Boogies' Mission Report - A mission report written by 10 Boogies, the Agents for Perion. This compiles a list of events that have taken place recently.
|
||||
4032178 - Hack Attempt - A record that shows that Chief Grays tried to hack into the system.
|
||||
4032179 - Ereve Investigation Permit - A permitt that allows one to investigate every part of Ereve. When the Red Alert is on, this permit is the only way you can roam freely around Ereve.
|
||||
4032190 - Orange Mushroom Doll - An adorable doll modeled after the Orange Mushroom, but something doesn't seem right…
|
||||
4032190 - Orange Mushroom Doll - An adorable doll modeled after the Orange Mushroom, but something doesn't seem right<EFBFBD>
|
||||
4032196 - Concentrated Formula: Step 1 - A formula concentrated with effective and potent ingredients. The first formula used to raise Mimiana and Mimio
|
||||
4032197 - Concentrated Formula: Step 2 - A formula concentrated with effective and potent ingredients. The second formula used to raise Mimiana and Mimio.
|
||||
4032198 - Concentrated Formula: Step 3 - A formula concentrated with effective and potent ingredients. The third formula used to raise Mimiana and Mimio.
|
||||
@@ -2048,16 +2048,16 @@
|
||||
4032270 - Glistening Sunlight - Glistening sunlight filled with the feeling of spring.
|
||||
4032271 - Pure Small Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032272 - Stationery of Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032273 - Pencil of Courage X 10 - A pencil that's used to write things you don’t have the courage to say.
|
||||
4032273 - Pencil of Courage X 10 - A pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032275 - Pure Normal Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032276 - Pure Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032277 - Pure Large Sprout - A fragrant new sprout filled with the energies of spring. Used as an ingredient for Pure Perfume.
|
||||
4032278 - Stationery of Deep Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032279 - Stationary of Hope and Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032280 - Letter of Love and Longing - Pink stationary that's rumored to return a favorable response if used to send a letter to a secret crush.
|
||||
4032281 - Pencil of Courage X 100 - A pencil that's used to write things you don’t have the courage to say.
|
||||
4032282 - Pencil of Courage X 1000 - AA pencil that's used to write things you don’t have the courage to say.
|
||||
4032283 - Pencil of Courage X 10000 - A pencil that's used to write things you don’t have the courage to say.
|
||||
4032281 - Pencil of Courage X 100 - A pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032282 - Pencil of Courage X 1000 - AA pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032283 - Pencil of Courage X 10000 - A pencil that's used to write things you don<EFBFBD>t have the courage to say.
|
||||
4032284 - Child's Broken Toy - A toy stolen from a child by a monster. It's broken and cannot be used.
|
||||
4032285 - Child's Broken Toy - A toy stolen from a child by a monster. It's broken and cannot be used.
|
||||
4032286 - Child's Broken Toy - A toy stolen from a child by a monster. It's broken and cannot be used.
|
||||
|
||||
@@ -1565,9 +1565,9 @@ ossyria
|
||||
200090057 - Empress' Road - To Ellinia
|
||||
200090058 - Empress' Road - To Ereve
|
||||
200090059 - Empress' Road - To Ellinia
|
||||
200010303 - Hidden Street - Eliza’s Garden
|
||||
211040102 - Hidden Street - Snow Soul’s Resting Place
|
||||
209000100 - Happy Village - Cliff’s Hut
|
||||
200010303 - Hidden Street - Eliza<EFBFBD>s Garden
|
||||
211040102 - Hidden Street - Snow Soul<EFBFBD>s Resting Place
|
||||
209000100 - Happy Village - Cliff<EFBFBD>s Hut
|
||||
219000000 - Hidden Street - Coke Town
|
||||
219000001 - Hidden Street - Coke Town Sundry Goods Shop
|
||||
219000002 - Hidden Street - House of Pouch
|
||||
@@ -2863,7 +2863,7 @@ etc
|
||||
980027000 - Goldrich's Maze - Maze of Erosion
|
||||
980027100 - Goldrich's Maze - Maze of Erosion
|
||||
980027200 - Goldrich's Maze - Maze of Erosion
|
||||
980027300 - Goldrich's Maze - Beginning of the Maze…
|
||||
980027300 - Goldrich's Maze - Beginning of the Maze<EFBFBD>
|
||||
980027400 - Goldrich's Maze - Monster's Maze
|
||||
980027500 - Goldrich's Maze - Monster's Maze
|
||||
980027600 - Goldrich's Maze - Monster's Maze
|
||||
@@ -2944,7 +2944,7 @@ etc
|
||||
980027001 - Goldrich's Maze - Maze of Erosion
|
||||
980027101 - Goldrich's Maze - Maze of Erosion
|
||||
980027201 - Goldrich's Maze - Maze of Erosion
|
||||
980027301 - Goldrich's Maze - Beginning of the Maze…
|
||||
980027301 - Goldrich's Maze - Beginning of the Maze<EFBFBD>
|
||||
980027401 - Goldrich's Maze - Monster's Maze
|
||||
980027501 - Goldrich's Maze - Monster's Maze
|
||||
980027601 - Goldrich's Maze - Monster's Maze
|
||||
@@ -3025,7 +3025,7 @@ etc
|
||||
980027002 - Goldrich's Maze - Maze of Erosion
|
||||
980027102 - Goldrich's Maze - Maze of Erosion
|
||||
980027202 - Goldrich's Maze - Maze of Erosion
|
||||
980027302 - Goldrich's Maze - Beginning of the Maze…
|
||||
980027302 - Goldrich's Maze - Beginning of the Maze<EFBFBD>
|
||||
980027402 - Goldrich's Maze - Monster's Maze
|
||||
980027502 - Goldrich's Maze - Monster's Maze
|
||||
980027602 - Goldrich's Maze - Monster's Maze
|
||||
@@ -3349,7 +3349,7 @@ etc
|
||||
913040007 - Opening - Cygnus Knights
|
||||
913040008 - Opening - Cygnus Knights
|
||||
913040009 - Opening - Cygnus Knights
|
||||
920020000 - Hidden Street - Eliza’s Garden
|
||||
920020000 - Hidden Street - Eliza<EFBFBD>s Garden
|
||||
922020300 - Hidden Street - Origin of the Clock Tower
|
||||
922220000 - Hidden Street - Gloomy Forest
|
||||
922230000 - Hidden Street - Lunar World
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
3010058 - WorldEnd - You will recover 50 HP every 10 seconds. Perhaps, as you recline, you will find the answer to many of life's questions.
|
||||
3010057 - BloodyRose - You will recover 50 HP every 10 seconds. You will experience the might of a conqueror after recovery.
|
||||
3010060 - Noblesse Chair - A chair makes you feel like you're sitting in the lap of luxury. Also recovers 50 HP every 10 seconds.
|
||||
3010061 - Underneath the Maple Tree… - A white chair created to celebrate Maple Story's 6th Anniversary. Sit on it to restore 35 HP and 10 MP every 10 seconds.
|
||||
3010061 - Underneath the Maple Tree<EFBFBD> - A white chair created to celebrate Maple Story's 6th Anniversary. Sit on it to restore 35 HP and 10 MP every 10 seconds.
|
||||
3010062 - Bamboo Chair - A chair that restores HP every 10 seconds when used. It's very strong since it was made from bamboo grown on Rien.
|
||||
3010063 - Moon and Star Cushion - A pretty cushion shaped like a moon. Recovers 60 HP every 10 seconds.
|
||||
3010064 - Male Desert Rabbit Cushion - 60 HP is restored every 10 seconds if you lean back on this cute Male Desert Rabbit Cushion.
|
||||
|
||||
28
pom.xml
28
pom.xml
@@ -19,22 +19,22 @@
|
||||
<mainClass>net.server.Server</mainClass>
|
||||
|
||||
<!-- Maven plugins -->
|
||||
<maven-surefire-plugin.version>3.0.0-M7</maven-surefire-plugin.version> <!-- For running unit tests -->
|
||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version> <!-- Disabled. (for building thin jar) -->
|
||||
<maven-assembly-plugin.version>3.4.2</maven-assembly-plugin.version> <!-- For packaging the executable fat jar -->
|
||||
<maven-surefire-plugin.version>3.0.0-M9</maven-surefire-plugin.version> <!-- For running unit tests -->
|
||||
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version> <!-- Disabled. (for building thin jar) -->
|
||||
<maven-assembly-plugin.version>3.5.0</maven-assembly-plugin.version> <!-- For packaging the executable fat jar -->
|
||||
|
||||
<!-- Dependencies -->
|
||||
<slf4j-api.version>1.7.36</slf4j-api.version> <!-- Logging facade -->
|
||||
<log4j.version>2.18.0</log4j.version> <!-- Slf4j implementation -->
|
||||
<graalvm.version>22.2.0</graalvm.version> <!-- ScriptEngine implementation -->
|
||||
<netty.version>4.1.79.Final</netty.version> <!-- Networking -->
|
||||
<log4j.version>2.20.0</log4j.version> <!-- Slf4j implementation -->
|
||||
<graalvm.version>22.3.1</graalvm.version> <!-- ScriptEngine implementation -->
|
||||
<netty.version>4.1.89.Final</netty.version> <!-- Networking -->
|
||||
<yamlbeans.version>1.15</yamlbeans.version> <!-- Config file -->
|
||||
<jcip-annotations.version>1.0</jcip-annotations.version> <!-- Annotations for concurrency documentation -->
|
||||
<HikariCP.version>5.0.1</HikariCP.version> <!-- Database connection pool -->
|
||||
<mysql-connector-java.version>8.0.30</mysql-connector-java.version> <!-- MySQL JDBC driver -->
|
||||
<jdbi-version>3.35.0</jdbi-version> <!-- Convenience wrapper around JDBC -->
|
||||
<junit.version>5.9.0</junit.version> <!-- Unit test -->
|
||||
<mockito.version>4.7.0</mockito.version> <!-- Unit test -->
|
||||
<mysql-connector-j.version>8.0.32</mysql-connector-j.version> <!-- MySQL JDBC driver -->
|
||||
<jdbi-version>3.37.1</jdbi-version> <!-- Convenience wrapper around JDBC -->
|
||||
<junit.version>5.9.2</junit.version> <!-- Unit test -->
|
||||
<mockito.version>5.1.1</mockito.version> <!-- Unit test -->
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -56,9 +56,9 @@
|
||||
<version>${HikariCP.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql-connector-java.version}</version>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql-connector-j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jdbi</groupId>
|
||||
@@ -204,4 +204,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -6482,7 +6482,6 @@ public class Character extends AbstractCharacterObject {
|
||||
ThreadManager.getInstance().newTask(r);
|
||||
}
|
||||
|
||||
levelUpMessages();
|
||||
guildUpdate();
|
||||
|
||||
FamilyEntry familyEntry = getFamilyEntry();
|
||||
@@ -6521,94 +6520,6 @@ public class Character extends AbstractCharacterObject {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void levelUpMessages() {
|
||||
if (level % 5 != 0) { //Performance FTW?
|
||||
return;
|
||||
}
|
||||
if (level == 5) {
|
||||
yellowMessage("Aww, you're level 5, how cute!");
|
||||
} else if (level == 10) {
|
||||
yellowMessage("Henesys Party Quest is now open to you! Head over to Henesys, find some friends, and try it out!");
|
||||
} else if (level == 15) {
|
||||
yellowMessage("Half-way to your 2nd job advancement, nice work!");
|
||||
} else if (level == 20) {
|
||||
yellowMessage("You can almost Kerning Party Quest!");
|
||||
} else if (level == 25) {
|
||||
yellowMessage("You seem to be improving, but you are still not ready to move on to the next step.");
|
||||
} else if (level == 30) {
|
||||
yellowMessage("You have finally reached level 30! Try job advancing, after that try the Mushroom Castle!");
|
||||
} else if (level == 35) {
|
||||
yellowMessage("Hey did you hear about this mall that opened in Kerning? Try visiting the Kerning Mall.");
|
||||
} else if (level == 40) {
|
||||
yellowMessage("Do @rates to see what all your rates are!");
|
||||
} else if (level == 45) {
|
||||
yellowMessage("I heard that a rock and roll artist died during the grand opening of the Kerning Mall. People are naming him the Spirit of Rock.");
|
||||
} else if (level == 50) {
|
||||
yellowMessage("You seem to be growing very fast, would you like to test your new found strength with the mighty Zakum?");
|
||||
} else if (level == 55) {
|
||||
yellowMessage("You can now try out the Ludibrium Maze Party Quest!");
|
||||
} else if (level == 60) {
|
||||
yellowMessage("Feels good to be near the end of 2nd job, doesn't it?");
|
||||
} else if (level == 65) {
|
||||
yellowMessage("You're only 5 more levels away from 3rd job, not bad!");
|
||||
} else if (level == 70) {
|
||||
yellowMessage("I see many people wearing a teddy bear helmet. I should ask someone where they got it from.");
|
||||
} else if (level == 75) {
|
||||
yellowMessage("You have reached level 3 quarters!");
|
||||
} else if (level == 80) {
|
||||
yellowMessage("You think you are powerful enough? Try facing horntail!");
|
||||
} else if (level == 85) {
|
||||
yellowMessage("Did you know? The majority of people who hit level 85 in Cosmic don't live to be 85 years old?");
|
||||
} else if (level == 90) {
|
||||
yellowMessage("Hey do you like the amusement park? I heard Spooky World is the best theme park around. I heard they sell cute teddy-bears.");
|
||||
} else if (level == 95) {
|
||||
yellowMessage("100% of people who hit level 95 in Cosmic don't live to be 95 years old.");
|
||||
} else if (level == 100) {
|
||||
yellowMessage("Mid-journey so far... You just reached level 100! Now THAT's such a feat, however to manage the 200 you will need even more passion and determination than ever! Good hunting!");
|
||||
} else if (level == 105) {
|
||||
yellowMessage("Have you ever been to leafre? I heard they have dragons!");
|
||||
} else if (level == 110) {
|
||||
yellowMessage("I see many people wearing a teddy bear helmet. I should ask someone where they got it from.");
|
||||
} else if (level == 115) {
|
||||
yellowMessage("I bet all you can think of is level 120, huh? Level 115 gets no love.");
|
||||
} else if (level == 120) {
|
||||
yellowMessage("Are you ready to learn from the masters? Head over to your job instructor!");
|
||||
} else if (level == 125) {
|
||||
yellowMessage("The struggle for mastery books has begun, huh?");
|
||||
} else if (level == 130) {
|
||||
yellowMessage("You should try Temple of Time. It should be pretty decent EXP.");
|
||||
} else if (level == 135) {
|
||||
yellowMessage("I hope you're still not struggling for mastery books!");
|
||||
} else if (level == 140) {
|
||||
yellowMessage("You're well into 4th job at this point, great work!");
|
||||
} else if (level == 145) {
|
||||
yellowMessage("Level 145 is serious business!");
|
||||
} else if (level == 150) {
|
||||
yellowMessage("You have becomed quite strong, but the journey is not yet over.");
|
||||
} else if (level == 155) {
|
||||
yellowMessage("At level 155, Zakum should be a joke to you. Nice job!");
|
||||
} else if (level == 160) {
|
||||
yellowMessage("Level 160 is pretty impressive. Try taking a picture and putting it on Instagram.");
|
||||
} else if (level == 165) {
|
||||
yellowMessage("At this level, you should start looking into doing some boss runs.");
|
||||
} else if (level == 170) {
|
||||
yellowMessage("Level 170, huh? You have the heart of a champion.");
|
||||
} else if (level == 175) {
|
||||
yellowMessage("You came a long way from level 1. Amazing job so far.");
|
||||
} else if (level == 180) {
|
||||
yellowMessage("Have you ever tried taking a boss on by yourself? It is quite difficult.");
|
||||
} else if (level == 185) {
|
||||
yellowMessage("Legend has it that you're a legend.");
|
||||
} else if (level == 190) {
|
||||
yellowMessage("You only have 10 more levels to go until you hit 200!");
|
||||
} else if (level == 195) {
|
||||
yellowMessage("Nothing is stopping you at this point, level 195!");
|
||||
} else if (level == 200) {
|
||||
yellowMessage("Very nicely done! You have reached the so-long dreamed LEVEL 200!!! You are truly a hero among men, cheers upon you!");
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlayerRates() {
|
||||
this.expRate *= GameConstants.getPlayerBonusExpRate(this.level / 20);
|
||||
this.mesoRate *= GameConstants.getPlayerBonusMesoRate(this.level / 20);
|
||||
|
||||
@@ -3,68 +3,121 @@ package client.command.commands.gm2;
|
||||
import client.Character;
|
||||
import client.Client;
|
||||
import client.command.Command;
|
||||
import constants.game.NpcChat;
|
||||
import constants.id.NpcId;
|
||||
import server.ThreadManager;
|
||||
import tools.exceptions.IdTypeNotSupportedException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IdCommand extends Command {
|
||||
{
|
||||
setDescription("Search in handbook.");
|
||||
}
|
||||
private final static int MAX_SEARCH_HITS = 100;
|
||||
private final Map<String, String> handbookDirectory = typeFilePaths();
|
||||
private final Map<String, HandbookFileItems> typeItems = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, String> handbookDirectory = new HashMap<>();
|
||||
private final Map<String, HashMap<String, String>> itemMap = new HashMap<>();
|
||||
private Map<String, String> typeFilePaths() {
|
||||
return Map.ofEntries(
|
||||
Map.entry("map", "handbook/Map.txt"),
|
||||
Map.entry("etc", "handbook/Etc.txt"),
|
||||
Map.entry("npc", "handbook/NPC.txt"),
|
||||
Map.entry("use", "handbook/Use.txt"),
|
||||
Map.entry("weapon", "handbook/Equip/Weapon.txt") // TODO add more into this
|
||||
);
|
||||
}
|
||||
|
||||
public IdCommand() {
|
||||
handbookDirectory.put("map", "handbook/Map.txt");
|
||||
handbookDirectory.put("etc", "handbook/Etc.txt");
|
||||
handbookDirectory.put("npc", "handbook/NPC.txt");
|
||||
handbookDirectory.put("use", "handbook/Use.txt");
|
||||
handbookDirectory.put("weapon", "handbook/Equip/Weapon.txt"); // TODO add more into this
|
||||
private static class HandbookFileItems {
|
||||
private final List<HandbookItem> items;
|
||||
|
||||
public HandbookFileItems(List<String> fileLines) {
|
||||
this.items = fileLines.stream()
|
||||
.map(this::parseLine)
|
||||
.filter(Predicate.not(Objects::isNull))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private HandbookItem parseLine(String line) {
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] splitLine = line.split(" - ", 2);
|
||||
if (splitLine.length < 2 || splitLine[1].isBlank()) {
|
||||
return null;
|
||||
}
|
||||
return new HandbookItem(splitLine[0], splitLine[1]);
|
||||
}
|
||||
|
||||
public List<HandbookItem> search(String query) {
|
||||
if (query == null || query.isBlank()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return items.stream()
|
||||
.filter(item -> item.matches(query))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
private record HandbookItem(String id, String name) {
|
||||
|
||||
public HandbookItem {
|
||||
Objects.requireNonNull(id);
|
||||
Objects.requireNonNull(name);
|
||||
}
|
||||
|
||||
public boolean matches(String query) {
|
||||
if (query == null) {
|
||||
return false;
|
||||
}
|
||||
return this.name.toLowerCase().contains(query.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Client client, final String[] params) {
|
||||
final Character player = client.getPlayer();
|
||||
final Character chr = client.getPlayer();
|
||||
if (params.length < 2) {
|
||||
player.yellowMessage("Syntax: !id <type> <query>");
|
||||
chr.yellowMessage("Syntax: !id <type> <query>");
|
||||
return;
|
||||
}
|
||||
final String queryItem = joinStringArr(Arrays.copyOfRange(params, 1, params.length), " ");
|
||||
player.yellowMessage("Querying for entry... May take some time... Please try to refine your search.");
|
||||
final String type = params[0].toLowerCase();
|
||||
final String[] queryItems = Arrays.copyOfRange(params, 1, params.length);
|
||||
final String query = String.join(" ", queryItems);
|
||||
chr.yellowMessage("Querying for entry... May take some time... Please try to refine your search.");
|
||||
Runnable queryRunnable = () -> {
|
||||
try {
|
||||
populateIdMap(params[0].toLowerCase());
|
||||
populateIdMap(type);
|
||||
|
||||
Map<String, String> resultList = fetchResults(itemMap.get(params[0]), queryItem);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final HandbookFileItems handbookFileItems = typeItems.get(type);
|
||||
if (handbookFileItems == null) {
|
||||
return;
|
||||
}
|
||||
final List<HandbookItem> searchHits = handbookFileItems.search(query);
|
||||
|
||||
if (resultList.size() > 0) {
|
||||
int count = 0;
|
||||
for (Map.Entry<String, String> entry : resultList.entrySet()) {
|
||||
sb.append(String.format("Id for %s is: #b%s#k", entry.getKey(), entry.getValue()) + "\r\n");
|
||||
if (++count > 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.append(String.format("Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.", resultList.size(), count) + "\r\n");
|
||||
|
||||
player.getAbstractPlayerInteraction().npcTalk(NpcId.MAPLE_ADMINISTRATOR, sb.toString());
|
||||
if (!searchHits.isEmpty()) {
|
||||
String searchHitsText = searchHits.stream()
|
||||
.limit(MAX_SEARCH_HITS)
|
||||
.map(item -> "Id for %s is: #b%s#k".formatted(item.name, item.id))
|
||||
.collect(Collectors.joining(NpcChat.NEW_LINE));
|
||||
int hitsCount = Math.min(searchHits.size(), MAX_SEARCH_HITS);
|
||||
String summaryText = "Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.".formatted(searchHits.size(), hitsCount);
|
||||
String fullText = searchHitsText + NpcChat.NEW_LINE + summaryText;
|
||||
chr.getAbstractPlayerInteraction().npcTalk(NpcId.MAPLE_ADMINISTRATOR, fullText);
|
||||
} else {
|
||||
player.yellowMessage(String.format("Id not found for item: %s, of type: %s.", queryItem, params[0]));
|
||||
chr.yellowMessage(String.format("Id not found for item: %s, of type: %s.", query, type));
|
||||
}
|
||||
} catch (IdTypeNotSupportedException e) {
|
||||
player.yellowMessage("Your query type is not supported.");
|
||||
chr.yellowMessage("Your query type is not supported.");
|
||||
} catch (IOException e) {
|
||||
player.yellowMessage("Error reading file, please contact your administrator.");
|
||||
chr.yellowMessage("Error reading file, please contact your administrator.");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -72,40 +125,15 @@ public class IdCommand extends Command {
|
||||
}
|
||||
|
||||
private void populateIdMap(String type) throws IdTypeNotSupportedException, IOException {
|
||||
if (!handbookDirectory.containsKey(type)) {
|
||||
final String filePath = handbookDirectory.get(type);
|
||||
if (filePath == null) {
|
||||
throw new IdTypeNotSupportedException();
|
||||
}
|
||||
itemMap.put(type, new HashMap<>());
|
||||
try (BufferedReader reader = Files.newBufferedReader(Path.of(handbookDirectory.get(type)))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String[] row = line.split(" - ", 2);
|
||||
if (row.length == 2) {
|
||||
itemMap.get(type).put(row[1].toLowerCase(), row[0]);
|
||||
}
|
||||
}
|
||||
if (typeItems.containsKey(type)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private String joinStringArr(String[] arr, String separator) {
|
||||
if (null == arr || 0 == arr.length) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append(arr[0]);
|
||||
for (int i = 1; i < arr.length; i++) {
|
||||
sb.append(separator).append(arr[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Map<String, String> fetchResults(Map<String, String> queryMap, String queryItem) {
|
||||
Map<String, String> results = new HashMap<>();
|
||||
for (String item : queryMap.keySet()) {
|
||||
if (item.indexOf(queryItem) != -1) {
|
||||
results.put(item, queryMap.get(item));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
final List<String> fileLines = Files.readAllLines(Path.of(filePath));
|
||||
typeItems.put(type, new HandbookFileItems(fileLines));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,6 +706,12 @@ public class InventoryManipulator {
|
||||
Inventory inv = chr.getInventory(type);
|
||||
Item source = inv.getItem(src);
|
||||
|
||||
if (chr.isGM() && chr.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_DROP) {
|
||||
chr.message("You cannot drop items at your GM level.");
|
||||
log.info("GM %s tried to drop item id %d", chr.getName(), source.getItemId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (chr.getTrade() != null || chr.getMiniGame() != null || source == null) { //Only check needed would prob be merchants (to see if the player is in one)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,14 +92,15 @@ public class DueyProcessor {
|
||||
}
|
||||
|
||||
private static Pair<Integer, Integer> getAccountCharacterIdFromCNAME(String name) {
|
||||
Pair<Integer, Integer> ids = null;
|
||||
Pair<Integer, Integer> ids = new Pair<>(-1, -1);
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT id,accountid FROM characters WHERE name = ?")) {
|
||||
ps.setString(1, name);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
ids = new Pair<>(rs.getInt("accountid"), rs.getInt("id"));
|
||||
ids.left = rs.getInt("accountid");
|
||||
ids.right = rs.getInt("id");
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
@@ -284,7 +285,20 @@ public class DueyProcessor {
|
||||
public static void dueySendItem(Client c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient, boolean quick) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
if (c.getPlayer().isGM() && c.getPlayer().gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_USE_DUEY) {
|
||||
c.getPlayer().message("You cannot use Duey to send items at your GM level.");
|
||||
log.info(String.format("GM %s tried to send a package to %s", c.getPlayer().getName(), recipient));
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_INCORRECT_REQUEST.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
int fee = Trade.getFee(sendMesos);
|
||||
if (sendMessage != null && sendMessage.length() > 100) {
|
||||
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with Quick Delivery on duey.");
|
||||
log.warn("Chr {} tried to use duey with too long of a text", c.getPlayer().getName());
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
if (!quick) {
|
||||
fee += 5000;
|
||||
} else if (!c.getPlayer().haveItem(ItemId.QUICK_DELIVERY_TICKET)) {
|
||||
@@ -302,30 +316,25 @@ public class DueyProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Integer, Integer> accIdCid;
|
||||
if (c.getPlayer().getMeso() >= finalcost) {
|
||||
accIdCid = getAccountCharacterIdFromCNAME(recipient);
|
||||
int recipientAccId = accIdCid.getLeft();
|
||||
if (recipientAccId != -1) {
|
||||
if (recipientAccId == c.getAccID()) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SAMEACC_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(c.getPlayer().getMeso() < finalcost) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NOT_ENOUGH_MESOS.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
int recipientCid = accIdCid.getRight();
|
||||
if (recipientCid == -1) {
|
||||
var accIdCid = getAccountCharacterIdFromCNAME(recipient);
|
||||
var recipientAccId = accIdCid.getLeft();
|
||||
var recipientCid = accIdCid.getRight();
|
||||
|
||||
if (recipientAccId == -1 || recipientCid == -1) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (recipientAccId == c.getAccID()) {
|
||||
c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SAMEACC_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (quick) {
|
||||
InventoryManipulator.removeById(c, InventoryType.CASH, ItemId.QUICK_DELIVERY_TICKET, (short) 1, false, false);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ public class StorageProcessor {
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
Character chr = c.getPlayer();
|
||||
Storage storage = chr.getStorage();
|
||||
String gmBlockedStorageMessage = "You cannot use the storage as a GM of this level.";
|
||||
|
||||
byte mode = p.readByte();
|
||||
|
||||
if (chr.getLevel() < 15) {
|
||||
@@ -61,7 +63,7 @@ public class StorageProcessor {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
switch (mode) {
|
||||
case 4: { // take out
|
||||
case 4: { // Take out
|
||||
byte type = p.readByte();
|
||||
byte slot = p.readByte();
|
||||
if (slot < 0 || slot > storage.getSlots()) { // removal starts at zero
|
||||
@@ -70,8 +72,17 @@ public class StorageProcessor {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
slot = storage.getSlot(InventoryType.getByType(type), slot);
|
||||
Item item = storage.getItem(slot);
|
||||
|
||||
if (hasGMRestrictions(chr)) {
|
||||
chr.dropMessage(1, gmBlockedStorageMessage);
|
||||
log.info(String.format("GM %s blocked from using storage", chr.getName()));
|
||||
chr.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (item != null) {
|
||||
if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
|
||||
c.sendPacket(PacketCreator.getStorageError((byte) 0x0C));
|
||||
@@ -107,7 +118,7 @@ public class StorageProcessor {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 5: { // store
|
||||
case 5: { // Store
|
||||
short slot = p.readShort();
|
||||
int itemId = p.readInt();
|
||||
short quantity = p.readShort();
|
||||
@@ -120,6 +131,14 @@ public class StorageProcessor {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasGMRestrictions(chr)) {
|
||||
chr.dropMessage(1, gmBlockedStorageMessage);
|
||||
log.info(String.format("GM %s blocked from using storage", chr.getName()));
|
||||
chr.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (quantity < 1) {
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
@@ -173,16 +192,24 @@ public class StorageProcessor {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: // arrange items
|
||||
case 6: // Arrange items
|
||||
if (YamlConfig.config.server.USE_STORAGE_ITEM_SORT) {
|
||||
storage.arrangeItems(c);
|
||||
}
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
break;
|
||||
case 7: { // meso
|
||||
case 7: { // Mesos
|
||||
int meso = p.readInt();
|
||||
int storageMesos = storage.getMeso();
|
||||
int playerMesos = chr.getMeso();
|
||||
|
||||
if (hasGMRestrictions(chr)) {
|
||||
chr.dropMessage(1, gmBlockedStorageMessage);
|
||||
log.info(String.format("GM %s blocked from using storage", chr.getName()));
|
||||
chr.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if ((meso > 0 && storageMesos >= meso) || (meso < 0 && playerMesos >= -meso)) {
|
||||
if (meso < 0 && (storageMesos - meso) < 0) {
|
||||
meso = Integer.MIN_VALUE + storageMesos;
|
||||
@@ -208,7 +235,7 @@ public class StorageProcessor {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: // close... unless the player decides to enter cash shop!
|
||||
case 8: // Close (unless the player decides to enter cash shop)
|
||||
storage.close();
|
||||
break;
|
||||
}
|
||||
@@ -217,4 +244,8 @@ public class StorageProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasGMRestrictions(Character character) {
|
||||
return character.isGM() && character.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_USE_STORAGE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +309,12 @@ public class ServerConfig {
|
||||
//Event End Timestamp
|
||||
public long EVENT_END_TIMESTAMP;
|
||||
|
||||
//GM Security Configuration
|
||||
public int MINIMUM_GM_LEVEL_TO_TRADE;
|
||||
public int MINIMUM_GM_LEVEL_TO_USE_STORAGE;
|
||||
public int MINIMUM_GM_LEVEL_TO_USE_DUEY;
|
||||
public int MINIMUM_GM_LEVEL_TO_DROP;
|
||||
|
||||
//Custom NPC overrides. List of NPC IDs.
|
||||
public Map<String, String> NPCS_SCRIPTABLE = new HashMap<>();
|
||||
}
|
||||
|
||||
8
src/main/java/constants/game/NpcChat.java
Normal file
8
src/main/java/constants/game/NpcChat.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package constants.game;
|
||||
|
||||
public final class NpcChat {
|
||||
public static final String NEW_LINE = "\r\n";
|
||||
|
||||
private NpcChat() {}
|
||||
|
||||
}
|
||||
@@ -71,8 +71,9 @@ public class ByteBufOutPacket implements OutPacket {
|
||||
|
||||
@Override
|
||||
public void writeString(String value) {
|
||||
writeShort((short) value.length());
|
||||
writeBytes(value.getBytes(CharsetConstants.CHARSET));
|
||||
byte[] bytes = value.getBytes(CharsetConstants.CHARSET);
|
||||
writeShort(bytes.length);
|
||||
writeBytes(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -58,126 +58,137 @@ public final class ChangeMapHandler extends AbstractPacketHandler {
|
||||
if (chr.getTrade() != null) {
|
||||
Trade.cancelTrade(chr, Trade.TradeResult.UNSUCCESSFUL_ANOTHER_MAP);
|
||||
}
|
||||
if (p.available() == 0) { //Cash Shop :)
|
||||
if (!chr.getCashShop().isOpened()) {
|
||||
c.disconnect(false, false);
|
||||
return;
|
||||
|
||||
boolean enteringMapFromCashShop = p.available() == 0;
|
||||
if (enteringMapFromCashShop) {
|
||||
enterFromCashShop(c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chr.getCashShop().isOpened()) {
|
||||
c.disconnect(false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
p.readByte(); // 1 = from dying 0 = regular portals
|
||||
int targetMapId = p.readInt();
|
||||
String portalName = p.readString();
|
||||
Portal portal = chr.getMap().getPortal(portalName);
|
||||
p.readByte();
|
||||
boolean wheel = p.readByte() > 0;
|
||||
|
||||
boolean chasing = p.readByte() == 1 && chr.isGM() && p.available() == 2 * Integer.BYTES;
|
||||
if (chasing) {
|
||||
chr.setChasing(true);
|
||||
chr.setPosition(new Point(p.readInt(), p.readInt()));
|
||||
}
|
||||
String[] socket = c.getChannelServer().getIP().split(":");
|
||||
chr.getCashShop().open(false);
|
||||
|
||||
chr.setSessionTransitionState();
|
||||
try {
|
||||
c.sendPacket(PacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
|
||||
} catch (UnknownHostException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
if (chr.getCashShop().isOpened()) {
|
||||
c.disconnect(false, false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
p.readByte(); // 1 = from dying 0 = regular portals
|
||||
int targetid = p.readInt();
|
||||
String startwp = p.readString();
|
||||
Portal portal = chr.getMap().getPortal(startwp);
|
||||
p.readByte();
|
||||
boolean wheel = p.readByte() > 0;
|
||||
if (targetMapId != -1) {
|
||||
if (!chr.isAlive()) {
|
||||
MapleMap map = chr.getMap();
|
||||
if (wheel && chr.haveItemWithId(ItemId.WHEEL_OF_FORTUNE, false)) {
|
||||
// thanks lucasziron (lziron) for showing revivePlayer() triggering by Wheel
|
||||
|
||||
boolean chasing = p.readByte() == 1 && chr.isGM();
|
||||
if (chasing) {
|
||||
chr.setChasing(true);
|
||||
chr.setPosition(new Point(p.readInt(), p.readInt()));
|
||||
}
|
||||
InventoryManipulator.removeById(c, InventoryType.CASH, ItemId.WHEEL_OF_FORTUNE, 1, true, false);
|
||||
chr.sendPacket(PacketCreator.showWheelsLeft(chr.getItemQuantity(ItemId.WHEEL_OF_FORTUNE, false)));
|
||||
|
||||
if (targetid != -1) {
|
||||
if (!chr.isAlive()) {
|
||||
MapleMap map = chr.getMap();
|
||||
if (wheel && chr.haveItemWithId(ItemId.WHEEL_OF_FORTUNE, false)) {
|
||||
// thanks lucasziron (lziron) for showing revivePlayer() triggering by Wheel
|
||||
|
||||
InventoryManipulator.removeById(c, InventoryType.CASH, ItemId.WHEEL_OF_FORTUNE, 1, true, false);
|
||||
chr.sendPacket(PacketCreator.showWheelsLeft(chr.getItemQuantity(ItemId.WHEEL_OF_FORTUNE, false)));
|
||||
|
||||
chr.updateHp(50);
|
||||
chr.changeMap(map, map.findClosestPlayerSpawnpoint(chr.getPosition()));
|
||||
} else {
|
||||
boolean executeStandardPath = true;
|
||||
if (chr.getEventInstance() != null) {
|
||||
executeStandardPath = chr.getEventInstance().revivePlayer(chr);
|
||||
chr.updateHp(50);
|
||||
chr.changeMap(map, map.findClosestPlayerSpawnpoint(chr.getPosition()));
|
||||
} else {
|
||||
boolean executeStandardPath = true;
|
||||
if (chr.getEventInstance() != null) {
|
||||
executeStandardPath = chr.getEventInstance().revivePlayer(chr);
|
||||
}
|
||||
if (executeStandardPath) {
|
||||
chr.respawn(map.getReturnMapId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chr.isGM()) {
|
||||
MapleMap to = chr.getWarpMap(targetMapId);
|
||||
chr.changeMap(to, to.getPortal(0));
|
||||
} else {
|
||||
final int divi = chr.getMapId() / 100;
|
||||
boolean warp = false;
|
||||
if (divi == 0) {
|
||||
if (targetMapId == 10000) {
|
||||
warp = true;
|
||||
}
|
||||
if (executeStandardPath) {
|
||||
chr.respawn(map.getReturnMapId());
|
||||
} else if (divi == 20100) {
|
||||
if (targetMapId == MapId.LITH_HARBOUR) {
|
||||
c.sendPacket(PacketCreator.lockUI(false));
|
||||
c.sendPacket(PacketCreator.disableUI(false));
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi == 9130401) { // Only allow warp if player is already in Intro map, or else = hack
|
||||
if (targetMapId == MapId.EREVE || targetMapId / 100 == 9130401) { // Cygnus introduction
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi == 9140900) { // Aran Introduction
|
||||
if (targetMapId == MapId.ARAN_TUTO_2 || targetMapId == MapId.ARAN_TUTO_3 || targetMapId == MapId.ARAN_TUTO_4 || targetMapId == MapId.ARAN_INTRO) {
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi / 10 == 1020) { // Adventurer movie clip Intro
|
||||
if (targetMapId == 1020000) {
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi / 10 >= 980040 && divi / 10 <= 980045) {
|
||||
if (targetMapId == MapId.WITCH_TOWER_ENTRANCE) {
|
||||
warp = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (chr.isGM()) {
|
||||
MapleMap to = chr.getWarpMap(targetid);
|
||||
if (warp) {
|
||||
final MapleMap to = chr.getWarpMap(targetMapId);
|
||||
chr.changeMap(to, to.getPortal(0));
|
||||
} else {
|
||||
final int divi = chr.getMapId() / 100;
|
||||
boolean warp = false;
|
||||
if (divi == 0) {
|
||||
if (targetid == 10000) {
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi == 20100) {
|
||||
if (targetid == MapId.LITH_HARBOUR) {
|
||||
c.sendPacket(PacketCreator.lockUI(false));
|
||||
c.sendPacket(PacketCreator.disableUI(false));
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi == 9130401) { // Only allow warp if player is already in Intro map, or else = hack
|
||||
if (targetid == MapId.EREVE || targetid / 100 == 9130401) { // Cygnus introduction
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi == 9140900) { // Aran Introduction
|
||||
if (targetid == MapId.ARAN_TUTO_2 || targetid == MapId.ARAN_TUTO_3 || targetid == MapId.ARAN_TUTO_4 || targetid == MapId.ARAN_INTRO) {
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi / 10 == 1020) { // Adventurer movie clip Intro
|
||||
if (targetid == 1020000) {
|
||||
warp = true;
|
||||
}
|
||||
} else if (divi / 10 >= 980040 && divi / 10 <= 980045) {
|
||||
if (targetid == MapId.WITCH_TOWER_ENTRANCE) {
|
||||
warp = true;
|
||||
}
|
||||
}
|
||||
if (warp) {
|
||||
final MapleMap to = chr.getWarpMap(targetid);
|
||||
chr.changeMap(to, to.getPortal(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (portal != null && !portal.getPortalStatus()) {
|
||||
c.sendPacket(PacketCreator.blockedMessage(1));
|
||||
if (portal != null && !portal.getPortalStatus()) {
|
||||
c.sendPacket(PacketCreator.blockedMessage(1));
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (chr.getMapId() == MapId.FITNESS_EVENT_LAST) {
|
||||
chr.getFitness().resetTimes();
|
||||
} else if (chr.getMapId() == MapId.OLA_EVENT_LAST_1 || chr.getMapId() == MapId.OLA_EVENT_LAST_2) {
|
||||
chr.getOla().resetTimes();
|
||||
}
|
||||
|
||||
if (portal != null) {
|
||||
if (portal.getPosition().distanceSq(chr.getPosition()) > 400000) {
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (chr.getMapId() == MapId.FITNESS_EVENT_LAST) {
|
||||
chr.getFitness().resetTimes();
|
||||
} else if (chr.getMapId() == MapId.OLA_EVENT_LAST_1 || chr.getMapId() == MapId.OLA_EVENT_LAST_2) {
|
||||
chr.getOla().resetTimes();
|
||||
}
|
||||
|
||||
if (portal != null) {
|
||||
if (portal.getPosition().distanceSq(chr.getPosition()) > 400000) {
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
portal.enterPortal(c);
|
||||
} else {
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
portal.enterPortal(c);
|
||||
} else {
|
||||
c.sendPacket(PacketCreator.enableActions());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void enterFromCashShop(Client c) {
|
||||
final Character chr = c.getPlayer();
|
||||
|
||||
if (!chr.getCashShop().isOpened()) {
|
||||
c.disconnect(false, false);
|
||||
return;
|
||||
}
|
||||
String[] socket = c.getChannelServer().getIP().split(":");
|
||||
chr.getCashShop().open(false);
|
||||
|
||||
chr.setSessionTransitionState();
|
||||
try {
|
||||
c.sendPacket(PacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
|
||||
} catch (UnknownHostException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ package net.server.channel.handlers;
|
||||
|
||||
import client.Character;
|
||||
import client.Client;
|
||||
import config.YamlConfig;
|
||||
import net.AbstractPacketHandler;
|
||||
import net.packet.InPacket;
|
||||
import tools.PacketCreator;
|
||||
@@ -42,6 +43,11 @@ public final class MesoDropHandler extends AbstractPacketHandler {
|
||||
p.skip(4);
|
||||
int meso = p.readInt();
|
||||
|
||||
if (player.isGM() && player.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_DROP) {
|
||||
player.message("You cannot drop mesos at your GM level.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (c.tryacquireClient()) { // thanks imbee for noticing players not being able to throw mesos too fast
|
||||
try {
|
||||
if (meso <= player.getMeso() && meso > 9 && meso < 50001) {
|
||||
|
||||
@@ -69,7 +69,7 @@ public final class WeddingHandler extends AbstractPacketHandler {
|
||||
if (!item.isUntradeable()) {
|
||||
if (itemid == item.getItemId() && quantity <= item.getQuantity()) {
|
||||
newItem = item.copy();
|
||||
|
||||
newItem.setQuantity(quantity);
|
||||
marriage.addGiftItem(groomWishlist, newItem);
|
||||
InventoryManipulator.removeFromSlot(c, type, slot, quantity, false, false);
|
||||
|
||||
@@ -161,4 +161,4 @@ public final class WeddingHandler extends AbstractPacketHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,23 @@ public class AbstractPlayerInteraction {
|
||||
warpParty(id, portalId, mapid, mapid);
|
||||
}
|
||||
|
||||
public void warpParty(int map, String portalName) {
|
||||
|
||||
int mapid = getMapId();
|
||||
var warpMap = c.getChannelServer().getMapFactory().getMap(map);
|
||||
|
||||
var portal = warpMap.getPortal(portalName);
|
||||
|
||||
if (portal == null) {
|
||||
portal = warpMap.getPortal(0);
|
||||
}
|
||||
|
||||
var portalId = portal.getId();
|
||||
|
||||
warpParty(map, portalId, mapid, mapid);
|
||||
|
||||
}
|
||||
|
||||
public void warpParty(int id, int fromMinId, int fromMaxId) {
|
||||
warpParty(id, 0, fromMinId, fromMaxId);
|
||||
}
|
||||
|
||||
@@ -448,6 +448,20 @@ public class Trade {
|
||||
}
|
||||
|
||||
public static void inviteTrade(Character c1, Character c2) {
|
||||
|
||||
if ((c1.isGM() && !c2.isGM()) && c1.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_TRADE) {
|
||||
c1.message("You cannot trade with non-GM characters.");
|
||||
log.info(String.format("GM %s blocked from trading with %s due to GM level.", c1.getName(), c2.getName()));
|
||||
cancelTrade(c1, TradeResult.NO_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!c1.isGM() && c2.isGM()) && c2.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_TRADE) {
|
||||
c1.message("You cannot trade with this GM character.");
|
||||
cancelTrade(c1, TradeResult.NO_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (InviteCoordinator.hasInvite(InviteType.TRADE, c1.getId())) {
|
||||
if (hasTradeInviteBack(c1, c2)) {
|
||||
c1.message("You are already managing this player's trade invitation.");
|
||||
|
||||
Reference in New Issue
Block a user