From bd3787f9d566c0491c37a5b3b722cad3cebcbbec Mon Sep 17 00:00:00 2001 From: MagicBot Date: Tue, 27 Oct 2026 18:57:40 -0400 Subject: [PATCH] Refuse dev commands that would change static world objects World Boot Task 0.6. Static objects are built from client data each boot, so dev commands answer "Must use external tooling to modify gameworld" and log the command, target and caller instead of changing them. - /addbuilding, /mob and /npc always refuse (addsWorldContent hook in doCmd). - rotate, pull, setbuildingaltitude, remove, rename, npcname, setowner, setguild, setrank and forcerename refuse a static target. - purge skips static objects and reports how many it skipped. Value changes: none for non-static objects; the purge message now also reports the skipped count. Co-Authored-By: Claude Opus 5.5 --- src/engine/devcmd/AbstractDevCmd.java | 25 +++++++++++++++++++ src/engine/devcmd/cmds/AddBuildingCmd.java | 5 ++++ src/engine/devcmd/cmds/AddMobCmd.java | 5 ++++ src/engine/devcmd/cmds/AddNPCCmd.java | 5 ++++ src/engine/devcmd/cmds/PullCmd.java | 3 +++ src/engine/devcmd/cmds/PurgeObjectsCmd.java | 9 ++++++- src/engine/devcmd/cmds/RemoveObjectCmd.java | 9 +++++++ src/engine/devcmd/cmds/RenameCmd.java | 4 +++ src/engine/devcmd/cmds/RotateCmd.java | 12 +++++++++ .../devcmd/cmds/SetBuildingAltitudeCmd.java | 3 +++ .../devcmd/cmds/SetForceRenameCityCmd.java | 2 ++ src/engine/devcmd/cmds/SetGuildCmd.java | 3 +++ src/engine/devcmd/cmds/SetNpcNameCmd.java | 3 +++ src/engine/devcmd/cmds/SetOwnerCmd.java | 3 +++ src/engine/devcmd/cmds/SetRankCmd.java | 7 ++++++ 15 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/engine/devcmd/AbstractDevCmd.java b/src/engine/devcmd/AbstractDevCmd.java index a1d76e33..cd72c5cd 100644 --- a/src/engine/devcmd/AbstractDevCmd.java +++ b/src/engine/devcmd/AbstractDevCmd.java @@ -13,6 +13,7 @@ import engine.gameManager.ChatManager; import engine.gameManager.DbManager; import engine.mbEnums.GameObjectType; import engine.objects.*; +import org.pmw.tinylog.Logger; import java.util.ArrayList; @@ -99,12 +100,18 @@ public abstract class AbstractDevCmd { if (args.length > 0 && args[0].equalsIgnoreCase("?")) { this.sendHelp(pcSender); this.sendUsage(pcSender); + } else if (this.addsWorldContent()) { + this.refuseWorldChange(pcSender, target); } else { this.tr = target; this._doCmd(pcSender, args, target); } } + protected boolean addsWorldContent() { + return false; + } + protected abstract void _doCmd(PlayerCharacter pcSender, String[] args, AbstractGameObject target); @@ -178,4 +185,22 @@ public abstract class AbstractDevCmd { ChatManager.chatSystemInfo(pc, msgText); } + // Static world objects are built from client data each boot, so they are changed with external tooling only. + protected boolean refuseStaticTarget(PlayerCharacter pc, AbstractGameObject target) { + + if (!(target instanceof AbstractWorldObject) || !((AbstractWorldObject) target).isStaticWorldObject()) + return false; + + this.refuseWorldChange(pc, target); + return true; + } + + protected void refuseWorldChange(PlayerCharacter pc, AbstractGameObject target) { + + String targetText = (target == null) ? "no target" : target.getObjectType() + " " + target.getObjectUUID(); + + this.throwbackError(pc, "Must use external tooling to modify gameworld"); + Logger.info("Refused dev command " + this.getMainCmdString() + " on " + targetText + " by " + pc.getFirstName()); + } + } diff --git a/src/engine/devcmd/cmds/AddBuildingCmd.java b/src/engine/devcmd/cmds/AddBuildingCmd.java index 186f1f73..2ba6065f 100644 --- a/src/engine/devcmd/cmds/AddBuildingCmd.java +++ b/src/engine/devcmd/cmds/AddBuildingCmd.java @@ -108,6 +108,11 @@ public class AddBuildingCmd extends AbstractDevCmd { } + @Override + protected boolean addsWorldContent() { + return true; + } + @Override protected String _getHelpString() { return "Creates a building of type 'buildingID' at the location your character is standing."; diff --git a/src/engine/devcmd/cmds/AddMobCmd.java b/src/engine/devcmd/cmds/AddMobCmd.java index 9c7c5b27..a0c34b65 100644 --- a/src/engine/devcmd/cmds/AddMobCmd.java +++ b/src/engine/devcmd/cmds/AddMobCmd.java @@ -75,6 +75,11 @@ public class AddMobCmd extends AbstractDevCmd { } } + @Override + protected boolean addsWorldContent() { + return true; + } + @Override protected String _getHelpString() { return "Creates a Mob of type 'mobID' at the location your character is standing"; diff --git a/src/engine/devcmd/cmds/AddNPCCmd.java b/src/engine/devcmd/cmds/AddNPCCmd.java index b7b091b4..780cd539 100644 --- a/src/engine/devcmd/cmds/AddNPCCmd.java +++ b/src/engine/devcmd/cmds/AddNPCCmd.java @@ -94,6 +94,11 @@ public class AddNPCCmd extends AbstractDevCmd { } } + @Override + protected boolean addsWorldContent() { + return true; + } + @Override protected String _getHelpString() { return "Creates an NPC of type 'npcID' at the location your character is standing"; diff --git a/src/engine/devcmd/cmds/PullCmd.java b/src/engine/devcmd/cmds/PullCmd.java index 0b26d16b..6067cddc 100644 --- a/src/engine/devcmd/cmds/PullCmd.java +++ b/src/engine/devcmd/cmds/PullCmd.java @@ -66,6 +66,9 @@ public class PullCmd extends AbstractDevCmd { return; } + if (this.refuseStaticTarget(pcSender, target)) + return; + targetLoc = pcSender.getLoc(); serverZone = ZoneManager.findSmallestZone(targetLoc); switch (target.getObjectType()) { diff --git a/src/engine/devcmd/cmds/PurgeObjectsCmd.java b/src/engine/devcmd/cmds/PurgeObjectsCmd.java index c362f023..3dd4aa7e 100644 --- a/src/engine/devcmd/cmds/PurgeObjectsCmd.java +++ b/src/engine/devcmd/cmds/PurgeObjectsCmd.java @@ -245,11 +245,17 @@ public class PurgeObjectsCmd extends AbstractDevCmd { HashSet objectList = WorldGrid.getObjectsInRangePartial(_currentLocation, _targetRange, _targetMask); + int staticSkipped = 0; // Iterate through array and remove objects from game world and database for (AbstractWorldObject awo : objectList) { + if (awo.isStaticWorldObject()) { + staticSkipped++; + continue; + } + switch (awo.getObjectType()) { case Building: removeBuilding(pc, (Building) awo); @@ -266,7 +272,8 @@ public class PurgeObjectsCmd extends AbstractDevCmd { } // Send results to user - throwbackInfo(pc, "Purge: " + objectList.size() + " objects were removed in range " + _targetRange); + throwbackInfo(pc, "Purge: " + (objectList.size() - staticSkipped) + " objects were removed in range " + _targetRange + + ", " + staticSkipped + " static objects skipped"); } catch (Exception e) { Logger.error(e); } diff --git a/src/engine/devcmd/cmds/RemoveObjectCmd.java b/src/engine/devcmd/cmds/RemoveObjectCmd.java index 87943c8e..cda37e06 100644 --- a/src/engine/devcmd/cmds/RemoveObjectCmd.java +++ b/src/engine/devcmd/cmds/RemoveObjectCmd.java @@ -119,6 +119,9 @@ public class RemoveObjectCmd extends AbstractDevCmd { private void removeBuilding(PlayerCharacter pc, Building building) { + if (this.refuseStaticTarget(pc, building)) + return; + Zone currentZone = ZoneManager.findSmallestZone(pc.getLoc()); if (currentZone == null) { @@ -164,6 +167,9 @@ public class RemoveObjectCmd extends AbstractDevCmd { private void removeNPC(PlayerCharacter pc, NPC npc) { + if (this.refuseStaticTarget(pc, npc)) + return; + Zone currentZone = ZoneManager.findSmallestZone(pc.getLoc()); if (currentZone == null) { this.throwbackError(pc, "Could not locate zone for player."); @@ -196,6 +202,9 @@ public class RemoveObjectCmd extends AbstractDevCmd { private void removeMob(PlayerCharacter pc, Mob mob) { + if (this.refuseStaticTarget(pc, mob)) + return; + Zone currentZone = ZoneManager.findSmallestZone(pc.getLoc()); if (currentZone == null) { diff --git a/src/engine/devcmd/cmds/RenameCmd.java b/src/engine/devcmd/cmds/RenameCmd.java index 0f2cb72f..05cfec6e 100644 --- a/src/engine/devcmd/cmds/RenameCmd.java +++ b/src/engine/devcmd/cmds/RenameCmd.java @@ -47,6 +47,10 @@ public class RenameCmd extends AbstractDevCmd { building = (Building) target; } else npc = getTargetAsNPC(pcSender); + + if (this.refuseStaticTarget(pcSender, npc) || this.refuseStaticTarget(pcSender, building)) + return; + if (npc != null) { DbManager.NPCQueries.SET_PROPERTY(npc, "npc_name", args[0]); String name = args[0]; diff --git a/src/engine/devcmd/cmds/RotateCmd.java b/src/engine/devcmd/cmds/RotateCmd.java index d9e16f57..78dae2eb 100644 --- a/src/engine/devcmd/cmds/RotateCmd.java +++ b/src/engine/devcmd/cmds/RotateCmd.java @@ -186,6 +186,10 @@ public class RotateCmd extends AbstractDevCmd { } private void rotateBuilding(PlayerCharacter pc, Building building, float yaw) { + + if (this.refuseStaticTarget(pc, building)) + return; + building.setRotation(Quaternion.fromYaw(yaw)); building.getBounds().setBounds(building); WorldGrid.updateObject(building, pc); @@ -196,6 +200,10 @@ public class RotateCmd extends AbstractDevCmd { } private void rotateNPC(PlayerCharacter pc, NPC npc, float yaw) { + + if (this.refuseStaticTarget(pc, npc)) + return; + npc.setRotation(characterRotation(yaw)); DbManager.NPCQueries.SET_PROPERTY(npc, "npc_rotation", npc.getRotation().w); WorldGrid.updateObject(npc, pc); @@ -204,6 +212,10 @@ public class RotateCmd extends AbstractDevCmd { } private void rotateMob(PlayerCharacter pc, Mob mob, float yaw) { + + if (this.refuseStaticTarget(pc, mob)) + return; + mob.setRotation(characterRotation(yaw)); DbManager.MobQueries.SET_PROPERTY(mob, "mob_rotation", mob.getRotation().w); WorldGrid.updateObject(mob, pc); diff --git a/src/engine/devcmd/cmds/SetBuildingAltitudeCmd.java b/src/engine/devcmd/cmds/SetBuildingAltitudeCmd.java index eeddb5fa..c5b1bfae 100644 --- a/src/engine/devcmd/cmds/SetBuildingAltitudeCmd.java +++ b/src/engine/devcmd/cmds/SetBuildingAltitudeCmd.java @@ -66,6 +66,9 @@ public class SetBuildingAltitudeCmd extends AbstractDevCmd { Building targetBuilding = (Building) target; + if (this.refuseStaticTarget(pc, targetBuilding)) + return; + float altitude = 0; try { diff --git a/src/engine/devcmd/cmds/SetForceRenameCityCmd.java b/src/engine/devcmd/cmds/SetForceRenameCityCmd.java index 9cf6aeeb..9b18e380 100644 --- a/src/engine/devcmd/cmds/SetForceRenameCityCmd.java +++ b/src/engine/devcmd/cmds/SetForceRenameCityCmd.java @@ -36,6 +36,8 @@ public class SetForceRenameCityCmd extends AbstractDevCmd { City city = City.getCity(zone.playerCityUUID); if (city == null) return; + if (this.refuseStaticTarget(pc, city)) + return; city.setForceRename(rename); } diff --git a/src/engine/devcmd/cmds/SetGuildCmd.java b/src/engine/devcmd/cmds/SetGuildCmd.java index 8d603f94..f13b236f 100644 --- a/src/engine/devcmd/cmds/SetGuildCmd.java +++ b/src/engine/devcmd/cmds/SetGuildCmd.java @@ -37,6 +37,9 @@ public class SetGuildCmd extends AbstractDevCmd { targetNPC = getTargetAsNPC(pcSender); targetGuild = Guild.getGuild(Integer.parseInt(args[0])); + if (this.refuseStaticTarget(pcSender, targetNPC)) + return; + DbManager.NPCQueries.SET_PROPERTY(targetNPC, "npc_guildID", args[0]); targetNPC.setGuild(targetGuild); diff --git a/src/engine/devcmd/cmds/SetNpcNameCmd.java b/src/engine/devcmd/cmds/SetNpcNameCmd.java index cf7dd34a..90e0b95b 100644 --- a/src/engine/devcmd/cmds/SetNpcNameCmd.java +++ b/src/engine/devcmd/cmds/SetNpcNameCmd.java @@ -41,6 +41,9 @@ public class SetNpcNameCmd extends AbstractDevCmd { NPC npc = (NPC) target; + if (this.refuseStaticTarget(pc, npc)) + return; + String name = words[0]; NPC.UpdateName(npc, name); diff --git a/src/engine/devcmd/cmds/SetOwnerCmd.java b/src/engine/devcmd/cmds/SetOwnerCmd.java index d421e244..c8cb4c56 100644 --- a/src/engine/devcmd/cmds/SetOwnerCmd.java +++ b/src/engine/devcmd/cmds/SetOwnerCmd.java @@ -35,6 +35,9 @@ public class SetOwnerCmd extends AbstractDevCmd { _targetBuilding = getTargetAsBuilding(pcSender); + if (this.refuseStaticTarget(pcSender, _targetBuilding)) + return; + // if it's a tol change ownership of the city if (_targetBuilding.getBlueprint() != null && diff --git a/src/engine/devcmd/cmds/SetRankCmd.java b/src/engine/devcmd/cmds/SetRankCmd.java index dc0121c0..bea2193d 100644 --- a/src/engine/devcmd/cmds/SetRankCmd.java +++ b/src/engine/devcmd/cmds/SetRankCmd.java @@ -51,6 +51,10 @@ public class SetRankCmd extends AbstractDevCmd { } if (target != null) { + + if (this.refuseStaticTarget(player, target)) + return; + switch (target.getObjectType()) { case Building: Building targetBuilding = (Building) target; @@ -99,6 +103,9 @@ public class SetRankCmd extends AbstractDevCmd { return; } + if (this.refuseStaticTarget(player, targetBuilding)) + return; + Blueprint blueprint = targetBuilding.getBlueprint(); if (blueprint == null) {