package mightypork.rogue; import java.io.File; import java.io.RandomAccessFile; import java.nio.channels.FileLock; import javax.swing.JOptionPane; import mightypork.rogue.display.DisplaySystem; import mightypork.rogue.display.Screen; import mightypork.rogue.display.ScreenSplash; import mightypork.rogue.input.InputSystem; import mightypork.rogue.input.KeyStroke; import mightypork.rogue.input.events.MouseMotionEvent; import mightypork.rogue.sounds.SoundSystem; import mightypork.rogue.tasks.TaskTakeScreenshot; import mightypork.rogue.util.Utils; import mightypork.utils.logging.Log; import mightypork.utils.logging.LogInstance; import mightypork.utils.patterns.Destroyable; import mightypork.utils.patterns.subscription.MessageBus; import mightypork.utils.time.TimerDelta; import mightypork.utils.time.TimerInterpolating; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.Display; public class App implements Destroyable { /** instance pointer */ private static App inst; private InputSystem input; private SoundSystem sounds; private DisplaySystem display; private MessageBus events; /** current screen */ private Screen screen; /** Flag that screenshot is scheduled to be taken next loop */ private boolean scheduledScreenshot = false; /** * Get the instance * * @return instance of App */ public static App inst() { return inst; } /** * @param args */ public static void main(String[] args) { Thread.setDefaultUncaughtExceptionHandler(new CrashHandler()); inst = new App(); try { inst.start(); } catch (Throwable t) { onCrash(t); } } /** * Show crash report dialog with error stack trace. * * @param error */ public static void onCrash(Throwable error) { Log.e("The game has crashed.", error); inst.exit(); } /** * Quit to OS
* Destroy app & exit VM */ public void exit() { destroy(); System.exit(0); } /** * Get current screen * * @return screen */ public Screen getCurrentScreen() { return screen; } public void initialize() { Log.i("Initializing subsystems"); initLock(); initBus(); initLogger(); initDisplay(); initSound(); initInput(); } @Override public void destroy() { if (sounds != null) sounds.destroy(); if (input != null) input.destroy(); if (display != null) display.destroy(); } private void initLock() { if (!Config.SINGLE_INSTANCE) return; if (!lockInstance()) { System.out.println("Working directory is locked.\nOnly one instance can run at a time."); //@formatter:off JOptionPane.showMessageDialog( null, "The game is already running.", "Instance error", JOptionPane.ERROR_MESSAGE ); //@formatter:on exit(); return; } } private boolean lockInstance() { final File lockFile = new File(Paths.WORKDIR, ".lock"); try { final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw"); final FileLock fileLock = randomAccessFile.getChannel().tryLock(); if (fileLock != null) { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { fileLock.release(); randomAccessFile.close(); lockFile.delete(); } catch (Exception e) { System.err.println("Unable to remove lock file."); e.printStackTrace(); } } }); return true; } } catch (Exception e) { System.err.println("Unable to create and/or lock file."); e.printStackTrace(); } return false; } /** * initialize inputs */ private void initBus() { events = new MessageBus(); events.addSubscriber(this); } /** * initialize sound system */ private void initSound() { sounds = new SoundSystem(); sounds.setMasterVolume(1); } /** * initialize inputs */ private void initInput() { input = new InputSystem(); input.bindKeyStroke(new KeyStroke(Keyboard.KEY_F2), new Runnable() { @Override public void run() { Log.f3("F2, taking screenshot."); scheduledScreenshot = true; } }); input.bindKeyStroke(new KeyStroke(false, Keyboard.KEY_F11), new Runnable() { @Override public void run() { Log.f3("F11, toggling fullscreen."); display.switchFullscreen(); } }); input.bindKeyStroke(new KeyStroke(Keyboard.KEY_LCONTROL, Keyboard.KEY_Q), new Runnable() { @Override public void run() { Log.f3("CTRL+Q, shutting down."); exit(); } }); } /** * initialize display */ private void initDisplay() { display = new DisplaySystem(); display.createMainWindow(Const.WINDOW_W, Const.WINDOW_H, true, Config.START_IN_FS, Const.TITLEBAR); display.setTargetFps(Const.FPS_RENDER); } /** * initialize main logger */ private void initLogger() { LogInstance li = Log.create("runtime", Paths.LOGS, 10); li.enable(Config.LOGGING_ENABLED); li.enableSysout(Config.LOG_TO_STDOUT); } private void start() { initialize(); mainLoop(); exit(); } /** timer */ private TimerDelta timerRender; private TimerInterpolating timerGui; private void mainLoop() { screen = new ScreenSplash(); screen.setActive(true); timerRender = new TimerDelta(); timerGui = new TimerInterpolating(Const.FPS_GUI_UPDATE); while (!display.isCloseRequested()) { display.beginFrame(); // gui update timerGui.sync(); int ticks = timerGui.getSkipped(); if (ticks >= 1) { input.poll(); timerGui.startNewFrame(); } double delta = timerRender.getDelta(); sounds.update(delta); // Screen screen.update(delta); if (scheduledScreenshot) { takeScreenshot(); scheduledScreenshot = false; } display.endFrame(); } } /** * Do take a screenshot */ public void takeScreenshot() { sounds.getEffect("gui.shutter").play(1); Utils.runAsThread(new TaskTakeScreenshot()); } // // static accessors // /** * @return sound system of the running instance */ public static SoundSystem soundsys() { return inst.sounds; } /** * @return input system of the running instance */ public static InputSystem input() { return inst.input; } /** * @return display system of the running instance */ public static DisplaySystem disp() { return inst.display; } /** * @return event bus of the running instance */ public static MessageBus msgbus() { return inst.events; } /** * @return screen of the running instance */ public static Screen screen() { return inst.getCurrentScreen(); } public static boolean broadcast(Object message) { boolean was = msgbus().broadcast(message); if (!was) Log.w("Message not accepted by any channel: " + message); return was; } }