From 58dc5333e6c36bb09c3a33815742aa8f320d138d Mon Sep 17 00:00:00 2001 From: austin Date: Tue, 15 Apr 2025 20:42:39 -0400 Subject: [PATCH] database work + serialization work + save pos --- assets/DB/createTables.sql | 39 ++++- assets/DB/structs/createStruct.sql | 6 - assets/DB/structs/createStructsTables.sql | 10 -- assets/DB/structs/deleteStruct.sql | 6 - assets/DB/structs/getStructDataByID.sql | 1 - assets/DB/structs/getStructLocalPos.sql | 1 - assets/DB/structs/getStructsAtWorldPos.sql | 1 - assets/DB/towns/createTown.sql | 6 - assets/DB/towns/createTownsTables.sql | 10 -- assets/DB/towns/deleteTown.sql | 6 - assets/DB/towns/getTownsAtWorldPos.sql | 2 - docs/src/progress/renderertodo.md | 8 + .../ui/menu/ingame/MenuGeneratorsInGame.java | 5 +- .../entity/EntityDataStrings.java | 1 + .../state/server/ServerCharacterData.java | 74 ++++++++ .../types/common/CommonEntityUtils.java | 3 + .../entity/types/creature/CreatureUtils.java | 15 ++ .../net/client/protocol/ServerProtocol.java | 4 + .../parser/net/message/NetworkMessage.java | 5 + .../net/parser/net/message/ServerMessage.java | 33 ++++ .../net/parser/net/message/TypeBytes.java | 2 + .../java/electrosphere/net/server/Server.java | 62 +++++-- .../net/server/ServerConnectionHandler.java | 158 ++++++++++++++---- .../server/protocol/CharacterProtocol.java | 35 +--- .../net/server/protocol/ServerProtocol.java | 14 +- .../server/character/CharacterService.java | 124 ++++++++++++++ .../character/PlayerCharacterCreation.java | 22 ++- .../server/content/ServerContentManager.java | 10 ++ .../serialization/ContentSerialization.java | 87 +++++----- .../gridded/GriddedDataCellManager.java | 1 - .../server/db/DatabaseController.java | 6 + .../server/db/DatabaseResultIterator.java | 33 ++-- .../server/db/DatabaseResultRow.java | 9 +- .../server/db/DatabaseUtils.java | 26 +-- .../electrosphere/server/saves/SaveUtils.java | 6 +- src/net/server.json | 5 + 36 files changed, 619 insertions(+), 217 deletions(-) delete mode 100644 assets/DB/structs/createStruct.sql delete mode 100644 assets/DB/structs/createStructsTables.sql delete mode 100644 assets/DB/structs/deleteStruct.sql delete mode 100644 assets/DB/structs/getStructDataByID.sql delete mode 100644 assets/DB/structs/getStructLocalPos.sql delete mode 100644 assets/DB/structs/getStructsAtWorldPos.sql delete mode 100644 assets/DB/towns/createTown.sql delete mode 100644 assets/DB/towns/createTownsTables.sql delete mode 100644 assets/DB/towns/deleteTown.sql delete mode 100644 assets/DB/towns/getTownsAtWorldPos.sql create mode 100644 src/main/java/electrosphere/entity/state/server/ServerCharacterData.java create mode 100644 src/main/java/electrosphere/server/character/CharacterService.java diff --git a/assets/DB/createTables.sql b/assets/DB/createTables.sql index 584cb3cb..a04ca97c 100644 --- a/assets/DB/createTables.sql +++ b/assets/DB/createTables.sql @@ -1,5 +1,5 @@ ---main table +-- main table CREATE TABLE mainTable (propName VARCHAR PRIMARY KEY, propValue VARCHAR); INSERT INTO mainTable (propName, propValue) VALUES ("ver","1"); @@ -7,3 +7,40 @@ INSERT INTO mainTable (propName, propValue) VALUES ("ver","1"); + +-- CHARACTERS +-- positions +CREATE TABLE charaWorldPositions (playerId INTEGER PRIMARY KEY, id INTEGER, posX INTEGER, posY INTEGER); +CREATE INDEX charaWorldPositionsIDIndex ON charaWorldPositions (id); +CREATE INDEX charaWorldPositionsPosIndex ON charaWorldPositions (posX, posY); + +-- real positions of characters (ie player's characters) +CREATE TABLE charaRealPos (id INTEGER PRIMARY KEY NOT NULL, x REAL NOT NULL, y REAL NOT NULL, z REAL NOT NULL); + +-- data +CREATE TABLE charaData (id INTEGER PRIMARY KEY AUTOINCREMENT, playerId INTEGER, dataVal VARCHAR); +CREATE INDEX charaDataIDIndex ON charaData (id); + + + + + + +-- AUTH +-- accounts definition +CREATE TABLE accounts ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL, + pwdhash TEXT NOT NULL +); + + + + + + + + + + + diff --git a/assets/DB/structs/createStruct.sql b/assets/DB/structs/createStruct.sql deleted file mode 100644 index 02c3bab0..00000000 --- a/assets/DB/structs/createStruct.sql +++ /dev/null @@ -1,6 +0,0 @@ - ---positions -INSERT INTO structWorldPositions (structID,posX,posY) VALUES (0,10,10); - ---data -INSERT INTO structData(structID,dataVal) VALUES(0,'{"localX"=10,"localY"=10}'); diff --git a/assets/DB/structs/createStructsTables.sql b/assets/DB/structs/createStructsTables.sql deleted file mode 100644 index 4e473cb2..00000000 --- a/assets/DB/structs/createStructsTables.sql +++ /dev/null @@ -1,10 +0,0 @@ - ---structures ---positions -CREATE TABLE structWorldPositions (id INTEGER PRIMARY KEY, structID INTEGER, posX INTEGER, posY INTEGER); -CREATE INDEX structWorldPositionsIDIndex ON structWorldPositions (structID); -CREATE INDEX structWorldPositionsPosIndex ON structWorldPositions (posX, posY); - ---data -CREATE TABLE structData (id INTEGER PRIMARY KEY, structID INTEGER, dataVal VARCHAR); -CREATE INDEX structDataIDIndex ON structData (structID); diff --git a/assets/DB/structs/deleteStruct.sql b/assets/DB/structs/deleteStruct.sql deleted file mode 100644 index 68b1841f..00000000 --- a/assets/DB/structs/deleteStruct.sql +++ /dev/null @@ -1,6 +0,0 @@ - ---positions -DELETE FROM structWorldPositions WHERE structID=0; - ---data -DELETE FROM structData WHERE structID=0; diff --git a/assets/DB/structs/getStructDataByID.sql b/assets/DB/structs/getStructDataByID.sql deleted file mode 100644 index bf1da6c5..00000000 --- a/assets/DB/structs/getStructDataByID.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT * FROM structData WHERE structID IN (1, 2); \ No newline at end of file diff --git a/assets/DB/structs/getStructLocalPos.sql b/assets/DB/structs/getStructLocalPos.sql deleted file mode 100644 index 34fe7d07..00000000 --- a/assets/DB/structs/getStructLocalPos.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT * FROM structData WHERE structID=0; \ No newline at end of file diff --git a/assets/DB/structs/getStructsAtWorldPos.sql b/assets/DB/structs/getStructsAtWorldPos.sql deleted file mode 100644 index 85e63665..00000000 --- a/assets/DB/structs/getStructsAtWorldPos.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT * FROM structWorldPositions WHERE posX = 10 AND posY = 10; \ No newline at end of file diff --git a/assets/DB/towns/createTown.sql b/assets/DB/towns/createTown.sql deleted file mode 100644 index c8a0e546..00000000 --- a/assets/DB/towns/createTown.sql +++ /dev/null @@ -1,6 +0,0 @@ - ---positions -INSERT INTO townWorldPositions (townID,posX,posY) VALUES (0,10,10); - ---data -INSERT INTO townData(townID,propName,propValue) VALUES(0,"name","someTown"); diff --git a/assets/DB/towns/createTownsTables.sql b/assets/DB/towns/createTownsTables.sql deleted file mode 100644 index 5f03b3e7..00000000 --- a/assets/DB/towns/createTownsTables.sql +++ /dev/null @@ -1,10 +0,0 @@ - ---towns ---positions -CREATE TABLE townWorldPositions (id INTEGER PRIMARY KEY, townID INTEGER, posX INTEGER, posY INTEGER); -CREATE INDEX townWorldPositionsIDIndex ON townWorldPositions (townID); -CREATE INDEX townWorldPositionsPosIndex ON townWorldPositions (posX, posY); - ---data -CREATE TABLE townData (id INTEGER PRIMARY KEY, townID INTEGER, dataVal VARCHAR); -CREATE INDEX townDataIDIndex ON townData (townID); diff --git a/assets/DB/towns/deleteTown.sql b/assets/DB/towns/deleteTown.sql deleted file mode 100644 index 84f8e6ed..00000000 --- a/assets/DB/towns/deleteTown.sql +++ /dev/null @@ -1,6 +0,0 @@ - ---positions -DELETE FROM townWorldPositions WHERE townID=0; - ---data -DELETE FROM townData WHERE townID=0; diff --git a/assets/DB/towns/getTownsAtWorldPos.sql b/assets/DB/towns/getTownsAtWorldPos.sql deleted file mode 100644 index 886c2c27..00000000 --- a/assets/DB/towns/getTownsAtWorldPos.sql +++ /dev/null @@ -1,2 +0,0 @@ ---given x=10, y=11 -SELECT townID FROM townWorldPositions WHERE posX = 10 AND posY = 11; \ No newline at end of file diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index bd0ffa03..b7e6f5cb 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1493,6 +1493,14 @@ Fix yoga not applying on item drop window creation Fix client not destroying item on remove from inventory Tests for above bugs Fix rendering 3rd person model-attached entities drawing when in first person +fix database handling REAL type +CharacterService first implementation (abstracts away database calls from functions) +Store player entity position on save +Cull old SQL +Clarify content serialization pipelines for character vs non-character entities +Server send disconnect packet on disconnection +Associate each server connection with a character ID +Code cleanup diff --git a/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInGame.java b/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInGame.java index 30804b6c..4b365cdf 100644 --- a/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInGame.java +++ b/src/main/java/electrosphere/client/ui/menu/ingame/MenuGeneratorsInGame.java @@ -106,8 +106,9 @@ public class MenuGeneratorsInGame { //Save { - Button button = Button.createButton("Save", () -> { + Button button = Button.createButton("Save and Quit", () -> { SaveUtils.overwriteSave(Globals.currentSave.getName()); + Globals.signalSystem.post(SignalType.ENGINE_RETURN_TO_TITLE); }); button.setMarginTop(BUTTON_MARGIN); button.setMarginLeft(BUTTON_MARGIN); @@ -116,7 +117,7 @@ public class MenuGeneratorsInGame { //Quit { - Button button = Button.createButton("Quit", () -> { + Button button = Button.createButton("Shutdown", () -> { Main.running = false; }); button.setMarginTop(BUTTON_MARGIN); diff --git a/src/main/java/electrosphere/entity/EntityDataStrings.java b/src/main/java/electrosphere/entity/EntityDataStrings.java index ca8d7386..7aa7a94c 100644 --- a/src/main/java/electrosphere/entity/EntityDataStrings.java +++ b/src/main/java/electrosphere/entity/EntityDataStrings.java @@ -326,6 +326,7 @@ public class EntityDataStrings { * Server-specific btrees */ public static final String TREE_SERVERPLAYERVIEWDIR = "treeServerPlayerViewDir"; + public static final String TREE_SERVERCHARACTERDATA = "treeServerCharacterData"; /** * Physics synchronization diff --git a/src/main/java/electrosphere/entity/state/server/ServerCharacterData.java b/src/main/java/electrosphere/entity/state/server/ServerCharacterData.java new file mode 100644 index 00000000..8da64bfa --- /dev/null +++ b/src/main/java/electrosphere/entity/state/server/ServerCharacterData.java @@ -0,0 +1,74 @@ +package electrosphere.entity.state.server; + +import electrosphere.entity.Entity; +import electrosphere.entity.EntityDataStrings; + +/** + * Stores data that associated an entity to a character in the character database + */ +public class ServerCharacterData { + + /** + * The character id + */ + private int characterId; + + /** + * The associated entity + */ + private Entity parent; + + /** + * Constructor + * @param parent + * @param characterId + */ + private ServerCharacterData(Entity parent, int characterId){ + this.parent = parent; + this.characterId = characterId; + } + + /** + * Attaches a ServerPlayerViewDirTree to a given entity + * @param entity The entity to add to + */ + public static void attachServerPlayerViewDirTree(Entity entity, int characterId){ + ServerCharacterData tree = new ServerCharacterData(entity, characterId); + entity.putData(EntityDataStrings.TREE_SERVERCHARACTERDATA, tree); + } + + /** + * Checks if the entity has associated character data + * @param entity The entity + * @return true if the entity contains character data, false otherwise + */ + public static boolean hasServerPlayerCharacterDataTree(Entity entity){ + return entity.containsKey(EntityDataStrings.TREE_SERVERCHARACTERDATA); + } + + /** + * Gets the character data on the entity + * @param entity The entity + * @return The ServerCharacterData + */ + public static ServerCharacterData getServerCharacterData(Entity entity){ + return (ServerCharacterData)entity.getData(EntityDataStrings.TREE_SERVERCHARACTERDATA); + } + + /** + * Gets the associated character id for this entity + * @return The id + */ + public int getCharacterId() { + return characterId; + } + + /** + * Gets the parent entity of this data + * @return The parent entity + */ + public Entity getParent() { + return parent; + } + +} diff --git a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java index c950c8db..e89cc90a 100644 --- a/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java +++ b/src/main/java/electrosphere/entity/types/common/CommonEntityUtils.java @@ -835,6 +835,9 @@ public class CommonEntityUtils { * @param type The type */ private static void setTyping(Entity entity, CommonEntityType type){ + if(type == null){ + throw new Error("Provided null typing!"); + } if(type instanceof CreatureData){ CommonEntityUtils.setEntityType(entity, EntityType.CREATURE); CommonEntityUtils.setEntitySubtype(entity, type.getId()); diff --git a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java index 17bbd6ed..5ada874e 100644 --- a/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java +++ b/src/main/java/electrosphere/entity/types/creature/CreatureUtils.java @@ -482,14 +482,29 @@ public class CreatureUtils { return (String)CommonEntityUtils.getEntitySubtype(e); } + /** + * Gets the associated player ID that controls this entity + * @param e The entity + * @return The player ID + */ public static int getControllerPlayerId(Entity e){ return (int)e.getData(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID); } + /** + * Sets the associated player ID that controls this creature + * @param e The entity + * @param id The id + */ public static void setControllerPlayerId(Entity e, int id){ e.putData(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID, id); } + /** + * Checks if this entity has a player that controls it + * @param e The entity + * @return true if a player controls it, false otherwise + */ public static boolean hasControllerPlayerId(Entity e){ return e.containsKey(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID); } diff --git a/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java b/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java index 0a31c51a..e76206d7 100644 --- a/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java +++ b/src/main/java/electrosphere/net/client/protocol/ServerProtocol.java @@ -1,6 +1,7 @@ package electrosphere.net.client.protocol; import electrosphere.engine.Globals; +import electrosphere.logger.LoggerInterface; import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.template.ClientProtocolTemplate; @@ -24,6 +25,9 @@ public class ServerProtocol implements ClientProtocolTemplate { //let the networking loop know we received a pong message Globals.clientConnection.markReceivedPongMessage(); break; + case DISCONNECT: { + LoggerInterface.loggerNetworking.WARNING("Server sent signal to disconnect!"); + } break; } } diff --git a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java index 5b9889b0..5d39887f 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -255,6 +255,11 @@ public abstract class NetworkMessage { rVal = ServerMessage.parsePongMessage(byteBuffer,pool); } break; + case TypeBytes.SERVER_MESSAGE_TYPE_DISCONNECT: + if(ServerMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = ServerMessage.parseDisconnectMessage(byteBuffer,pool); + } + break; } break; case TypeBytes.MESSAGE_TYPE_AUTH: diff --git a/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java b/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java index bb479c2c..b65b2d1e 100644 --- a/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/ServerMessage.java @@ -9,6 +9,7 @@ public class ServerMessage extends NetworkMessage { public enum ServerMessageType { PING, PONG, + DISCONNECT, } /** @@ -64,6 +65,12 @@ public class ServerMessage extends NetworkMessage { } else { return false; } + case TypeBytes.SERVER_MESSAGE_TYPE_DISCONNECT: + if(byteBuffer.getRemaining() >= TypeBytes.SERVER_MESSAGE_TYPE_DISCONNECT_SIZE){ + return true; + } else { + return false; + } } return false; } @@ -106,6 +113,25 @@ public class ServerMessage extends NetworkMessage { return rVal; } + /** + * Parses a message of type Disconnect + */ + public static ServerMessage parseDisconnectMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + ServerMessage rVal = (ServerMessage)pool.get(MessageType.SERVER_MESSAGE); + rVal.messageType = ServerMessageType.DISCONNECT; + ServerMessage.stripPacketHeader(byteBuffer); + return rVal; + } + + /** + * Constructs a message of type Disconnect + */ + public static ServerMessage constructDisconnectMessage(){ + ServerMessage rVal = new ServerMessage(ServerMessageType.DISCONNECT); + rVal.serialize(); + return rVal; + } + @Override void serialize(){ switch(this.messageType){ @@ -123,6 +149,13 @@ public class ServerMessage extends NetworkMessage { //entity messaage header rawBytes[1] = TypeBytes.SERVER_MESSAGE_TYPE_PONG; break; + case DISCONNECT: + rawBytes = new byte[2]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_SERVER; + //entity messaage header + rawBytes[1] = TypeBytes.SERVER_MESSAGE_TYPE_DISCONNECT; + break; } serialized = true; } diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java index 8181c7e1..4d320ab2 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java +++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java @@ -103,11 +103,13 @@ public class TypeBytes { */ public static final byte SERVER_MESSAGE_TYPE_PING = 0; public static final byte SERVER_MESSAGE_TYPE_PONG = 1; + public static final byte SERVER_MESSAGE_TYPE_DISCONNECT = 2; /* Server packet sizes */ public static final byte SERVER_MESSAGE_TYPE_PING_SIZE = 2; public static final byte SERVER_MESSAGE_TYPE_PONG_SIZE = 2; + public static final byte SERVER_MESSAGE_TYPE_DISCONNECT_SIZE = 2; /* Auth subcategories diff --git a/src/main/java/electrosphere/net/server/Server.java b/src/main/java/electrosphere/net/server/Server.java index c6e297d6..b11abcbb 100644 --- a/src/main/java/electrosphere/net/server/Server.java +++ b/src/main/java/electrosphere/net/server/Server.java @@ -28,32 +28,46 @@ import java.util.concurrent.TimeUnit; */ public class Server implements Runnable { - //tracks whether the server is open or not + /** + * tracks whether the server is open or not + */ private boolean isOpen = false; - //the port the server is running on - int port; + /** + * the port the server is running on + */ + private int port; - //the socket for the server - ServerSocket serverSocket; + /** + * the socket for the server + */ + private ServerSocket serverSocket; - //Used to synchronize additions/subtractions to the connections stored by this server - Semaphore connectListLock = new Semaphore(1); + /** + * Used to synchronize additions/subtractions to the connections stored by this server + */ + private Semaphore connectListLock = new Semaphore(1); - //map of socket->connection - Map socketConnectionMap = new HashMap(); + /** + * map of socket->connection + */ + private Map socketConnectionMap = new HashMap(); - //the list of active connections - List activeConnections = new LinkedList(); + /** + * the list of active connections + */ + private List activeConnections = new LinkedList(); - //The list of connections to clean up - List connectionsToCleanup = new CopyOnWriteArrayList(); + /** + * The list of connections to clean up + */ + private List connectionsToCleanup = new CopyOnWriteArrayList(); /** * Inits the server */ - void initServer(){ + private void initServer(){ // clientMap = new HashMap(); } @@ -67,7 +81,7 @@ public class Server implements Runnable { @Override public void run() { - initServer(); + this.initServer(); try { serverSocket = new ServerSocket(port); //if we set port to 0, java searches for any available port to open @@ -204,6 +218,24 @@ public class Server implements Runnable { this.connectListLock.release(); } + /** + * Saves state from the server connections and shuts down the connections + */ + public void saveAndClose(){ + this.connectListLock.acquireUninterruptibly(); + //store each player's character + for(ServerConnectionHandler connection : this.activeConnections){ + connection.disconnect(); + } + //close the server + try { + this.serverSocket.close(); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR(e); + } + this.connectListLock.release(); + } + /** * Gets whether the server is open or not * @return true if is open, false otherwise diff --git a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java index 1c00bd2b..e7696c0a 100644 --- a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java +++ b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java @@ -25,66 +25,112 @@ import java.util.concurrent.TimeUnit; */ public class ServerConnectionHandler implements Runnable { - //local carrier variables + //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 = 60 * 1000; + + /** + * local carrier variables + */ boolean local = false; - //socket carrier variables + /** + * socket carrier variables + */ Socket socket; //the streams for the connection // CryptoInputStream inputStream; // CryptoOutputStream outputStream; + /** * The input stream for packets */ InputStream inputStream; + /** * The output stream for packets */ OutputStream outputStream; - //the network parser for the streams + /** + * the network parser for the streams + */ NetworkParser networkParser; - //initialized status + /** + * initialized status + */ boolean initialized; - //authentication status + + /** + * authentication status + */ boolean isAuthenticated = false; - //the player id + /** + * the player id + */ int playerID; - //the player's entity id + + /** + * the player's entity id + */ int playerEntityID; + /** + * The id of the character that is associated with the player's entity + */ + int characterId; + /** * Tracks whether this connection is still communicating with the client */ boolean isConnected = true; - //the creature template associated with this player + /** + * the creature template associated with this player + */ CreatureTemplate currentCreatureTemplate; - //the server protocol object associated with this player + /** + * the server protocol object associated with this player + */ MessageProtocol messageProtocol; - //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 = 60 * 1000; - //used to keep track of ping/pong messages with client + /** + * Keeps track of the last time this connection received a ping from the client + */ long lastPingTime = 0; + /** + * Keeps track of the last time this connection received a pong from the client + */ long lastPongTime = 0; - //flag to disconnect due to pipe break + + /** + * flag to disconnect due to pipe break + */ boolean socketException = false; - //debug netmonitor stuff + /** + * debug netmonitor stuff + */ String netMonitorHandle; - //Used to copy messages from network parser to NetMonitor + + /** + * Used to copy messages from network parser to NetMonitor + */ List netMonitorCache = new LinkedList(); - //the lock used for synchronizing the synchronous message queue + /** + * the lock used for synchronizing the synchronous message queue + */ Semaphore synchronousMessageLock = new Semaphore(1); - //the queue of synchonous network messages + + /** + * the queue of synchonous network messages + */ List synchronousMessageQueue = new CopyOnWriteArrayList(); /** @@ -198,7 +244,7 @@ public class ServerConnectionHandler implements Runnable { // //parse messages both incoming and outgoing try { - receivedMessageThisLoop = parseMessages(); + receivedMessageThisLoop = this.parseMessages(); } catch (SocketException e) { //if we get a SocketException broken pipe (basically the client dc'd without telling us) //set flag to disconnect client @@ -228,7 +274,7 @@ public class ServerConnectionHandler implements Runnable { 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()); + this.addMessagetoOutgoingQueue(ServerMessage.constructPingMessage()); lastPingTime = currentTime; if(lastPongTime == 0){ lastPongTime = lastPingTime; @@ -245,14 +291,14 @@ public class ServerConnectionHandler implements Runnable { //disconnected from the server LoggerInterface.loggerNetworking.WARNING("Client timeout"); //run disconnect routine - disconnect(); + this.disconnect(); break; } if(this.socketException == true){ //disconnected from the server LoggerInterface.loggerNetworking.WARNING("Client disconnected"); //run disconnect routine - disconnect(); + this.disconnect(); break; } } @@ -266,7 +312,7 @@ public class ServerConnectionHandler implements Runnable { * @throws SocketException * @return true if connection is alive, false otherwise */ - boolean parseMessages() throws SocketException, IOException { + private boolean parseMessages() throws SocketException, IOException { boolean rVal = false; // //Read in messages @@ -333,15 +379,27 @@ public class ServerConnectionHandler implements Runnable { this.messageProtocol.handleSyncMessages(); } + /** + * Gets the player's id + * @return The player's id + */ public int getPlayerId(){ return playerID; } + /** + * Sets the player's entity's id + * @param id + */ public void setPlayerEntityId(int id){ LoggerInterface.loggerNetworking.DEBUG("Set player(" + this.playerID + ")'s entity ID to be " + id); playerEntityID = id; } + /** + * Gets the player's entity's id + * @return The id + */ public int getPlayerEntityId(){ return playerEntityID; } @@ -374,22 +432,14 @@ public class ServerConnectionHandler implements Runnable { return this.socket; } + /** + * Adds a message to the outgoing queue + * @param message The message + */ public void addMessagetoOutgoingQueue(NetworkMessage message){ networkParser.addOutgoingMessage(message); } - boolean isConnectionPlayerEntity(int id){ - return id == this.playerEntityID; - } - - /** - * Returns true if running both client and server in same instance of the app and this is the player for that instance of the app - * @return - */ - boolean isServerClient(){ - return Globals.RUN_SERVER && Globals.RUN_CLIENT && Globals.clientPlayer != null && this.playerID == Globals.clientPlayer.getId(); - } - /** * Sets the current creature template for the connection * @param currentCreatureTemplate The new creature template @@ -406,6 +456,9 @@ public class ServerConnectionHandler implements Runnable { return this.currentCreatureTemplate; } + /** + * Marks that this connection received a pong message + */ public void markReceivedPongMessage(){ lastPongTime = System.currentTimeMillis(); } @@ -413,8 +466,21 @@ public class ServerConnectionHandler implements Runnable { /** * Routine to run when the client disconnects */ - private void disconnect(){ + protected void disconnect(){ //close socket + this.synchronousMessageLock.acquireUninterruptibly(); + + //queue message to tell client it disconnected + this.networkParser.addOutgoingMessage(ServerMessage.constructDisconnectMessage()); + + //flush outgoing messages + try { + this.networkParser.pushMessagesOut(); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR(e); + } + + //close the socket if(socket != null && socket.isConnected()){ try { socket.close(); @@ -422,6 +488,8 @@ public class ServerConnectionHandler implements Runnable { LoggerInterface.loggerNetworking.ERROR("Error closing socket", e); } } + + this.synchronousMessageLock.release(); this.isConnected = false; //add connection to server list of connections to cleanup if(Globals.server != null){ @@ -440,4 +508,22 @@ public class ServerConnectionHandler implements Runnable { return this.networkParser.getNumberOfBytesRead(); } + /** + * Gets the id of the character associated with the player's entity + * @return The character's id + */ + public int getCharacterId() { + return characterId; + } + + /** + * Sets the id of the character associated with the player's entity + * @param characterId The character's id + */ + public void setCharacterId(int characterId) { + this.characterId = characterId; + } + + + } diff --git a/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java b/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java index fbf2f430..308add98 100644 --- a/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/CharacterProtocol.java @@ -1,6 +1,5 @@ package electrosphere.net.server.protocol; -import java.util.LinkedList; import java.util.List; import java.util.Random; @@ -24,10 +23,9 @@ import electrosphere.net.parser.net.message.PlayerMessage; import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.player.Player; import electrosphere.net.template.ServerProtocolTemplate; +import electrosphere.server.character.CharacterService; import electrosphere.server.character.PlayerCharacterCreation; import electrosphere.server.datacell.Realm; -import electrosphere.server.db.DatabaseResult; -import electrosphere.server.db.DatabaseResultRow; import electrosphere.util.Utilities; /** @@ -45,18 +43,7 @@ public class CharacterProtocol implements ServerProtocolTemplate characters = new LinkedList(); - DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData WHERE playerId=?;",connectionHandler.getPlayer().getDBID()); - if(result.hasResult()){ - //if we get a valid response from the database, check that it actually matches hashes - for(DatabaseResultRow row : result){ - CharacterDescription description = new CharacterDescription(); - CreatureTemplate template = gson.fromJson(row.getAsString("dataVal"),CreatureTemplate.class); - description.setTemplate(template); - description.setId(row.getAsInteger("id") + ""); - characters.add(description); - } - } + List characters = CharacterService.getCharacters(connectionHandler.getPlayer().getDBID()); ClientCharacterListDTO dto = new ClientCharacterListDTO(); dto.setCharacters(characters); connectionHandler.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCharacterListMessage(gson.toJson(dto))); @@ -74,11 +61,7 @@ public class CharacterProtocol implements ServerProtocolTemplate { - //the connection handler associated with this protocol object + /** + * the connection handler associated with this protocol object + */ ServerConnectionHandler connectionHandler; @Override public ServerMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, ServerMessage message) { switch(message.getMessageSubtype()){ - case PING: + case PING: { connectionHandler.addMessagetoOutgoingQueue(ServerMessage.constructPongMessage()); - break; - case PONG: + } break; + case PONG: { connectionHandler.markReceivedPongMessage(); + } break; + case DISCONNECT: + //silently ignore break; } return null; @@ -29,6 +34,7 @@ public class ServerProtocol implements ServerProtocolTemplate { @Override public void handleSyncMessage(ServerConnectionHandler connectionHandler, ServerMessage message) { switch(message.getMessageSubtype()){ + case DISCONNECT: case PING: case PONG: //silently ignore diff --git a/src/main/java/electrosphere/server/character/CharacterService.java b/src/main/java/electrosphere/server/character/CharacterService.java new file mode 100644 index 00000000..14a46299 --- /dev/null +++ b/src/main/java/electrosphere/server/character/CharacterService.java @@ -0,0 +1,124 @@ +package electrosphere.server.character; + +import java.util.LinkedList; +import java.util.List; + +import org.joml.Vector3d; + +import com.google.gson.Gson; + +import electrosphere.client.entity.character.CharacterDescription; +import electrosphere.engine.Globals; +import electrosphere.entity.Entity; +import electrosphere.entity.EntityUtils; +import electrosphere.entity.state.server.ServerCharacterData; +import electrosphere.entity.state.server.ServerPlayerViewDirTree; +import electrosphere.entity.types.creature.CreatureTemplate; +import electrosphere.logger.LoggerInterface; +import electrosphere.server.db.DatabaseResult; +import electrosphere.server.db.DatabaseResultRow; + +/** + * Service for interacting with macro-level characters + */ +public class CharacterService { + + /** + * Creates a character in the database + * @param template The creature template for the character + * @param playerId The player's id + */ + public static void createCharacter(CreatureTemplate template, int playerId){ + Globals.dbController.executePreparedStatement( + "INSERT INTO charaData (playerId,dataVal) VALUES (?,?);", + playerId, + new Gson().toJson(template) + ); + } + + /** + * Gets the template for a character + * @param playerId The player's id + * @param characterId The character's id + * @return The template if it exists, null otherwise + */ + public static CreatureTemplate getTemplate(int playerId, int characterId){ + DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData WHERE playerId=? AND id=?;",playerId, characterId); + if(result.hasResult()){ + Gson gson = new Gson(); + //if we get a valid response from the database, check that it actually matches hashes + for(DatabaseResultRow row : result){ + CreatureTemplate template = gson.fromJson(row.getAsString("dataVal"),CreatureTemplate.class); + return template; + } + } + LoggerInterface.loggerDB.WARNING("Failed to locate creature template for playerId=" + playerId + " characterId=" + characterId); + return null; + } + + /** + * Gets the characters that a player has + * @param playerId The player's id + * @return The list of characters that player has + */ + public static List getCharacters(int playerId){ + DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData WHERE playerId=?;",playerId); + Gson gson = new Gson(); + List rVal = new LinkedList(); + if(result.hasResult()){ + //if we get a valid response from the database, check that it actually matches hashes + for(DatabaseResultRow row : result){ + CharacterDescription description = new CharacterDescription(); + CreatureTemplate template = gson.fromJson(row.getAsString("dataVal"),CreatureTemplate.class); + description.setTemplate(template); + description.setId(row.getAsInteger("id") + ""); + rVal.add(description); + } + } + return rVal; + } + + /** + * Gets the position of a character + * @param characterId The character's Id + * @return The position if it is stored in the DB, null otherwise + */ + public static Vector3d getCharacterPosition(int characterId){ + DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT x, y, z FROM charaRealPos WHERE id=?;",characterId); + if(result.hasResult()){ + //if we get a valid response from the database, return the position + for(DatabaseResultRow row : result){ + double x = row.getAsDouble("x"); + double y = row.getAsDouble("y"); + double z = row.getAsDouble("z"); + return new Vector3d(x,y,z); + } + } + return null; + } + + /** + * Saves a character from an entity + * @param characterEntity The entity + */ + public static void saveCharacter(Entity characterEntity){ + if(!ServerCharacterData.hasServerPlayerCharacterDataTree(characterEntity)){ + throw new Error("Trying to save entity hat does not contain character data!"); + } + ServerCharacterData characterData = ServerCharacterData.getServerCharacterData(characterEntity); + int characterId = characterData.getCharacterId(); + + //store exact position if it's a player's entity + if(ServerPlayerViewDirTree.hasTree(characterEntity)){ + Vector3d realPos = EntityUtils.getPosition(characterEntity); + Globals.dbController.executePreparedStatement( + "INSERT OR REPLACE INTO charaRealPos (id, x, y, z) VALUES (?,?,?,?);", + characterId, + realPos.x, + realPos.y, + realPos.z + ); + } + } + +} diff --git a/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java b/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java index 5ba2dbd3..9ffc79c4 100644 --- a/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java +++ b/src/main/java/electrosphere/server/character/PlayerCharacterCreation.java @@ -5,12 +5,14 @@ import org.joml.Vector3i; import electrosphere.engine.Globals; import electrosphere.entity.Entity; +import electrosphere.entity.state.server.ServerCharacterData; import electrosphere.entity.state.server.ServerPlayerViewDirTree; import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.logger.LoggerInterface; import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.player.Player; +import electrosphere.net.server.protocol.CharacterProtocol; import electrosphere.server.datacell.Realm; /** @@ -28,7 +30,10 @@ public class PlayerCharacterCreation { // //get template - CreatureTemplate template = connectionHandler.getCurrentCreatureTemplate(); + CreatureTemplate template = CharacterService.getTemplate(connectionHandler.getPlayer().getDBID(), connectionHandler.getCharacterId()); + if(connectionHandler.getCharacterId() == CharacterProtocol.SPAWN_EXISTING_TEMPLATE){ + template = connectionHandler.getCurrentCreatureTemplate(); + } String raceName = template.getCreatureType(); // @@ -67,21 +72,23 @@ public class PlayerCharacterCreation { * @param serverConnectionHandler The connection handler */ public static void attachEntityToPlayerObject(Entity entity, Player playerObject, ServerConnectionHandler serverConnectionHandler){ - int playerCharacterId = entity.getId(); - serverConnectionHandler.setPlayerEntityId(playerCharacterId); + int playerEntityId = entity.getId(); + serverConnectionHandler.setPlayerEntityId(playerEntityId); CreatureUtils.setControllerPlayerId(entity, serverConnectionHandler.getPlayerId()); Player player = serverConnectionHandler.getPlayer(); player.setPlayerEntity(entity); //custom player btrees - PlayerCharacterCreation.addPlayerServerBTrees(entity); + PlayerCharacterCreation.addPlayerServerBTrees(entity, serverConnectionHandler); } /** * Adds behavior trees that are unique to players a given entity * @param entity The entity to add to + * @param serverConnectionHandler The server connection handler for the entity */ - static void addPlayerServerBTrees(Entity entity){ + static void addPlayerServerBTrees(Entity entity, ServerConnectionHandler serverConnectionHandler){ ServerPlayerViewDirTree.attachServerPlayerViewDirTree(entity); + ServerCharacterData.attachServerPlayerViewDirTree(entity, serverConnectionHandler.getCharacterId()); } /** @@ -91,7 +98,10 @@ public class PlayerCharacterCreation { * @return The spawn point for the player */ public static Vector3d solveSpawnPoint(Realm realm, ServerConnectionHandler connectionHandler){ - Vector3d spawnPoint = realm.getSpawnPoint(); + Vector3d spawnPoint = CharacterService.getCharacterPosition(connectionHandler.getCharacterId()); + if(spawnPoint == null){ + spawnPoint = realm.getSpawnPoint(); + } return spawnPoint; } diff --git a/src/main/java/electrosphere/server/content/ServerContentManager.java b/src/main/java/electrosphere/server/content/ServerContentManager.java index 1e326745..3b63d420 100644 --- a/src/main/java/electrosphere/server/content/ServerContentManager.java +++ b/src/main/java/electrosphere/server/content/ServerContentManager.java @@ -6,6 +6,8 @@ import org.joml.Vector3i; import electrosphere.engine.Globals; import electrosphere.entity.Entity; +import electrosphere.entity.state.server.ServerCharacterData; +import electrosphere.server.character.CharacterService; import electrosphere.server.content.serialization.ContentSerialization; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.ServerDataCell; @@ -78,10 +80,18 @@ public class ServerContentManager { * @param entities the collection of entities to save */ public void saveContentToDisk(Long locationKey, Collection entities){ + //serialize all non-character entities ContentSerialization serialization = ContentSerialization.constructContentSerialization(entities); String dirPath = SaveUtils.deriveSaveDirectoryPath(Globals.currentSave.getName()); String fullPath = dirPath + "/content/" + locationKey + ".dat"; FileUtils.serializeObjectToFilePath(fullPath, serialization); + + //store all character entities in database + for(Entity entity : entities){ + if(ServerCharacterData.hasServerPlayerCharacterDataTree(entity)){ + CharacterService.saveCharacter(entity); + } + } } /** diff --git a/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java index 56b89031..a2f9f3ce 100644 --- a/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java +++ b/src/main/java/electrosphere/server/content/serialization/ContentSerialization.java @@ -24,7 +24,9 @@ import electrosphere.util.Utilities; public class ContentSerialization { - //The entities, serialized + /** + * The entities, serialized + */ List serializedEntities = new LinkedList(); /** @@ -38,52 +40,55 @@ public class ContentSerialization { if(!CreatureUtils.hasControllerPlayerId(entity)){ EntityType type = CommonEntityUtils.getEntityType(entity); if(type != null){ - switch(type){ - case CREATURE: { - EntitySerialization serializedEntity = new EntitySerialization(); - serializedEntity.setPosition(EntityUtils.getPosition(entity)); - serializedEntity.setRotation(EntityUtils.getRotation(entity)); - serializedEntity.setType(EntityType.CREATURE.getValue()); - serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); - if(CreatureUtils.getCreatureTemplate(entity) != null){ - serializedEntity.setTemplate(Utilities.stringify(CreatureUtils.getCreatureTemplate(entity))); - } - rVal.serializedEntities.add(serializedEntity); - } break; - case ITEM: { - if(!AttachUtils.isAttached(entity)){ - EntitySerialization serializedEntity = new EntitySerialization(); - serializedEntity.setPosition(EntityUtils.getPosition(entity)); - serializedEntity.setRotation(EntityUtils.getRotation(entity)); - serializedEntity.setType(EntityType.ITEM.getValue()); - serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); - rVal.serializedEntities.add(serializedEntity); - } - } break; - case FOLIAGE: { - EntitySerialization serializedEntity = new EntitySerialization(); - serializedEntity.setPosition(EntityUtils.getPosition(entity)); - serializedEntity.setRotation(EntityUtils.getRotation(entity)); - serializedEntity.setType(EntityType.FOLIAGE.getValue()); - serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); - serializedEntity.setTemplate(FoliageUtils.getFoliageSeed(entity) + ""); - rVal.serializedEntities.add(serializedEntity); - } break; - case COMMON: { - EntitySerialization serializedEntity = new EntitySerialization(); - serializedEntity.setPosition(EntityUtils.getPosition(entity)); - serializedEntity.setRotation(EntityUtils.getRotation(entity)); - serializedEntity.setType(EntityType.COMMON.getValue()); - serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); - rVal.serializedEntities.add(serializedEntity); - } break; - } + EntitySerialization serializedEntity = constructEntitySerialization(entity); + rVal.serializedEntities.add(serializedEntity); } } } return rVal; } + /** + * Constructs a serialization of an entity + * @param entity The entity + * @return The serialization of the entity + */ + public static EntitySerialization constructEntitySerialization(Entity entity){ + EntitySerialization serializedEntity = new EntitySerialization(); + serializedEntity.setPosition(EntityUtils.getPosition(entity)); + serializedEntity.setRotation(EntityUtils.getRotation(entity)); + EntityType type = CommonEntityUtils.getEntityType(entity); + if(type != null){ + switch(type){ + case CREATURE: { + serializedEntity.setType(EntityType.CREATURE.getValue()); + serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); + if(CreatureUtils.getCreatureTemplate(entity) != null){ + serializedEntity.setTemplate(Utilities.stringify(CreatureUtils.getCreatureTemplate(entity))); + } + } break; + case ITEM: { + if(!AttachUtils.isAttached(entity)){ + serializedEntity.setType(EntityType.ITEM.getValue()); + serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); + } + } break; + case FOLIAGE: { + serializedEntity.setType(EntityType.FOLIAGE.getValue()); + serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); + serializedEntity.setTemplate(FoliageUtils.getFoliageSeed(entity) + ""); + } break; + case COMMON: { + serializedEntity.setType(EntityType.COMMON.getValue()); + serializedEntity.setSubtype(CommonEntityUtils.getEntitySubtype(entity)); + } break; + } + } else { + throw new Error("Trying to save entity that does not have a type!"); + } + return serializedEntity; + } + /** * Actually creates the entities from a content serialization * @param contentRaw The content serialization diff --git a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java index 7feb9190..347bff58 100644 --- a/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java +++ b/src/main/java/electrosphere/server/datacell/gridded/GriddedDataCellManager.java @@ -37,7 +37,6 @@ import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.datacell.interfaces.DataCellManager; import electrosphere.server.datacell.interfaces.VoxelCellManager; import electrosphere.server.datacell.physics.PhysicsDataCell; -import electrosphere.server.datacell.utils.EntityLookupUtils; import electrosphere.server.fluid.manager.ServerFluidChunk; import electrosphere.server.fluid.manager.ServerFluidManager; import electrosphere.server.terrain.manager.ServerTerrainManager; diff --git a/src/main/java/electrosphere/server/db/DatabaseController.java b/src/main/java/electrosphere/server/db/DatabaseController.java index 6b3f4a9c..6b614d50 100644 --- a/src/main/java/electrosphere/server/db/DatabaseController.java +++ b/src/main/java/electrosphere/server/db/DatabaseController.java @@ -12,6 +12,11 @@ import java.util.Properties; * A controller for database logic */ public class DatabaseController { + + /** + * file extension for allowed database files to open + */ + public static final String FILE_EXT = ".sqlite"; /** * The database connection @@ -43,6 +48,7 @@ public class DatabaseController { * Executes a write statement to the database * @param statementRaw The raw string for the statement * @param arguments The arguments to be inserted into the raw sql + * @return true if there is a result set, false if there is an update count or no result */ public boolean executePreparedStatement(String statementRaw, Object...arguments){ try { diff --git a/src/main/java/electrosphere/server/db/DatabaseResultIterator.java b/src/main/java/electrosphere/server/db/DatabaseResultIterator.java index f243b663..33f203ab 100644 --- a/src/main/java/electrosphere/server/db/DatabaseResultIterator.java +++ b/src/main/java/electrosphere/server/db/DatabaseResultIterator.java @@ -90,21 +90,24 @@ public class DatabaseResultIterator implements Iterator { //increment at the beginning because result sets are indexed starting at 1 columnIncrementer++; switch(type){ - case Types.INTEGER: - row.putValue(metadata.getColumnName(columnIncrementer), rs.getInt(columnIncrementer)); - break; - case Types.VARCHAR: - row.putValue(metadata.getColumnName(columnIncrementer), rs.getString(columnIncrementer)); - break; - case Types.BIGINT: - row.putValue(metadata.getColumnName(columnIncrementer), rs.getLong(columnIncrementer)); - break; - case Types.FLOAT: - row.putValue(metadata.getColumnName(columnIncrementer), rs.getFloat(columnIncrementer)); - break; - case Types.DOUBLE: - row.putValue(metadata.getColumnName(columnIncrementer), rs.getDouble(columnIncrementer)); - break; + case Types.INTEGER: { + row.putValue(metadata.getColumnName(columnIncrementer), rs.getInt(columnIncrementer)); + } break; + case Types.VARCHAR: { + row.putValue(metadata.getColumnName(columnIncrementer), rs.getString(columnIncrementer)); + } break; + case Types.BIGINT: { + row.putValue(metadata.getColumnName(columnIncrementer), rs.getLong(columnIncrementer)); + } break; + case Types.FLOAT: { + row.putValue(metadata.getColumnName(columnIncrementer), rs.getFloat(columnIncrementer)); + } break; + case Types.DOUBLE: { + row.putValue(metadata.getColumnName(columnIncrementer), rs.getDouble(columnIncrementer)); + } break; + case Types.REAL: { + row.putValue(metadata.getColumnName(columnIncrementer), rs.getDouble(columnIncrementer)); + } break; default: LoggerInterface.loggerEngine.WARNING("Unsupported type from database in DatabaseResultIterator " + type); break; diff --git a/src/main/java/electrosphere/server/db/DatabaseResultRow.java b/src/main/java/electrosphere/server/db/DatabaseResultRow.java index 4b89c271..58fb49fe 100644 --- a/src/main/java/electrosphere/server/db/DatabaseResultRow.java +++ b/src/main/java/electrosphere/server/db/DatabaseResultRow.java @@ -3,11 +3,14 @@ package electrosphere.server.db; import java.util.HashMap; import java.util.Map; +/** + * A row result from a database operation + */ public class DatabaseResultRow { - - // protected addColumn(int column) - //stores all the values of the row in an easily indexible-by-column-name format + /** + * stores all the values of the row in an easily indexible-by-column-name format + */ Map values = new HashMap(); /** diff --git a/src/main/java/electrosphere/server/db/DatabaseUtils.java b/src/main/java/electrosphere/server/db/DatabaseUtils.java index 4e77b914..cb49374e 100644 --- a/src/main/java/electrosphere/server/db/DatabaseUtils.java +++ b/src/main/java/electrosphere/server/db/DatabaseUtils.java @@ -6,38 +6,40 @@ import electrosphere.util.FileUtils; import java.io.IOException; /** - * - * @author satellite + * Utilities for working with the database */ public class DatabaseUtils { + /** + * Initializes the central db file + * @param path The path to initialize at + * @return true if it succeeded, false otherwise + */ public static boolean initCentralDBFile(String path){ String sanitizedPath = FileUtils.sanitizeFilePath(path); if(!FileUtils.checkFileExists(sanitizedPath)){ return false; } - String dbFilePath = sanitizedPath + "/central.db"; + String dbFilePath = sanitizedPath + "/central" + DatabaseController.FILE_EXT; if(Globals.dbController == null){ Globals.dbController = new DatabaseController(); } if(!Globals.dbController.isConnected()){ Globals.dbController.connect(dbFilePath); } - runScript(Globals.dbController,"createTables.sql"); + DatabaseUtils.runScript(Globals.dbController,"createTables.sql"); //both of these are used for arena mode as well as main game - runScript(Globals.dbController,"/auth/createAuthTables.sql"); - runScript(Globals.dbController,"/character/createCharacterTables.sql"); - //create adventure-only files - if(!dbFilePath.equals("./saves/arena/central.db")){ - //we only want to create these if we're not in arena mode - runScript(Globals.dbController,"/towns/createTownsTables.sql"); - runScript(Globals.dbController,"/structs/createStructsTables.sql"); - } Globals.dbController.disconnect(); return true; } + /** + * Runs a script + * @param controller The controller + * @param scriptPath The script's path + * @return true if it succeeds, false otherwise + */ public static boolean runScript(DatabaseController controller, String scriptPath){ String rawScript = ""; try { diff --git a/src/main/java/electrosphere/server/saves/SaveUtils.java b/src/main/java/electrosphere/server/saves/SaveUtils.java index e628b3ea..e68f4f02 100644 --- a/src/main/java/electrosphere/server/saves/SaveUtils.java +++ b/src/main/java/electrosphere/server/saves/SaveUtils.java @@ -8,6 +8,7 @@ import electrosphere.entity.scene.SceneFile; import electrosphere.entity.scene.SceneLoader; import electrosphere.game.server.world.ServerWorldData; import electrosphere.logger.LoggerInterface; +import electrosphere.server.db.DatabaseController; import electrosphere.server.db.DatabaseUtils; import electrosphere.server.fluid.generation.DefaultFluidGenerator; import electrosphere.server.fluid.manager.ServerFluidManager; @@ -155,6 +156,9 @@ public class SaveUtils { //write server structures Globals.realmManager.save(saveName); + + //close the server + Globals.server.close(); } /** @@ -211,7 +215,7 @@ public class SaveUtils { } //load db - String dbFilePath = FileUtils.sanitizeFilePath(dirPath) + "/central.db"; + String dbFilePath = FileUtils.sanitizeFilePath(dirPath) + "/central" + DatabaseController.FILE_EXT; Globals.dbController.connect(dbFilePath); return true; diff --git a/src/net/server.json b/src/net/server.json index 583d73b9..dea75ae6 100644 --- a/src/net/server.json +++ b/src/net/server.json @@ -17,6 +17,11 @@ "messageName" : "Pong", "description" : "Replies to a ping from the other side of the socket", "data" : [] + }, + { + "messageName" : "Disconnect", + "description" : "Tell the other side of the socket that this side is disconnecting", + "data" : [] } ] },