character database work
Some checks reported errors
studiorailgun/Renderer/pipeline/head Something is wrong with the build of this commit

This commit is contained in:
austin 2024-11-29 20:47:52 -05:00
parent 460989eeab
commit 080cdeca33
23 changed files with 400 additions and 49 deletions

View File

@ -6,5 +6,5 @@ CREATE INDEX charaWorldPositionsIDIndex ON charaWorldPositions (id);
CREATE INDEX charaWorldPositionsPosIndex ON charaWorldPositions (posX, posY); CREATE INDEX charaWorldPositionsPosIndex ON charaWorldPositions (posX, posY);
--data --data
CREATE TABLE charaData (playerId INTEGER PRIMARY KEY, id INTEGER, dataVal VARCHAR); CREATE TABLE charaData (id INTEGER PRIMARY KEY AUTOINCREMENT, playerId INTEGER, dataVal VARCHAR);
CREATE INDEX charaDataIDIndex ON charaData (id); CREATE INDEX charaDataIDIndex ON charaData (id);

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Mon Nov 25 18:13:49 EST 2024 #Fri Nov 29 20:08:47 EST 2024
buildNumber=405 buildNumber=406

View File

@ -1184,6 +1184,8 @@ Actor panel additional functionality
Better style for character creation menu Better style for character creation menu
Fix AABB calculation from assimp-loaded models Fix AABB calculation from assimp-loaded models
Fix singleplayer launching at all Fix singleplayer launching at all
Store characters in database
Spawn characters from database
# TODO # TODO

View File

@ -20,6 +20,16 @@ public class AuthenticationManager {
*/ */
boolean isMock = false; boolean isMock = false;
/**
* An invalid login
*/
public static final int INVALID_LOGIN = -1;
/**
* ID for a mock login
*/
public static final int MOCK_LOGIN = 0;
/** /**
* Private constructor * Private constructor
*/ */
@ -38,9 +48,15 @@ public class AuthenticationManager {
return rVal; return rVal;
} }
public boolean authenticate(String username, String password){ /**
* Authenticates the player
* @param username The username of the player
* @param password The password of the player
* @return The id of the player if they logged in successfully, INVALID_LOGIN if the authentication failed
*/
public int authenticate(String username, String password){
if(isMock){ if(isMock){
return true; return MOCK_LOGIN;
} }
//first we hash the input password //first we hash the input password
String hashedPassword = getHashedString(password); String hashedPassword = getHashedString(password);
@ -54,19 +70,32 @@ public class AuthenticationManager {
String pwdhash = row.getAsString("pwdhash"); String pwdhash = row.getAsString("pwdhash");
if(pwdhash.equals(hashedPassword)){ if(pwdhash.equals(hashedPassword)){
LoggerInterface.loggerAuth.INFO("Authenticated user " + username); LoggerInterface.loggerAuth.INFO("Authenticated user " + username);
return true; return row.getAsInteger("id");
} }
} }
//If we didn't find a single account, go ahead and create it //If we didn't find a single account, go ahead and create it
if(!foundRow){ if(!foundRow){
LoggerInterface.loggerAuth.INFO("Created user " + username); LoggerInterface.loggerAuth.INFO("Created user " + username);
Globals.dbController.executePreparedStatement("INSERT INTO accounts (username, pwdhash) VALUES(?, ?);",username,hashedPassword); Globals.dbController.executePreparedStatement("INSERT INTO accounts (username, pwdhash) VALUES(?, ?);",username,hashedPassword);
//TODO: verify we created the account
return true; //verify the account was created
result = Globals.dbController.executePreparedQuery("SELECT id, username, pwdhash FROM accounts WHERE username=?;",username);
if(result.hasResult()){
foundRow = false;
//if we get a valid response from the database, check that it actually matches hashes
for(DatabaseResultRow row : result){
foundRow = true;
String pwdhash = row.getAsString("pwdhash");
if(pwdhash.equals(hashedPassword)){
LoggerInterface.loggerAuth.INFO("Authenticated user " + username);
return row.getAsInteger("id");
}
}
}
} }
} }
LoggerInterface.loggerAuth.INFO("Failed to authenticate user " + username); LoggerInterface.loggerAuth.INFO("Failed to authenticate user " + username);
return false; return INVALID_LOGIN;
} }
static final int saltLength = 16; static final int saltLength = 16;

