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(); | |
| 		} | |
|  | |
| 	} | |
| } |