Skip to content

Commit

Permalink
Merge pull request #99 from ShrimpCryptid/development
Browse files Browse the repository at this point in the history
refactor: Code cleanup, hid most log messages outside of debug mode.
  • Loading branch information
ShrimpCryptid authored Nov 27, 2023
2 parents ae7cfec + 3a019f0 commit 4b4804f
Show file tree
Hide file tree
Showing 4 changed files with 28,920 additions and 28,764 deletions.
4 changes: 0 additions & 4 deletions backend/fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ kill_signal = "SIGINT"
kill_timeout = "5s"
processes = []

[mounts]
source="sho_data"
destination="/data"

[build.args]
PORT = "8080"

Expand Down
99 changes: 66 additions & 33 deletions backend/src/main/java/server/SecretHitlerServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class SecretHitlerServer {
private static final String CODE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTWXYZ"; // u,v characters can look ambiguous
private static final int CODE_LENGTH = 4;

private static final float UPDATE_FREQUENCY_MIN = 1;
private static final float UPDATE_FREQUENCY_SECONDS = 60;
// </editor-fold>

///// Private Fields
Expand All @@ -94,6 +94,32 @@ public class SecretHitlerServer {

////// Private Methods

// TODO: Replace this with actual log levels, or a logging library.
/**
* Optionally prints an input string if the debug flag is enabled.
*/
private static void debugPrint(String input) {
if (DEBUG) {
System.out.print(input);
}
}

private static void debugPrintLn(String input) {
debugPrint(input + "\n");
}

/**
* Prints the current list of lobbies and their players.
*/
private static void printLobbyStatus() {
synchronized (codeToLobby) {
System.out.println("Lobbies (" + codeToLobby.mappingCount() + ") : " + codeToLobby.keySet().toString());
for (Map.Entry<String, Lobby> entry : codeToLobby.entrySet()) {
System.out.println(" " + entry.getKey() + ": " + entry.getValue().getUserNames());
}
}
}

private static int getHerokuAssignedPort() {
String herokuPort = System.getenv("PORT");
if (herokuPort != null) {
Expand All @@ -108,9 +134,7 @@ public static void main(String[] args) {
loadDatabaseBackup();
removeInactiveLobbies(); // immediately clean in case of redundant lobbies.

if (DEBUG) {
System.out.println("Running in DEBUG mode.");
}
debugPrintLn("Running in DEBUG mode.");

// Only initialize Javalin communication after the database has been queried.
Javalin serverApp = Javalin.create(config -> {
Expand Down Expand Up @@ -140,26 +164,30 @@ public static void main(String[] args) {
// Add hook for termination that backs up the lobbies to the database.
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
System.out.println("Attempting to back up lobby data.");
debugPrintLn("Attempting to back up lobby data.");
storeDatabaseBackup();
printLobbyStatus();
}
});

// Add timer for periodic updates.
int delay = 0;
int period = (int) (UPDATE_FREQUENCY_MIN * 60.0f * 1000.0f);
int delayMs = 0;
int periodMs = (int) (UPDATE_FREQUENCY_SECONDS * 1000.0f);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
removeInactiveLobbies();
if (!codeToLobby.isEmpty()) {
printLobbyStatus();
}
// If there are active lobbies, store a backup of the game.
if (!codeToLobby.isEmpty() && hasLobbyChanged) {
storeDatabaseBackup();
hasLobbyChanged = false;
}
}
}, delay, period);
}, delayMs, periodMs);
}

