Merge pull request 'database-update-1' (#106) from database-update-1 into master

Reviewed-on: https://git.austinwhoover.com/gitadmin/Renderer/pulls/106
This commit is contained in:
gitadmin 2022-05-09 01:41:23 +00:00
commit 12900a9b26
29 changed files with 620 additions and 210 deletions

3
.gitignore vendored
View File

@ -27,3 +27,6 @@
#docs backup files
/docs/~$NetworkFlow.drawio.bkp
/docs/~$NetworkFlow.drawio.dtmp
#saves
/saves/arena

View File

@ -0,0 +1,7 @@
-- accounts definition
CREATE TABLE accounts (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
pwdhash TEXT NOT NULL
);

View File

@ -0,0 +1,3 @@
--given username=testusername
SELECT pwdhash FROM accounts WHERE username='testusername';

View File

@ -0,0 +1,10 @@
--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);
--data
CREATE TABLE charaData (playerId INTEGER PRIMARY KEY, id INTEGER, dataVal VARCHAR);
CREATE INDEX charaDataIDIndex ON charaData (id);

View File

@ -3,36 +3,7 @@
CREATE TABLE mainTable (propName VARCHAR PRIMARY KEY, propValue VARCHAR);
INSERT INTO mainTable (propName, propValue) VALUES ("ver","1");
--characters
--positions
CREATE TABLE charaWorldPositions (id INTEGER PRIMARY KEY, charID INTEGER, posX INTEGER, posY INTEGER);
CREATE INDEX charaWorldPositionsIDIndex ON charaWorldPositions (charID);
CREATE INDEX charaWorldPositionsPosIndex ON charaWorldPositions (posX, posY);
--data
CREATE TABLE charaData (id INTEGER PRIMARY KEY, charID INTEGER, dataVal VARCHAR);
CREATE INDEX charaDataIDIndex ON charaData (charID);
--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);
--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

@ -0,0 +1,10 @@
--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

@ -0,0 +1,10 @@
--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);

162
pom.xml
View File

@ -12,111 +12,126 @@
<lwjgl.version>3.2.3</lwjgl.version>
<joml.version>1.9.19</joml.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.lwjgl/lwjgl -->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-assimp</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-glfw</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengl</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengles</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-openal</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-stb</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<!-- generic LWJGL runtimes -->
<!--License: BSD-->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>runtime</scope>
</dependency>
<!-- assimp runtimes -->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>import</scope>
</dependency>
<!--ASSIMP-->
<!--License: BSD-->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-assimp</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-assimp</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>runtime</scope>
</dependency>
<!-- GLFW runtimes -->
<!--GLFW-->
<!--License: BSD-->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-glfw</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-glfw</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>runtime</scope>
</dependency>
<!-- opengl runtimes -->
<!--OpenGL-->
<!--License: BSD-->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengl</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengl</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>runtime</scope>
</dependency>
<!-- Embedded opengl runtimes -->
<!--OpenGL ES-->
<!--License: BSD-->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengles</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-opengles</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>runtime</scope>
</dependency>
<!-- Audio runtimes -->
<!--OpenAL-->
<!--License: BSD-->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-openal</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-openal</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>runtime</scope>
</dependency>
<!--STD-->
<!--License: BSD-->
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-stb</artifactId>
<version>${lwjgl.version}</version>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-stb</artifactId>
<version>${lwjgl.version}</version>
<classifier>${lwjgl.natives}</classifier>
<scope>runtime</scope>
</dependency>
<!--JOML-->
<!--License: MIT-->
<dependency>
<groupId>org.joml</groupId>
<artifactId>joml</artifactId>
<version>${joml.version}</version>
</dependency>
<!--GSON-->
<!--License: Apache-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<!--jBulletFork-->
<!--License: ZLIB-->
<!--
manual: http://www.cs.kent.edu/~ruttan/GameEngines/lectures/Bullet_User_Manual
because their docs ( http://jbullet.advel.cz/javadoc/com/bulletphysics/collision/shapes/CylinderShape.html ) suck
@ -128,22 +143,8 @@
<version>0.1</version>
</dependency>
<!--
Potential alternative binding for bullet ?
<dependency>
<groupId>com.badlogicgames.gdx</groupId>
<artifactId>gdx-bullet</artifactId>
<version>1.10.0</version>
</dependency>
-->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-crypto -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-crypto</artifactId>
<version>1.1.0</version>
</dependency>
<!--SQLITE-JDBC-->
<!--License: Apache-->
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
@ -190,38 +191,7 @@
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>electrosphere.main.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>

View File

@ -1,11 +1,81 @@
package electrosphere.auth;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import electrosphere.game.server.db.DatabaseResult;
import electrosphere.game.server.db.DatabaseResultRow;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
public class AuthenticationManager {
public boolean authenticate(String username, String password){
boolean rVal = true;
//TODO: actually authenticate
//first we hash the input password
String hashedPassword = getHashedString(password);
//then query the database for the username and hash for the input username
DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT id, username, pwdhash FROM accounts WHERE username=?;",username);
if(result.hasResult()){
boolean 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 true;
}
}
//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;
}
}
LoggerInterface.loggerAuth.INFO("Failed to authenticate user " + username);
return false;
}
static final int saltLength = 16;
public static String getHashedString(String input){
String rVal = "";
//generate salt
char[] charArray = input.toCharArray();
byte[] salt = new byte[saltLength];
for(int i = 0; i < saltLength; i++){
if(i < charArray.length){
salt[i] = (byte)charArray[i];
} else {
salt[i] = (byte)i;
}
}
//perform hash
KeySpec spec = new PBEKeySpec(charArray, salt, 65536, 512);
try {
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
byte[] hash = f.generateSecret(spec).getEncoded();
Base64.Encoder enc = Base64.getEncoder();
// System.out.printf("salt: %s%n", enc.encodeToString(salt));
// System.out.printf("hash: %s%n", enc.encodeToString(hash));
// System.out.println(Arrays.toString(hash));
rVal = enc.encodeToString(hash);
} catch (NoSuchAlgorithmException e) {
LoggerInterface.loggerAuth.ERROR("NoSuchAlgorithmException in hash string", e);
} catch (InvalidKeySpecException e) {
LoggerInterface.loggerAuth.ERROR("InvalidKeySpecException in hash string", e);
}
return rVal;
}

