Compare commits

...

148 Commits

Author SHA1 Message Date
P0nk
d9d2da1f35 Merge branch 'master' into feat/postgresql-database
# Conflicts:
#	database/sql/1-db_database.sql
#	docker-compose.yml
#	pom.xml
#	scripts/npc/mapleTV.js
#	src/main/java/net/server/Server.java
2025-07-22 21:21:05 +02:00
P0nk
e4430a3860 Merge branch 'master' into feat/postgresql-database 2024-10-21 05:52:25 +02:00
P0nk
f2b8ced976 Move chr MySQL saving to CharacterSaver 2024-10-06 19:57:59 +02:00
P0nk
a4f8086da1 Log out everyone on startup 2024-10-03 21:36:19 +02:00
P0nk
e295a24d98 Unban macs & hwid 2024-10-03 21:36:04 +02:00
P0nk
64f99a29c6 Unban account & ip 2024-10-03 21:10:05 +02:00
P0nk
40425ac4e1 Ban ip, macs & hwid in PG
Finally rid of all db code in Client
2024-10-03 19:17:01 +02:00
P0nk
2b6ef9feb5 Save client addresses async on chr select
Almost rid of all db queries in Client
2024-10-03 08:28:55 +02:00
P0nk
e38344ceae Refactor BanService#ban 2024-10-02 06:54:55 +02:00
P0nk
5d81e05458 Redo mac bans - reduce amount of db queries on login
Works just like ip and hwid bans in that they are loaded on startup
2024-10-01 22:12:24 +02:00
P0nk
af02f8b744 Redo hwid bans - reduce amount of db queries on login
Works by loading all hwid bans on startup and querying the collection in memory
rather than making calls on every login.
2024-10-01 07:04:25 +02:00
P0nk
7661cd0f75 Redo ip bans - reduce amount of db queries on login
Works by loading all ip bans on startup and querying the collection in memory
rather than making calls on every login.
2024-09-30 19:31:21 +02:00
P0nk
167937bb88 Check banned hwid on login 2024-09-29 22:22:23 +02:00
P0nk
988d898d6f Clean up tempBanCalendar 2024-09-29 21:37:33 +02:00
P0nk
e35060da2a Handle ban solely through BanService 2024-09-29 20:25:11 +02:00
P0nk
c0ee1f8ffe Encapsulate account creation in AccountService 2024-09-29 19:33:20 +02:00
P0nk
99006c2dda Set ban through AccountService 2024-09-29 19:30:24 +02:00
P0nk
f142e21bbb Fix broken test 2024-09-29 18:14:05 +02:00
P0nk
1c6245fa6c birthday represented as LocalDate 2024-09-29 18:09:05 +02:00
P0nk
a307afae3c Unify ban handling on login 2024-09-29 17:55:51 +02:00
P0nk
50524a7740 Show correct ban reason 2024-09-29 17:30:09 +02:00
P0nk
d00b4ed678 Rename "temp_ban_timestamp" -> "temp_banned_until" 2024-09-29 16:34:04 +02:00
P0nk
b45620154c LoginState enum 2024-09-29 16:22:32 +02:00
P0nk
5450c29178 Reduce login state updates, fix multi-login on same acc 2024-09-29 15:55:25 +02:00
P0nk
fa666c98e6 Rename loginattempt 2024-09-29 14:24:56 +02:00
P0nk
902f1a154e Log out in PG on exit game
Can nog log in, enter game, exit game and re-login successfully.
2024-09-29 09:26:55 +02:00
P0nk
4e1aa1eb1a Rename NOT_LOGGED_IN -> LOGGED_OUT 2024-09-29 09:15:01 +02:00
P0nk
a580e44bc9 Rename AccountService methods 2024-09-29 09:14:32 +02:00
P0nk
da4a467453 Set in transition on log in, now able to enter the game
The state is not set properly on logout though, so once you log out you can't log back in
2024-09-29 08:22:26 +02:00
P0nk
0bb14e415e Log in through AccountService on player login 2024-09-29 07:52:42 +02:00
P0nk
0a8591aa08 Merge "serverTransition" and "inTransition" fields
They serve the same purpose, no point in separating
2024-09-29 07:43:47 +02:00
P0nk
813768ec47 Remove AddWorld,AddChannel,RemoveWorld,RemoveChannel cmds
These are incredibly error prone and hinder the database migration work.
2024-09-29 07:32:20 +02:00
P0nk
5595f5763b Log in through AccountService 2024-09-28 18:30:41 +02:00
P0nk
1be775394e Disable slow tests that don't provide much value 2024-09-28 18:30:31 +02:00
P0nk
55c9d4abbb Log out through AccountService 2024-09-28 18:26:23 +02:00
P0nk
93bbe868cb Use LoginState everywhere 2024-09-28 07:02:05 +02:00
P0nk
ec39f0fa06 Avoid additional db query to get login state during login 2024-09-28 06:55:45 +02:00
P0nk
439280947c Save gender to PG 2024-09-27 23:20:02 +02:00
P0nk
5abae50be5 Rework login, get account from PG 2024-09-27 22:52:12 +02:00
P0nk
082e0c0486 Save chr slots to PG 2024-09-27 17:59:26 +02:00
P0nk
2044166967 Save pic to PG 2024-09-27 17:34:36 +02:00
P0nk
f33d4fbc1c Save pin to PG 2024-09-27 06:50:07 +02:00
P0nk
0f2ef341ce Save accepted ToS to PG 2024-09-26 21:14:52 +02:00
P0nk
c7f835da0d Add DatabaseTest to easily test repositories 2024-09-26 19:51:18 +02:00
P0nk
647e67f6e8 Auto-create account in both MySQL and PG 2024-09-26 07:59:27 +02:00
P0nk
bf9c02bc16 Fix failed chr save due to null jail expiration 2024-09-26 07:59:11 +02:00
P0nk
1d5c26e67c Remove SHA-512 password migration 2024-09-26 06:48:13 +02:00
P0nk
b85233359f Save new chr to PG (doesn't work yet because no account) 2024-09-25 18:31:38 +02:00
P0nk
7335914695 Simplify sp saving 2024-09-25 18:07:50 +02:00
P0nk
b4e673baab Make some chr stats nullable to ease migration to PG 2024-09-25 17:48:12 +02:00
P0nk
767c4402e7 Route chr creation to dummy CharacterCreator 2024-09-25 07:21:27 +02:00
P0nk
a9967d53b1 Fix chr loading due to "monster_card" table not found 2024-09-25 06:58:19 +02:00
P0nk
00fc3ed1a1 Handle created chr with spec 2024-09-25 06:33:03 +02:00
P0nk
98d76ad45e Prepare centralized chr creation 2024-09-25 06:32:43 +02:00
P0nk
0c9643fd7e CharacterSaver integration test with Testcontainers 2024-09-15 11:41:03 +02:00
P0nk
ce5dee39ae Reorder migration scripts - move acc+chr creation earlier 2024-09-14 17:48:20 +02:00
P0nk
e57d2a9ee2 Create account, chr tables & save chr to Postgres 2024-09-14 17:44:48 +02:00
P0nk
f827e23ccc Clean up rewardpoints 2024-09-14 15:39:06 +02:00
P0nk
f8726640c3 Remove configurable language feature 2024-09-14 15:34:01 +02:00
P0nk
4778482cdd Remove quest point feature 2024-09-14 15:02:51 +02:00
P0nk
7297cd09b6 Separate PG from MySQL chr saving 2024-09-14 14:56:15 +02:00
P0nk
08eeeb54dc Rename ...Dao -> ...Repository 2024-09-14 12:06:09 +02:00
P0nk
abbec8120e Restructure chr saving - gather stats in record 2024-09-14 11:57:54 +02:00
P0nk
23a8a33e5f Move transaction handling to CharacterSaver 2024-09-13 23:59:29 +02:00
P0nk
758347f7bc Small refactor of disconnectInternal for readability 2024-09-13 23:23:35 +02:00
P0nk
1b6c0167de Fix buddy staying online in buddy list if dc in cash shop 2024-09-13 23:17:42 +02:00
P0nk
8bce0922d9 Fix broken global drops since merge from master 2024-09-13 23:02:12 +02:00
P0nk
f41268cdde Handle disconnect solely in TransitionService 2024-09-13 22:59:55 +02:00
P0nk
719b079cbc Refactor unnecessary wrapper boolean 2024-09-13 21:47:52 +02:00
P0nk
7bd5fa387d Merge branch 'refs/heads/master' into feat/postgresql-database 2024-09-13 21:29:50 +02:00
P0nk
09a75e89c9 Merge branch 'refs/heads/master' into feat/postgresql-database
# Conflicts:
#	src/main/java/server/life/MonsterInformationProvider.java
#	src/main/java/server/maps/MapleMap.java
2024-09-03 17:45:27 +02:00
P0nk
559fe2d550 Merge branch 'refs/heads/master' into feat/postgresql-database
# Conflicts:
#	config.yaml
#	docker-compose.yml
#	pom.xml
#	src/main/java/client/Character.java
#	src/main/java/client/Client.java
#	src/main/java/client/MonsterBook.java
#	src/main/java/client/command/commands/gm0/BuyBackCommand.java
#	src/main/java/client/processor/stat/AssignAPProcessor.java
#	src/main/java/config/ServerConfig.java
#	src/main/java/net/server/channel/Channel.java
#	src/main/java/net/server/channel/handlers/AbstractDealDamageHandler.java
#	src/main/java/net/server/channel/handlers/BuddylistModifyHandler.java
#	src/main/java/net/server/channel/handlers/CloseRangeDamageHandler.java
#	src/main/java/net/server/channel/handlers/EnterMTSHandler.java
#	src/main/java/net/server/channel/handlers/NPCTalkHandler.java
#	src/main/java/net/server/channel/handlers/RangedAttackHandler.java
#	src/main/java/net/server/channel/handlers/SummonDamageHandler.java
#	src/main/java/net/server/channel/handlers/UseCashItemHandler.java
#	src/main/java/net/server/handlers/login/CreateCharHandler.java
#	src/main/java/net/server/world/World.java
#	src/main/java/scripting/npc/NPCConversationManager.java
#	src/main/java/server/ItemInformationProvider.java
#	src/main/java/server/life/Monster.java
#	src/main/java/server/life/MonsterInformationProvider.java
#	src/main/java/server/maps/MapleMap.java
#	src/main/java/tools/PacketCreator.java
#	src/test/java/service/NoteServiceTest.java
#	src/test/java/testutil/Any.java
2024-09-02 20:43:55 +02:00
P0nk
a7d03cf8c0 Merge branch 'master' into feat/postgresql-database 2024-02-04 15:12:14 +01:00
P0nk
033d91ed71 Merge branch 'master' into feat/postgresql-database
# Conflicts:
#	config.yaml
#	src/main/java/config/ServerConfig.java
2023-11-08 21:21:07 +01:00
P0nk
392a350eab Merge branch 'master' into feat/postgresql-database
# Conflicts:
#	docker-compose.yml
2023-08-20 18:30:52 +02:00
P0nk
f33df59f49 Move some Client#forceDisconnect calls to TransitionService 2023-08-11 22:00:33 +02:00
P0nk
cd75e85bec Move some autoban logic to new BanService 2023-08-10 22:52:09 +02:00
P0nk
cb31121fe7 Disconnect client with TransitionService 2023-08-10 21:58:55 +02:00
P0nk
d5682a5f65 Add client disconnection logic to TransitionService
Problem: disconnecting requires access to CharacterSaver,
which is not available in Client.
Having it in a service like this solves that problem.

Next step is to migrate all calls to Client#disconnect and Client#forceDisconnect
to their TransitionService counterparts.
2023-08-08 21:51:12 +02:00
P0nk
f6d06ba82a Extract "char list" from Client to handler 2023-08-06 21:14:32 +02:00
P0nk
bbee8d7caa Merge chr name + id wrappers into new CharacterIdentity record 2023-08-06 20:35:26 +02:00
P0nk
5b5888cf65 Remove unused stuff in Client 2023-08-06 20:11:32 +02:00
P0nk
48d9aaa871 Clean up Client - visibleWorlds & canRequestCharlist
canRequestCharlist is a relic from the past when "View all char"
functionality was hacked together with wrong packets.

visibleWorlds I'm less sure about. I suppose it's useful if you add world
(via command) while someone is still on the login screen.
But the functionality of adding/removing worlds live is a recipe for disaster
and will eventually (likely) be removed.
2023-08-06 20:02:39 +02:00
P0nk
f44083aeba Refactor Client#finishLogin 2023-08-06 17:26:34 +02:00
P0nk
810dbcc1d7 Remove "reward points" feature 2023-08-06 16:55:58 +02:00
P0nk
449ab01bc2 Remove "vote points" feature 2023-08-06 16:55:34 +02:00
P0nk
2686b2b02d Disconnect client by throwing exception in handler
This makes it easier to add checks in handlers, which should improve security over time.
I think this approach is more readable and testable than calling Client#disconnect straight up,
while it also decentralizes the handling.
2023-08-06 15:48:49 +02:00
P0nk
e9819fac87 Save cooldowns and diseases as part of normal flow 2023-08-04 16:34:59 +02:00
P0nk
4e39142fb3 Direct almost all chr saving through CharacterSaver
Client#disconnectInternal remains.
Had to remove some configurable save points to keep it simple.
2023-08-04 16:14:51 +02:00
P0nk
e52f646558 Fix batch insert monster cards
batch.add() clears all bindings, unlike vanilla JDBC
2023-08-04 15:38:14 +02:00
P0nk
02d4ff524a Fix global drops not dropping
No longer able to shuffle the list since DropProvider
return an unmodifiable list in the case of global drops.
2023-08-04 15:23:02 +02:00
P0nk
05b7ec77c8 Add ChannelService to handle cc'ing 2023-08-04 15:01:49 +02:00
P0nk
f6aa8ceba6 Rewrite MonsterBook, touch up chr loading
Temporarily disabled loading monster cards from db
2023-07-25 21:27:35 +02:00
P0nk
b3ec325e95 Add monster card table and initial dao 2023-07-25 18:46:03 +02:00
P0nk
e31465a1b5 Add MonsterCard 2023-07-25 17:52:38 +02:00
P0nk
84ad5b4db8 Remove scroll generator (custom feature)
This allowed me to remove the getCardTierSize db query
2023-07-25 17:20:16 +02:00
P0nk
855b66c459 Merge branch 'master' into feat/postgresql-database 2023-07-25 17:03:35 +02:00
P0nk
46f767d79c Merge branch 'master' into feat/postgresql-database 2023-05-29 15:56:53 +02:00
P0nk
785f74ed21 Fix HelpCommand not working without static CommandsExecutor 2023-03-31 07:59:43 +02:00
P0nk
699da37f06 Simplify NPC script start 2023-03-31 07:33:49 +02:00
P0nk
c3badba73b Convert ShopItem to record 2023-03-30 07:07:33 +02:00
P0nk
5f0e9a355b Move Shop stuff to own package 2023-03-30 06:47:07 +02:00
P0nk
39d759595d Get shop as Optional 2023-03-30 06:41:13 +02:00
P0nk
fe9dd75a23 Instantiate shops in ShopFactory 2023-03-29 21:59:00 +02:00
P0nk
2139147dcd Add getShop to DAO, preparing to refactor shop loading 2023-03-29 21:14:19 +02:00
P0nk
c71ca7f4d5 Inject ShopFactory 2023-03-29 20:51:31 +02:00
P0nk
705efb4340 Remove access to shops from scripts 2023-03-29 20:38:34 +02:00
P0nk
ec1450cec1 Rewrite Mo NPC script to be GMS-like 2023-03-29 19:54:59 +02:00
P0nk
290bf78db3 Notes about difficulties of DI for scripts 2023-03-18 16:04:01 +01:00
P0nk
02c1fe46f3 Remove custom shops
These custom shops we introduced in HeavenMS.
2082014 - Asia selling scrolls
9201101 - T-1337 selling special potions
2023-03-18 15:14:00 +01:00
P0nk
7315dd80fc Remove redundant scripts for Ariant weapon & armor merchants 2023-03-18 15:08:06 +01:00
P0nk
a496c0f2b4 Add ShopDao 2023-03-18 14:32:21 +01:00
P0nk
ab0239ba84 Add shop and shop_item PG tables with data 2023-03-17 20:53:16 +01:00
P0nk
f2f3abdb32 Move last pieces of monster drops to DropProvider 2023-03-16 18:55:33 +01:00
P0nk
5a35b55d7a Cache continent drops 2023-03-16 18:03:43 +01:00
P0nk
02aa4a237f Global monster drops are retrieved from DropProvider (postgres db) 2023-03-16 18:00:21 +01:00
P0nk
cc88d382e6 Monster drops are retrieved from DropProvider (postgres db)
This commit emphasizes the need for events to be reworked.
This is the chain of constructors the DropProvider has to pass through to reach MapleMap:
Channel -> EventScriptManager -> EventManager -> EventInstanceManager -> MapManager -> MapleMap
2023-03-16 08:31:38 +01:00
P0nk
a95fa2efc1 Refactor LootInventory & LootManager 2023-03-16 00:25:21 +01:00
P0nk
45e2b93724 Remove "multiple same equip drop" feature 2023-03-15 23:26:15 +01:00
P0nk
fa3481fa99 Pass CommandContext to commands
CommandContext is the carrier of dependencies.
Currently, it only carries a DropProvider, but
it will grow bit by bit as more static singletons
and other similar structures get refactored.
2023-03-15 23:19:04 +01:00
P0nk
eed94ec34a Refactor CommandsExecutor - is no longer static singleton
Preparing for change in Command#handle,
which is going to take a CommandContext as an additional argument.
This way we can pass in command dependencies in a safe way
instead of requiring them to access static methods.
2023-03-15 22:56:40 +01:00
P0nk
a6a7a26ebc Clean up Steal stuff in MIP 2023-03-15 22:41:36 +01:00
P0nk
7ed7b25268 Fix race condition when concurrently Stealing 2023-03-15 22:38:54 +01:00
P0nk
703ae30a27 Move "steal item" logic to DropProvider 2023-03-15 22:22:14 +01:00
P0nk
6bf8785f22 Fix error in monster_drop query 2023-03-15 22:17:42 +01:00
P0nk
53768555a2 Add table, data, and provider method for global drops 2023-03-09 20:08:20 +01:00
P0nk
a70f4e303d Add DropProvider and dao for monster drops 2023-03-08 22:46:24 +01:00
P0nk
7a848cf6a1 Add monster_drop table with data 2023-03-08 22:31:14 +01:00
P0nk
0be48568d7 Get MakerItemCreateEntry from new info provider 2023-03-08 21:09:47 +01:00
P0nk
1fd0963401 Fix maker disassembly fee not being applied 2023-03-04 22:53:05 +01:00
P0nk
d2d4b442d2 Maker disassembly info to MakerInfoProvider
MakerProcessor is such a mess...
2023-03-04 22:33:34 +01:00
P0nk
9d6574d3ba Cache maker info with Caffeine 2023-03-04 21:27:18 +01:00
P0nk
f2ca67aba4 Add tests for MakerInfoProvider
These are going to be handy soon,
as I'm about to add Caffeine for caching.
2023-03-04 21:02:04 +01:00
P0nk
6073b20d65 Cache MakerRecipe, move stimulant to MakerInfoProvider 2023-03-04 17:20:16 +01:00
P0nk
b329709776 Expose containerized databases to host
This way you can connect with your favorite db client
2023-03-04 16:04:18 +01:00
P0nk
932f9fc443 Merge branch 'master' into feat/postgresql-database
# Conflicts:
#	pom.xml
2023-03-02 18:40:27 +01:00
P0nk
c0c0a2d2d9 Get maker reagent from PG db, rework processor (no statics) 2023-03-02 08:18:13 +01:00
P0nk
5cecb7adb6 Add maker_random_reward table and data (formerly "makerrewarddata") 2023-03-01 21:09:32 +01:00
P0nk
26357a8d7b Add maker_reagent table and data (formerly "makerreagentdata") 2023-03-01 20:51:26 +01:00
P0nk
9819fd5bb1 Add maker_recipe_ingredient table and data (formerly "makerrecipedata") 2023-03-01 20:45:51 +01:00
P0nk
adbdb89917 Add maker_recipe table and data (formerly "makercreatedata") 2023-03-01 20:34:48 +01:00
P0nk
b7c79800eb Fix note not showing if case mismatch in name
This wouldn't be a problem in the first case,
if there was a foreign key connection between
note and character tables.
Can't add that quite yet though.
2023-02-28 21:30:20 +01:00
P0nk
55e1f65bbd Fix server not able to use note sequence 2023-02-27 19:55:01 +01:00
P0nk
d018b0f5e5 Add note receiver index
Only makes a difference if there is
a large amount of notes in the database.
Better be safe than sorry.
2023-02-27 18:16:23 +01:00
P0nk
12745c207d Move notes to the Postgres database 2023-02-25 00:40:36 +01:00
P0nk
f6f3c9c3e3 Initiate Postgres connection pool on startup 2023-02-25 00:11:33 +01:00
P0nk
8bb825ef02 Add Docker compose support for Postgres 2023-02-24 23:42:13 +01:00
P0nk
f1192279bf Add Flyway, run db migration in local PostgreSQL db
First step in my plan to switch away from MySQL.
2023-02-24 21:49:00 +01:00
392 changed files with 36427 additions and 5121 deletions

View File

@@ -165,6 +165,14 @@ server:
DB_PASS: ""
INIT_CONNECTION_POOL_TIMEOUT: 90 # Seconds
PG_DB_URL: "jdbc:postgresql://localhost:5432/cosmic"
PG_DB_SCHEMA: "cosmic"
PG_DB_ADMIN_USERNAME: "cosmic_admin"
PG_DB_ADMIN_PASSWORD: "redsnailshell"
PG_DB_USERNAME: "cosmic_server"
PG_DB_PASSWORD: "bluesnailshell"
PG_DB_CLEAN: false # !!! WARNING !!! Deletes the entire database - starts from scratch.
#Login Configuration
WORLDS: 1 #Initial number of worlds on the server.
WLDLIST_SIZE: 21 #Max possible worlds on the server.
@@ -185,7 +193,6 @@ server:
BYPASS_PIN_EXPIRATION: 15 #Enables PIN bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable.
AUTOMATIC_REGISTER: true #Automatically register players when they login with a nonexistent username.
BCRYPT_MIGRATION: true #Performs a migration from old SHA-1 and SHA-512 password to bcrypt.
COLLECTIVE_CHARSLOT: false #Available character slots are contabilized globally rather than per world server.
DETERRED_MULTICLIENT: false #Enables detection of multi-client and suspicious remote IP on the login system.
#Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses.
@@ -234,7 +241,6 @@ server:
USE_AUTOBAN: false #Commands the server to detect infractors automatically.
USE_AUTOBAN_LOG: true #Log autoban related messages. Still logs even with USE_AUTOBAN disabled.
USE_EXP_GAIN_LOG: false #Logs characters exp gains; logs world rate & coupon exp, total gained exp, and current exp, level can be calculated from "ExpTable".
USE_AUTOSAVE: true #Enables server autosaving feature (saves characters to DB each 1 hour).
USE_SERVER_AUTOASSIGNER: false #HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
USE_REFRESH_RANK_MOVE: true
USE_ENFORCE_ADMIN_ACCOUNT: false #Forces accounts having GM characters to be treated as a "GM account" by the client (localhost). Some of the GM account perks is the ability to FLY, but unable to TRADE.
@@ -246,7 +252,6 @@ server:
USE_ENFORCE_ITEM_SUGGESTION: false #Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players.
USE_ENFORCE_UNMERCHABLE_CASH: true #Forces players to not sell CASH items via merchants, drops of it disappears.
USE_ENFORCE_UNMERCHABLE_PET: true #Forces players to not sell pets via merchants, drops of it disappears. (since non-named pets gets dirty name and other possible DB-related issues)
USE_ENFORCE_MERCHANT_SAVE: true #Forces automatic DB save on merchant owners, at every item movement on shop.
USE_ENFORCE_MDOOR_POSITION: false #Forces mystic door to be spawned near spawnpoints.
USE_SPAWN_CLEAN_MDOOR: false #Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment.
USE_SPAWN_RELEVANT_LOOT: true #Forces to only spawn loots that are collectable by the player or any of their party members.
@@ -355,7 +360,6 @@ server:
USE_FULL_HOLY_SYMBOL: false #Holy symbol doesn't require EXP sharers to work in full.
#Character Configuration
USE_ADD_SLOTS_BY_LEVEL: false #Slots are added each 20 levels.
USE_ADD_RATES_BY_LEVEL: false #Rates are added each 20 levels.
USE_STACK_COUPON_RATES: false #Multiple coupons effects builds up together.
USE_PERFECT_PITCH: false #For lvl 30 or above, each lvlup grants player 1 perfect pitch.
@@ -363,12 +367,6 @@ server:
#Quest Configuration
USE_QUEST_RATE: false #Exp/Meso gained by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates.
#Quest Points Configuration
QUEST_POINT_REPEATABLE_INTERVAL: 25 #Minimum interval between repeatable quest completions for quest points to be awarded.
QUEST_POINT_REQUIREMENT: 0 #Exchange factor between N quest points to +1 fame, set 0 to disable the entire quest point mechanism.
QUEST_POINT_PER_QUEST_COMPLETE: 0 #Each completed quest awards N quest points, set 0 to disable.
QUEST_POINT_PER_EVENT_CLEAR: 0 #Each completed event instance awards N quest points, set 0 to disable.
#Guild Configuration
CREATE_GUILD_MIN_PARTNERS: 6 #Minimum number of members on Guild Headquarters to establish a new guild.
CREATE_GUILD_COST: 1500000

2
database/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
docker-db-data/*
docker-pg-db-data/*

View File

@@ -1,3 +0,0 @@
*
*/
!.gitignore

View File

@@ -0,0 +1,3 @@
CREATE DATABASE cosmic;
CREATE USER cosmic_admin WITH CREATEROLE ENCRYPTED PASSWORD 'redsnailshell';
GRANT ALL PRIVILEGES ON DATABASE cosmic TO cosmic_admin;

View File

@@ -0,0 +1 @@

View File

@@ -4,6 +4,7 @@ services:
build: .
depends_on:
- db
- pg_db
ports:
# Login server
- "8484:8484"
@@ -19,7 +20,8 @@ services:
- ./wz:/opt/server/wz
environment:
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
PG_DB_HOST: "pg_db"
db:
image: mysql:8.4.0
environment:
@@ -30,3 +32,13 @@ services:
- "3307:3306"
volumes:
- ./database/docker-db-data:/var/lib/mysql
pg_db:
image: postgres:15.0
ports:
- "5433:5432"
environment:
POSTGRES_PASSWORD: postgres
volumes:
- ./database/docker-pg-db-data:/var/lib/postgresql/data
- ./database/postgres-scripts:/docker-entrypoint-initdb.d

45
pom.xml
View File

@@ -69,6 +69,11 @@
<liquibase-core.version>4.32.0</liquibase-core.version> <!-- Database migrations -->
<junit.version>5.13.1</junit.version> <!-- Unit test -->
<mockito.version>5.18.0</mockito.version> <!-- Unit test -->
<testcontainers.version>1.20.1</testcontainers.version> <!-- Docker test with real temporary database -->
<postgresql.version>42.5.4</postgresql.version> <!-- PostgreSQL JDBC driver -->
<flyway.version>9.15.1</flyway.version> <!-- Database migration -->
<caffeine.version>3.1.4</caffeine.version> <!-- Caching -->
<lombok.version>1.18.34</lombok.version> <!-- Code generation -->
</properties>
<dependencies>
@@ -82,6 +87,17 @@
<artifactId>jcip-annotations</artifactId>
<version>${jcip-annotations.version}</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>${caffeine.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Database -->
<dependency>
@@ -94,11 +110,21 @@
<artifactId>mysql-connector-j</artifactId>
<version>${mysql-connector-j.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
</dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-core</artifactId>
<version>${jdbi-version}</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
@@ -192,6 +218,25 @@
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -1,222 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Rooney
Map Name (Map ID)
Used to exchange VP for Maple Leaves, and Maple Leaves for rewards.
*/
var itemToUse = 4001126;
var chairs = [3010000, 3010001, 3010002, 3010003, 3010004, 3010005, 3010006, 3010007, 3010008, 3010009, 3010010, 3010011, 3010012, 3010013, 3010015, 3010016, 3010017, 3010018, 3010019, 3010022, 3010023, 3010024, 3010025, 3010026, 3010028, 3010040, 3010041, 3010043, 3010045, 3010046, 3010047, 3010057, 3010058, 3010060, 3010061, 3010062, 3010063, 3010064, 3010065, 3010066, 3010067, 3010069, 3010071, 3010072, 3010073, 3010080, 3010081, 3010082, 3010083, 3010084, 3010085, 3010097, 3010098, 3010099, 3010101, 3010106, 3010116, 3011000, 3012005, 3012010, 3012011];
var scrolls = [2040603, 2044503, 2041024, 2041025, 2044703, 2044603, 2043303, 2040807, 2040806, 2040006, 2040007, 2043103, 2043203, 2043003, 2040506, 2044403, 2040903, 2040709, 2040710, 2040711, 2044303, 2043803, 2040403, 2044103, 2044203, 2044003, 2043703];
var weapons = [1302020, 1302030, 1302033, 1302058, 1302064, 1302080, 1312032, 1322054, 1332025, 1332055, 1332056, 1372034, 1382009, 1382012, 1382039, 1402039, 1412011, 1412027, 1422014, 1422029, 1432012, 1432040, 1432046, 1442024, 1442030, 1442051, 1452016, 1452022, 1452045, 1462014, 1462019, 1462040, 1472030, 1472032, 1472055, 1482020, 1482021, 1482022, 1492020, 1492021, 1492022, 1092030, 1092045, 1092046, 1092047];
var nxAmount = 3000;
var chairAmount = 2;
var weaponAmount = 2;
var buffAmount = 2;
var hiredMerchantLength = 7;
var buff1ID = 2022273;
var buff2ID = 2022179;
var status;
var vp;
var choice;
function start() {
//vp = cm.getClient().getVotePoints();
//if(vp == null)
vp = 0;
status = -1;
action(1, 0, 0);
}
function action(mode, type, selection) {
if (mode < 0) {
cm.dispose();
} else {
if (mode == 1) {
status++;
} else {
status--;
}
if (status == 0 && mode == 1) {
if (cm.getPlayer().getLevel() < 20) {
cm.sendOk("Hello, I am the Vote Point exchanger for #rMapleSolaxia#k!\r\n\r\nI am sorry, but I can only exchange Vote Points for players #blevel 20 or over#k.");
cm.dispose();
return;
}
var outStr = "Hello, I am the Vote Point exchanger for #rMapleSolaxia#k!\r\n";
outStr += "You currently have #r#c" + itemToUse + "##k #t" + itemToUse + "# and #r" + vp + "#k Vote Points.#b\r\n\r\n";
outStr += "#L0#I would like to exchange my vote points for Maple Leaves#l\r\n";
outStr += "#L1#I would like to exchange 1 #t" + itemToUse + "# for " + nxAmount + " NX Cash#l\r\n";
outStr += "#L2#I would like to exchange 1 #t" + itemToUse + "# for " + chairAmount + " Random Chair" + (chairAmount > 1 ? "s" : "") + "#l\r\n";
outStr += "#L3#I would like to exchange 1 #t" + itemToUse + "# for " + weaponAmount + " Maple Weapons#l\r\n";
outStr += "#L4#I would like to exchange 1 #t" + itemToUse + "# for " + buffAmount + " #t" + buff1ID + "#s and " + buffAmount + " #t" + buff2ID + "#s#l\r\n";
outStr += "#L5#I would like to exchange 1 #t" + itemToUse + "# for a " + hiredMerchantLength + " Day Hired Merchant#l\r\n";
cm.sendSimple(outStr);
} else if (status == 1) {
choice = selection;
if (selection > 0) {
if (!cm.haveItem(itemToUse) && vp == 0) {
cm.sendOk("I'm sorry, but you don't have any #t" + itemToUse + " or Vote Points.");
cm.dispose();
return;
}
}
if (selection == 0) {
// Exchange VP for leaves
if (vp <= 0) {
cm.sendOk("I'm sorry, but you don't have any Vote Points to exchange!");
cm.dispose();
return;
}
cm.sendYesNo("Would you like to exchange " + vp + " Vote Point" + (vp > 0 ? "s" : "") + " for " + vp + " #t" + itemToUse + "# " + (vp > 0 ? "s" : "") + "?");
} else if (selection == 1) {
// Exchange 1 Leaf for Cash
cm.sendYesNo("Would you like to exchange 1 #t" + itemToUse + "# for " + nxAmount + " NX Cash?");
} else if (selection == 2) {
// Exchange 1 Leaf for Chair
cm.sendYesNo("Would you like to exchange 1 #t" + itemToUse + "# for " + chairAmount + " Random Chair" + (chairAmount > 1 ? "s" : "") + "?");
} else if (selection == 3) {
// Exchange 1 Leaf for Maple Weapons
cm.sendYesNo("Would you like to exchange 1 #t" + itemToUse + "# for " + weaponAmount + " Random Maple Weapons?");
} else if (selection == 4) {
// Exchange 1 Leaf for Apples/Cheese
cm.sendYesNo("Would you like to exchange 1 #t" + itemToUse + "# for " + buffAmount + " #t" + buff1ID + "# and #t" + buff2ID + "#?");
} else if (selection == 5) {
// Echange 1 Leaf for Merchant
cm.sendYesNo("Would you like to exchange 1 #t" + itemToUse + "# for a " + hiredMerchantLength + " Day Hired Merchant?");
} else {
cm.dispose();
}
} else if (status == 2) {
var useVP = false;
if (!cm.hasItem(itemToUse) && vp > 0) {
useVP = true;
}
const InventoryType = Java.type('client.inventory.InventoryType');
if (choice == 0) {
// VP Exchange
if (!cm.canHold(itemToUse)) {
cm.sendOk("It looks like you don't have enough space in your #rETC#k inventory to hold the #t" + itemToUse + "#" + (vp > 0 ? "s" : "") + ".");
cm.dispose();
return;
}
cm.getClient().useVotePoints(vp);
cm.gainItem(itemToUse, vp);
cm.dispose();
} else if (choice == 1) {
// Leaf for Cash
if (useVP) {
cm.getClient().useVotePoints(1);
} else {
cm.gainItem(itemToUse, -1);
}
cm.getPlayer().getCashShop().gainCash(1, nxAmount);
const PacketCreator = Java.type('tools.PacketCreator');
cm.getPlayer().sendPacket(PacketCreator.earnTitleMessage("You have earned " + nxAmount + " NX"));
cm.logLeaf(nxAmount + " NX");
cm.dispose();
} else if (choice == 2) {
if (!cm.getPlayer().getInventory(InventoryType.SETUP).isFull(chairAmount)) {
var chairStr = "";
for (var i = 0; i < chairAmount; i++) {
var chair = chairs[Math.floor(Math.random() * chairs.length)];
cm.gainItem(chair, 1, true);
chairStr += chair + " ";
}
if (useVP) {
cm.getClient().useVotePoints(1);
} else {
cm.gainItem(itemToUse, -1);
}
cm.logLeaf("Chair ID: " + chairStr);
cm.dispose();
} else {
cm.sendOk("Please make sure you have enough space to hold the items!");
}
} else if (choice == 3) {
if (!cm.getPlayer().getInventory(InventoryType.EQUIP).isFull(weaponAmount)) {
var weaponStr = "";
for (var i = 0; i < weaponAmount; i++) {
var weapon = weapons[Math.floor(Math.random() * weapons.length)];
cm.gainItem(weapon, 1, true, true);
weaponStr += weapon + " ";
}
if (useVP) {
cm.getClient().useVotePoints(1);
} else {
cm.gainItem(itemToUse, -1);
}
cm.logLeaf("Maple Weapon IDs: " + weaponStr);
cm.dispose();
} else {
cm.sendOk("Please make sure you have enough space to hold the items!");
}
} else if (choice == 4) {
if (!cm.getPlayer().getInventory(InventoryType.USE).isFull(2)) {
cm.gainItem(buff1ID, buffAmount, true);
cm.gainItem(buff2ID, buffAmount, true);
cm.gainItem(itemToUse, -1);
cm.logLeaf(buffAmount + " cheeses and apples");
cm.dispose();
} else {
cm.sendOk("Please make sure you have enough space to hold the items!");
}
} else if (choice == 5) {
if (!cm.haveItem(5030000, 1)) {
if (!cm.getPlayer().getInventory(InventoryType.CASH).isFull(1)) {
cm.gainItem(5030000, 1, false, true, 1000 * 60 * 60 * 24 * hiredMerchantLength);
if (useVP) {
cm.getClient().useVotePoints(1);
} else {
cm.gainItem(itemToUse, -1);
}
cm.logLeaf(hiredMerchantLength + " day hired merchant");
cm.dispose();
} else {
cm.sendOk("Please make sure you have enough space to hold these items!");
}
} else {
cm.sendOk("I can't give you a merchant if you already have one!");
}
}
} else {
cm.dispose();
}
}
}

View File

@@ -1,25 +0,0 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
function start() {
const ShopFactory = Java.type('server.ShopFactory');
ShopFactory.getInstance().getShop(11000).sendShop(cm.getClient());
cm.dispose();
}

View File

@@ -1,11 +0,0 @@
var status = 0;
function start() {
status = -1;
action(1, 0, 0);
}
function action(mode, type, selection) {
cm.openShopNPC(2100002);
cm.dispose();
}

View File

@@ -1,11 +0,0 @@
var status = 0;
function start() {
status = -1;
action(1, 0, 0);
}
function action(mode, type, selection) {
cm.openShopNPC(2100003);
cm.dispose();
}

View File

@@ -1,14 +1,88 @@
/**
*9201098 - Mo
*@author Ronan
* 9201098 - Mo
* @author Ronan
* @author Ponk
*/
let status = 0;
let selectedItem = undefined;
/*
References:
- https://www.youtube.com/watch?v=g6y2zmCGglI
- https://www.youtube.com/watch?v=CttmlVWLJKM
*/
function start() {
if (cm.getQuestStatus(8224) == 2) {
cm.openShopNPC(9201099);
} else {
cm.sendOk("Hm, at who do you think you are looking at?");
if (cm.getQuestStatus(8224) !== 2) {
cm.sendDefault();
cm.dispose();
return;
}
cm.dispose();
cm.sendSimple("Name's Mo. I've got Mo' items for Mo' mesos. What business do you bring me?\r\n#L0##bI'd like to buy some items#k");
}
function action(action, type, selection) {
if (!action) {
cm.dispose();
return;
}
if (status === 0) {
let index = 0;
const selections = "#e" + shopItems()
.map(i => {
const mesoText = i.quantity === 1 ? "meso" : `meso per ${i.quantity} arrows`;
return `\r\n#L${index++}##i${i.itemId}# #z${i.itemId}# #b${i.cost} ${mesoText}#k`;
})
.join("");
cm.sendSimple("An ally of the Raven Ninja Clan is welcome to buy from me!" + selections);
status++;
} else if (status === 1 && selection !== -1) {
selectedItem = shopItems()[selection];
cm.sendAcceptDecline("Are you sure you want to buy it?");
status++;
} else if (status === 2) {
if (!selectedItem) {
cm.dispose();
return;
}
if (!cm.hasMeso(selectedItem.cost)) {
cm.sendOk("You don't have enough mesos.");
cm.dispose();
return;
}
if (!cm.canHold(selectedItem.itemId, selectedItem.quantity)) {
cm.sendOk("There's no room in your inventory.");
cm.dispose();
return;
}
cm.loseMeso(selectedItem.cost);
cm.gainItem(selectedItem.itemId, selectedItem.quantity);
cm.dispose();
}
}
function shopItems() {
return [
{itemId: 2050004, quantity: 1, cost: 400}, // All-Cure Potion
{itemId: 2050000, quantity: 1, cost: 200}, // Antidote
{itemId: 2020012, quantity: 1, cost: 4500}, // Melting Cheese
{itemId: 2020013, quantity: 1, cost: 5000}, // Reindeer Milk
{itemId: 2020014, quantity: 1, cost: 8100}, // Sunrise Dew
{itemId: 2020015, quantity: 1, cost: 9690}, // Sunset Dew
{itemId: 2050001, quantity: 1, cost: 200}, // Eyedrop
{itemId: 2050002, quantity: 1, cost: 300}, // Tonic
{itemId: 2050003, quantity: 1, cost: 500}, // Holy Water
{itemId: 2022000, quantity: 1, cost: 1650}, // Pure Water
{itemId: 2002017, quantity: 1, cost: 5000}, // Warrior Elixir
{itemId: 2060004, quantity: 2000, cost: 40_000}, // Diamond Arrow for Bow
{itemId: 2061004, quantity: 2000, cost: 40_000}, // Diamond Arrow for Crossbow
{itemId: 2070010, quantity: 1, cost: 2000}, // Icicle
{itemId: 2022003, quantity: 1, cost: 1100}, // Unagi
{itemId: 2000006, quantity: 1, cost: 620}, // Mana Elixir
{itemId: 2022002, quantity: 1, cost: 1000}, // Cider
{itemId: 2030020, quantity: 1, cost: 400}, // Return to New Leaf City Scroll
]
}

View File

@@ -14,9 +14,9 @@ var staff_heading = "!";
var levels = ["Common", "Donator", "JrGM", "GM", "SuperGM", "Developer", "Admin"];
var commands;
// Expectation: "ce" bound to an instance of java.client.command.CommandsExecutor
function writeHeavenMSCommands() {
const CommandsExecutor = Java.type('client.command.CommandsExecutor');
commands = CommandsExecutor.getInstance().getGmCommands();
commands = ce.getGmCommands();
}
function start() {

View File

@@ -39,7 +39,7 @@ public abstract class AbstractCharacterObject extends AbstractAnimatedMapObject
protected MapleMap map;
protected int str, dex, luk, int_, hp, maxhp, mp, maxmp;
protected int hpMpApUsed, remainingAp;
protected int[] remainingSp = new int[10];
protected int[] remainingSp = new int[10]; // TODO: change to a simple int. Evan is not in v83, so why support it?
protected transient int clientmaxhp, clientmaxmp, localmaxhp = 50, localmaxmp = 5;
protected float transienthp = Float.NEGATIVE_INFINITY, transientmp = Float.NEGATIVE_INFINITY;

View File

@@ -21,6 +21,7 @@
*/
package client;
import model.CharacterIdentity;
import net.packet.Packet;
import net.server.PlayerStorage;
import tools.DatabaseConnection;
@@ -48,7 +49,7 @@ public class BuddyList {
private final Map<Integer, BuddylistEntry> buddies = new LinkedHashMap<>();
private int capacity;
private final Deque<CharacterNameAndId> pendingRequests = new LinkedList<>();
private final Deque<CharacterIdentity> pendingRequests = new LinkedList<>();
public BuddyList(int capacity) {
this.capacity = capacity;
@@ -150,7 +151,7 @@ public class BuddyList {
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
if (rs.getInt("pending") == 1) {
pendingRequests.push(new CharacterNameAndId(rs.getInt("buddyid"), rs.getString("buddyname")));
pendingRequests.push(new CharacterIdentity(rs.getString("buddyname"), rs.getInt("buddyid")));
} else {
put(new BuddylistEntry(rs.getString("buddyname"), rs.getString("group"), rs.getInt("buddyid"), (byte) -1, true));
}
@@ -167,7 +168,7 @@ public class BuddyList {
}
}
public CharacterNameAndId pollPendingRequest() {
public CharacterIdentity pollPendingRequest() {
return pendingRequests.pollLast();
}
@@ -176,7 +177,7 @@ public class BuddyList {
if (pendingRequests.isEmpty()) {
c.sendPacket(PacketCreator.requestBuddylistAdd(cidFrom, c.getPlayer().getId(), nameFrom));
} else {
pendingRequests.push(new CharacterNameAndId(cidFrom, nameFrom));
pendingRequests.push(new CharacterIdentity(nameFrom, cidFrom));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
package client;
import lombok.Builder;
@Builder
public record CharacterStats(
int account,
int world,
String name,
int id,
int level,
int fame,
int str,
int dex,
int luk,
int int_,
int exp,
int gachaExp,
int hp,
int mp,
int maxHp,
int maxMp,
int sp,
int ap,
int gmLevel,
int skin,
int gender,
int job,
int hair,
int face,
int mapId,
int meso,
int hpMpApUsed,
int spawnPortal,
Integer party,
int buddyCapacity,
Integer messenger,
Integer messengerPosition,
Integer mountLevel,
Integer mountExp,
Integer mountTiredness,
int equipSlots,
int useSlots,
int setupSlots,
int etcSlots,
Integer monsterBookCover,
Integer dojoVanquisherStage,
int dojoPoints,
Integer dojoStage,
boolean dojoTutorialComplete,
int dojoVanquisherKills,
int matchCardWins,
int matchCardLosses,
int matchCardTies,
int omokWins,
int omokLosses,
int omokTies,
String dataString,
Long jailExpiration,
Integer partnerId,
Integer marriageItemId,
Long lastExpGainTime,
int ariantPoints,
boolean canRecvPartySearchInvite
) {
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
package client;
/**
* @author Ponk
*/
public class Gender {
public static final byte MALE = 0;
public static final byte FEMALE = 1;
public static final byte NOT_SET = 10;
}

View File

@@ -0,0 +1,29 @@
package client;
import java.util.Arrays;
import java.util.Optional;
/**
* @author Ponk
*/
public enum LoginState {
LOGGED_OUT(0),
SERVER_TRANSITION(1),
LOGGED_IN(2);
private final byte value;
LoginState(int value) {
this.value = (byte) value;
}
public byte getValue() {
return value;
}
public static Optional<LoginState> fromValue(int value) {
return Arrays.stream(values())
.filter(v -> v.getValue() == value)
.findFirst();
}
}

View File

@@ -1,225 +1,110 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client;
import tools.DatabaseConnection;
import database.monsterbook.MonsterCard;
import net.jcip.annotations.ThreadSafe;
import tools.PacketCreator;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public final class MonsterBook {
private int specialCard = 0;
private int normalCard = 0;
private int bookLevel = 1;
private final Map<Integer, Integer> cards = new LinkedHashMap<>();
private final Lock lock = new ReentrantLock();
// TODO: add tests
@ThreadSafe
public class MonsterBook {
private final Map<Integer, MonsterCard> cards;
private int bookLevel;
public Set<Entry<Integer, Integer>> getCardSet() {
lock.lock();
try {
return new HashSet<>(cards.entrySet());
} finally {
lock.unlock();
}
public MonsterBook(List<MonsterCard> monsterCards) {
this.cards = monsterCards.stream()
.collect(Collectors.toMap(MonsterCard::cardId, Function.identity()));
}
public void addCard(final Client c, final int cardid) {
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), PacketCreator.showForeignCardEffect(c.getPlayer().getId()), false);
public synchronized List<MonsterCard> getCards() {
return new ArrayList<>(cards.values());
}
Integer qty;
lock.lock();
try {
qty = cards.get(cardid);
if (qty != null) {
if (qty < 5) {
cards.put(cardid, qty + 1);
}
} else {
cards.put(cardid, 1);
qty = 0;
if (cardid / 1000 >= 2388) {
specialCard++;
} else {
normalCard++;
}
}
} finally {
lock.unlock();
public synchronized void addCard(int cardId, Client client) {
var monsterCard = cards.get(cardId);
if (monsterCard != null && monsterCard.isMaxLevel()) {
client.sendPacket(PacketCreator.addMonsterCardAlreadyFull());
return;
}
if (qty < 5) {
if (qty == 0) { // leveling system only accounts unique cards
calculateLevel();
}
c.sendPacket(PacketCreator.addCard(false, cardid, qty + 1));
c.sendPacket(PacketCreator.showGainCard());
boolean isNewCard = monsterCard == null;
final MonsterCard card;
if (isNewCard) {
card = new MonsterCard(cardId, (byte) 1);
cards.put(cardId, card);
calculateAndSetLevel();
} else {
c.sendPacket(PacketCreator.addCard(true, cardid, 5));
}
}
private void calculateLevel() {
lock.lock();
try {
int collectionExp = (normalCard + specialCard);
int level = 0, expToNextlevel = 1;
do {
level++;
expToNextlevel += level * 10;
} while (collectionExp >= expToNextlevel);
bookLevel = level; // thanks IxianMace for noticing book level differing between book UI and character info UI
} finally {
lock.unlock();
}
}
public int getBookLevel() {
lock.lock();
try {
return bookLevel;
} finally {
lock.unlock();
}
}
public Map<Integer, Integer> getCards() {
lock.lock();
try {
return Collections.unmodifiableMap(cards);
} finally {
lock.unlock();
}
}
public int getTotalCards() {
lock.lock();
try {
return specialCard + normalCard;
} finally {
lock.unlock();
}
}
public int getNormalCard() {
lock.lock();
try {
return normalCard;
} finally {
lock.unlock();
}
}
public int getSpecialCard() {
lock.lock();
try {
return specialCard;
} finally {
lock.unlock();
}
}
public void loadCards(final int charid) throws SQLException {
lock.lock();
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT cardid, level FROM monsterbook WHERE charid = ? ORDER BY cardid ASC")) {
ps.setInt(1, charid);
try (ResultSet rs = ps.executeQuery()) {
int cardid;
int level;
while (rs.next()) {
cardid = rs.getInt("cardid");
level = rs.getInt("level");
if (cardid / 1000 >= 2388) {
specialCard++;
} else {
normalCard++;
}
cards.put(cardid, level);
}
}
} finally {
lock.unlock();
card = new MonsterCard(cardId, (byte) (monsterCard.level() + 1));
cards.put(cardId, card);
}
calculateLevel();
var chr = client.getPlayer();
chr.sendPacket(PacketCreator.addMonsterCard(card));
chr.sendPacket(PacketCreator.showMonsterCardEffect());
chr.getMap().broadcastMessage(chr, PacketCreator.showForeignMonsterCardEffect(chr.getId()), false);
}
public void saveCards(Connection con, int chrId) throws SQLException {
private synchronized void calculateAndSetLevel() {
int collectionExp = getTotalCards();
int level = 0;
int expToNextLevel = 1;
do {
level++;
expToNextLevel += level * 10;
} while (collectionExp >= expToNextLevel);
this.bookLevel = level;
}
public synchronized int getBookLevel() {
return bookLevel;
}
public synchronized int getNormalCards() {
return (int) cards.values().stream()
.filter(Predicate.not(MonsterCard::isSpecial))
.count();
}
public synchronized int getSpecialCards() {
return (int) cards.values().stream()
.filter(MonsterCard::isSpecial)
.count();
}
public synchronized int getTotalCards() {
return cards.size();
}
public synchronized void saveCards(Connection con, int chrId) throws SQLException {
final String query = """
INSERT INTO monsterbook (charid, cardid, level)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE level = ?;
""";
try (final PreparedStatement ps = con.prepareStatement(query)) {
for (Map.Entry<Integer, Integer> cardAndLevel : cards.entrySet()) {
final int card = cardAndLevel.getKey();
final int level = cardAndLevel.getValue();
for (MonsterCard card : cards.values()) {
// insert
ps.setInt(1, chrId);
ps.setInt(2, card);
ps.setInt(3, level);
ps.setInt(2, card.cardId());
ps.setInt(3, card.level());
// update
ps.setInt(4, level);
ps.setInt(4, card.level());
ps.addBatch();
}
ps.executeBatch();
}
}
public static int[] getCardTierSize() {
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM monstercarddata GROUP BY floor(cardid / 1000);", ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = ps.executeQuery()) {
rs.last();
int[] tierSizes = new int[rs.getRow()];
rs.beforeFirst();
while (rs.next()) {
tierSizes[rs.getRow() - 1] = rs.getInt(1);
}
return tierSizes;
} catch (SQLException e) {
e.printStackTrace();
return new int[0];
}
}
}

View File

@@ -88,10 +88,6 @@ public enum AutobanFactory {
return expiretime;
}
public void addPoint(AutobanManager ban, String reason) {
ban.addPoint(this, reason);
}
public void alert(Character chr, String reason) {
if (YamlConfig.config.server.USE_AUTOBAN) {
if (chr != null && isIgnored(chr.getId())) {
@@ -105,13 +101,6 @@ public enum AutobanFactory {
}
}
public void autoban(Character chr, String value) {
if (YamlConfig.config.server.USE_AUTOBAN) {
chr.autoban("Autobanned for (" + this.name() + ": " + value + ")");
//chr.sendPolice("You will be disconnected for (" + this.name() + ": " + value + ")");
}
}
/**
* Toggle ignored status for a character id.
* An ignored character will not trigger GM alerts.

View File

@@ -7,6 +7,7 @@ package client.autoban;
import client.Character;
import config.YamlConfig;
import net.netty.GameViolationException;
import net.server.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,35 +36,27 @@ public class AutobanManager {
this.chr = chr;
}
public void addPoint(AutobanFactory fac, String reason) {
if (YamlConfig.config.server.USE_AUTOBAN) {
if (chr.isGM() || chr.isBanned()) {
return;
}
if (lastTime.containsKey(fac)) {
if (lastTime.get(fac) < (Server.getInstance().getCurrentTime() - fac.getExpire())) {
points.put(fac, points.get(fac) / 2); //So the points are not completely gone.
}
}
if (fac.getExpire() != -1) {
lastTime.put(fac, Server.getInstance().getCurrentTime());
}
if (points.containsKey(fac)) {
points.put(fac, points.get(fac) + 1);
} else {
points.put(fac, 1);
}
if (points.get(fac) >= fac.getMaximum()) {
chr.autoban(reason);
/**
* @return true if the added point should result in an autoban
*/
public boolean addPoint(AutobanFactory fac) {
if (lastTime.containsKey(fac)) {
if (lastTime.get(fac) < (Server.getInstance().getCurrentTime() - fac.getExpire())) {
points.put(fac, points.get(fac) / 2); //So the points are not completely gone.
}
}
if (YamlConfig.config.server.USE_AUTOBAN_LOG) {
// Lets log every single point too.
log.info("Autoban - chr {} caused {} {}", Character.makeMapleReadable(chr.getName()), fac.name(), reason);
if (fac.getExpire() != -1) {
lastTime.put(fac, Server.getInstance().getCurrentTime());
}
if (points.containsKey(fac)) {
points.put(fac, points.get(fac) + 1);
} else {
points.put(fac, 1);
}
return points.get(fac) >= fac.getMaximum();
}
public void addMiss() {
@@ -118,11 +111,10 @@ public class AutobanManager {
if (this.timestamp[type] == time) {
this.timestampcounter[type]++;
if (this.timestampcounter[type] >= times) {
if (YamlConfig.config.server.USE_AUTOBAN) {
chr.getClient().disconnect(false, false);
}
log.info("Autoban - Chr {} was caught spamming TYPE {} and has been disconnected", chr, type);
if (YamlConfig.config.server.USE_AUTOBAN) {
throw new GameViolationException("Auto ban");
}
}
} else {
this.timestamp[type] = time;

View File

@@ -30,7 +30,7 @@ public abstract class Command {
protected int rank;
protected String description;
public abstract void execute(Client client, String[] params);
public abstract void execute(Client client, String[] params, CommandContext ctx);
public String getDescription() {
return description;

View File

@@ -0,0 +1,19 @@
package client.command;
import database.character.CharacterSaver;
import database.drop.DropProvider;
import server.shop.ShopFactory;
import service.BanService;
import service.TransitionService;
/**
* @author Ponk
*/
public record CommandContext(CommandsExecutor commandsExecutor, DropProvider dropProvider, ShopFactory shopFactory,
CharacterSaver characterSaver, TransitionService transitionService, BanService banService
) {
public CommandContext with(CommandsExecutor ce) {
return new CommandContext(ce, dropProvider, shopFactory, characterSaver, transitionService, banService);
}
}

View File

@@ -24,7 +24,6 @@
package client.command;
import client.Client;
import client.command.commands.gm0.ChangeLanguageCommand;
import client.command.commands.gm0.DisposeCommand;
import client.command.commands.gm0.DropLimitCommand;
import client.command.commands.gm0.EnableAuthCommand;
@@ -38,7 +37,6 @@ import client.command.commands.gm0.MapOwnerClaimCommand;
import client.command.commands.gm0.OnlineCommand;
import client.command.commands.gm0.RanksCommand;
import client.command.commands.gm0.RatesCommand;
import client.command.commands.gm0.ReadPointsCommand;
import client.command.commands.gm0.ReportBugCommand;
import client.command.commands.gm0.ShowRatesCommand;
import client.command.commands.gm0.StaffCommand;
@@ -106,8 +104,6 @@ import client.command.commands.gm3.FameCommand;
import client.command.commands.gm3.FlyCommand;
import client.command.commands.gm3.GiveMesosCommand;
import client.command.commands.gm3.GiveNxCommand;
import client.command.commands.gm3.GiveRpCommand;
import client.command.commands.gm3.GiveVpCommand;
import client.command.commands.gm3.HairCommand;
import client.command.commands.gm3.HealMapCommand;
import client.command.commands.gm3.HealPersonCommand;
@@ -189,15 +185,10 @@ import client.command.commands.gm6.EraseAllPNpcsCommand;
import client.command.commands.gm6.GetAccCommand;
import client.command.commands.gm6.MapPlayersCommand;
import client.command.commands.gm6.SaveAllCommand;
import client.command.commands.gm6.ServerAddChannelCommand;
import client.command.commands.gm6.ServerAddWorldCommand;
import client.command.commands.gm6.ServerRemoveChannelCommand;
import client.command.commands.gm6.ServerRemoveWorldCommand;
import client.command.commands.gm6.SetGmLevelCommand;
import client.command.commands.gm6.ShutdownCommand;
import client.command.commands.gm6.SpawnAllPNpcsCommand;
import client.command.commands.gm6.SupplyRateCouponCommand;
import client.command.commands.gm6.WarpWorldCommand;
import constants.id.MapId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -210,16 +201,27 @@ import java.util.List;
public class CommandsExecutor {
private static final Logger log = LoggerFactory.getLogger(CommandsExecutor.class);
private static final CommandsExecutor instance = new CommandsExecutor();
private static final char USER_HEADING = '@';
private static final char GM_HEADING = '!';
private final HashMap<String, Command> registeredCommands = new HashMap<>();
private final List<Pair<List<String>, List<String>>> commandsNameDesc = new ArrayList<>();
private final CommandContext commandContext;
private Pair<List<String>, List<String>> levelCommandsCursor;
public static CommandsExecutor getInstance() {
return instance;
public CommandsExecutor(CommandContext commandContext) {
this.commandContext = commandContext.with(this);
registerCommands();
}
private void registerCommands() {
registerLv0Commands();
registerLv1Commands();
registerLv2Commands();
registerLv3Commands();
registerLv4Commands();
registerLv5Commands();
registerLv6Commands();
}
public static boolean isCommand(Client client, String content) {
@@ -230,16 +232,6 @@ public class CommandsExecutor {
return heading == USER_HEADING;
}
private CommandsExecutor() {
registerLv0Commands();
registerLv1Commands();
registerLv2Commands();
registerLv3Commands();
registerLv4Commands();
registerLv5Commands();
registerLv6Commands();
}
public List<Pair<List<String>, List<String>>> getGmCommands() {
return commandsNameDesc;
}
@@ -287,7 +279,7 @@ public class CommandsExecutor {
params = new String[]{};
}
command.execute(client, params);
command.execute(client, params, commandContext);
log.info("Chr {} used command {}", client.getPlayer().getName(), command.getClass().getSimpleName());
}
@@ -347,14 +339,12 @@ public class CommandsExecutor {
addCommand("uptime", UptimeCommand.class);
addCommand("gacha", GachaCommand.class);
addCommand("dispose", DisposeCommand.class);
addCommand("changel", ChangeLanguageCommand.class);
addCommand("equiplv", EquipLvCommand.class);
addCommand("showrates", ShowRatesCommand.class);
addCommand("rates", RatesCommand.class);
addCommand("online", OnlineCommand.class);
addCommand("gm", GmCommand.class);
addCommand("reportbug", ReportBugCommand.class);
addCommand("points", ReadPointsCommand.class);
addCommand("joinevent", JoinEventCommand.class);
addCommand("leaveevent", LeaveEventCommand.class);
addCommand("ranks", RanksCommand.class);
@@ -454,9 +444,7 @@ public class CommandsExecutor {
addCommand("togglewhitechat", 3, ChatCommand.class);
addCommand("fame", 3, FameCommand.class);
addCommand("givenx", 3, GiveNxCommand.class);
addCommand("givevp", 3, GiveVpCommand.class);
addCommand("givems", 3, GiveMesosCommand.class);
addCommand("giverp", 3, GiveRpCommand.class);
addCommand("expeds", 3, ExpedsCommand.class);
addCommand("kill", 3, KillCommand.class);
addCommand("seed", 3, SeedCommand.class);
@@ -542,7 +530,6 @@ public class CommandsExecutor {
levelCommandsCursor = new Pair<>(new ArrayList<String>(), new ArrayList<String>());
addCommand("setgmlevel", 6, SetGmLevelCommand.class);
addCommand("warpworld", 6, WarpWorldCommand.class);
addCommand("saveall", 6, SaveAllCommand.class);
addCommand("dcall", 6, DCAllCommand.class);
addCommand("mapplayers", 6, MapPlayersCommand.class);
@@ -553,10 +540,6 @@ public class CommandsExecutor {
addCommand("supplyratecoupon", 6, SupplyRateCouponCommand.class);
addCommand("spawnallpnpcs", 6, SpawnAllPNpcsCommand.class);
addCommand("eraseallpnpcs", 6, EraseAllPNpcsCommand.class);
addCommand("addchannel", 6, ServerAddChannelCommand.class);
addCommand("addworld", 6, ServerAddWorldCommand.class);
addCommand("removechannel", 6, ServerRemoveChannelCommand.class);
addCommand("removeworld", 6, ServerRemoveWorldCommand.class);
addCommand("devtest", 6, DevtestCommand.class);
commandsNameDesc.add(levelCommandsCursor);

View File

@@ -1,42 +0,0 @@
/*
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
@Author: Arthur L - Refactored command content into modules
*/
package client.command.commands.gm0;
import client.Client;
import client.command.Command;
public class ChangeLanguageCommand extends Command {
{
setDescription("Change language settings.");
}
@Override
public void execute(Client c, String[] params) {
if (params.length < 1) {
c.getPlayer().yellowMessage("Syntax: !changel <0=ptb, 1=esp, 2=eng>");
return;
}
c.setLanguage(Integer.parseInt(params[0]));
}
}

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import scripting.npc.NPCScriptManager;
import scripting.quest.QuestScriptManager;
import tools.PacketCreator;
@@ -35,7 +36,7 @@ public class DisposeCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
NPCScriptManager.getInstance().dispose(c);
QuestScriptManager.getInstance().dispose(c);
c.sendPacket(PacketCreator.enableActions());

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class DropLimitCommand extends Command {
@@ -33,7 +34,7 @@ public class DropLimitCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
int dropCount = c.getPlayer().getMap().getDroppedItemCount();
if (((float) dropCount) / YamlConfig.config.server.ITEM_LIMIT_ON_MAP < 0.75f) {
c.getPlayer().showHint("Current drop count: #b" + dropCount + "#k / #e" + YamlConfig.config.server.ITEM_LIMIT_ON_MAP + "#n", 300);

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import net.server.coordinator.login.LoginBypassCoordinator;
public class EnableAuthCommand extends Command {
@@ -33,7 +34,7 @@ public class EnableAuthCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
if (c.tryacquireClient()) {
try {
LoginBypassCoordinator.getInstance().unregisterLoginBypassEntry(c.getHwid(), c.getAccID());

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class EquipLvCommand extends Command {
{
@@ -32,7 +33,7 @@ public class EquipLvCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
c.getPlayer().showAllEquipFeatures();
}
}

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.NpcId;
import server.ItemInformationProvider;
import server.gachapon.Gachapon;
@@ -35,7 +36,7 @@ public class GachaCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Gachapon.GachaponType gacha = null;
String search = c.getPlayer().getLastCommandMessage();
String gachaName = "";

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import net.server.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +41,7 @@ public class GmCommand extends Command {
private static final Logger log = LoggerFactory.getLogger(GmCommand.class);
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
String[] tips = {
"Please only use @gm in emergencies or to report somebody.",
"To report a bug or make a suggestion, use the forum.",

View File

@@ -25,15 +25,19 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.NpcId;
import java.util.Map;
public class HelpCommand extends Command {
{
setDescription("Show available commands.");
}
@Override
public void execute(Client client, String[] params) {
client.getAbstractPlayerInteraction().openNpc(NpcId.STEWARD, "commands");
public void execute(Client client, String[] params, CommandContext ctx) {
Map<String, Object> bindings = Map.of("ce", ctx.commandsExecutor());
client.getAbstractPlayerInteraction().openNpc(NpcId.STEWARD, "commands", bindings);
}
}

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.MapId;
import server.events.gm.Event;
import server.maps.FieldLimit;
@@ -36,7 +37,7 @@ public class JoinEventCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (!FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) {
Event event = c.getChannelServer().getEvent();

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class LeaveEventCommand extends Command {
{
@@ -33,7 +34,7 @@ public class LeaveEventCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
int returnMap = player.getSavedLocation("EVENT");
if (returnMap != -1) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
import server.maps.MapleMap;
@@ -35,7 +36,7 @@ public class MapOwnerClaimCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
if (c.tryacquireClient()) {
try {
Character chr = c.getPlayer();

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import net.server.Server;
import net.server.channel.Channel;
@@ -35,7 +36,7 @@ public class OnlineCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
for (Channel ch : Server.getInstance().getChannelsFromWorld(player.getWorld())) {
player.yellowMessage("Players in Channel " + ch.getId() + ":");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.NpcId;
import net.server.Server;
import net.server.guild.GuildPackets;
@@ -39,7 +40,7 @@ public class RanksCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
List<Pair<String, Integer>> worldRanking = Server.getInstance().getWorldPlayerRanking(player.getWorld());

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class RatesCommand extends Command {
@@ -34,7 +35,7 @@ public class RatesCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
// travel rates not applicable since it's intrinsically a server/environment rate rather than a character rate

View File

@@ -1,38 +0,0 @@
package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
public class ReadPointsCommand extends Command {
{
setDescription("Show point total.");
}
@Override
public void execute(Client client, String[] params) {
Character player = client.getPlayer();
if (params.length > 2) {
player.yellowMessage("Syntax: @points (rp|vp|all)");
return;
} else if (params.length == 0) {
player.yellowMessage("RewardPoints: " + player.getRewardPoints() + " | "
+ "VotePoints: " + player.getClient().getVotePoints());
return;
}
switch (params[0]) {
case "rp":
player.yellowMessage("RewardPoints: " + player.getRewardPoints());
break;
case "vp":
player.yellowMessage("VotePoints: " + player.getClient().getVotePoints());
break;
default:
player.yellowMessage("RewardPoints: " + player.getRewardPoints() + " | "
+ "VotePoints: " + player.getClient().getVotePoints());
break;
}
}
}

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import net.server.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,7 +40,7 @@ public class ReportBugCommand extends Command {
private static final Logger log = LoggerFactory.getLogger(ReportBugCommand.class);
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class ShowRatesCommand extends Command {
@@ -34,7 +35,7 @@ public class ShowRatesCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
String showMsg = "#eEXP RATE#n" + "\r\n";
showMsg += "World EXP Rate: #k" + c.getWorldServer().getExpRate() + "x#k" + "\r\n";

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.NpcId;
public class StaffCommand extends Command {
@@ -33,7 +34,7 @@ public class StaffCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
c.getAbstractPlayerInteraction().openNpc(NpcId.HERACLE, "credits");
}
}

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class StatDexCommand extends Command {
@@ -34,7 +35,7 @@ public class StatDexCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
int remainingAp = player.getRemainingAp();

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class StatIntCommand extends Command {
@@ -34,7 +35,7 @@ public class StatIntCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
int remainingAp = player.getRemainingAp();

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class StatLukCommand extends Command {
@@ -34,7 +35,7 @@ public class StatLukCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
int remainingAp = player.getRemainingAp();

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class StatStrCommand extends Command {
@@ -34,7 +35,7 @@ public class StatStrCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
int remainingAp = player.getRemainingAp();
int amount;

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@@ -37,7 +38,7 @@ public class TimeCommand extends Command {
}
@Override
public void execute(Client client, String[] params) {
public void execute(Client client, String[] params, CommandContext ctx) {
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getDefault());
client.getPlayer().yellowMessage("Cosmic Server Time: " + dateFormat.format(new Date()));

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class ToggleExpCommand extends Command {
{
@@ -32,7 +33,7 @@ public class ToggleExpCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
if (c.tryacquireClient()) {
try {
c.getPlayer().toggleExpGain(); // Vcoc's idea

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm0;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import net.server.Server;
import static java.util.concurrent.TimeUnit.DAYS;
@@ -38,7 +39,7 @@ public class UptimeCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
long milliseconds = System.currentTimeMillis() - Server.uptime;
int seconds = (int) (milliseconds / SECONDS.toMillis(1)) % 60;
int minutes = (int) ((milliseconds / MINUTES.toMillis(1)) % 60);

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm1;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.life.Monster;
public class BossHpCommand extends Command {
@@ -34,7 +35,7 @@ public class BossHpCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
for (Monster monster : player.getMap().getAllMonsters()) {
if (monster != null && monster.isBoss() && monster.getHp() > 0) {

View File

@@ -27,6 +27,7 @@ import client.Character;
import client.Client;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
public class BuffMeCommand extends Command {
{
@@ -34,7 +35,7 @@ public class BuffMeCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
SkillFactory.getSkill(4101004).getEffect(SkillFactory.getSkill(4101004).getMaxLevel()).applyTo(player);
SkillFactory.getSkill(2311003).getEffect(SkillFactory.getSkill(2311003).getMaxLevel()).applyTo(player);

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm1;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.game.GameConstants;
import constants.id.NpcId;
import server.maps.FieldLimit;
@@ -77,7 +78,7 @@ public class GotoCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
String sendStr = "Syntax: #b@goto <map name>#k. Available areas:\r\n\r\n#rTowns:#k\r\n" + GOTO_TOWNS_INFO;

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm1;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.life.Monster;
public class MobHpCommand extends Command {
@@ -34,7 +35,7 @@ public class MobHpCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
for (Monster monster : player.getMap().getAllMonsters()) {
if (monster != null && monster.getHp() > 0) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm1;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.NpcId;
import server.ItemInformationProvider;
import server.life.MonsterDropEntry;
@@ -40,7 +41,7 @@ public class WhatDropsFromCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.dropMessage(5, "Please do @whatdropsfrom <monster name>");
@@ -56,7 +57,7 @@ public class WhatDropsFromCommand extends Command {
int mobId = data.getLeft();
String mobName = data.getRight();
output += mobName + " drops the following items:\r\n\r\n";
for (MonsterDropEntry drop : MonsterInformationProvider.getInstance().retrieveDrop(mobId)) {
for (MonsterDropEntry drop : ctx.dropProvider().getMonsterDropEntries(mobId)) {
try {
String name = ItemInformationProvider.getInstance().getName(drop.itemId);
if (name == null || name.equals("null") || drop.chance == 0) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm1;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.NpcId;
import server.ItemInformationProvider;
import server.life.MonsterInformationProvider;
@@ -43,7 +44,7 @@ public class WhoDropsCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.dropMessage(5, "Please do @whodrops <item name>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class ApCommand extends Command {
@@ -34,7 +35,7 @@ public class ApCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !ap [<playername>] <newap>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.MobId;
import net.server.Server;
import server.life.LifeFactory;
@@ -37,7 +38,7 @@ public class BombCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length > 0) {
Character victim = c.getWorldServer().getPlayerStorage().getCharacterByName(params[0]);

View File

@@ -28,6 +28,7 @@ import client.Client;
import client.Skill;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
public class BuffCommand extends Command {
{
@@ -35,7 +36,7 @@ public class BuffCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !buff <buffid>");

View File

@@ -27,6 +27,7 @@ import client.Character;
import client.Client;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
public class BuffMapCommand extends Command {
{
@@ -34,7 +35,7 @@ public class BuffMapCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
SkillFactory.getSkill(9101001).getEffect(SkillFactory.getSkill(9101001).getMaxLevel()).applyTo(player, true);
SkillFactory.getSkill(9101002).getEffect(SkillFactory.getSkill(9101002).getMaxLevel()).applyTo(player, true);

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class ClearDropsCommand extends Command {
{
@@ -33,7 +34,7 @@ public class ClearDropsCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
player.getMap().clearDrops(player);
player.dropMessage(5, "Cleared dropped items");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.maps.SavedLocationType;
public class ClearSavedLocationsCommand extends Command {
@@ -34,7 +35,7 @@ public class ClearSavedLocationsCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer(), victim;
if (params.length > 0) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.manipulator.InventoryManipulator;
@@ -36,7 +37,7 @@ public class ClearSlotCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !clearslot <all, equip, use, setup, etc or cash.>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class DcCommand extends Command {
{
@@ -33,21 +34,22 @@ public class DcCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !dc <playername>");
return;
}
Character victim = c.getWorldServer().getPlayerStorage().getCharacterByName(params[0]);
String chrName = params[0];
Character victim = c.getWorldServer().getPlayerStorage().getCharacterByName(chrName);
if (victim == null) {
victim = c.getChannelServer().getPlayerStorage().getCharacterByName(params[0]);
victim = c.getChannelServer().getPlayerStorage().getCharacterByName(chrName);
if (victim == null) {
victim = player.getMap().getCharacterByName(params[0]);
victim = player.getMap().getCharacterByName(chrName);
if (victim != null) {
try {//sometimes bugged because the map = null
victim.getClient().disconnect(true, false);
ctx.transitionService().disconnect(victim.getClient(), true);
player.getMap().removePlayer(victim);
} catch (Exception e) {
e.printStackTrace();
@@ -60,6 +62,6 @@ public class DcCommand extends Command {
if (player.gmLevel() < victim.gmLevel()) {
victim = player;
}
victim.getClient().disconnect(false, false);
ctx.transitionService().disconnect(victim.getClient(), false);
}
}

View File

@@ -27,6 +27,7 @@ import client.Character;
import client.Client;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
public class EmpowerMeCommand extends Command {
{
@@ -34,7 +35,7 @@ public class EmpowerMeCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
final int[] array = {2311003, 2301004, 1301007, 4101004, 2001002, 1101007, 1005, 2301003, 5121009, 1111002, 4111001, 4111002, 4211003, 4211005, 1321000, 2321004, 3121002};
for (int i : array) {

View File

@@ -21,6 +21,7 @@ package client.command.commands.gm2;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
/**
* @author Ronan
@@ -31,7 +32,7 @@ public class GachaListCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
c.getAbstractPlayerInteraction().openNpc(10000, "gachaponInfo");
}
}

View File

@@ -25,15 +25,17 @@ package client.command.commands.gm2;
import client.Client;
import client.command.Command;
import server.ShopFactory;
import client.command.CommandContext;
public class GmShopCommand extends Command {
{
setDescription("Open the GM shop.");
}
private static final int GM_SHOP_ID = 1337;
@Override
public void execute(Client c, String[] params) {
ShopFactory.getInstance().getShop(1337).sendShop(c);
public void execute(Client c, String[] params, CommandContext ctx) {
ctx.shopFactory().getShop(GM_SHOP_ID).ifPresent(shop -> shop.sendShop(c));
}
}

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class HealCommand extends Command {
{
@@ -33,7 +34,7 @@ public class HealCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
player.healHpMp();
}

View File

@@ -27,6 +27,7 @@ import client.Character;
import client.Client;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
public class HideCommand extends Command {
{
@@ -34,7 +35,7 @@ public class HideCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
SkillFactory.getSkill(9101004).getEffect(SkillFactory.getSkill(9101004).getMaxLevel()).applyTo(player);

View File

@@ -3,6 +3,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.game.NpcChat;
import constants.id.NpcId;
import server.ThreadManager;
@@ -86,7 +87,7 @@ public class IdCommand extends Command {
}
@Override
public void execute(Client client, final String[] params) {
public void execute(Client client, final String[] params, CommandContext ctx) {
final Character chr = client.getPlayer();
if (params.length < 2) {
chr.yellowMessage("Syntax: !id <type> <query>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import client.inventory.Pet;
import client.inventory.manipulator.InventoryManipulator;
import config.YamlConfig;
@@ -40,7 +41,7 @@ public class ItemCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import client.inventory.InventoryType;
import client.inventory.Item;
import client.inventory.Pet;
@@ -41,7 +42,7 @@ public class ItemDropCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.MapId;
import server.maps.MapleMap;
import server.maps.Portal;
@@ -38,7 +39,7 @@ public class JailCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !jail <playername> [<minutes>]");

View File

@@ -27,6 +27,7 @@ import client.Character;
import client.Client;
import client.Job;
import client.command.Command;
import client.command.CommandContext;
public class JobCommand extends Command {
{
@@ -34,7 +35,7 @@ public class JobCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length == 1) {
int jobid = Integer.parseInt(params[0]);

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class LevelCommand extends Command {
@@ -34,7 +35,7 @@ public class LevelCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !level <newlevel>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class LevelProCommand extends Command {
{
@@ -33,7 +34,7 @@ public class LevelProCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !levelpro <newlevel>");

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm2;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.maps.MapItem;
import server.maps.MapObject;
import server.maps.MapObjectType;
@@ -39,7 +40,7 @@ public class LootCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
List<MapObject> items = c.getPlayer().getMap().getMapObjectsInRange(c.getPlayer().getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapObjectType.ITEM));
for (MapObject item : items) {
MapItem mapItem = (MapItem) item;

View File

@@ -29,6 +29,7 @@ import client.Job;
import client.Skill;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
import provider.Data;
import provider.DataProviderFactory;
import provider.wz.WZFiles;
@@ -39,7 +40,7 @@ public class MaxSkillCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
for (Data skill_ : DataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
try {

View File

@@ -27,6 +27,7 @@ import client.Character;
import client.Client;
import client.Stat;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class MaxStatCommand extends Command {
@@ -35,7 +36,7 @@ public class MaxStatCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
player.loseExp(player.getExp(), false, false);
player.setLevel(255);

View File

@@ -3,6 +3,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.life.MobSkillType;
@@ -16,7 +17,7 @@ public class MobSkillCommand extends Command {
}
@Override
public void execute(Client client, String[] params) {
public void execute(Client client, String[] params, CommandContext ctx) {
if (params.length < 2) {
throw new IllegalArgumentException("Mob skill command requires two args: mob skill id and level");
}

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.maps.MapleMap;
public class ReachCommand extends Command {
@@ -34,7 +35,7 @@ public class ReachCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !reach <playername>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import client.inventory.InventoryType;
import client.inventory.Item;
import constants.inventory.ItemConstants;
@@ -37,7 +38,7 @@ public class RechargeCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
ItemInformationProvider ii = ItemInformationProvider.getInstance();
for (Item torecharge : c.getPlayer().getInventory(InventoryType.USE).list()) {

View File

@@ -29,6 +29,7 @@ import client.Job;
import client.Skill;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
import provider.Data;
import provider.DataProviderFactory;
import provider.wz.WZFiles;
@@ -39,7 +40,7 @@ public class ResetSkillCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
for (Data skill_ : DataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
try {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import constants.id.NpcId;
import provider.Data;
import provider.DataProvider;
@@ -53,7 +54,7 @@ public class SearchCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 2) {
player.yellowMessage("Syntax: !search <type> <name>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class SetSlotCommand extends Command {
{
@@ -33,7 +34,7 @@ public class SetSlotCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !setslot <newlevel>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class SetStatCommand extends Command {
{
@@ -33,7 +34,7 @@ public class SetStatCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !setstat <newstat>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import config.YamlConfig;
public class SpCommand extends Command {
@@ -34,7 +35,7 @@ public class SpCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !sp [<playername>] <newsp>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import net.server.Server;
import net.server.channel.Channel;
import server.maps.MapleMap;
@@ -36,7 +37,7 @@ public class SummonCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !warphere <playername>");
@@ -62,7 +63,7 @@ public class SummonCommand extends Command {
if (player.getClient().getChannel() != victim.getClient().getChannel()) {//And then change channel if needed.
victim.dropMessage("Changing channel, please wait a moment.");
victim.getClient().changeChannel(player.getClient().getChannel());
ctx.transitionService().changeChannel(victim.getClient(), player.getClient().getChannel());
}
try {

View File

@@ -25,6 +25,7 @@ package client.command.commands.gm2;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import tools.PacketCreator;
public class UnBugCommand extends Command {
@@ -33,7 +34,7 @@ public class UnBugCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
c.getPlayer().getMap().broadcastMessage(PacketCreator.enableActions());
}
}

View File

@@ -27,6 +27,7 @@ import client.Character;
import client.Client;
import client.SkillFactory;
import client.command.Command;
import client.command.CommandContext;
public class UnHideCommand extends Command {
{
@@ -34,7 +35,7 @@ public class UnHideCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
SkillFactory.getSkill(9101004).getEffect(SkillFactory.getSkill(9101004).getMaxLevel()).applyTo(player);

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class UnJailCommand extends Command {
{
@@ -33,7 +34,7 @@ public class UnJailCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !unjail <playername>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.maps.MapleMap;
import java.awt.*;
@@ -37,7 +38,7 @@ public class WarpAreaCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !warparea <mapid>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.maps.FieldLimit;
import server.maps.MapleMap;
import server.maps.MiniDungeonInfo;
@@ -36,7 +37,7 @@ public class WarpCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !warp <mapid>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.maps.MapleMap;
import java.util.Collection;
@@ -36,7 +37,7 @@ public class WarpMapCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !warpmap <mapid>");

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm2;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
import server.life.Monster;
import server.life.NPC;
import server.life.PlayerNPC;
@@ -39,7 +40,7 @@ public class WhereaMiCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
HashSet<Character> chars = new HashSet<>();

View File

@@ -26,14 +26,7 @@ package client.command.commands.gm3;
import client.Character;
import client.Client;
import client.command.Command;
import net.server.Server;
import server.TimerManager;
import tools.DatabaseConnection;
import tools.PacketCreator;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import client.command.CommandContext;
public class BanCommand extends Command {
{
@@ -41,7 +34,7 @@ public class BanCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 2) {
player.yellowMessage("Syntax: !ban <IGN> <Reason> (Please be descriptive)");
@@ -49,39 +42,6 @@ public class BanCommand extends Command {
}
String ign = params[0];
String reason = joinStringFrom(params, 1);
Character target = c.getChannelServer().getPlayerStorage().getCharacterByName(ign);
if (target != null) {
String readableTargetName = Character.makeMapleReadable(target.getName());
String ip = target.getClient().getRemoteAddress();
//Ban ip
try (Connection con = DatabaseConnection.getConnection()) {
if (ip.matches("/[0-9]{1,3}\\..*")) {
try (PreparedStatement ps = con.prepareStatement("INSERT INTO ipbans VALUES (DEFAULT, ?, ?)")) {
ps.setString(1, ip);
ps.setString(2, String.valueOf(target.getClient().getAccID()));
ps.executeUpdate();
}
}
} catch (SQLException ex) {
ex.printStackTrace();
c.getPlayer().message("Error occured while banning IP address");
c.getPlayer().message(target.getName() + "'s IP was not banned: " + ip);
}
target.getClient().banMacs();
reason = c.getPlayer().getName() + " banned " + readableTargetName + " for " + reason + " (IP: " + ip + ") " + "(MAC: " + c.getMacs() + ")";
target.ban(reason);
target.yellowMessage("You have been banned by #b" + c.getPlayer().getName() + " #k.");
target.yellowMessage("Reason: " + reason);
c.sendPacket(PacketCreator.getGMEffect(4, (byte) 0));
final Character rip = target;
TimerManager.getInstance().schedule(() -> rip.getClient().disconnect(false, false), 5000); //5 Seconds
Server.getInstance().broadcastMessage(c.getWorld(), PacketCreator.serverNotice(6, "[RIP]: " + ign + " has been banned."));
} else if (Character.ban(ign, reason, false)) {
c.sendPacket(PacketCreator.getGMEffect(4, (byte) 0));
Server.getInstance().broadcastMessage(c.getWorld(), PacketCreator.serverNotice(6, "[RIP]: " + ign + " has been banned."));
} else {
c.sendPacket(PacketCreator.getGMEffect(6, (byte) 1));
}
ctx.banService().permaBan(c, ign, (byte) 0, reason);
}
}

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm3;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class ChatCommand extends Command {
{
@@ -33,7 +34,7 @@ public class ChatCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
player.toggleWhiteChat();
player.message("Your chat is now " + (player.getWhiteChat() ? " white" : "normal") + ".");

View File

@@ -27,6 +27,7 @@ import client.BuffStat;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class CheckDmgCommand extends Command {
{
@@ -34,7 +35,7 @@ public class CheckDmgCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
Character victim = c.getWorldServer().getPlayerStorage().getCharacterByName(params[0]);
if (victim != null) {

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm3;
import client.Character;
import client.Client;
import client.command.Command;
import client.command.CommandContext;
public class ClosePortalCommand extends Command {
{
@@ -33,7 +34,7 @@ public class ClosePortalCommand extends Command {
}
@Override
public void execute(Client c, String[] params) {
public void execute(Client c, String[] params, CommandContext ctx) {
Character player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !closeportal <portalid>");

Some files were not shown because too many files have changed in this diff Show More