Merge branch 'database' (read more)
* Implemented user accounts. * Other minor tweaks I think the general "clientIndex" variable needs to be changed to "accountIndex", since this will allow for much easier authentication.
This commit is contained in:
@@ -277,7 +277,6 @@ void InWorld::HandleDisconnect(SerialPacket packet) {
|
|||||||
|
|
||||||
void InWorld::HandleRegionContent(SerialPacket packet) {
|
void InWorld::HandleRegionContent(SerialPacket packet) {
|
||||||
//replace existing regions
|
//replace existing regions
|
||||||
//TODO: account for map index
|
|
||||||
if (regionPager.FindRegion(packet.regionInfo.x, packet.regionInfo.y)) {
|
if (regionPager.FindRegion(packet.regionInfo.x, packet.regionInfo.y)) {
|
||||||
regionPager.UnloadRegion(packet.regionInfo.x, packet.regionInfo.y);
|
regionPager.UnloadRegion(packet.regionInfo.x, packet.regionInfo.y);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ union SerialPacket {
|
|||||||
//information about the client
|
//information about the client
|
||||||
struct ClientInformation {
|
struct ClientInformation {
|
||||||
Metadata meta;
|
Metadata meta;
|
||||||
|
//TODO: change clientIndex to accountIndex for player ID
|
||||||
int clientIndex;
|
int clientIndex;
|
||||||
int characterIndex;
|
int characterIndex;
|
||||||
char username[PACKET_STRING_SIZE];
|
char username[PACKET_STRING_SIZE];
|
||||||
|
|||||||
@@ -19,6 +19,18 @@
|
|||||||
* 3. This notice may not be removed or altered from any source
|
* 3. This notice may not be removed or altered from any source
|
||||||
* distribution.
|
* distribution.
|
||||||
*/
|
*/
|
||||||
#include "client_data.hpp"
|
#ifndef ACCOUNTDATA_HPP_
|
||||||
|
#define ACCOUNTDATA_HPP_
|
||||||
|
|
||||||
int ClientData::uidCounter = 0;
|
#include <string>
|
||||||
|
|
||||||
|
struct AccountData {
|
||||||
|
std::string username;
|
||||||
|
//password
|
||||||
|
bool blackListed = false;
|
||||||
|
bool whiteListed = true;
|
||||||
|
|
||||||
|
int clientIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -31,7 +31,6 @@
|
|||||||
struct CharacterData {
|
struct CharacterData {
|
||||||
//metadata
|
//metadata
|
||||||
int clientIndex;
|
int clientIndex;
|
||||||
std::string username;
|
|
||||||
std::string handle;
|
std::string handle;
|
||||||
std::string avatar;
|
std::string avatar;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,12 @@
|
|||||||
* 3. This notice may not be removed or altered from any source
|
* 3. This notice may not be removed or altered from any source
|
||||||
* distribution.
|
* distribution.
|
||||||
*/
|
*/
|
||||||
#include "character_data.hpp"
|
#ifndef COMBATINSTANCE_HPP_
|
||||||
|
#define COMBATINSTANCE_HPP_
|
||||||
|
|
||||||
int CharacterData::uidCounter = 0;
|
struct CombatInstance {
|
||||||
|
//uid
|
||||||
|
static int uidCounter;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -24,7 +24,9 @@
|
|||||||
|
|
||||||
//server specific stuff
|
//server specific stuff
|
||||||
#include "client_data.hpp"
|
#include "client_data.hpp"
|
||||||
|
#include "account_data.hpp"
|
||||||
#include "character_data.hpp"
|
#include "character_data.hpp"
|
||||||
|
#include "combat_instance.hpp"
|
||||||
|
|
||||||
//maps
|
//maps
|
||||||
#include "map_allocator.hpp"
|
#include "map_allocator.hpp"
|
||||||
@@ -74,7 +76,15 @@ private:
|
|||||||
//TODO: a function that only sends to characters in a certain proximity
|
//TODO: a function that only sends to characters in a certain proximity
|
||||||
void PumpPacket(SerialPacket);
|
void PumpPacket(SerialPacket);
|
||||||
|
|
||||||
//TODO: manage the database
|
//Account management
|
||||||
|
int CreateUserAccount(std::string username, int clientIndex);
|
||||||
|
int LoadUserAccount(std::string username, int clientIndex);
|
||||||
|
int SaveUserAccount(int uid);
|
||||||
|
void UnloadUserAccount(int uid);
|
||||||
|
void DeleteUserAccount(int uid);
|
||||||
|
|
||||||
|
//TODO: character management
|
||||||
|
|
||||||
//TODO: combat systems
|
//TODO: combat systems
|
||||||
|
|
||||||
//APIs
|
//APIs
|
||||||
@@ -84,7 +94,9 @@ private:
|
|||||||
|
|
||||||
//server tables
|
//server tables
|
||||||
std::map<int, ClientData> clientMap;
|
std::map<int, ClientData> clientMap;
|
||||||
|
std::map<int, AccountData> accountMap;
|
||||||
std::map<int, CharacterData> characterMap;
|
std::map<int, CharacterData> characterMap;
|
||||||
|
std::map<int, CombatInstance> combatMap;
|
||||||
|
|
||||||
//maps
|
//maps
|
||||||
//TODO: I need to handle multiple map objects
|
//TODO: I need to handle multiple map objects
|
||||||
|
|||||||
@@ -46,11 +46,17 @@ void ServerApplication::HandleJoinRequest(SerialPacket packet) {
|
|||||||
ClientData newClient;
|
ClientData newClient;
|
||||||
newClient.address = packet.meta.srcAddress;
|
newClient.address = packet.meta.srcAddress;
|
||||||
|
|
||||||
|
//load the user account
|
||||||
|
int uid = LoadUserAccount(packet.clientInfo.username, ClientData::uidCounter);
|
||||||
|
if (uid < 0) {
|
||||||
|
std::cerr << "Error: Account already loaded: " << uid << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: move this into the character management code
|
//TODO: move this into the character management code
|
||||||
//create the new character
|
//create the new character
|
||||||
CharacterData newCharacter;
|
CharacterData newCharacter;
|
||||||
newCharacter.clientIndex = ClientData::uidCounter;
|
newCharacter.clientIndex = ClientData::uidCounter;
|
||||||
newCharacter.username = packet.clientInfo.username;
|
|
||||||
newCharacter.handle = packet.clientInfo.handle;
|
newCharacter.handle = packet.clientInfo.handle;
|
||||||
newCharacter.avatar = packet.clientInfo.avatar;
|
newCharacter.avatar = packet.clientInfo.avatar;
|
||||||
|
|
||||||
@@ -113,6 +119,15 @@ void ServerApplication::HandleDisconnect(SerialPacket packet) {
|
|||||||
network.Send(&clientMap[packet.clientInfo.clientIndex].address, buffer, PACKET_BUFFER_SIZE);
|
network.Send(&clientMap[packet.clientInfo.clientIndex].address, buffer, PACKET_BUFFER_SIZE);
|
||||||
clientMap.erase(packet.clientInfo.clientIndex);
|
clientMap.erase(packet.clientInfo.clientIndex);
|
||||||
|
|
||||||
|
//unload the client's account
|
||||||
|
//TODO: change clientIndex to accountIndex for player ID
|
||||||
|
for (auto it : accountMap) {
|
||||||
|
if (it.second.clientIndex == packet.clientInfo.clientIndex) {
|
||||||
|
UnloadUserAccount(it.first);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//prep the delete packet
|
//prep the delete packet
|
||||||
SerialPacket delPacket;
|
SerialPacket delPacket;
|
||||||
delPacket.meta.type = SerialPacket::Type::CHARACTER_DELETE;
|
delPacket.meta.type = SerialPacket::Type::CHARACTER_DELETE;
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
/* Copyright: (c) Kayne Ruse 2014
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
*
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
*
|
||||||
|
* 3. This notice may not be removed or altered from any source
|
||||||
|
* distribution.
|
||||||
|
*/
|
||||||
|
#include "server_application.hpp"
|
||||||
|
|
||||||
|
#include "sqlite3/sqlite3.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
//Define the queries
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
static const char* CREATE_USER_ACCOUNT = "INSERT INTO UserAccounts (username) VALUES (?);";
|
||||||
|
static const char* LOAD_USER_ACCOUNT = "SELECT * FROM UserAccounts WHERE username = ?;";
|
||||||
|
static const char* SAVE_USER_ACCOUNT = "INSERT OR REPLACE INTO UserAccounts VALUES (?, ?, ?, ?);";
|
||||||
|
static const char* DELETE_USER_ACCOUNT = "DELETE FROM UserAccounts WHERE uid = ?;";
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
//Define the methods
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
int ServerApplication::CreateUserAccount(std::string username, int clientIndex) {
|
||||||
|
//create this user account, failing if it exists, leave this account in memory
|
||||||
|
sqlite3_stmt* statement = nullptr;
|
||||||
|
|
||||||
|
//prep
|
||||||
|
if (sqlite3_prepare_v2(database, CREATE_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//parameter
|
||||||
|
if (sqlite3_bind_text(statement, 1, username.c_str(), username.size() + 1, SQLITE_STATIC) != SQLITE_OK) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//execute
|
||||||
|
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||||
|
//if this fails, than this account exists
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
|
||||||
|
//load this account into memory
|
||||||
|
return LoadUserAccount(username, clientIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServerApplication::LoadUserAccount(std::string username, int clientIndex) {
|
||||||
|
//load this user account, failing if it is in memory, creating it if it doesn't exist
|
||||||
|
sqlite3_stmt* statement = nullptr;
|
||||||
|
|
||||||
|
//prep
|
||||||
|
if (sqlite3_prepare_v2(database, LOAD_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//parameter
|
||||||
|
if (sqlite3_bind_text(statement, 1, username.c_str(), username.size() + 1, SQLITE_STATIC) != SQLITE_OK) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//execute
|
||||||
|
int ret = sqlite3_step(statement);
|
||||||
|
|
||||||
|
//process the result
|
||||||
|
if (ret == SQLITE_ROW) {
|
||||||
|
//get the index
|
||||||
|
int uid = sqlite3_column_int(statement, 0);
|
||||||
|
|
||||||
|
//check to see if this account is already loaded
|
||||||
|
if (accountMap.find(uid) != accountMap.end()) {
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//extract the data into memory
|
||||||
|
AccountData& newAccount = accountMap[uid];
|
||||||
|
newAccount.username = reinterpret_cast<const char*>(sqlite3_column_text(statement, 1));
|
||||||
|
newAccount.blackListed = sqlite3_column_int(statement, 2);
|
||||||
|
newAccount.whiteListed = sqlite3_column_int(statement, 3);
|
||||||
|
newAccount.clientIndex = clientIndex;
|
||||||
|
|
||||||
|
//finish the routine
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
|
||||||
|
if (ret == SQLITE_DONE) {
|
||||||
|
//create the non-existant account instead
|
||||||
|
return CreateUserAccount(username, clientIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw(std::runtime_error(std::string() + "Unknown SQL error in LoadUserAccount: " + sqlite3_errmsg(database) ));
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServerApplication::SaveUserAccount(int uid) {
|
||||||
|
//save this user account from memory, replacing it if it exists in the database
|
||||||
|
//DOCS: To use this method, change the in-memory copy, and then call this function using that object's UID.
|
||||||
|
|
||||||
|
//this method fails if this account is not loaded
|
||||||
|
if (accountMap.find(uid) == accountMap.end()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountData& account = accountMap[uid];
|
||||||
|
sqlite3_stmt* statement = nullptr;
|
||||||
|
|
||||||
|
//prep
|
||||||
|
if (sqlite3_prepare_v2(database, SAVE_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//parameters
|
||||||
|
bool ret = false;
|
||||||
|
ret |= sqlite3_bind_int(statement, 1, uid) != SQLITE_OK;
|
||||||
|
ret |= sqlite3_bind_text(statement, 2, account.username.c_str(), account.username.size() + 1, SQLITE_STATIC) != SQLITE_OK;
|
||||||
|
ret |= sqlite3_bind_int(statement, 3, account.blackListed) != SQLITE_OK;
|
||||||
|
ret |= sqlite3_bind_int(statement, 4, account.whiteListed) != SQLITE_OK;
|
||||||
|
|
||||||
|
//check for binding errors
|
||||||
|
if (ret) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//execute
|
||||||
|
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||||
|
//if this fails, than something went horribly wrong
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
throw( std::runtime_error(std::string() + "Unknown SQL error when saving an account: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
|
||||||
|
//successful execution
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerApplication::UnloadUserAccount(int uid) {
|
||||||
|
//save this user account, and then unload it
|
||||||
|
SaveUserAccount(uid);
|
||||||
|
accountMap.erase(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerApplication::DeleteUserAccount(int uid) {
|
||||||
|
//delete a user account from the database, and remove it from memory
|
||||||
|
sqlite3_stmt* statement = nullptr;
|
||||||
|
|
||||||
|
//prep
|
||||||
|
if (sqlite3_prepare_v2(database, DELETE_USER_ACCOUNT, -1, &statement, nullptr) != SQLITE_OK) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to prepare an SQL statement: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//parameter
|
||||||
|
if (sqlite3_bind_int(statement, 1, uid) != SQLITE_OK) {
|
||||||
|
throw( std::runtime_error(std::string() + "Failed to replace a prepared statement's parameter: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//execute
|
||||||
|
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||||
|
//if this fails, than something went horribly wrong
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
throw( std::runtime_error(std::string() + "Unknown SQL error when deleting an account: " + sqlite3_errmsg(database)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//finish the routine
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
accountMap.erase(uid);
|
||||||
|
}
|
||||||
@@ -27,6 +27,14 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
//-------------------------
|
||||||
|
//Define the various UIDs
|
||||||
|
//-------------------------
|
||||||
|
|
||||||
|
int ClientData::uidCounter = 0;
|
||||||
|
int CharacterData::uidCounter = 0;
|
||||||
|
int CombatInstance::uidCounter = 0;
|
||||||
|
|
||||||
//-------------------------
|
//-------------------------
|
||||||
//Define the public members
|
//Define the public members
|
||||||
//-------------------------
|
//-------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user