parent
af8535620b
commit
dbc32c4d0a
@ -1,133 +1,133 @@ |
|||||||
package junk; |
//package junk;
|
||||||
|
//
|
||||||
|
//
|
||||||
import java.io.File; |
//import java.io.File;
|
||||||
import java.util.ArrayList; |
//import java.util.ArrayList;
|
||||||
import java.util.List; |
//import java.util.List;
|
||||||
import java.util.logging.Level; |
//import java.util.logging.Level;
|
||||||
|
//
|
||||||
import mightypork.gamecore.core.AppBackend; |
//import mightypork.gamecore.core.AppBackend;
|
||||||
import mightypork.gamecore.resources.ResourceInitializer; |
//import mightypork.gamecore.resources.ResourceInitializer;
|
||||||
import mightypork.gamecore.resources.loading.AsyncResourceLoader; |
//import mightypork.gamecore.resources.loading.AsyncResourceLoader;
|
||||||
import mightypork.gamecore.resources.loading.ResourceLoader; |
//import mightypork.gamecore.resources.loading.ResourceLoader;
|
||||||
|
//
|
||||||
|
//
|
||||||
/** |
///**
|
||||||
* Init options holder class
|
// * Init options holder class
|
||||||
*/ |
// */
|
||||||
public class AppInitOptions { |
//public class AppInitOptions {
|
||||||
|
//
|
||||||
String logDir = "log"; |
// String logDir = "log";
|
||||||
String logFilePrefix = "runtime"; |
// String logFilePrefix = "runtime";
|
||||||
|
//
|
||||||
String screenshotDir = "screenshots"; |
// String screenshotDir = "screenshots";
|
||||||
|
//
|
||||||
boolean busLogging = false; |
// boolean busLogging = false;
|
||||||
|
//
|
||||||
String configFile = "settings.cfg"; |
// String configFile = "settings.cfg";
|
||||||
String configComment = "Main config file"; |
// String configComment = "Main config file";
|
||||||
|
//
|
||||||
final List<ResourceInitializer> resourceLists = new ArrayList<>(); |
// final List<ResourceInitializer> resourceLists = new ArrayList<>();
|
||||||
final List<KeySetup> keyLists = new ArrayList<>(); |
// final List<KeySetup> keyLists = new ArrayList<>();
|
||||||
final List<ConfigSetup> configLists = new ArrayList<>(); |
// final List<ConfigSetup> configLists = new ArrayList<>();
|
||||||
|
//
|
||||||
ResourceLoader resourceLoader = new AsyncResourceLoader(); |
// ResourceLoader resourceLoader = new AsyncResourceLoader();
|
||||||
Level logLevel = Level.ALL; |
// Level logLevel = Level.ALL;
|
||||||
public boolean sigleInstance = true; |
// public boolean sigleInstance = true;
|
||||||
Level logSoutLevel = Level.ALL; |
// Level logSoutLevel = Level.ALL;
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setConfigFile(String filename, String comment) |
// public void setConfigFile(String filename, String comment)
|
||||||
{ |
// {
|
||||||
configFile = filename; |
// configFile = filename;
|
||||||
configComment = comment; |
// configComment = comment;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void addConfig(ConfigSetup cfg) |
// public void addConfig(ConfigSetup cfg)
|
||||||
{ |
// {
|
||||||
configLists.add(cfg); |
// configLists.add(cfg);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void addKeys(KeySetup keys) |
// public void addKeys(KeySetup keys)
|
||||||
{ |
// {
|
||||||
keyLists.add(keys); |
// keyLists.add(keys);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void addResources(ResourceInitializer res) |
// public void addResources(ResourceInitializer res)
|
||||||
{ |
// {
|
||||||
resourceLists.add(res); |
// resourceLists.add(res);
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setBackend(AppBackend backend) |
// public void setBackend(AppBackend backend)
|
||||||
{ |
// {
|
||||||
this.backend = backend; |
// this.backend = backend;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
/** |
// /**
|
||||||
* Set whether to run in single instance mode, or allow multiple instances.<br> |
// * Set whether to run in single instance mode, or allow multiple instances.<br>
|
||||||
* Multiple instances running can cause various collisions (eg. when writing |
// * Multiple instances running can cause various collisions (eg. when writing
|
||||||
* config file or logging). |
// * config file or logging).
|
||||||
* |
// *
|
||||||
* @param sigleInstance true to allow only one instance |
// * @param sigleInstance true to allow only one instance
|
||||||
*/ |
// */
|
||||||
public void setSigleInstance(boolean sigleInstance) |
// public void setSigleInstance(boolean sigleInstance)
|
||||||
{ |
// {
|
||||||
this.sigleInstance = sigleInstance; |
// this.sigleInstance = sigleInstance;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
/** |
// /**
|
||||||
* Set working directory path. If not exists, it will be created. |
// * Set working directory path. If not exists, it will be created.
|
||||||
* |
// *
|
||||||
* @param workdir work dir path |
// * @param workdir work dir path
|
||||||
*/ |
// */
|
||||||
public void setWorkdir(File workdir) |
// public void setWorkdir(File workdir)
|
||||||
{ |
// {
|
||||||
this.workdir = workdir; |
// this.workdir = workdir;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setBusLogging(boolean yes) |
// public void setBusLogging(boolean yes)
|
||||||
{ |
// {
|
||||||
busLogging = yes; |
// busLogging = yes;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel) |
// public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel)
|
||||||
{ |
// {
|
||||||
this.logDir = logDir; |
// this.logDir = logDir;
|
||||||
this.logFilePrefix = filePrefix; |
// this.logFilePrefix = filePrefix;
|
||||||
this.logArchiveCount = archivedCount; |
// this.logArchiveCount = archivedCount;
|
||||||
this.logLevel = logLevel; |
// this.logLevel = logLevel;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setResourceLoader(ResourceLoader resLoader) |
// public void setResourceLoader(ResourceLoader resLoader)
|
||||||
{ |
// {
|
||||||
resourceLoader = resLoader; |
// resourceLoader = resLoader;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setScreenshotDir(String path) |
// public void setScreenshotDir(String path)
|
||||||
{ |
// {
|
||||||
this.screenshotDir = path; |
// this.screenshotDir = path;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setLockFile(String lockFile) |
// public void setLockFile(String lockFile)
|
||||||
{ |
// {
|
||||||
this.lockFile = lockFile; |
// this.lockFile = lockFile;
|
||||||
} |
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
public void setLogLevel(Level logLevel, Level soutLevel) |
// public void setLogLevel(Level logLevel, Level soutLevel)
|
||||||
{ |
// {
|
||||||
this.logLevel = logLevel; |
// this.logLevel = logLevel;
|
||||||
this.logSoutLevel = soutLevel; |
// this.logSoutLevel = soutLevel;
|
||||||
} |
// }
|
||||||
} |
//}
|
||||||
|
@ -1,184 +1,185 @@ |
|||||||
package junk; |
//package junk;
|
||||||
|
//
|
||||||
|
//
|
||||||
import java.lang.Thread.UncaughtExceptionHandler; |
//import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
|
//
|
||||||
import mightypork.gamecore.core.App; |
//import mightypork.gamecore.core.App;
|
||||||
import mightypork.gamecore.core.AppBackend; |
//import mightypork.gamecore.core.AppBackend;
|
||||||
import mightypork.gamecore.core.MainLoop; |
//import mightypork.gamecore.core.MainLoop;
|
||||||
import mightypork.gamecore.core.config.Config; |
//import mightypork.gamecore.core.DeltaMainLoop;
|
||||||
import mightypork.gamecore.gui.screens.ScreenRegistry; |
//import mightypork.gamecore.core.config.Config;
|
||||||
import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay; |
//import mightypork.gamecore.gui.screens.ScreenRegistry;
|
||||||
import mightypork.gamecore.resources.Res; |
//import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay;
|
||||||
import mightypork.gamecore.resources.ResourceInitializer; |
//import mightypork.gamecore.resources.Res;
|
||||||
import mightypork.utils.files.WorkDir; |
//import mightypork.gamecore.resources.ResourceInitializer;
|
||||||
import mightypork.utils.logging.Log; |
//import mightypork.utils.files.WorkDir;
|
||||||
|
//import mightypork.utils.logging.Log;
|
||||||
|
//
|
||||||
/** |
//
|
||||||
* Basic screen-based game with subsystems.<br> |
///**
|
||||||
* This class takes care of the initialization sequence. |
// * Basic screen-based game with subsystems.<br>
|
||||||
* |
// * This class takes care of the initialization sequence.
|
||||||
* @author Ondřej Hruška (MightyPork) |
// *
|
||||||
*/ |
// * @author Ondřej Hruška (MightyPork)
|
||||||
public abstract class BaseApp extends App implements UncaughtExceptionHandler { |
// */
|
||||||
|
//public abstract class BaseApp extends App implements UncaughtExceptionHandler {
|
||||||
// modules
|
//
|
||||||
private MainLoop gameLoop; |
// // modules
|
||||||
private ScreenRegistry screenRegistry; |
// private MainLoop gameLoop;
|
||||||
|
// private ScreenRegistry screenRegistry;
|
||||||
private boolean started = false; |
//
|
||||||
private boolean lockObtained = false; |
// private boolean started = false;
|
||||||
|
// private boolean lockObtained = false;
|
||||||
// init opt holder
|
//
|
||||||
private final AppInitOptions opt = new AppInitOptions(); |
// // init opt holder
|
||||||
|
// private final AppInitOptions opt = new AppInitOptions();
|
||||||
|
//
|
||||||
/** |
//
|
||||||
* Get init options |
// /**
|
||||||
* |
// * Get init options
|
||||||
* @return opt holder |
// *
|
||||||
*/ |
// * @return opt holder
|
||||||
public AppInitOptions getInitOptions() |
// */
|
||||||
{ |
// public AppInitOptions getInitOptions()
|
||||||
if (started) { |
// {
|
||||||
throw new IllegalStateException("Cannot alter init options after starting the App."); |
// if (started) {
|
||||||
} |
// throw new IllegalStateException("Cannot alter init options after starting the App.");
|
||||||
|
// }
|
||||||
return opt; |
//
|
||||||
} |
// return opt;
|
||||||
|
// }
|
||||||
|
//
|
||||||
public BaseApp(AppBackend backend) |
//
|
||||||
{ |
// public BaseApp(AppBackend backend)
|
||||||
super(backend); |
// {
|
||||||
} |
// super(backend);
|
||||||
|
// }
|
||||||
|
//
|
||||||
/** |
//
|
||||||
* Start the application |
// /**
|
||||||
*/ |
// * Start the application
|
||||||
@Override |
// */
|
||||||
public final void start() |
// @Override
|
||||||
{ |
// public final void start()
|
||||||
initialize(); |
// {
|
||||||
|
// initialize();
|
||||||
Log.i("Starting main loop..."); |
//
|
||||||
|
// Log.i("Starting main loop...");
|
||||||
// open first screen !!!
|
//
|
||||||
started = true; |
// // open first screen !!!
|
||||||
gameLoop.start(); |
// started = true;
|
||||||
} |
// gameLoop.start();
|
||||||
|
// }
|
||||||
|
//
|
||||||
/** |
//
|
||||||
* Init the app |
// /**
|
||||||
*/ |
// * Init the app
|
||||||
protected void initialize() |
// */
|
||||||
{ |
// protected void initialize()
|
||||||
WorkDir.setBaseDir(opt.workdir); |
// {
|
||||||
|
// WorkDir.setBaseDir(opt.workdir);
|
||||||
if (opt.sigleInstance) initLock(); |
//
|
||||||
lockObtained = true; |
// if (opt.sigleInstance) initLock();
|
||||||
|
// lockObtained = true;
|
||||||
for (final RouteSetup rs : opt.routeLists) { |
//
|
||||||
WorkDir.registerRoutes(rs); |
// for (final RouteSetup rs : opt.routeLists) {
|
||||||
} |
// WorkDir.registerRoutes(rs);
|
||||||
WorkDir.addPath("_screenshot_dir", opt.screenshotDir); |
// }
|
||||||
|
// WorkDir.addPath("_screenshot_dir", opt.screenshotDir);
|
||||||
// apply configurations
|
//
|
||||||
Config.init(WorkDir.getFile(opt.configFile), opt.configComment); |
// // apply configurations
|
||||||
|
// Config.init(WorkDir.getFile(opt.configFile), opt.configComment);
|
||||||
// add keys to config
|
//
|
||||||
for (final KeySetup l : opt.keyLists) { |
// // add keys to config
|
||||||
Config.registerKeys(l); |
// for (final KeySetup l : opt.keyLists) {
|
||||||
} |
// Config.registerKeys(l);
|
||||||
|
// }
|
||||||
// add options to config
|
//
|
||||||
for (final ConfigSetup c : opt.configLists) { |
// // add options to config
|
||||||
Config.registerOptions(c); |
// for (final ConfigSetup c : opt.configLists) {
|
||||||
} |
// Config.registerOptions(c);
|
||||||
Config.load(); |
// }
|
||||||
|
// Config.load();
|
||||||
/* |
//
|
||||||
* Display |
// /*
|
||||||
*/ |
// * Display
|
||||||
Log.f2("Initializing Display System..."); |
// */
|
||||||
initDisplay(gfx()); |
// Log.f2("Initializing Display System...");
|
||||||
|
// initDisplay(gfx());
|
||||||
/* |
//
|
||||||
* Audio |
// /*
|
||||||
*/ |
// * Audio
|
||||||
Log.f2("Initializing Sound System..."); |
// */
|
||||||
soundSystem = new SoundSystem(this); |
// Log.f2("Initializing Sound System...");
|
||||||
initSoundSystem(soundSystem); |
// soundSystem = new SoundSystem(this);
|
||||||
|
// initSoundSystem(soundSystem);
|
||||||
/* |
//
|
||||||
* Input |
// /*
|
||||||
*/ |
// * Input
|
||||||
Log.f2("Initializing Input System..."); |
// */
|
||||||
inputSystem = new LwjglInputModule(this); |
// Log.f2("Initializing Input System...");
|
||||||
initInputSystem(inputSystem); |
// inputSystem = new LwjglInputModule(this);
|
||||||
|
// initInputSystem(inputSystem);
|
||||||
/* |
//
|
||||||
* Prepare main loop |
// /*
|
||||||
*/ |
// * Prepare main loop
|
||||||
Log.f1("Creating Screen Registry and Game Loop..."); |
// */
|
||||||
screenRegistry = new ScreenRegistry(this); |
// Log.f1("Creating Screen Registry and Game Loop...");
|
||||||
gameLoop = createMainLoop(); |
// screenRegistry = new ScreenRegistry(this);
|
||||||
gameLoop.setRootRenderable(screenRegistry); |
// gameLoop = createMainLoop();
|
||||||
|
// gameLoop.setRootRenderable(screenRegistry);
|
||||||
/* |
//
|
||||||
* Load resources |
// /*
|
||||||
* |
// * Load resources
|
||||||
* Resources should be registered to registries, and AsyncResourceLoader will load them. |
// *
|
||||||
*/ |
// * Resources should be registered to registries, and AsyncResourceLoader will load them.
|
||||||
Log.f1("Loading resources..."); |
// */
|
||||||
if (opt.resourceLoader != null) { |
// Log.f1("Loading resources...");
|
||||||
opt.resourceLoader.setBaseDir(this); |
// if (opt.resourceLoader != null) {
|
||||||
} |
// opt.resourceLoader.setBaseDir(this);
|
||||||
|
// }
|
||||||
Res.setBaseDir(this); |
//
|
||||||
|
// Res.setBaseDir(this);
|
||||||
for (final ResourceInitializer rl : opt.resourceLists) { |
//
|
||||||
Res.load(rl); |
// for (final ResourceInitializer rl : opt.resourceLists) {
|
||||||
} |
// Res.load(rl);
|
||||||
|
// }
|
||||||
/* |
//
|
||||||
* Screen registry |
// /*
|
||||||
* |
// * Screen registry
|
||||||
* Must be after resources, because screens can request them during instantiation. |
// *
|
||||||
*/ |
// * Must be after resources, because screens can request them during instantiation.
|
||||||
Log.f2("Registering screens..."); |
// */
|
||||||
initScreens(screenRegistry); |
// Log.f2("Registering screens...");
|
||||||
} |
// initScreens(screenRegistry);
|
||||||
|
// }
|
||||||
|
//
|
||||||
/** |
//
|
||||||
* Register game screens to the registry. |
// /**
|
||||||
* |
// * Register game screens to the registry.
|
||||||
* @param screens |
// *
|
||||||
*/ |
// * @param screens
|
||||||
protected void initScreens(ScreenRegistry screens) |
// */
|
||||||
{ |
// protected void initScreens(ScreenRegistry screens)
|
||||||
screens.addOverlay(new CrossfadeOverlay(this)); |
// {
|
||||||
} |
// screens.addOverlay(new CrossfadeOverlay(this));
|
||||||
|
// }
|
||||||
|
//
|
||||||
/** |
//
|
||||||
* Create game loop instance |
// /**
|
||||||
* |
// * Create game loop instance
|
||||||
* @return the game loop. |
// *
|
||||||
*/ |
// * @return the game loop.
|
||||||
protected MainLoop createMainLoop() |
// */
|
||||||
{ |
// protected MainLoop createMainLoop()
|
||||||
return new MainLoop(this); |
// {
|
||||||
} |
// return new DeltaMainLoop(this);
|
||||||
|
// }
|
||||||
|
//
|
||||||
protected void beforeShutdown() |
//
|
||||||
{ |
// protected void beforeShutdown()
|
||||||
// ???
|
// {
|
||||||
if (lockObtained) Config.save(); |
// // ???
|
||||||
} |
// if (lockObtained) Config.save();
|
||||||
} |
// }
|
||||||
|
//}
|
||||||
|
@ -0,0 +1,128 @@ |
|||||||
|
package mightypork.gamecore.core; |
||||||
|
|
||||||
|
|
||||||
|
import java.util.Deque; |
||||||
|
import java.util.concurrent.ConcurrentLinkedDeque; |
||||||
|
|
||||||
|
import mightypork.gamecore.graphics.Renderable; |
||||||
|
import mightypork.utils.annotations.Stub; |
||||||
|
import mightypork.utils.eventbus.clients.BusNode; |
||||||
|
import mightypork.utils.eventbus.events.UpdateEvent; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
import mightypork.utils.math.timing.Profiler; |
||||||
|
import mightypork.utils.math.timing.TimerDelta; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Delta-timed game loop with task queue etc. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class DeltaMainLoop extends BusNode implements MainLoop { |
||||||
|
|
||||||
|
/** |
||||||
|
* Max time spent on main loop tasks per cycle (s) |
||||||
|
*/ |
||||||
|
protected double MAX_TIME_TASKS = 1 / 30D; |
||||||
|
|
||||||
|
/** |
||||||
|
* Max delta time (s) per frame.<br> |
||||||
|
* If delta is larger than this, it's clamped to it. |
||||||
|
*/ |
||||||
|
protected double MAX_DELTA = 1 / 20D; |
||||||
|
|
||||||
|
private final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>(); |
||||||
|
private TimerDelta timer; |
||||||
|
private Renderable rootRenderable; |
||||||
|
private volatile boolean running = true; |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void setRootRenderable(Renderable rootRenderable) |
||||||
|
{ |
||||||
|
this.rootRenderable = rootRenderable; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() |
||||||
|
{ |
||||||
|
timer = new TimerDelta(); |
||||||
|
|
||||||
|
while (running) { |
||||||
|
App.gfx().beginFrame(); |
||||||
|
|
||||||
|
double delta = timer.getDelta(); |
||||||
|
if (delta > MAX_DELTA) { |
||||||
|
Log.f3("(timing) Clamping delta: was " + delta + " s, MAX_DELTA = " + MAX_DELTA + " s"); |
||||||
|
delta = MAX_DELTA; |
||||||
|
} |
||||||
|
|
||||||
|
// dispatch update event
|
||||||
|
App.bus().sendDirect(new UpdateEvent(delta)); |
||||||
|
|
||||||
|
// run main loop tasks
|
||||||
|
Runnable r; |
||||||
|
final long t = Profiler.begin(); |
||||||
|
|
||||||
|
while ((r = tasks.poll()) != null) { |
||||||
|
Log.f3(" * Main loop task."); |
||||||
|
r.run(); |
||||||
|
|
||||||
|
if (Profiler.end(t) > MAX_TIME_TASKS) { |
||||||
|
Log.f3("! Time's up, postponing task to next cycle."); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
beforeRender(); |
||||||
|
|
||||||
|
if (rootRenderable != null) { |
||||||
|
rootRenderable.render(); |
||||||
|
} |
||||||
|
|
||||||
|
afterRender(); |
||||||
|
|
||||||
|
App.gfx().endFrame(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Called before render |
||||||
|
*/ |
||||||
|
@Stub |
||||||
|
protected void beforeRender() |
||||||
|
{ |
||||||
|
//
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Called after render |
||||||
|
*/ |
||||||
|
@Stub |
||||||
|
protected void afterRender() |
||||||
|
{ |
||||||
|
//
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() |
||||||
|
{ |
||||||
|
running = false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public synchronized void queueTask(Runnable task, boolean skipQueue) |
||||||
|
{ |
||||||
|
if (skipQueue) { |
||||||
|
tasks.addFirst(task); |
||||||
|
} else { |
||||||
|
tasks.addLast(task); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,143 +1,45 @@ |
|||||||
package mightypork.gamecore.core; |
package mightypork.gamecore.core; |
||||||
|
|
||||||
|
|
||||||
import java.util.Deque; |
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque; |
|
||||||
|
|
||||||
import mightypork.gamecore.graphics.Renderable; |
import mightypork.gamecore.graphics.Renderable; |
||||||
import mightypork.gamecore.gui.screens.ScreenRegistry; |
import mightypork.gamecore.gui.screens.ScreenRegistry; |
||||||
import mightypork.utils.annotations.Stub; |
|
||||||
import mightypork.utils.eventbus.clients.BusNode; |
|
||||||
import mightypork.utils.eventbus.events.UpdateEvent; |
|
||||||
import mightypork.utils.interfaces.Destroyable; |
import mightypork.utils.interfaces.Destroyable; |
||||||
import mightypork.utils.logging.Log; |
|
||||||
import mightypork.utils.math.timing.Profiler; |
|
||||||
import mightypork.utils.math.timing.TimerDelta; |
|
||||||
|
|
||||||
|
|
||||||
/** |
/** |
||||||
* Delta-timed game loop with task queue etc. |
* A main loop of the app. |
||||||
* |
* |
||||||
* @author Ondřej Hruška (MightyPork) |
* @author Ondřej Hruška (MightyPork) |
||||||
*/ |
*/ |
||||||
public class MainLoop extends BusNode implements Destroyable { |
public interface MainLoop extends Destroyable { |
||||||
|
|
||||||
/** |
|
||||||
* Max time spent on main loop tasks per cycle (s) |
|
||||||
*/ |
|
||||||
protected double MAX_TIME_TASKS = 1 / 30D; |
|
||||||
|
|
||||||
/** |
|
||||||
* Max delta time (s) per frame.<br> |
|
||||||
* If delta is larger than this, it's clamped to it. |
|
||||||
*/ |
|
||||||
protected double MAX_DELTA = 1 / 20D; |
|
||||||
|
|
||||||
private final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>(); |
|
||||||
private TimerDelta timer; |
|
||||||
private Renderable rootRenderable; |
|
||||||
private volatile boolean running = true; |
|
||||||
|
|
||||||
|
|
||||||
/** |
/** |
||||||
* Set primary renderable |
* Set primary renderable |
||||||
* |
* |
||||||
* @param rootRenderable main {@link Renderable}, typically a |
* @param rootRenderable main {@link Renderable}, typically a |
||||||
* {@link ScreenRegistry} |
* {@link ScreenRegistry} |
||||||
*/ |
*/ |
||||||
public void setRootRenderable(Renderable rootRenderable) |
public abstract void setRootRenderable(Renderable rootRenderable); |
||||||
{ |
|
||||||
this.rootRenderable = rootRenderable; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
/** |
||||||
* Start the loop |
* Start the loop. The loop should be terminated when the destroy() method |
||||||
|
* is called. |
||||||
*/ |
*/ |
||||||
public void start() |
public abstract void start(); |
||||||
{ |
|
||||||
timer = new TimerDelta(); |
|
||||||
|
|
||||||
while (running) { |
|
||||||
App.gfx().beginFrame(); |
|
||||||
|
|
||||||
double delta = timer.getDelta(); |
|
||||||
if (delta > MAX_DELTA) { |
|
||||||
Log.f3("(timing) Clamping delta: was " + delta + " s, MAX_DELTA = " + MAX_DELTA + " s"); |
|
||||||
delta = MAX_DELTA; |
|
||||||
} |
|
||||||
|
|
||||||
// dispatch update event
|
|
||||||
App.bus().sendDirect(new UpdateEvent(delta)); |
|
||||||
|
|
||||||
// run main loop tasks
|
|
||||||
Runnable r; |
|
||||||
final long t = Profiler.begin(); |
|
||||||
|
|
||||||
while ((r = tasks.poll()) != null) { |
|
||||||
Log.f3(" * Main loop task."); |
|
||||||
r.run(); |
|
||||||
|
|
||||||
if (Profiler.end(t) > MAX_TIME_TASKS) { |
|
||||||
Log.f3("! Time's up, postponing task to next cycle."); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
beforeRender(); |
|
||||||
|
|
||||||
if (rootRenderable != null) { |
|
||||||
rootRenderable.render(); |
|
||||||
} |
|
||||||
|
|
||||||
afterRender(); |
|
||||||
|
|
||||||
App.gfx().endFrame(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Called before render |
|
||||||
*/ |
|
||||||
@Stub |
|
||||||
protected void beforeRender() |
|
||||||
{ |
|
||||||
//
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Called after render |
|
||||||
*/ |
|
||||||
@Stub |
|
||||||
protected void afterRender() |
|
||||||
{ |
|
||||||
//
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
@Override |
||||||
public void destroy() |
public abstract void destroy(); |
||||||
{ |
|
||||||
running = false; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
/** |
||||||
* Add a task to queue to be executed in the main loop (in rendering |
* Add a task to queue to be executed in the main loop (in rendering |
||||||
* context) |
* context). This may be eg. loading textures. |
||||||
* |
* |
||||||
* @param task task |
* @param task task |
||||||
* @param skipQueue true to skip the queue |
* @param skipQueue true to skip the queue |
||||||
*/ |
*/ |
||||||
public synchronized void queueTask(Runnable task, boolean skipQueue) |
public abstract void queueTask(Runnable task, boolean skipQueue); |
||||||
{ |
|
||||||
if (skipQueue) { |
|
||||||
tasks.addFirst(task); |
|
||||||
} else { |
|
||||||
tasks.addLast(task); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
} |
||||||
|
@ -0,0 +1,40 @@ |
|||||||
|
package mightypork.gamecore.core.init; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.gamecore.core.MainLoop; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Task to add a resource loader.<br> |
||||||
|
* By default the async resource loader is used |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public abstract class InitTaskMainLoop extends InitTask { |
||||||
|
|
||||||
|
/** The loader. */ |
||||||
|
protected MainLoop loop; |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() |
||||||
|
{ |
||||||
|
loop = getLoopImpl(); |
||||||
|
app.setMainLoop(loop); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Create a loader impl |
||||||
|
* |
||||||
|
* @return loader |
||||||
|
*/ |
||||||
|
protected abstract MainLoop getLoopImpl(); |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "resource_loader"; |
||||||
|
} |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue