From 7745ef6949cc94f9f5ab7bb1de127276f555006e Mon Sep 17 00:00:00 2001
From: FatBoy-DOTC <justin.chucksinsulating@gmail.com>
Date: Mon, 20 May 2024 10:07:04 -0500
Subject: [PATCH] optimized looting system

---
 src/engine/gameManager/LootActionManager.java | 199 +++++++++++++++
 src/engine/net/client/ClientMessagePump.java  | 231 +-----------------
 2 files changed, 200 insertions(+), 230 deletions(-)
 create mode 100644 src/engine/gameManager/LootActionManager.java

diff --git a/src/engine/gameManager/LootActionManager.java b/src/engine/gameManager/LootActionManager.java
new file mode 100644
index 00000000..91e713f9
--- /dev/null
+++ b/src/engine/gameManager/LootActionManager.java
@@ -0,0 +1,199 @@
+package engine.gameManager;
+
+import engine.Enum;
+import engine.exception.MsgSendException;
+import engine.net.Dispatch;
+import engine.net.DispatchMessage;
+import engine.net.client.ClientConnection;
+import engine.net.client.msg.LootMsg;
+import engine.net.client.msg.TransferItemFromEquipToInventoryMsg;
+import engine.net.client.msg.UpdateGoldMsg;
+import engine.objects.*;
+import engine.server.MBServerStatics;
+import org.pmw.tinylog.Logger;
+
+import static engine.math.FastMath.sqr;
+
+public class LootActionManager {
+    public static void loot(LootMsg msg, ClientConnection origin) throws MsgSendException {
+        PlayerCharacter player = SessionManager.getPlayerCharacter(origin);
+        if (player == null || !player.isAlive() || player.getAltitude() > 0) {
+            return;
+        }
+
+        Item item = msg.getItem();
+        if (item == null) {
+            return;
+        }
+
+        if (item.lootLock.tryLock()) {
+            try {
+                handleLoot(msg, player, item, origin);
+            } catch (Exception e) {
+                Logger.info(e.getMessage());
+            } finally {
+                item.lootLock.unlock();
+            }
+        }
+    }
+
+    private static void handleLoot(LootMsg msg, PlayerCharacter player, Item item, ClientConnection origin) {
+        int targetType = msg.getTargetType();
+        int targetID = msg.getTargetID();
+
+        if (!isValidTargetType(targetType)) {
+            targetType = msg.getSourceID2();
+            targetID = msg.getUnknown01();
+        }
+
+        AbstractCharacter targetCharacter;
+        Corpse corpse;
+
+        if (isCharacterOrMob(targetType)) {
+            targetCharacter = getTargetCharacter(targetType, targetID);
+            if (targetCharacter == null || isInvalidLootTarget(player, targetCharacter)) {
+                return;
+            }
+
+            if (!lootCharacter(player, item, origin, targetCharacter)) {
+                return;
+            }
+
+            handleCharacterLoot(player, msg, item, targetCharacter);
+        } else if (targetType == Enum.GameObjectType.Corpse.ordinal()) {
+            corpse = Corpse.getCorpse(targetID);
+            if (corpse == null || isTooFarToLoot(player, corpse)) {
+                return;
+            }
+
+            if (!lootCorpse(player, item, origin, corpse)) {
+                return;
+            }
+
+            handleCorpseLoot(player, msg, item, corpse);
+        }
+    }
+
+    private static boolean isValidTargetType(int targetType) {
+        return targetType == Enum.GameObjectType.PlayerCharacter.ordinal() ||
+                targetType == Enum.GameObjectType.Mob.ordinal() ||
+                targetType == Enum.GameObjectType.Corpse.ordinal();
+    }
+
+    private static boolean isCharacterOrMob(int targetType) {
+        return targetType == Enum.GameObjectType.PlayerCharacter.ordinal() ||
+                targetType == Enum.GameObjectType.Mob.ordinal() ||
+                targetType == Enum.GameObjectType.NPC.ordinal();
+    }
+
+    private static AbstractCharacter getTargetCharacter(int targetType, int targetID) {
+        if (targetType == Enum.GameObjectType.PlayerCharacter.ordinal()) {
+            return PlayerCharacter.getFromCache(targetID);
+        } else if (targetType == Enum.GameObjectType.NPC.ordinal()) {
+            return NPC.getFromCache(targetID);
+        } else if (targetType == Enum.GameObjectType.Mob.ordinal()) {
+            return Mob.getFromCache(targetID);
+        }
+        return null;
+    }
+
+    private static boolean isInvalidLootTarget(PlayerCharacter player, AbstractCharacter target) {
+        return target.equals(player) ||
+                (target.getObjectType().equals(Enum.GameObjectType.PlayerCharacter) &&
+                        player.getObjectUUID() != target.getObjectUUID() &&
+                        ((PlayerCharacter) target).isInSafeZone()) ||
+                player.getLoc().distanceSquared2D(target.getLoc()) > sqr(MBServerStatics.LOOT_RANGE) ||
+                target.isAlive();
+    }
+
+    private static boolean isTooFarToLoot(PlayerCharacter player, Corpse corpse) {
+        return player.getLoc().distanceSquared2D(corpse.getLoc()) > sqr(MBServerStatics.LOOT_RANGE);
+    }
+
+    private static boolean lootCharacter(PlayerCharacter player, Item item, ClientConnection origin, AbstractCharacter target) {
+        if (!GroupManager.goldSplit(player, item, origin, target)) {
+            if (target.getCharItemManager() != null) {
+                Item itemRet = target.getCharItemManager().lootItemFromMe(item, player, origin);
+                if (target.getObjectType() == Enum.GameObjectType.Mob && itemRet != null && item.getObjectType() == Enum.GameObjectType.MobLoot) {
+                    handleMobEquipmentLoot(player, target);
+                }
+                return itemRet != null;
+            }
+        }
+        return false;
+    }
+
+    private static void handleMobEquipmentLoot(PlayerCharacter player, AbstractCharacter target) {
+        Mob mobTarget = (Mob) target;
+        for (MobEquipment equip : mobTarget.getEquip().values()) {
+            TransferItemFromEquipToInventoryMsg back = new TransferItemFromEquipToInventoryMsg(mobTarget, equip.getSlot());
+            DispatchMessage.dispatchMsgToInterestArea(mobTarget, back, Enum.DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
+
+            LootMsg lootMsg = new LootMsg(0, 0, target.getObjectType().ordinal(), target.getObjectUUID(), equip);
+            Dispatch dispatch = Dispatch.borrow(player, lootMsg);
+            DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
+            break;
+        }
+    }
+
+    private static boolean lootCorpse(PlayerCharacter player, Item item, ClientConnection origin, Corpse corpse) {
+        Item itemRet = null;
+        if (player.getObjectUUID() == corpse.getBelongsToID()) {
+            itemRet = corpse.lootItem(item, player);
+        } else if (!GroupManager.goldSplit(player, item, origin, corpse)) {
+            itemRet = corpse.lootItem(item, player);
+        }
+        return itemRet != null;
+    }
+
+    private static void handleCharacterLoot(PlayerCharacter player, LootMsg msg, Item item, AbstractCharacter target) {
+        if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) {
+            updateGold(player, target);
+        } else {
+            dispatchLootMessage(player, msg, target, item);
+        }
+        sendGroupLootMessage(player, item);
+    }
+
+    private static void handleCorpseLoot(PlayerCharacter player, LootMsg msg, Item item, Corpse corpse) {
+        if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) {
+            updateGold(player, corpse);
+        } else {
+            DispatchMessage.dispatchMsgToInterestArea(corpse, msg, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true);
+        }
+        sendGroupLootMessage(player, item);
+    }
+
+    private static void updateGold(PlayerCharacter player, AbstractWorldObject target) {
+        UpdateGoldMsg updateTargetGold = new UpdateGoldMsg(target);
+        updateTargetGold.configure();
+        DispatchMessage.dispatchMsgToInterestArea(target, updateTargetGold, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
+
+        UpdateGoldMsg ugm = new UpdateGoldMsg(player);
+        ugm.configure();
+        Dispatch dispatch = Dispatch.borrow(player, ugm);
+        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY);
+    }
+
+    private static void dispatchLootMessage(PlayerCharacter player, LootMsg msg, AbstractCharacter target, Item item) {
+        msg.setSourceType1(0);
+        msg.setSourceType2(0);
+        msg.setSourceID1(0);
+        msg.setSourceID2(0);
+        Dispatch dispatch = Dispatch.borrow(player, msg);
+        DispatchMessage.dispatchMsgToInterestArea(target, msg, Enum.DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true);
+
+        LootMsg newItemMsg = new LootMsg(Enum.GameObjectType.PlayerCharacter.ordinal(), player.getObjectUUID(), 0, 0, item);
+        dispatch = Dispatch.borrow(player, newItemMsg);
+        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);
+    }
+
+    private static void sendGroupLootMessage(PlayerCharacter player, Item item) {
+        Group group = GroupManager.getGroup(player);
+        if (group != null && group.getSplitGold() && !item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) {
+            String name = item.getName();
+            String text = player.getFirstName() + " has looted " + name + '.';
+            ChatManager.chatGroupInfoCanSee(player, text);
+        }
+    }
+}
diff --git a/src/engine/net/client/ClientMessagePump.java b/src/engine/net/client/ClientMessagePump.java
index 54cbc291..4cfb8ad6 100644
--- a/src/engine/net/client/ClientMessagePump.java
+++ b/src/engine/net/client/ClientMessagePump.java
@@ -773,235 +773,6 @@ public class ClientMessagePump implements NetMsgHandler {
 
     }
 
