package engine.net.client.handlers; import engine.Enum; import engine.Enum.*; import engine.InterestManagement.InterestManager; import engine.InterestManagement.RealmMap; import engine.InterestManagement.WorldGrid; import engine.db.archive.CityRecord; import engine.db.archive.DataWarehouse; import engine.exception.MsgSendException; import engine.gameManager.*; import engine.math.Bounds; import engine.math.Vector3fImmutable; import engine.net.Dispatch; import engine.net.DispatchMessage; 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(PlaceAssetMsg.class); } 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); DispatchMessage.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().getGuildState() != GuildState.Sworn || playerCharacter.getGuild().getGuildState() != 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) { 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().getGuildState() == 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; } 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) throws MsgSendException { // 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.getItemBase().getUseID() == 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.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) break; buildingCreated = placeWarehouse(playerCharacter, origin, msg); break; case SIEGETENT: case BULWARK: if (contract == null) break; if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) break; buildingCreated = placeSiegeEquip(playerCharacter, origin, msg); break; case SPIRE: if (contract == null) break; if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) break; buildingCreated = placeSpire(playerCharacter, origin, msg); break; case SHRINE: if (contract == null) break; if (!playerCharacter.getCharItemManager().doesCharOwnThisItem(contract.getObjectUUID())) break; buildingCreated = placeShrine(playerCharacter, origin, msg); break; case BARRACK: if (contract == null) break; if (!playerCharacter.getCharItemManager().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.getCharItemManager().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.getCharItemManager().delete(contract); playerCharacter.getCharItemManager().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.getWarehouse() != 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 awoList; HashSet attackerBuildings = new HashSet<>(); HashSet 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 cityObjects; // MySql result set HashMap 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(), serverRealm.getRealmID(), 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.setGuildState(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, Enum.CityBoundsType.ZONE.halfExtents, Enum.CityBoundsType.ZONE.halfExtents); DispatchMessage.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.getCharItemManager().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.getCharItemManager(); if (itemMan == null) return false; goldItem = itemMan.getGoldInventory(); // Grab list of walls we're placing ArrayList 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 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; ArrayList warehouseObjects; 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(); warehouseObjects = DbManager.WarehouseQueries.CREATE_WAREHOUSE( 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 (warehouseObjects == null) { PlaceAssetMsg.sendPlaceAssetError(player.getClientConnection(), 1, "A Serious error has occurred. Please post details for to ensure transaction integrity"); return false; } // Load the building into the simulation for (AbstractGameObject ago : warehouseObjects) { if (ago.getObjectType() == GameObjectType.Building) { newMesh = (Building) ago; newMesh.setObjectTypeMask(MBServerStatics.MASK_BUILDING); MaintenanceManager.setMaintDateTime(newMesh, LocalDateTime.now().plusDays(7)); newMesh.setLoc(newMesh.getLoc()); InterestManager.setObjectDirty(newMesh); newMesh.runAfterLoad(); } else if (ago.getObjectType() == GameObjectType.Warehouse) { Warehouse warehouse = (Warehouse) ago; City city = City.getCity(currentZone.playerCityUUID); if (city == null) return true; city.setWarehouseBuildingID(newMesh.getObjectUUID()); Warehouse.warehouseByBuildingUUID.put(newMesh.getObjectUUID(), warehouse); } } return true; } }