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 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-26 16:02:10 -04:00
1 parent 15437246ce
commit d586536c74
5 files changed
+120 -18

No files matched your search

@@ -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);
+4 -5
View File
@@ -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) {
@@ -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;
+3 -3
View File
@@ -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");
+63
View File
@@ -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<Integer, Condemned> condemned;
private ArrayList<Building> 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
*/