forked from MagicBane/Server
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
868 lines
25 KiB
868 lines
25 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
|
|
package engine.server.world; |
|
|
|
import engine.Enum; |
|
import engine.Enum.DispatchChannel; |
|
import engine.Enum.MinionType; |
|
import engine.Enum.SupportMsgType; |
|
import engine.InterestManagement.HeightMap; |
|
import engine.InterestManagement.RealmMap; |
|
import engine.InterestManagement.WorldGrid; |
|
import engine.db.archive.DataWarehouse; |
|
import engine.db.handlers.dbPowerHandler; |
|
import engine.exception.MsgSendException; |
|
import engine.gameManager.*; |
|
import engine.job.JobContainer; |
|
import engine.job.JobScheduler; |
|
import engine.jobs.LogoutCharacterJob; |
|
import engine.mobileAI.Threads.MobAIThread; |
|
import engine.mobileAI.Threads.MobRespawnThread; |
|
import engine.net.Dispatch; |
|
import engine.net.DispatchMessage; |
|
import engine.net.ItemProductionManager; |
|
import engine.net.Network; |
|
import engine.net.client.ClientConnection; |
|
import engine.net.client.ClientConnectionManager; |
|
import engine.net.client.ClientMessagePump; |
|
import engine.net.client.Protocol; |
|
import engine.net.client.msg.RefinerScreenMsg; |
|
import engine.net.client.msg.TrainerInfoMsg; |
|
import engine.net.client.msg.UpdateStateMsg; |
|
import engine.net.client.msg.chat.ChatSystemMsg; |
|
import engine.objects.*; |
|
import engine.server.MBServerStatics; |
|
import engine.util.ThreadUtils; |
|
import engine.workthreads.DisconnectTrashTask; |
|
import engine.workthreads.HourlyJobThread; |
|
import engine.workthreads.PurgeOprhans; |
|
import engine.workthreads.WarehousePushThread; |
|
import org.pmw.tinylog.Configurator; |
|
import org.pmw.tinylog.Level; |
|
import org.pmw.tinylog.Logger; |
|
import org.pmw.tinylog.labelers.TimestampLabeler; |
|
import org.pmw.tinylog.policies.StartupPolicy; |
|
import org.pmw.tinylog.writers.RollingFileWriter; |
|
|
|
import java.io.*; |
|
import java.net.DatagramSocket; |
|
import java.net.InetAddress; |
|
import java.net.URL; |
|
import java.nio.file.Files; |
|
import java.nio.file.Paths; |
|
import java.time.Duration; |
|
import java.time.LocalDateTime; |
|
import java.util.ArrayList; |
|
import java.util.List; |
|
import java.util.Timer; |
|
|
|
import static engine.gameManager.SimulationManager.SERVERHEARTBEAT; |
|
import static java.lang.System.exit; |
|
|
|
public class WorldServer { |
|
|
|
public static int worldMapID = Integer.parseInt(ConfigManager.MB_WORLD_MAPID.getValue()); |
|
public static int worldRealmMap = Integer.parseInt(ConfigManager.MB_WORLD_REALMMAP.getValue()); |
|
|
|
public static int worldUUID = 1; // Root object in database |
|
public static Enum.AccountStatus worldAccessLevel = Enum.AccountStatus.valueOf(ConfigManager.MB_WORLD_ACCESS_LVL.getValue()); |
|
private static LocalDateTime bootTime = LocalDateTime.now(); |
|
public boolean isRunning = false; |
|
|
|
// Member variable declaration |
|
|
|
public WorldServer() { |
|
super(); |
|
} |
|
|
|
public static void main(String[] args) { |
|
|
|
WorldServer worldServer; |
|
|
|
// Configure TinyLogger |
|
Configurator.defaultConfig() |
|
.addWriter(new RollingFileWriter("logs/world/world.txt", 30, new TimestampLabeler(), new StartupPolicy())) |
|
.level(Level.DEBUG) |
|
.formatPattern("{level} {date:yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {class}.{method}({line}) : {message}") |
|
.writingThread("main", 2) |
|
.activate(); |
|
|
|
if (ConfigManager.init() == false) { |
|
Logger.error("ABORT! Missing config entry!"); |
|
return; |
|
} |
|
|
|
try { |
|
|
|
worldServer = new WorldServer(); |
|
|
|
ConfigManager.serverType = Enum.ServerType.WORLDSERVER; |
|
ConfigManager.worldServer = worldServer; |
|
ConfigManager.handler = new ClientMessagePump(worldServer); |
|
|
|
worldServer.init(); |
|
|
|
int retVal = worldServer.exec(); |
|
|
|
if (retVal != 0) |
|
Logger.error( |
|
".exec() returned value: '" + retVal); |
|
exit(retVal); |
|
|
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
exit(1); |
|
} |
|
} |
|
|
|
public static void trainerInfo(TrainerInfoMsg msg, ClientConnection origin) { |
|
|
|
NPC npc = NPC.getFromCache(msg.getObjectID()); |
|
float sellPercent = 1; |
|
|
|
if (npc != null) { |
|
|
|
if (origin.getPlayerCharacter() != null) |
|
sellPercent = npc.getSellPercent(origin.getPlayerCharacter()); |
|
else |
|
sellPercent = npc.getSellPercent(); |
|
|
|
msg.setTrainPercent(sellPercent); //TrainMsg.getTrainPercent(npc)); |
|
} |
|
|
|
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
|
|
} |
|
|
|
public static void refinerScreen(RefinerScreenMsg msg, ClientConnection origin) |
|
throws MsgSendException { |
|
|
|
NPC npc = NPC.getFromCache(msg.getNpcID()); |
|
|
|
if (npc != null) |
|
msg.setUnknown02(0); //cost to refine? |
|
|
|
Dispatch dispatch = Dispatch.borrow(origin.getPlayerCharacter(), msg); |
|
DispatchMessage.dispatchMsgDispatch(dispatch, Enum.DispatchChannel.SECONDARY); |
|
} |
|
|
|
public static String getUptimeString() { |
|
|
|
String outString = null; |
|
java.time.Duration uptimeDuration; |
|
String newLine = System.getProperty("line.separator"); |
|
|
|
try { |
|
outString = "[LUA_UPTIME()]" + newLine; |
|
uptimeDuration = java.time.Duration.between(LocalDateTime.now(), WorldServer.bootTime); |
|
long uptimeSeconds = Math.abs(uptimeDuration.getSeconds()); |
|
String uptime = String.format("%d hours %02d minutes %02d seconds", uptimeSeconds / 3600, (uptimeSeconds % 3600) / 60, (uptimeSeconds % 60)); |
|
outString += "uptime: " + uptime; |
|
outString += " pop: " + SessionManager.getActivePlayerCharacterCount() + " max pop: " + SessionManager._maxPopulation; |
|
} catch (Exception e) { |
|
Logger.error("Failed to build string"); |
|
} |
|
return outString; |
|
} |
|
|
|
public static void writePopulationFile() { |
|
|
|
int population = SessionManager.getActivePlayerCharacterCount(); |
|
try { |
|
|
|
|
|
File populationFile = new File(ConfigManager.DEFAULT_DATA_DIR + ConfigManager.MB_WORLD_NAME.getValue().replaceAll("'", "") + ".pop"); |
|
FileWriter fileWriter; |
|
|
|
try { |
|
fileWriter = new FileWriter(populationFile, false); |
|
fileWriter.write(Integer.toString(population)); |
|
fileWriter.close(); |
|
} catch (IOException e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} |
|
} |
|
|
|
private int exec() { |
|
|
|
LocalDateTime nextHeartbeatTime = LocalDateTime.now(); |
|
LocalDateTime nextPopulationFileTime = LocalDateTime.now(); |
|
LocalDateTime nextFlashTrashCheckTime = LocalDateTime.now(); |
|
LocalDateTime nextHourlyJobTime = LocalDateTime.now().withMinute(0).withSecond(0).plusHours(1); |
|
LocalDateTime nextWareHousePushTime = LocalDateTime.now(); |
|
|
|
// Begin execution of main game loop |
|
|
|
this.isRunning = true; |
|
|
|
while (true) { |
|
|
|
if (LocalDateTime.now().isAfter(nextHeartbeatTime)) { |
|
SERVERHEARTBEAT.tick(); |
|
nextHeartbeatTime = LocalDateTime.now().plusNanos(50000000); |
|
} |
|
|
|
if (LocalDateTime.now().isAfter(nextPopulationFileTime)) { |
|
writePopulationFile(); |
|
nextPopulationFileTime = LocalDateTime.now().plusMinutes(1); |
|
} |
|
|
|
if (LocalDateTime.now().isAfter(nextFlashTrashCheckTime)) { |
|
processFlashFile(); |
|
processTrashFile(); |
|
nextFlashTrashCheckTime = LocalDateTime.now().plusSeconds(15); |
|
} |
|
|
|
if (LocalDateTime.now().isAfter(nextHourlyJobTime)) { |
|
Thread hourlyJobThread = new Thread(new HourlyJobThread()); |
|
hourlyJobThread.setName("hourlyJob"); |
|
hourlyJobThread.start(); |
|
nextHourlyJobTime = LocalDateTime.now().withMinute(0).withSecond(0).plusHours(1); |
|
} |
|
|
|
if (LocalDateTime.now().isAfter(nextWareHousePushTime)) { |
|
Thread warehousePushThread = new Thread(new WarehousePushThread()); |
|
warehousePushThread.setName("warehousePush"); |
|
warehousePushThread.start(); |
|
nextWareHousePushTime = LocalDateTime.now().plusMinutes(15); |
|
} |
|
|
|
ThreadUtils.sleep(50); |
|
} |
|
} |
|
|
|
private void initClientConnectionManager() { |
|
|
|
try { |
|
|
|
String name = ConfigManager.MB_WORLD_NAME.getValue(); |
|
|
|
if (ConfigManager.MB_EXTERNAL_ADDR.getValue().equals("0.0.0.0")) { |
|
|
|
// Autoconfigure External IP address. Only used in loginserver but useful |
|
// here for bootstrap display |
|
|
|
Logger.info("AUTOCONFIG EXTERNAL IP ADDRESS"); |
|
URL whatismyip = new URL("http://checkip.amazonaws.com"); |
|
|
|
BufferedReader in = new BufferedReader(new InputStreamReader( |
|
whatismyip.openStream())); |
|
ConfigManager.MB_EXTERNAL_ADDR.setValue(in.readLine()); |
|
} |
|
|
|
if (ConfigManager.MB_BIND_ADDR.getValue().equals("0.0.0.0")) { |
|
|
|
try (final DatagramSocket socket = new DatagramSocket()) { |
|
socket.connect(InetAddress.getByName("8.8.8.8"), 10002); |
|
ConfigManager.MB_BIND_ADDR.setValue(socket.getLocalAddress().getHostAddress()); |
|
} |
|
|
|
} |
|
|
|
Logger.info("External address: " + ConfigManager.MB_EXTERNAL_ADDR.getValue() + ":" + ConfigManager.MB_WORLD_PORT.getValue()); |
|
Logger.info("Internal address: " + ConfigManager.MB_BIND_ADDR.getValue() + ":" + ConfigManager.MB_WORLD_PORT.getValue()); |
|
|
|
InetAddress addy = InetAddress.getByName(ConfigManager.MB_BIND_ADDR.getValue()); |
|
int port = Integer.parseInt(ConfigManager.MB_WORLD_PORT.getValue()); |
|
|
|
ClientConnectionManager connectionManager = new ClientConnectionManager(name + ".ClientConnMan", addy, |
|
port); |
|
connectionManager.startup(); |
|
|
|
} catch (IOException e) { |
|
Logger.error("Exception while creating a ClientConnectionManager."); |
|
} |
|
} |
|
|
|
private boolean init() { |
|
|
|
Logger.info("MAGICBANE SERVER GREETING:"); |
|
Logger.info(ConfigManager.MB_WORLD_GREETING.getValue()); |
|
|
|
Logger.info("Initialize network protocol"); |
|
Protocol.initProtocolLookup(); |
|
|
|
Logger.info("Initialize database layer"); |
|
initDatabaselayer(); |
|
|
|
Logger.info("Starting network Dispatcher"); |
|
DispatchMessage.startMessagePump(); |
|
|
|
Logger.info("Setting cross server session behavior"); |
|
SessionManager.setCrossServerBehavior(1); // Sets cross server behavior |
|
|
|
Logger.info("Starting Item Production thread"); |
|
ItemProductionManager.ITEMPRODUCTIONMANAGER.startMessagePump(); |
|
|
|
Logger.info("Initializing Errant Guild"); |
|
Guild.getErrantGuild(); |
|
|
|
Logger.info("Initializing PowersManager."); |
|
PowersManager.initPowersManager(true); |
|
|
|
Logger.info("Initializing granted Skills for Runes"); |
|
DbManager.SkillsBaseQueries.LOAD_ALL_RUNE_SKILLS(); |
|
|
|
Logger.info("Initializing Player Friends"); |
|
DbManager.PlayerCharacterQueries.LOAD_PLAYER_FRIENDS(); |
|
|
|
Logger.info("Initializing NPC Profits"); |
|
DbManager.NPCQueries.LOAD_NPC_PROFITS(); |
|
|
|
Logger.info("Initializing Petition Table"); |
|
DbManager.PetitionQueries.CREATE_PETITION_TABLE(); |
|
|
|
Logger.info("Initializing MeshBounds"); |
|
MeshBounds.InitializeBuildingBounds(); |
|
|
|
Logger.info("Loading ItemBases"); |
|
ItemBase.loadAllItemBases(); |
|
|
|
Logger.info("Loading PromotionClasses"); |
|
DbManager.PromotionQueries.GET_ALL_PROMOTIONS(); |
|
|
|
Logger.info("Loading NPC and Mob Rune Sets"); |
|
NPCManager.LoadAllRuneSets(); |
|
|
|
Logger.info("Loading Booty Sets"); |
|
LootManager._bootySetMap = DbManager.LootQueries.LOAD_BOOTY_TABLES(); |
|
|
|
// Load new loot system |
|
Logger.info("Initializing Loot Manager"); |
|
LootManager.init(); |
|
|
|
RuneBaseAttribute.LoadAllAttributes(); |
|
RuneBase.LoadAllRuneBases(); |
|
BaseClass.LoadAllBaseClasses(); |
|
Race.loadAllRaces(); |
|
RuneBaseEffect.LoadRuneBaseEffects(); |
|
Logger.info("Loading MobBases."); |
|
DbManager.MobBaseQueries.GET_ALL_MOBBASES(); |
|
|
|
Logger.info("Loading Mob Powers"); |
|
PowersManager.AllMobPowers = dbPowerHandler.LOAD_MOB_POWERS(); |
|
|
|
Logger.info("Loading item enchants"); |
|
DbManager.LootQueries.LOAD_ENCHANT_VALUES(); |
|
|
|
Logger.info("Loading zone extent cache"); |
|
DbManager.ZoneQueries.LOAD_ZONE_EXTENTS(); |
|
|
|
Logger.info("Loading Realms"); |
|
Realm.loadAllRealms(); |
|
|
|
Logger.info("Loading RealmMap"); |
|
RealmMap.loadRealmImageMap(); |
|
|
|
Logger.info("Loading Kits"); |
|
DbManager.KitQueries.GET_ALL_KITS(); |
|
|
|
Logger.info("Loading World Grid"); |
|
WorldGrid.InitializeGridObjects(); |
|
|
|
Logger.info("Starting InterestManager."); |
|
WorldGrid.startLoadJob(); |
|
|
|
Logger.info("Loading blueprint data."); |
|
StaticColliders.loadAllStaticColliders(); |
|
BuildingRegions.loadAllStaticColliders(); |
|
Blueprint.loadAllDoorNumbers(); |
|
Blueprint.loadAllBlueprints(); |
|
|
|
Logger.info("Initializing Heightmap data"); |
|
HeightMap.loadAlHeightMaps(); |
|
|
|
Logger.info("Loading Race data"); |
|
Enum.RaceType.initRaceTypeTables(); |
|
Race.loadAllRaces(); |
|
|
|
Logger.info("Loading building slot/stuck location data."); |
|
BuildingLocation.loadBuildingLocations(); |
|
|
|
// Starting before loading of structures/guilds/characters |
|
// so the database connections are available to write |
|
// historical data. |
|
|
|
Logger.info("Starting Data Warehouse"); |
|
DataWarehouse.bootStrap(); |
|
|
|
Logger.info("Loading Minion Bases."); |
|
MinionType.InitializeMinions(); |
|
|
|
Logger.info("Loading Pirate Names."); |
|
NPCManager.loadAllPirateNames(); |
|
|
|
Logger.info("Loading Support Types"); |
|
SupportMsgType.InitializeSupportMsgType(); |
|
|
|
//Load Buildings, Mobs and NPCs for server |
|
|
|
getWorldBuildingsMobsNPCs(); |
|
|
|
// Configure realms for serialization |
|
// Doing this after the world is loaded |
|
|
|
Logger.info("Configuring realm serialization data"); |
|
Realm.configureAllRealms(); |
|
|
|
Logger.info("Loading Mine data."); |
|
Mine.loadAllMines(); |
|
|
|
Logger.info("Loading Shrine data."); |
|
DbManager.ShrineQueries.LOAD_ALL_SHRINES(); |
|
|
|
Logger.info("Initialize Resource type lookup"); |
|
Enum.ResourceType.InitializeResourceTypes(); |
|
|
|
Logger.info("Loading Warehouse data."); |
|
DbManager.WarehouseQueries.LOAD_ALL_WAREHOUSES(); |
|
|
|
Logger.info("Loading Runegate data."); |
|
Runegate.loadAllRunegates(); |
|
|
|
Logger.info("Loading Max Skills for Trainers"); |
|
DbManager.SkillsBaseQueries.LOAD_ALL_MAX_SKILLS_FOR_CONTRACT(); |
|
|
|
//pick a startup Hotzone |
|
ZoneManager.generateAndSetRandomHotzone(); |
|
|
|
Logger.info("Loading All Players from database to Server Cache"); |
|
long start = System.currentTimeMillis(); |
|
|
|
try { |
|
DbManager.PlayerCharacterQueries.GET_ALL_CHARACTERS(); |
|
} catch (Exception e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
long end = System.currentTimeMillis(); |
|
|
|
Logger.info("Loading All Players took " + (end - start) + " ms."); |
|
|
|
ItemProductionManager.ITEMPRODUCTIONMANAGER.initialize(); |
|
|
|
Logger.info("Loading Player Heraldries"); |
|
DbManager.PlayerCharacterQueries.LOAD_HERALDY(); |
|
|
|
Logger.info("Running Heraldry Audit for Deleted Players"); |
|
Heraldry.AuditHeraldry(); |
|
|
|
//intiate mob ai thread |
|
Logger.info("Starting Mob AI Thread"); |
|
MobAIThread.startAIThread(); |
|
|
|
for (Zone zone : ZoneManager.getAllZones()) { |
|
if (zone.getHeightMap() != null) { |
|
if (zone.getHeightMap().getBucketWidthX() == 0) { |
|
System.out.println("Zone load num: " + zone.getLoadNum() + " has no bucket width"); |
|
} |
|
} |
|
} |
|
|
|
Logger.info("World data loaded."); |
|
|
|
//set default accesslevel for server *** Refactor who two separate variables? |
|
MBServerStatics.accessLevel = worldAccessLevel; |
|
Logger.info("Default access level set to " + MBServerStatics.accessLevel); |
|
|
|
Logger.info("Initializing Network"); |
|
Network.init(); |
|
|
|
Logger.info("Initializing Client Connection Manager"); |
|
initClientConnectionManager(); |
|
|
|
//intiate mob respawn thread |
|
Logger.info("Starting Mob Respawn Thread"); |
|
MobRespawnThread.startRespawnThread(); |
|
|
|
// Run maintenance |
|
|
|
MaintenanceManager.dailyMaintenance(); |
|
|
|
Logger.info("Starting Orphan Item Purge"); |
|
PurgeOprhans.startPurgeThread(); |
|
|
|
// Open/Close mines for the current window |
|
|
|
Logger.info("Processing mine window."); |
|
HourlyJobThread.processMineWindow(); |
|
|
|
// Calculate bootstrap time and rest boot time to current time. |
|
|
|
Duration bootDuration = Duration.between(LocalDateTime.now(), bootTime); |
|
long bootSeconds = Math.abs(bootDuration.getSeconds()); |
|
String boottime = String.format("%d hours %02d minutes %02d seconds", bootSeconds / 3600, (bootSeconds % 3600) / 60, (bootSeconds % 60)); |
|
Logger.info("Bootstrap time was " + boottime); |
|
|
|
bootTime = LocalDateTime.now(); |
|
|
|
Logger.info("Running garbage collection..."); |
|
System.gc(); |
|
return true; |
|
} |
|
|
|
protected boolean initDatabaselayer() { |
|
|
|
// Try starting a GOM <-> DB connection. |
|
try { |
|
|
|
Logger.info("Configuring GameObjectManager to use Database: '" |
|
+ ConfigManager.MB_DATABASE_NAME.getValue() + "' on " |
|
+ ConfigManager.MB_DATABASE_ADDRESS.getValue() + ':' |
|
+ ConfigManager.MB_DATABASE_PORT.getValue()); |
|
|
|
DbManager.configureConnectionPool(); |
|
|
|
} catch (Exception e) { |
|
Logger.error(e.getMessage()); |
|
return false; |
|
} |
|
|
|
PreparedStatementShared.submitPreparedStatementsCleaningJob(); |
|
|
|
if (MBServerStatics.DB_DEBUGGING_ON_BY_DEFAULT) { |
|
PreparedStatementShared.enableDebugging(); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
private void getWorldBuildingsMobsNPCs() { |
|
|
|
ArrayList<Zone> rootParent; |
|
|
|
rootParent = DbManager.ZoneQueries.GET_MAP_NODES(worldUUID); |
|
|
|
if (rootParent.isEmpty()) { |
|
Logger.error("populateWorldBuildings: No entries found in worldMap for parent " + worldUUID); |
|
return; |
|
} |
|
|
|
//Set sea floor object for server |
|
|
|
Zone seaFloor = rootParent.get(0); |
|
seaFloor.setParent(null); |
|
ZoneManager.setSeaFloor(seaFloor); |
|
|
|
rootParent.addAll(DbManager.ZoneQueries.GET_ALL_NODES(seaFloor)); |
|
|
|
long start = System.currentTimeMillis(); |
|
|
|
for (Zone zone : rootParent) { |
|
|
|
ZoneManager.addZone(zone.getLoadNum(), zone); |
|
zone.generateWorldAltitude(); |
|
|
|
|
|
//Handle Buildings |
|
|
|
try { |
|
ArrayList<Building> bList; |
|
bList = DbManager.BuildingQueries.GET_ALL_BUILDINGS_FOR_ZONE(zone); |
|
|
|
for (Building b : bList) { |
|
|
|
b.setObjectTypeMask(MBServerStatics.MASK_BUILDING); |
|
b.setLoc(b.getLoc()); |
|
} |
|
} catch (Exception e) { |
|
Logger.error(e); |
|
e.printStackTrace(); |
|
} |
|
|
|
//Handle Mobs |
|
|
|
try { |
|
ArrayList<Mob> mobs; |
|
mobs = DbManager.MobQueries.GET_ALL_MOBS_FOR_ZONE(zone); |
|
|
|
for (Mob m : mobs) { |
|
m.setObjectTypeMask(MBServerStatics.MASK_MOB | m.getTypeMasks()); |
|
m.setLoc(m.getLoc()); |
|
|
|
// Load Minions for Guard Captains here. |
|
|
|
if (m.building != null && m.building.getBlueprint() != null && m.building.getBlueprint().getBuildingGroup() == Enum.BuildingGroup.BARRACK) |
|
DbManager.MobQueries.LOAD_GUARD_MINIONS(m); |
|
} |
|
} catch (Exception e) { |
|
Logger.error(e); |
|
e.printStackTrace(); |
|
} |
|
|
|
//Handle NPCs |
|
|
|
try { |
|
ArrayList<NPC> npcs; |
|
|
|
// Ignore NPCs on the seafloor (npc guild leaders, etc) |
|
|
|
if (zone.equals(seaFloor)) |
|
continue; |
|
|
|
npcs = DbManager.NPCQueries.GET_ALL_NPCS_FOR_ZONE(zone); |
|
|
|
for (NPC n : npcs) { |
|
n.setObjectTypeMask(MBServerStatics.MASK_NPC); |
|
n.setLoc(n.getLoc()); |
|
} |
|
|
|
} catch (Exception e) { |
|
Logger.error(e); |
|
e.printStackTrace(); |
|
} |
|
//Handle cities |
|
|
|
ZoneManager.loadCities(zone); |
|
ZoneManager.populateWorldZones(zone); |
|
|
|
} |
|
|
|
Logger.info("time to load World Objects: " + (System.currentTimeMillis() - start) + " ms"); |
|
} |
|
|
|
/** |
|
* Called to remove a client on "leave world", "quit game", killed client |
|
* process, etc. |
|
*/ |
|
|
|
public void removeClient(ClientConnection origin) { |
|
|
|
if (origin == null) { |
|
Logger.info( |
|
"ClientConnection null in removeClient."); |
|
return; |
|
} |
|
|
|
PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter( |
|
origin); |
|
|
|
if (playerCharacter == null) |
|
// TODO log this |
|
return; |
|
|
|
//cancel any trade |
|
if (playerCharacter.getCharItemManager() != null) |
|
playerCharacter.getCharItemManager().endTrade(true); |
|
|
|
// Release any mine claims |
|
|
|
Mine.releaseMineClaims(playerCharacter); |
|
|
|
// logout |
|
long delta = MBServerStatics.LOGOUT_TIMER_MS; |
|
|
|
if (System.currentTimeMillis() - playerCharacter.getTimeStamp("LastCombatPlayer") < 60000) { |
|
delta = 60000; |
|
|
|
} |
|
playerCharacter.stopMovement(playerCharacter.getLoc()); |
|
UpdateStateMsg updateStateMsg = new UpdateStateMsg(); |
|
updateStateMsg.setPlayer(playerCharacter); |
|
|
|
updateStateMsg.setActivity(5); |
|
DispatchMessage.dispatchMsgToInterestArea(playerCharacter, updateStateMsg, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); |
|
|
|
if (playerCharacter.region != null) |
|
if (PlayerCharacter.CanBindToBuilding(playerCharacter, playerCharacter.region.parentBuildingID)) |
|
playerCharacter.bindBuilding = playerCharacter.region.parentBuildingID; |
|
else |
|
playerCharacter.bindBuilding = 0; |
|
|
|
playerCharacter.getLoadedObjects().clear(); |
|
playerCharacter.getLoadedStaticObjects().clear(); |
|
|
|
LogoutCharacterJob logoutJob = new LogoutCharacterJob(playerCharacter, this); |
|
JobContainer jc = JobScheduler.getInstance().scheduleJob(logoutJob, |
|
System.currentTimeMillis() + delta); |
|
playerCharacter.getTimers().put("Logout", jc); |
|
playerCharacter.getTimestamps().put("logout", System.currentTimeMillis()); |
|
|
|
//send update to friends that you are logged off. |
|
|
|
PlayerFriends.SendFriendsStatus(playerCharacter, false); |
|
|
|
} |
|
|
|
public void logoutCharacter(PlayerCharacter player) { |
|
|
|
if (player == null) { |
|
Logger.error("Unable to find PlayerCharacter to logout"); |
|
return; |
|
} |
|
//remove player from loaded mobs agro maps |
|
for (AbstractWorldObject awo : WorldGrid.getObjectsInRangePartial(player.getLoc(), MBServerStatics.CHARACTER_LOAD_RANGE, MBServerStatics.MASK_MOB)) { |
|
Mob loadedMob = (Mob) awo; |
|
loadedMob.playerAgroMap.remove(player.getObjectUUID()); |
|
} |
|
player.getTimestamps().put("logout", System.currentTimeMillis()); |
|
player.setEnteredWorld(false); |
|
|
|
// remove from simulation and zero current loc |
|
|
|
WorldGrid.RemoveWorldObject(player); |
|
|
|
// clear Logout Timer |
|
|
|
if (player.getTimers() != null) |
|
player.getTimers().remove("Logout"); |
|
|
|
if (player.getPet() != null) |
|
player.getPet().dismiss(); |
|
|
|
NPCManager.dismissNecroPets(player); |
|
|
|
// Set player inactive so they quit loading for other players |
|
|
|
player.setActive(false); |
|
|
|
// Remove from group |
|
|
|
Group group = GroupManager.getGroup(player); |
|
|
|
try { |
|
if (group != null) |
|
GroupManager.LeaveGroup(player); |
|
} catch (MsgSendException e) { |
|
Logger.error(e.toString()); |
|
} |
|
|
|
player.respawnLock.writeLock().lock(); |
|
try { |
|
if (!player.isAlive()) |
|
player.respawn(false, false, true); |
|
} catch (Exception e) { |
|
Logger.error(e); |
|
} finally { |
|
player.respawnLock.writeLock().unlock(); |
|
} |
|
} |
|
|
|
private void processTrashFile() { |
|
|
|
ArrayList<String> machineList; |
|
ArrayList<PlayerCharacter> trashList = new ArrayList<>(); |
|
ArrayList<Integer> accountList = new ArrayList<>(); |
|
|
|
File trashFile = new File("trash"); |
|
|
|
if (trashFile.exists() == false) |
|
return; |
|
|
|
// Build list of machineID's in the trash file |
|
|
|
machineList = DbManager.AccountQueries.GET_TRASH_LIST(); |
|
|
|
// Build list of trash characters associated with that machineID |
|
|
|
for (String machineID : machineList) { |
|
trashList = DbManager.AccountQueries.GET_ALL_CHARS_FOR_MACHINE(machineID); |
|
|
|
|
|
// Deactivate these players and add them to loginCache table |
|
|
|
for (PlayerCharacter trashPlayer : trashList) { |
|
|
|
if (trashPlayer == null) |
|
continue; |
|
|
|
// Need to collate accounts. |
|
|
|
if (!accountList.contains(trashPlayer.getAccount().getObjectUUID())) |
|
accountList.add(trashPlayer.getAccount().getObjectUUID()); |
|
|
|
DbManager.PlayerCharacterQueries.SET_ACTIVE(trashPlayer, false); |
|
DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(trashPlayer.getObjectUUID(), "character"); |
|
} |
|
} |
|
|
|
// delete vault of associated accounts and then invalidate them |
|
// in the login cache. |
|
|
|
for (Integer accountID : accountList) { |
|
DbManager.AccountQueries.DELETE_VAULT_FOR_ACCOUNT(accountID); |
|
DbManager.AccountQueries.INVALIDATE_LOGIN_CACHE(accountID, "account"); |
|
} |
|
|
|
// Trigger the Login Server to invalidate these accounts in the cache.. |
|
|
|
try { |
|
Files.write(Paths.get("cacheInvalid"), "".getBytes()); |
|
} catch (IOException e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
// If any of these players are active disconnect them. |
|
// The account and player should be removed from the login |
|
// server cache file by now. |
|
|
|
Timer timer = new Timer("Disconnect Trash"); |
|
timer.schedule(new DisconnectTrashTask(trashList), 3000L); |
|
|
|
// Clean up after ourselves |
|
|
|
try { |
|
Files.deleteIfExists(Paths.get("trash")); |
|
DbManager.AccountQueries.CLEAR_TRASH_TABLE(); |
|
} catch (IOException e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
} |
|
|
|
private void processFlashFile() { |
|
|
|
File flashFile = new File("flash"); |
|
String flashString; |
|
List<String> fileContents; |
|
|
|
if (flashFile.exists() == false) |
|
return; |
|
|
|
try { |
|
fileContents = Files.readAllLines(Paths.get("flash")); |
|
} catch (IOException e) { |
|
return; |
|
} |
|
|
|
// Flash file detected: read contents |
|
// and send as a flash. |
|
|
|
flashString = fileContents.toString(); |
|
|
|
if (flashString == null) |
|
return; |
|
|
|
if (flashString == "") |
|
flashString = "Rebooting for to fix bug."; |
|
|
|
Logger.info("Sending flash from external interface"); |
|
Logger.info("Msg: " + flashString); |
|
|
|
ChatSystemMsg msg = new ChatSystemMsg(null, flashString); |
|
msg.setChannel(engine.Enum.ChatChannelType.FLASH.getChannelID()); |
|
msg.setMessageType(Enum.ChatMessageType.INFO.ordinal()); |
|
DispatchMessage.dispatchMsgToAll(msg); |
|
|
|
// Delete file |
|
|
|
try { |
|
Files.deleteIfExists(Paths.get("flash")); |
|
} catch (IOException e) { |
|
e.printStackTrace(); |
|
} |
|
|
|
} |
|
} |