This commit is contained in:
		
							parent
							
								
									8bf1c23704
								
							
						
					
					
						commit
						74167038f7
					
				| @ -1,3 +1,3 @@ | ||||
| #maven.buildNumber.plugin properties file | ||||
| #Thu Aug 15 18:38:57 EDT 2024 | ||||
| buildNumber=204 | ||||
| #Fri Aug 16 07:57:34 EDT 2024 | ||||
| buildNumber=233 | ||||
|  | ||||
							
								
								
									
										9
									
								
								docs/src/debug/BugLog.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								docs/src/debug/BugLog.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| @page BugLog Bug Log | ||||
| 
 | ||||
| A log of bugs encountered | ||||
| 
 | ||||
| 
 | ||||
| ### 08-16-2024 | ||||
|  - Starting a server with a local client didn't actually create the server socket/thread. This meant no other players could connect. | ||||
|  - On a client disconnection, the server connection was cleared by IP instead of by socket, so if two connections were from the same IP (ie localhost) it would disconnect the first one to connect. | ||||
| 
 | ||||
| @ -10,4 +10,8 @@ Arena was not loading because handling of edge-of-world chunks was not being han | ||||
| for a world of 2x2x2, when loading the chunk at (1,0,0) you need data for chunk (2,0,0) | ||||
| (2,0,0) is out of bounds of the world size, but the drawcellmanager would not mark the chunk data as present because it wasn't properly checking for bounds | ||||
| This was fixed by properly checking for bounds in drawcellmanager; however, it then started failing to fetch data for (2,0,0) and NPEing | ||||
| Fixed by guarding in the method that generates chunk data | ||||
| Fixed by guarding in the method that generates chunk data | ||||
| 
 | ||||
| ### 08-16-2024 | ||||
| Second client could not connect to server started with local player because the routine to start local server didn't actually start a socket | ||||
| 
 | ||||
|  | ||||
| @ -2,4 +2,5 @@ | ||||
| 
 | ||||
| 
 | ||||
| [TOC] | ||||
| - @subpage BugLog | ||||
| - @subpage LoadingHalting | ||||
| @ -584,6 +584,10 @@ Play animations offset by network delay | ||||
|  - Attack animation | ||||
| Fix viewmodel animation framerate | ||||
| 
 | ||||
| (08/16/2024) | ||||
| Fix server not starting | ||||
| Fix client disconnection causing wrong socket to be closed from server | ||||
| 
 | ||||
| 
 | ||||
| # TODO | ||||
| 
 | ||||