View File

@ -0,0 +1,53 @@
package electrosphere.client.entity.character;
import electrosphere.entity.types.creature.CreatureTemplate;
/**
* Describes a character
*/
public class CharacterDescription {
/**
* The id of the character
*/
String id;
/**
* The character's template data
*/
CreatureTemplate template;
/**
* Gets the id of the character
* @return The id
*/
public String getId() {
return id;
}
/**
* Sets the id of the character
* @param id The id
*/
public void setId(String id) {
this.id = id;
}
/**
* Gets the creature template for the character
* @return The creature template
*/
public CreatureTemplate getTemplate() {
return template;
}
/**
* Sets the creature template for the character
* @param template The creature template
*/
public void setTemplate(CreatureTemplate template) {
this.template = template;
}
}

View File

@ -0,0 +1,31 @@
package electrosphere.client.entity.character;
import java.util.List;
/**
* DTO for sending available characters to the client
*/
public class ClientCharacterListDTO {
/**
* The list of characters stored in the DTO
*/
List<CharacterDescription> characters;
/**
* Gets the list of characters
* @return The list of characters
*/
public List<CharacterDescription> getCharacters() {
return characters;
}
/**
* Sets the list of characters
* @param characters The list of characters
*/
public void setCharacters(List<CharacterDescription> characters) {
this.characters = characters;
}
}

View File

@ -0,0 +1,30 @@
package electrosphere.client.entity.character;
/**
* Tracks the characers available to the client
*/
public class ClientCharacterManager {
/**
* The list of characters available
*/
ClientCharacterListDTO characterList;
/**
* Gets the character list
* @return The character list
*/
public ClientCharacterListDTO getCharacterList() {
return characterList;
}
/**
* Sets the character list
* @param characterList The character list
*/
public void setCharacterList(ClientCharacterListDTO characterList) {
this.characterList = characterList;
}
}

View File

@ -1,14 +1,20 @@
package electrosphere.client.ui.menu.mainmenu; package electrosphere.client.ui.menu.mainmenu;
import electrosphere.client.entity.character.CharacterDescription;
import electrosphere.client.ui.components.CharacterCustomizer; import electrosphere.client.ui.components.CharacterCustomizer;
import electrosphere.client.ui.menu.WindowUtils; import electrosphere.client.ui.menu.WindowUtils;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.loadingthreads.LoadingThread;
import electrosphere.engine.loadingthreads.LoadingThread.LoadingThreadType;
import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.net.parser.net.message.CharacterMessage; import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.renderer.ui.elements.Button; import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.FormElement; import electrosphere.renderer.ui.elements.FormElement;
import electrosphere.renderer.ui.elements.StringCarousel; import electrosphere.renderer.ui.elements.StringCarousel;
import electrosphere.renderer.ui.elementtypes.Element; import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
import electrosphere.renderer.ui.events.ValueChangeEvent; import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.util.Utilities; import electrosphere.util.Utilities;
@ -29,11 +35,35 @@ public class MenuCharacterCreation {
public static Element createCharacterSelectionWindow(){ public static Element createCharacterSelectionWindow(){
FormElement rVal = new FormElement(); FormElement rVal = new FormElement();
//the list of characters
Div selectContainer = Div.createCol();
if(Globals.clientCharacterManager.getCharacterList() != null){
for(CharacterDescription description : Globals.clientCharacterManager.getCharacterList().getCharacters()){
String buttonTitle = "Character " + description.getId();
Div charNameContainer = Div.createRow(Button.createButton(buttonTitle, () -> {
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage(description.getId()));
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage());
Globals.threadManager.start(new LoadingThread(LoadingThreadType.CLIENT_WORLD));
}));
selectContainer.addChild(charNameContainer);
}
}
//button (create) //button (create)
rVal.addChild(Button.createButton("Create Character", () -> { Div createContainer = Div.createDiv();
createContainer.addChild(Button.createButton("Create Character", () -> {
WindowUtils.replaceMainMenuContents(MenuCharacterCreation.createRaceSelectionMenu()); WindowUtils.replaceMainMenuContents(MenuCharacterCreation.createRaceSelectionMenu());
})); }));
//main layout
Div mainLayout = Div.createCol(
selectContainer,
createContainer
);
mainLayout.setAlignItems(YogaAlignment.Center);
rVal.addChild(mainLayout);
return rVal; return rVal;
} }

