diff --git a/src/engine/InterestManagement/Terrain.java b/src/engine/InterestManagement/Terrain.java index 4db671ab..feef47bd 100644 --- a/src/engine/InterestManagement/Terrain.java +++ b/src/engine/InterestManagement/Terrain.java @@ -62,11 +62,22 @@ public class Terrain { // the blending area between child and parent terrains when // they are stitched together. - Vector2f major_blend = new Vector2f(this.zone.template.max_blend / this.zone.major_radius, - this.zone.template.min_blend / this.zone.major_radius); + float max_blend = this.zone.template.max_blend; + float min_blend = this.zone.template.min_blend; - Vector2f minor_blend = new Vector2f(this.zone.template.max_blend / this.zone.minor_radius, - this.zone.template.min_blend / this.zone.minor_radius); + // Zones with a zero blend inherit from their parent terrain + + if (this.zone.template.max_blend == 0) { + Zone parentZone = this.getNextZoneWithTerrain(this.zone.parent); + max_blend = parentZone.template.max_blend; + min_blend = parentZone.template.min_blend; + } + + Vector2f major_blend = new Vector2f(max_blend / this.zone.major_radius, + min_blend / this.zone.major_radius); + + Vector2f minor_blend = new Vector2f(max_blend / this.zone.minor_radius, + min_blend / this.zone.minor_radius); if (major_blend.y > 0.4f) blend_ratio.x = major_blend.y; diff --git a/src/engine/db/handlers/dbMobHandler.java b/src/engine/db/handlers/dbMobHandler.java index ad9e0a70..ae8cb0da 100644 --- a/src/engine/db/handlers/dbMobHandler.java +++ b/src/engine/db/handlers/dbMobHandler.java @@ -42,7 +42,7 @@ public class dbMobHandler extends dbHandlerBase { preparedStatement.setFloat(6, toAdd.bindLoc.z); preparedStatement.setInt(7, 0); preparedStatement.setFloat(8, toAdd.spawnRadius); - preparedStatement.setInt(9, toAdd.spawnTime); + preparedStatement.setInt(9, toAdd.spawnDelay); preparedStatement.setInt(10, toAdd.contractUUID); preparedStatement.setInt(11, toAdd.buildingUUID); preparedStatement.setInt(12, toAdd.level); diff --git a/src/engine/devcmd/cmds/GetHeightCmd.java b/src/engine/devcmd/cmds/GetHeightCmd.java index 4c48abf8..2683925a 100644 --- a/src/engine/devcmd/cmds/GetHeightCmd.java +++ b/src/engine/devcmd/cmds/GetHeightCmd.java @@ -60,6 +60,8 @@ public class GetHeightCmd extends AbstractDevCmd { this.throwbackInfo(playerCharacter, "Grid : " + "[" + gridSquare.x + "]" + "[" + gridSquare.y + "]"); this.throwbackInfo(playerCharacter, "offset: " + "[" + childZoneOffset.x + "]" + "[" + childZoneOffset.y + "]"); this.throwbackInfo(playerCharacter, "Normalized offset: " + "[" + normalizedOffset.x + "]" + "[" + normalizedOffset.y + "]"); + this.throwbackInfo(playerCharacter, "Heightmap blend Values: max/min" + heightmapZone.template.min_blend + " /" + heightmapZone.template.max_blend); + this.throwbackInfo(playerCharacter, "Parent blend Values: nax/min" + parentZone.template.min_blend + " /" + parentZone.template.max_blend); this.throwbackInfo(playerCharacter, "Blend coefficient: " + heightmapZone.terrain.getTerrainBlendCoefficient(childZoneOffset)); this.throwbackInfo(playerCharacter, "------------"); diff --git a/src/engine/gameManager/ZoneManager.java b/src/engine/gameManager/ZoneManager.java index f0bc0a4b..b842b968 100644 --- a/src/engine/gameManager/ZoneManager.java +++ b/src/engine/gameManager/ZoneManager.java @@ -121,8 +121,8 @@ public enum ZoneManager { public static void resetHotZones() { for (Zone zone : ZoneManager.macroZones) - if (zone.hasBeenHotzone) - zone.hasBeenHotzone = false; + if (zone.wasHotzonw) + zone.wasHotzonw = false; } @@ -150,7 +150,7 @@ public enum ZoneManager { ZoneManager.hotZone = zone; ZoneManager.hotZoneCycle = 1; // Used with HOTZONE_DURATION from config. - zone.hasBeenHotzone = true; + zone.wasHotzonw = true; ZoneManager.hotZoneLastUpdate = LocalDateTime.now().withMinute(0).withSecond(0).atZone(ZoneId.systemDefault()).toInstant(); } @@ -242,7 +242,7 @@ public enum ZoneManager { //no duplicate hotZones - if (zone.hasBeenHotzone == true) + if (zone.wasHotzonw == true) return false; // Enforce min level diff --git a/src/engine/mobileAI/MobAI.java b/src/engine/mobileAI/MobAI.java index 22cd65dd..169a7157 100644 --- a/src/engine/mobileAI/MobAI.java +++ b/src/engine/mobileAI/MobAI.java @@ -15,6 +15,7 @@ import engine.gameManager.*; import engine.math.Vector3f; import engine.math.Vector3fImmutable; import engine.mobileAI.Threads.MobAIThread; +import engine.mobileAI.Threads.Respawner; import engine.mobileAI.utilities.CombatUtilities; import engine.mobileAI.utilities.MovementUtilities; import engine.net.DispatchMessage; @@ -813,11 +814,9 @@ public class MobAI { } } } - } else if (System.currentTimeMillis() > (aiAgent.deathTime + (aiAgent.spawnTime * 1000))) { - - if (Zone.respawnQue.contains(aiAgent) == false) { - Zone.respawnQue.add(aiAgent); - } + } else if (System.currentTimeMillis() > (aiAgent.deathTime + (aiAgent.spawnDelay * 1000))) { + aiAgent.respawnTime = aiAgent.deathTime + (aiAgent.spawnDelay * 1000); + Respawner.respawnQueue.put(aiAgent); } } catch (Exception e) { Logger.info(aiAgent.getObjectUUID() + " " + aiAgent.getName() + " Failed At: CheckForRespawn" + " " + e.getMessage()); diff --git a/src/engine/mobileAI/Threads/MobRespawnThread.java b/src/engine/mobileAI/Threads/Respawner.java similarity index 53% rename from src/engine/mobileAI/Threads/MobRespawnThread.java rename to src/engine/mobileAI/Threads/Respawner.java index da628be7..9aaa477f 100644 --- a/src/engine/mobileAI/Threads/MobRespawnThread.java +++ b/src/engine/mobileAI/Threads/Respawner.java @@ -6,20 +6,17 @@ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com -package engine.mobileAI.Threads; -import engine.gameManager.ZoneManager; +package engine.mobileAI.Threads; import engine.objects.Mob; -import engine.objects.Zone; import org.pmw.tinylog.Logger; -public class MobRespawnThread implements Runnable { - +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; - public MobRespawnThread() { - Logger.info(" MobRespawnThread thread has started!"); - - } +public enum Respawner implements Runnable { + RESPAWNER; + public static BlockingQueue respawnQueue = new DelayQueue(); @Override public void run() { @@ -27,27 +24,18 @@ public class MobRespawnThread implements Runnable { while (true) { try { - for (Zone zone : ZoneManager.getAllZones()) - if (zone.respawnQue.isEmpty() == false && zone.lastRespawn + 100 < System.currentTimeMillis()) { - - Mob respawner = zone.respawnQue.iterator().next(); - - if (respawner == null) - continue; - - respawner.respawn(); - zone.respawnQue.remove(respawner); - zone.lastRespawn = System.currentTimeMillis(); - } - } catch (Exception e) { - Logger.error(e); + Mob mobile = respawnQueue.take(); + mobile.respawn(); + } catch (InterruptedException e) { + Logger.error(e.toString()); } } } - public static void startRespawnThread() { + + public static void start() { Thread respawnThread; - respawnThread = new Thread(new MobRespawnThread()); + respawnThread = new Thread(RESPAWNER); respawnThread.setName("respawnThread"); respawnThread.start(); } diff --git a/src/engine/net/client/msg/ManageCityAssetsMsg.java b/src/engine/net/client/msg/ManageCityAssetsMsg.java index 9392838e..1c32d841 100644 --- a/src/engine/net/client/msg/ManageCityAssetsMsg.java +++ b/src/engine/net/client/msg/ManageCityAssetsMsg.java @@ -727,9 +727,9 @@ public class ManageCityAssetsMsg extends ClientNetMsg { if (!npcHire.isAlive()) { writer.put((byte) 1); // 1 SHOWs respawning - int respawnRemaining = (int) (((Mob) npcHire).deathTime + ((Mob) npcHire).spawnTime * 1000 - System.currentTimeMillis()) / 1000; + int respawnRemaining = (int) (((Mob) npcHire).deathTime + ((Mob) npcHire).spawnDelay * 1000 - System.currentTimeMillis()) / 1000; writer.putInt(respawnRemaining); // Seconds in respawn remaining. - writer.putInt(((Mob) npcHire).spawnTime); // max seconds for respawn + writer.putInt(((Mob) npcHire).spawnDelay); // max seconds for respawn } else writer.put((byte) 0); diff --git a/src/engine/net/client/msg/ManageNPCMsg.java b/src/engine/net/client/msg/ManageNPCMsg.java index 53797601..b9255b0a 100644 --- a/src/engine/net/client/msg/ManageNPCMsg.java +++ b/src/engine/net/client/msg/ManageNPCMsg.java @@ -364,14 +364,14 @@ public class ManageNPCMsg extends ClientNetMsg { writer.putInt(1); long curTime = System.currentTimeMillis() / 1000; - long upgradeTime = (mob.deathTime + (mob.spawnTime * 1000)) / 1000; + long upgradeTime = (mob.deathTime + (mob.spawnDelay * 1000)) / 1000; long timeLife = upgradeTime - curTime; if (upgradeTime * 1000 > System.currentTimeMillis()) { if (mob.guardCaptain.isAlive()) { writer.put((byte) 0);//shows respawning timer - writer.putInt(mob.spawnTime); - writer.putInt(mob.spawnTime); + writer.putInt(mob.spawnDelay); + writer.putInt(mob.spawnDelay); writer.putInt((int) timeLife); //time remaining for mob that is dead writer.putInt(0); writer.put((byte) 0); @@ -688,14 +688,14 @@ public class ManageNPCMsg extends ClientNetMsg { writer.putInt(1); long curTime = System.currentTimeMillis() / 1000; - long upgradeTime = (mob.deathTime + (mob.spawnTime * 1000)) / 1000; + long upgradeTime = (mob.deathTime + (mob.spawnDelay * 1000)) / 1000; long timeLife = upgradeTime - curTime; if (upgradeTime * 1000 > System.currentTimeMillis()) { if (mob.guardCaptain.isAlive()) { writer.put((byte) 0);//shows respawning timer - writer.putInt(mob.spawnTime); - writer.putInt(mob.spawnTime); + writer.putInt(mob.spawnDelay); + writer.putInt(mob.spawnDelay); writer.putInt((int) timeLife); //time remaining for mob that is dead writer.putInt(0); writer.put((byte) 0); diff --git a/src/engine/objects/Mob.java b/src/engine/objects/Mob.java index 8f7f2b2d..27f7d641 100644 --- a/src/engine/objects/Mob.java +++ b/src/engine/objects/Mob.java @@ -26,6 +26,7 @@ import engine.net.DispatchMessage; import engine.net.client.msg.PetMsg; import engine.net.client.msg.PlaceAssetMsg; import engine.server.MBServerStatics; +import org.jetbrains.annotations.NotNull; import org.joda.time.DateTime; import org.pmw.tinylog.Logger; @@ -34,11 +35,14 @@ import java.sql.SQLException; import java.util.EnumSet; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import static engine.net.client.msg.ErrorPopupMsg.sendErrorPopup; +import static java.lang.Math.toIntExact; -public class Mob extends AbstractIntelligenceAgent { +public class Mob extends AbstractIntelligenceAgent implements Delayed { private static int staticID = 0; //mob specific @@ -51,10 +55,11 @@ public class Mob extends AbstractIntelligenceAgent { public boolean despawned = false; public Vector3fImmutable destination = Vector3fImmutable.ZERO; public MobBase mobBase; - public int spawnTime; + public int spawnDelay; public Zone parentZone; public boolean hasLoot = false; public long deathTime = 0; + public long respawnTime = 0; public int equipmentSetID = 0; public int runeSet = 0; public int bootySet = 0; @@ -104,7 +109,7 @@ public class Mob extends AbstractIntelligenceAgent { this.agentType = AIAgentType.MOBILE; this.spawnRadius = rs.getFloat("mob_spawnRadius"); - this.spawnTime = rs.getInt("mob_spawnTime"); + this.spawnDelay = rs.getInt("mob_spawnTime"); statLat = rs.getFloat("mob_spawnX"); statAlt = rs.getFloat("mob_spawnY"); @@ -135,8 +140,8 @@ public class Mob extends AbstractIntelligenceAgent { if (this.upgradeDateTime != null) Mob.submitUpgradeJob(this); - if (this.mobBase != null && this.spawnTime == 0) - this.spawnTime = this.mobBase.getSpawnTime(); + if (this.mobBase != null && this.spawnDelay == 0) + this.spawnDelay = this.mobBase.getSpawnTime(); this.runeSet = rs.getInt("runeSet"); this.bootySet = rs.getInt("bootySet"); @@ -450,7 +455,7 @@ public class Mob extends AbstractIntelligenceAgent { minionMobile.deathTime = System.currentTimeMillis(); minionMobile.guardCaptain = guardCaptain; - minionMobile.spawnTime = (int) (-2.500 * guardCaptain.building.getRank() + 22.5) * 60; + minionMobile.spawnDelay = (int) (-2.500 * guardCaptain.building.getRank() + 22.5) * 60; minionMobile.behaviourType = Enum.MobBehaviourType.GuardMinion; minionMobile.agentType = AIAgentType.GUARDMINION; minionMobile.guardedCity = guardCaptain.guardedCity; @@ -509,7 +514,7 @@ public class Mob extends AbstractIntelligenceAgent { siegeMinion.behaviourType = MobBehaviourType.SiegeEngine; siegeMinion.agentType = AIAgentType.SIEGEENGINE; siegeMinion.bindLoc = Vector3fImmutable.ZERO; - siegeMinion.spawnTime = (60 * 15); + siegeMinion.spawnDelay = (60 * 15); siegeMinion.runAfterLoad(); @@ -650,10 +655,10 @@ public class Mob extends AbstractIntelligenceAgent { } public String getSpawnTimeAsString() { - if (this.spawnTime == 0) + if (this.spawnDelay == 0) return MBServerStatics.DEFAULT_SPAWN_TIME_MS / 1000 + " seconds (Default)"; else - return this.spawnTime + " seconds"; + return this.spawnDelay + " seconds"; } @@ -875,7 +880,7 @@ public class Mob extends AbstractIntelligenceAgent { this.playerAgroMap.clear(); if (this.behaviourType.ordinal() == Enum.MobBehaviourType.GuardMinion.ordinal()) - this.spawnTime = (int) (-2.500 * this.guardCaptain.building.getRank() + 22.5) * 60; + this.spawnDelay = (int) (-2.500 * this.guardCaptain.building.getRank() + 22.5) * 60; if (this.isPet()) { @@ -1547,12 +1552,12 @@ public class Mob extends AbstractIntelligenceAgent { switch (this.behaviourType) { case GuardCaptain: this.agentType = AIAgentType.GUARDCAPTAIN; - this.spawnTime = 600; + this.spawnDelay = 600; this.guardedCity = ZoneManager.getCityAtLocation(this.building.getLoc()); break; case GuardWallArcher: this.agentType = AIAgentType.GUARDWALLARCHER; - this.spawnTime = 450; + this.spawnDelay = 450; this.guardedCity = ZoneManager.getCityAtLocation(this.building.getLoc()); break; } @@ -1883,4 +1888,15 @@ public class Mob extends AbstractIntelligenceAgent { lock.writeLock().unlock(); } } + + @Override + public long getDelay(@NotNull TimeUnit unit) { + long timeRemaining = this.respawnTime - System.currentTimeMillis(); + return unit.convert(timeRemaining, TimeUnit.MILLISECONDS); + } + + @Override + public int compareTo(@NotNull Delayed o) { + return toIntExact(this.respawnTime - ((Mob) o).respawnTime); + } } diff --git a/src/engine/objects/Zone.java b/src/engine/objects/Zone.java index 32fb9c72..e380a3fd 100644 --- a/src/engine/objects/Zone.java +++ b/src/engine/objects/Zone.java @@ -29,8 +29,6 @@ import java.util.concurrent.ConcurrentHashMap; public class Zone extends AbstractWorldObject { - public static final Set respawnQue = Collections.newSetFromMap(new ConcurrentHashMap<>()); - public static long lastRespawn = 0; public final Set zoneBuildingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); public final Set zoneNPCSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); public final Set zoneMobSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -54,7 +52,7 @@ public class Zone extends AbstractWorldObject { public float absZ = 0.0f; public int min_level; public int max_level; - public boolean hasBeenHotzone = false; + public boolean wasHotzonw = false; public ArrayList nodes = new ArrayList<>(); public int parentZoneID; public Zone parent = null; @@ -165,12 +163,16 @@ public class Zone extends AbstractWorldObject { if (ZoneManager.seaFloor == null) ZoneManager.seaFloor = this; + this.setParent(); + this.setBounds(); + if (this.template.terrain_type.equals("NONE")) this.terrain = null; else this.terrain = new Terrain(this); - this.setParent(); + this.global_height = ZoneManager.calculateGlobalZoneHeight(this); + setSeaLevel(); if (this.min_level == 0 && this.parent != null) { this.min_level = this.parent.min_level; @@ -222,9 +224,6 @@ public class Zone extends AbstractWorldObject { this.max_level = this.parent.max_level; } - this.setBounds(); - this.global_height = ZoneManager.calculateGlobalZoneHeight(this); - setSeaLevel(); } private void setSeaLevel() { diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index 627d495b..39de164a 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -23,7 +23,7 @@ import engine.job.JobContainer; import engine.job.JobScheduler; import engine.jobs.LogoutCharacterJob; import engine.mobileAI.Threads.MobAIThread; -import engine.mobileAI.Threads.MobRespawnThread; +import engine.mobileAI.Threads.Respawner; import engine.net.Dispatch; import engine.net.DispatchMessage; import engine.net.ItemProductionManager; @@ -486,7 +486,7 @@ public class WorldServer { //intiate mob respawn thread Logger.info("Starting Mob Respawn Thread"); - MobRespawnThread.startRespawnThread(); + Respawner.start(); // Run maintenance