/**
Expand Down Expand Up @@ -191,8 +219,8 @@ private static void removeInactiveLobbies() {
}
}
if (removedCount > 0) {
System.out.println(String.format("Removed %d inactive lobbies: %s", removedCount, removedLobbyCodes));
System.out.println("Available lobbies: " + codeToLobby.keySet());
debugPrintLn(String.format("Removed %d inactive lobbies: %s", removedCount, removedLobbyCodes));
printLobbyStatus();
hasLobbyChanged = true;
}
}
Expand Down Expand Up @@ -226,9 +254,10 @@ private static Connection getDatabaseConnection() {

Class.forName("org.postgresql.Driver");
c = DriverManager.getConnection(dbUrl, username, password);
System.out.println("Successfully connected to database.");
debugPrintLn("Successfully connected to database.");
return c;
} catch (Exception e) {
// Print failures no matter what
System.out.println("Failed to connect to database.");
System.err.println(e);
return null;
Expand Down Expand Up @@ -278,7 +307,7 @@ private static void loadDatabaseBackup() {
ObjectInputStream objectStream = new ObjectInputStream(lobbyByteStream)) {
codeToLobby = (ConcurrentHashMap<String, Lobby>) objectStream.readObject();
objectStream.close();
System.out.println("Successfully parsed lobby data from the database.");
debugPrintLn("Successfully parsed lobby data from the database.");
} catch (Exception e) {
System.out.println("Failed to parse lobby data from stored backup. ");
System.err.println(e.getClass().getName() + ": " + e.getMessage());
Expand All @@ -288,6 +317,7 @@ private static void loadDatabaseBackup() {
System.out.println("Failed to retrieve lobby backups from the database.");
System.err.println(e.getClass().getName() + ": " + e.getMessage());
}
printLobbyStatus();
}

private static void storeDatabaseBackup() {
Expand Down Expand Up @@ -333,7 +363,7 @@ private static void storeDatabaseBackup() {
System.err.println(e);
return;
}
System.out.println("Successfully saved Lobby state to the database.");
debugPrintLn("Successfully saved Lobby state to the database.");
}

/**
Expand Down Expand Up @@ -497,7 +527,7 @@ private static String generateCode() {
*/
private static void onWebsocketConnect(WsConnectContext ctx) {
if (ctx.queryParam(PARAM_LOBBY) == null || ctx.queryParam(PARAM_NAME) == null) {
System.out.println("A websocket request was missing a parameter and was disconnected.");
debugPrintLn("A websocket request was missing a parameter and was disconnected.");
ctx.session.close(StatusCode.PROTOCOL,
"Must have the '" + PARAM_LOBBY + "' and '" + PARAM_NAME + "' parameters.");
return;
Expand All @@ -508,32 +538,32 @@ private static void onWebsocketConnect(WsConnectContext ctx) {
String name = ctx.queryParam(PARAM_NAME);

if (code == null || name == null || name.isEmpty() || name.isBlank()) {
System.out.println("FAILED (Lobby or name is empty/null)");
debugPrintLn("FAILED (Lobby or name is empty/null)");
ctx.session.close(StatusCode.PROTOCOL, "Lobby and name must be specified.");
}

System.out.print("Attempting to connect user '" + name + "' to lobby '" + code + "': ");
debugPrint("Attempting to connect user '" + name + "' to lobby '" + code + "': ");
if (!codeToLobby.containsKey(code)) { // the lobby does not exist.
System.out.println("FAILED (The lobby does not exist)");
debugPrintLn("FAILED (The lobby does not exist)");
ctx.session.close(StatusCode.PROTOCOL, "The lobby '" + code + "' does not exist.");
return;
}

Lobby lobby = codeToLobby.get(code);
if (lobby.hasUserWithName(name)) { // duplicate names not allowed
System.out.println("FAILED (Repeat username)");
debugPrintLn("FAILED (Repeat username)");
ctx.session.close(StatusCode.PROTOCOL, "A user with the name " + name + " is already in the lobby.");
return;
} else if (lobby.isFull()) {
System.out.println("FAILED (Lobby is full)");
debugPrintLn("FAILED (Lobby is full)");
ctx.session.close(StatusCode.PROTOCOL, "The lobby " + code + " is currently full.");
return;
} else if (lobby.isInGame() && !lobby.canAddUserDuringGame(name)) {
System.out.println("FAILED (Lobby in game)");
debugPrintLn("FAILED (Lobby in game)");
ctx.session.close(StatusCode.PROTOCOL, "The lobby " + code + " is currently in a game..");
return;
}
System.out.println("SUCCESS");
debugPrintLn("SUCCESS");
lobby.addUser(ctx, name);
userToLobby.put(ctx, lobby); // keep track of which lobby this connection is in.
lobby.updateAllUsers();
Expand Down Expand Up @@ -575,23 +605,21 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
String name = message.getString(PARAM_NAME);
String lobbyCode = message.getString(PARAM_LOBBY);

String log_message = "Received a message from user '" + name + "' in lobby '" + lobbyCode + "' ("
String logMessage = "Received a message from user '" + name + "' in lobby '" + lobbyCode + "' ("
+ ctx.message() + "): ";
int log_length = log_message.length();
System.out.print(log_message);
int log_length = logMessage.length();

if (!codeToLobby.containsKey(lobbyCode)) {
System.out.println("FAILED (Lobby requested does not exist)");
debugPrintLn(logMessage + " FAILED (Lobby requested does not exist)");
ctx.session.close(StatusCode.PROTOCOL, "The lobby does not exist.");
return;
}

Lobby lobby = codeToLobby.get(lobbyCode);

synchronized (lobby) {

if (!lobby.hasUser(ctx, name)) {
System.out.println("FAILED (Lobby does not have the user)");
debugPrintLn(logMessage + " FAILED (Lobby does not have the user)");
ctx.session.close(StatusCode.PROTOCOL, "The user is not in the lobby " + lobbyCode + ".");
return;
}
Expand All @@ -606,8 +634,8 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
sendOKMessage = false;
updateUsers = false;
// Erase the previous line with spaces and \r
System.out.print("\r" + (' ' * log_length));
System.out.print("\r");
debugPrint("\r" + (' ' * log_length));
debugPrint("\r");
JSONObject msg = new JSONObject();
msg.put(PARAM_PACKET_TYPE, PACKET_PONG);
ctx.send(msg.toString());
Expand Down Expand Up @@ -695,21 +723,23 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
break;

default: // This is an invalid command.
throw new RuntimeException("FAILED (unrecognized command " + message.get(PARAM_COMMAND) + ")");
throw new RuntimeException("unrecognized command " + message.get(PARAM_COMMAND));
} // End switch

if (sendOKMessage) {
System.out.println("SUCCESS");
debugPrintLn(logMessage + " SUCCESS");
JSONObject msg = new JSONObject();
msg.put(PARAM_PACKET_TYPE, PACKET_OK);
ctx.send(msg.toString());
}

} catch (NullPointerException e) {
System.out.println("FAILED (" + e.toString() + ")");
// Show error messages by default, since they indicate API access
// issues.
System.out.println(logMessage + " FAILED (" + e.toString() + ")");
ctx.session.close(StatusCode.PROTOCOL, "NullPointerException:" + e.toString());
} catch (RuntimeException e) {
System.out.println("FAILED (" + e.toString() + ")");
System.out.println(logMessage + " FAILED (" + e.toString() + ")");
ctx.session.close(StatusCode.PROTOCOL, "RuntimeException:" + e.toString());
}
if (updateUsers) {
Expand All @@ -719,6 +749,9 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
hasLobbyChanged = true;
}

// TODO: This is bad. This is bad code practice. Exceptions should not be
// used for control flow.

/**
* Verifies that the user is the president.
*
Expand Down
13 changes: 13 additions & 0 deletions backend/src/main/java/server/util/Lobby.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.stream.Collectors;

/**
* A Lobby holds a collection of websocket connections, each representing a
Expand Down Expand Up @@ -93,6 +94,18 @@ synchronized public Set<WsContext> getConnections() {
return userToUsername.keySet();
}

/**
* Returns the list of usernames currently in the lobby or game. Includes
* bot names if the game is running and has bots.
*/
synchronized public List<String> getUserNames() {
if (game != null) {
return game.getPlayerList().stream().map(player -> player.getUsername()).collect(Collectors.toList());
} else {
return new ArrayList<String>(userToUsername.values());
}
}

/////// User Management
// <editor-fold desc="User Management">

Expand Down
Loading

0 comments on commit 4b4804f

Please sign in to comment.