|
|
|
package mightypork.rogue;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.RandomAccessFile;
|
|
|
|
import java.nio.channels.FileLock;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.logging.Level;
|
|
|
|
|
|
|
|
import javax.swing.JOptionPane;
|
|
|
|
|
|
|
|
import mightypork.gamecore.AppAccess;
|
|
|
|
import mightypork.gamecore.GameLoop;
|
|
|
|
import mightypork.gamecore.SlickLogRedirector;
|
|
|
|
import mightypork.gamecore.control.bus.EventBus;
|
|
|
|
import mightypork.gamecore.control.bus.events.*;
|
|
|
|
import mightypork.gamecore.control.interf.Destroyable;
|
|
|
|
import mightypork.gamecore.control.interf.Updateable;
|
|
|
|
import mightypork.gamecore.gui.screens.ScreenRegistry;
|
|
|
|
import mightypork.gamecore.input.InputSystem;
|
|
|
|
import mightypork.gamecore.input.KeyStroke;
|
|
|
|
import mightypork.gamecore.input.Keys;
|
|
|
|
import mightypork.gamecore.render.DisplaySystem;
|
|
|
|
import mightypork.gamecore.resources.sounds.SoundSystem;
|
|
|
|
import mightypork.rogue.events.ActionRequest;
|
|
|
|
import mightypork.rogue.events.ActionRequest.RequestType;
|
|
|
|
import mightypork.rogue.screens.test_bouncyboxes.ScreenTestBouncy;
|
|
|
|
import mightypork.rogue.screens.test_cat_sound.ScreenTestCat;
|
|
|
|
import mightypork.rogue.screens.test_font.ScreenTestFont;
|
|
|
|
import mightypork.utils.logging.Log;
|
|
|
|
import mightypork.utils.logging.LogInstance;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Main class
|
|
|
|
*
|
|
|
|
* @author MightyPork
|
|
|
|
*/
|
|
|
|
public class App implements AppAccess {
|
|
|
|
|
|
|
|
/** instance pointer */
|
|
|
|
private static App inst;
|
|
|
|
|
|
|
|
// modules
|
|
|
|
private InputSystem inputSystem;
|
|
|
|
private DisplaySystem displaySystem;
|
|
|
|
private static SoundSystem soundSystem;
|
|
|
|
private EventBus eventBus;
|
|
|
|
private GameLoop mainLoop;
|
|
|
|
private ScreenRegistry screens;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param args
|
|
|
|
*/
|
|
|
|
public static void main(String[] args)
|
|
|
|
{
|
|
|
|
Config.init();
|
|
|
|
|
|
|
|
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
|
|
|
|
|
|
|
|
inst = new App();
|
|
|
|
|
|
|
|
try {
|
|
|
|
inst.start();
|
|
|
|
} catch (final Throwable t) {
|
|
|
|
onCrash(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start the application
|
|
|
|
*/
|
|
|
|
private void start()
|
|
|
|
{
|
|
|
|
initialize();
|
|
|
|
|
|
|
|
Log.i("Starting main loop...");
|
|
|
|
|
|
|
|
// open first screen
|
|
|
|
mainLoop.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a crash
|
|
|
|
*
|
|
|
|
* @param error
|
|
|
|
*/
|
|
|
|
public static void onCrash(Throwable error)
|
|
|
|
{
|
|
|
|
if (Log.ready()) {
|
|
|
|
Log.e("The game has crashed!", error);
|
|
|
|
} else {
|
|
|
|
System.err.println("The game has crashed!");
|
|
|
|
error.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inst != null) inst.shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void shutdown()
|
|
|
|
{
|
|
|
|
Log.i("Shutting down subsystems...");
|
|
|
|
|
|
|
|
if (bus() != null) {
|
|
|
|
bus().send(new DestroyEvent());
|
|
|
|
bus().destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.i("Terminating...");
|
|
|
|
System.exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void initialize()
|
|
|
|
{
|
|
|
|
// to get dot instead of comma in floats
|
|
|
|
Locale.setDefault(Locale.ENGLISH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lock working directory
|
|
|
|
*/
|
|
|
|
initLock();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup logging
|
|
|
|
*/
|
|
|
|
final LogInstance log = Log.create("runtime", Paths.LOGS, 10);
|
|
|
|
log.setFileLevel(Level.WARNING);
|
|
|
|
log.setSysoutLevel(Level.ALL);
|
|
|
|
log.enable(Config.LOGGING_ENABLED);
|
|
|
|
log.enableSysout(Config.LOG_TO_STDOUT);
|
|
|
|
org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log));
|
|
|
|
|
|
|
|
Log.f1("Initializing subsystems...");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Event bus
|
|
|
|
*/
|
|
|
|
Log.f2("Initializing Event Bus...");
|
|
|
|
eventBus = new EventBus();
|
|
|
|
eventBus.logSending = true;
|
|
|
|
initChannels();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display
|
|
|
|
*/
|
|
|
|
Log.f2("Initializing Display System...");
|
|
|
|
displaySystem = new DisplaySystem(this);
|
|
|
|
displaySystem.createMainWindow(Const.WINDOW_W, Const.WINDOW_H, true, Config.START_IN_FS, Const.TITLEBAR);
|
|
|
|
displaySystem.setTargetFps(Const.FPS_RENDER);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Audio
|
|
|
|
*/
|
|
|
|
Log.f2("Initializing Sound System...");
|
|
|
|
soundSystem = new SoundSystem(this);
|
|
|
|
soundSystem.setMasterVolume(1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Input
|
|
|
|
*/
|
|
|
|
Log.f2("Initializing Input System...");
|
|
|
|
inputSystem = new InputSystem(this);
|
|
|
|
setupGlobalKeystrokes();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare main loop
|
|
|
|
*/
|
|
|
|
Log.f1("Preparing game systems...");
|
|
|
|
screens = new ScreenRegistry(this);
|
|
|
|
mainLoop = new MainLoop(this, screens);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load resources
|
|
|
|
*/
|
|
|
|
Log.f1("Loading resources...");
|
|
|
|
|
|
|
|
Res.load(this);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Screen registry
|
|
|
|
*/
|
|
|
|
Log.f2("Initializing screens...");
|
|
|
|
initScreens();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void initScreens()
|
|
|
|
{
|
|
|
|
Log.f3("Registering game screens...");
|
|
|
|
|
|
|
|
screens.add(new ScreenTestBouncy(this));
|
|
|
|
screens.add(new ScreenTestCat(this));
|
|
|
|
screens.add(new ScreenTestFont(this));
|
|
|
|
|
|
|
|
screens.showScreen("test.bouncy");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void initChannels()
|
|
|
|
{
|
|
|
|
Log.f3("Registering channels...");
|
|
|
|
|
|
|
|
// framework events
|
|
|
|
bus().addChannel(DestroyEvent.class, Destroyable.class);
|
|
|
|
bus().addChannel(UpdateEvent.class, Updateable.class);
|
|
|
|
|
|
|
|
// input events
|
|
|
|
bus().addChannel(ScreenChangeEvent.class, ScreenChangeEvent.Listener.class);
|
|
|
|
bus().addChannel(KeyboardEvent.class, KeyboardEvent.Listener.class);
|
|
|
|
bus().addChannel(MouseMotionEvent.class, MouseMotionEvent.Listener.class);
|
|
|
|
bus().addChannel(MouseButtonEvent.class, MouseButtonEvent.Listener.class);
|
|
|
|
|
|
|
|
// control events
|
|
|
|
bus().addChannel(ScreenRequestEvent.class, ScreenRequestEvent.Listener.class);
|
|
|
|
bus().addChannel(ResourceLoadRequest.class, ResourceLoadRequest.Listener.class);
|
|
|
|
bus().addChannel(ActionRequest.class, ActionRequest.Listener.class);
|
|
|
|
bus().addChannel(MainLoopTaskRequest.class, MainLoopTaskRequest.Listener.class);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void setupGlobalKeystrokes()
|
|
|
|
{
|
|
|
|
Log.f3("Setting up hot keys...");
|
|
|
|
|
|
|
|
// Go fullscreen
|
|
|
|
input().bindKeyStroke(new KeyStroke(Keys.KEY_F11), new Runnable() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
bus().send(new ActionRequest(RequestType.FULLSCREEN));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Take screenshot
|
|
|
|
input().bindKeyStroke(new KeyStroke(Keys.KEY_F2), new Runnable() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
bus().send(new ActionRequest(RequestType.SCREENSHOT));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Exit
|
|
|
|
input().bindKeyStroke(new KeyStroke(Keys.KEY_LCONTROL, Keys.KEY_Q), new Runnable() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
bus().send(new ActionRequest(RequestType.SHUTDOWN));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void initLock()
|
|
|
|
{
|
|
|
|
if (!Config.SINGLE_INSTANCE) return;
|
|
|
|
|
|
|
|
if (!lockInstance()) {
|
|
|
|
System.err.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
|
|
|
|
|
|
|
|
shutdown();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static 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 (final Exception e) {
|
|
|
|
System.err.println("Unable to remove lock file.");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (final Exception e) {
|
|
|
|
System.err.println("Unable to create and/or lock file.");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return sound system of the running instance
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public SoundSystem snd()
|
|
|
|
{
|
|
|
|
return soundSystem;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return input system of the running instance
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public InputSystem input()
|
|
|
|
{
|
|
|
|
return inputSystem;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return display system of the running instance
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public DisplaySystem disp()
|
|
|
|
{
|
|
|
|
return displaySystem;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return event bus
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public EventBus bus()
|
|
|
|
{
|
|
|
|
return eventBus;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|