almost works

master
Ondřej Hruška 10 years ago
parent dbc32c4d0a
commit 92056f3418
  1. 142
      src/mightypork/gamecore/core/App.java
  2. 5
      src/mightypork/gamecore/core/events/ShutdownEvent.java
  3. 67
      src/mightypork/gamecore/core/init/InitTask.java
  4. 38
      src/mightypork/gamecore/core/init/InitTaskDisplay.java
  5. 16
      src/mightypork/gamecore/core/init/InitTaskMainLoop.java
  6. 12
      src/mightypork/gamecore/core/init/InitTaskResources.java
  7. 33
      src/mightypork/gamecore/core/plugins/screenshot/InitTaskPluginScreenshot.java
  8. 9
      src/mightypork/gamecore/core/plugins/screenshot/ScreenshotPlugin.java
  9. 10
      src/mightypork/gamecore/core/plugins/screenshot/ScreenshotRequest.java
  10. 29
      src/mightypork/gamecore/graphics/GraphicsModule.java
  11. 2
      src/mightypork/gamecore/input/KeyStroke.java
  12. 2
      src/mightypork/gamecore/input/Keys.java
  13. 58
      src/mightypork/gamecore/resources/loading/AsyncResourceLoader.java

@ -10,6 +10,7 @@ import mightypork.gamecore.core.events.ShutdownEvent;
import mightypork.gamecore.core.init.InitTask;
import mightypork.gamecore.core.plugins.AppPlugin;
import mightypork.gamecore.graphics.GraphicsModule;
import mightypork.gamecore.graphics.Renderable;
import mightypork.gamecore.input.InputModule;
import mightypork.utils.annotations.Stub;
import mightypork.utils.eventbus.EventBus;
@ -25,23 +26,25 @@ import mightypork.utils.logging.Log;
* @author MightyPork
*/
public class App extends BusNode {
private static App instance;
private final AppBackend backend;
private final EventBus eventBus = new EventBus();
private boolean started = false;
private boolean inited = false;
/** List of installed App plugins */
protected final DelegatingList plugins = new DelegatingList();
/** List of initializers */
protected final List<InitTask> initializers = new ArrayList<>();
/** The used main loop instance */
protected MainLoop mainLoop;
private Renderable mainRenderable;
/**
* Create an app with given backend.
*
@ -52,24 +55,24 @@ public class App extends BusNode {
if (App.instance != null) {
throw new IllegalStateException("App already initialized");
}
// store current instance in static field
App.instance = this;
// join the bus
this.eventBus.subscribe(this);
// create plugin registry attached to bus
this.eventBus.subscribe(this.plugins);
// initialize and use backend
this.backend = backend;
this.backend.bind(this);
this.eventBus.subscribe(backend);
this.backend.initialize();
}
/**
* Add a plugin to the app. Plugins can eg. listen to bus events and react
* to them.
@ -78,17 +81,13 @@ public class App extends BusNode {
*/
public void addPlugin(AppPlugin plugin)
{
if (started) {
throw new IllegalStateException("App already started, cannot add plugins.");
}
// attach to event bus
plugins.add(plugin);
plugin.bind(this);
plugin.initialize();
}
/**
* Add an initializer to the app.
*
@ -99,11 +98,11 @@ public class App extends BusNode {
if (started) {
throw new IllegalStateException("App already started, cannot add initializers.");
}
initializers.add(initializer);
}
/**
* Set the main loop implementation
*
@ -112,9 +111,21 @@ public class App extends BusNode {
public void setMainLoop(MainLoop loop)
{
this.mainLoop = loop;
bus().subscribe(loop); // ?
}
/**
* Set the main renderable
*
* @param renderable the main renderable
*/
public void setMainRenderable(Renderable renderable)
{
this.mainRenderable = renderable;
}
/**
* Get current backend
*
@ -124,8 +135,8 @@ public class App extends BusNode {
{
return backend;
}
/**
* Initialize the App and start operating.<br>
* This method should be called after adding all required initializers and
@ -137,59 +148,60 @@ public class App extends BusNode {
throw new IllegalStateException("Already started.");
}
started = true;
Log.i("Starting init...");
init();
if (mainLoop == null) {
throw new IllegalStateException("The app has no main loop assigned.");
}
Log.i("Starting main loop...");
mainLoop.setRootRenderable(mainRenderable);
mainLoop.start();
}
private void init()
{
if (inited) {
throw new IllegalStateException("Already inited.");
}
inited = true;
// pre-init hook, just in case anyone wanted to have one.
Log.f2("Calling pre-init hook...");
preInit();
Log.f2("Running init tasks...");
// sort initializers by order.
final List<InitTask> orderedInitializers = InitTask.inOrder(initializers);
for (final InitTask initTask : orderedInitializers) {
Log.f1("Running init task \"" + initTask.getName() + "\"...");
initTask.bind(this);
// set the task options
initTask.init();
initTask.before();
// main task action
initTask.run();
// after hook for extra actions immeditaely after the task completes
initTask.after();
}
// user can now start the main loop etc.
Log.f2("Calling post-init hook...");
postInit();
}
/**
* Hook called before the initialization sequence starts.
*/
@ -197,8 +209,8 @@ public class App extends BusNode {
protected void preInit()
{
}
/**
* Hook called after the initialization sequence is finished.
*/
@ -206,8 +218,8 @@ public class App extends BusNode {
protected void postInit()
{
}
/**
* Shut down the running instance.<br>
* Deinitialize backend modules and terminate the JVM.
@ -216,9 +228,9 @@ public class App extends BusNode {
{
if (instance != null) {
Log.i("Dispatching Shutdown event...");
bus().send(new ShutdownEvent(new Runnable() {
@Override
public void run()
{
@ -231,19 +243,19 @@ public class App extends BusNode {
} catch (final Throwable e) {
Log.e(e);
}
Log.i("Shutdown completed.");
System.exit(0);
}
}));
} else {
Log.w("App is not running.");
System.exit(0);
}
}
/**
* Get the currently running App instance.
*
@ -253,8 +265,8 @@ public class App extends BusNode {
{
return instance;
}
/**
* Get graphics module from the running app's backend
*
@ -264,8 +276,8 @@ public class App extends BusNode {
{
return instance.backend.getGraphics();
}
/**
* Get audio module from the running app's backend
*
@ -275,8 +287,8 @@ public class App extends BusNode {
{
return instance.backend.getAudio();
}
/**
* Get input module from the running app's backend
*
@ -286,8 +298,8 @@ public class App extends BusNode {
{
return instance.backend.getInput();
}
/**
* Get event bus instance.
*
@ -297,8 +309,8 @@ public class App extends BusNode {
{
return instance.eventBus;
}
/**
* Get the main config, if initialized.
*
@ -309,8 +321,8 @@ public class App extends BusNode {
{
return cfg("main");
}
/**
* Get a config by alias.
*

@ -1,6 +1,7 @@
package mightypork.gamecore.core.events;
import mightypork.gamecore.core.App;
import mightypork.utils.eventbus.BusEvent;
import mightypork.utils.eventbus.EventBus;
import mightypork.utils.logging.Log;
@ -42,7 +43,9 @@ public class ShutdownEvent extends BusEvent<ShutdownListener> {
{
if (!isConsumed()) {
Log.i("Shutting down...");
shutdownTask.run();
App.bus().send(new MainLoopRequest(shutdownTask, true));
} else {
Log.i("Shutdown aborted.");
}

@ -21,11 +21,11 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InitTask {
/** App instance assigned using <code>bind()</code> */
protected App app;
/**
* Assign the initialized app instance to an "app" field.
*
@ -35,8 +35,8 @@ public abstract class InitTask {
{
this.app = app;
}
/**
* An init method that is called before the <code>run()</code> method.<br>
* This method should be left unimplemented in the task, and can be used to
@ -47,8 +47,8 @@ public abstract class InitTask {
{
//
}
/**
* Hook for extra action before the main task action.<br>
* Can be overridden during app configuration to "bake-in" extra actions.
@ -58,14 +58,14 @@ public abstract class InitTask {
{
//
}
/**
* Run the initializer on app.
*/
public abstract void run();
/**
* Hook executed after the "run()" method.<br>
* Can be overridden during app configuration to "bake-in" extra actions.
@ -75,8 +75,8 @@ public abstract class InitTask {
{
//
}
/**
* Get name of this initializer (for dependency resolver).<br>
* The name should be short, snake_case and precise.
@ -84,8 +84,8 @@ public abstract class InitTask {
* @return name
*/
public abstract String getName();
/**
* Get what other initializers must be already loaded before this can load.<br>
* Depending on itself or creating a circular dependency will cause error.<br>
@ -99,8 +99,8 @@ public abstract class InitTask {
{
return new String[] {};
}
/**
* Order init tasks so that all dependencies are loaded before thye are
* needed by the tasks.
@ -111,25 +111,26 @@ public abstract class InitTask {
public static List<InitTask> inOrder(List<InitTask> tasks)
{
final List<InitTask> remaining = new ArrayList<>(tasks);
final List<InitTask> ordered = new ArrayList<>();
final Set<String> loaded = new HashSet<>();
// resolve task order
int addedThisIteration = 0;
do {
addedThisIteration = 0;
for (final Iterator<InitTask> i = remaining.iterator(); i.hasNext();) {
final InitTask task = i.next();
String[] deps = task.getDependencies();
if (deps == null) deps = new String[] {};
int unmetDepsCount = deps.length;
for (final String d : deps) {
if (loaded.contains(d)) unmetDepsCount--;
}
if (unmetDepsCount == 0) {
ordered.add(task);
loaded.add(task.getName());
@ -138,38 +139,38 @@ public abstract class InitTask {
}
}
} while (addedThisIteration > 0);
// check if any tasks are left out
if (remaining.size() > 0) {
// build error message for each bad task
int badInitializers = 0;
for (final InitTask task : remaining) {
if (Reflect.hasAnnotation(task.getClass(), OptionalInitTask.class)) {
continue;
}
badInitializers++;
String notSatisfied = "";
for (final String d : task.getDependencies()) {
if (!loaded.contains(d)) {
if (!notSatisfied.isEmpty()) {
notSatisfied += ", ";
}
notSatisfied += d;
}
}
Log.w("InitTask \"" + task.getName() + "\" - missing dependencies: " + notSatisfied);
}
if (badInitializers > 0) throw new RuntimeException("Some InitTask dependencies could not be satisfied.");
}
return ordered;
}
}

@ -10,12 +10,12 @@ import mightypork.gamecore.graphics.GraphicsModule;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskDisplay extends InitTask {
private int width = 800, height = 600, fps = 60;
private boolean resizable, fullscreen;
private String title = "Game";
/**
* Set initial window size
*
@ -27,8 +27,8 @@ public class InitTaskDisplay extends InitTask {
this.width = width;
this.height = height;
}
/**
* Set whether the window should be resizable
*
@ -38,8 +38,8 @@ public class InitTaskDisplay extends InitTask {
{
this.resizable = resizable;
}
/**
* Set window title
*
@ -49,8 +49,8 @@ public class InitTaskDisplay extends InitTask {
{
this.title = title;
}
/**
* Set desired framerate.
*
@ -60,8 +60,8 @@ public class InitTaskDisplay extends InitTask {
{
this.fps = fps;
}
/**
* Set whether the window should start in fullscreen
*
@ -71,26 +71,28 @@ public class InitTaskDisplay extends InitTask {
{
this.fullscreen = fullscreen;
}
@Override
public void run()
{
final GraphicsModule gfx = app.getBackend().getGraphics();
gfx.setSize(width, height);
gfx.setResizable(resizable);
gfx.setTitle(title);
gfx.setTargetFps(fps);
if (fullscreen) gfx.setFullscreen(true);
gfx.createDisplay();
}
@Override
public String getName()
{
return "display";
}
}

@ -11,30 +11,30 @@ import mightypork.gamecore.core.MainLoop;
* @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";
return "main_loop";
}
}

@ -11,24 +11,24 @@ import mightypork.gamecore.resources.ResourceInitializer;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InitTaskResources extends InitTask implements ResourceInitializer {
@Override
public void run()
{
Res.load(this);
}
@Override
public String getName()
{
return "resources";
}
@Override
public String[] getDependencies()
{
return new String[] { "resource_loader" };
return new String[] { "resource_loader", "main_loop" };
}
}

@ -2,6 +2,7 @@ package mightypork.gamecore.core.plugins.screenshot;
import mightypork.gamecore.core.init.InitTask;
import mightypork.gamecore.core.plugins.AppPlugin;
import mightypork.utils.files.WorkDir;
@ -11,10 +12,10 @@ import mightypork.utils.files.WorkDir;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskPluginScreenshot extends InitTask {
private String screenshotDir;
/**
* Initialize to use the "screenshots" directory
*/
@ -22,8 +23,8 @@ public class InitTaskPluginScreenshot extends InitTask {
{
this("screenshots");
}
/**
* Initialize to use the given directory for saving.
*
@ -33,8 +34,8 @@ public class InitTaskPluginScreenshot extends InitTask {
{
this.screenshotDir = dir;
}
/**
* Set screenshot directory
*
@ -44,13 +45,19 @@ public class InitTaskPluginScreenshot extends InitTask {
{
this.screenshotDir = dir;
}
@Override
public void run()
{
WorkDir.addPath("_screenshot_dir", screenshotDir);
app.addPlugin(new ScreenshotPlugin());
app.addPlugin(getPluginImpl());
}
protected AppPlugin getPluginImpl()
{
return new ScreenshotPlugin();
}
@ -59,12 +66,12 @@ public class InitTaskPluginScreenshot extends InitTask {
{
return "plugin_screenshot";
}
@Override
public String[] getDependencies()
{
return new String[] { "workdir" };
}
}

@ -14,15 +14,16 @@ import mightypork.utils.Support;
*
* @author Ondřej Hruška (MightyPork)
*/
public class ScreenshotPlugin extends AppPlugin {
public class ScreenshotPlugin extends AppPlugin implements ScreenshotRequestListener {
/**
* Take screenshot. Called by the trigger event.
*/
void takeScreenshot()
@Override
public void onScreenshotRequest()
{
App.bus().send(new MainLoopRequest(new Runnable() {
@Override
public void run()
{

@ -11,12 +11,12 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
* @author MightyPork
*/
@SingleReceiverEvent
public class ScreenshotRequest extends BusEvent<ScreenshotPlugin> {
public class ScreenshotRequest extends BusEvent<ScreenshotRequestListener> {
@Override
protected void handleBy(ScreenshotPlugin handler)
protected void handleBy(ScreenshotRequestListener handler)
{
handler.takeScreenshot();
handler.onScreenshotRequest();
}
}

@ -267,7 +267,7 @@ public abstract class GraphicsModule extends BackendModule {
/**
* Get backend-flavoured deferred texture. This should support PNG images.
* Get backend-flavoured deferred PNG texture.
*
* @param path path to texture
* @return the deferred font
@ -276,7 +276,7 @@ public abstract class GraphicsModule extends BackendModule {
/**
* Get backend-flavoured deferred font. This should support TTF fonts.
* Get backend-flavoured deferred TTF font.
*
* @param path path to font, or font name in the system
* @return the deferred font
@ -284,6 +284,25 @@ public abstract class GraphicsModule extends BackendModule {
public abstract DeferredFont createFontResource(String path);
/**
* Get backend-flavoured deferred TTF font.
*
* @param path path to font, or font name in the system
* @param chars chars that should be supported
* @param size font size used when rendering the font to a backing texture.
* For bitmap fonts, the actual font height.
* @return the deferred font
*/
public DeferredFont createFont(String path, String chars, double size)
{
final DeferredFont font = createFontResource(path);
font.setChars(chars);
font.setSize(size);
return font;
}
/**
* Set target fps (for syncing in endFrame() call).<br>
* With vsync enabled, the target fps may not be met.
@ -416,4 +435,10 @@ public abstract class GraphicsModule extends BackendModule {
* @return screen height
*/
public abstract int getHeight();
/**
* Create the display (window) based on current settings.
*/
public abstract void createDisplay();
}

@ -60,7 +60,7 @@ public class KeyStroke {
*/
public boolean isDown()
{
return key.isDown() && (Keys.getActiveMod() == mod);
return key.isDown() && (Keys.getActiveMods() == mod);
}

@ -346,7 +346,7 @@ public class Keys {
*
* @return active mod mask (mod bits ored)
*/
public static int getActiveMod()
public static int getActiveMods()
{
int mods = 0;

@ -20,14 +20,14 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public class AsyncResourceLoader extends Thread implements ResourceLoader, Destroyable {
private final ExecutorService exs = Executors.newFixedThreadPool(2);
private final LinkedBlockingQueue<DeferredResource> toLoad = new LinkedBlockingQueue<>();
private volatile boolean stopped;
private volatile boolean mainLoopQueuing = true;
/**
* Create a resource loader.
*/
@ -35,8 +35,8 @@ public class AsyncResourceLoader extends Thread implements ResourceLoader, Destr
{
super("Deferred loader");
}
@Override
public synchronized void init()
{
@ -44,8 +44,8 @@ public class AsyncResourceLoader extends Thread implements ResourceLoader, Destr
setDaemon(true);
super.start();
}
/**
* True to queue resources that must load in rendering context to main loop.
* May cause lag at the beginning, but results in smoother performance later
@ -57,23 +57,23 @@ public class AsyncResourceLoader extends Thread implements ResourceLoader, Destr
{
mainLoopQueuing = yes;
}
@Override
public void loadResource(final DeferredResource resource)
{
if (resource.isLoaded()) return;
// textures & fonts needs to be loaded in main thread
if (Reflect.hasAnnotation(resource, MustLoadInRenderingContext.class)) {
if (!mainLoopQueuing) {
// just let it be
} else {
Log.f3("(loader) Delegating to main thread: " + Support.str(resource));
App.bus().send(new MainLoopRequest(new Runnable() {
@Override
public void run()
{
@ -81,31 +81,31 @@ public class AsyncResourceLoader extends Thread implements ResourceLoader, Destr
}
}, false));
}
return;
}
toLoad.add(resource);
}
@Override
public void run()
{
Log.f3("Asynchronous resource loader started.");
while (!stopped) {
try {
final DeferredResource def = toLoad.take();
if (def == null) continue;
if (!def.isLoaded()) {
Log.f3("(loader) Scheduling... " + Support.str(def));
exs.submit(new Runnable() {
@Override
public void run()
{
@ -115,15 +115,15 @@ public class AsyncResourceLoader extends Thread implements ResourceLoader, Destr
}
});
}
} catch (final InterruptedException ignored) {
//
}
}
}
// apparently, destroy method exists on thread :/
@SuppressWarnings("deprecation")
@Override
@ -133,5 +133,5 @@ public class AsyncResourceLoader extends Thread implements ResourceLoader, Destr
stopped = true;
exs.shutdownNow();
}
}

Loading…
Cancel
Save