Files
Server/src/engine/net/client/handlers/PlaceAssetMsgHandler.java
T
MagicBotandClaude Opus 5.5 c774746736 Read deed consumers from DeedTemplate
Deed placement (ObjectActionMsgHandler, PlaceAssetMsgHandler) reads
deed_structure_id from DeedTemplate._deeds. The deed, employment
contract and charter type checks (PlaceAssetMsgHandler,
ActivateNPCMsgHandler, GuildCreationFinalizeHandler, UseCharterMsg,
Item.serialize) read item_type through the cache template bridge.
Charters go through the shared getter rather than _deeds because
template 545 "Charter" is a CHARTER in the ITEM folder.

Parity (offline, static_item_templates vs COBJECTS, scripts/p13.py,
881 templates: all 880 DEED plus ITEM 545): 0 differences in
item_type and deed_structure_id. Every DB DEED, EMPLOYMENTCONTRACT,
PETTOTEM and FURNITUREDEED id is in the DEED folder. No value changes
expected. No pet totem or furniture deed consumer reads template
fields, so neither changes.

JSON-Refactor-Plan Task 1.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:42:54 -04:00

1372 lines
50 KiB
Java

package engine.net.client.handlers;
import engine.InterestManagement.InterestManager;
import engine.InterestManagement.RealmMap;
import engine.InterestManagement.WorldGrid;
import engine.db.archive.CityRecord;
import engine.db.archive.DataWarehouse;
import engine.gameManager.*;
import engine.math.Bounds;
import engine.math.Vector3fImmutable;
import engine.mbEnums;
import engine.mbEnums.*;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.CityZoneMsg;
import engine.net.client.msg.ClientNetMsg;
import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.PlaceAssetMsg;
import engine.net.client.msg.PlaceAssetMsg.PlacementInfo;
import engine.objects.*;
import engine.server.MBServerStatics;
import org.joda.time.DateTime;
import org.pmw.tinylog.Logger;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
* @Summary: Processes application protocol message which requests
* creation of new city / buildings from seeds/deeds in inventory.
*/
public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
// Useful constants
// ActionType 1 = client request
// 2 = Server confirms open window
// 3 = Request to place asset
// 4 = Server confirms/close window
private static final int CLIENTREQ_UNKNOWN = 1;
private static final int SERVER_OPENWINDOW = 2;
private static final int CLIENTREQ_NEWBUILDING = 3; // Request to place asset
private static final int SERVER_CLOSEWINDOW = 4;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public PlaceAssetMsgHandler() {
super();
}
private static void closePlaceAssetWindow(ClientConnection origin) {
// Action type 4 is the server telling the client to
// close the asset placement window.
// This is believed to be a confirmation message to the client
PlaceAssetMsg pam = new PlaceAssetMsg();
pam.setActionType(4);
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), pam);
DispatchManager.dispatchMsgDispatch(dispatch, DispatchChannel.SECONDARY);
}
// Default method: Validates and places all buildings that do not
// require special treatment in some fashion.
private static boolean validateTreeOfLifePlacement(PlayerCharacter playerCharacter, Realm serverRealm, Zone serverZone,
ClientConnection origin, PlaceAssetMsg msg) {
PlacementInfo placementInfo = msg.getFirstPlacementInfo();
// Your guild already owns a tree
if (playerCharacter.getGuild().getOwnedCity() != null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Your guild already owns a tree!");
return false;
}
// Validate that the player is the leader of a guild
if (GuildStatusController.isGuildLeader(playerCharacter.getGuildStatus()) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 10, ""); // Must be a guild leader
return false;
}
// Validate that the player is the leader of a guild
// that is not currently Sovereign *** BUG? Doesn't look right. isGuildLeader()?
if ((playerCharacter.getGuild().guildState != GuildState.Sworn
|| playerCharacter.getGuild().guildState != GuildState.Errant) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 17, ""); // Your is not an errant or soverign guild
return false;
}
// All trees must be placed within a continent.
if (!serverZone.isContinent()) {
PlaceAssetMsg.sendPlaceAssetError(origin, 69, ""); // Tree must be within a territory
return false;
}
if (serverRealm == null || serverRealm.getCanPlaceCities() == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 57, playerCharacter.getName()); // No building may be placed within this territory
return false;
}
// Cannot place a tree underwater
if (ZoneManager.isLocUnderwater(placementInfo.getLoc())) {
PlaceAssetMsg.sendPlaceAssetError(origin, 6, ""); // Cannot place underwater
return false;
}
//Test city not too close to any other zone
if (!ZoneManager.validTreePlacementLoc(serverZone, placementInfo.getLoc().x, placementInfo.getLoc().z)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 39, ""); // Too close to another tree
return false;
}
// Validate that Realm is not at it's city limit
if (serverRealm.isRealmFull() == true) {
int numCities;
numCities = serverRealm.getNumCities();
PlaceAssetMsg.sendPlaceAssetError(origin, 58, Integer.toString(numCities)); // This territory is full
return false;
}
return true;
}
private static boolean validateBuildingPlacement(Zone serverZone, PlaceAssetMsg msg, ClientConnection origin, PlayerCharacter player, PlacementInfo placementInfo) {
Bane bane;
if (serverZone.guild_zone == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 52, player.getName());
return false;
}
City city = ZoneManager.getCityAtLocation(placementInfo.getLoc());
if (player.getGuild().equals(city.getGuild()) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 40, player.getName());
return false;
}
if (city.isLocationOnCityGrid(placementInfo.getLoc()) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 41, player.getName());
return false;
}
// Retrieve the building details we're placing
if (serverZone.isNPCCity == true) {
PlaceAssetMsg.sendPlaceAssetError(origin, 15, ""); // Cannot place in a peace zone
return false;
}
// Errant guilds cannot place assets
if (player.getGuild().guildState == GuildState.Errant) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Only sovereign or sworn guilds may place assets.");
return false;
}
// Player must be GL or IC of a guild to place buildings.
if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false && GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 10, ""); // You must be a guild leader
return false;
}
// Cannot place a building underwater
if (ZoneManager.isLocUnderwater(placementInfo.getLoc())) {
PlaceAssetMsg.sendPlaceAssetError(origin, 6, ""); // Cannot place underwater
return false;
}
// Players cannot place buildings in mob zones.
if ((serverZone.isMacroZone() == true)
|| (serverZone.parent.isMacroZone() == true)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName()); // No building may be placed within this territory
return false;
}
Realm serverRealm = RealmMap.getRealmAtLocation(city.getLoc());
// Cannot place buildings on seafloor or other restricted realms
if (serverRealm == null || serverRealm.getCanPlaceCities() == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 57, player.getName()); // No building may be placed within this territory
return false;
}
// Cannot place assets on a dead tree
if ((serverZone.guild_zone)
&& (City.getCity(serverZone.playerCityUUID).getTOL().getRank() == -1)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Cannot place asset on dead tree until world heals");
return false;
}
// Siege equipment has its own validation and never reaches here
bane = city.getBane();
if (bane != null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 74, ""); //"Only buildings of war may be placed during a bane"
return false;
}
if (placementCollisionCheck(serverZone, origin, placementInfo)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 3, ""); // Conflict between proposed assets
return false;
}
return true;
}
private static boolean placementCollisionCheck(Zone serverZone, ClientConnection origin, PlacementInfo placementInfo) {
// Overlap check
for (Building building : serverZone.zoneBuildingSet) {
if ((building.getBlueprintUUID() != 0) && (Bounds.collide(placementInfo, building) == true)) {
// Ignore and remove from simulation if we are placing over rubble
if (building.getRank() == -1) {
if ((building.getBlueprintUUID() != 0)
&& (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE)) {
Shrine.RemoveShrineFromCacheByBuilding(building);
if (building.getCity() != null) {
}
}
building.removeFromCache();
WorldGrid.RemoveWorldObject(building);
WorldGrid.removeObject(building);
building.getParentZone().zoneBuildingSet.remove(building);
if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup().equals(BuildingGroup.BARRACK)) {
building.RemoveFromBarracksList();
}
continue;
}
PlaceAssetMsg.sendPlaceAssetError(origin, 3, ""); // Conflict between proposed assets
return true;
}
}
return false;
}
private static boolean validateCityBuildingPlacement(Zone serverZone, PlaceAssetMsg msg, ClientConnection origin, PlayerCharacter player, PlacementInfo buildingInfo) {
// Perform shared common validation first
if (validateBuildingPlacement(serverZone, msg, origin, player, buildingInfo) == false)
return false;
// Must be a player city
if (serverZone.guild_zone == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 41, player.getName()); // Cannot place outside a guild zone
return false;
}
//Test zone has a city object
City city = City.getCity(serverZone.playerCityUUID);
if (city == null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 52, ""); //"no city to associate asset with"
return false;
}
// City assets must be placed on the city grid
if (!city.isLocationOnCityGrid(buildingInfo.getLoc())) {
PlaceAssetMsg.sendPlaceAssetError(origin, 52, "");
return false;
}
// Make sure it's not an errant tree
if ((city.getGuild() == null || city.getGuild().isEmptyGuild() == true)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 18, ""); //"There are no guild trees to be found"
return false;
}
//Test player is in correct guild to place buildings
if (!player.isCSR)
if (player.getGuild().getObjectUUID() != city.getGuild().getObjectUUID()) {
PlaceAssetMsg.sendPlaceAssetError(origin, 9, ""); //You must be a guild member to place this asset
return false;
}
return true;
}
@Override
protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) {
// Member variable declaration
PlaceAssetMsg msg;
Boolean buildingCreated;
// Character location and session
PlayerCharacter playerCharacter;
PlacementInfo buildingList;
Blueprint buildingBlueprint;
// Tell compiler it's ok to trust us and parse
// what we need from the message structure
msg = (PlaceAssetMsg) baseMsg;
// Action type 3 is a client requesting to place an object
// For all other action types let's just early exit
if (msg.getActionType() != CLIENTREQ_NEWBUILDING)
return true;
// assign our character
playerCharacter = SessionManager.getPlayerCharacter(origin);
// We need to figure out what exactly the player is attempting
// to place, as some objects like tol/bane/walls are edge cases.
// So let's get the first item in their list.
buildingList = msg.getFirstPlacementInfo();
// Early exit if null building list.
if (buildingList == null) {
Logger.error("Player " + playerCharacter.getCombinedName()
+ " null building list on deed use");
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
closePlaceAssetWindow(origin);
return true;
}
Item contract = null;
for (Item inventoryItem : playerCharacter.getInventory()) {
if (inventoryItem.template.cacheTemplate.getItemType().equals(ItemType.DEED) &&
DeedTemplate._deeds.get(inventoryItem.templateID).deed_structure_id == buildingList.getBlueprintUUID()) {
contract = inventoryItem;
break;
}
}
// Grab the blueprint from the uuid in the message
buildingBlueprint = Blueprint.getBlueprint(buildingList.getBlueprintUUID());
// Early exit if blueprint can't be retrieved for the object.
if (buildingBlueprint == null) {
Logger.error("Player " + playerCharacter.getCombinedName()
+ " null blueprint UUID: " + buildingList.getBlueprintUUID() + " on deed use");
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
closePlaceAssetWindow(origin);
return true;
}
// Let's now attempt to place the building
buildingCreated = false;
// Many buildings have particular validation and
// post-creation cleanup requirements.
boolean close = true;
lock.writeLock().lock();
boolean isSiege = false;
try {
switch (buildingBlueprint.getBuildingGroup()) {
case TOL:
if (contract == null)
break;
buildingCreated = placeTreeOfLife(playerCharacter, origin, msg);
break;
case WAREHOUSE:
if (contract == null)
break;
if (!playerCharacter.charItemManager.doesCharOwnThisItem(contract.getObjectUUID()))
break;
buildingCreated = placeWarehouse(playerCharacter, origin, msg);
break;
case SIEGETENT:
case BULWARK:
if (contract == null)
break;
if (!playerCharacter.charItemManager.doesCharOwnThisItem(contract.getObjectUUID()))
break;
buildingCreated = placeSiegeEquip(playerCharacter, origin, msg);
break;
case SPIRE:
if (contract == null)
break;
if (!playerCharacter.charItemManager.doesCharOwnThisItem(contract.getObjectUUID()))
break;
buildingCreated = placeSpire(playerCharacter, origin, msg);
break;
case SHRINE:
if (contract == null)
break;
if (!playerCharacter.charItemManager.doesCharOwnThisItem(contract.getObjectUUID()))
break;
buildingCreated = placeShrine(playerCharacter, origin, msg);
break;
case BARRACK:
if (contract == null)
break;
if (!playerCharacter.charItemManager.doesCharOwnThisItem(contract.getObjectUUID()))
break;
buildingCreated = placeBarrack(playerCharacter, origin, msg);
break;
case WALLSTRAIGHT:
case WALLCORNER:
case SMALLGATE:
case ARTYTOWER:
case WALLSTAIRS:
case WALLSTRAIGHTTOWER:
buildingCreated = placeCityWalls(playerCharacter, origin, msg);
close = false;
break;
default:
if (contract == null)
break;
if (!playerCharacter.charItemManager.doesCharOwnThisItem(contract.getObjectUUID()))
break;
buildingCreated = placeSingleBuilding(playerCharacter, origin, msg);
break;
}
} catch (Exception e) {
Logger.error("PlaceAssetHandler", e.getMessage());
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
// Remove the appropriate deed.
if (buildingCreated == true)
if (contract != null) {
playerCharacter.charItemManager.delete(contract);
playerCharacter.charItemManager.updateInventory();
}
// Close the window. We're done!
//DONT CLOSE THE WINDOW IF WALL KTHANX
if (close)
closePlaceAssetWindow(origin);
return true;
}
private boolean placeSingleBuilding(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) {
PlacementInfo buildingList;
Zone serverZone;
// Retrieve the building details we're placing
buildingList = msg.getFirstPlacementInfo();
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
// Early exit if something went horribly wrong
// with locating the current or zone
if (serverZone == null) {
Logger.error("Null zone in placeSingleBuilding");
return false;
}
// Method checks validation conditions arising when placing
// buildings. Player must be on a city grid, must be
// inner council of the city's guild, etc.
if (validateBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false)
return false; // Close window here?
// Place the building
if (createStructure(playerCharacter, buildingList, serverZone) == null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return false;
}
return true;
}
private boolean placeWarehouse(PlayerCharacter player, ClientConnection origin, PlaceAssetMsg msg) {
Zone serverZone;
City cityObject;
PlacementInfo buildingList;
// Retrieve the building details we're placing
buildingList = msg.getFirstPlacementInfo();
// Setup working variables we'll need
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
// Early exit if something went horribly wrong
if (serverZone == null)
return false;
cityObject = City.getCity(serverZone.playerCityUUID);
// Early exit if something went horribly wrong
if (cityObject == null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 52, "");
return false;
}
// Method checks validation conditions arising when placing
// buildings. Player must be on a city grid, must be
// inner council of the city's guild, etc.
if (validateCityBuildingPlacement(serverZone, msg, origin, player, buildingList) == false)
return false;
if (cityObject.warehouse != null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 50, ""); //"You can only have one warehouse"
return false;
}
// Create the warehouse object and it's entry in the database
if (createWarehouse(player, msg.getFirstPlacementInfo(), serverZone) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return false;
}
return true;
}
private boolean placeSiegeEquip(PlayerCharacter player, ClientConnection origin, PlaceAssetMsg msg) {
Zone serverZone;
Building siegeBuilding;
PlacementInfo buildingList;
City serverCity;
Bane bane;
// Retrieve the building details we're placing
buildingList = msg.getFirstPlacementInfo();
// Setup working variables we'll need
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
// Early exit if something went horribly wrong
// with locating the current city and/or zone
if (serverZone == null) {
Logger.error("Error obtaining reference to zone");
return false;
}
serverCity = ZoneManager.getCityAtLocation(buildingList.getLoc());
// No valid player city found
if (serverCity == null || serverCity.getParent().guild_zone == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 52, ""); // Cannot place outisde a guild zone
return false;
}
// No bane no bow
bane = serverCity.getBane();
if (bane == null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 66, ""); // There is no bane circle to support this building of war
return false;
}
// Must belong to either attacker or defenders.
if ((player.getGuild().equals(serverCity.getBane().getOwner().getGuild()) == false)
&& (player.getGuild().equals(serverCity.getGuild()) == false)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 54, ""); // Must belong to attacker or defender
return false;
}
// Player must be GL or IC of the bane guild to place bow.
if (GuildStatusController.isGuildLeader(player.getGuildStatus()) == false
&& GuildStatusController.isInnerCouncil(player.getGuildStatus()) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 10, player.getName()); // You must be a guild leader to place this asset
return false;
}
// Attackers cannot place on grid until bane is live
if (bane.getSiegePhase() != SiegePhase.WAR &&
player.getGuild().equals(serverCity.getBane().getOwner().getGuild()) &&
serverCity.isLocationOnCityGrid(buildingList.getLoc())) {
PlaceAssetMsg.sendPlaceAssetError(origin, 53, player.getName()); // Buildings of war cannot be placed around a city grid unless there is an active bane
return false;
}
// If there is a bane placed, we limit bow placement to 2x the stone rank's worth of attacker assets
// and 1x the tree rank for defenders
if (validateSiegeLimits(player, origin, serverCity.getBane()) == false)
return false;
// Collision check (Removes rubble side effect)
if (placementCollisionCheck(serverZone, origin, buildingList)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 3, ""); // Conflict between proposed assets
return false;
}
// Create the siege Building
siegeBuilding = createStructure(player, msg.getFirstPlacementInfo(), serverZone);
// Oops something went really wrong
if (siegeBuilding == null)
return false;
// passes validation: can assign auto-protection to war asset
siegeBuilding.setProtectionState(ProtectionState.PROTECTED);
return true;
}
private boolean validateSiegeLimits(PlayerCharacter playerCharacter, ClientConnection origin, Bane bane) {
City serverCity = bane.getCity();
HashSet<AbstractWorldObject> awoList;
HashSet<AbstractWorldObject> attackerBuildings = new HashSet<>();
HashSet<AbstractWorldObject> defenderBuildings = new HashSet<>();
;
int maxAttackerAssets = serverCity.getBane().getStone().getRank() * 2;
int maxDefenderAssets = serverCity.getRank();
// Count bow for attackers and defenders
awoList = WorldGrid.getObjectsInRangePartial(serverCity, 1000, MBServerStatics.MASK_BUILDING);
for (AbstractWorldObject awo : awoList) {
Building building = (Building) awo;
if (building.getBlueprint() != null)
if (!building.getBlueprint().isSiegeEquip())
continue;
if (!building.getLoc().isInsideCircle(serverCity.getLoc(), CityBoundsType.ZONE.halfExtents))
continue;
if (building.getGuild() == null)
continue;
if (building.getGuild().isEmptyGuild())
continue;
if (!building.getGuild().equals(serverCity.getGuild()) && !building.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
continue;
if (building.getRank() < 0) {
continue;
}
if (building.getGuild().equals(serverCity.getGuild()))
defenderBuildings.add(building);
if (building.getGuild().equals(serverCity.getBane().getOwner().getGuild()))
attackerBuildings.add(building);
}
// Validate bane limits on siege assets
if (playerCharacter.getGuild().equals(serverCity.getGuild())) {
//defender attempting to place asset
if (defenderBuildings.size() >= maxDefenderAssets) {
PlaceAssetMsg.sendPlaceAssetError(origin, 62, "");
return false;
}
}
if (playerCharacter.getGuild().equals(serverCity.getBane().getStone().getGuild())) {
//attacker attempting to place asset
if (attackerBuildings.size() >= maxAttackerAssets) {
PlaceAssetMsg.sendPlaceAssetError(origin, 61, "");
return false;
}
}
// Passed validation
return true;
}
private boolean placeTreeOfLife(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) {
Realm serverRealm;
Zone serverZone;
ArrayList<AbstractGameObject> cityObjects; // MySql result set
HashMap<GameObjectType, AbstractGameObject> cityObjectMap = new HashMap<>();
PlacementInfo treeInfo;
Guild playerNation;
PlacementInfo treePlacement = msg.getFirstPlacementInfo();
Building treeObject;
City cityObject;
Zone zoneObject;
// Setup working variables we'll need
serverRealm = RealmMap.getRealmAtLocation(treePlacement.getLoc());
serverZone = ZoneManager.findSmallestZone(treePlacement.getLoc());
// Early exit if something went horribly wrong
// with locating the current realm and/or zone
if (serverRealm == null || serverZone == null)
return false;
// Method checks validation conditions arising when placing
// trees
if (validateTreeOfLifePlacement(playerCharacter, serverRealm, serverZone, origin, msg) == false)
return false;
// Retrieve tree info for the w value it's passing.
treeInfo = msg.getFirstPlacementInfo();
if (treeInfo == null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return false;
}
Vector3fImmutable plantLoc = new Vector3fImmutable(treeInfo.getLoc().x,
0,
treeInfo.getLoc().z);
cityObjects = DbManager.CityQueries.CREATE_CITY(playerCharacter.getObjectUUID(), serverZone.getObjectUUID(),
plantLoc.x - serverZone.absX, plantLoc.y,
plantLoc.z - serverZone.absZ, treeInfo.getRot().y, treeInfo.getW(), playerCharacter.getGuild().getName(), LocalDateTime.now());
// Uh oh!
if (cityObjects == null || cityObjects.isEmpty()) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return false;
}
// Assign our worker variables after figuring out what
// is what in the result set.
for (AbstractGameObject gameObject : cityObjects)
cityObjectMap.put(gameObject.getObjectType(), gameObject);
treeObject = (Building) cityObjectMap.get(GameObjectType.Building);
cityObject = (City) cityObjectMap.get(GameObjectType.City);
zoneObject = (Zone) cityObjectMap.get(GameObjectType.Zone);
// not allowed to plant a tree if ur not an errant guild.
// Desub from any previous nation.
// This should be done automatically in a method inside Guild *** Refactor
// Player is now a Sovereign guild, configure them as such.
playerCharacter.getGuild().setNation(playerCharacter.getGuild());
playerNation = playerCharacter.getGuild();
playerNation.guildState = GuildState.Sovereign;
// Update guild binds and tags
GuildManager.updateAllGuildBinds(playerNation, cityObject);
GuildManager.updateAllGuildTags(playerNation);
//load the new city on the clients
CityZoneMsg czm = new CityZoneMsg(1, treeObject.getLoc().x, treeObject.getLoc().y, treeObject.getLoc().z, cityObject.getCityName(), zoneObject, mbEnums.CityBoundsType.ZONE.halfExtents, mbEnums.CityBoundsType.ZONE.halfExtents);
DispatchManager.dispatchMsgToAll(czm);
// Set maintenance date
MaintenanceManager.setMaintDateTime(treeObject, LocalDateTime.now().plusDays(7));
// Send all the cities to the clients?
// *** Refactor : figure out how to send like, one?
City.lastCityUpdate = System.currentTimeMillis();
treeObject.setLoc(treeObject.getLoc());
// As this is a new static object set it's dirtyFlag
// so players already near it will have the object loaded.
InterestManager.setObjectDirty(treeObject);
serverRealm.addCity(cityObject.getObjectUUID());
playerNation.setCityUUID(cityObject.getObjectUUID());
// Bypass warehouse entry if we're an admin
if (playerCharacter.getAccount().status.equals(AccountStatus.ADMIN))
return true;
// Push this event to the data warehouse
CityRecord cityRecord = CityRecord.borrow(cityObject, RecordEventType.CREATE);
DataWarehouse.pushToWarehouse(cityRecord);
return true;
}
// Method validates the location we have selected for our new city
private boolean placeSpire(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) {
Zone serverZone;
Building spireBuilding;
Blueprint blueprint;
City cityObject;
PlacementInfo buildingList;
// Setup working variables we'll need
buildingList = msg.getFirstPlacementInfo();
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
// Early exit if something went horribly wrong
// with locating the current realm and/or city
if (serverZone == null)
return false;
cityObject = City.getCity(serverZone.playerCityUUID);
if (cityObject == null)
return false;
// Method checks validation conditions arising when placing
// buildings. Player must be on a city grid, must be
// inner council of the city's guild, etc.
if (validateCityBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false)
return false;
// Loop through all buildings in this city looking for a spire of the.
// same type we are placing. There can be only one of each type
int spireCount = 0;
blueprint = Blueprint.getBlueprint(msg.getFirstPlacementInfo().getBlueprintUUID());
for (Building building : serverZone.zoneBuildingSet) {
if (building.getBlueprint().getBuildingGroup() == BuildingGroup.SPIRE) {
if (building.getBlueprintUUID() == blueprint.getMeshForRank(0)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 46, ""); // "Spire of that type exists"
return false;
}
spireCount++;
}
}
// Too many spires for this tree's rank?
if (spireCount >= Blueprint.getMaxShrines(cityObject.getTOL().getRank())) {
PlaceAssetMsg.sendPlaceAssetError(origin, 45, ""); //Tree cannot support anymore spires
return false;
}
// Create the spire
spireBuilding = createStructure(playerCharacter, msg.getFirstPlacementInfo(), serverZone);
return spireBuilding != null;
}
private boolean placeShrine(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) {
Zone serverZone;
Blueprint blueprint;
City cityObject;
PlacementInfo buildingList;
// Setup working variables we'll need
buildingList = msg.getFirstPlacementInfo();
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
// Early exit if something went horribly wrong
// with locating the current realm and/or zone
if (serverZone == null)
return false;
// Method checks validation conditions arising when placing
// buildings. Player must be on a city grid, must be
// inner council of the city's guild, etc.
if (validateCityBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false)
return false;
// Loop through all buildings in this city looking for a shrine of the.
// same type we are placing. There can be only one of each type
int shrineCount = 0;
cityObject = City.getCity(serverZone.playerCityUUID);
// Cannot place shrine in abandoned city. Shrines must be owned
// by the tol owner not the person placing them.
if (cityObject.getTOL().getOwnerUUID() == 0) {
PlaceAssetMsg.sendPlaceAssetError(origin, 42, ""); //Tree cannot support anymore shrines
return false;
}
blueprint = Blueprint.getBlueprint(msg.getFirstPlacementInfo().getBlueprintUUID());
if (blueprint == null) {
return false;
}
for (Building building : serverZone.zoneBuildingSet) {
if (building.getBlueprint() == null)
continue;
if (building.getBlueprint().getBuildingGroup() == BuildingGroup.SHRINE) {
if (building.getBlueprintUUID() == blueprint.getMeshForRank(0)) {
PlaceAssetMsg.sendPlaceAssetError(origin, 43, ""); // "shrine of that type exists"
return false;
}
shrineCount++;
}
}
// Too many shrines for this tree's rank?
if (shrineCount >= Blueprint.getMaxShrines(cityObject.getTOL().getRank())) {
PlaceAssetMsg.sendPlaceAssetError(origin, 42, ""); //Tree cannot support anymore shrines
return false;
}
// Create the shrine
return createShrine((PlayerCharacter) cityObject.getTOL().getOwner(), msg.getFirstPlacementInfo(), serverZone);
}
private boolean placeBarrack(PlayerCharacter playerCharacter, ClientConnection origin, PlaceAssetMsg msg) {
Zone serverZone;
City cityObject;
PlacementInfo buildingList;
// Setup working variables
buildingList = msg.getFirstPlacementInfo();
serverZone = ZoneManager.findSmallestZone(buildingList.getLoc());
// Early exit if something went horribly wrong
// with locating the current realm and/or zone
if (serverZone == null)
return false;
// Method checks validation conditions arising when placing
// buildings. Player must be on a city grid, must be
// inner council of the city's guild, etc.
if (validateCityBuildingPlacement(serverZone, msg, origin, playerCharacter, buildingList) == false)
return false;
// Loop through all buildings in this city counting barracks .
int barracksCount = 0;
cityObject = City.getCity(serverZone.playerCityUUID);
// Cannot place barracks in abandoned city.
if (cityObject.getTOL().getOwnerUUID() == 0) {
PlaceAssetMsg.sendPlaceAssetError(origin, 42, ""); //Tree cannot support anymore shrines
return false;
}
for (Building building : serverZone.zoneBuildingSet) {
if (building.getBlueprint().getBuildingGroup() == BuildingGroup.BARRACK)
barracksCount++;
}
// Too many shrines for this tree's rank?
if (barracksCount >= cityObject.getTOL().getRank()) {
PlaceAssetMsg.sendPlaceAssetError(origin, 47, ""); //Tree cannot support anymore shrines
return false;
}
// Create the shrine
return createBarracks((PlayerCharacter) cityObject.getTOL().getOwner(), msg.getFirstPlacementInfo(), serverZone);
}
private boolean placeCityWalls(PlayerCharacter player, ClientConnection origin, PlaceAssetMsg msg) {
// Member variables
Zone serverZone;
City cityObject;
int placementCost = 0;
CharacterItemManager itemMan;
Item goldItem;
Building wallPiece;
// Setup working variables we'll need
serverZone = ZoneManager.findSmallestZone(player.getLoc());
// Early exit if something went horribly wrong
if (serverZone == null)
return false;
if (player.charItemManager.getGoldTrading() > 0) {
ErrorPopupMsg.sendErrorPopup(player, 195);
return false;
}
// Method checks validation conditions arising when placing
// buildings. Player must be on a city grid, must be
// inner council of the city's guild, etc.
if (validateCityBuildingPlacement(serverZone, msg, origin, player, msg.getFirstPlacementInfo()) == false)
return false;
cityObject = City.getCity(serverZone.playerCityUUID);
// We need to be able to access how much gold a character is carrying
itemMan = player.charItemManager;
if (itemMan == null)
return false;
goldItem = itemMan.getGoldInventory();
// Grab list of walls we're placing
ArrayList<PlacementInfo> walls = msg.getPlacementInfo();
// Character must be able to afford walls
for (PlacementInfo wall : walls) {
placementCost += PlaceAssetMsg.getWallCost(wall.getBlueprintUUID());
}
// Early exit if not enough gold in character's inventory to place walls
if (placementCost > goldItem.getNumOfItems()) {
PlaceAssetMsg.sendPlaceAssetError(origin, 28, ""); // Not enough gold
return false;
}
placementCost = 0; // reset placement cost for fix bug with wall pieces somethings not taking gold out if forced an error.
// Overlap check and wall deed verifications
for (PlacementInfo wall : walls) {
if (Blueprint.isMeshWallPiece(wall.getBlueprintUUID()) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 48, ""); //"Assets (except walls) must be placed one at a time"
continue;
}
// Ignore wall pieces not on the city grid
if (cityObject.isLocationOnCityGrid(wall.getLoc()) == false) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "Asset " + cityObject.getName() + " not on citygrid");
continue;
}
// Does this wall collide with any other building?
for (Building building : serverZone.zoneBuildingSet) {
//TODO Clean up collision with placementInfo. don't need to create the same placementinfo bounds for collision checks on each building.
if ((building.getBlueprintUUID() != 0) && (Bounds.collide(wall, building) == true)) {
if (building.getRank() == -1) {
building.removeFromCache();
WorldGrid.RemoveWorldObject(building);
WorldGrid.removeObject(building);
building.getParentZone().parent.zoneBuildingSet.remove(building);
if (building.getBlueprint() != null && building.getBlueprint().getBuildingGroup().equals(BuildingGroup.BARRACK)) {
building.RemoveFromBarracksList();
}
continue;
}
// remove gold from walls already placed before returning.
PlaceAssetMsg.sendPlaceAssetError(origin, 3, building.getName()); //"Conflict between assets"
return false;
}
}
placementCost = PlaceAssetMsg.getWallCost(wall.getBlueprintUUID());
if (!itemMan.modifyInventoryGold(-placementCost)) {
ChatManager.chatSystemInfo(player, player.getFirstName() + " can't has free moneys! no for real.. Thor.. seriously... I didnt fix it because you getting laid isnt important enough for me.");
return false;
}
// Attempt to place wall piece
wallPiece = createStructure(player, wall, serverZone);
if (wallPiece == null) {
PlaceAssetMsg.sendPlaceAssetError(origin, 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
continue;
}
// walls are auto protected
wallPiece.setProtectionState(ProtectionState.PROTECTED);
PlaceAssetMsg.sendPlaceAssetConfirmWall(origin, serverZone);
}
return true;
}
private Building createStructure(PlayerCharacter playerCharacter, PlacementInfo buildingInfo, Zone currentZone) {
Blueprint blueprint;
Building newMesh;
DateTime completionDate;
float vendorRotation;
float buildingRotation;
blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID());
if (blueprint == null) {
Logger.error("CreateStructure: DB returned null blueprint.");
return null;
}
// All siege buildings build in 15 minutes
if ((blueprint.getBuildingGroup().equals(BuildingGroup.SIEGETENT))
|| (blueprint.getBuildingGroup().equals(BuildingGroup.BULWARK)))
completionDate = DateTime.now().plusMinutes(15);
else
completionDate = DateTime.now().plusHours(blueprint.getRankTime(1));
Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone));
buildingRotation = buildingInfo.getRot().y;
vendorRotation = buildingInfo.getW();
// 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),
localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.NONE, 0, 0,
completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation);
// Make sure we have a valid mesh
if (newMesh == null) {
Logger.error("CreateStructure: DB returned null object.");
return null;
}
newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING);
MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7));
newMesh.setLoc(newMesh.getLoc());
InterestManager.setObjectDirty(newMesh);
return newMesh;
}
// Validates that player is able to place buildings
private boolean createShrine(PlayerCharacter player, PlacementInfo buildingInfo, Zone currentZone) {
Blueprint blueprint;
Building newMesh;
Shrine newShrine;
City city;
ShrineType shrineType;
if (player == null)
return false;
blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID());
if (blueprint == null) {
Logger.error("CreateShrine: DB returned null blueprint.");
return false;
}
shrineType = Shrine.getShrineTypeByBlueprintUUID(blueprint.getBlueprintUUID());
city = City.getCity(currentZone.playerCityUUID);
if (city == null)
return false;
if (!city.isLocationOnCityGrid(buildingInfo.getLoc()))
return false;
Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone));
float buildingRotation = buildingInfo.getRot().y;
float vendorRotation = buildingInfo.getW();
ArrayList<AbstractGameObject> shrineObjects = DbManager.ShrineQueries.CREATE_SHRINE(
currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0),
localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0,
DateTime.now().plusHours(blueprint.getRankTime(1)), blueprint.getMeshForRank(0), vendorRotation, buildingRotation, shrineType.name());
if (shrineObjects == null) {
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return false;
}
for (AbstractGameObject ago : shrineObjects) {
switch (ago.getObjectType()) {
case Building:
newMesh = (Building) ago;
newMesh.runAfterLoad();
newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING);
MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7));
newMesh.setLoc(newMesh.getLoc());
InterestManager.setObjectDirty(newMesh);
break;
case Shrine:
newShrine = (Shrine) ago;
newShrine.getShrineType().addShrineToServerList(newShrine);
break;
default:
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
break;
}
}
return true;
}
private boolean createBarracks(PlayerCharacter player, PlacementInfo buildingInfo, Zone currentZone) {
Blueprint blueprint;
Building newMesh;
Shrine newShrine;
City city;
if (player == null)
return false;
blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID());
if (blueprint == null) {
Logger.error("CreateShrine: DB returned null blueprint.");
return false;
}
city = City.getCity(currentZone.playerCityUUID);
if (city == null)
return false;
if (!city.isLocationOnCityGrid(buildingInfo.getLoc()))
return false;
Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone));
float buildingRotation = buildingInfo.getRot().y;
float vendorRotation = buildingInfo.getW();
DateTime completionDate = DateTime.now().plusHours(blueprint.getRankTime(1));
newMesh = DbManager.BuildingQueries.CREATE_BUILDING(
currentZone.getObjectUUID(), player.getObjectUUID(), blueprint.getName(), blueprint.getMeshForRank(0),
localLoc, 1.0f, blueprint.getMaxHealth(0), ProtectionState.PROTECTED, 0, 0,
completionDate, blueprint.getMeshForRank(0), vendorRotation, buildingRotation);
// Make sure we have a valid mesh
if (newMesh == null) {
Logger.error("CreateStructure: DB returned null object.");
return false;
}
newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING);
MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7));
newMesh.setLoc(newMesh.getLoc());
InterestManager.setObjectDirty(newMesh);
return true;
}
private boolean createWarehouse(PlayerCharacter player, PlacementInfo buildingInfo, Zone currentZone) {
Blueprint blueprint;
Building newMesh = null;
Building warehouseBuilding;
blueprint = Blueprint.getBlueprint(buildingInfo.getBlueprintUUID());
if (blueprint == null) {
Logger.error("CreateWarehouse: DB returned null blueprint.");
return false;
}
Vector3fImmutable localLoc = new Vector3fImmutable(ZoneManager.worldToLocal(buildingInfo.getLoc(), currentZone));
float buildingRotation = buildingInfo.getRot().y;
float vendorRotation = buildingInfo.getW();
City city = City.getCity(currentZone.playerCityUUID);
warehouseBuilding = DbManager.BuildingQueries.CREATE_BUILDING(
currentZone.getObjectUUID(), 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);
if (warehouseBuilding == null) {
PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity");
return false;
}
Warehouse warehouse = new Warehouse(warehouseBuilding);
Warehouse.destroyedWarehouses.values().removeIf(destroyed -> destroyed.city == city);
city.warehouse = warehouse;
DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse);
// Load the building into the simulation
warehouseBuilding.setObjectTypeMask(MBServerStatics.MASK_BUILDING);
MaintenanceManager.setMaintDateTime(warehouseBuilding, LocalDateTime.now().plusDays(7));
warehouseBuilding.setLoc(warehouseBuilding.getLoc());
InterestManager.setObjectDirty(warehouseBuilding);
warehouseBuilding.runAfterLoad();
if (city == null)
return true;
return true;
}
}