From a27f9f8991d11d06e84113d4f31da1ea8a99f7c6 Mon Sep 17 00:00:00 2001 From: austin Date: Sat, 29 Apr 2023 18:25:42 -0400 Subject: [PATCH] NetMonitor implementation --- .gitignore | 3 + assets/Config/settings.json | 4 +- .../java/electrosphere/engine/Globals.java | 12 ++ src/main/java/electrosphere/engine/Main.java | 4 + .../game/config/UserSettings.java | 11 ++ .../net/client/ClientNetworking.java | 22 ++++ .../electrosphere/net/monitor/NetMonitor.java | 106 ++++++++++++++++++ .../net/parser/net/raw/NetworkParser.java | 25 ++++- .../net/server/ServerConnectionHandler.java | 43 ++++++- 9 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 src/main/java/electrosphere/net/monitor/NetMonitor.java diff --git a/.gitignore b/.gitignore index 8bef61f6..c80e82a3 100644 --- a/.gitignore +++ b/.gitignore @@ -32,5 +32,8 @@ /saves/arena /saves/random_sp_world +#debug +/netmonitor + #vscode .settings \ No newline at end of file diff --git a/assets/Config/settings.json b/assets/Config/settings.json index 085a37b0..fcaa2206 100644 --- a/assets/Config/settings.json +++ b/assets/Config/settings.json @@ -21,6 +21,8 @@ "graphicsDebugDrawCollisionSpheres" : false, "graphicsDebugDrawPhysicsObjects" : false, "graphicsDebugDrawMovementVectors" : false, - "graphicsDebugDrawNavmesh" : false + "graphicsDebugDrawNavmesh" : false, + + "netRunNetMonitor" : true } \ No newline at end of file diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index a173e96e..c5c14d7b 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -35,6 +35,7 @@ import electrosphere.game.server.world.ServerWorldData; import electrosphere.logger.LoggerInterface; import electrosphere.menu.WindowUtils; import electrosphere.net.client.ClientNetworking; +import electrosphere.net.monitor.NetMonitor; import electrosphere.net.server.Server; import electrosphere.net.server.player.Player; import electrosphere.net.server.player.PlayerManager; @@ -329,6 +330,13 @@ public class Globals { public static boolean RENDER_FLAG_RENDER_WHITE_BACKGROUND = false; public static boolean RENDER_FLAG_RENDER_UI = true; public static boolean RENDER_FLAG_RENDER_UI_BOUNDS = false; + + + + // + // Debugging tools + // + public static NetMonitor netMonitor; @@ -366,6 +374,10 @@ public class Globals { gameConfigCurrent = gameConfigDefault; //player manager playerManager = new PlayerManager(); + //net monitor + if(Globals.userSettings.getNetRunNetMonitor()){ + netMonitor = new NetMonitor(); + } } public static void initDefaultAudioResources(){ diff --git a/src/main/java/electrosphere/engine/Main.java b/src/main/java/electrosphere/engine/Main.java index ee4c5e97..0c931f94 100644 --- a/src/main/java/electrosphere/engine/Main.java +++ b/src/main/java/electrosphere/engine/Main.java @@ -302,6 +302,10 @@ public class Main { if(!Globals.HEADLESS && Globals.RUN_CLIENT){ Globals.audioEngine.shutdown(); } + //if netmonitor is running, close + if(Globals.netMonitor != null){ + Globals.netMonitor.close(); + } } public static long getCurrentFrame(){ diff --git a/src/main/java/electrosphere/game/config/UserSettings.java b/src/main/java/electrosphere/game/config/UserSettings.java index 90e90d58..b5010e15 100644 --- a/src/main/java/electrosphere/game/config/UserSettings.java +++ b/src/main/java/electrosphere/game/config/UserSettings.java @@ -40,10 +40,14 @@ public class UserSettings { int renderResolutionX; int renderResolutionY; //debug + //debug visuals boolean graphicsDebugDrawCollisionSpheres; boolean graphicsDebugDrawPhysicsObjects; boolean graphicsDebugDrawMovementVectors; boolean graphicsDebugDrawNavmesh; + //debug network + boolean netRunNetMonitor; + float graphicsViewRange; @@ -121,6 +125,10 @@ public class UserSettings { public boolean getGraphicsPerformanceOIT(){ return graphicsPerformanceOIT; } + + public boolean getNetRunNetMonitor(){ + return netRunNetMonitor; + } public void setGraphicsDebugDrawCollisionSpheres(boolean draw){ @@ -181,6 +189,9 @@ public class UserSettings { rVal.graphicsPerformanceOIT = true; rVal.renderResolutionX = 1920; rVal.renderResolutionY = 1080; + + //debug settings + rVal.netRunNetMonitor = false; return rVal; } diff --git a/src/main/java/electrosphere/net/client/ClientNetworking.java b/src/main/java/electrosphere/net/client/ClientNetworking.java index ef38afe0..2fa2a4f5 100644 --- a/src/main/java/electrosphere/net/client/ClientNetworking.java +++ b/src/main/java/electrosphere/net/client/ClientNetworking.java @@ -21,6 +21,8 @@ import java.math.BigInteger; import java.net.Socket; import java.net.SocketException; import java.security.spec.RSAKeyGenParameterSpec; +import java.util.LinkedList; +import java.util.List; import java.util.Properties; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; @@ -62,6 +64,9 @@ public class ClientNetworking implements Runnable{ //times for calculating ping-pong long lastPingTime = 0; long lastPongTime = 0; + + //debugging stuff + String netMonitorHandle = null; public ClientNetworking(String address, int port){ this.address = address; @@ -95,6 +100,9 @@ public class ClientNetworking implements Runnable{ // System.exit(1); // } + //Used to copy messages from network parser to NetMonitor + List netMonitorCache = new LinkedList(); + if(!this.local) { //attempt connection int connectionAttempts = 0; @@ -118,6 +126,10 @@ public class ClientNetworking implements Runnable{ } } + if(connected && Globals.netMonitor != null){ + this.netMonitorHandle = Globals.netMonitor.registerConnection(); + } + //grab input/output streams try { inputStream = socket.getInputStream(); @@ -137,6 +149,7 @@ public class ClientNetworking implements Runnable{ parser.readMessagesIn(); //outgoing messages parser.pushMessagesOut(); + //ping logic long currentTime = System.currentTimeMillis(); //basically if we haven't sent a ping in a while, send one @@ -170,6 +183,10 @@ public class ClientNetworking implements Runnable{ if(initialized){ while(parser.hasIncomingMessaage()){ NetworkMessage message = parser.popIncomingMessage(); + //net monitor + if(Globals.netMonitor != null){ + Globals.netMonitor.logMessage(netMonitorHandle, message, true); + } //print network message printMessage(message); //do something @@ -200,6 +217,11 @@ public class ClientNetworking implements Runnable{ public void queueOutgoingMessage(NetworkMessage message){ + //net monitor stuff + if(Globals.netMonitor != null){ + Globals.netMonitor.logMessage(netMonitorHandle, message, false); + } + //actually queue parser.addOutgoingMessage(message); } diff --git a/src/main/java/electrosphere/net/monitor/NetMonitor.java b/src/main/java/electrosphere/net/monitor/NetMonitor.java new file mode 100644 index 00000000..edc1ded1 --- /dev/null +++ b/src/main/java/electrosphere/net/monitor/NetMonitor.java @@ -0,0 +1,106 @@ +package electrosphere.net.monitor; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import com.google.gson.Gson; + +import electrosphere.logger.LoggerInterface; +import electrosphere.net.client.ClientNetworking; +import electrosphere.net.parser.net.message.NetworkMessage; +import electrosphere.net.server.ServerConnectionHandler; + +public class NetMonitor { + + static final String NET_MONITOR_FOLDER = "./netmonitor"; + + private Map handleFileMap = new HashMap(); + private Map writtenInitialMap = new HashMap(); + + private Gson gson; + + public NetMonitor(){ + gson = new Gson(); + } + + + /** + * Registers a new connection object + * @return Tracking handle to refer to the new connection object + */ + public String registerConnection(){ + UUID uuid = UUID.randomUUID(); + String handle = uuid.toString(); + String filePath = NET_MONITOR_FOLDER + "/" + handle + ".pacap"; + try { + FileOutputStream fileStream = new FileOutputStream(new File(filePath), false); + handleFileMap.put(handle, fileStream); + writtenInitialMap.put(handle,false); + fileStream.write("{\"messages\":[".getBytes()); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR("Can't open NetMonitor file", e); + } + return handle; + } + + /** + * Logs a message sent or received by the connection at the given handle + * @param handle The handle of the connection + * @param message The message that was sent or received + * @param isIncoming True if the message was incoming into the connection object, false if outgoing + */ + public void logMessage(String handle, NetworkMessage message, boolean isIncoming){ + long time = System.currentTimeMillis(); + LoggedMessage loggedMessage = new LoggedMessage(time,isIncoming,message); + FileOutputStream outStream = handleFileMap.get(handle); + String stringified = gson.toJson(loggedMessage); + if(writtenInitialMap.get(handle)){ + stringified = "," + stringified; + } else { + writtenInitialMap.put(handle, true); + } + try { + outStream.write(stringified.getBytes()); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR("Failed to log message to handle " + handle, e); + } + } + + /** + * Closes down the NetMonitor and closes all files + */ + public void close(){ + for(String key : handleFileMap.keySet()){ + FileOutputStream outStream = handleFileMap.get(key); + if(outStream != null){ + try { + outStream.write("]}".getBytes()); + outStream.close(); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR("NetMonitor failed to close file", e); + } + } + } + } + + + + static class LoggedMessage { + long time; + boolean isIncoming; + NetworkMessage message; + + public LoggedMessage(long time, boolean isIncoming, NetworkMessage message){ + this.time = time; + this.isIncoming = isIncoming; + this.message = message; + } + } + +} diff --git a/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java b/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java index 1938e97b..7cc5a544 100644 --- a/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java +++ b/src/main/java/electrosphere/net/parser/net/raw/NetworkParser.java @@ -4,6 +4,7 @@ import electrosphere.net.parser.net.message.NetworkMessage; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class NetworkParser { @@ -11,11 +12,11 @@ public class NetworkParser { InputStream incomingStream; OutputStream outgoingStream; - CopyOnWriteArrayList incomingMessageQueue = new CopyOnWriteArrayList(); - CopyOnWriteArrayList outgoingMessageQueue = new CopyOnWriteArrayList(); + CopyOnWriteArrayList incomingMessageQueue = new CopyOnWriteArrayList(); + CopyOnWriteArrayList outgoingMessageQueue = new CopyOnWriteArrayList(); - CopyOnWriteArrayList incomingByteQueue = new CopyOnWriteArrayList(); - CopyOnWriteArrayList outgoingByteQueue = new CopyOnWriteArrayList(); + CopyOnWriteArrayList incomingByteQueue = new CopyOnWriteArrayList(); + CopyOnWriteArrayList outgoingByteQueue = new CopyOnWriteArrayList(); @@ -77,5 +78,21 @@ public class NetworkParser { public void addOutgoingMessage(NetworkMessage message){ outgoingMessageQueue.add(message); } + + /** + * Copies the current contents of the incoming messages queue to a provided list + * @param messages The list to copy the incoming messages to + */ + public void copyIncomingMessages(List messages){ + messages.addAll(incomingMessageQueue); + } + + /** + * Copies the current contents of the outgoing messages queue to a provided list + * @param messages The list to copy the outgoing messages to + */ + public void copyOutgoingMessages(List messages){ + messages.addAll(outgoingMessageQueue); + } } diff --git a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java index 0694e677..52dc0cb6 100644 --- a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java +++ b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java @@ -29,6 +29,8 @@ import java.net.Socket; import java.net.SocketException; import java.nio.ByteBuffer; import java.security.spec.RSAKeyGenParameterSpec; +import java.util.LinkedList; +import java.util.List; import java.util.Properties; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; @@ -74,6 +76,12 @@ public class ServerConnectionHandler implements Runnable { long lastPongTime = 0; //flag to disconnect due to pipe break boolean socketException = false; + + + //debug netmonitor stuff + String netMonitorHandle; + //Used to copy messages from network parser to NetMonitor + List netMonitorCache = new LinkedList(); public ServerConnectionHandler(Socket socket) { this.socket = socket; @@ -131,9 +139,19 @@ public class ServerConnectionHandler implements Runnable { System.exit(1); } } + + NetworkMessage pingMessage = ServerMessage.constructPingMessage(); + NetworkMessage authRequestMessage = AuthMessage.constructAuthRequestMessage(); - networkParser.addOutgoingMessage(ServerMessage.constructPingMessage()); - networkParser.addOutgoingMessage(AuthMessage.constructAuthRequestMessage()); + //net monitor registration + if(Globals.netMonitor != null){ + this.netMonitorHandle = Globals.netMonitor.registerConnection(); + Globals.netMonitor.logMessage(netMonitorHandle, pingMessage, false); + Globals.netMonitor.logMessage(netMonitorHandle, authRequestMessage, false); + } + + networkParser.addOutgoingMessage(pingMessage); + networkParser.addOutgoingMessage(authRequestMessage); initialized = true; while(Main.isRunning()){ @@ -178,11 +196,32 @@ public class ServerConnectionHandler implements Runnable { void parseMessages() throws SocketException { //attempt poll incoming messages networkParser.readMessagesIn(); + + //net monitor + if(Globals.netMonitor != null){ + //incoming + netMonitorCache.clear(); + networkParser.copyIncomingMessages(netMonitorCache); + for(NetworkMessage message : netMonitorCache){ + Globals.netMonitor.logMessage(netMonitorHandle, message, true); + } + } + //ponder incoming messages while(networkParser.hasIncomingMessaage()){ NetworkMessage message = networkParser.popIncomingMessage(); serverProtocol.handleMessage(message); } + + if(Globals.netMonitor != null){ + //outgoing + netMonitorCache.clear(); + networkParser.copyOutgoingMessages(netMonitorCache); + for(NetworkMessage message : netMonitorCache){ + Globals.netMonitor.logMessage(netMonitorHandle, message, false); + } + } + //push outgoing message networkParser.pushMessagesOut(); try {