|
|
|
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
|
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
|
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
|
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
|
|
|
// 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<Integer, AbstractWorldObject>[][] DynamicGridMap;
|
|
|
|
public static ConcurrentHashMap<Integer, AbstractWorldObject>[][] 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<AbstractWorldObject> getInRange(Vector3f loc, double r) {
|
|
|
|
HashSet<AbstractWorldObject> outbound = new HashSet<>();
|
|
|
|
return outbound;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static HashSet<AbstractWorldObject> getObjectsInRangePartial(Vector3fImmutable loc, double r, int mask) {
|
|
|
|
HashSet<AbstractWorldObject> 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<Integer, AbstractWorldObject> 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<AbstractWorldObject> getObjectsInRangePartialNecroPets(Vector3fImmutable loc, double r) {
|
|
|
|
HashSet<AbstractWorldObject> outbound = new HashSet<>();
|
|
|
|
return outbound;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static HashSet<AbstractWorldObject> getObjectsInRangeContains(Vector3fImmutable loc, double r, int mask) {
|
|
|
|
HashSet<AbstractWorldObject> outbound = getObjectsInRangePartial(loc, r, mask);
|
|
|
|
return outbound;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static HashSet<AbstractWorldObject> 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<Integer, AbstractWorldObject>();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int x = 0; x <= dynamicWidth; x++)
|
|
|
|
for (int y = 0; y <= dynamicHeight; y++) {
|
|
|
|
WorldGrid.DynamicGridMap[x][y] = new ConcurrentHashMap<Integer, AbstractWorldObject>();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|