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

View File

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

View File

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

View File

@ -20,6 +20,16 @@ public class AuthenticationManager {
*/
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
*/
@ -38,9 +48,15 @@ public class AuthenticationManager {
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){
return true;
return MOCK_LOGIN;
}
//first we hash the input password
String hashedPassword = getHashedString(password);
@ -54,19 +70,32 @@ public class AuthenticationManager {
String pwdhash = row.getAsString("pwdhash");
if(pwdhash.equals(hashedPassword)){
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(!foundRow){
LoggerInterface.loggerAuth.INFO("Created user " + username);
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);
return false;
return INVALID_LOGIN;
}
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;
import electrosphere.client.entity.character.CharacterDescription;
import electrosphere.client.ui.components.CharacterCustomizer;
import electrosphere.client.ui.menu.WindowUtils;
import electrosphere.engine.Globals;
import electrosphere.engine.loadingthreads.LoadingThread;
import electrosphere.engine.loadingthreads.LoadingThread.LoadingThreadType;
import electrosphere.entity.types.creature.CreatureTemplate;
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.Div;
import electrosphere.renderer.ui.elements.FormElement;
import electrosphere.renderer.ui.elements.StringCarousel;
import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.util.Utilities;
@ -29,11 +35,35 @@ public class MenuCharacterCreation {
public static Element createCharacterSelectionWindow(){
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)
rVal.addChild(Button.createButton("Create Character", () -> {
Div createContainer = Div.createDiv();
createContainer.addChild(Button.createButton("Create Character", () -> {
WindowUtils.replaceMainMenuContents(MenuCharacterCreation.createRaceSelectionMenu());
}));
//main layout
Div mainLayout = Div.createCol(
selectContainer,
createContainer
);
mainLayout.setAlignItems(YogaAlignment.Center);
rVal.addChild(mainLayout);
return rVal;
}

View File

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

View File

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

View File

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

View File

@ -219,7 +219,7 @@ public class ClientNetworking implements Runnable {
long currentTime = System.currentTimeMillis();
//basically if we haven't sent a ping in a while, send one
if(currentTime - lastPingTime > SEND_PING_THRESHOLD){
queueOutgoingMessage(ServerMessage.constructPingMessage());
this.queueOutgoingMessage(ServerMessage.constructPingMessage());
lastPingTime = currentTime;
if(lastPongTime == 0){
lastPongTime = lastPingTime;

View File

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

View File

@ -1,8 +1,14 @@
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.loadingthreads.LoadingThread;
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.TerrainMessage;
import electrosphere.net.template.ClientProtocolTemplate;
@ -20,17 +26,22 @@ public class CharacterProtocol implements ClientProtocolTemplate<CharacterMessag
@Override
public void handleSyncMessage(CharacterMessage message) {
switch(message.getMessageSubtype()){
case RESPONSECREATECHARACTERSUCCESS:
//trigger request to spawn character
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage());
case RESPONSECREATECHARACTERSUCCESS: {
//trigger request to spawn character if the character list is undefined (ie if special loading case)
Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage(electrosphere.net.server.protocol.CharacterProtocol.SPAWN_EXISTING_TEMPLATE + ""));
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestMetadataMessage());
LoadingThread clientThread = new LoadingThread(LoadingThreadType.CLIENT_WORLD);
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 REQUESTCREATECHARACTER:
case REQUESTSPAWNCHARACTER:
case RESPONSECHARACTERLIST:
case RESPONSECREATECHARACTERFAILURE:
case RESPONSESPAWNCHARACTER:
case EDITORSWAP:

View File

@ -23,7 +23,7 @@ public class PlayerProtocol implements ClientProtocolTemplate<PlayerMessage> {
Globals.profiler.beginCpuSample("PlayerProtocol.handlePlayerMessage");
switch(message.getMessageSubtype()){
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());
break;
case SETINITIALDISCRETEPOSITION:

View File

@ -93,11 +93,7 @@ public class CharacterMessage extends NetworkMessage {
return false;
}
case TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER:
if(byteBuffer.getRemaining() >= TypeBytes.CHARACTER_MESSAGE_TYPE_REQUESTSPAWNCHARACTER_SIZE){
return true;
} else {
return false;
}
return CharacterMessage.canParseRequestSpawnCharacterMessage(byteBuffer);
case TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSESPAWNCHARACTER:
return CharacterMessage.canParseResponseSpawnCharacterMessage(byteBuffer);
case TypeBytes.CHARACTER_MESSAGE_TYPE_EDITORSWAP:
@ -248,20 +244,44 @@ public class CharacterMessage extends NetworkMessage {
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
*/
public static CharacterMessage parseRequestSpawnCharacterMessage(CircularByteBuffer byteBuffer){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
stripPacketHeader(byteBuffer);
rVal.setdata(ByteStreamUtils.popStringFromByteQueue(byteBuffer));
return rVal;
}
/**
* Constructs a message of type RequestSpawnCharacter
*/
public static CharacterMessage constructRequestSpawnCharacterMessage(){
public static CharacterMessage constructRequestSpawnCharacterMessage(String data){
CharacterMessage rVal = new CharacterMessage(CharacterMessageType.REQUESTSPAWNCHARACTER);
rVal.setdata(data);
rVal.serialize();
return rVal;
}
@ -383,11 +403,19 @@ public class CharacterMessage extends NetworkMessage {
rawBytes[1] = TypeBytes.CHARACTER_MESSAGE_TYPE_RESPONSECREATECHARACTERFAILURE;
break;
case REQUESTSPAWNCHARACTER:
rawBytes = new byte[2];
rawBytes = new byte[2+4+data.length()];
//message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_CHARACTER;
//entity messaage header
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;
case RESPONSESPAWNCHARACTER:
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_RESPONSECREATECHARACTERSUCCESS_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;
/*

View File

@ -21,6 +21,11 @@ public class Player {
*/
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
*/
@ -41,6 +46,11 @@ public class Player {
*/
int id;
/**
* The database's id of the player
*/
int dbId;
/**
* The world position of this player
*/
@ -64,10 +74,12 @@ public class Player {
/**
* Constructor
* @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;
id = connectionHandler.getPlayerId();
this.dbId = dbId;
this.simulationRadius = Globals.userSettings.getGameplayPhysicsCellRadius();
}
@ -75,8 +87,9 @@ public class Player {
* Used when initing a local connection
* @param id The id of the local connection
*/
public Player(int id){
public Player(int id, int dbId){
this.id = id;
this.dbId = dbId;
}
/**
@ -182,6 +195,14 @@ public class Player {
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;
import electrosphere.auth.AuthenticationManager;
import electrosphere.engine.Globals;
import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.PlayerMessage;
@ -17,11 +18,11 @@ public class AuthProtocol implements ServerProtocolTemplate<AuthMessage> {
switch(message.getMessageSubtype()){
case AUTHDETAILS:
//auth check
boolean successfulLogin = Globals.authenticationManager.authenticate(message.getuser(), message.getpass());
if(successfulLogin){
int loginId = Globals.authenticationManager.authenticate(message.getuser(), message.getpass());
if(loginId != AuthenticationManager.INVALID_LOGIN){
//TODO: actually set connection/protocol to authenticated
connectionHandler.addMessagetoOutgoingQueue(AuthMessage.constructAuthSuccessMessage());
Player newPlayer = new Player(connectionHandler);
Player newPlayer = new Player(connectionHandler, loginId);
Globals.playerManager.registerPlayer(newPlayer);
//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){

View File

@ -1,10 +1,15 @@
package electrosphere.net.server.protocol;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
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.loadingthreads.LoadingUtils;
import electrosphere.entity.Entity;
@ -21,6 +26,8 @@ import electrosphere.net.server.player.Player;
import electrosphere.net.template.ServerProtocolTemplate;
import electrosphere.server.character.PlayerCharacterCreation;
import electrosphere.server.datacell.Realm;
import electrosphere.server.db.DatabaseResult;
import electrosphere.server.db.DatabaseResultRow;
import electrosphere.util.Utilities;
/**
@ -28,20 +35,50 @@ import electrosphere.util.Utilities;
*/
public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessage> {
/**
* Should spawn the existing template
*/
public static final int SPAWN_EXISTING_TEMPLATE = -1;
@Override
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;
}
@Override
public void handleSyncMessage(ServerConnectionHandler connectionHandler, CharacterMessage message) {
switch(message.getMessageSubtype()){
case REQUESTCHARACTERLIST:
//TODO
break;
case REQUESTCREATECHARACTER: {
CreatureTemplate template = Utilities.deserialize(message.getdata(), CreatureTemplate.class);
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.addMessagetoOutgoingQueue(CharacterMessage.constructResponseCreateCharacterSuccessMessage());
} else {
@ -49,11 +86,15 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
}
} break;
case REQUESTSPAWNCHARACTER: {
CharacterProtocol.spawnEntityForClient(connectionHandler);
int charaId = Integer.parseInt(message.getdata());
CharacterProtocol.spawnEntityForClient(connectionHandler, charaId);
} break;
case EDITORSWAP: {
CharacterProtocol.swapPlayerCharacter(connectionHandler);
} break;
case REQUESTCHARACTERLIST:
//handled async
break;
case RESPONSECHARACTERLIST:
case RESPONSECREATECHARACTERSUCCESS:
case RESPONSECREATECHARACTERFAILURE:
@ -68,7 +109,18 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
* @param connectionHandler The connection handler for the player
* @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);
Realm realm = Globals.playerManager.getPlayerRealm(connectionHandler.getPlayer());
Vector3d spawnPoint = realm.getSpawnPoint();
@ -147,7 +199,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag
//spawn the new one
player.setHasSentPlayerEntity(false);
Entity newEntity = CharacterProtocol.spawnEntityForClient(connectionHandler);
Entity newEntity = CharacterProtocol.spawnEntityForClient(connectionHandler, CharacterProtocol.SPAWN_EXISTING_TEMPLATE);
ServerEntityUtils.repositionEntity(newEntity, position);
return newEntity;
}

View File

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

View File

@ -10,10 +10,24 @@ import java.util.List;
import electrosphere.logger.LoggerInterface;
/**
* Iterates through a database result
*/
public class DatabaseResultIterator implements Iterator<DatabaseResultRow> {
/**
* The result set
*/
ResultSet rs;
/**
* The metadata of the result set
*/
ResultSetMetaData metadata;
/**
* The type list of each column
*/
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
//instead the assumption is it always starts ON the first element
// 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) {
LoggerInterface.loggerEngine.ERROR("SQL Exception", e);
}

View File

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