database work + serialization work + save pos
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-04-15 20:42:39 -04:00
parent c085e34dbb
commit 58dc5333e6
36 changed files with 619 additions and 217 deletions

View File

@ -1,5 +1,5 @@
--main table -- main table
CREATE TABLE mainTable (propName VARCHAR PRIMARY KEY, propValue VARCHAR); CREATE TABLE mainTable (propName VARCHAR PRIMARY KEY, propValue VARCHAR);
INSERT INTO mainTable (propName, propValue) VALUES ("ver","1"); 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
);

View File

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

View File

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

View File

@ -1,6 +0,0 @@
--positions
DELETE FROM structWorldPositions WHERE structID=0;
--data
DELETE FROM structData WHERE structID=0;

View File

@ -1 +0,0 @@
SELECT * FROM structData WHERE structID IN (1, 2);

View File

@ -1 +0,0 @@
SELECT * FROM structData WHERE structID=0;

View File

@ -1 +0,0 @@
SELECT * FROM structWorldPositions WHERE posX = 10 AND posY = 10;

View File

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

View File

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

View File

@ -1,6 +0,0 @@
--positions
DELETE FROM townWorldPositions WHERE townID=0;
--data
DELETE FROM townData WHERE townID=0;

View File

@ -1,2 +0,0 @@
--given x=10, y=11
SELECT townID FROM townWorldPositions WHERE posX = 10 AND posY = 11;

View File

@ -1493,6 +1493,14 @@ Fix yoga not applying on item drop window creation
Fix client not destroying item on remove from inventory Fix client not destroying item on remove from inventory
Tests for above bugs Tests for above bugs
Fix rendering 3rd person model-attached entities drawing when in first person 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

View File

