From 60eaa4299702856f51769d0c5098de64d86fde65 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Mon, 30 Jun 2014 12:37:37 -0400 Subject: [PATCH] Only allow a single program instance to be run. Creates a ServerSocket instance on a port (default: 49250). Credits to marcostudios for the suggestion. Signed-off-by: Jeffrey Han --- src/itdelatrisu/opsu/Opsu.java | 43 ++++++++++++++++--- src/itdelatrisu/opsu/states/MainMenuExit.java | 2 + src/itdelatrisu/opsu/states/Options.java | 25 +++++++++++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index f40ba498..4b657682 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -29,7 +29,9 @@ import itdelatrisu.opsu.states.SongMenu; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintStream; +import java.net.ServerSocket; import org.newdawn.slick.AppGameContainer; import org.newdawn.slick.Color; @@ -66,6 +68,11 @@ public class Opsu extends StateBasedGame { STATE_GAMEPAUSEMENU = 6, STATE_GAMERANKING = 7; + /** + * Used to restrict the program to a single instance. + */ + private static ServerSocket SERVER_SOCKET; + public Opsu(String name) { super(name); } @@ -82,12 +89,6 @@ public class Opsu extends StateBasedGame { } public static void main(String[] args) { - // set path for lwjgl natives - NOT NEEDED if using JarSplice -// System.setProperty("org.lwjgl.librarypath", new File("native").getAbsolutePath()); - - // set the resource path - ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/"))); - // log all errors to a file Log.setVerbose(false); try { @@ -102,13 +103,29 @@ public class Opsu extends StateBasedGame { } }); + // parse configuration file + Options.parseOptions(); + + // only allow a single instance + try { + SERVER_SOCKET = new ServerSocket(Options.getPort()); + } catch (IOException e) { + Log.error(String.format("Another program is already running on port %d.", Options.getPort()), e); + System.exit(1); + } + + // set path for lwjgl natives - NOT NEEDED if using JarSplice +// System.setProperty("org.lwjgl.librarypath", new File("native").getAbsolutePath()); + + // set the resource path + ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/"))); + // start the game Opsu osuGame = new Opsu("opsu!"); try { AppGameContainer app = new AppGameContainer(osuGame); // basic game settings - Options.parseOptions(); int[] containerSize = Options.getContainerSize(); app.setDisplayMode(containerSize[0], containerSize[1], false); String[] icons = { "icon16.png", "icon32.png" }; @@ -147,6 +164,18 @@ public class Opsu extends StateBasedGame { Options.saveOptions(); ((AppGameContainer) this.getContainer()).destroy(); + closeSocket(); return true; } + + /** + * Closes the server socket. + */ + public static void closeSocket() { + try { + SERVER_SOCKET.close(); + } catch (IOException e) { + Log.error("Failed to close server socket.", e); + } + } } diff --git a/src/itdelatrisu/opsu/states/MainMenuExit.java b/src/itdelatrisu/opsu/states/MainMenuExit.java index ce91a7cc..d01c9ee7 100644 --- a/src/itdelatrisu/opsu/states/MainMenuExit.java +++ b/src/itdelatrisu/opsu/states/MainMenuExit.java @@ -131,6 +131,7 @@ public class MainMenuExit extends BasicGameState { if (yesButton.contains(x, y)) { Options.saveOptions(); + Opsu.closeSocket(); container.exit(); } else if (noButton.contains(x, y)) game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition(Color.black)); @@ -141,6 +142,7 @@ public class MainMenuExit extends BasicGameState { switch (key) { case Input.KEY_1: Options.saveOptions(); + Opsu.closeSocket(); container.exit(); break; case Input.KEY_2: diff --git a/src/itdelatrisu/opsu/states/Options.java b/src/itdelatrisu/opsu/states/Options.java index 4a0160ef..135bb7e2 100644 --- a/src/itdelatrisu/opsu/states/Options.java +++ b/src/itdelatrisu/opsu/states/Options.java @@ -225,6 +225,11 @@ public class Options extends BasicGameState { */ private static int screenshotFormatIndex = 0; + /** + * Port binding. + */ + private static int port = 0; + /** * Back button (shared by other states). */ @@ -673,6 +678,19 @@ public class Options extends BasicGameState { */ public static boolean isComboBurstEnabled() { return showComboBursts; } + /** + * Returns the port number to bind to. + * @return the port + */ + public static int getPort() { + if (port == 0) { + // choose a random port + port = 49250; + optionsChanged = true; // force file creation + } + return port; + } + /** * Returns the current beatmap directory. * If invalid, this will attempt to search for the directory, @@ -761,6 +779,11 @@ public class Options extends BasicGameState { case "ComboBurst": showComboBursts = Boolean.parseBoolean(value); break; + case "Port": + i = Integer.parseInt(value); + if (i > 0 && i <= 65535) + port = i; + break; } } } catch (IOException e) { @@ -814,6 +837,8 @@ public class Options extends BasicGameState { writer.newLine(); writer.write(String.format("ComboBurst = %b", showComboBursts)); writer.newLine(); + writer.write(String.format("Port = %d", port)); + writer.newLine(); writer.close(); } catch (IOException e) { Log.error(String.format("Failed to write to file '%s'.", OPTIONS_FILE), e);