diff --git a/config.yaml b/config.yaml index 835fe88b93..3fae109d3c 100644 --- a/config.yaml +++ b/config.yaml @@ -461,6 +461,12 @@ server: #Event End Timestamp EVENT_END_TIMESTAMP: 1428897600000 + # GM Security Configuration + MINIMUM_GM_LEVEL_TO_TRADE: 4 + MINIMUM_GM_LEVEL_TO_USE_STORAGE: 4 + MINIMUM_GM_LEVEL_TO_USE_DUEY: 4 + MINIMUM_GM_LEVEL_TO_DROP: 4 + #Any NPC ids that should search for a js override script (useful if they already have wz entries since otherwise they're ignored). NPCS_SCRIPTABLE: #9200000: Talk to Cody # Cody diff --git a/src/main/java/client/inventory/manipulator/InventoryManipulator.java b/src/main/java/client/inventory/manipulator/InventoryManipulator.java index 98f6fc3f19..331a7061b0 100644 --- a/src/main/java/client/inventory/manipulator/InventoryManipulator.java +++ b/src/main/java/client/inventory/manipulator/InventoryManipulator.java @@ -706,6 +706,12 @@ public class InventoryManipulator { Inventory inv = chr.getInventory(type); Item source = inv.getItem(src); + if (chr.isGM() && chr.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_DROP) { + chr.message("You cannot drop items at your GM level."); + log.info("GM %s tried to drop item id %d", chr.getName(), source.getItemId()); + return; + } + if (chr.getTrade() != null || chr.getMiniGame() != null || source == null) { //Only check needed would prob be merchants (to see if the player is in one) return; } diff --git a/src/main/java/client/processor/npc/DueyProcessor.java b/src/main/java/client/processor/npc/DueyProcessor.java index 1633e99b71..345c135c7a 100644 --- a/src/main/java/client/processor/npc/DueyProcessor.java +++ b/src/main/java/client/processor/npc/DueyProcessor.java @@ -284,6 +284,13 @@ public class DueyProcessor { public static void dueySendItem(Client c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient, boolean quick) { if (c.tryacquireClient()) { try { + if (c.getPlayer().isGM() && c.getPlayer().gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_USE_DUEY) { + c.getPlayer().message("You cannot use Duey to send items at your GM level."); + log.info(String.format("GM %s tried to send a package to %s", c.getPlayer().getName(), recipient)); + c.sendPacket(PacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_INCORRECT_REQUEST.getCode())); + return; + } + int fee = Trade.getFee(sendMesos); if (sendMessage != null && sendMessage.length() > 100) { AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with Quick Delivery on duey."); diff --git a/src/main/java/client/processor/npc/StorageProcessor.java b/src/main/java/client/processor/npc/StorageProcessor.java index bd7f4333a0..6fa24a7399 100644 --- a/src/main/java/client/processor/npc/StorageProcessor.java +++ b/src/main/java/client/processor/npc/StorageProcessor.java @@ -50,6 +50,8 @@ public class StorageProcessor { ItemInformationProvider ii = ItemInformationProvider.getInstance(); Character chr = c.getPlayer(); Storage storage = chr.getStorage(); + String gmBlockedStorageMessage = "You cannot use the storage as a GM of this level."; + byte mode = p.readByte(); if (chr.getLevel() < 15) { @@ -61,7 +63,7 @@ public class StorageProcessor { if (c.tryacquireClient()) { try { switch (mode) { - case 4: { // take out + case 4: { // Take out byte type = p.readByte(); byte slot = p.readByte(); if (slot < 0 || slot > storage.getSlots()) { // removal starts at zero @@ -70,8 +72,17 @@ public class StorageProcessor { c.disconnect(true, false); return; } + slot = storage.getSlot(InventoryType.getByType(type), slot); Item item = storage.getItem(slot); + + if (hasGMRestrictions(chr)) { + chr.dropMessage(1, gmBlockedStorageMessage); + log.info(String.format("GM %s blocked from using storage", chr.getName())); + chr.sendPacket(PacketCreator.enableActions()); + return; + } + if (item != null) { if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) { c.sendPacket(PacketCreator.getStorageError((byte) 0x0C)); @@ -107,7 +118,7 @@ public class StorageProcessor { } break; } - case 5: { // store + case 5: { // Store short slot = p.readShort(); int itemId = p.readInt(); short quantity = p.readShort(); @@ -120,6 +131,14 @@ public class StorageProcessor { c.disconnect(true, false); return; } + + if (hasGMRestrictions(chr)) { + chr.dropMessage(1, gmBlockedStorageMessage); + log.info(String.format("GM %s blocked from using storage", chr.getName())); + chr.sendPacket(PacketCreator.enableActions()); + return; + } + if (quantity < 1) { c.sendPacket(PacketCreator.enableActions()); return; @@ -173,16 +192,24 @@ public class StorageProcessor { } break; } - case 6: // arrange items + case 6: // Arrange items if (YamlConfig.config.server.USE_STORAGE_ITEM_SORT) { storage.arrangeItems(c); } c.sendPacket(PacketCreator.enableActions()); break; - case 7: { // meso + case 7: { // Mesos int meso = p.readInt(); int storageMesos = storage.getMeso(); int playerMesos = chr.getMeso(); + + if (hasGMRestrictions(chr)) { + chr.dropMessage(1, gmBlockedStorageMessage); + log.info(String.format("GM %s blocked from using storage", chr.getName())); + chr.sendPacket(PacketCreator.enableActions()); + return; + } + if ((meso > 0 && storageMesos >= meso) || (meso < 0 && playerMesos >= -meso)) { if (meso < 0 && (storageMesos - meso) < 0) { meso = Integer.MIN_VALUE + storageMesos; @@ -208,7 +235,7 @@ public class StorageProcessor { } break; } - case 8: // close... unless the player decides to enter cash shop! + case 8: // Close (unless the player decides to enter cash shop) storage.close(); break; } @@ -217,4 +244,8 @@ public class StorageProcessor { } } } + + private static boolean hasGMRestrictions(Character character) { + return character.isGM() && character.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_USE_STORAGE; + } } diff --git a/src/main/java/config/ServerConfig.java b/src/main/java/config/ServerConfig.java index 064599729a..499ac738ef 100644 --- a/src/main/java/config/ServerConfig.java +++ b/src/main/java/config/ServerConfig.java @@ -309,6 +309,12 @@ public class ServerConfig { //Event End Timestamp public long EVENT_END_TIMESTAMP; + //GM Security Configuration + public int MINIMUM_GM_LEVEL_TO_TRADE; + public int MINIMUM_GM_LEVEL_TO_USE_STORAGE; + public int MINIMUM_GM_LEVEL_TO_USE_DUEY; + public int MINIMUM_GM_LEVEL_TO_DROP; + //Custom NPC overrides. List of NPC IDs. public Map NPCS_SCRIPTABLE = new HashMap<>(); } diff --git a/src/main/java/net/server/channel/handlers/MesoDropHandler.java b/src/main/java/net/server/channel/handlers/MesoDropHandler.java index 2c8254aed7..f99c7edbee 100644 --- a/src/main/java/net/server/channel/handlers/MesoDropHandler.java +++ b/src/main/java/net/server/channel/handlers/MesoDropHandler.java @@ -23,6 +23,7 @@ package net.server.channel.handlers; import client.Character; import client.Client; +import config.YamlConfig; import net.AbstractPacketHandler; import net.packet.InPacket; import tools.PacketCreator; @@ -42,6 +43,11 @@ public final class MesoDropHandler extends AbstractPacketHandler { p.skip(4); int meso = p.readInt(); + if (player.isGM() && player.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_DROP) { + player.message("You cannot drop mesos at your GM level."); + return; + } + if (c.tryacquireClient()) { // thanks imbee for noticing players not being able to throw mesos too fast try { if (meso <= player.getMeso() && meso > 9 && meso < 50001) { diff --git a/src/main/java/server/Trade.java b/src/main/java/server/Trade.java index 2f95cb9219..9233d57426 100644 --- a/src/main/java/server/Trade.java +++ b/src/main/java/server/Trade.java @@ -448,6 +448,20 @@ public class Trade { } public static void inviteTrade(Character c1, Character c2) { + + if ((c1.isGM() && !c2.isGM()) && c1.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_TRADE) { + c1.message("You cannot trade with non-GM characters."); + log.info(String.format("GM %s blocked from trading with %s due to GM level.", c1.getName(), c2.getName())); + cancelTrade(c1, TradeResult.NO_RESPONSE); + return; + } + + if ((!c1.isGM() && c2.isGM()) && c2.gmLevel() < YamlConfig.config.server.MINIMUM_GM_LEVEL_TO_TRADE) { + c1.message("You cannot trade with this GM character."); + cancelTrade(c1, TradeResult.NO_RESPONSE); + return; + } + if (InviteCoordinator.hasInvite(InviteType.TRADE, c1.getId())) { if (hasTradeInviteBack(c1, c2)) { c1.message("You are already managing this player's trade invitation.");