View File

@ -17,6 +17,7 @@ import electrosphere.client.block.ClientBlockManager;
import electrosphere.client.block.cells.BlockTextureAtlas; import electrosphere.client.block.cells.BlockTextureAtlas;
import electrosphere.client.block.cells.ClientBlockCellManager; import electrosphere.client.block.cells.ClientBlockCellManager;
import electrosphere.client.chemistry.ClientChemistryCollisionCallback; import electrosphere.client.chemistry.ClientChemistryCollisionCallback;
import electrosphere.client.entity.character.ClientCharacterManager;
import electrosphere.client.entity.particle.ParticleService; import electrosphere.client.entity.particle.ParticleService;
import electrosphere.client.fluid.cells.FluidCellManager; import electrosphere.client.fluid.cells.FluidCellManager;
import electrosphere.client.fluid.manager.ClientFluidManager; import electrosphere.client.fluid.manager.ClientFluidManager;
@ -352,6 +353,7 @@ public class Globals {
//client world data //client world data
public static ClientWorldData clientWorldData; public static ClientWorldData clientWorldData;
public static ClientCharacterManager clientCharacterManager = new ClientCharacterManager();
//client gridded manager //client gridded manager
public static ClientTerrainManager clientTerrainManager; public static ClientTerrainManager clientTerrainManager;
@ -703,6 +705,7 @@ public class Globals {
if(Globals.realmManager != null){ if(Globals.realmManager != null){
Globals.realmManager.reset(); Globals.realmManager.reset();
} }
Globals.dbController.disconnect();
} }
/** /**

View File

@ -102,6 +102,7 @@ public class ClientLoading {
Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT); Globals.controlHandler.hintUpdateControlState(ControlHandler.ControlsState.NO_INPUT);
//initialize the "real" objects simulation //initialize the "real" objects simulation
initClientSimulation(); initClientSimulation();
LoadingUtils.setSimulationsToReady();
//initialize the gridded managers (client) //initialize the gridded managers (client)
initDrawCellManager(true); initDrawCellManager(true);
initFoliageManager(); initFoliageManager();

View File

@ -24,6 +24,7 @@ import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.server.Server; import electrosphere.net.server.Server;
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;
import electrosphere.server.simulation.MicroSimulation; import electrosphere.server.simulation.MicroSimulation;
@ -167,7 +168,7 @@ public class LoadingUtils {
template.getCreatureToolbarData().setSlotItem("2", new ToolbarItem(73, "entityinspector")); template.getCreatureToolbarData().setSlotItem("2", new ToolbarItem(73, "entityinspector"));
//set player character template //set player character template
serverPlayerConnection.setCreatureTemplate(template); serverPlayerConnection.setCreatureTemplate(template);
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage()); Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage(CharacterProtocol.SPAWN_EXISTING_TEMPLATE + ""));
//set player world-space coordinates //set player world-space coordinates
Player playerObject = Globals.playerManager.getFirstPlayer(); Player playerObject = Globals.playerManager.getFirstPlayer();

View File

@ -219,7 +219,7 @@ public class ClientNetworking 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){
queueOutgoingMessage(ServerMessage.constructPingMessage()); this.queueOutgoingMessage(ServerMessage.constructPingMessage());
lastPingTime = currentTime; lastPingTime = currentTime;
if(lastPongTime == 0){ if(lastPongTime == 0){
lastPongTime = lastPingTime; lastPongTime = lastPingTime;

View File

@ -3,6 +3,7 @@ package electrosphere.net.client.protocol;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.parser.net.message.LoreMessage; import electrosphere.net.parser.net.message.LoreMessage;
import electrosphere.net.template.ClientProtocolTemplate; import electrosphere.net.template.ClientProtocolTemplate;
@ -24,6 +25,8 @@ public class AuthProtocol implements ClientProtocolTemplate<AuthMessage> {
Globals.clientPassword = ""; Globals.clientPassword = "";
//request playable races //request playable races
Globals.clientConnection.queueOutgoingMessage(LoreMessage.constructRequestRacesMessage()); Globals.clientConnection.queueOutgoingMessage(LoreMessage.constructRequestRacesMessage());
//request characters available to this player
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestCharacterListMessage());
//log that we succeeded //log that we succeeded
LoggerInterface.loggerAuth.INFO("Successfully logged in"); LoggerInterface.loggerAuth.INFO("Successfully logged in");
break; break;

View File

@ -1,8 +1,14 @@
package electrosphere.net.client.protocol; package electrosphere.net.client.protocol;
import com.google.gson.Gson;
import electrosphere.client.entity.character.ClientCharacterListDTO;
import electrosphere.client.ui.menu.WindowUtils;
import electrosphere.client.ui.menu.mainmenu.MenuCharacterCreation;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.loadingthreads.LoadingThread; import electrosphere.engine.loadingthreads.LoadingThread;
import electrosphere.engine.loadingthreads.LoadingThread.LoadingThreadType; import electrosphere.engine.loadingthreads.LoadingThread.LoadingThreadType;
import electrosphere.engine.signal.Signal.SignalType;
import electrosphere.net.parser.net.message.CharacterMessage; import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.template.ClientProtocolTemplate; import electrosphere.net.template.ClientProtocolTemplate;
@ -20,17 +26,22 @@ public class CharacterProtocol implements ClientProtocolTemplate<CharacterMessag
@Override @Override
public void handleSyncMessage(CharacterMessage message) { public void handleSyncMessage(CharacterMessage message) {
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case RESPONSECREATECHARACTERSUCCESS: case RESPONSECREATECHARACTERSUCCESS: {
//trigger request to spawn character //trigger request to spawn character if the character list is undefined (ie if special loading case)
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage()); Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage(electrosphere.net.server.protocol.CharacterProtocol.SPAWN_EXISTING_TEMPLATE + ""));
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage()); Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage());
LoadingThread clientThread = new LoadingThread(LoadingThreadType.CLIENT_WORLD); LoadingThread clientThread = new LoadingThread(LoadingThreadType.CLIENT_WORLD);
Globals.threadManager.start(clientThread); Globals.threadManager.start(clientThread);
break; } break;
case RESPONSECHARACTERLIST: {
Globals.clientCharacterManager.setCharacterList(new Gson().fromJson(message.getdata(), ClientCharacterListDTO.class));
Globals.signalSystem.post(SignalType.UI_MODIFICATION,() -> {
WindowUtils.replaceMainMenuContents(MenuCharacterCreation.createCharacterSelectionWindow());
});
} break;
case REQUESTCHARACTERLIST: case REQUESTCHARACTERLIST:
case REQUESTCREATECHARACTER: case REQUESTCREATECHARACTER:
case REQUESTSPAWNCHARACTER: case REQUESTSPAWNCHARACTER:
case RESPONSECHARACTERLIST:
case RESPONSECREATECHARACTERFAILURE: case RESPONSECREATECHARACTERFAILURE:
case RESPONSESPAWNCHARACTER: case RESPONSESPAWNCHARACTER:
case EDITORSWAP: case EDITORSWAP:

View File

@ -23,7 +23,7 @@ public class PlayerProtocol implements ClientProtocolTemplate<PlayerMessage> {
Globals.profiler.beginCpuSample("PlayerProtocol.handlePlayerMessage"); Globals.profiler.beginCpuSample("PlayerProtocol.handlePlayerMessage");
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case SET_ID: case SET_ID:
Globals.clientPlayer = new Player(message.getplayerID()); Globals.clientPlayer = new Player(message.getplayerID(), Player.CLIENT_DB_ID);
LoggerInterface.loggerNetworking.DEBUG("[CLIENT] Player ID is " + Globals.clientPlayer.getId()); LoggerInterface.loggerNetworking.DEBUG("[CLIENT] Player ID is " + Globals.clientPlayer.getId());
break; break;
case SETINITIALDISCRETEPOSITION: case SETINITIALDISCRETEPOSITION:

View File

@ -93,11 +93,7 @@ public class CharacterMessage extends NetworkMessage {
return false; return false;
} }
case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER: case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER:
if(byteBuffer.getRemaining() >= TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER_SIZE){ return CharacterMessage.canParseRequestSpawnCharacterMessage(byteBuffer);
return true;
} else {
return false;
}
case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSESPAWNCHARACTER: case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSESPAWNCHARACTER:
return CharacterMessage.canParseResponseSpawnCharacterMessage(byteBuffer); return CharacterMessage.canParseResponseSpawnCharacterMessage(byteBuffer);
case TypeBytes.CHARACTER_MESSAGE_TYPE_EDITORSWAP: case TypeBytes.CHARACTER_MESSAGE_TYPE_EDITORSWAP:
@ -248,20 +244,44 @@ public class CharacterMessage extends NetworkMessage {
return rVal; return rVal;
} }
/**
* Checks if a message of type RequestSpawnCharacter can be parsed from the byte stream
*/
public static boolean canParseRequestSpawnCharacterMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList<Byte>();
int dataSize = 0;
if(currentStreamLength < 6){
return false;
} else {
temporaryByteQueue.add(byteBuffer.peek(2 + 0));
temporaryByteQueue.add(byteBuffer.peek(2 + 1));
temporaryByteQueue.add(byteBuffer.peek(2 + 2));
temporaryByteQueue.add(byteBuffer.peek(2 + 3));
dataSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
}
if(currentStreamLength < 6 + dataSize){
return false;
}
return true;
}
/** /**
* Parses a message of type RequestSpawnCharacter * Parses a message of type RequestSpawnCharacter
*/ */
public static CharacterMessage parseRequestSpawnCharacterMessage(CircularByteBuffer byteBuffer){ public static CharacterMessage parseRequestSpawnCharacterMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
stripPacketHeader(byteBuffer); stripPacketHeader(byteBuffer);
rVal.setdata(ByteStreamUtils.popStringFromByteQueue(byteBuffer));
return rVal; return rVal;
} }
/** /**
* Constructs a message of type RequestSpawnCharacter * Constructs a message of type RequestSpawnCharacter
*/ */
public static CharacterMessage constructRequestSpawnCharacterMessage(){ public static CharacterMessage constructRequestSpawnCharacterMessage(String data){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER); CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
rVal.setdata(data);
rVal.serialize(); rVal.serialize();
return rVal; return rVal;
} }
@ -383,11 +403,19 @@ public class CharacterMessage extends NetworkMessage {
rawBytes[1] = TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE; rawBytes[1] = TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE;
break; break;
case REQUESTSPAWNCHARACTER: case REQUESTSPAWNCHARACTER:
rawBytes = new byte[2]; rawBytes = new byte[2+4+data.length()];
//message header //message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_CHARACTER; rawBytes[0] = TypeBytes.MESSAGE_TYPE_CHARACTER;
//entity messaage header //entity messaage header
rawBytes[1] = TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER; rawBytes[1] = TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER;
intValues = ByteStreamUtils.serializeIntToBytes(data.length());
for(int i = 0; i < 4; i++){
rawBytes[2+i] = intValues[i];
}
stringBytes = data.getBytes();
for(int i = 0; i < data.length(); i++){
rawBytes[6+i] = stringBytes[i];
}
break; break;
case RESPONSESPAWNCHARACTER: case RESPONSESPAWNCHARACTER:
rawBytes = new byte[2+4+data.length()]; rawBytes = new byte[2+4+data.length()];

View File

@ -137,7 +137,6 @@ public class TypeBytes {
public static final byte CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_REQUESTCHARACTERLIST_SIZE = 2;
public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERSUCCESS_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERSUCCESS_SIZE = 2;
public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE_SIZE = 2;
public static final byte CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER_SIZE = 2;
public static final byte CHARACTER_MESSAGE_TYPE_EDITORSWAP_SIZE = 2; public static final byte CHARACTER_MESSAGE_TYPE_EDITORSWAP_SIZE = 2;
/* /*

View File

@ -21,6 +21,11 @@ public class Player {
*/ */
public static final int DEFAULT_SIMULATION_RADIUS = 3; public static final int DEFAULT_SIMULATION_RADIUS = 3;
/**
* DBID the client assigns to the player object
*/
public static final int CLIENT_DB_ID = 0;
/** /**
* Id incrementer lock * Id incrementer lock
*/ */
@ -41,6 +46,11 @@ public class Player {
*/ */
int id; int id;
/**
* The database's id of the player
*/
int dbId;
/** /**
* The world position of this player * The world position of this player
*/ */
@ -64,10 +74,12 @@ public class Player {
/** /**
* Constructor * Constructor
* @param connectionHandler The corresponding connection * @param connectionHandler The corresponding connection
* @param dbId The database's id of the player
*/ */
public Player(ServerConnectionHandler connectionHandler){ public Player(ServerConnectionHandler connectionHandler, int dbId){
this.connectionHandler = connectionHandler; this.connectionHandler = connectionHandler;
id = connectionHandler.getPlayerId(); id = connectionHandler.getPlayerId();
this.dbId = dbId;
this.simulationRadius = Globals.userSettings.getGameplayPhysicsCellRadius(); this.simulationRadius = Globals.userSettings.getGameplayPhysicsCellRadius();
} }
@ -75,8 +87,9 @@ public class Player {
* Used when initing a local connection * Used when initing a local connection
* @param id The id of the local connection * @param id The id of the local connection
*/ */
public Player(int id){ public Player(int id, int dbId){
this.id = id; this.id = id;
this.dbId = dbId;
} }
/** /**
@ -182,6 +195,14 @@ public class Player {
this.hasSentPlayerEntity = hasSentPlayerEntity; this.hasSentPlayerEntity = hasSentPlayerEntity;
} }
/**
* Gets the database's id for the player
* @return The database's id for the player
*/
public int getDBID(){
return this.dbId;
}

View File

@ -1,5 +1,6 @@
package electrosphere.net.server.protocol; package electrosphere.net.server.protocol;
import electrosphere.auth.AuthenticationManager;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.PlayerMessage; import electrosphere.net.parser.net.message.PlayerMessage;
@ -17,11 +18,11 @@ public class AuthProtocol implements ServerProtocolTemplate<AuthMessage> {
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case AUTHDETAILS: case AUTHDETAILS:
//auth check //auth check
boolean successfulLogin = Globals.authenticationManager.authenticate(message.getuser(), message.getpass()); int loginId = Globals.authenticationManager.authenticate(message.getuser(), message.getpass());
if(successfulLogin){ if(loginId != AuthenticationManager.INVALID_LOGIN){
//TODO: actually set connection/protocol to authenticated //TODO: actually set connection/protocol to authenticated
connectionHandler.addMessagetoOutgoingQueue(AuthMessage.constructAuthSuccessMessage()); connectionHandler.addMessagetoOutgoingQueue(AuthMessage.constructAuthSuccessMessage());
Player newPlayer = new Player(connectionHandler); Player newPlayer = new Player(connectionHandler, loginId);
Globals.playerManager.registerPlayer(newPlayer); Globals.playerManager.registerPlayer(newPlayer);
//there is a race condition here where if a local non-server client connects first then it breaks //there is a race condition here where if a local non-server client connects first then it breaks
if(connectionHandler.getIPAddress().contains("127.0.0.1") && Globals.RUN_CLIENT == true && Globals.clientPlayer == null){ if(connectionHandler.getIPAddress().contains("127.0.0.1") && Globals.RUN_CLIENT == true && Globals.clientPlayer == null){

View File

@ -1,10 +1,15 @@
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;
import org.joml.Vector3d; import org.joml.Vector3d;
import com.google.gson.Gson;
import electrosphere.client.entity.character.CharacterDescription;
import electrosphere.client.entity.character.ClientCharacterListDTO;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.loadingthreads.LoadingUtils; import electrosphere.engine.loadingthreads.LoadingUtils;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
@ -21,6 +26,8 @@ import electrosphere.net.server.player.Player;
import electrosphere.net.template.ServerProtocolTemplate; import electrosphere.net.template.ServerProtocolTemplate;
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;
/** /**
@ -28,20 +35,50 @@ import electrosphere.util.Utilities;
*/ */
public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessage> { public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessage> {
/**
* Should spawn the existing template
*/
public static final int SPAWN_EXISTING_TEMPLATE = -1;
@Override @Override
public CharacterMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, CharacterMessage message) { public CharacterMessage handleAsyncMessage(ServerConnectionHandler connectionHandler, CharacterMessage message) {
switch(message.getMessageSubtype()){
case REQUESTCHARACTERLIST: {
Gson gson = new Gson();
List<CharacterDescription> characters = new LinkedList<CharacterDescription>();
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();
dto.setCharacters(characters);
connectionHandler.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCharacterListMessage(gson.toJson(dto)));
return null;
}
default: {
} break;
}
return message; return message;
} }
@Override @Override
public void handleSyncMessage(ServerConnectionHandler connectionHandler, CharacterMessage message) { public void handleSyncMessage(ServerConnectionHandler connectionHandler, CharacterMessage message) {
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case REQUESTCHARACTERLIST:
//TODO
break;
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(
"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 {
@ -49,11 +86,15 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
} }
} break; } break;
case REQUESTSPAWNCHARACTER: { case REQUESTSPAWNCHARACTER: {
CharacterProtocol.spawnEntityForClient(connectionHandler); int charaId = Integer.parseInt(message.getdata());
CharacterProtocol.spawnEntityForClient(connectionHandler, charaId);
} break; } break;
case EDITORSWAP: { case EDITORSWAP: {
CharacterProtocol.swapPlayerCharacter(connectionHandler); CharacterProtocol.swapPlayerCharacter(connectionHandler);
} break; } break;
case REQUESTCHARACTERLIST:
//handled async
break;
case RESPONSECHARACTERLIST: case RESPONSECHARACTERLIST:
case RESPONSECREATECHARACTERSUCCESS: case RESPONSECREATECHARACTERSUCCESS:
case RESPONSECREATECHARACTERFAILURE: case RESPONSECREATECHARACTERFAILURE:
@ -68,7 +109,18 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
* @param connectionHandler The connection handler for the player * @param connectionHandler The connection handler for the player
* @return THe player's entity * @return THe player's entity
*/ */
static Entity spawnEntityForClient(ServerConnectionHandler connectionHandler){ static Entity spawnEntityForClient(ServerConnectionHandler connectionHandler, int id){
if(id != CharacterProtocol.SPAWN_EXISTING_TEMPLATE){
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 = realm.getSpawnPoint(); Vector3d spawnPoint = realm.getSpawnPoint();
@ -147,7 +199,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
//spawn the new one //spawn the new one
player.setHasSentPlayerEntity(false); player.setHasSentPlayerEntity(false);
Entity newEntity = CharacterProtocol.spawnEntityForClient(connectionHandler); Entity newEntity = CharacterProtocol.spawnEntityForClient(connectionHandler, CharacterProtocol.SPAWN_EXISTING_TEMPLATE);
ServerEntityUtils.repositionEntity(newEntity, position); ServerEntityUtils.repositionEntity(newEntity, position);
return newEntity; return newEntity;
} }

View File

@ -10,19 +10,53 @@ import java.sql.SQLException;
*/ */
public class DatabaseResult implements Iterable<DatabaseResultRow> { public class DatabaseResult implements Iterable<DatabaseResultRow> {
boolean isQuery = false; /**
boolean isStatement = false; * The deserializer for the data
boolean succeeded = false; */
boolean hasResultSet = false;
String code;
ResultSet rs;
private static Gson deserializer; private static Gson deserializer;
/**
* Init deserializer
*/
static { static {
deserializer = new Gson(); deserializer = new Gson();
} }
/**
* True if this was a query
*/
boolean isQuery = false;
/**
* True if this was a statement
*/
boolean isStatement = false;
/**
* True if the request succeeded, false otherwise
*/
boolean succeeded = false;
/**
* True if the result has data, false otherwise
*/
boolean hasResultSet = false;
/**
* The code for the query
*/
String code;
/**
* The raw result data
*/
ResultSet rs;
/**
* Creates a query
* @param code The code for the query
* @return The results of the query
*/
protected static DatabaseResult createQuery(String code){ protected static DatabaseResult createQuery(String code){
DatabaseResult rVal = new DatabaseResult(); DatabaseResult rVal = new DatabaseResult();
rVal.isQuery = true; rVal.isQuery = true;
@ -30,6 +64,11 @@ public class DatabaseResult implements Iterable<DatabaseResultRow> {
return rVal; return rVal;
} }
/**
* Creates a statement
* @param code The code for the statement
* @return The results of the statement
*/
protected static DatabaseResult createStatement(String code){ protected static DatabaseResult createStatement(String code){
DatabaseResult rVal = new DatabaseResult(); DatabaseResult rVal = new DatabaseResult();
rVal.isStatement = true; rVal.isStatement = true;
@ -72,10 +111,6 @@ public class DatabaseResult implements Iterable<DatabaseResultRow> {
} }
return rVal; return rVal;
} }
public void logRawResult(){
}
@Override @Override
public DatabaseResultIterator iterator() { public DatabaseResultIterator iterator() {

View File

@ -10,10 +10,24 @@ import java.util.List;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
/**
* Iterates through a database result
*/
public class DatabaseResultIterator implements Iterator<DatabaseResultRow> { public class DatabaseResultIterator implements Iterator<DatabaseResultRow> {
/**
* The result set
*/
ResultSet rs; ResultSet rs;
/**
* The metadata of the result set
*/
ResultSetMetaData metadata; ResultSetMetaData metadata;
/**
* The type list of each column
*/
List<Integer> typeList; List<Integer> typeList;
/** /**
@ -34,6 +48,11 @@ public class DatabaseResultIterator implements Iterator<DatabaseResultRow> {
//if it did, we'd call this to be explicitly clear where we want to start //if it did, we'd call this to be explicitly clear where we want to start
//instead the assumption is it always starts ON the first element //instead the assumption is it always starts ON the first element
// this.rs.first(); // this.rs.first();
//starts before the first, so must iterate once into the result set in order to not double-sample the first row
if(this.rs.isBeforeFirst()){
this.rs.next();
}
} catch (SQLException e) { } catch (SQLException e) {
LoggerInterface.loggerEngine.ERROR("SQL Exception", e); LoggerInterface.loggerEngine.ERROR("SQL Exception", e);
} }

View File

@ -43,7 +43,9 @@
{ {
"messageName" : "RequestSpawnCharacter", "messageName" : "RequestSpawnCharacter",
"description" : "Requests that the server spawn the client in as a given character", "description" : "Requests that the server spawn the client in as a given character",
"data" : [] "data" : [
"data"
]
}, },
{ {
"messageName" : "ResponseSpawnCharacter", "messageName" : "ResponseSpawnCharacter",