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 <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2014-06-30 12:37:37 -04:00
parent 6bdb026447
commit 60eaa42997
3 changed files with 63 additions and 7 deletions

View File

@ -29,7 +29,9 @@ import itdelatrisu.opsu.states.SongMenu;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.net.ServerSocket;
import org.newdawn.slick.AppGameContainer; import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.Color; import org.newdawn.slick.Color;
@ -66,6 +68,11 @@ public class Opsu extends StateBasedGame {
STATE_GAMEPAUSEMENU = 6, STATE_GAMEPAUSEMENU = 6,
STATE_GAMERANKING = 7; STATE_GAMERANKING = 7;
/**
* Used to restrict the program to a single instance.
*/
private static ServerSocket SERVER_SOCKET;
public Opsu(String name) { public Opsu(String name) {
super(name); super(name);
} }
@ -82,12 +89,6 @@ public class Opsu extends StateBasedGame {
} }
public static void main(String[] args) { 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 all errors to a file
Log.setVerbose(false); Log.setVerbose(false);
try { 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 // start the game
Opsu osuGame = new Opsu("opsu!"); Opsu osuGame = new Opsu("opsu!");
try { try {
AppGameContainer app = new AppGameContainer(osuGame); AppGameContainer app = new AppGameContainer(osuGame);
// basic game settings // basic game settings
Options.parseOptions();
int[] containerSize = Options.getContainerSize(); int[] containerSize = Options.getContainerSize();
app.setDisplayMode(containerSize[0], containerSize[1], false); app.setDisplayMode(containerSize[0], containerSize[1], false);
String[] icons = { "icon16.png", "icon32.png" }; String[] icons = { "icon16.png", "icon32.png" };
@ -147,6 +164,18 @@ public class Opsu extends StateBasedGame {
Options.saveOptions(); Options.saveOptions();
((AppGameContainer) this.getContainer()).destroy(); ((AppGameContainer) this.getContainer()).destroy();
closeSocket();
return true; 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);
}
}
} }

View File

@ -131,6 +131,7 @@ public class MainMenuExit extends BasicGameState {
if (yesButton.contains(x, y)) { if (yesButton.contains(x, y)) {
Options.saveOptions(); Options.saveOptions();
Opsu.closeSocket();
container.exit(); container.exit();
} else if (noButton.contains(x, y)) } else if (noButton.contains(x, y))
game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_MAINMENU, new EmptyTransition(), new FadeInTransition(Color.black));
@ -141,6 +142,7 @@ public class MainMenuExit extends BasicGameState {
switch (key) { switch (key) {
case Input.KEY_1: case Input.KEY_1:
Options.saveOptions(); Options.saveOptions();
Opsu.closeSocket();
container.exit(); container.exit();
break; break;
case Input.KEY_2: case Input.KEY_2:

View File

@ -225,6 +225,11 @@ public class Options extends BasicGameState {
*/ */
private static int screenshotFormatIndex = 0; private static int screenshotFormatIndex = 0;
/**
* Port binding.
*/
private static int port = 0;
/** /**
* Back button (shared by other states). * Back button (shared by other states).
*/ */
@ -673,6 +678,19 @@ public class Options extends BasicGameState {
*/ */
public static boolean isComboBurstEnabled() { return showComboBursts; } 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. * Returns the current beatmap directory.
* If invalid, this will attempt to search for the directory, * If invalid, this will attempt to search for the directory,
@ -761,6 +779,11 @@ public class Options extends BasicGameState {
case "ComboBurst": case "ComboBurst":
showComboBursts = Boolean.parseBoolean(value); showComboBursts = Boolean.parseBoolean(value);
break; break;
case "Port":
i = Integer.parseInt(value);
if (i > 0 && i <= 65535)
port = i;
break;
} }
} }
} catch (IOException e) { } catch (IOException e) {
@ -814,6 +837,8 @@ public class Options extends BasicGameState {
writer.newLine(); writer.newLine();
writer.write(String.format("ComboBurst = %b", showComboBursts)); writer.write(String.format("ComboBurst = %b", showComboBursts));
writer.newLine(); writer.newLine();
writer.write(String.format("Port = %d", port));
writer.newLine();
writer.close(); writer.close();
} catch (IOException e) { } catch (IOException e) {
Log.error(String.format("Failed to write to file '%s'.", OPTIONS_FILE), e); Log.error(String.format("Failed to write to file '%s'.", OPTIONS_FILE), e);