|  | ||||
| @ -72,7 +72,7 @@ public class LevelEditorLoading { | ||||
|         //initialize the local connection | ||||
|         Globals.clientUsername = "leveleditor"; | ||||
|         Globals.clientPassword = AuthenticationManager.getHashedString("leveleditor"); | ||||
|         ServerConnectionHandler serverPlayerConnection = LoadingUtils.initLocalConnection(false); | ||||
|         ServerConnectionHandler serverPlayerConnection = LoadingUtils.initLocalConnection(true); | ||||
|         //wait for player object creation | ||||
|         while(Globals.playerManager.getPlayers().size() < 1){ | ||||
|             try { | ||||
|  | ||||
| @ -85,10 +85,9 @@ public class ServerEntityUtils { | ||||
|      * @param entity the entity to destroy | ||||
|      */ | ||||
|     public static void destroyEntity(Entity entity){ | ||||
|         // | ||||
|         //get info required to destroy | ||||
|         ServerDataCell cell = DataCellSearchUtils.getEntityDataCell(entity); | ||||
|         Realm realm = Globals.realmManager.getEntityRealm(entity); | ||||
|         if(entity == null){ | ||||
|             throw new IllegalArgumentException("Trying to destroy null!"); | ||||
|         } | ||||
| 
 | ||||
|         // | ||||
|         //destroy the child entities, too | ||||
| @ -100,22 +99,28 @@ public class ServerEntityUtils { | ||||
|         } | ||||
| 
 | ||||
|         // | ||||
|         //cell specific logic | ||||
|         if(cell != null){ | ||||
|             cell.broadcastNetworkMessage(EntityMessage.constructDestroyMessage(entity.getId())); | ||||
|             cell.getScene().deregisterEntity(entity); | ||||
|         } | ||||
|         //get info required to destroy | ||||
|         Realm realm = Globals.realmManager.getEntityRealm(entity); | ||||
|         ServerDataCell cell = null; | ||||
| 
 | ||||
|         // | ||||
|         //realm specific logic | ||||
|         if(realm != null){ | ||||
|             realm.getCollisionEngine().destroyPhysics(entity); | ||||
|             cell = DataCellSearchUtils.getEntityDataCell(entity); | ||||
|         } | ||||
| 
 | ||||
|         // | ||||
|         //cell specific logic | ||||
|         if(cell != null){ | ||||
|             cell.broadcastNetworkMessage(EntityMessage.constructDestroyMessage(entity.getId())); | ||||
|             ServerBehaviorTreeUtils.deregisterEntity(entity); | ||||
|             cell.getScene().deregisterEntity(entity); | ||||
|         } | ||||
| 
 | ||||
|         // | ||||
|         //detatch from all global tracking | ||||
|         HitboxCollectionState.destroyHitboxState(entity); | ||||
|         ServerBehaviorTreeUtils.deregisterEntity(entity); | ||||
|         Globals.realmManager.removeEntity(entity); | ||||
|         EntityLookupUtils.removeEntity(entity); | ||||
|         Globals.aiManager.removeAI(entity); | ||||
|  | ||||
| @ -146,6 +146,37 @@ public class ClientGroundMovementTree implements BehaviorTree { | ||||
|             Vector3d position = EntityUtils.getPosition(parent); | ||||
|             Quaterniond rotation = EntityUtils.getRotation(parent); | ||||
|             float velocity = CreatureUtils.getVelocity(parent); | ||||
|             if(this.parent == Globals.playerEntity){ | ||||
|                 Globals.clientConnection.queueOutgoingMessage( | ||||
|                     EntityMessage.constructmoveUpdateMessage( | ||||
|                         Globals.clientSceneWrapper.mapClientToServerId(parent.getId()), | ||||
|                         Globals.timekeeper.getNumberOfSimFramesElapsed(), | ||||
|                         position.x, | ||||
|                         position.y, | ||||
|                         position.z, | ||||
|                         rotation.x, | ||||
|                         rotation.y, | ||||
|                         rotation.z, | ||||
|                         rotation.w, | ||||
|                         velocity, | ||||
|                         ClientGroundMovementTree.getMovementRelativeFacingEnumAsShort(facing), | ||||
|                         0 //magic number corresponding to state startup | ||||
|                     ) | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Requests to the server that the movetree stop | ||||
|      */ | ||||
|     public void slowdown(){ | ||||
|         state = MovementTreeState.SLOWDOWN; | ||||
|         //if we aren't the server, alert the server we intend to slow down | ||||
|         Vector3d position = EntityUtils.getPosition(parent); | ||||
|         Quaterniond rotation = EntityUtils.getRotation(parent); | ||||
|         float velocity = CreatureUtils.getVelocity(parent); | ||||
|         if(this.parent == Globals.playerEntity){ | ||||
|             Globals.clientConnection.queueOutgoingMessage( | ||||
|                 EntityMessage.constructmoveUpdateMessage( | ||||
|                     Globals.clientSceneWrapper.mapClientToServerId(parent.getId()), | ||||
| @ -159,39 +190,12 @@ public class ClientGroundMovementTree implements BehaviorTree { | ||||
|                     rotation.w, | ||||
|                     velocity, | ||||
|                     ClientGroundMovementTree.getMovementRelativeFacingEnumAsShort(facing), | ||||
|                     0 //magic number corresponding to state startup | ||||
|                     2 //magic number corresponding to state slowdown | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Requests to the server that the movetree stop | ||||
|      */ | ||||
|     public void slowdown(){ | ||||
|         state = MovementTreeState.SLOWDOWN; | ||||
|         //if we aren't the server, alert the server we intend to slow down | ||||
|         Vector3d position = EntityUtils.getPosition(parent); | ||||
|         Quaterniond rotation = EntityUtils.getRotation(parent); | ||||
|         float velocity = CreatureUtils.getVelocity(parent); | ||||
|         Globals.clientConnection.queueOutgoingMessage( | ||||
|             EntityMessage.constructmoveUpdateMessage( | ||||
|                 Globals.clientSceneWrapper.mapClientToServerId(parent.getId()), | ||||
|                 Globals.timekeeper.getNumberOfSimFramesElapsed(), | ||||
|                 position.x, | ||||
|                 position.y, | ||||
|                 position.z, | ||||
|                 rotation.x, | ||||
|                 rotation.y, | ||||
|                 rotation.z, | ||||
|                 rotation.w, | ||||
|                 velocity, | ||||
|                 ClientGroundMovementTree.getMovementRelativeFacingEnumAsShort(facing), | ||||
|                 2 //magic number corresponding to state slowdown | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void simulate(float deltaTime){ | ||||
|         Actor entityActor = EntityUtils.getActor(parent); | ||||
|  | ||||
| @ -172,7 +172,11 @@ public class ClientNetworking implements Runnable { | ||||
|             //attempt poll incoming messages | ||||
|             parser.readMessagesIn(); | ||||
|             //outgoing messages | ||||
|             parser.pushMessagesOut(); | ||||
|             try { | ||||
|                 parser.pushMessagesOut(); | ||||
|             } catch(IOException e){ | ||||
|                 LoggerInterface.loggerNetworking.ERROR(e); | ||||
|             } | ||||
|             //parses messages asynchronously | ||||
|             this.parseMessagesAsynchronously(); | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| package electrosphere.net.parser.net.raw; | ||||
| 
 | ||||
| import electrosphere.net.parser.net.message.NetworkMessage; | ||||
| package electrosphere.net.parser.net.raw; | ||||
| 
 | ||||
| import electrosphere.net.parser.net.message.NetworkMessage; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
| @ -55,15 +55,11 @@ public class NetworkParser { | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public void pushMessagesOut(){ | ||||
|     public void pushMessagesOut() throws IOException { | ||||
|         for(NetworkMessage message : outgoingMessageQueue){ | ||||
|             outgoingMessageQueue.remove(message); | ||||
|             try { | ||||
| //                System.out.println("Write message of type " + message.getType()); | ||||
|                 outgoingStream.write(message.getRawBytes()); | ||||
|             } catch (IOException ex) { | ||||
|                 ex.printStackTrace(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
| @ -106,6 +106,7 @@ public class MessageProtocol { | ||||
|         if(result != null){ | ||||
|             this.synchronousMessageLock.acquireUninterruptibly(); | ||||
|             this.synchronousMessageQueue.add(result); | ||||
|             LoggerInterface.loggerNetworking.DEBUG_LOOP("ADD SYNC MESSAGE [Sync queue size: " + this.synchronousMessageQueue.size() + "]"); | ||||
|             this.synchronousMessageLock.release(); | ||||
|         } | ||||
|         Globals.profiler.endCpuSample(); | ||||
| @ -114,6 +115,7 @@ public class MessageProtocol { | ||||
|     public void handleSyncMessages(){ | ||||
|         Globals.profiler.beginAggregateCpuSample("MessageProtocol(client).handleSyncMessages"); | ||||
|         this.synchronousMessageLock.acquireUninterruptibly(); | ||||
|         LoggerInterface.loggerNetworking.DEBUG_LOOP("HANDLE SYNC MESSAGE [Sync queue size: " + this.synchronousMessageQueue.size() + "]"); | ||||
|         for(NetworkMessage message : synchronousMessageQueue){ | ||||
|             switch(message.getType()){ | ||||
|                 case AUTH_MESSAGE: | ||||
|  | ||||
| @ -2,6 +2,7 @@ package electrosphere.net.server; | ||||
| 
 | ||||
| import electrosphere.engine.Globals; | ||||
| import electrosphere.engine.Main; | ||||
| import electrosphere.entity.ServerEntityUtils; | ||||
| import electrosphere.logger.LoggerInterface; | ||||
| import electrosphere.net.NetUtils; | ||||
| import electrosphere.net.parser.net.message.NetworkMessage; | ||||
| @ -14,7 +15,11 @@ import java.net.ServerSocket; | ||||
| import java.net.Socket; | ||||
| import java.net.SocketException; | ||||
| import java.util.HashMap; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.CopyOnWriteArrayList; | ||||
| import java.util.concurrent.Semaphore; | ||||
| 
 | ||||
| /** | ||||
|  * Lowest level networking class for the server | ||||
| @ -27,10 +32,17 @@ public class Server implements Runnable{ | ||||
|     //the socket for the server | ||||
|     ServerSocket serverSocket; | ||||
|      | ||||
|     //the map of ip->connection handler | ||||
|     Map<String,ServerConnectionHandler> clientMap = new HashMap<String,ServerConnectionHandler>(); | ||||
|      | ||||
|     //Used to synchronize additions/subtractions to the connections stored by this server | ||||
|     Semaphore connectListLock = new Semaphore(1); | ||||
| 
 | ||||
|     //map of socket->connection | ||||
|     Map<Socket,ServerConnectionHandler> socketConnectionMap = new HashMap<Socket,ServerConnectionHandler>(); | ||||
| 
 | ||||
|     //the list of active connections | ||||
|     List<ServerConnectionHandler> activeConnections = new LinkedList<ServerConnectionHandler>(); | ||||
|      | ||||
|     //The list of connections to clean up | ||||
|     List<ServerConnectionHandler> connectionsToCleanup = new CopyOnWriteArrayList<ServerConnectionHandler>(); | ||||
|      | ||||
|      | ||||
|     /** | ||||
| @ -60,19 +72,20 @@ public class Server implements Runnable{ | ||||
|             } | ||||
|         } catch(BindException ex){ | ||||
|             LoggerInterface.loggerNetworking.ERROR("Failed to bind server socket!",ex); | ||||
|             ex.printStackTrace(); | ||||
|         } catch (IOException ex) { | ||||
|             LoggerInterface.loggerNetworking.ERROR("Failed to start server socket!",ex); | ||||
|             ex.printStackTrace(); | ||||
|             LoggerInterface.loggerNetworking.ERROR("", ex); | ||||
|         } | ||||
|         while(Main.isRunning()){ | ||||
|             Socket newSocket; | ||||
|             try { | ||||
|                 newSocket = serverSocket.accept(); | ||||
|                 connectListLock.acquireUninterruptibly(); | ||||
|                 ServerConnectionHandler newClient = new ServerConnectionHandler(newSocket); | ||||
|                 clientMap.put(newSocket.getInetAddress().getHostAddress(), newClient); | ||||
|                 // clientMap.put(newSocket.getInetAddress().getHostAddress(), newClient); | ||||
|                 socketConnectionMap.put(newSocket, newClient); | ||||
|                 activeConnections.add(newClient); | ||||
|                 new Thread(newClient).start(); | ||||
|                 connectListLock.release(); | ||||
|             } catch (SocketException ex){ | ||||
|                 LoggerInterface.loggerNetworking.ERROR("Socket closed!",ex); | ||||
|             } catch (IOException ex) { | ||||
| @ -85,9 +98,11 @@ public class Server implements Runnable{ | ||||
|      * Synchronously handles queued packets for each client connection | ||||
|      */ | ||||
|     public void synchronousPacketHandling(){ | ||||
|         for(ServerConnectionHandler connectionHandler : this.clientMap.values()){ | ||||
|         connectListLock.acquireUninterruptibly(); | ||||
|         for(ServerConnectionHandler connectionHandler : activeConnections){ | ||||
|             connectionHandler.handleSynchronousPacketQueue(); | ||||
|         } | ||||
|         connectListLock.release(); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
| @ -108,11 +123,13 @@ public class Server implements Runnable{ | ||||
|      * @param message The message to broadcast | ||||
|      */ | ||||
|     public void broadcastMessage(NetworkMessage message){ | ||||
|         for(ServerConnectionHandler client : clientMap.values()){ | ||||
|         connectListLock.acquireUninterruptibly(); | ||||
|         for(ServerConnectionHandler client : activeConnections){ | ||||
|             if(Globals.clientPlayer == null || client.playerID != Globals.clientPlayer.getId()){ | ||||
|                 client.addMessagetoOutgoingQueue(message); | ||||
|             } | ||||
|         } | ||||
|         connectListLock.release(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -122,9 +139,37 @@ public class Server implements Runnable{ | ||||
|      * @return The connection object for the provided streams | ||||
|      */ | ||||
|     public ServerConnectionHandler addLocalPlayer(InputStream serverInputStream, OutputStream serverOutputStream){ | ||||
|         connectListLock.acquireUninterruptibly(); | ||||
|         ServerConnectionHandler newClient = new ServerConnectionHandler(serverInputStream,serverOutputStream); | ||||
|         clientMap.put("127.0.0.1", newClient); | ||||
|         activeConnections.add(newClient); | ||||
|         new Thread(newClient).start(); | ||||
|         connectListLock.release(); | ||||
|         return newClient; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Adds a client to the queue of connections to cleanup | ||||
|      * @param serverConnectionHandler The connection | ||||
|      */ | ||||
|     public void addClientToCleanup(ServerConnectionHandler serverConnectionHandler){ | ||||
|         this.connectListLock.acquireUninterruptibly(); | ||||
|         this.connectionsToCleanup.add(serverConnectionHandler); | ||||
|         this.connectListLock.release(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Cleans up dead connections on the server | ||||
|      */ | ||||
|     public void cleanupDeadConnections(){ | ||||
|         this.connectListLock.acquireUninterruptibly(); | ||||
|         for(ServerConnectionHandler connection : this.connectionsToCleanup){ | ||||
|             //tell all clients to destroy the entity | ||||
|             ServerEntityUtils.destroyEntity(connection.getPlayer().getPlayerEntity()); | ||||
|             this.activeConnections.remove(connection); | ||||
|             if(connection.getSocket() != null){ | ||||
|                 this.socketConnectionMap.remove(connection.getSocket()); | ||||
|             } | ||||
|         } | ||||
|         this.connectListLock.release(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -3,7 +3,6 @@ package electrosphere.net.server; | ||||
| import electrosphere.entity.types.creature.CreatureTemplate; | ||||
| import electrosphere.engine.Globals; | ||||
| import electrosphere.engine.Main; | ||||
| import electrosphere.entity.ServerEntityUtils; | ||||
| import electrosphere.logger.LoggerInterface; | ||||
| import electrosphere.net.parser.net.message.AuthMessage; | ||||
| import electrosphere.net.parser.net.message.NetworkMessage; | ||||
| @ -61,6 +60,11 @@ public class ServerConnectionHandler implements Runnable { | ||||
|     //the player's entity id | ||||
|     int playerEntityID; | ||||
| 
 | ||||
|     /** | ||||
|      * Tracks whether this connection is still communicating with the client | ||||
|      */ | ||||
|     boolean isConnected = true; | ||||
| 
 | ||||
|     //the creature template associated with this player | ||||
|     CreatureTemplate currentCreatureTemplate; | ||||
|      | ||||
| @ -189,7 +193,7 @@ public class ServerConnectionHandler implements Runnable { | ||||
| 
 | ||||
| 
 | ||||
|         initialized = true; | ||||
|         while(Main.isRunning()){ | ||||
|         while(Main.isRunning() && this.isConnected == true){ | ||||
| 
 | ||||
|             // | ||||
|             // Main Loop | ||||
| @ -203,6 +207,16 @@ public class ServerConnectionHandler implements Runnable { | ||||
|                 //TODO: fix, this doesn't actually catch the socket exception which is exceedingly obnoxious | ||||
|                 socketException = true; | ||||
|                 LoggerInterface.loggerNetworking.DEBUG(e.getLocalizedMessage()); | ||||
|                 this.disconnect(); | ||||
|                 break; | ||||
|             } catch (IOException 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()); | ||||
|                 this.disconnect(); | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             // | ||||
| @ -233,6 +247,8 @@ public class ServerConnectionHandler implements Runnable { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         LoggerInterface.loggerNetworking.WARNING("Server connection thread ended"); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -240,7 +256,7 @@ public class ServerConnectionHandler implements Runnable { | ||||
|      * without my linter freaking out | ||||
|      * @throws SocketException | ||||
|      */ | ||||
|     void parseMessages() throws SocketException { | ||||
|     void parseMessages() throws SocketException, IOException { | ||||
|         // | ||||
|         //Read in messages | ||||
|         // | ||||
| @ -333,6 +349,10 @@ public class ServerConnectionHandler implements Runnable { | ||||
|         return Globals.playerManager.getPlayerFromId(playerID); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the ip address of the connection | ||||
|      * @return The ip address | ||||
|      */ | ||||
|     public String getIPAddress(){ | ||||
|         if(local){ | ||||
|             return "127.0.0.1"; | ||||
| @ -340,6 +360,14 @@ public class ServerConnectionHandler implements Runnable { | ||||
|             return socket.getRemoteSocketAddress().toString(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the socket of the connection, if it is a socket based connection. Otherwise returns null. | ||||
|      * @return The socket if it exists, null otherwise | ||||
|      */ | ||||
|     public Socket getSocket(){ | ||||
|         return this.socket; | ||||
|     } | ||||
|      | ||||
|     public void addMessagetoOutgoingQueue(NetworkMessage message){ | ||||
|         networkParser.addOutgoingMessage(message); | ||||
| @ -372,7 +400,7 @@ public class ServerConnectionHandler implements Runnable { | ||||
|     /** | ||||
|      * Routine to run when the client disconnects | ||||
|      */ | ||||
|     void disconnect(){ | ||||
|     private void disconnect(){ | ||||
|         //close socket | ||||
|         if(socket.isConnected()){ | ||||
|             try { | ||||
| @ -381,10 +409,9 @@ public class ServerConnectionHandler implements Runnable { | ||||
|                 LoggerInterface.loggerNetworking.ERROR("Error closing socket", e); | ||||
|             } | ||||
|         } | ||||
|         //figure out what player we are | ||||
|         Player playerObject = Globals.playerManager.getPlayerFromId(getPlayerId()); | ||||
|         //tell all clients to destroy the entity | ||||
|         ServerEntityUtils.destroyEntity(playerObject.getPlayerEntity()); | ||||
|         this.isConnected = false; | ||||
|         //add connection to server list of connections to cleanup | ||||
|         Globals.server.addClientToCleanup(this); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -13,6 +13,12 @@ public class MainServerFunctions { | ||||
|      */ | ||||
|     public static void simulate(){ | ||||
| 
 | ||||
|         // | ||||
|         //Cleanup disconnected clients | ||||
|         if(Globals.server != null){ | ||||
|             Globals.server.cleanupDeadConnections(); | ||||
|         } | ||||
| 
 | ||||
|         // | ||||
|         //Synchronous player message parsing\ | ||||
|         Globals.profiler.beginCpuSample("Server synchronous packet parsing"); | ||||
|  | ||||
| @ -34,10 +34,11 @@ public class ServerBehaviorTreeUtils { | ||||
|     public static void deregisterEntity(Entity entity){ | ||||
|         Set<BehaviorTree> trees = entityBTreeMap.remove(entity); | ||||
|         ServerDataCell currentCell = DataCellSearchUtils.getEntityDataCell(entity); | ||||
|         for(BehaviorTree tree : trees){ | ||||
|             currentCell.getScene().deregisterBehaviorTree(tree); | ||||
|         if(trees != null){ | ||||
|             for(BehaviorTree tree : trees){ | ||||
|                 currentCell.getScene().deregisterBehaviorTree(tree); | ||||
|             } | ||||
|         } | ||||
|         currentCell.getScene().deregisterEntity(entity); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user