@ -106,8 +106,9 @@ public class MenuGeneratorsInGame {
//Save //Save
{ {
Button button = Button.createButton("Save", () -> { Button button = Button.createButton("Save and Quit", () -> {
SaveUtils.overwriteSave(Globals.currentSave.getName()); SaveUtils.overwriteSave(Globals.currentSave.getName());
Globals.signalSystem.post(SignalType.ENGINE_RETURN_TO_TITLE);
}); });
button.setMarginTop(BUTTON_MARGIN); button.setMarginTop(BUTTON_MARGIN);
button.setMarginLeft(BUTTON_MARGIN); button.setMarginLeft(BUTTON_MARGIN);
@ -116,7 +117,7 @@ public class MenuGeneratorsInGame {
//Quit //Quit
{ {
Button button = Button.createButton("Quit", () -> { Button button = Button.createButton("Shutdown", () -> {
Main.running = false; Main.running = false;
}); });
button.setMarginTop(BUTTON_MARGIN); button.setMarginTop(BUTTON_MARGIN);

View File

@ -326,6 +326,7 @@ public class EntityDataStrings {
* Server-specific btrees * Server-specific btrees
*/ */
public static final String TREE_SERVERPLAYERVIEWDIR = "treeServerPlayerViewDir"; public static final String TREE_SERVERPLAYERVIEWDIR = "treeServerPlayerViewDir";
public static final String TREE_SERVERCHARACTERDATA = "treeServerCharacterData";
/** /**
* Physics synchronization * Physics synchronization

View File

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

View File

@ -835,6 +835,9 @@ public class CommonEntityUtils {
* @param type The type * @param type The type
*/ */
private static void setTyping(Entity entity, CommonEntityType type){ private static void setTyping(Entity entity, CommonEntityType type){
if(type == null){
throw new Error("Provided null typing!");
}
if(type instanceof CreatureData){ if(type instanceof CreatureData){
CommonEntityUtils.setEntityType(entity, EntityType.CREATURE); CommonEntityUtils.setEntityType(entity, EntityType.CREATURE);
CommonEntityUtils.setEntitySubtype(entity, type.getId()); CommonEntityUtils.setEntitySubtype(entity, type.getId());

View File

@ -482,14 +482,29 @@ public class CreatureUtils {
return (String)CommonEntityUtils.getEntitySubtype(e); 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){ public static int getControllerPlayerId(Entity e){
return (int)e.getData(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID); 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){ public static void setControllerPlayerId(Entity e, int id){
e.putData(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID, 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){ public static boolean hasControllerPlayerId(Entity e){
return e.containsKey(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID); return e.containsKey(EntityDataStrings.DATA_STRING_CREATURE_CONTROLLER_PLAYER_ID);
} }

View File

@ -1,6 +1,7 @@
package electrosphere.net.client.protocol; package electrosphere.net.client.protocol;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.parser.net.message.ServerMessage;
import electrosphere.net.template.ClientProtocolTemplate; import electrosphere.net.template.ClientProtocolTemplate;
@ -24,6 +25,9 @@ public class ServerProtocol implements ClientProtocolTemplate<ServerMessage> {
//let the networking loop know we received a pong message //let the networking loop know we received a pong message
Globals.clientConnection.markReceivedPongMessage(); Globals.clientConnection.markReceivedPongMessage();
break; break;
case DISCONNECT: {
LoggerInterface.loggerNetworking.WARNING("Server sent signal to disconnect!");
} break;
} }
} }

View File

@ -255,6 +255,11 @@ public abstract class NetworkMessage {
rVal = ServerMessage.parsePongMessage(byteBuffer,pool); rVal = ServerMessage.parsePongMessage(byteBuffer,pool);
} }
break; break;
case TypeBytes.SERVER_MESSAGE_TYPE_DISCONNECT:
if(ServerMessage.canParseMessage(byteBuffer,secondByte)){
rVal = ServerMessage.parseDisconnectMessage(byteBuffer,pool);
}
break;
} }
break; break;
case TypeBytes.MESSAGE_TYPE_AUTH: case TypeBytes.MESSAGE_TYPE_AUTH:

View File

@ -9,6 +9,7 @@ public class ServerMessage extends NetworkMessage {
public enum ServerMessageType { public enum ServerMessageType {
PING, PING,
PONG, PONG,
DISCONNECT,
} }
/** /**
@ -64,6 +65,12 @@ public class ServerMessage extends NetworkMessage {
} else { } else {
return false; return false;
} }
case TypeBytes.SERVER_MESSAGE_TYPE_DISCONNECT:
if(byteBuffer.getRemaining() >= TypeBytes.SERVER_MESSAGE_TYPE_DISCONNECT_SIZE){
return true;
} else {
return false;
}
} }
return false; return false;
} }
@ -106,6 +113,25 @@ public class ServerMessage extends NetworkMessage {
return rVal; 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 @Override
void serialize(){ void serialize(){
switch(this.messageType){ switch(this.messageType){
@ -123,6 +149,13 @@ public class ServerMessage extends NetworkMessage {
//entity messaage header //entity messaage header
rawBytes[1] = TypeBytes.SERVER_MESSAGE_TYPE_PONG; rawBytes[1] = TypeBytes.SERVER_MESSAGE_TYPE_PONG;
break; 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; serialized = true;
} }

View File

@ -103,11 +103,13 @@ public class TypeBytes {
*/ */
public static final byte SERVER_MESSAGE_TYPE_PING = 0; 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_PONG = 1;
public static final byte SERVER_MESSAGE_TYPE_DISCONNECT = 2;
/* /*
Server packet sizes Server packet sizes
*/ */
public static final byte SERVER_MESSAGE_TYPE_PING_SIZE = 2; 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_PONG_SIZE = 2;
public static final byte SERVER_MESSAGE_TYPE_DISCONNECT_SIZE = 2;
/* /*
Auth subcategories Auth subcategories

View File

@ -28,32 +28,46 @@ import java.util.concurrent.TimeUnit;
*/ */
public class Server implements Runnable { 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; 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<Socket,ServerConnectionHandler> socketConnectionMap = new HashMap<Socket,ServerConnectionHandler>(); * map of socket->connection
*/
private Map<Socket,ServerConnectionHandler> socketConnectionMap = new HashMap<Socket,ServerConnectionHandler>();
//the list of active connections /**
List<ServerConnectionHandler> activeConnections = new LinkedList<ServerConnectionHandler>(); * the list of active connections
*/
private List<ServerConnectionHandler> activeConnections = new LinkedList<ServerConnectionHandler>();
//The list of connections to clean up /**
List<ServerConnectionHandler> connectionsToCleanup = new CopyOnWriteArrayList<ServerConnectionHandler>(); * The list of connections to clean up
*/
private List<ServerConnectionHandler> connectionsToCleanup = new CopyOnWriteArrayList<ServerConnectionHandler>();
/** /**
* Inits the server * Inits the server
*/ */
void initServer(){ private void initServer(){
// clientMap = new HashMap<String,ServerConnectionHandler>(); // clientMap = new HashMap<String,ServerConnectionHandler>();
} }
@ -67,7 +81,7 @@ public class Server implements Runnable {
@Override @Override
public void run() { public void run() {
initServer(); this.initServer();
try { try {
serverSocket = new ServerSocket(port); serverSocket = new ServerSocket(port);
//if we set port to 0, java searches for any available port to open //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(); 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 * Gets whether the server is open or not
* @return true if is open, false otherwise * @return true if is open, false otherwise

View File

@ -25,66 +25,112 @@ import java.util.concurrent.TimeUnit;
*/ */
public class ServerConnectionHandler implements Runnable { 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; boolean local = false;
//socket carrier variables /**
* socket carrier variables
*/
Socket socket; Socket socket;
//the streams for the connection //the streams for the connection
// CryptoInputStream inputStream; // CryptoInputStream inputStream;
// CryptoOutputStream outputStream; // CryptoOutputStream outputStream;
/** /**
* The input stream for packets * The input stream for packets
*/ */
InputStream inputStream; InputStream inputStream;
/** /**
* The output stream for packets * The output stream for packets
*/ */
OutputStream outputStream; OutputStream outputStream;
//the network parser for the streams /**
* the network parser for the streams
*/
NetworkParser networkParser; NetworkParser networkParser;
//initialized status /**
* initialized status
*/
boolean initialized; boolean initialized;
//authentication status
/**
* authentication status
*/
boolean isAuthenticated = false; boolean isAuthenticated = false;
//the player id /**
* the player id
*/
int playerID; int playerID;
//the player's entity id
/**
* the player's entity id
*/
int playerEntityID; 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 * Tracks whether this connection is still communicating with the client
*/ */
boolean isConnected = true; boolean isConnected = true;
//the creature template associated with this player /**
* the creature template associated with this player
*/
CreatureTemplate currentCreatureTemplate; CreatureTemplate currentCreatureTemplate;
//the server protocol object associated with this player /**
* the server protocol object associated with this player
*/
MessageProtocol messageProtocol; MessageProtocol messageProtocol;
//thresholds for determining when to send pings and when a client has disconnected /**
static final long SEND_PING_THRESHOLD = 3000; * Keeps track of the last time this connection received a ping from the client
static final long PING_DISCONNECT_THRESHOLD = 60 * 1000; */
//used to keep track of ping/pong messages with client
long lastPingTime = 0; long lastPingTime = 0;
/**
* Keeps track of the last time this connection received a pong from the client
*/
long lastPongTime = 0; long lastPongTime = 0;
//flag to disconnect due to pipe break
/**
* flag to disconnect due to pipe break
*/
boolean socketException = false; boolean socketException = false;
//debug netmonitor stuff /**
* debug netmonitor stuff
*/
String netMonitorHandle; String netMonitorHandle;
//Used to copy messages from network parser to NetMonitor
/**
* Used to copy messages from network parser to NetMonitor
*/
List<NetworkMessage> netMonitorCache = new LinkedList<NetworkMessage>(); List<NetworkMessage> netMonitorCache = new LinkedList<NetworkMessage>();
//the lock used for synchronizing the synchronous message queue /**
* the lock used for synchronizing the synchronous message queue
*/
Semaphore synchronousMessageLock = new Semaphore(1); Semaphore synchronousMessageLock = new Semaphore(1);
//the queue of synchonous network messages
/**
* the queue of synchonous network messages
*/
List<NetworkMessage> synchronousMessageQueue = new CopyOnWriteArrayList<NetworkMessage>(); List<NetworkMessage> synchronousMessageQueue = new CopyOnWriteArrayList<NetworkMessage>();
/** /**
@ -198,7 +244,7 @@ public class ServerConnectionHandler implements Runnable {
// //
//parse messages both incoming and outgoing //parse messages both incoming and outgoing
try { try {
receivedMessageThisLoop = parseMessages(); receivedMessageThisLoop = this.parseMessages();
} catch (SocketException e) { } catch (SocketException e) {
//if we get a SocketException broken pipe (basically the client dc'd without telling us) //if we get a SocketException broken pipe (basically the client dc'd without telling us)
//set flag to disconnect client //set flag to disconnect client
@ -228,7 +274,7 @@ public class ServerConnectionHandler implements Runnable {
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
//basically if we haven't sent a ping in a while, send one //basically if we haven't sent a ping in a while, send one
if(currentTime - lastPingTime > SEND_PING_THRESHOLD){ if(currentTime - lastPingTime > SEND_PING_THRESHOLD){
addMessagetoOutgoingQueue(ServerMessage.constructPingMessage()); this.addMessagetoOutgoingQueue(ServerMessage.constructPingMessage());
lastPingTime = currentTime; lastPingTime = currentTime;
if(lastPongTime == 0){ if(lastPongTime == 0){
lastPongTime = lastPingTime; lastPongTime = lastPingTime;
@ -245,14 +291,14 @@ public class ServerConnectionHandler implements Runnable {
//disconnected from the server //disconnected from the server
LoggerInterface.loggerNetworking.WARNING("Client timeout"); LoggerInterface.loggerNetworking.WARNING("Client timeout");
//run disconnect routine //run disconnect routine
disconnect(); this.disconnect();
break; break;
} }
if(this.socketException == true){ if(this.socketException == true){
//disconnected from the server //disconnected from the server
LoggerInterface.loggerNetworking.WARNING("Client disconnected"); LoggerInterface.loggerNetworking.WARNING("Client disconnected");
//run disconnect routine //run disconnect routine
disconnect(); this.disconnect();
break; break;
} }
} }
@ -266,7 +312,7 @@ public class ServerConnectionHandler implements Runnable {
* @throws SocketException * @throws SocketException
* @return true if connection is alive, false otherwise * @return true if connection is alive, false otherwise
*/ */
boolean parseMessages() throws SocketException, IOException { private boolean parseMessages() throws SocketException, IOException {
boolean rVal = false; boolean rVal = false;
// //
//Read in messages //Read in messages
@ -333,15 +379,27 @@ public class ServerConnectionHandler implements Runnable {
this.messageProtocol.handleSyncMessages(); this.messageProtocol.handleSyncMessages();
} }
/**
* Gets the player's id
* @return The player's id
*/
public int getPlayerId(){ public int getPlayerId(){
return playerID; return playerID;
} }
/**
* Sets the player's entity's id
* @param id
*/
public void setPlayerEntityId(int id){ public void setPlayerEntityId(int id){
LoggerInterface.loggerNetworking.DEBUG("Set player(" + this.playerID + ")'s entity ID to be " + id); LoggerInterface.loggerNetworking.DEBUG("Set player(" + this.playerID + ")'s entity ID to be " + id);
playerEntityID = id; playerEntityID = id;
} }
/**
* Gets the player's entity's id
* @return The id
*/
public int getPlayerEntityId(){ public int getPlayerEntityId(){
return playerEntityID; return playerEntityID;
} }
@ -374,22 +432,14 @@ public class ServerConnectionHandler implements Runnable {
return this.socket; return this.socket;
} }
/**
* Adds a message to the outgoing queue
* @param message The message
*/
public void addMessagetoOutgoingQueue(NetworkMessage message){ public void addMessagetoOutgoingQueue(NetworkMessage message){
networkParser.addOutgoingMessage(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 * Sets the current creature template for the connection
* @param currentCreatureTemplate The new creature template * @param currentCreatureTemplate The new creature template
@ -406,6 +456,9 @@ public class ServerConnectionHandler implements Runnable {
return this.currentCreatureTemplate; return this.currentCreatureTemplate;
} }
/**
* Marks that this connection received a pong message
*/
public void markReceivedPongMessage(){ public void markReceivedPongMessage(){
lastPongTime = System.currentTimeMillis(); lastPongTime = System.currentTimeMillis();
} }
@ -413,8 +466,21 @@ public class ServerConnectionHandler implements Runnable {
/** /**
* Routine to run when the client disconnects * Routine to run when the client disconnects
*/ */
private void disconnect(){ protected void disconnect(){
//close socket //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()){ if(socket != null && socket.isConnected()){
try { try {
socket.close(); socket.close();
@ -422,6 +488,8 @@ public class ServerConnectionHandler implements Runnable {
LoggerInterface.loggerNetworking.ERROR("Error closing socket", e); LoggerInterface.loggerNetworking.ERROR("Error closing socket", e);
} }
} }
this.synchronousMessageLock.release();
this.isConnected = false; this.isConnected = false;
//add connection to server list of connections to cleanup //add connection to server list of connections to cleanup
if(Globals.server != null){ if(Globals.server != null){
@ -440,4 +508,22 @@ public class ServerConnectionHandler implements Runnable {
return this.networkParser.getNumberOfBytesRead(); 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;
}
} }

View File

@ -1,6 +1,5 @@
package electrosphere.net.server.protocol; package electrosphere.net.server.protocol;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@ -24,10 +23,9 @@ import electrosphere.net.parser.net.message.PlayerMessage;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
import electrosphere.net.template.ServerProtocolTemplate; import electrosphere.net.template.ServerProtocolTemplate;
import electrosphere.server.character.CharacterService;
import electrosphere.server.character.PlayerCharacterCreation; import electrosphere.server.character.PlayerCharacterCreation;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.db.DatabaseResult;
import electrosphere.server.db.DatabaseResultRow;
import electrosphere.util.Utilities; import electrosphere.util.Utilities;
/** /**
@ -45,18 +43,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case REQUESTCHARACTERLIST: { case REQUESTCHARACTERLIST: {
Gson gson = new Gson(); Gson gson = new Gson();
List<CharacterDescription> characters = new LinkedList<CharacterDescription>(); List<CharacterDescription> characters = CharacterService.getCharacters(connectionHandler.getPlayer().getDBID());
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);
}
}
ClientCharacterListDTO dto = new ClientCharacterListDTO(); ClientCharacterListDTO dto = new ClientCharacterListDTO();
dto.setCharacters(characters); dto.setCharacters(characters);
connectionHandler.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCharacterListMessage(gson.toJson(dto))); connectionHandler.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCharacterListMessage(gson.toJson(dto)));
@ -74,11 +61,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
case REQUESTCREATECHARACTER: { case REQUESTCREATECHARACTER: {
CreatureTemplate template = Utilities.deserialize(message.getdata(), CreatureTemplate.class); CreatureTemplate template = Utilities.deserialize(message.getdata(), CreatureTemplate.class);
if(template != null){ if(template != null){
Globals.dbController.executePreparedStatement( CharacterService.createCharacter(template, connectionHandler.getPlayer().getDBID());
"INSERT INTO charaData (playerId,dataVal) VALUES (?,?);",
connectionHandler.getPlayer().getDBID(),
new Gson().toJson(template)
);
connectionHandler.setCreatureTemplate(Utilities.deserialize(message.getdata(), CreatureTemplate.class)); connectionHandler.setCreatureTemplate(Utilities.deserialize(message.getdata(), CreatureTemplate.class));
connectionHandler.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCreateCharacterSuccessMessage()); connectionHandler.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCreateCharacterSuccessMessage());
} else { } else {
@ -111,17 +94,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
* @return THe player's entity * @return THe player's entity
*/ */
static Entity spawnEntityForClient(ServerConnectionHandler connectionHandler, int id){ static Entity spawnEntityForClient(ServerConnectionHandler connectionHandler, int id){
if(id != CharacterProtocol.SPAWN_EXISTING_TEMPLATE){ connectionHandler.setCharacterId(id);
DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData WHERE playerId=? AND id=?;",connectionHandler.getPlayer().getDBID(), id);
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);
connectionHandler.setCreatureTemplate(template);
}
}
}
Entity rVal = PlayerCharacterCreation.spawnPlayerCharacter(connectionHandler); Entity rVal = PlayerCharacterCreation.spawnPlayerCharacter(connectionHandler);
Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer()); Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer());
Vector3d spawnPoint = PlayerCharacterCreation.solveSpawnPoint(realm, connectionHandler); Vector3d spawnPoint = PlayerCharacterCreation.solveSpawnPoint(realm, connectionHandler);

View File

@ -9,18 +9,23 @@ import electrosphere.net.template.ServerProtocolTemplate;
*/ */
public class ServerProtocol implements ServerProtocolTemplate<ServerMessage> { public class ServerProtocol implements ServerProtocolTemplate<ServerMessage> {
//the connection handler associated with this protocol object /**
* the connection handler associated with this protocol object
*/
ServerConnectionHandler connectionHandler; ServerConnectionHandler connectionHandler;
@Override @Override
public ServerMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, ServerMessage message) { public ServerMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, ServerMessage message) {
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case PING: case PING: {
connectionHandler.addMessagetoOutgoingQueue(ServerMessage.constructPongMessage()); connectionHandler.addMessagetoOutgoingQueue(ServerMessage.constructPongMessage());
break; } break;
case PONG: case PONG: {
connectionHandler.markReceivedPongMessage(); connectionHandler.markReceivedPongMessage();
} break;
case DISCONNECT:
//silently ignore
break; break;
} }
return null; return null;
@ -29,6 +34,7 @@ public class ServerProtocol implements ServerProtocolTemplate<ServerMessage> {
@Override @Override
public void handleSyncMessage(ServerConnectionHandler connectionHandler, ServerMessage message) { public void handleSyncMessage(ServerConnectionHandler connectionHandler, ServerMessage message) {
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case DISCONNECT:
case PING: case PING:
case PONG: case PONG:
//silently ignore //silently ignore

View File

@ -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<CharacterDescription> getCharacters(int playerId){
DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, dataVal FROM charaData WHERE playerId=?;",playerId);
Gson gson = new Gson();
List<CharacterDescription> rVal = new LinkedList<CharacterDescription>();
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
);
}
}
}

View File

@ -5,12 +5,14 @@ import org.joml.Vector3i;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.state.server.ServerCharacterData;
import electrosphere.entity.state.server.ServerPlayerViewDirTree; import electrosphere.entity.state.server.ServerPlayerViewDirTree;
import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
import electrosphere.net.server.protocol.CharacterProtocol;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
/** /**
@ -28,7 +30,10 @@ public class PlayerCharacterCreation {
// //
//get template //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(); String raceName = template.getCreatureType();
// //
@ -67,21 +72,23 @@ public class PlayerCharacterCreation {
* @param serverConnectionHandler The connection handler * @param serverConnectionHandler The connection handler
*/ */
public static void attachEntityToPlayerObject(Entity entity, Player playerObject, ServerConnectionHandler serverConnectionHandler){ public static void attachEntityToPlayerObject(Entity entity, Player playerObject, ServerConnectionHandler serverConnectionHandler){
int playerCharacterId = entity.getId(); int playerEntityId = entity.getId();
serverConnectionHandler.setPlayerEntityId(playerCharacterId); serverConnectionHandler.setPlayerEntityId(playerEntityId);
CreatureUtils.setControllerPlayerId(entity, serverConnectionHandler.getPlayerId()); CreatureUtils.setControllerPlayerId(entity, serverConnectionHandler.getPlayerId());
Player player = serverConnectionHandler.getPlayer(); Player player = serverConnectionHandler.getPlayer();
player.setPlayerEntity(entity); player.setPlayerEntity(entity);
//custom player btrees //custom player btrees
PlayerCharacterCreation.addPlayerServerBTrees(entity); PlayerCharacterCreation.addPlayerServerBTrees(entity, serverConnectionHandler);
} }
/** /**
* Adds behavior trees that are unique to players a given entity * Adds behavior trees that are unique to players a given entity
* @param entity The entity to add to * @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); ServerPlayerViewDirTree.attachServerPlayerViewDirTree(entity);
ServerCharacterData.attachServerPlayerViewDirTree(entity, serverConnectionHandler.getCharacterId());
} }
/** /**
@ -91,7 +98,10 @@ public class PlayerCharacterCreation {
* @return The spawn point for the player * @return The spawn point for the player
*/ */
public static Vector3d solveSpawnPoint(Realm realm, ServerConnectionHandler connectionHandler){ 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; return spawnPoint;
} }

View File

@ -6,6 +6,8 @@ import org.joml.Vector3i;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.state.server.ServerCharacterData;
import electrosphere.server.character.CharacterService;
import electrosphere.server.content.serialization.ContentSerialization; import electrosphere.server.content.serialization.ContentSerialization;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.datacell.ServerDataCell;
@ -78,10 +80,18 @@ public class ServerContentManager {
* @param entities the collection of entities to save * @param entities the collection of entities to save
*/ */
public void saveContentToDisk(Long locationKey, Collection<Entity> entities){ public void saveContentToDisk(Long locationKey, Collection<Entity> entities){
//serialize all non-character entities
ContentSerialization serialization = ContentSerialization.constructContentSerialization(entities); ContentSerialization serialization = ContentSerialization.constructContentSerialization(entities);
String dirPath = SaveUtils.deriveSaveDirectoryPath(Globals.currentSave.getName()); String dirPath = SaveUtils.deriveSaveDirectoryPath(Globals.currentSave.getName());
String fullPath = dirPath + "/content/" + locationKey + ".dat"; String fullPath = dirPath + "/content/" + locationKey + ".dat";
FileUtils.serializeObjectToFilePath(fullPath, serialization); FileUtils.serializeObjectToFilePath(fullPath, serialization);
//store all character entities in database
for(Entity entity : entities){
if(ServerCharacterData.hasServerPlayerCharacterDataTree(entity)){
CharacterService.saveCharacter(entity);
}
}
} }
/** /**

View File

@ -24,7 +24,9 @@ import electrosphere.util.Utilities;
public class ContentSerialization { public class ContentSerialization {
//The entities, serialized /**
* The entities, serialized
*/
List<EntitySerialization> serializedEntities = new LinkedList<EntitySerialization>(); List<EntitySerialization> serializedEntities = new LinkedList<EntitySerialization>();
/** /**
@ -38,52 +40,55 @@ public class ContentSerialization {
if(!CreatureUtils.hasControllerPlayerId(entity)){ if(!CreatureUtils.hasControllerPlayerId(entity)){
EntityType type = CommonEntityUtils.getEntityType(entity); EntityType type = CommonEntityUtils.getEntityType(entity);
if(type != null){ if(type != null){
switch(type){ EntitySerialization serializedEntity = constructEntitySerialization(entity);
case CREATURE: { rVal.serializedEntities.add(serializedEntity);
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;
}
} }
} }
} }
return rVal; 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 * Actually creates the entities from a content serialization
* @param contentRaw The content serialization * @param contentRaw The content serialization

View File

@ -37,7 +37,6 @@ import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.interfaces.DataCellManager; import electrosphere.server.datacell.interfaces.DataCellManager;
import electrosphere.server.datacell.interfaces.VoxelCellManager; import electrosphere.server.datacell.interfaces.VoxelCellManager;
import electrosphere.server.datacell.physics.PhysicsDataCell; import electrosphere.server.datacell.physics.PhysicsDataCell;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.fluid.manager.ServerFluidChunk; import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.fluid.manager.ServerFluidManager; import electrosphere.server.fluid.manager.ServerFluidManager;
import electrosphere.server.terrain.manager.ServerTerrainManager; import electrosphere.server.terrain.manager.ServerTerrainManager;

View File

@ -13,6 +13,11 @@ import java.util.Properties;
*/ */
public class DatabaseController { public class DatabaseController {
/**
* file extension for allowed database files to open
*/
public static final String FILE_EXT = ".sqlite";
/** /**
* The database connection * The database connection
*/ */
@ -43,6 +48,7 @@ public class DatabaseController {
* Executes a write statement to the database * Executes a write statement to the database
* @param statementRaw The raw string for the statement * @param statementRaw The raw string for the statement
* @param arguments The arguments to be inserted into the raw sql * @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){ public boolean executePreparedStatement(String statementRaw, Object...arguments){
try { try {

View File

@ -90,21 +90,24 @@ public class DatabaseResultIterator implements Iterator<DatabaseResultRow> {
//increment at the beginning because result sets are indexed starting at 1 //increment at the beginning because result sets are indexed starting at 1
columnIncrementer++; columnIncrementer++;
switch(type){ switch(type){
case Types.INTEGER: case Types.INTEGER: {
row.putValue(metadata.getColumnName(columnIncrementer), rs.getInt(columnIncrementer)); row.putValue(metadata.getColumnName(columnIncrementer), rs.getInt(columnIncrementer));
break; } break;
case Types.VARCHAR: case Types.VARCHAR: {
row.putValue(metadata.getColumnName(columnIncrementer), rs.getString(columnIncrementer)); row.putValue(metadata.getColumnName(columnIncrementer), rs.getString(columnIncrementer));
break; } break;
case Types.BIGINT: case Types.BIGINT: {
row.putValue(metadata.getColumnName(columnIncrementer), rs.getLong(columnIncrementer)); row.putValue(metadata.getColumnName(columnIncrementer), rs.getLong(columnIncrementer));
break; } break;
case Types.FLOAT: case Types.FLOAT: {
row.putValue(metadata.getColumnName(columnIncrementer), rs.getFloat(columnIncrementer)); row.putValue(metadata.getColumnName(columnIncrementer), rs.getFloat(columnIncrementer));
break; } break;
case Types.DOUBLE: case Types.DOUBLE: {
row.putValue(metadata.getColumnName(columnIncrementer), rs.getDouble(columnIncrementer)); row.putValue(metadata.getColumnName(columnIncrementer), rs.getDouble(columnIncrementer));
break; } break;
case Types.REAL: {
row.putValue(metadata.getColumnName(columnIncrementer), rs.getDouble(columnIncrementer));
} break;
default: default:
LoggerInterface.loggerEngine.WARNING("Unsupported type from database in DatabaseResultIterator " + type); LoggerInterface.loggerEngine.WARNING("Unsupported type from database in DatabaseResultIterator " + type);
break; break;

View File

@ -3,11 +3,14 @@ package electrosphere.server.db;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/**
* A row result from a database operation
*/
public class DatabaseResultRow { 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<String,Object> values = new HashMap<String,Object>(); Map<String,Object> values = new HashMap<String,Object>();
/** /**

View File

@ -6,38 +6,40 @@ import electrosphere.util.FileUtils;
import java.io.IOException; import java.io.IOException;
/** /**
* * Utilities for working with the database
* @author satellite
*/ */
public class DatabaseUtils { 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){ public static boolean initCentralDBFile(String path){
String sanitizedPath = FileUtils.sanitizeFilePath(path); String sanitizedPath = FileUtils.sanitizeFilePath(path);
if(!FileUtils.checkFileExists(sanitizedPath)){ if(!FileUtils.checkFileExists(sanitizedPath)){
return false; return false;
} }
String dbFilePath = sanitizedPath + "/central.db"; String dbFilePath = sanitizedPath + "/central" + DatabaseController.FILE_EXT;
if(Globals.dbController == null){ if(Globals.dbController == null){
Globals.dbController = new DatabaseController(); Globals.dbController = new DatabaseController();
} }
if(!Globals.dbController.isConnected()){ if(!Globals.dbController.isConnected()){
Globals.dbController.connect(dbFilePath); 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 //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(); Globals.dbController.disconnect();
return true; 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){ public static boolean runScript(DatabaseController controller, String scriptPath){
String rawScript = ""; String rawScript = "";
try { try {

View File

@ -8,6 +8,7 @@ import electrosphere.entity.scene.SceneFile;
import electrosphere.entity.scene.SceneLoader; import electrosphere.entity.scene.SceneLoader;
import electrosphere.game.server.world.ServerWorldData; import electrosphere.game.server.world.ServerWorldData;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.server.db.DatabaseController;
import electrosphere.server.db.DatabaseUtils; import electrosphere.server.db.DatabaseUtils;
import electrosphere.server.fluid.generation.DefaultFluidGenerator; import electrosphere.server.fluid.generation.DefaultFluidGenerator;
import electrosphere.server.fluid.manager.ServerFluidManager; import electrosphere.server.fluid.manager.ServerFluidManager;
@ -155,6 +156,9 @@ public class SaveUtils {
//write server structures //write server structures
Globals.realmManager.save(saveName); Globals.realmManager.save(saveName);
//close the server
Globals.server.close();
} }
/** /**
@ -211,7 +215,7 @@ public class SaveUtils {
} }
//load db //load db
String dbFilePath = FileUtils.sanitizeFilePath(dirPath) + "/central.db"; String dbFilePath = FileUtils.sanitizeFilePath(dirPath) + "/central" + DatabaseController.FILE_EXT;
Globals.dbController.connect(dbFilePath); Globals.dbController.connect(dbFilePath);
return true; return true;

View File

@ -17,6 +17,11 @@
"messageName" : "Pong", "messageName" : "Pong",
"description" : "Replies to a ping from the other side of the socket", "description" : "Replies to a ping from the other side of the socket",
"data" : [] "data" : []
},
{
"messageName" : "Disconnect",
"description" : "Tell the other side of the socket that this side is disconnecting",
"data" : []
} }
] ]
}, },