-    private static void loot(LootMsg msg, ClientConnection origin) throws MsgSendException {
-
-        PlayerCharacter player = SessionManager.getPlayerCharacter(origin);
-        if (player == null)
-            return;
-
-        if (!player.isAlive())
-            return;
-
-        Item item = msg.getItem();
-
-        if (item == null)
-            return;
-
-        if (item.lootLock.tryLock()) {
-            try {
-                Item itemRet = null;
-                // get current owner
-                int targetType = msg.getTargetType();
-                int targetID = msg.getTargetID();
-
-                if (targetType == GameObjectType.PlayerCharacter.ordinal() || targetType == GameObjectType.Mob.ordinal() || targetType == GameObjectType.Corpse.ordinal()) {
-                } else { //needed for getting contracts for some reason
-                    targetType = msg.getSourceID2();
-                    targetID = msg.getUnknown01();
-                }
-
-                //can't loot while flying
-                if (player.getAltitude() > 0)
-                    return;
-
-                AbstractCharacter tar = null;
-                Corpse corpse = null;
-
-                if (targetType == GameObjectType.PlayerCharacter.ordinal() || targetType == GameObjectType.Mob.ordinal()) {
-
-                    if (targetType == GameObjectType.PlayerCharacter.ordinal()) {
-                        tar = PlayerCharacter.getFromCache(targetID);
-
-                        if (tar == null)
-                            return;
-
-                        if (player.getObjectUUID() != tar.getObjectUUID() && ((PlayerCharacter) tar).isInSafeZone())
-                            return;
-
-                    } else if (targetType == GameObjectType.NPC.ordinal())
-                        tar = NPC.getFromCache(targetID);
-                    else if (targetType == GameObjectType.Mob.ordinal())
-                        tar = Mob.getFromCache(targetID);
-                    if (tar == null)
-                        return;
-
-                    if (tar.equals(player)) {
-                        ErrorPopupMsg.sendErrorMsg(player, "Cannot loot this item.");
-                        return;
-                    }
-
-
-                    if (player.getLoc().distanceSquared2D(tar.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) {
-                        ErrorPopupMsg.sendErrorMsg(player, "You are too far away to loot this corpse.");
-
-                        Logger.info(player.getFirstName() + " tried looting at " + player.getLoc().distance2D(tar.getLoc()) + " distance.");
-                        return;
-                    }
-
-                    //can't loot from someone who is alive.
-                    if (AbstractWorldObject.IsAbstractCharacter(tar)) {
-                        if (tar.isAlive())
-                            return;
-                        //					Logger.error("WorldServer.loot", "Looting from live player");
-                    }
-
-                    if (!GroupManager.goldSplit(player, item, origin, tar)) {
-
-                        if (tar.getCharItemManager() != null) {
-
-                            itemRet = tar.getCharItemManager().lootItemFromMe(item, player, origin);
-
-                            //Take equipment off mob
-                            if (tar.getObjectType() == GameObjectType.Mob && itemRet != null) {
-                                Mob mobTarget = (Mob) tar;
-
-                                if (item != null && item.getObjectType() == GameObjectType.MobLoot) {
-
-                                    for (MobEquipment equip : mobTarget.getEquip().values()) {
-
-                                        TransferItemFromEquipToInventoryMsg back = new TransferItemFromEquipToInventoryMsg(mobTarget, equip.getSlot());
-                                        DispatchMessage.dispatchMsgToInterestArea(mobTarget, back, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
-
-                                        LootMsg lootMsg = new LootMsg(0, 0, tar.getObjectType().ordinal(), tar.getObjectUUID(), equip);
-                                        Dispatch dispatch = Dispatch.borrow(player, lootMsg);
-                                        DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
-                                        break;
-                                    }
-                                }
-                            }
-                        }
-
-                    }
-                } else if (targetType == GameObjectType.Corpse.ordinal()) {
-                    corpse = Corpse.getCorpse(targetID);
-                    if (corpse == null)
-                        return;
-
-                    if (player.getLoc().distanceSquared2D(corpse.getLoc()) > sqr(MBServerStatics.LOOT_RANGE)) {
-                        ErrorPopupMsg.sendErrorMsg(player, "You are too far away to loot this corpse.");
-
-                        Logger.info(player.getFirstName() + " tried looting at " + player.getLoc().distance2D(corpse.getLoc()) + " distance.");
-                        return;
-                    }
-
-
-                    //can't loot other players in safe zone.
-                    if (corpse.getBelongsToType() == GameObjectType.PlayerCharacter.ordinal()) {
-
-                        if (player.getObjectUUID() == corpse.getBelongsToID())
-                            itemRet = corpse.lootItem(item, player);
-                        else if (!GroupManager.goldSplit(player, item, origin, corpse)) {
-                            itemRet = corpse.lootItem(item, player);
-
-                        }
-
-                        if (itemRet == null)
-                            return;
-
-
-                        if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) {
-                            // this is done to prevent the temporary goldItem item
-                            // (from the mob) from appearing in player's inventory.
-                            // It also updates the goldItem quantity display
-                            UpdateGoldMsg updateTargetGold = null;
-
-
-                            if (corpse != null)
-                                updateTargetGold = new UpdateGoldMsg(corpse);
-
-                            updateTargetGold.configure();
-                            DispatchMessage.dispatchMsgToInterestArea(corpse, updateTargetGold, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false);
-
-                            UpdateGoldMsg ugm = new UpdateGoldMsg(player);
-                            ugm.configure();
-                            Dispatch dispatch = Dispatch.borrow(player, ugm);
-                            DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
-
-                            // respond back loot message. Try sending to everyone.
-
-                        } else {
-
-                            DispatchMessage.dispatchMsgToInterestArea(corpse, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true);
-
-
-                            //player.getCharItemManager().updateInventory();
-                        }
-
-                        //TODO send group loot message if player is grouped and visible
-                        Group group = GroupManager.getGroup(player);
-
-                        if (group != null && group.getSplitGold() && (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD) == false)) {
-                            String name = item.getName();
-                            String text = player.getFirstName() + " has looted " + name + '.';
-                            ChatManager.chatGroupInfoCanSee(player, text);
-                        }
-
-                        return;
-                    }
-
-
-                } else
-                    return;
-
-
-                if (itemRet == null) {
-                    return;
-                }
-
-                if (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD)) {
-                    // this is done to prevent the temporary goldItem item
-                    // (from the mob) from appearing in player's inventory.
-                    // It also updates the goldItem quantity display
-                    UpdateGoldMsg updateTargetGold = null;
-
-                    if (tar != null)
-                        updateTargetGold = new UpdateGoldMsg(tar);
-                    else if (corpse != null)
-                        updateTargetGold = new UpdateGoldMsg(corpse);
-
-                    updateTargetGold.configure();
-                    DispatchMessage.dispatchMsgToInterestArea(tar, updateTargetGold, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false);
-
-                    UpdateGoldMsg ugm = new UpdateGoldMsg(player);
-                    ugm.configure();
-                    Dispatch dispatch = Dispatch.borrow(player, ugm);
-                    DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
-
-                    // respond back loot message. Try sending to everyone.
-
-                } else {
-                    msg.setSourceType1(0);
-                    msg.setSourceType2(0);
-                    msg.setSourceID1(0);
-                    msg.setSourceID2(0);
-                    Dispatch dispatch = Dispatch.borrow(player, msg);
-                    //DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
-                    DispatchMessage.dispatchMsgToInterestArea(tar, msg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, true);
-                    LootMsg newItemMsg = new LootMsg(GameObjectType.PlayerCharacter.ordinal(), player.getObjectUUID(), 0, 0, itemRet);
-                    dispatch = Dispatch.borrow(player, newItemMsg);
-                    DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
-
-                    //player.getCharItemManager().updateInventory();
-                }
-
-                //TODO send group loot message if player is grouped and visible
-                Group group = GroupManager.getGroup(player);
-
-                if (group != null && group.getSplitGold() && (item.getItemBase().getType().equals(engine.Enum.ItemType.GOLD) == false)) {
-                    String name = item.getName();
-                    String text = player.getFirstName() + " has looted " + name + '.';
-                    ChatManager.chatGroupInfoCanSee(player, text);
-                }
-            } catch (Exception e) {
-                Logger.info(e.getMessage());
-            } finally {
-                item.lootLock.unlock();
-            }
-        }
-
-
-    }
-
     // called when player types /show
     private static void show(ShowMsg msg, ClientConnection origin) throws MsgSendException {
 
@@ -1997,7 +1768,7 @@ public class ClientMessagePump implements NetMsgHandler {
                     lootWindowRequest((LootWindowRequestMsg) msg, origin);
                     break;
                 case MOVEOBJECTTOCONTAINER:
-                    loot((LootMsg) msg, origin);
+                    LootActionManager.loot((LootMsg) msg, origin);
                     break;
                 case SHOWCOMBATINFO:
                     show((ShowMsg) msg, origin);