From d586536c7485f8beab332925cefa7a33b46f6983 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Mon, 26 Oct 2026 16:02:00 -0400 Subject: [PATCH] Create player buildings in memory with a single DB write Building gets a no-arg constructor, a createBuilding factory (in the style of Mob.createMob) and persistBuilding, which inserts the row through building_CREATE, sets the returned UID, caches the object and runs runAfterLoad. The GET_BUILDINGBYUUID re-select after the insert is gone. PlaceAssetMsgHandler (createStructure, createBarracks, createWarehouse), Bane and AddBuildingCmd move onto it. Value changes: - floor/level start at -1 and the other unset fields at the obj_building column defaults, same as the reloaded row. - upgradeDateTime keeps milliseconds instead of the DATETIME-rounded value (sub-second difference in the upgrade job time). - Warehouse placement no longer runs runAfterLoad twice, which submitted the warehouse upgrade job twice. Co-Authored-By: Claude Opus 5.5 --- src/engine/db/handlers/dbBuildingHandler.java | 41 ++++++++++++ src/engine/devcmd/cmds/AddBuildingCmd.java | 9 ++- .../client/handlers/PlaceAssetMsgHandler.java | 19 +++--- src/engine/objects/Bane.java | 6 +- src/engine/objects/Building.java | 63 +++++++++++++++++++ 5 files changed, 120 insertions(+), 18 deletions(-) diff --git a/src/engine/db/handlers/dbBuildingHandler.java b/src/engine/db/handlers/dbBuildingHandler.java index f14ea4fa..4dbba492 100644 --- a/src/engine/db/handlers/dbBuildingHandler.java +++ b/src/engine/db/handlers/dbBuildingHandler.java @@ -85,6 +85,47 @@ public class dbBuildingHandler extends dbHandlerBase { return building; } + public int PERSIST(Building building) { + + int objectUUID = 0; + + try (Connection connection = DbManager.getConnection(); + PreparedStatement preparedStatement = connection.prepareStatement("CALL `building_CREATE`(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ,? ,? ,?, ?);")) { + + preparedStatement.setInt(1, building.parentZoneUUID); + preparedStatement.setInt(2, building.getOwnerUUID()); + preparedStatement.setString(3, building.getName()); + preparedStatement.setInt(4, building.meshUUID); + preparedStatement.setFloat(5, building.statLat); + preparedStatement.setFloat(6, building.statAlt); + preparedStatement.setFloat(7, building.statLon); + preparedStatement.setFloat(8, building.getMeshScale().x); + preparedStatement.setInt(9, (int) building.getHealth()); + preparedStatement.setString(10, building.getProtectionState().name()); + preparedStatement.setInt(11, building.getStrongboxValue()); + preparedStatement.setInt(12, building.getRank()); + + if (building.upgradeDateTime != null) + preparedStatement.setTimestamp(13, java.sql.Timestamp.valueOf(building.upgradeDateTime)); + else + preparedStatement.setNull(13, java.sql.Types.DATE); + + preparedStatement.setInt(14, building.getBlueprintUUID()); + preparedStatement.setFloat(15, building.getw()); + preparedStatement.setFloat(16, building.getRot().y); + + ResultSet rs = preparedStatement.executeQuery(); + + if (rs.next()) + objectUUID = (int) rs.getLong("UID"); + + } catch (SQLException e) { + Logger.error(e); + } + + return objectUUID; + } + public boolean DELETE_FROM_DATABASE(final Building b) { return removeFromBuildings(b); diff --git a/src/engine/devcmd/cmds/AddBuildingCmd.java b/src/engine/devcmd/cmds/AddBuildingCmd.java index 64dc24d4..cb73b091 100644 --- a/src/engine/devcmd/cmds/AddBuildingCmd.java +++ b/src/engine/devcmd/cmds/AddBuildingCmd.java @@ -90,11 +90,10 @@ public class AddBuildingCmd extends AbstractDevCmd { blueprintUUID = blueprint.getBlueprintUUID(); } - Building building = DbManager.BuildingQueries. - CREATE_BUILDING( - zone.getObjectUUID(), 0, buildingName, ID, - localLoc, 1.0f, 0, ProtectionState.PROTECTED, 0, rank, - null, blueprintUUID, w, rot.y); + Building building = Building.persistBuilding(Building.createBuilding( + zone, 0, buildingName, ID, + localLoc, 1.0f, 0, ProtectionState.PROTECTED, 0, rank, + null, blueprintUUID, w, rot.y)); if (building == null) { diff --git a/src/engine/net/client/handlers/PlaceAssetMsgHandler.java b/src/engine/net/client/handlers/PlaceAssetMsgHandler.java index d44e1f73..29ea2451 100644 --- a/src/engine/net/client/handlers/PlaceAssetMsgHandler.java +++ b/src/engine/net/client/handlers/PlaceAssetMsgHandler.java @@ -1180,10 +1180,10 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler { // if W return is negative, this is a -90 rotation not a 90? - newMesh = DbManager.BuildingQueries.CREATE_BUILDING( - currentZone.getObjectUUID(), playerCharacter.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), + newMesh = Building.persistBuilding(Building.createBuilding( + currentZone, playerCharacter.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.NONE, 0, 0, - completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation); + completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation)); // Make sure we have a valid mesh @@ -1303,10 +1303,10 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler { DateTime completionDate = DateTime.now().plusHours(blueprint.getRankTime(1)); - newMesh = DbManager.BuildingQueries.CREATE_BUILDING( - currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), + newMesh = Building.persistBuilding(Building.createBuilding( + currentZone, player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0, - completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation); + completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation)); // Make sure we have a valid mesh if (newMesh == null) { @@ -1342,10 +1342,10 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler { City city = City.getCity(currentZone.playerCityUUID); - warehouseBuilding = DbManager.BuildingQueries.CREATE_BUILDING( - currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), + warehouseBuilding = Building.persistBuilding(Building.createBuilding( + currentZone, player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0), localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.NONE, 0, 0, - DateTime.now().plusHours(blueprint.getRankTime(1)), blueprint.getMeshForRank(0), vendorRotation, buildingRotation); + DateTime.now().plusHours(blueprint.getRankTime(1)), blueprint.getMeshForRank(0), vendorRotation, buildingRotation)); if (warehouseBuilding == null) { PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); @@ -1363,7 +1363,6 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler { MaintenanceManager.setMaintDateTime(warehouseBuilding, LocalDateTime.now().plusDays(7)); warehouseBuilding.setLoc(warehouseBuilding.getLoc()); InterestManager.setObjectDirty(warehouseBuilding); - warehouseBuilding.runAfterLoad(); if (city == null) return true; diff --git a/src/engine/objects/Bane.java b/src/engine/objects/Bane.java index 8a0a7a90..43951735 100644 --- a/src/engine/objects/Bane.java +++ b/src/engine/objects/Bane.java @@ -210,10 +210,10 @@ public final class Bane { return false; } - Building stone = DbManager.BuildingQueries.CREATE_BUILDING( - cityZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getBlueprintUUID(), + Building stone = Building.persistBuilding(Building.createBuilding( + cityZone, player.getObjectUUID(), blueprint.getName(), blueprint.getBlueprintUUID(), localLocation, 1.0f, blueprint.getMaxHealth(rank), ProtectionState.PROTECTED, 0, rank, - null, blueprint.getBlueprintUUID(), 1, 0.0f); + null, blueprint.getBlueprintUUID(), 1, 0.0f)); if (stone == null) { PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); diff --git a/src/engine/objects/Building.java b/src/engine/objects/Building.java index 09d69d51..385e3470 100644 --- a/src/engine/objects/Building.java +++ b/src/engine/objects/Building.java @@ -32,10 +32,12 @@ import engine.net.ByteBufferWriter; import engine.net.client.msg.ApplyBuildingEffectMsg; import engine.net.client.msg.UpdateObjectMsg; import engine.server.MBServerStatics; +import org.joda.time.DateTime; import org.pmw.tinylog.Logger; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; @@ -99,6 +101,14 @@ public class Building extends AbstractWorldObject { private ConcurrentHashMap condemned; private ArrayList children = null; + /** + * No Id Constructor + */ + + public Building() { + super(); + } + /** * ResultSet Constructor */ @@ -173,6 +183,59 @@ public class Building extends AbstractWorldObject { } } + // Defaults mirror the obj_building column defaults so a new building matches a reloaded one. + public static Building createBuilding(Zone parentZone, int ownerUUID, String name, int meshUUID, + Vector3fImmutable localLoc, float scale, int currentHP, + ProtectionState protectionState, int currentGold, int rank, + DateTime upgradeDate, int blueprintUUID, float w, float rotY) { + + Building building = new Building(); + + building.setObjectTypeMask(MBServerStatics.MASK_BUILDING); + building.gridObjectType = GridObjectType.STATIC; + building.parentZoneUUID = parentZone.getObjectUUID(); + building.ownerUUID = ownerUUID; + building.name = name; + building.meshUUID = meshUUID; + building.blueprintUUID = blueprintUUID; + building.statLat = localLoc.x; + building.statAlt = localLoc.y; + building.statLon = localLoc.z; + building.meshScale = new Vector3f(scale, scale, scale); + building.setHealth(currentHP); + building.protectionState = protectionState; + building._strongboxValue = currentGold; + building.maxGold = 15000000; + building.rank = rank; + building.w = w; + building.setRot(new Vector3f(0f, rotY, 0f)); + building.parentBuildingID = 0; + building.floor = -1; + building.level = -1; + + if (upgradeDate != null) + building.upgradeDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(upgradeDate.getMillis()), ZoneId.systemDefault()); + + return building; + } + + // Writes a new player building once and loads it into the simulation; returns null if the insert failed. + public static Building persistBuilding(Building building) { + + int objectUUID; + + objectUUID = DbManager.BuildingQueries.PERSIST(building); + + if (objectUUID <= 0) + return null; + + building.setObjectUUID(objectUUID); + DbManager.addToCache(building); + building.runAfterLoad(); + + return building; + } + /* * Getters */