diff --git a/src/main/java/electrosphere/engine/LoadingThread.java b/src/main/java/electrosphere/engine/LoadingThread.java index 966c23a0..24ad3317 100644 --- a/src/main/java/electrosphere/engine/LoadingThread.java +++ b/src/main/java/electrosphere/engine/LoadingThread.java @@ -362,12 +362,6 @@ public class LoadingThread extends Thread { } static void initClientTerrainManager(){ - while(!Globals.clientConnection.getClientProtocol().hasReceivedWorld()){ - try { - TimeUnit.MILLISECONDS.sleep(5); - } catch (InterruptedException ex) { - } - } Globals.clientTerrainManager = new ClientTerrainManager(Globals.clientWorldData); } @@ -393,6 +387,14 @@ public class LoadingThread extends Thread { Globals.spawnPoint.set((float)startX,(float)Globals.commonWorldData.getElevationAtPoint(new Vector3d(startX,0,startZ)),(float)startZ); } } else { + //basically wait for the client to receive the world metadata + while(!Globals.clientConnection.getClientProtocol().hasReceivedWorld()){ + try { + TimeUnit.MILLISECONDS.sleep(5); + } catch (InterruptedException ex) { + } + } + //then create common world data Globals.commonWorldData = new CommonWorldData(Globals.clientWorldData, Globals.clientTerrainManager); } } @@ -434,16 +436,16 @@ public class LoadingThread extends Thread { static void initDrawCellManager(){ + //if it hasn't already been initialized, create draw cell manager if(Globals.drawCellManager == null){ Globals.drawCellManager = new DrawCellManager( Globals.commonWorldData, Globals.clientTerrainManager, Globals.clientPlayerData.getWorldPositionX(), Globals.clientPlayerData.getWorldPositionY() - // Globals.terrainManager.getDynamicInterpolationRatio(), - // Globals.terrainManager.getRandomDampener() ); - } else { + } + if(Globals.RUN_CLIENT == true) { //set our draw cell manager to actually generate drawable chunks Globals.drawCellManager.setGenerateDrawables(true); } diff --git a/src/main/java/electrosphere/menu/MenuGenerators.java b/src/main/java/electrosphere/menu/MenuGenerators.java index 9875a776..d7462e4c 100644 --- a/src/main/java/electrosphere/menu/MenuGenerators.java +++ b/src/main/java/electrosphere/menu/MenuGenerators.java @@ -1,5 +1,6 @@ package electrosphere.menu; +import electrosphere.auth.AuthenticationManager; import electrosphere.controls.ControlHandler.ControlsState; import electrosphere.engine.LoadingThread; import electrosphere.entity.Entity; @@ -353,16 +354,38 @@ public class MenuGenerators { portInput.setText(NetUtils.getPort() + ""); rVal.addChild(portInput); + //label (address) + Label usernameLabel = new Label(100,screenTop + 350,1.0f); + usernameLabel.setText("Username"); + rVal.addChild(usernameLabel); + + //text entry (address) + TextInput usernameInput = new TextInput(100,screenTop + 425,1.0f); + usernameInput.setText(""); + rVal.addChild(usernameInput); + + //label (port) + Label passwordLabel = new Label(100,screenTop + 500,1.0f); + passwordLabel.setText("Password"); + rVal.addChild(passwordLabel); + + //text entry (port) + TextInput passwordInput = new TextInput(100,screenTop + 575,1.0f); + passwordInput.setText(""); + rVal.addChild(passwordInput); + //button (connect) Button connectButton = new Button(); - Label connectLabel = new Label(100,screenTop + 350,1.0f); + Label connectLabel = new Label(100,screenTop + 650,1.0f); connectLabel.setText("Connect"); connectButton.addChild(connectLabel); rVal.addChild(connectButton); connectButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){ NetUtils.setAddress(addressInput.getText()); NetUtils.setPort(Integer.parseInt(portInput.getText())); - LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_MAIN_GAME); + Globals.clientUsername = usernameInput.getText(); + Globals.clientPassword = AuthenticationManager.getHashedString(passwordInput.getText()); + LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CHARACTER_SERVER); Globals.loadingThreadsList.add(clientThread); Globals.RUN_CLIENT = true; Globals.RUN_SERVER = false; @@ -372,7 +395,7 @@ public class MenuGenerators { //button (back) Button backButton = new Button(); - Label backLabel = new Label(100,screenTop + 425,1.0f); + Label backLabel = new Label(100,screenTop + 725,1.0f); backLabel.setText("Back"); backButton.addChild(backLabel); rVal.addChild(backButton); diff --git a/src/main/java/electrosphere/net/client/ClientNetworking.java b/src/main/java/electrosphere/net/client/ClientNetworking.java index f69e9c87..86600f2e 100644 --- a/src/main/java/electrosphere/net/client/ClientNetworking.java +++ b/src/main/java/electrosphere/net/client/ClientNetworking.java @@ -10,6 +10,7 @@ import electrosphere.net.client.protocol.ClientProtocol; import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.PlayerMessage; +import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.parser.net.raw.NetworkParser; import java.io.IOException; import java.io.InputStream; @@ -47,6 +48,11 @@ public class ClientNetworking implements Runnable{ ClientProtocol clientProtocol = new ClientProtocol(); static final int MAX_CONNECTION_ATTEMPTS = 10; + + static final long SEND_PING_THRESHOLD = 3000; + static final long PING_DISCONNECT_THRESHOLD = 20000; + long lastPingTime = 0; + long lastPongTime = 0; public ClientNetworking(String address, int port){ this.address = address; @@ -112,6 +118,30 @@ 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 + if(currentTime - lastPingTime > SEND_PING_THRESHOLD){ + queueOutgoingMessage(ServerMessage.constructPingMessage()); + lastPingTime = currentTime; + if(lastPongTime == 0){ + lastPongTime = lastPingTime; + } + } + if(lastPingTime - lastPongTime > PING_DISCONNECT_THRESHOLD){ + //disconnected from the server + LoggerInterface.loggerNetworking.WARNING("Disconnected from server"); + //close socket + if(socket.isConnected()){ + try { + socket.close(); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR("Error closing socket", e); + } + } + //TODO: kick us back to the main menu + break; + } } } @@ -136,5 +166,9 @@ public class ClientNetworking implements Runnable{ public ClientProtocol getClientProtocol(){ return clientProtocol; } + + public void markReceivedPongMessage(){ + lastPongTime = System.currentTimeMillis(); + } } diff --git a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java index 05137ed1..dce4275c 100644 --- a/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/EntityProtocol.java @@ -44,6 +44,10 @@ public class EntityProtocol { EntityUtils.setEntityID(newlySpawnedEntity, message.getentityID()); break; case DESTROY: + //only obey if we're not also the server + if(!Globals.RUN_SERVER){ + Globals.entityManager.deregisterEntity(Globals.entityManager.getEntityFromId(message.getentityID())); + } break; case MOVE: //literally just adding this to scope so I can use `` Entity target; `` again @@ -84,6 +88,12 @@ public class EntityProtocol { case ATTACKUPDATE: CreatureUtils.attachEntityMessageToAttackTree(Globals.entityManager.getEntityFromId(message.getentityID()),message); break; + case KILL: + break; + case SETPOSITION: + break; + case SETFACING: + break; } } diff --git a/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java b/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java index 4f223d54..ab629697 100644 --- a/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java @@ -11,7 +11,8 @@ public class ServerProtocol { Globals.clientConnection.queueOutgoingMessage(ServerMessage.constructPongMessage()); break; case PONG: - //silently drop + //let the networking loop know we received a pong message + Globals.clientConnection.markReceivedPongMessage(); break; } } diff --git a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java index da72f661..e275356a 100644 --- a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java +++ b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java @@ -18,6 +18,7 @@ import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.PlayerMessage; import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.parser.net.raw.NetworkParser; +import electrosphere.net.server.player.Player; import electrosphere.net.server.protocol.ServerProtocol; import java.io.IOException; @@ -35,6 +36,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.spec.SecretKeySpec; + +import org.joml.Vector3d; import org.joml.Vector3f; /** @@ -58,6 +61,15 @@ public class ServerConnectionHandler implements Runnable { CreatureTemplate currentCreatureTemplate; ServerProtocol serverProtocol; + + //thresholds for determining when to send pings and when a client has disconnected + static final long SEND_PING_THRESHOLD = 3000; + static final long PING_DISCONNECT_THRESHOLD = 20000; + //used to keep track of ping/pong messages with client + long lastPingTime = 0; + long lastPongTime = 0; + //flag to disconnect due to pipe break + boolean socketException = false; public ServerConnectionHandler(Socket socket) { this.socket = socket; @@ -104,21 +116,59 @@ public class ServerConnectionHandler implements Runnable { initialized = true; while(Main.isRunning()){ - //attempt poll incoming messages - networkParser.readMessagesIn(); - //ponder incoming messages - while(networkParser.hasIncomingMessaage()){ - NetworkMessage message = networkParser.popIncomingMessage(); - serverProtocol.handleMessage(message); - } - //push outgoing message - networkParser.pushMessagesOut(); + //parse messages both incoming and outgoing try { - //sleep - TimeUnit.MILLISECONDS.sleep(1); - } catch (InterruptedException ex) { - ex.printStackTrace(); + parseMessages(); + } catch (SocketException e) { + //if we get a SocketException broken pipe (basically the client dc'd without telling us) + //set flag to disconnect client + //TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious + socketException = true; + LoggerInterface.loggerNetworking.DEBUG(e.getLocalizedMessage()); } + //ping logic + long currentTime = System.currentTimeMillis(); + //basically if we haven't sent a ping in a while, send one + if(currentTime - lastPingTime > SEND_PING_THRESHOLD){ + addMessagetoOutgoingQueue(ServerMessage.constructPingMessage()); + lastPingTime = currentTime; + if(lastPongTime == 0){ + lastPongTime = lastPingTime; + } + } + //check if we meet disconnection criteria + //has it been too long since the last ping? + //have we had a socket exception? + if(lastPingTime - lastPongTime > PING_DISCONNECT_THRESHOLD || this.socketException == true){ + //disconnected from the server + LoggerInterface.loggerNetworking.WARNING("Client disconnected"); + //run disconnect routine + disconnect(); + break; + } + } + } + + /** + * Had to wrap the message parsing block in a function to throw a SocketException + * without my linter freaking out + * @throws SocketException + */ + void parseMessages() throws SocketException { + //attempt poll incoming messages + networkParser.readMessagesIn(); + //ponder incoming messages + while(networkParser.hasIncomingMessaage()){ + NetworkMessage message = networkParser.popIncomingMessage(); + serverProtocol.handleMessage(message); + } + //push outgoing message + networkParser.pushMessagesOut(); + try { + //sleep + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException ex) { + ex.printStackTrace(); } } @@ -165,8 +215,6 @@ public class ServerConnectionHandler implements Runnable { networkParser.addOutgoingMessage(message); } break; - case SPAWNCREATURE: - break; default: networkParser.addOutgoingMessage(message); break; @@ -186,4 +234,31 @@ public class ServerConnectionHandler implements Runnable { return this.currentCreatureTemplate; } + public void markReceivedPongMessage(){ + lastPongTime = System.currentTimeMillis(); + } + + /** + * Routine to run when the client disconnects + */ + void disconnect(){ + //close socket + if(socket.isConnected()){ + try { + socket.close(); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR("Error closing socket", e); + } + } + //figure out what player we are + Player playerObject = Globals.playerManager.getPlayerFromId(getPlayerId()); + //get player entity & position + Entity playerEntity = playerObject.getPlayerEntity(); + Vector3d position = EntityUtils.getPosition(playerEntity); + //deregister entity + Globals.entityManager.deregisterEntity(playerObject.getPlayerEntity()); + //tell all clients to destroy the entity + Globals.dataCellManager.getDataCellAtPoint(position).broadcastNetworkMessage(EntityMessage.constructDestroyMessage(playerEntity.getId())); + } + } diff --git a/src/main/java/electrosphere/net/server/protocol/AuthProtocol.java b/src/main/java/electrosphere/net/server/protocol/AuthProtocol.java index 97013cf7..60950ca9 100644 --- a/src/main/java/electrosphere/net/server/protocol/AuthProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/AuthProtocol.java @@ -18,7 +18,7 @@ public class AuthProtocol { connectionHandler.addMessagetoOutgoingQueue(AuthMessage.constructAuthSuccessMessage()); Player newPlayer = new Player(connectionHandler); Globals.playerManager.registerPlayer(newPlayer); - if(connectionHandler.getIPAddress().contains("127.0.0.1")){ + if(connectionHandler.getIPAddress().contains("127.0.0.1") && Globals.RUN_CLIENT == true){ Globals.clientPlayer = newPlayer; } connectionHandler.addMessagetoOutgoingQueue(PlayerMessage.constructSet_IDMessage(connectionHandler.getPlayerId())); diff --git a/src/main/java/electrosphere/net/server/protocol/ServerProtocol.java b/src/main/java/electrosphere/net/server/protocol/ServerProtocol.java index 4e200e9e..c391c8f9 100644 --- a/src/main/java/electrosphere/net/server/protocol/ServerProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/ServerProtocol.java @@ -29,7 +29,6 @@ import electrosphere.net.server.player.Player; public class ServerProtocol { ServerConnectionHandler connectionHandler; - int playerCharacterID; public ServerProtocol(ServerConnectionHandler connectionHandler){ this.connectionHandler = connectionHandler; @@ -70,7 +69,7 @@ public class ServerProtocol { connectionHandler.addMessagetoOutgoingQueue(ServerMessage.constructPongMessage()); break; case PONG: - //silently drop + connectionHandler.markReceivedPongMessage(); break; } }