View File

@ -773,7 +773,7 @@ public class ControlHandler {
controls.get(INPUT_CODE_CHARACTER_OPEN).setOnClick(new ControlMethod(){public void execute(){
if(InventoryUtils.hasEquipInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowStrings.WINDOW_CHARACTER) == null){
//create window
Window mainMenuWindow = MenuGenerators.createCharacterMenu(InventoryUtils.getEquipInventory(Globals.playerEntity));
Window mainMenuWindow = MenuGenerators.createCharacterInventoryMenu(InventoryUtils.getEquipInventory(Globals.playerEntity));
//register
Globals.elementManager.registerWindow(WindowStrings.WINDOW_CHARACTER, mainMenuWindow);
//make visible

View File

@ -36,6 +36,7 @@ import electrosphere.game.server.terrain.models.TerrainModification;
import electrosphere.game.server.town.Town;
import electrosphere.game.server.world.MacroData;
import electrosphere.game.server.datacell.DataCellManager;
import electrosphere.game.server.db.DatabaseUtils;
import electrosphere.game.simulation.MicroSimulation;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
@ -65,8 +66,12 @@ import electrosphere.game.server.unit.UnitUtils;
import electrosphere.renderer.ui.DrawableElement;
import electrosphere.renderer.ui.WidgetUtils;
import electrosphere.renderer.ui.Window;
import electrosphere.util.FileUtils;
import electrosphere.util.Utilities;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
@ -183,6 +188,12 @@ public class LoadingThread extends Thread {
initServerArenaWorldData();
//init data cell manager
initDataCellManager();
//for testing purposes
FileUtils.recursivelyDelete("/home/satellite/temp/saves/arena");
//init database connection
SaveUtils.initSave("arena");
//connect to database
SaveUtils.loadSave("arena");
//init authentication
initAuthenticationManager();
//initialize the server thread (server only)
@ -416,7 +427,6 @@ public class LoadingThread extends Thread {
Thread clientThread = null;
if(Globals.RUN_CLIENT){
Globals.clientConnection = new ClientNetworking(NetUtils.getAddress(),NetUtils.getPort());
System.out.println(Globals.clientConnection.socket);
clientThread = new Thread(Globals.clientConnection);
clientThread.start();
}

View File

@ -30,41 +30,74 @@ public class DatabaseController {
conn = DriverManager.getConnection(fullAddress, connectionProps);
} catch (SQLException ex) {
LoggerInterface.loggerFileIO.ERROR("Failure to connect to db", ex);
ex.printStackTrace();
}
}
// public boolean executeStatement(String statementRaw){
// boolean rVal = false;
// try {
// PreparedStatement statement = conn.prepareStatement(statementRaw);
// rVal = statement.execute();
// } catch (SQLException ex) {
// LoggerInterface.loggerFileIO.ERROR("SQL statement execution error", ex);
// }
// return rVal;
// }
public DatabaseResult executeStatement(String statementRaw){
DatabaseResult rVal = DatabaseResult.createStatement(statementRaw);
/**
* Executes a write statement to the database
* @param statementRaw The raw string for the statement
* @param arguments The arguments to be inserted into the raw sql
*/
public boolean executePreparedStatement(String statementRaw, Object...arguments){
try {
PreparedStatement statement = conn.prepareStatement(statementRaw);
statement.execute();
ResultSet results = statement.getResultSet();
if(results != null){
rVal.addResultSet(results);
//Set arguments for prepared statements
int argumentIndex = 1;
for(Object currentArg : arguments){
if(currentArg instanceof String){
statement.setString(argumentIndex, (String)currentArg);
} else if(currentArg instanceof Integer){
statement.setInt(argumentIndex, (int)currentArg);
} else if(currentArg instanceof Float){
statement.setFloat(argumentIndex, (float)currentArg);
} else if(currentArg instanceof Boolean){
statement.setBoolean(argumentIndex, (boolean)currentArg);
} else if(currentArg instanceof Long){
statement.setLong(argumentIndex, (long)currentArg);
} else if(currentArg instanceof Double){
statement.setDouble(argumentIndex, (double)currentArg);
}
argumentIndex++;
}
//actually execute
return statement.execute();
} catch (SQLException ex) {
rVal.succeeded = false;
rVal.hasResultSet = false;
LoggerInterface.loggerFileIO.ERROR("SQL statement execution error", ex);
LoggerInterface.loggerFileIO.ERROR("SQL query execution error", ex);
}
return rVal;
return false;
}
public DatabaseResult executeQuery(String statementRaw){
DatabaseResult rVal = DatabaseResult.createStatement(statementRaw);
/**
* Executes a query against the database
* @param statementRaw The raw sql
* @param arguments The arguments to be injected into the raw sql
* @return A DatabaseResult representing the results of the query, or null if there was no result
*/
public DatabaseResult executePreparedQuery(String statementRaw, Object...arguments){
DatabaseResult rVal = DatabaseResult.createQuery(statementRaw);
try {
PreparedStatement statement = conn.prepareStatement(statementRaw);
//Set arguments for prepared statements
int argumentIndex = 1;
for(Object currentArg : arguments){
if(currentArg instanceof String){
statement.setString(argumentIndex, (String)currentArg);
} else if(currentArg instanceof Integer){
statement.setInt(argumentIndex, (int)currentArg);
} else if(currentArg instanceof Float){
statement.setFloat(argumentIndex, (float)currentArg);
} else if(currentArg instanceof Boolean){
statement.setBoolean(argumentIndex, (boolean)currentArg);
} else if(currentArg instanceof Long){
statement.setLong(argumentIndex, (long)currentArg);
} else if(currentArg instanceof Double){
statement.setDouble(argumentIndex, (double)currentArg);
}
argumentIndex++;
}
//actually execute
ResultSet results = statement.executeQuery();
if(results != null){
rVal.addResultSet(results);
@ -79,6 +112,9 @@ public class DatabaseController {
public boolean isConnected(){
boolean rVal = false;
if(conn == null){
return false;
}
try {
rVal = conn.isValid(100);
} catch (SQLException ex) {

View File

@ -4,6 +4,7 @@ import com.google.gson.Gson;
import electrosphere.logger.LoggerInterface;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -11,7 +12,8 @@ import java.util.logging.Logger;
*
* @author amaterasu
*/
public class DatabaseResult {
public class DatabaseResult implements Iterable<DatabaseResultRow> {
boolean isQuery = false;
boolean isStatement = false;
boolean succeeded = false;
@ -25,29 +27,30 @@ public class DatabaseResult {
}
public static DatabaseResult createQuery(String code){
protected static DatabaseResult createQuery(String code){
DatabaseResult rVal = new DatabaseResult();
rVal.isQuery = true;
rVal.code = code;
return rVal;
}
public static DatabaseResult createStatement(String code){
protected static DatabaseResult createStatement(String code){
DatabaseResult rVal = new DatabaseResult();
rVal.isStatement = true;
rVal.code = code;
return rVal;
}
public void addResultSet(ResultSet rs){
protected void addResultSet(ResultSet rs){
hasResultSet = true;
this.rs = rs;
}
public ResultSet getResultSet(){
protected ResultSet getResultSet(){
return rs;
}
public boolean hasResultSet(){
public boolean hasResult(){
return hasResultSet;
}
@ -77,4 +80,9 @@ public class DatabaseResult {
public void logRawResult(){
}
@Override
public DatabaseResultIterator iterator() {
return new DatabaseResultIterator(rs);
}
}

View File

@ -0,0 +1,98 @@
package electrosphere.game.server.db;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import electrosphere.logger.LoggerInterface;
public class DatabaseResultIterator implements Iterator<DatabaseResultRow> {
ResultSet rs;
ResultSetMetaData metadata;
List<Integer> typeList;
/**
* Creates a result iterator -- used internal to package
* @param result
*/
protected DatabaseResultIterator(ResultSet result){
this.rs = result;
try {
this.metadata = this.rs.getMetaData();
int columnCount = metadata.getColumnCount();
this.typeList = new LinkedList<Integer>();
for(int i = 0; i < columnCount; i++){
//result sets are indexed starting at 1
this.typeList.add(metadata.getColumnType(i+1));
}
//the sqlite driver doesn't support reverse navigation unfortunatelly
//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();
} catch (SQLException e) {
LoggerInterface.loggerEngine.ERROR("SQL Exception", e);
}
}
/**
* Lets us know if the result has a next value
*/
@Override
public boolean hasNext() {
try {
return !rs.isAfterLast();
} catch (SQLException e) {
LoggerInterface.loggerEngine.ERROR("Critical failure in DatabaseResultIterator", e);
return false;
}
}
/**
* Gets the next value in the result
*/
@Override
public DatabaseResultRow next() {
DatabaseResultRow row = new DatabaseResultRow();
int columnIncrementer = 0;
//basically go through each type and add it to the row object that we return
//the types are stored in the typeList field on this object when it is created
try {
for(int type : typeList){
//increment at the beginning because result sets are indexed starting at 1
columnIncrementer++;
switch(type){
case Types.INTEGER:
row.putValue(metadata.getColumnName(columnIncrementer), rs.getInt(columnIncrementer));
break;
case Types.VARCHAR:
row.putValue(metadata.getColumnName(columnIncrementer), rs.getString(columnIncrementer));
break;
case Types.BIGINT:
row.putValue(metadata.getColumnName(columnIncrementer), rs.getLong(columnIncrementer));
break;
case Types.FLOAT:
row.putValue(metadata.getColumnName(columnIncrementer), rs.getFloat(columnIncrementer));
break;
case Types.DOUBLE:
row.putValue(metadata.getColumnName(columnIncrementer), rs.getDouble(columnIncrementer));
break;
default:
LoggerInterface.loggerEngine.WARNING("Unsupported type from database in DatabaseResultIterator " + type);
break;
}
}
//make sure to increment the result set so we don't infinitely loop the first element
this.rs.next();
} catch (SQLException e){
LoggerInterface.loggerEngine.ERROR("Unhandled SQL exception", e);
}
return row;
}
}

View File

@ -0,0 +1,76 @@
package electrosphere.game.server.db;
import java.util.HashMap;
import java.util.Map;
public class DatabaseResultRow {
// protected addColumn(int column)
//stores all the values of the row in an easily indexible-by-column-name format
Map<String,Object> values = new HashMap<String,Object>();
/**
* Used internally for putting values into the row
* @param columnName
* @param value
*/
protected void putValue(String columnName, Object value){
values.put(columnName,value);
}
/**
* Gets the given value
* @param columnName the name of the column to check
* @return the value on said column
*/
public Object getValue(String columnName){
return values.get(columnName);
}
/**
* Gets the given value as a string
* @param columnName the name of the column to check
* @return the value on said column as a string
*/
public String getAsString(String columnName){
return (String)values.get(columnName);
}
/**
* Gets the given value as an integer
* @param columnName the name of the column to check
* @return the value on said column as an integer
*/
public int getAsInteger(String columnName){
return (Integer)values.get(columnName);
}
/**
* Gets the given value as a long
* @param columnName the name of the column to check
* @return the value on said column as a long
*/
public long getAsLong(String columnName){
return (Long)values.get(columnName);
}
/**
* Gets the given value as a float
* @param columnName the name of the column to check
* @return the value on said column as a float
*/
public float getAsFloat(String columnName){
return (Float)values.get(columnName);
}
/**
* Gets the given value as a double
* @param columnName the name of the column to check
* @return the value on said column as a double
*/
public double getAsDouble(String columnName){
return (Double)values.get(columnName);
}
}

View File

@ -1,6 +1,7 @@
package electrosphere.game.server.db;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
import electrosphere.util.FileUtils;
import java.io.IOException;
import java.util.logging.Level;
@ -11,29 +12,58 @@ import java.util.logging.Logger;
* @author satellite
*/
public class DatabaseUtils {
public static boolean initCentralDBFile(String path){
String sanitizedPath = "." + FileUtils.sanitizeFilePath(path);
String sanitizedPath = FileUtils.sanitizeFilePath(path);
if(!FileUtils.checkFileExists(sanitizedPath)){
return false;
}
String dbFilePath = sanitizedPath + "/central.db";
DatabaseController controller = new DatabaseController();
controller.connect(dbFilePath);
if(Globals.dbController == null){
Globals.dbController = new DatabaseController();
}
if(!Globals.dbController.isConnected()){
Globals.dbController.connect(dbFilePath);
}
runScript(Globals.dbController,"createTables.sql");
//both of these are used for arena mode as well as main game
runScript(Globals.dbController,"/auth/createAuthTables.sql");
runScript(Globals.dbController,"/character/createCharacterTables.sql");
//create adventure-only files
if(!dbFilePath.equals("./saves/arena/central.db")){
//we only want to create these if we're not in arena mode
runScript(Globals.dbController,"/towns/createTownsTables.sql");
runScript(Globals.dbController,"/structs/createStructsTables.sql");
}
Globals.dbController.disconnect();
return true;
}
public static boolean runScript(DatabaseController controller, String scriptPath){
String rawScript = "";
try {
rawScript = FileUtils.getSQLScriptFileAsString("createTables.sql");
rawScript = FileUtils.getSQLScriptFileAsString(scriptPath);
} catch (IOException ex) {
LoggerInterface.loggerEngine.ERROR("Failure reading create db script", ex);
return false;
}
String[] scriptLines = rawScript.split("\n");
String accumulatorString = "";
for(String line : scriptLines){
if(line.length() > 1 && !line.startsWith("--")){
System.out.println("EXECUTE: " + line);
controller.executeStatement(line);
if(line.contains(";")){
accumulatorString = accumulatorString + line;
System.out.println("EXECUTE: " + accumulatorString);
controller.executePreparedStatement(accumulatorString);
accumulatorString = "";
} else {
accumulatorString = accumulatorString + line;
}
}
}
controller.disconnect();
return true;
}
}

View File

@ -16,7 +16,8 @@ import java.util.List;
public class SaveUtils {
static String deriveSaveDirectoryPath(String saveName){
return "./saves/" + saveName;
String path = "/home/satellite/temp/saves/" + saveName;
return path;
}
/**
@ -30,7 +31,7 @@ public class SaveUtils {
if(FileUtils.checkFileExists(dirPath)){
return false;
}
//create dir
// create dir
if(!FileUtils.createDirectory(dirPath)){
//we for some unknown reason, couldn't make the save dir
return false;
@ -61,8 +62,10 @@ public class SaveUtils {
String dirPath = deriveSaveDirectoryPath(saveName);
String dbFilePath = FileUtils.sanitizeFilePath(dirPath) + "/central.db";
Globals.dbController.connect(dbFilePath);
Globals.serverTerrainManager.load(saveName);
Globals.serverWorldData = FileUtils.loadObjectFromSavePath(saveName, "world.json", ServerWorldData.class);
if(!saveName.equals("arena")){
Globals.serverTerrainManager.load(saveName);
Globals.serverWorldData = FileUtils.loadObjectFromSavePath(saveName, "world.json", ServerWorldData.class);
}
return true;
}

View File

@ -2,6 +2,7 @@ package electrosphere.game.server.structure.virtual;
import com.google.gson.Gson;
import electrosphere.game.server.db.DatabaseResult;
import electrosphere.game.server.db.DatabaseResultRow;
import electrosphere.logger.LoggerInterface;
import electrosphere.main.Globals;
import java.sql.ResultSet;
@ -29,24 +30,19 @@ public class StructureManager {
public static Structure createStructure(int worldX, int worldY, float localX, float localY, String type){
structIDIterator++;
Structure rVal = new Structure(worldX, worldY, localX, localY, type);
Globals.dbController.executeStatement("INSERT INTO structWorldPositions (structID,posX,posY) VALUES (" + structIDIterator + "," + worldX + "," + worldY + ");");
Globals.dbController.executeStatement("INSERT INTO structData(structID,propName,propValue) VALUES(" + structIDIterator + ",'localX','" + localX + "');");
Globals.dbController.executeStatement("INSERT INTO structData(structID,propName,propValue) VALUES(" + structIDIterator + ",'localY','" + localY + "');");
Globals.dbController.executePreparedStatement("INSERT INTO structWorldPositions (structID,posX,posY) VALUES (?,?,?);",structIDIterator,worldX,worldY);
Globals.dbController.executePreparedStatement("INSERT INTO structData(structID,propName,propValue) VALUES(?,'localX','?');",structIDIterator,localX);
Globals.dbController.executePreparedStatement("INSERT INTO structData(structID,propName,propValue) VALUES(?,'localY','?');",structIDIterator,localY);
return rVal;
}
public static List<Structure> getStructuresInChunk(int worldX, int worldY){
List<Structure> rVal = new LinkedList<Structure>();
DatabaseResult result = Globals.dbController.executeStatement("SELECT * FROM structWorldPositions WHERE posX = " + worldX + " AND posY = " + worldY + ";");
DatabaseResult result = Globals.dbController.executePreparedQuery("SELECT * FROM structWorldPositions WHERE posX = ? AND posY = ?;",worldX,worldY);
List<Integer> returnedIDs = new LinkedList<Integer>();
if(result.hasResultSet()){
ResultSet positionSet = result.getResultSet();
try {
while(positionSet.next()){
returnedIDs.add(positionSet.getInt("structID"));
}
} catch (SQLException ex) {
LoggerInterface.loggerEngine.ERROR("Error looping through positions returned in StructureManager.getStructuresInChunk", ex);
if(result.success() && result.hasResult()){
for(DatabaseResultRow row : result){
returnedIDs.add(row.getAsInteger("structID"));
}
}
if(returnedIDs.size() > 0){
@ -55,17 +51,11 @@ public class StructureManager {
ids = ids + ID + ",";
}
ids = ids.substring(0, ids.length() - 1);
result = Globals.dbController.executeStatement("SELECT * FROM structData WHERE structID IN (" + ids + ");");
if(result.success() && result.hasResultSet()){
ResultSet rs = result.getResultSet();
try {
while(rs.next()){
String rawData = rs.getString("dataVal");
Structure newStruct = serializer.fromJson(rawData, Structure.class);
rVal.add(newStruct);
}
} catch (SQLException ex) {
LoggerInterface.loggerEngine.ERROR("Error looping through structures returned in StructureManager.getStructuresInChunk", ex);
result = Globals.dbController.executePreparedQuery("SELECT * FROM structData WHERE structID IN (?);",ids);
if(result.success() && result.hasResult()){
for(DatabaseResultRow row : result){
Structure newStruct = serializer.fromJson(row.getAsString("dataVal"), Structure.class);
rVal.add(newStruct);
}
}
}

View File

@ -40,6 +40,7 @@ public class Logger {
public void ERROR(String message, Exception e){
if(level == LogLevel.DEBUG || level == LogLevel.INFO || level == LogLevel.WARNING || level == LogLevel.ERROR){
System.out.println(message);
e.printStackTrace();
}
}
}

View File

@ -16,6 +16,7 @@ public class LoggerInterface {
public static Logger loggerRenderer;
public static Logger loggerEngine;
public static Logger loggerStartup;
public static Logger loggerAuth;
public static void initLoggers(){
loggerStartup = new Logger(LogLevel.WARNING);
@ -24,6 +25,7 @@ public class LoggerInterface {
loggerGameLogic = new Logger(LogLevel.WARNING);
loggerRenderer = new Logger(LogLevel.WARNING);
loggerEngine = new Logger(LogLevel.WARNING);
loggerAuth = new Logger(LogLevel.INFO);
loggerStartup.INFO("Initialized loggers");
}
}

View File

@ -176,6 +176,8 @@ public class Globals {
//
public static PlayerManager playerManager;
public static Player clientPlayer;
public static String clientUsername;
public static String clientPassword;
//
//Generic OpenGL Statements

View File

@ -4,6 +4,7 @@ import com.google.gson.Gson;
import electrosphere.audio.AudioEngine;
import electrosphere.audio.AudioSource;
import electrosphere.audio.AudioUtils;
import electrosphere.auth.AuthenticationManager;
import electrosphere.controls.ControlHandler;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.renderer.Material;
@ -48,9 +49,14 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.Provider;
import java.security.Security;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import org.joml.Vector3d;
@ -107,6 +113,16 @@ public class Main {
//init global variables
Globals.initGlobals();
// if(1==1){
// SaveUtils.loadSave("arena");
// Globals.authenticationManager = new AuthenticationManager();
// String rawPass = "testpassword";
// String hashedPassword = AuthenticationManager.getHashedString(rawPass);
// boolean authed = Globals.authenticationManager.authenticate("testuser", hashedPassword);
// System.out.println("Authenticated: " + authed);
// System.exit(0);
// }
//world gen testing
//gen terrain

View File

@ -102,14 +102,12 @@ public class MenuGenerators {
arenaButton.addChild(arenaLabel);
rVal.addChild(arenaButton);
arenaButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CHARACTER_SERVER);
Globals.loadingThreadsList.add(clientThread);
LoadingThread serverThread = new LoadingThread(LoadingThread.LOAD_ARENA);
Globals.loadingThreadsList.add(serverThread);
Globals.RUN_CLIENT = true;
Globals.RUN_SERVER = true;
clientThread.start();
serverThread.start();
WindowUtils.replaceMainMenuContents(MenuGeneratorsArena.createArenaHostLoginMenu());
return false;
}});
@ -455,7 +453,7 @@ public class MenuGenerators {
//clear ui
WindowUtils.cleanItemDraggingWindow();
String sourceWindowId = WindowStrings.WINDOW_CHARACTER;
WindowUtils.replaceWindow(sourceWindowId,MenuGenerators.createCharacterMenu(inventory));
WindowUtils.replaceWindow(sourceWindowId,MenuGenerators.createCharacterInventoryMenu(inventory));
//null globals
Globals.dragSourceInventory = null;
Globals.draggedItem = null;
@ -924,7 +922,7 @@ public class MenuGenerators {
WindowUtils.cleanItemDraggingWindow();
//rerender both inventories
//re-render inventory
WindowUtils.replaceWindow(WindowStrings.WINDOW_CHARACTER, MenuGenerators.createCharacterMenu(sourceInventory));
WindowUtils.replaceWindow(WindowStrings.WINDOW_CHARACTER, MenuGenerators.createCharacterInventoryMenu(sourceInventory));
//re-render inventory
WindowUtils.replaceWindow(WindowUtils.getInventoryWindowID(inventory.getId()), MenuGenerators.createNaturalInventoryMenu(inventory));
}
@ -953,7 +951,7 @@ public class MenuGenerators {
}
public static Window createCharacterMenu(RelationalInventoryState inventory){
public static Window createCharacterInventoryMenu(RelationalInventoryState inventory){
// int screenTop = Globals.WINDOW_HEIGHT - 150;
int width = 500;
int height = 500;
@ -987,7 +985,7 @@ public class MenuGenerators {
WindowUtils.cleanItemDraggingWindow();
//rerender both inventories
//re-render inventory
WindowUtils.replaceWindow(WindowStrings.WINDOW_CHARACTER, MenuGenerators.createCharacterMenu(inventory));
WindowUtils.replaceWindow(WindowStrings.WINDOW_CHARACTER, MenuGenerators.createCharacterInventoryMenu(inventory));
//re-render inventory
WindowUtils.replaceWindow(WindowUtils.getInventoryWindowID(sourceInventory.getId()), MenuGenerators.createNaturalInventoryMenu(sourceInventory));
}
@ -1088,7 +1086,7 @@ public class MenuGenerators {
WindowUtils.cleanItemDraggingWindow();
//rerender both inventories
//re-render inventory
WindowUtils.replaceWindow(WindowStrings.WINDOW_CHARACTER, MenuGenerators.createCharacterMenu(inventory));
WindowUtils.replaceWindow(WindowStrings.WINDOW_CHARACTER, MenuGenerators.createCharacterInventoryMenu(inventory));
//re-render inventory
WindowUtils.replaceWindow(WindowUtils.getInventoryWindowID(sourceInventory.getId()), MenuGenerators.createNaturalInventoryMenu(sourceInventory));
}

View File

@ -0,0 +1,70 @@
package electrosphere.menu;
import electrosphere.auth.AuthenticationManager;
import electrosphere.engine.LoadingThread;
import electrosphere.main.Globals;
import electrosphere.net.NetUtils;
import electrosphere.renderer.ui.ClickableElement;
import electrosphere.renderer.ui.Element;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.TextInput;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.form.FormElement;
public class MenuGeneratorsArena {
public static Element createArenaHostLoginMenu(){
FormElement rVal = new FormElement();
int screenTop = 150;
//label (address)
Label usernameLabel = new Label(100,screenTop + 50,1.0f);
usernameLabel.setText("Username");
rVal.addChild(usernameLabel);
//text entry (address)
TextInput usernameInput = new TextInput(100,screenTop + 125,1.0f);
usernameInput.setText("");
rVal.addChild(usernameInput);
//label (port)
Label passwordLabel = new Label(100,screenTop + 200,1.0f);
passwordLabel.setText("Password");
rVal.addChild(passwordLabel);
//text entry (port)
TextInput passwordInput = new TextInput(100,screenTop + 275,1.0f);
passwordInput.setText("");
rVal.addChild(passwordInput);
//button (connect)
Button connectButton = new Button();
Label connectLabel = new Label(100,screenTop + 350,1.0f);
connectLabel.setText("Login");
connectButton.addChild(connectLabel);
rVal.addChild(connectButton);
connectButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
Globals.clientUsername = usernameInput.getText();
Globals.clientPassword = AuthenticationManager.getHashedString(passwordInput.getText());
LoadingThread clientThread = new LoadingThread(LoadingThread.LOAD_CHARACTER_SERVER);
Globals.loadingThreadsList.add(clientThread);
clientThread.start();
return false;
}});
//button (back)
// Button backButton = new Button();
// Label backLabel = new Label(100,screenTop + 425,1.0f);
// backLabel.setText("Back");
// backButton.addChild(backLabel);
// rVal.addChild(backButton);
// backButton.setOnClick(new ClickableElement.ClickEventCallback(){public boolean execute(ClickEvent event){
// WindowUtils.replaceMainMenuContents(MenuGenerators.createMultiplayerMenu());
// return false;
// }});
return rVal;
}
}

View File

@ -26,8 +26,6 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.crypto.stream.CryptoInputStream;
import org.apache.commons.crypto.stream.CryptoOutputStream;
/**
*
@ -94,7 +92,7 @@ public class ClientNetworking implements Runnable{
}
if(connectionAttempts > MAX_CONNECTION_ATTEMPTS){
LoggerInterface.loggerNetworking.ERROR("Max client connection attempts!", new Exception());
System.exit(1);
// System.exit(1);
}
}

View File

@ -14,15 +14,19 @@ public class AuthProtocol {
case AUTHREQUEST:
//Try login
//TODO: actually get user/pass
Globals.clientConnection.queueOutgoingMessage(AuthMessage.constructAuthDetailsMessage("myuser","mypass"));
Globals.clientConnection.queueOutgoingMessage(AuthMessage.constructAuthDetailsMessage(Globals.clientUsername,Globals.clientPassword));
break;
case AUTHSUCCESS:
//clean password hash from memory
Globals.clientPassword = "";
//request playable races
Globals.clientConnection.queueOutgoingMessage(LoreMessage.constructRequestRacesMessage());
//log that we succeeded
LoggerInterface.loggerAuth.INFO("Successfully logged in");
break;
case AUTHFAILURE:
//TODO: handle better
LoggerInterface.loggerEngine.ERROR("Auth failure",new Exception("Auth failure"));
LoggerInterface.loggerAuth.ERROR("Auth failure",new Exception("Auth failure"));
break;
//ignore stack
case AUTHDETAILS:

View File

@ -1,5 +1,6 @@
package electrosphere.net.server;
import electrosphere.game.server.saves.SaveUtils;
import electrosphere.main.Globals;
import electrosphere.main.Main;
import electrosphere.net.NetUtils;

View File

@ -35,8 +35,6 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.crypto.stream.CryptoInputStream;
import org.apache.commons.crypto.stream.CryptoOutputStream;
import org.joml.Vector3f;
/**

View File

@ -222,7 +222,7 @@ public class FileUtils {
* @return true if directory was created, false if it was not
*/
public static boolean createDirectory(String directoryName){
String sanitizedPath = "." + sanitizeFilePath(directoryName);
String sanitizedPath = sanitizeFilePath(directoryName);
File targetDir = new File(sanitizedPath);
if(targetDir.exists()){
return false;
@ -242,6 +242,21 @@ public class FileUtils {
}
return rVal;
}
public static void recursivelyDelete(String path){
File file = new File(path);
if(file.isDirectory()){
for(File child : file.listFiles()){
recursivelyDelete(child.getAbsolutePath());
}
}
try {
Files.delete(file.toPath());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}