diff --git a/client/client_application.cpp b/client/client_application.cpp index 62e4d25..4ae481b 100644 --- a/client/client_application.cpp +++ b/client/client_application.cpp @@ -45,12 +45,12 @@ //Public access members //------------------------- -void ClientApplication::Init(int argc, char** argv) { +void ClientApplication::Init(int argc, char* argv[]) { std::cout << "Beginning " << argv[0] << std::endl; //load the prerequisites ConfigUtility& config = ConfigUtility::GetSingleton(); - config.Load("rsc\\config.cfg"); + config.Load("rsc\\config.cfg", argc, argv); //------------------------- //Initialize the APIs diff --git a/client/client_application.hpp b/client/client_application.hpp index 773243f..dbbc785 100644 --- a/client/client_application.hpp +++ b/client/client_application.hpp @@ -35,7 +35,7 @@ class ClientApplication: public Singleton { public: //public methods - void Init(int argc, char** argv); + void Init(int argc, char* argv[]); void Proc(); void Quit(); diff --git a/client/client_utilities/makefile b/client/client_utilities/makefile new file mode 100644 index 0000000..8d12afe --- /dev/null +++ b/client/client_utilities/makefile @@ -0,0 +1,37 @@ +#config +INCLUDES+=. +LIBS+= +CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) + +#source +CXXSRC=$(wildcard *.cpp) + +#objects +OBJDIR=obj +OBJ+=$(addprefix $(OBJDIR)/,$(CXXSRC:.cpp=.o)) + +#output +OUTDIR=.. +OUT=$(addprefix $(OUTDIR)/,server.a) + +#targets +all: $(OBJ) $(OUT) + ar -crs $(OUT) $(OBJ) + +$(OBJ): | $(OBJDIR) + +$(OUT): | $(OUTDIR) + +$(OBJDIR): + mkdir $(OBJDIR) + +$(OUTDIR): + mkdir $(OUTDIR) + +$(OBJDIR)/%.o: %.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +clean: + $(RM) *.o *.a *.exe + +rebuild: clean all diff --git a/client/main.cpp b/client/main.cpp index 0903053..c8584e5 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -30,7 +30,7 @@ using namespace std; -int main(int argc, char** argv) { +int main(int argc, char* argv[]) { try { //create the singletons ConfigUtility::Create(); diff --git a/client/makefile b/client/makefile index c8ca863..c46fbcb 100644 --- a/client/makefile +++ b/client/makefile @@ -1,5 +1,5 @@ #config -INCLUDES+=. scenes ../common/debugging ../common/gameplay ../common/graphics ../common/map ../common/network ../common/network/packet_types ../common/ui ../common/utilities +INCLUDES+=. client_utilities scenes ../common/debugging ../common/gameplay ../common/graphics ../common/map ../common/network ../common/network/packet_types ../common/ui ../common/utilities LIBS+=client.a ../libcommon.a -lSDL_net -lwsock32 -liphlpapi -lmingw32 -lSDLmain -lSDL -llua CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) @@ -16,6 +16,7 @@ OUT=$(addprefix $(OUTDIR)/,client) #targets all: $(OBJ) $(OUT) + $(MAKE) -C client_utilities $(MAKE) -C scenes $(CXX) $(CXXFLAGS) -o $(OUT) $(OBJ) $(LIBS) diff --git a/client/scenes/lobby_menu.cpp b/client/scenes/lobby_menu.cpp index 92ce5d1..7346f45 100644 --- a/client/scenes/lobby_menu.cpp +++ b/client/scenes/lobby_menu.cpp @@ -25,7 +25,6 @@ #include "utility.hpp" #include -#include //------------------------- //Public access members @@ -113,7 +112,7 @@ void LobbyMenu::Render(SDL_Surface* const screen) { (Uint16)listBox.w, (Uint16)listBox.h }; r.y += i * listBox.h; - SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 255, 127, 39)); + SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 49, 150, 5)); } //draw the server name @@ -226,8 +225,7 @@ void LobbyMenu::HandleJoinResponse(ClientPacket* const argPacket) { } void LobbyMenu::HandleJoinRejection(TextPacket* const argPacket) { - //TODO: Better output - std::cerr << "Error: " << argPacket->text << std::endl; + //TODO: Better output for join rejection } //------------------------- diff --git a/common/utilities/config_utility.cpp b/common/utilities/config_utility.cpp index f761570..e6f9f39 100644 --- a/common/utilities/config_utility.cpp +++ b/common/utilities/config_utility.cpp @@ -22,14 +22,61 @@ #include "config_utility.hpp" #include +#include #include +#include #include -void ConfigUtility::Load(std::string fname) { +void ConfigUtility::Load(std::string fname, int argc, char* argv[]) { //clear the stored configuration configMap.clear(); - //pass to the recursive method - configMap = Read(fname); + + //use the default file + if (argc < 2) { + configMap = Read(fname); + return; + } + + //some variables to use + table_t redirectedFile; + table_t cmdLineParams; + char key[256], val[256]; + bool redirectUsed = false; + + //reading from the command line + for (int i = 1; i < argc; ++i) { + //read from a specified config file + if (!strncmp(argv[i], "-config=", 8)) { + redirectedFile = Read(argv[i] + 8); + redirectUsed = true; + continue; + } + + //set some specific values + if (!strncmp(argv[i], "-C", 2)) { + //wipe the variables + memset(key, 0, 256); + memset(key, 0, 256); + + //read the key-value pair + if (sscanf(argv[i], "-C%[^=]=%[^\0]", key, val) != 2) { + std::ostringstream os; + os << "Failed to read a command line config argument (expected -C%s=%s):" << std::endl; + os << "\targv[" << i << "]: " << argv[i] << std::endl; + os << "\tkey: " << key << std::endl; + os << "\tval: " << val << std::endl; + throw(std::runtime_error( os.str() )); + } + cmdLineParams[key] = val; + } + } + + //finally, construct the final config table + if (!redirectUsed) { + redirectedFile = Read(fname); + } + configMap.insert(cmdLineParams.begin(), cmdLineParams.end()); + configMap.insert(redirectedFile.begin(), redirectedFile.end()); } ConfigUtility::table_t ConfigUtility::Read(std::string fname) { @@ -38,10 +85,9 @@ ConfigUtility::table_t ConfigUtility::Read(std::string fname) { std::ifstream is(fname); if (!is.is_open()) { - std::string msg; - msg += "Failed to open a config file: "; - msg += fname; - throw(std::runtime_error(msg)); + std::ostringstream os; + os << "Failed to open a config file: " << fname; + throw(std::runtime_error( os.str() )); } std::string key, val; @@ -69,15 +115,23 @@ ConfigUtility::table_t ConfigUtility::Read(std::string fname) { getline(is, key,'='); getline(is, val); - //trim the strings at the start & end - while(key.size() && isspace(*key.begin())) key.erase(0, 1); - while(val.size() && isspace(*val.begin())) val.erase(0, 1); + //eat the whitespace at the start & end + while(key.size() && isspace( *key.begin() )) { + key.erase(0, 1); + } + while(val.size() && isspace( *val.begin() )) { + val.erase(0, 1); + } - while(key.size() && isspace(*(key.end()-1))) key.erase(key.end() - 1); - while(val.size() && isspace(*(val.end()-1))) val.erase(val.end() - 1); + while(key.size() && isspace( *(key.end()-1) )) { + key.erase(key.end() - 1); + } + while(val.size() && isspace( *(val.end()-1) )) { + val.erase(val.end() - 1); + } - //disallow empty/wiped values - if (key.size() == 0) { + //disallow empty/wiped pairs + if (key.size() == 0 || val.size() == 0) { continue; } diff --git a/common/utilities/config_utility.hpp b/common/utilities/config_utility.hpp index 8b85528..fd6fb99 100644 --- a/common/utilities/config_utility.hpp +++ b/common/utilities/config_utility.hpp @@ -29,7 +29,7 @@ class ConfigUtility : public Singleton { public: - void Load(std::string fname); + void Load(std::string fname, int argc = 0, char* argv[] = nullptr); //convert to a type std::string& String(std::string); diff --git a/rsc/scripts/setup_server.lua b/rsc/scripts/setup_server.lua index 6396596..ab851c1 100644 --- a/rsc/scripts/setup_server.lua +++ b/rsc/scripts/setup_server.lua @@ -34,7 +34,7 @@ function islandGenerator(region) end --Get some regions ---BUG: The server fails without at least one room +--BUG: #35 The server fails without at least one room --TODO: Create rooms with names? newRoom = RoomManager.CreateRoom() pager = Room.GetPager(newRoom) diff --git a/rsc/scripts/setup_server.sql b/rsc/scripts/setup_server.sql index 88d0a82..80da6fe 100644 --- a/rsc/scripts/setup_server.sql +++ b/rsc/scripts/setup_server.sql @@ -1,33 +1,51 @@ ---TODO: why is the database setup script scripted, while accessing, etc. hardcoded? ---there should be a way to control the database more directly ---TODO: move this script into a hardocded Init() method? - CREATE TABLE IF NOT EXISTS Accounts ( - uid INTEGER PRIMARY KEY AUTOINCREMENT, - username varchar(100) UNIQUE, + uid INTEGER PRIMARY KEY AUTOINCREMENT, + username varchar(100) UNIQUE, + --TODO: server-client security --- password varchar(100), - blacklisted BIT DEFAULT 0, - whitelisted BIT DEFAULT 1, - mod BIT DEFAULT 0, - admin BIT DEFAULT 0 +-- passhash varchar(100), +-- passsalt varchar(100), + + --server controls + blacklisted BIT DEFAULT 0, + whitelisted BIT DEFAULT 1, + mod BIT DEFAULT 0, + admin BIT DEFAULT 0 ); CREATE TABLE IF NOT EXISTS Characters ( - uid INTEGER PRIMARY KEY AUTOINCREMENT, + uid INTEGER PRIMARY KEY AUTOINCREMENT, --metadata - owner INTEGER REFERENCES Accounts(uid), - handle varchar(100) UNIQUE, - avatar varchar(100), - birth timestamp NOT NULL DEFAULT (datetime()), + owner INTEGER REFERENCES Accounts(uid), + handle varchar(100) UNIQUE, + avatar varchar(100), + birth timestamp NOT NULL DEFAULT (datetime()), - --position + --position in the world roomIndex INTEGER DEFAULT 0, originX INTEGER DEFAULT 0, originY INTEGER DEFAULT 0, --statistics + baseStats INTEGER REFERENCES StatisticSets(uid), + + --equipment + weapon INTEGER REFERENCES WornEquipment(uid), + helmet INTEGER REFERENCES WornEquipment(uid), + armour INTEGER REFERENCES WornEquipment(uid) + --etc. +); + +------------------------- +--Utility tables +------------------------- + +CREATE TABLE IF NOT EXISTS StatisticSets ( + --metadata + uid INTEGER PRIMARY KEY AUTOINCREMENT, + + --general use statistics level INTEGER DEFAULT 0, exp INTEGER DEFAULT 0, maxHP INTEGER DEFAULT 0, @@ -41,28 +59,45 @@ CREATE TABLE IF NOT EXISTS Characters ( speed INTEGER DEFAULT 0, accuracy REAL DEFAULT 0.0, evasion REAL DEFAULT 0.0, - luck REAL DEFAULT 0.0, + luck REAL DEFAULT 0.0 +); - --equipment - weapon INTEGER REFERENCES WornEquipment(uid), - helmet INTEGER REFERENCES WornEquipment(uid), - armour INTEGER REFERENCES WornEquipment(uid) - --etc. +CREATE TABLE IF NOT EXISTS InWorldItems ( + --metadata + uid INTEGER PRIMARY KEY AUTOINCREMENT, + itemType INTEGER, + + --position in the world + roomIndex INTEGER DEFAULT 0, + originX INTEGER DEFAULT 0, + originY INTEGER DEFAULT 0, + + --unique information + stackSize INTEGER DEFAULT 0, + durability INTEGER DEFAULT 0, + stats INTEGER REFERENCES StatisticSets(uid) ); CREATE TABLE IF NOT EXISTS InventoryItems ( --metadata - uid INTEGER PRIMARY KEY AUTOINCREMENT, - itemID INTEGER, --type - stackSize INTEGER DEFAULT 0, - owner INTEGER REFERENCES Characters(uid) + uid INTEGER PRIMARY KEY AUTOINCREMENT, + owner INTEGER REFERENCES Characters(uid), + itemType INTEGER, + + --unique information + stackSize INTEGER DEFAULT 0, + durability INTEGER DEFAULT 0, + stats INTEGER REFERENCES StatisticSets(uid) ); CREATE TABLE IF NOT EXISTS WornEquipment ( --metadata - uid INTEGER PRIMARY KEY AUTOINCREMENT, - itemID INTEGER, --type - owner INTEGER REFERENCES Characters(uid) - --hold all equipment info - --stat mods, special effects, etc. -); \ No newline at end of file + uid INTEGER PRIMARY KEY AUTOINCREMENT, + owner INTEGER REFERENCES Characters(uid), + itemType INTEGER, + + --unique information + durability INTEGER DEFAULT 0, + stats INTEGER REFERENCES StatisticSets(uid) + --TODO: attached script? +); diff --git a/server/characters/character_manager.cpp b/server/characters/character_manager.cpp index f24378f..4d2078a 100644 --- a/server/characters/character_manager.cpp +++ b/server/characters/character_manager.cpp @@ -31,27 +31,7 @@ static const char* CREATE_CHARACTER = "INSERT INTO Characters (owner, handle, avatar) VALUES (?, ?, ?);"; static const char* LOAD_CHARACTER = "SELECT * FROM Characters WHERE handle = ?;"; - -static const char* SAVE_CHARACTER = "UPDATE OR FAIL Characters SET " - "roomIndex = ?2," - "originX = ?3," - "originY = ?4," - "level = ?5," - "exp = ?6," - "maxHP = ?7," - "health = ?8," - "maxMP = ?9," - "mana = ?10," - "attack = ?11," - "defence = ?12," - "intelligence = ?13," - "resistance = ?14," - "speed = ?15," - "accuracy = ?16," - "evasion = ?17," - "luck = ?18" -" WHERE uid = ?1;"; - +static const char* SAVE_CHARACTER = "UPDATE OR FAIL Characters SET roomIndex = ?2, originX = ?3, originY = ?4 WHERE uid = ?1;"; static const char* DELETE_CHARACTER = "DELETE FROM Characters WHERE uid = ?;"; //------------------------- @@ -141,22 +121,6 @@ int CharacterManager::LoadCharacter(int owner, std::string handle, std::string a newChar.origin.x = (double)sqlite3_column_int(statement, 6); newChar.origin.y = (double)sqlite3_column_int(statement, 7); - //statistics - newChar.baseStats.level = sqlite3_column_int(statement, 8); - newChar.baseStats.exp = sqlite3_column_int(statement, 9); - newChar.baseStats.maxHP = sqlite3_column_int(statement, 10); - newChar.baseStats.health = sqlite3_column_int(statement, 11); - newChar.baseStats.maxMP = sqlite3_column_int(statement, 12); - newChar.baseStats.mana = sqlite3_column_int(statement, 13); - newChar.baseStats.attack = sqlite3_column_int(statement, 14); - newChar.baseStats.defence = sqlite3_column_int(statement, 15); - newChar.baseStats.intelligence = sqlite3_column_int(statement, 16); - newChar.baseStats.resistance = sqlite3_column_int(statement, 17); - newChar.baseStats.speed = sqlite3_column_int(statement, 18); - newChar.baseStats.accuracy = sqlite3_column_double(statement, 19); - newChar.baseStats.evasion = sqlite3_column_double(statement, 20); - newChar.baseStats.luck = sqlite3_column_double(statement, 21); - //gameplay components: equipment, items, buffs, debuffs... //finish the routine @@ -198,22 +162,6 @@ int CharacterManager::SaveCharacter(int uid) { ret |= sqlite3_bind_int(statement, 3, (int)character.origin.x) != SQLITE_OK; ret |= sqlite3_bind_int(statement, 4, (int)character.origin.y) != SQLITE_OK; - //statistics - ret |= sqlite3_bind_int(statement, 5, character.baseStats.level) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 6, character.baseStats.exp) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 7, character.baseStats.maxHP) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 8, character.baseStats.health) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 9, character.baseStats.maxMP) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 10, character.baseStats.mana) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 11, character.baseStats.attack) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 12, character.baseStats.defence) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 13, character.baseStats.intelligence) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 14, character.baseStats.resistance) != SQLITE_OK; - ret |= sqlite3_bind_int(statement, 15, character.baseStats.speed) != SQLITE_OK; - ret |= sqlite3_bind_double(statement, 16, character.baseStats.accuracy) != SQLITE_OK; - ret |= sqlite3_bind_double(statement, 17, character.baseStats.evasion) != SQLITE_OK; - ret |= sqlite3_bind_double(statement, 18, character.baseStats.luck) != SQLITE_OK; - //gameplay components: equipment, items, buffs, debuffs... //check for binding errors diff --git a/server/main.cpp b/server/main.cpp index 8987d26..4cca4da 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -30,7 +30,7 @@ using namespace std; -int main(int argc, char** argv) { +int main(int argc, char* argv[]) { try { //create the singletons AccountManager::Create(); diff --git a/server/makefile b/server/makefile index e3d7530..c8c5ef2 100644 --- a/server/makefile +++ b/server/makefile @@ -1,5 +1,5 @@ #config -INCLUDES+=. accounts characters rooms ../common/debugging ../common/gameplay ../common/map ../common/network ../common/network/packet_types ../common/utilities +INCLUDES+=. accounts characters rooms server_utilities ../common/debugging ../common/gameplay ../common/map ../common/network ../common/network/packet_types ../common/utilities LIBS+=server.a ../libcommon.a -lSDL_net -lwsock32 -liphlpapi -lmingw32 -lSDLmain -lSDL -llua -lsqlite3 CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) @@ -19,6 +19,7 @@ all: $(OBJ) $(OUT) $(MAKE) -C accounts $(MAKE) -C characters $(MAKE) -C rooms + $(MAKE) -C server_utilities $(CXX) $(CXXFLAGS) -o $(OUT) $(OBJ) $(LIBS) $(OBJ): | $(OBJDIR) diff --git a/server/server_application.hpp b/server/server_application.hpp index 105cffe..3337588 100644 --- a/server/server_application.hpp +++ b/server/server_application.hpp @@ -47,7 +47,7 @@ class ServerApplication: public Singleton { public: //public methods - void Init(int argc, char** argv); + void Init(int argc, char* argv[]); void Proc(); void Quit(); diff --git a/server/server_logic.cpp b/server/server_logic.cpp index b770cdf..51ed785 100644 --- a/server/server_logic.cpp +++ b/server/server_logic.cpp @@ -34,12 +34,12 @@ //public methods //------------------------- -void ServerApplication::Init(int argc, char** argv) { +void ServerApplication::Init(int argc, char* argv[]) { //NOTE: I might need to rearrange the init process so that lua & SQL can interact with the map system as needed. std::cout << "Beginning " << argv[0] << std::endl; //load the prerequisites - config.Load("rsc\\config.cfg"); + config.Load("rsc\\config.cfg", argc, argv); //------------------------- //Initialize the APIs @@ -146,7 +146,7 @@ void ServerApplication::Proc() { HandlePacket(packetBuffer); } //update the internals - //BUG: #30 Update the internals i.e. player positions + //... //TODO: This could be checked only every few seconds //Check connections diff --git a/server/server_methods.cpp b/server/server_methods.cpp index 2cc3b31..bf77776 100644 --- a/server/server_methods.cpp +++ b/server/server_methods.cpp @@ -170,9 +170,6 @@ void ServerApplication::HandleRegionRequest(RegionPacket* const argPacket) { //------------------------- void ServerApplication::HandleCharacterNew(CharacterPacket* const argPacket) { - //BUG: #27 Characters can be created with an invalid account index - //TODO: Make sure that a character's owner's account is loaded before continuing - //NOTE: misnomer, try to load the character first int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar); @@ -285,6 +282,7 @@ void ServerApplication::HandleSynchronize(ClientPacket* const argPacket) { void ServerApplication::CleanupLostConnection(int clientIndex) { //NOTE: This assumes each player has only one account and character at a time + //TODO: handle multiple characters (bots, etc.) //find the account int accountIndex = -1; @@ -304,7 +302,7 @@ void ServerApplication::CleanupLostConnection(int clientIndex) { } } - //send a dissconnection message just in case + //send a disconnection message just in case ClientPacket newPacket; newPacket.type = SerialPacketType::DISCONNECT; network.SendTo(clientMap[clientIndex].GetAddress(), &newPacket); diff --git a/server/server_utilities/makefile b/server/server_utilities/makefile new file mode 100644 index 0000000..f24e1ba --- /dev/null +++ b/server/server_utilities/makefile @@ -0,0 +1,37 @@ +#config +INCLUDES+=. ../../common/utilities +LIBS+= +CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES)) + +#source +CXXSRC=$(wildcard *.cpp) + +#objects +OBJDIR=obj +OBJ+=$(addprefix $(OBJDIR)/,$(CXXSRC:.cpp=.o)) + +#output +OUTDIR=.. +OUT=$(addprefix $(OUTDIR)/,server.a) + +#targets +all: $(OBJ) $(OUT) + ar -crs $(OUT) $(OBJ) + +$(OBJ): | $(OBJDIR) + +$(OUT): | $(OUTDIR) + +$(OBJDIR): + mkdir $(OBJDIR) + +$(OUTDIR): + mkdir $(OUTDIR) + +$(OBJDIR)/%.o: %.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +clean: + $(RM) *.o *.a *.exe + +rebuild: clean all diff --git a/common/utilities/sql_utility.cpp b/server/server_utilities/sql_utility.cpp similarity index 100% rename from common/utilities/sql_utility.cpp rename to server/server_utilities/sql_utility.cpp diff --git a/common/utilities/sql_utility.hpp b/server/server_utilities/sql_utility.hpp similarity index 100% rename from common/utilities/sql_utility.hpp rename to server/server_utilities/sql_utility.hpp diff --git a/todo.txt b/todo.txt index 2098c57..e4c83ea 100644 --- a/todo.txt +++ b/todo.txt @@ -1,9 +1,5 @@ -TODO: Config switch for the debug output -TODO: A better way of handling the disconnection message -TODO: LobbyMenu::HandleJoinRejection() TODO: Get the rooms working, even if only via hotkeys TODO: Fix shoddy movement -TODO: Move the statistics into their own SQL table, instead of duplicating the structure a dozen times TODO: Remove the big "Shut Down" button TODO: Make a way for the server owner to control the server directly @@ -13,5 +9,4 @@ TODO: make the whole thing more fault tolerant TODO: Authentication TODO: Time delay for requesting region packets -TODO: command line parameters overriding config.cfg settings TODO: A proper logging system