Skip to content

Commit 4b4804f

Browse files
Merge pull request #99 from ShrimpCryptid/development
refactor: Code cleanup, hid most log messages outside of debug mode.
2 parents ae7cfec + 3a019f0 commit 4b4804f

File tree

4 files changed

+28920
-28764
lines changed

4 files changed

+28920
-28764
lines changed

backend/fly.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ kill_signal = "SIGINT"
55
kill_timeout = "5s"
66
processes = []
77

8-
[mounts]
9-
source="sho_data"
10-
destination="/data"
11-
128
[build.args]
139
PORT = "8080"
1410

backend/src/main/java/server/SecretHitlerServer.java

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public class SecretHitlerServer {
7979
private static final String CODE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTWXYZ"; // u,v characters can look ambiguous
8080
private static final int CODE_LENGTH = 4;
8181

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

8585
///// Private Fields
@@ -94,6 +94,32 @@ public class SecretHitlerServer {
9494

9595
////// Private Methods
9696

97+
// TODO: Replace this with actual log levels, or a logging library.
98+
/**
99+
* Optionally prints an input string if the debug flag is enabled.
100+
*/
101+
private static void debugPrint(String input) {
102+
if (DEBUG) {
103+
System.out.print(input);
104+
}
105+
}
106+
107+
private static void debugPrintLn(String input) {
108+
debugPrint(input + "\n");
109+
}
110+
111+
/**
112+
* Prints the current list of lobbies and their players.
113+
*/
114+
private static void printLobbyStatus() {
115+
synchronized (codeToLobby) {
116+
System.out.println("Lobbies (" + codeToLobby.mappingCount() + ") : " + codeToLobby.keySet().toString());
117+
for (Map.Entry<String, Lobby> entry : codeToLobby.entrySet()) {
118+
System.out.println(" " + entry.getKey() + ": " + entry.getValue().getUserNames());
119+
}
120+
}
121+
}
122+
97123
private static int getHerokuAssignedPort() {
98124
String herokuPort = System.getenv("PORT");
99125
if (herokuPort != null) {
@@ -108,9 +134,7 @@ public static void main(String[] args) {
108134
loadDatabaseBackup();
109135
removeInactiveLobbies(); // immediately clean in case of redundant lobbies.
110136

111-
if (DEBUG) {
112-
System.out.println("Running in DEBUG mode.");
113-
}
137+
debugPrintLn("Running in DEBUG mode.");
114138

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

148173
// Add timer for periodic updates.
149-
int delay = 0;
150-
int period = (int) (UPDATE_FREQUENCY_MIN * 60.0f * 1000.0f);
174+
int delayMs = 0;
175+
int periodMs = (int) (UPDATE_FREQUENCY_SECONDS * 1000.0f);
151176
Timer timer = new Timer();
152177
timer.schedule(new TimerTask() {
153178
@Override
154179
public void run() {
155180
removeInactiveLobbies();
181+
if (!codeToLobby.isEmpty()) {
182+
printLobbyStatus();
183+
}
156184
// If there are active lobbies, store a backup of the game.
157185
if (!codeToLobby.isEmpty() && hasLobbyChanged) {
158186
storeDatabaseBackup();
159187
hasLobbyChanged = false;
160188
}
161189
}
162-
}, delay, period);
190+
}, delayMs, periodMs);
163191
}
164192

165193
/**
@@ -191,8 +219,8 @@ private static void removeInactiveLobbies() {
191219
}
192220
}
193221
if (removedCount > 0) {
194-
System.out.println(String.format("Removed %d inactive lobbies: %s", removedCount, removedLobbyCodes));
195-
System.out.println("Available lobbies: " + codeToLobby.keySet());
222+
debugPrintLn(String.format("Removed %d inactive lobbies: %s", removedCount, removedLobbyCodes));
223+
printLobbyStatus();
196224
hasLobbyChanged = true;
197225
}
198226
}
@@ -226,9 +254,10 @@ private static Connection getDatabaseConnection() {
226254

227255
Class.forName("org.postgresql.Driver");
228256
c = DriverManager.getConnection(dbUrl, username, password);
229-
System.out.println("Successfully connected to database.");
257+
debugPrintLn("Successfully connected to database.");
230258
return c;
231259
} catch (Exception e) {
260+
// Print failures no matter what
232261
System.out.println("Failed to connect to database.");
233262
System.err.println(e);
234263
return null;
@@ -278,7 +307,7 @@ private static void loadDatabaseBackup() {
278307
ObjectInputStream objectStream = new ObjectInputStream(lobbyByteStream)) {
279308
codeToLobby = (ConcurrentHashMap<String, Lobby>) objectStream.readObject();
280309
objectStream.close();
281-
System.out.println("Successfully parsed lobby data from the database.");
310+
debugPrintLn("Successfully parsed lobby data from the database.");
282311
} catch (Exception e) {
283312
System.out.println("Failed to parse lobby data from stored backup. ");
284313
System.err.println(e.getClass().getName() + ": " + e.getMessage());
@@ -288,6 +317,7 @@ private static void loadDatabaseBackup() {
288317
System.out.println("Failed to retrieve lobby backups from the database.");
289318
System.err.println(e.getClass().getName() + ": " + e.getMessage());
290319
}
320+
printLobbyStatus();
291321
}
292322

293323
private static void storeDatabaseBackup() {
@@ -333,7 +363,7 @@ private static void storeDatabaseBackup() {
333363
System.err.println(e);
334364
return;
335365
}
336-
System.out.println("Successfully saved Lobby state to the database.");
366+
debugPrintLn("Successfully saved Lobby state to the database.");
337367
}
338368

339369
/**
@@ -497,7 +527,7 @@ private static String generateCode() {
497527
*/
498528
private static void onWebsocketConnect(WsConnectContext ctx) {
499529
if (ctx.queryParam(PARAM_LOBBY) == null || ctx.queryParam(PARAM_NAME) == null) {
500-
System.out.println("A websocket request was missing a parameter and was disconnected.");
530+
debugPrintLn("A websocket request was missing a parameter and was disconnected.");
501531
ctx.session.close(StatusCode.PROTOCOL,
502532
"Must have the '" + PARAM_LOBBY + "' and '" + PARAM_NAME + "' parameters.");
503533
return;
@@ -508,32 +538,32 @@ private static void onWebsocketConnect(WsConnectContext ctx) {
508538
String name = ctx.queryParam(PARAM_NAME);
509539

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

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

522552
Lobby lobby = codeToLobby.get(code);
523553
if (lobby.hasUserWithName(name)) { // duplicate names not allowed
524-
System.out.println("FAILED (Repeat username)");
554+
debugPrintLn("FAILED (Repeat username)");
525555
ctx.session.close(StatusCode.PROTOCOL, "A user with the name " + name + " is already in the lobby.");
526556
return;
527557
} else if (lobby.isFull()) {
528-
System.out.println("FAILED (Lobby is full)");
558+
debugPrintLn("FAILED (Lobby is full)");
529559
ctx.session.close(StatusCode.PROTOCOL, "The lobby " + code + " is currently full.");
530560
return;
531561
} else if (lobby.isInGame() && !lobby.canAddUserDuringGame(name)) {
532-
System.out.println("FAILED (Lobby in game)");
562+
debugPrintLn("FAILED (Lobby in game)");
533563
ctx.session.close(StatusCode.PROTOCOL, "The lobby " + code + " is currently in a game..");
534564
return;
535565
}
536-
System.out.println("SUCCESS");
566+
debugPrintLn("SUCCESS");
537567
lobby.addUser(ctx, name);
538568
userToLobby.put(ctx, lobby); // keep track of which lobby this connection is in.
539569
lobby.updateAllUsers();
@@ -575,23 +605,21 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
575605
String name = message.getString(PARAM_NAME);
576606
String lobbyCode = message.getString(PARAM_LOBBY);
577607

578-
String log_message = "Received a message from user '" + name + "' in lobby '" + lobbyCode + "' ("
608+
String logMessage = "Received a message from user '" + name + "' in lobby '" + lobbyCode + "' ("
579609
+ ctx.message() + "): ";
580-
int log_length = log_message.length();
581-
System.out.print(log_message);
610+
int log_length = logMessage.length();
582611

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

589618
Lobby lobby = codeToLobby.get(lobbyCode);
590619

591620
synchronized (lobby) {
592-
593621
if (!lobby.hasUser(ctx, name)) {
594-
System.out.println("FAILED (Lobby does not have the user)");
622+
debugPrintLn(logMessage + " FAILED (Lobby does not have the user)");
595623
ctx.session.close(StatusCode.PROTOCOL, "The user is not in the lobby " + lobbyCode + ".");
596624
return;
597625
}
@@ -606,8 +634,8 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
606634
sendOKMessage = false;
607635
updateUsers = false;
608636
// Erase the previous line with spaces and \r
609-
System.out.print("\r" + (' ' * log_length));
610-
System.out.print("\r");
637+
debugPrint("\r" + (' ' * log_length));
638+
debugPrint("\r");
611639
JSONObject msg = new JSONObject();
612640
msg.put(PARAM_PACKET_TYPE, PACKET_PONG);
613641
ctx.send(msg.toString());
@@ -695,21 +723,23 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
695723
break;
696724

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

701729
if (sendOKMessage) {
702-
System.out.println("SUCCESS");
730+
debugPrintLn(logMessage + " SUCCESS");
703731
JSONObject msg = new JSONObject();
704732
msg.put(PARAM_PACKET_TYPE, PACKET_OK);
705733
ctx.send(msg.toString());
706734
}
707735

708736
} catch (NullPointerException e) {
709-
System.out.println("FAILED (" + e.toString() + ")");
737+
// Show error messages by default, since they indicate API access
738+
// issues.
739+
System.out.println(logMessage + " FAILED (" + e.toString() + ")");
710740
ctx.session.close(StatusCode.PROTOCOL, "NullPointerException:" + e.toString());
711741
} catch (RuntimeException e) {
712-
System.out.println("FAILED (" + e.toString() + ")");
742+
System.out.println(logMessage + " FAILED (" + e.toString() + ")");
713743
ctx.session.close(StatusCode.PROTOCOL, "RuntimeException:" + e.toString());
714744
}
715745
if (updateUsers) {
@@ -719,6 +749,9 @@ private static void onWebSocketMessage(WsMessageContext ctx) {
719749
hasLobbyChanged = true;
720750
}
721751

752+
// TODO: This is bad. This is bad code practice. Exceptions should not be
753+
// used for control flow.
754+
722755
/**
723756
* Verifies that the user is the president.
724757
*

backend/src/main/java/server/util/Lobby.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.concurrent.ConcurrentHashMap;
1515
import java.util.concurrent.ConcurrentLinkedQueue;
1616
import java.util.concurrent.ConcurrentSkipListSet;
17+
import java.util.stream.Collectors;
1718

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

97+
/**
98+
* Returns the list of usernames currently in the lobby or game. Includes
99+
* bot names if the game is running and has bots.
100+
*/
101+
synchronized public List<String> getUserNames() {
102+
if (game != null) {
103+
return game.getPlayerList().stream().map(player -> player.getUsername()).collect(Collectors.toList());
104+
} else {
105+
return new ArrayList<String>(userToUsername.values());
106+
}
107+
}
108+
96109
/////// User Management
97110
// <editor-fold desc="User Management">
98111

0 commit comments

Comments
 (0)