// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.InterestManagement; import engine.Enum.GridObjectType; import engine.math.FastMath; import engine.math.Vector3f; import engine.math.Vector3fImmutable; import engine.net.DispatchMessage; import engine.net.client.ClientConnection; import engine.net.client.msg.LoadCharacterMsg; import engine.net.client.msg.LoadStructureMsg; import engine.net.client.msg.UnloadObjectsMsg; import engine.objects.*; import engine.server.MBServerStatics; import java.util.HashSet; import java.util.concurrent.ConcurrentHashMap; public class WorldGrid { public static ConcurrentHashMap[][] DynamicGridMap; public static ConcurrentHashMap[][] StaticGridMap; private static float dynamicBucketScale = 0.00390625f; // 256 bucket size, 1/256 private static float staticBucketScale = 0.00390625f; public static void startLoadJob() { Thread loadJobThread; loadJobThread = new Thread(InterestManager.INTERESTMANAGER); loadJobThread.setName("InterestManager"); loadJobThread.start(); } public static boolean moveWorldObject(AbstractWorldObject awo, Vector3fImmutable location) { awo.setLoc(location); return true; } public static HashSet getInRange(Vector3f loc, double r) { HashSet outbound = new HashSet<>(); return outbound; } public static HashSet getObjectsInRangePartial(Vector3fImmutable loc, double r, int mask) { HashSet outbound = new HashSet<>(); float scale; if ((mask & MBServerStatics.MASK_STATIC) != 0) scale = WorldGrid.staticBucketScale; else scale = WorldGrid.dynamicBucketScale; int gridX = (int) Math.abs(loc.x * scale); int gridZ = (int) Math.abs(loc.z * scale); int bucketSize = (int) (r * scale) + 1; //start at top left most corner to scan. int startingX = gridX - bucketSize; int startingZ = gridZ + bucketSize; int limitX = Math.abs((int) (MBServerStatics.MAX_WORLD_WIDTH * scale)); int limitZ = Math.abs((int) (MBServerStatics.MAX_WORLD_HEIGHT * scale)); //LimitZ is negative, remember to flip sign. if (startingX < 0) startingX = 0; if (startingZ < 0) startingZ = 0; if (startingX > limitX) startingX = limitX; if (startingZ > limitZ) startingZ = limitZ; int endX = startingX + (bucketSize * 2); int endZ = startingZ - (bucketSize * 2); if (endX < 0) endX = 0; if (endZ < 0) endZ = 0; if (endX > limitX) endX = limitX; if (endZ > limitZ) endZ = limitZ; int auditMob = 0; for (int x = startingX; x <= endX; x++) { for (int z = startingZ; z >= endZ; z--) { ConcurrentHashMap gridMap; if ((MBServerStatics.MASK_STATIC & mask) != 0) gridMap = WorldGrid.StaticGridMap[x][z]; else gridMap = WorldGrid.DynamicGridMap[x][z]; for (AbstractWorldObject gridObject : gridMap.values()) { if ((gridObject.getObjectTypeMask() & mask) == 0) continue; if (gridObject.getLoc().distanceSquared2D(loc) <= FastMath.sqr(r)) outbound.add(gridObject); } } } return outbound; } public static HashSet getObjectsInRangePartialNecroPets(Vector3fImmutable loc, double r) { HashSet outbound = new HashSet<>(); return outbound; } public static HashSet getObjectsInRangeContains(Vector3fImmutable loc, double r, int mask) { HashSet outbound = getObjectsInRangePartial(loc, r, mask); return outbound; } public static HashSet getObjectsInRangePartial(AbstractWorldObject awo, double range, int mask) { return getObjectsInRangePartial(awo.getLoc(), range, mask); } public static void InitializeGridObjects() { int dynamicWidth = (int) Math.abs(MBServerStatics.MAX_WORLD_WIDTH * WorldGrid.dynamicBucketScale); int dynamicHeight = (int) Math.abs(MBServerStatics.MAX_WORLD_HEIGHT * WorldGrid.dynamicBucketScale); int staticWidth = (int) Math.abs(MBServerStatics.MAX_WORLD_WIDTH * WorldGrid.staticBucketScale); int staticHeight = (int) Math.abs(MBServerStatics.MAX_WORLD_HEIGHT * WorldGrid.staticBucketScale); WorldGrid.DynamicGridMap = new ConcurrentHashMap[dynamicWidth + 1][dynamicHeight + 1]; WorldGrid.StaticGridMap = new ConcurrentHashMap[staticWidth + 1][staticHeight + 1]; //create new hash maps for each bucket for (int x = 0; x <= staticWidth; x++) for (int y = 0; y <= staticHeight; y++) { WorldGrid.StaticGridMap[x][y] = new ConcurrentHashMap(); } for (int x = 0; x <= dynamicWidth; x++) for (int y = 0; y <= dynamicHeight; y++) { WorldGrid.DynamicGridMap[x][y] = new ConcurrentHashMap(); } } public static void RemoveWorldObject(AbstractWorldObject gridObject) { if (gridObject == null) return; AbstractWorldObject.RemoveFromWorldGrid(gridObject); } public static boolean addObject(AbstractWorldObject gridObject, float x, float z) { if (gridObject == null) return false; if (x > MBServerStatics.MAX_WORLD_WIDTH) return false; if (z < MBServerStatics.MAX_WORLD_HEIGHT) return false; if (x < 0) return false; if (z > 0) return false; int gridX; int gridZ; if (gridObject.getGridObjectType().equals(GridObjectType.STATIC)) { gridX = Math.abs((int) (x * WorldGrid.staticBucketScale)); gridZ = Math.abs((int) (z * WorldGrid.staticBucketScale)); } else { gridX = Math.abs((int) (x * WorldGrid.dynamicBucketScale)); gridZ = Math.abs((int) (z * WorldGrid.dynamicBucketScale)); } WorldGrid.RemoveWorldObject(gridObject); return AbstractWorldObject.AddToWorldGrid(gridObject, gridX, gridZ); } public static void unloadObject(AbstractWorldObject awo) { UnloadObjectsMsg uom = new UnloadObjectsMsg(); uom.addObject(awo); DispatchMessage.sendToAllInRange(awo, uom); } public static void loadObject(AbstractWorldObject awo) { LoadStructureMsg lsm; LoadCharacterMsg lcm; switch (awo.getObjectType()) { case Building: lsm = new LoadStructureMsg(); lsm.addObject((Building) awo); DispatchMessage.sendToAllInRange(awo, lsm); break; case NPC: lcm = new LoadCharacterMsg((NPC) awo, false); DispatchMessage.sendToAllInRange(awo, lcm); break; case Mob: lcm = new LoadCharacterMsg((Mob) awo, false); DispatchMessage.sendToAllInRange(awo, lcm); break; default: // *** Refactor: Log error? break; } } public static void loadObject(AbstractWorldObject awo, ClientConnection origin) { LoadStructureMsg lsm; LoadCharacterMsg lcm; switch (awo.getObjectType()) { case Building: lsm = new LoadStructureMsg(); lsm.addObject((Building) awo); DispatchMessage.sendToAllInRange(awo, lsm); break; case NPC: lcm = new LoadCharacterMsg((NPC) awo, false); DispatchMessage.sendToAllInRange(awo, lcm); break; case Mob: lcm = new LoadCharacterMsg((Mob) awo, false); DispatchMessage.sendToAllInRange(awo, lcm); break; case PlayerCharacter: lcm = new LoadCharacterMsg((PlayerCharacter) awo, false); DispatchMessage.sendToAllInRange(awo, lcm); break; default: // *** Refactor: Log error? break; } } public static void unloadObject(AbstractWorldObject awo, ClientConnection origin) { UnloadObjectsMsg uom = new UnloadObjectsMsg(); uom.addObject(awo); DispatchMessage.sendToAllInRange(awo, uom); } public static void addObject(AbstractWorldObject awo, PlayerCharacter pc) { if (pc == null || awo == null) return; ClientConnection origin = pc.getClientConnection(); if (origin == null) return; loadObject(awo, origin); } public static void removeObject(AbstractWorldObject awo, PlayerCharacter pc) { if (pc == null || awo == null) return; ClientConnection origin = pc.getClientConnection(); if (origin == null) return; unloadObject(awo, origin); } public static void updateObject(AbstractWorldObject awo, PlayerCharacter pc) { if (pc == null || awo == null) return; ClientConnection origin = pc.getClientConnection(); if (origin == null) return; unloadObject(awo, origin); loadObject(awo, origin); } public static void updateObject(AbstractWorldObject awo) { if (awo == null) return; unloadObject(awo); loadObject(awo); } /* * */ public static void removeObject(AbstractWorldObject awo) { if (awo == null) return; unloadObject(awo); } }