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.
211 lines
5.7 KiB
211 lines
5.7 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// Magicbane Emulator Project © 2013 - 2022 |
|
// www.magicbane.com |
|
|
|
package engine.gameManager; |
|
|
|
import engine.Enum; |
|
import engine.Enum.GameObjectType; |
|
import engine.objects.AbstractGameObject; |
|
import engine.objects.City; |
|
import engine.objects.PlayerCharacter; |
|
import engine.objects.Runegate; |
|
import org.pmw.tinylog.Logger; |
|
|
|
import java.time.Duration; |
|
import java.time.Instant; |
|
import java.util.Collection; |
|
|
|
/* |
|
* This class contains all methods necessary to drive periodic |
|
* updates of the game simulation from the main _exec loop. |
|
*/ |
|
public enum SimulationManager { |
|
|
|
SERVERHEARTBEAT; |
|
|
|
private static SimulationManager instance = null; |
|
|
|
private static final long CITY_PULSE = 2000; |
|
private static final long RUNEGATE_PULSE = 3000; |
|
private static final long UPDATE_PULSE = 1000; |
|
private static final long FlIGHT_PULSE = 100; |
|
|
|
private long _cityPulseTime = System.currentTimeMillis() + CITY_PULSE; |
|
private long _runegatePulseTime = System.currentTimeMillis() |
|
+ RUNEGATE_PULSE; |
|
private long _updatePulseTime = System.currentTimeMillis() + UPDATE_PULSE; |
|
private long _flightPulseTime = System.currentTimeMillis() + FlIGHT_PULSE; |
|
|
|
public static Duration executionTime = Duration.ofNanos(1); |
|
public static Duration executionMax = Duration.ofNanos(1); |
|
|
|
private SimulationManager() { |
|
|
|
// don't allow instantiation. |
|
} |
|
|
|
public static String getPopulationString() { |
|
String outString; |
|
String newLine = System.getProperty("line.separator"); |
|
outString = "[LUA_POPULATION()]" + newLine; |
|
outString += DbManager.CSSessionQueries.GET_POPULATION_STRING(); |
|
return outString; |
|
} |
|
|
|
/* |
|
* Update the simulation. *** Important: Whatever you do in here, do it damn |
|
* quick! |
|
*/ |
|
public void tick() { |
|
|
|
/* |
|
* As we're on the main thread we must be sure to catch any possible |
|
* errors. |
|
* |
|
* IF something does occur, disable that particular heartbeat. Better |
|
* runegates stop working than the game itself! |
|
*/ |
|
|
|
Instant startTime = Instant.now(); |
|
|
|
try { |
|
if ((_flightPulseTime != 0) |
|
&& (System.currentTimeMillis() > _flightPulseTime)) |
|
pulseFlight(); |
|
} catch (Exception e) { |
|
Logger.error( |
|
"Fatal error in City Pulse: DISABLED. Error Message : " |
|
+ e.getMessage()); |
|
} |
|
try { |
|
|
|
if ((_updatePulseTime != 0) |
|
&& (System.currentTimeMillis() > _updatePulseTime)) |
|
pulseUpdate(); |
|
} catch (Exception e) { |
|
Logger.error( |
|
"Fatal error in Update Pulse: DISABLED"); |
|
// _runegatePulseTime = 0; |
|
} |
|
|
|
try { |
|
if ((_runegatePulseTime != 0) |
|
&& (System.currentTimeMillis() > _runegatePulseTime)) |
|
pulseRunegates(); |
|
} catch (Exception e) { |
|
Logger.error( |
|
"Fatal error in Runegate Pulse: DISABLED"); |
|
_runegatePulseTime = 0; |
|
} |
|
|
|
try { |
|
if ((_cityPulseTime != 0) |
|
&& (System.currentTimeMillis() > _cityPulseTime)) |
|
pulseCities(); |
|
} catch (Exception e) { |
|
Logger.error( |
|
"Fatal error in City Pulse: DISABLED. Error Message : " |
|
+ e.getMessage()); |
|
e.printStackTrace(); |
|
|
|
} |
|
|
|
SimulationManager.executionTime = Duration.between(startTime, Instant.now()); |
|
|
|
if (executionTime.compareTo(executionMax) > 0) |
|
executionMax = executionTime; |
|
} |
|
|
|
/* |
|
* Mainline simulation update method: handles movement and regen for all |
|
* player characters |
|
*/ |
|
|
|
private void pulseUpdate() { |
|
|
|
Collection<AbstractGameObject> playerList; |
|
|
|
playerList = DbManager.getList(GameObjectType.PlayerCharacter); |
|
|
|
// Call update() on each player in game |
|
|
|
if (playerList == null) |
|
return; |
|
|
|
for (AbstractGameObject ago : playerList) { |
|
PlayerCharacter player = (PlayerCharacter)ago; |
|
|
|
if (player == null) |
|
continue; |
|
player.update(); |
|
} |
|
|
|
_updatePulseTime = System.currentTimeMillis() + 500; |
|
} |
|
|
|
private void pulseFlight() { |
|
|
|
Collection<AbstractGameObject> playerList; |
|
|
|
playerList = DbManager.getList(GameObjectType.PlayerCharacter); |
|
|
|
// Call update() on each player in game |
|
|
|
if (playerList == null) |
|
return; |
|
|
|
for (AbstractGameObject ago : playerList) { |
|
PlayerCharacter player = (PlayerCharacter)ago; |
|
|
|
if (player == null) |
|
continue; |
|
|
|
|
|
player.updateFlight(); |
|
} |
|
|
|
_flightPulseTime = System.currentTimeMillis() + FlIGHT_PULSE; |
|
} |
|
|
|
private void pulseCities() { |
|
|
|
City city; |
|
|
|
// *** Refactor: Need a list cached somewhere as it doesn't change very |
|
// often at all. Have a cityListIsDirty boolean that gets set if it |
|
// needs an update. Will speed up this method a great deal. |
|
|
|
Collection<AbstractGameObject> cityList = DbManager.getList(Enum.GameObjectType.City); |
|
|
|
if (cityList == null) { |
|
Logger.info( "City List null"); |
|
return; |
|
} |
|
|
|
for (AbstractGameObject cityObject : cityList) { |
|
city = (City) cityObject; |
|
city.onEnter(); |
|
} |
|
|
|
_cityPulseTime = System.currentTimeMillis() + CITY_PULSE; |
|
} |
|
|
|
/* |
|
* Method runs proximity collision detection for all active portals on the |
|
* game's Runegates |
|
*/ |
|
private void pulseRunegates() { |
|
|
|
for (Runegate runegate : Runegate._runegates.values()) { |
|
runegate.collidePortals(); |
|
} |
|
|
|
_runegatePulseTime = System.currentTimeMillis() + RUNEGATE_PULSE; |
|
|
|
} |
|
}
|
|
|