server tlc
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-08-16 08:04:44 -04:00
parent 8bf1c23704
commit 74167038f7
15 changed files with 180 additions and 72 deletions

View File

@ -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
View 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.

View File

@ -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

View File

@ -2,4 +2,5 @@
[TOC]
- @subpage BugLog
- @subpage LoadingHalting

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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();
}
}
}

View File

@ -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:

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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");

View File

@ -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);
}
/**