Initial Repository Push
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,554 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2022
|
||||
// www.magicbane.com
|
||||
|
||||
package engine.InterestManagement;
|
||||
|
||||
import engine.Enum.DispatchChannel;
|
||||
import engine.Enum.GameObjectType;
|
||||
import engine.ai.MobileFSM;
|
||||
import engine.ai.MobileFSM.STATE;
|
||||
import engine.gameManager.GroupManager;
|
||||
import engine.gameManager.SessionManager;
|
||||
import engine.job.JobScheduler;
|
||||
import engine.jobs.RefreshGroupJob;
|
||||
import engine.net.AbstractNetMsg;
|
||||
import engine.net.Dispatch;
|
||||
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.MoveToPointMsg;
|
||||
import engine.net.client.msg.UnloadObjectsMsg;
|
||||
import engine.objects.*;
|
||||
import engine.server.MBServerStatics;
|
||||
import org.pmw.tinylog.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
import static engine.math.FastMath.sqr;
|
||||
|
||||
public enum InterestManager implements Runnable {
|
||||
|
||||
INTERESTMANAGER;
|
||||
|
||||
private static long lastTime;
|
||||
private static boolean keepGoing = true;
|
||||
|
||||
public void shutdown() {
|
||||
this.keepGoing = false;
|
||||
}
|
||||
|
||||
InterestManager() {
|
||||
Logger.info(" Interest Management thread is running.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
beginLoadJob();
|
||||
}
|
||||
|
||||
private void beginLoadJob() {
|
||||
|
||||
InterestManager.lastTime = System.currentTimeMillis();
|
||||
|
||||
while (InterestManager.keepGoing) {
|
||||
try {
|
||||
updateAllPlayers();
|
||||
} catch (Exception e) {
|
||||
Logger.error("InterestManager.BeginLoadJob:updateAllPlayers", e);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(advanceOneSecond());
|
||||
} catch (Exception e) {
|
||||
Logger.error("InterestManager.BeginLoadJob:advanceOneSecond", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long advanceOneSecond() {
|
||||
|
||||
long curTime = System.currentTimeMillis();
|
||||
long dur = 1000 + this.lastTime - curTime;
|
||||
|
||||
if (dur < 0) {
|
||||
// Last update took more then one second, not good...
|
||||
Logger.warn("LoadJob took more then one second to complete.");
|
||||
this.lastTime = curTime + 100;
|
||||
return 100;
|
||||
}
|
||||
this.lastTime += 1000;
|
||||
return dur;
|
||||
}
|
||||
|
||||
private void updateAllPlayers() {
|
||||
// get all players
|
||||
|
||||
for (PlayerCharacter pc : SessionManager.getAllActivePlayerCharacters()) {
|
||||
|
||||
if (pc == null)
|
||||
continue;
|
||||
|
||||
ClientConnection origin = pc.getClientConnection();
|
||||
|
||||
if (origin == null)
|
||||
continue;
|
||||
|
||||
if (!pc.isEnteredWorld())
|
||||
continue;
|
||||
|
||||
if (pc.getTeleportLock().readLock().tryLock()) {
|
||||
|
||||
try {
|
||||
updateStaticList(pc, origin);
|
||||
updateMobileList(pc, origin);
|
||||
} catch (Exception e) {
|
||||
Logger.error(e);
|
||||
} finally {
|
||||
pc.getTeleportLock().readLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStaticList(PlayerCharacter player, ClientConnection origin) {
|
||||
|
||||
// Only update if we've moved far enough to warrant it
|
||||
|
||||
float distanceSquared = player.getLoc().distanceSquared2D(player.getLastStaticLoc());
|
||||
|
||||
if (distanceSquared > sqr(25))
|
||||
player.setLastStaticLoc(player.getLoc());
|
||||
else
|
||||
return;
|
||||
|
||||
// Get Statics in range
|
||||
HashSet<AbstractWorldObject> toLoad = WorldGrid.getObjectsInRangePartial(player.getLoc(), MBServerStatics.STRUCTURE_LOAD_RANGE,
|
||||
MBServerStatics.MASK_STATIC);
|
||||
|
||||
// get list of obects loaded that need removed
|
||||
HashSet<AbstractWorldObject> loadedStaticObjects = player.getLoadedStaticObjects();
|
||||
|
||||
HashSet<AbstractWorldObject> toRemove = null;
|
||||
|
||||
toRemove = new HashSet<>(loadedStaticObjects);
|
||||
|
||||
toRemove.removeAll(toLoad);
|
||||
|
||||
// unload static objects now out of range
|
||||
if (toRemove.size() > 0) {
|
||||
UnloadObjectsMsg uom = new UnloadObjectsMsg();
|
||||
for (AbstractWorldObject obj : toRemove) {
|
||||
if (obj.getObjectType().equals(GameObjectType.Building))
|
||||
InterestManager.HandleSpecialUnload((Building) obj, origin);
|
||||
if (obj != null && !obj.equals(player))
|
||||
uom.addObject(obj);
|
||||
}
|
||||
|
||||
Dispatch dispatch = Dispatch.borrow(player, uom);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
||||
}
|
||||
|
||||
loadedStaticObjects.removeAll(toRemove);
|
||||
|
||||
// remove any object to load that are already loaded
|
||||
toLoad.removeAll(loadedStaticObjects);
|
||||
|
||||
LoadStructureMsg lsm = new LoadStructureMsg();
|
||||
LoadCharacterMsg lcm = null;
|
||||
ArrayList<LoadCharacterMsg> lcmList = new ArrayList<>();
|
||||
|
||||
for (AbstractWorldObject awo : toLoad) {
|
||||
if (awo.getObjectType().equals(GameObjectType.Building))
|
||||
lsm.addObject((Building) awo);
|
||||
else if (awo.getObjectType().equals(GameObjectType.Corpse)) {
|
||||
Corpse corpse = (Corpse) awo;
|
||||
lcm = new LoadCharacterMsg(corpse, PlayerCharacter.hideNonAscii());
|
||||
|
||||
Dispatch dispatch = Dispatch.borrow(player, lcm);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
||||
|
||||
|
||||
} else if (awo.getObjectType().equals(GameObjectType.NPC)) {
|
||||
NPC npc = (NPC) awo;
|
||||
lcm = new LoadCharacterMsg(npc, PlayerCharacter.hideNonAscii());
|
||||
|
||||
lcmList.add(lcm);
|
||||
}
|
||||
}
|
||||
|
||||
if (lsm.getStructureList().size() > 0) {
|
||||
Dispatch dispatch = Dispatch.borrow(player, lsm);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
||||
}
|
||||
|
||||
for (LoadCharacterMsg lc : lcmList) {
|
||||
|
||||
Dispatch dispatch = Dispatch.borrow(player, lc);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
||||
}
|
||||
|
||||
loadedStaticObjects.addAll(toLoad);
|
||||
}
|
||||
|
||||
private void updateMobileList(PlayerCharacter player, ClientConnection origin) {
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
// Get list of players in range
|
||||
// TODO for now use a generic getALL list, later tie into Quad Tree
|
||||
HashSet<AbstractWorldObject> toLoad = WorldGrid.getObjectsInRangePartial(player.getLoc(), MBServerStatics.CHARACTER_LOAD_RANGE,
|
||||
MBServerStatics.MASK_MOBILE);
|
||||
|
||||
HashSet<AbstractWorldObject> toRemove = new HashSet<>();
|
||||
|
||||
HashSet<AbstractWorldObject> toLoadToPlayer = new HashSet<>();
|
||||
|
||||
for (AbstractWorldObject loadedObject : toLoad) {
|
||||
|
||||
switch (loadedObject.getObjectType()) {
|
||||
case PlayerCharacter:
|
||||
PlayerCharacter loadedPlayer = (PlayerCharacter) loadedObject;
|
||||
|
||||
if (loadedPlayer.getObjectUUID() == player.getObjectUUID())
|
||||
continue;
|
||||
|
||||
if (player.getSeeInvis() < loadedPlayer.getHidden())
|
||||
continue;
|
||||
|
||||
if (loadedPlayer.safemodeInvis())
|
||||
continue;
|
||||
|
||||
if (player.getLoadedObjects().contains(loadedPlayer))
|
||||
continue;
|
||||
|
||||
if (!loadedPlayer.isInWorldGrid())
|
||||
continue;
|
||||
|
||||
toLoadToPlayer.add(loadedPlayer);
|
||||
break;
|
||||
//not playerCharacter, mobs,npcs and corpses cant be invis or safemode, just add normaly
|
||||
default:
|
||||
if (player.getLoadedObjects().contains(loadedObject))
|
||||
continue;
|
||||
|
||||
if (!loadedObject.isInWorldGrid())
|
||||
continue;
|
||||
|
||||
toLoadToPlayer.add(loadedObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float unloadDistance = MBServerStatics.CHARACTER_LOAD_RANGE;
|
||||
for (AbstractWorldObject playerLoadedObject : player.getLoadedObjects()) {
|
||||
|
||||
if (playerLoadedObject.getObjectType().equals(GameObjectType.PlayerCharacter)) {
|
||||
PlayerCharacter loadedPlayer = (PlayerCharacter) playerLoadedObject;
|
||||
if (player.getSeeInvis() < loadedPlayer.getHidden())
|
||||
toRemove.add(playerLoadedObject);
|
||||
else if (loadedPlayer.safemodeInvis())
|
||||
toRemove.add(playerLoadedObject);
|
||||
}
|
||||
|
||||
if (!playerLoadedObject.isInWorldGrid())
|
||||
toRemove.add(playerLoadedObject);
|
||||
else if (playerLoadedObject.getLoc().distanceSquared2D(player.getLoc()) > unloadDistance * unloadDistance)
|
||||
toRemove.add(playerLoadedObject);
|
||||
|
||||
}
|
||||
|
||||
player.getLoadedObjects().addAll(toLoadToPlayer);
|
||||
player.getLoadedObjects().removeAll(toRemove);
|
||||
|
||||
// get list of obects loaded to remove
|
||||
|
||||
// unload objects now out of range
|
||||
|
||||
if (toRemove.size() > 0) {
|
||||
|
||||
UnloadObjectsMsg uom = new UnloadObjectsMsg();
|
||||
|
||||
for (AbstractWorldObject obj : toRemove) {
|
||||
|
||||
try {
|
||||
if (obj != null)
|
||||
if (obj.equals(player)) // don't unload self
|
||||
continue;
|
||||
|
||||
uom.addObject(obj);
|
||||
|
||||
if (obj.getObjectType() == GameObjectType.Mob)
|
||||
((Mob) obj).getPlayerAgroMap().remove(player.getObjectUUID());
|
||||
} catch (Exception e) {
|
||||
Logger.error("UnloadCharacter", obj.getObjectUUID() + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (!uom.getObjectList().isEmpty()) {
|
||||
Dispatch dispatch = Dispatch.borrow(player, uom);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
||||
}
|
||||
}
|
||||
|
||||
LoadCharacterMsg lcm = null;
|
||||
ArrayList<AbstractWorldObject> players = new ArrayList<>();
|
||||
ArrayList<AbstractWorldObject> addToList = new ArrayList<>();
|
||||
|
||||
for (AbstractWorldObject awo : toLoadToPlayer) {
|
||||
// dont load yourself
|
||||
try {
|
||||
if (awo.equals(player))
|
||||
continue;
|
||||
|
||||
if ((awo.getObjectTypeMask() & MBServerStatics.MASK_PLAYER) != 0) {
|
||||
|
||||
// object to load is a player
|
||||
PlayerCharacter awopc = (PlayerCharacter) awo;
|
||||
|
||||
// dont load if invis
|
||||
if (player.getSeeInvis() < awopc.getHidden())
|
||||
continue;
|
||||
|
||||
lcm = new LoadCharacterMsg(awopc, PlayerCharacter.hideNonAscii());
|
||||
players.add(awo);
|
||||
|
||||
// check if in a group with the person being loaded
|
||||
// and if so set updateGroup flag
|
||||
|
||||
if (GroupManager.getGroup(player) != null
|
||||
&& GroupManager.getGroup(player) == GroupManager.getGroup(awopc))
|
||||
|
||||
// submit a job as for some reason the client needs a delay
|
||||
// with group updates
|
||||
// as it wont update if we do RefreshGroup directly after
|
||||
// sending the lcm below
|
||||
|
||||
JobScheduler.getInstance().scheduleJob(new RefreshGroupJob(player, awopc), MBServerStatics.LOAD_OBJECT_DELAY);
|
||||
|
||||
} else if ((awo.getObjectTypeMask() & MBServerStatics.MASK_MOB) != 0) {
|
||||
Mob awonpc = (Mob) awo;
|
||||
|
||||
if (!awonpc.isAlive() && (awonpc.isPet() || awonpc.isSiege() || awonpc.isNecroPet() || awonpc.isPlayerGuard()))
|
||||
continue;
|
||||
|
||||
if (awonpc.getState().equals(STATE.Respawn) || awonpc.getState().equals(STATE.Disabled))
|
||||
continue;
|
||||
|
||||
awonpc.getPlayerAgroMap().put(player.getObjectUUID(), false);
|
||||
MobileFSM.setAwake(awonpc, false);
|
||||
// IVarController.setVariable(awonpc, "IntelligenceDisableDelay", (double) (System.currentTimeMillis() + 5000));
|
||||
// awonpc.enableIntelligence();
|
||||
lcm = new LoadCharacterMsg(awonpc, PlayerCharacter.hideNonAscii());
|
||||
} else if ((awo.getObjectTypeMask() & MBServerStatics.MASK_NPC) != 0) {
|
||||
NPC awonpc = (NPC) awo;
|
||||
lcm = new LoadCharacterMsg(awonpc, PlayerCharacter.hideNonAscii());
|
||||
} else if ((awo.getObjectTypeMask() & MBServerStatics.MASK_PET) != 0) {
|
||||
Mob awonpc = (Mob) awo;
|
||||
|
||||
if (!awonpc.isAlive())
|
||||
continue;
|
||||
|
||||
awonpc.getPlayerAgroMap().put(player.getObjectUUID(), false);
|
||||
|
||||
if (awonpc.isMob())
|
||||
MobileFSM.setAwake(awonpc, false);
|
||||
// IVarController.setVariable(awonpc, "IntelligenceDisableDelay", (double) (System.currentTimeMillis() + 5000));
|
||||
// awonpc.enableIntelligence();
|
||||
lcm = new LoadCharacterMsg(awonpc, PlayerCharacter.hideNonAscii());
|
||||
}
|
||||
|
||||
addToList.add(awo);
|
||||
|
||||
if (lcm != null) {
|
||||
Dispatch dispatch = Dispatch.borrow(player, lcm);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
Logger.error(awo.getObjectUUID() + " " + e.getMessage());
|
||||
}
|
||||
//Delaying character loading to reduce bandwidth consumption
|
||||
}
|
||||
|
||||
// send effects for all players being loaded
|
||||
// do it on a timer otherwise we may get failures as te client needs
|
||||
// time to process lcm
|
||||
//Added effects to LoadCharacter Serialization.
|
||||
//JobScheduler.getInstance().scheduleJob(new LoadEffectsJob(players, origin), MBServerStatics.LOAD_OBJECT_DELAY);
|
||||
}
|
||||
|
||||
// Forces the loading of static objects (corpses and buildings).
|
||||
// Needed to override threshold limits on loading statics
|
||||
|
||||
public static void forceLoad(AbstractWorldObject awo) {
|
||||
|
||||
AbstractNetMsg msg = null;
|
||||
LoadStructureMsg lsm;
|
||||
LoadCharacterMsg lcm;
|
||||
NPC npc;
|
||||
Corpse corpse;
|
||||
HashSet<AbstractWorldObject> toUpdate;
|
||||
|
||||
switch (awo.getObjectType()) {
|
||||
case Building:
|
||||
lsm = new LoadStructureMsg();
|
||||
lsm.addObject((Building) awo);
|
||||
msg = lsm;
|
||||
break;
|
||||
case Corpse:
|
||||
corpse = (Corpse) awo;
|
||||
lcm = new LoadCharacterMsg(corpse, false);
|
||||
msg = lcm;
|
||||
break;
|
||||
case NPC:
|
||||
npc = (NPC) awo;
|
||||
lcm = new LoadCharacterMsg(npc, false);
|
||||
msg = lcm;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
toUpdate = WorldGrid.getObjectsInRangePartial(awo.getLoc(), MBServerStatics.CHARACTER_LOAD_RANGE, MBServerStatics.MASK_PLAYER);
|
||||
|
||||
boolean send;
|
||||
|
||||
for (AbstractWorldObject tar : toUpdate) {
|
||||
PlayerCharacter player = (PlayerCharacter) tar;
|
||||
HashSet<AbstractWorldObject> loadedStaticObjects = player.getLoadedStaticObjects();
|
||||
send = false;
|
||||
|
||||
if (!loadedStaticObjects.contains(awo)) {
|
||||
loadedStaticObjects.add(awo);
|
||||
send = true;
|
||||
}
|
||||
|
||||
if (send) {
|
||||
|
||||
Dispatch dispatch = Dispatch.borrow(player, msg);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, DispatchChannel.PRIMARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleSpecialUnload(Building building, ClientConnection origin) {
|
||||
|
||||
if (Regions.FurnitureRegionMap.get(building.getObjectUUID()) == null)
|
||||
return;
|
||||
|
||||
Regions buildingRegion = Regions.FurnitureRegionMap.get(building.getObjectUUID());
|
||||
|
||||
if (!buildingRegion.isOutside())
|
||||
return;
|
||||
|
||||
MoveToPointMsg moveMsg = new MoveToPointMsg(building);
|
||||
|
||||
if (origin != null)
|
||||
origin.sendMsg(moveMsg);
|
||||
}
|
||||
|
||||
public synchronized void HandleLoadForEnterWorld(PlayerCharacter player) {
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
ClientConnection origin = player.getClientConnection();
|
||||
|
||||
if (origin == null)
|
||||
return;
|
||||
|
||||
//Update static list
|
||||
try {
|
||||
updateStaticList(player, origin);
|
||||
} catch (Exception e) {
|
||||
Logger.error("InterestManager.updateAllStaticPlayers: " + player.getObjectUUID(), e);
|
||||
}
|
||||
|
||||
//Update mobile list
|
||||
try {
|
||||
updateMobileList(player, origin);
|
||||
} catch (Exception e) {
|
||||
Logger.error("InterestManager.updateAllMobilePlayers: " + player.getObjectUUID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void HandleLoadForTeleport(PlayerCharacter player) {
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
ClientConnection origin = player.getClientConnection();
|
||||
|
||||
if (origin == null)
|
||||
return;
|
||||
|
||||
//Update static list
|
||||
try {
|
||||
updateStaticList(player, origin);
|
||||
} catch (Exception e) {
|
||||
Logger.error("InterestManager.updateAllStaticPlayers: " + player.getObjectUUID(), e);
|
||||
}
|
||||
|
||||
//Update mobile list
|
||||
try {
|
||||
updateMobileList(player, origin);
|
||||
} catch (Exception e) {
|
||||
Logger.error("InterestManager.updateAllMobilePlayers: " + player.getObjectUUID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void reloadCharacter(AbstractCharacter absChar) {
|
||||
|
||||
UnloadObjectsMsg uom = new UnloadObjectsMsg();
|
||||
uom.addObject(absChar);
|
||||
LoadCharacterMsg lcm = new LoadCharacterMsg(absChar, false);
|
||||
|
||||
HashSet<AbstractWorldObject> toSend = WorldGrid.getObjectsInRangePartial(absChar.getLoc(), MBServerStatics.CHARACTER_LOAD_RANGE,
|
||||
MBServerStatics.MASK_PLAYER);
|
||||
|
||||
PlayerCharacter pc = null;
|
||||
|
||||
if (absChar.getObjectType().equals(GameObjectType.PlayerCharacter))
|
||||
pc = (PlayerCharacter) absChar;
|
||||
|
||||
for (AbstractWorldObject awo : toSend) {
|
||||
|
||||
PlayerCharacter pcc = (PlayerCharacter) awo;
|
||||
|
||||
if (pcc == null)
|
||||
continue;
|
||||
|
||||
ClientConnection cc = SessionManager.getClientConnection(pcc);
|
||||
|
||||
if (cc == null)
|
||||
continue;
|
||||
|
||||
if (pcc.getObjectUUID() == absChar.getObjectUUID())
|
||||
continue;
|
||||
|
||||
else {
|
||||
if (pc != null)
|
||||
if (pcc.getSeeInvis() < pc.getHidden())
|
||||
continue;
|
||||
|
||||
if (!cc.sendMsg(uom)) {
|
||||
String classType = uom.getClass().getSimpleName();
|
||||
Logger.error("Failed to send message ");
|
||||
}
|
||||
|
||||
if (!cc.sendMsg(lcm)) {
|
||||
String classType = lcm.getClass().getSimpleName();
|
||||
Logger.error("Failed to send message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2022
|
||||
// www.magicbane.com
|
||||
|
||||
package engine.InterestManagement;
|
||||
|
||||
/* This class is the main interface for Magicbane's
|
||||
* Interest management facilities.
|
||||
*/
|
||||
|
||||
import engine.Enum;
|
||||
import engine.math.Vector3fImmutable;
|
||||
import engine.net.Dispatch;
|
||||
import engine.net.DispatchMessage;
|
||||
import engine.net.client.msg.TerritoryChangeMessage;
|
||||
import engine.objects.City;
|
||||
import engine.objects.PlayerCharacter;
|
||||
import engine.objects.Realm;
|
||||
import engine.server.MBServerStatics;
|
||||
import engine.util.MapLoader;
|
||||
import org.pmw.tinylog.Logger;
|
||||
|
||||
import static engine.objects.Realm.getRealm;
|
||||
|
||||
public class RealmMap {
|
||||
|
||||
// Spatial hashmap. Used for detecting which Realm
|
||||
// a player is currently in..
|
||||
|
||||
public static int[][] _realmImageMap;
|
||||
|
||||
|
||||
public static int getRealmIDAtLocation(Vector3fImmutable pos) {
|
||||
|
||||
int xBuckets = (int) ((pos.getX() / MBServerStatics.MAX_WORLD_WIDTH) * MBServerStatics.SPATIAL_HASH_BUCKETSX);
|
||||
int yBuckets = (int) ((pos.getZ() / MBServerStatics.MAX_WORLD_HEIGHT) * MBServerStatics.SPATIAL_HASH_BUCKETSY);
|
||||
|
||||
if (yBuckets < 0 || yBuckets >= MBServerStatics.SPATIAL_HASH_BUCKETSY
|
||||
|| xBuckets < 0 || xBuckets >= MBServerStatics.SPATIAL_HASH_BUCKETSX) {
|
||||
Logger.error("WorldServerRealm.getRealmFromPosition",
|
||||
"Invalid range; Z: " + yBuckets + ", X: " + xBuckets);
|
||||
return 255;
|
||||
}
|
||||
|
||||
return RealmMap._realmImageMap[xBuckets][yBuckets];
|
||||
}
|
||||
|
||||
public static Realm getRealmForCity(City city) {
|
||||
Realm outRealm = null;
|
||||
outRealm = city.getRealm();
|
||||
return outRealm;
|
||||
}
|
||||
|
||||
public static Realm getRealmAtLocation(Vector3fImmutable worldVector) {
|
||||
|
||||
return getRealm(RealmMap.getRealmIDAtLocation(worldVector));
|
||||
|
||||
}
|
||||
|
||||
public static void updateRealm(PlayerCharacter player){
|
||||
|
||||
int realmID = RealmMap.getRealmIDAtLocation(player.getLoc());
|
||||
|
||||
if (realmID != player.getLastRealmID()){
|
||||
player.setLastRealmID(realmID);
|
||||
Realm realm = Realm.getRealm(realmID);
|
||||
if (realm != null){
|
||||
if (realm.isRuled()){
|
||||
City city = realm.getRulingCity();
|
||||
if (city != null){
|
||||
TerritoryChangeMessage tcm = new TerritoryChangeMessage((PlayerCharacter)realm.getRulingCity().getOwner(),realm);
|
||||
Dispatch dispatch = Dispatch.borrow(player, tcm);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);
|
||||
}else{
|
||||
TerritoryChangeMessage tcm = new TerritoryChangeMessage(null,realm);
|
||||
Dispatch dispatch = Dispatch.borrow(player, tcm);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);
|
||||
}
|
||||
|
||||
}else{
|
||||
TerritoryChangeMessage tcm = new TerritoryChangeMessage(null,realm);
|
||||
Dispatch dispatch = Dispatch.borrow(player, tcm);
|
||||
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.PRIMARY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static void loadRealmImageMap() {
|
||||
|
||||
RealmMap._realmImageMap = MapLoader.loadMap();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user