diff --git a/client/character.hpp b/client/character.hpp index 23924a4..6be3cc6 100644 --- a/client/character.hpp +++ b/client/character.hpp @@ -75,7 +75,7 @@ private: //base statistics Statistics stats; - //TODO: gameplay components: equipment, items, buffs, debuffs + //gameplay components: equipment, items, buffs, debuffs... //metadata int owner; diff --git a/client/client_application.cpp b/client/client_application.cpp index 3c5ae4c..62e4d25 100644 --- a/client/client_application.cpp +++ b/client/client_application.cpp @@ -89,6 +89,7 @@ void ClientApplication::Init(int argc, char** argv) { std::cout << "Internal sizes:" << std::endl; + DEBUG_OUTPUT_VAR(NETWORK_VERSION); DEBUG_OUTPUT_VAR(sizeof(Region::type_t)); DEBUG_OUTPUT_VAR(sizeof(Region)); DEBUG_OUTPUT_VAR(REGION_WIDTH); @@ -96,8 +97,10 @@ void ClientApplication::Init(int argc, char** argv) { DEBUG_OUTPUT_VAR(REGION_DEPTH); DEBUG_OUTPUT_VAR(REGION_TILE_FOOTPRINT); DEBUG_OUTPUT_VAR(REGION_SOLID_FOOTPRINT); + DEBUG_OUTPUT_VAR(PACKET_STRING_SIZE); DEBUG_OUTPUT_VAR(PACKET_BUFFER_SIZE); DEBUG_OUTPUT_VAR(MAX_PACKET_SIZE); + DEBUG_OUTPUT_VAR(static_cast(SerialPacketType::LAST)); #undef DEBUG_OUTPUT_VAR diff --git a/client/scenes/clean_up.cpp b/client/scenes/clean_up.cpp index 1be9e5c..54ced25 100644 --- a/client/scenes/clean_up.cpp +++ b/client/scenes/clean_up.cpp @@ -85,13 +85,15 @@ void CleanUp::Update() { SetNextScene(SceneList::MAINMENU); } - //BUGFIX: Eat incoming packets + //Eat incoming packets while(network.Receive()); } void CleanUp::Render(SDL_Surface* const screen) { + ConfigUtility& config = ConfigUtility::GetSingleton(); + backButton.DrawTo(screen); - font.DrawStringTo("You have been disconnected.", screen, 50, 30); + font.DrawStringTo(config["client.disconnectMessage"], screen, 50, 30); } //------------------------- diff --git a/client/scenes/in_world.cpp b/client/scenes/in_world.cpp index d8b0d35..78cb8d8 100644 --- a/client/scenes/in_world.cpp +++ b/client/scenes/in_world.cpp @@ -73,6 +73,14 @@ InWorld::InWorld( //TODO: Tile size and tile sheet should be loaded elsewhere tileSheet.Load(config["dir.tilesets"] + "terrain.bmp", 32, 32); + //send this player's character info + CharacterPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_NEW; + strncpy(newPacket.handle, config["client.handle"].c_str(), PACKET_STRING_SIZE); + strncpy(newPacket.avatar, config["client.avatar"].c_str(), PACKET_STRING_SIZE); + newPacket.accountIndex = accountIndex; + network.SendTo(Channels::SERVER, &newPacket); + //request a sync RequestSynchronize(); @@ -142,9 +150,11 @@ void InWorld::Update() { camera.y = localCharacter->GetOrigin().y - camera.marginY; //check the connection - if (Clock::now() - lastBeat > std::chrono::seconds(5)) { + if (Clock::now() - lastBeat > std::chrono::seconds(3)) { if (attemptedBeats > 2) { - throw(std::runtime_error("Connection lost")); + RequestDisconnect(); + SetNextScene(SceneList::CLEANUP); + ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server"; } ServerPacket newPacket; @@ -302,6 +312,9 @@ void InWorld::HandlePacket(SerialPacket* const argPacket) { case SerialPacketType::CHARACTER_UPDATE: HandleCharacterUpdate(static_cast(argPacket)); break; + case SerialPacketType::CHARACTER_REJECTION: + HandleCharacterRejection(static_cast(argPacket)); + break; case SerialPacketType::REGION_CONTENT: HandleRegionContent(static_cast(argPacket)); break; @@ -330,6 +343,7 @@ void InWorld::HandlePong(ServerPacket* const argPacket) { void InWorld::HandleDisconnect(ClientPacket* const argPacket) { //TODO: More needed in the disconnection SetNextScene(SceneList::CLEANUP); + ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have been disconnected"; } void InWorld::HandleCharacterNew(CharacterPacket* const argPacket) { @@ -366,7 +380,6 @@ void InWorld::HandleCharacterNew(CharacterPacket* const argPacket) { localCharacter = &newCharacter; //setup the camera - //TODO: move this? camera.width = GetScreen()->w; camera.height = GetScreen()->h; @@ -390,7 +403,6 @@ void InWorld::HandleCharacterDelete(CharacterPacket* const argPacket) { void InWorld::HandleCharacterUpdate(CharacterPacket* const argPacket) { if (characterMap.find(argPacket->characterIndex) == characterMap.end()) { - std::cout << "Warning: HandleCharacterUpdate() is passing to HandleCharacterNew()" << std::endl; HandleCharacterNew(argPacket); return; } @@ -405,6 +417,14 @@ void InWorld::HandleCharacterUpdate(CharacterPacket* const argPacket) { } } +void InWorld::HandleCharacterRejection(TextPacket* const argPacket) { + RequestDisconnect(); + SetNextScene(SceneList::CLEANUP); + ConfigUtility& config = ConfigUtility::GetSingleton(); + config["client.disconnectMessage"] = "Error: "; + config["client.disconnectMessage"] += argPacket->text; +} + void InWorld::HandleRegionContent(RegionPacket* const argPacket) { //replace existing regions regionPager.UnloadRegion(argPacket->x, argPacket->y); diff --git a/client/scenes/in_world.hpp b/client/scenes/in_world.hpp index 15fcdce..d80659d 100644 --- a/client/scenes/in_world.hpp +++ b/client/scenes/in_world.hpp @@ -83,6 +83,7 @@ protected: void HandleCharacterNew(CharacterPacket* const); void HandleCharacterDelete(CharacterPacket* const); void HandleCharacterUpdate(CharacterPacket* const); + void HandleCharacterRejection(TextPacket* const); void HandleRegionContent(RegionPacket* const); //Server control @@ -113,7 +114,8 @@ protected: //UI Button disconnectButton; Button shutDownButton; - //TODO: Review the camera + + //the camera structure struct { int x = 0, y = 0; int width = 0, height = 0; @@ -125,6 +127,7 @@ protected: Character* localCharacter = nullptr; //connections + //TODO: This needs it's own utility, for both InWorld and InCombat typedef std::chrono::steady_clock Clock; Clock::time_point lastBeat = Clock::now(); int attemptedBeats = 0; diff --git a/client/scenes/lobby_menu.cpp b/client/scenes/lobby_menu.cpp index 88d09e6..92ce5d1 100644 --- a/client/scenes/lobby_menu.cpp +++ b/client/scenes/lobby_menu.cpp @@ -25,6 +25,7 @@ #include "utility.hpp" #include +#include //------------------------- //Public access members @@ -63,7 +64,7 @@ LobbyMenu::LobbyMenu(int* const argClientIndex, int* const argAccountIndex): //set the server list's position listBox = {300, 50, 200, font.GetCharH()}; - //BUGFIX: Eat incoming packets + //Eat incoming packets while(network.Receive()); //Initial broadcast @@ -192,6 +193,9 @@ void LobbyMenu::HandlePacket(SerialPacket* const argPacket) { case SerialPacketType::JOIN_RESPONSE: HandleJoinResponse(static_cast(argPacket)); break; + case SerialPacketType::JOIN_REJECTION: + HandleJoinRejection(static_cast(argPacket)); + break; //handle errors default: throw(std::runtime_error(std::string() + "Unknown SerialPacketType encountered in LobbyMenu: " + to_string_custom(static_cast(argPacket->type)) )); @@ -219,14 +223,11 @@ void LobbyMenu::HandleJoinResponse(ClientPacket* const argPacket) { accountIndex = argPacket->accountIndex; network.Bind(argPacket->srcAddress, Channels::SERVER); SetNextScene(SceneList::INWORLD); +} - //send this player's character info - CharacterPacket newPacket; - newPacket.type = SerialPacketType::CHARACTER_NEW; - strncpy(newPacket.handle, config["client.handle"].c_str(), PACKET_STRING_SIZE); - strncpy(newPacket.avatar, config["client.avatar"].c_str(), PACKET_STRING_SIZE); - newPacket.accountIndex = accountIndex; - network.SendTo(Channels::SERVER, &newPacket); +void LobbyMenu::HandleJoinRejection(TextPacket* const argPacket) { + //TODO: Better output + std::cerr << "Error: " << argPacket->text << std::endl; } //------------------------- diff --git a/client/scenes/lobby_menu.hpp b/client/scenes/lobby_menu.hpp index e8a5d1c..1959c56 100644 --- a/client/scenes/lobby_menu.hpp +++ b/client/scenes/lobby_menu.hpp @@ -63,6 +63,7 @@ protected: void HandlePacket(SerialPacket* const); void HandleBroadcastResponse(ServerPacket* const); void HandleJoinResponse(ClientPacket* const); + void HandleJoinRejection(TextPacket* const); //server control void SendBroadcastRequest(); diff --git a/common/network/packet_types/character_packet.cpp b/common/network/packet_types/character_packet.cpp index a66b330..7f6620c 100644 --- a/common/network/packet_types/character_packet.cpp +++ b/common/network/packet_types/character_packet.cpp @@ -46,7 +46,7 @@ void serializeCharacter(void* buffer, CharacterPacket* packet) { //stats structure serializeStatistics(&buffer, &packet->stats); - //TODO: gameplay components: equipment, items, buffs, debuffs + //gameplay components: equipment, items, buffs, debuffs... } void deserializeCharacter(void* buffer, CharacterPacket* packet) { @@ -70,5 +70,5 @@ void deserializeCharacter(void* buffer, CharacterPacket* packet) { //stats structure deserializeStatistics(&buffer, &packet->stats); - //TODO: gameplay components: equipment, items, buffs, debuffs + //gameplay components: equipment, items, buffs, debuffs... } diff --git a/common/network/packet_types/character_packet.hpp b/common/network/packet_types/character_packet.hpp index 72a6fc4..5d94c16 100644 --- a/common/network/packet_types/character_packet.hpp +++ b/common/network/packet_types/character_packet.hpp @@ -44,7 +44,7 @@ struct CharacterPacket : SerialPacketBase { //gameplay Statistics stats; - //TODO: gameplay components: equipment, items, buffs, debuffs + //gameplay components: equipment, items, buffs, debuffs... }; void serializeCharacter(void* buffer, CharacterPacket* packet); diff --git a/common/network/packet_types/text_packet.cpp b/common/network/packet_types/text_packet.cpp new file mode 100644 index 0000000..fbe0d9f --- /dev/null +++ b/common/network/packet_types/text_packet.cpp @@ -0,0 +1,40 @@ +/* Copyright: (c) Kayne Ruse 2013, 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 "text_packet.hpp" + +#include "serial_utility.hpp" + +void serializeText(void* buffer, TextPacket* packet) { + serialCopy(&buffer, &packet->type, sizeof(SerialPacketType)); + + //content + serialCopy(&buffer, packet->name, PACKET_STRING_SIZE); + serialCopy(&buffer, packet->text, PACKET_STRING_SIZE); +} + +void deserializeText(void* buffer, TextPacket* packet) { + deserialCopy(&buffer, &packet->type, sizeof(SerialPacketType)); + + //content + deserialCopy(&buffer, packet->name, PACKET_STRING_SIZE); + deserialCopy(&buffer, packet->text, PACKET_STRING_SIZE); +} \ No newline at end of file diff --git a/common/network/packet_types/text_packet.hpp b/common/network/packet_types/text_packet.hpp new file mode 100644 index 0000000..8dfbe22 --- /dev/null +++ b/common/network/packet_types/text_packet.hpp @@ -0,0 +1,35 @@ +/* Copyright: (c) Kayne Ruse 2013, 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. +*/ +#ifndef TEXTPACKET_HPP_ +#define TEXTPACKET_HPP_ + +#include "serial_packet_base.hpp" + +struct TextPacket : SerialPacketBase { + char name[PACKET_STRING_SIZE]; + char text[PACKET_STRING_SIZE]; +}; + +void serializeText(void* buffer, TextPacket* packet); +void deserializeText(void* buffer, TextPacket* packet); + +#endif diff --git a/common/network/serial_packet.hpp b/common/network/serial_packet.hpp index c767f6a..7dd6c81 100644 --- a/common/network/serial_packet.hpp +++ b/common/network/serial_packet.hpp @@ -27,18 +27,20 @@ #include "client_packet.hpp" #include "region_packet.hpp" #include "server_packet.hpp" +#include "text_packet.hpp" //SerialPacketBase is defined in serial_packet_base.hpp typedef SerialPacketBase SerialPacket; //DOCS: NETWORK_VERSION is used to discern compatible servers and clients -constexpr int NETWORK_VERSION = 20140831; +constexpr int NETWORK_VERSION = 20140909; union MaxPacket { CharacterPacket a; ClientPacket b; RegionPacket c; ServerPacket d; + TextPacket e; }; constexpr int MAX_PACKET_SIZE = sizeof(MaxPacket); diff --git a/common/network/serial_packet_type.hpp b/common/network/serial_packet_type.hpp index cf9a4dd..1c517a8 100644 --- a/common/network/serial_packet_type.hpp +++ b/common/network/serial_packet_type.hpp @@ -52,7 +52,6 @@ enum class SerialPacketType { //Connecting to a server as a client JOIN_REQUEST, JOIN_RESPONSE, - JOIN_REJECTION, //client requests all information from the server SYNCHRONIZE, @@ -88,10 +87,23 @@ enum class SerialPacketType { CHARACTER_STATS_REQUEST, CHARACTER_STATS_RESPONSE, - //reject a character request + //------------------------- + //TextPacket + // name, text + //------------------------- + + //general speech + TEXT_BROADCAST, + + //rejection/error messages + SHUTDOWN_REJECTION, + JOIN_REJECTION, CHARACTER_REJECTION, + //------------------------- //not used + //------------------------- + LAST }; diff --git a/common/network/serial_utility.cpp b/common/network/serial_utility.cpp index 6f70999..6572887 100644 --- a/common/network/serial_utility.cpp +++ b/common/network/serial_utility.cpp @@ -26,6 +26,7 @@ #include "client_packet.hpp" #include "region_packet.hpp" #include "server_packet.hpp" +#include "text_packet.hpp" #include @@ -53,7 +54,6 @@ void serializePacket(void* buffer, SerialPacketBase* packet) { break; case SerialPacketType::JOIN_REQUEST: case SerialPacketType::JOIN_RESPONSE: - case SerialPacketType::JOIN_REJECTION: case SerialPacketType::SYNCHRONIZE: case SerialPacketType::DISCONNECT: case SerialPacketType::SHUTDOWN: @@ -68,9 +68,14 @@ void serializePacket(void* buffer, SerialPacketBase* packet) { case SerialPacketType::CHARACTER_UPDATE: case SerialPacketType::CHARACTER_STATS_REQUEST: case SerialPacketType::CHARACTER_STATS_RESPONSE: - case SerialPacketType::CHARACTER_REJECTION: serializeCharacter(buffer, static_cast(packet)); break; + case SerialPacketType::TEXT_BROADCAST: + case SerialPacketType::JOIN_REJECTION: + case SerialPacketType::SHUTDOWN_REJECTION: + case SerialPacketType::CHARACTER_REJECTION: + serializeText(buffer, static_cast(packet)); + break; } } @@ -88,7 +93,6 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) { break; case SerialPacketType::JOIN_REQUEST: case SerialPacketType::JOIN_RESPONSE: - case SerialPacketType::JOIN_REJECTION: case SerialPacketType::SYNCHRONIZE: case SerialPacketType::DISCONNECT: case SerialPacketType::SHUTDOWN: @@ -103,8 +107,13 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) { case SerialPacketType::CHARACTER_UPDATE: case SerialPacketType::CHARACTER_STATS_REQUEST: case SerialPacketType::CHARACTER_STATS_RESPONSE: - case SerialPacketType::CHARACTER_REJECTION: deserializeCharacter(buffer, static_cast(packet)); break; + case SerialPacketType::TEXT_BROADCAST: + case SerialPacketType::JOIN_REJECTION: + case SerialPacketType::SHUTDOWN_REJECTION: + case SerialPacketType::CHARACTER_REJECTION: + deserializeText(buffer, static_cast(packet)); + break; } } \ No newline at end of file diff --git a/common/network/udp_network_utility.cpp b/common/network/udp_network_utility.cpp index 6a2d243..e677854 100644 --- a/common/network/udp_network_utility.cpp +++ b/common/network/udp_network_utility.cpp @@ -26,7 +26,7 @@ #include -//BUGFIX: memset() is used before sending a packet to remove old data; you don't want to send sensitive data over the network +//NOTE: memset() is used before sending a packet to remove old data; you don't want to send sensitive data over the network //NOTE: don't confuse SerialPacketBase with UDPpacket void UDPNetworkUtility::Open(int port) { diff --git a/common/utilities/config_utility.cpp b/common/utilities/config_utility.cpp index e34a2db..f761570 100644 --- a/common/utilities/config_utility.cpp +++ b/common/utilities/config_utility.cpp @@ -88,7 +88,6 @@ ConfigUtility::table_t ConfigUtility::Read(std::string fname) { is.close(); //load in any subordinate config files - //TODO: Possibility of nesting config levels? if (retTable.find("config.next") != retTable.end()) { table_t subTable = Read(retTable["config.next"]); retTable.insert(subTable.begin(), subTable.end()); diff --git a/rsc/scripts/setup_server.lua b/rsc/scripts/setup_server.lua index 8b43720..6396596 100644 --- a/rsc/scripts/setup_server.lua +++ b/rsc/scripts/setup_server.lua @@ -17,7 +17,7 @@ tiles = { --custom generation systems here function islandGenerator(region) - io.write("Generating (", Region.GetX(region), ", ", Region.GetY(region), ")\n") +-- io.write("Generating (", Region.GetX(region), ", ", Region.GetY(region), ")\n") for i = 1, Region.GetWidth(region) do for j = 1, Region.GetHeight(region) do local dist = math.dist(0, 0, i + Region.GetX(region) -1, j + Region.GetY(region) -1) diff --git a/server/characters/character_manager.cpp b/server/characters/character_manager.cpp index baabac1..f24378f 100644 --- a/server/characters/character_manager.cpp +++ b/server/characters/character_manager.cpp @@ -157,7 +157,7 @@ int CharacterManager::LoadCharacter(int owner, std::string handle, std::string a newChar.baseStats.evasion = sqlite3_column_double(statement, 20); newChar.baseStats.luck = sqlite3_column_double(statement, 21); - //TODO: gameplay components: equipment, items, buffs, debuffs + //gameplay components: equipment, items, buffs, debuffs... //finish the routine sqlite3_finalize(statement); @@ -214,7 +214,7 @@ int CharacterManager::SaveCharacter(int uid) { ret |= sqlite3_bind_double(statement, 17, character.baseStats.evasion) != SQLITE_OK; ret |= sqlite3_bind_double(statement, 18, character.baseStats.luck) != SQLITE_OK; - //TODO: gameplay components: equipment, items, buffs, debuffs + //gameplay components: equipment, items, buffs, debuffs... //check for binding errors if (ret) { @@ -225,7 +225,7 @@ int CharacterManager::SaveCharacter(int uid) { 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)) ); + throw( std::runtime_error(std::string() + "Unknown SQL error when saving a character: " + sqlite3_errmsg(database)) ); } sqlite3_finalize(statement); @@ -258,7 +258,7 @@ void CharacterManager::DeleteCharacter(int uid) { 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)) ); + throw( std::runtime_error(std::string() + "Unknown SQL error when deleting a character: " + sqlite3_errmsg(database)) ); } //finish the routine diff --git a/server/server_logic.cpp b/server/server_logic.cpp index 281c5b2..b770cdf 100644 --- a/server/server_logic.cpp +++ b/server/server_logic.cpp @@ -110,6 +110,7 @@ void ServerApplication::Init(int argc, char** argv) { std::cout << "Internal sizes:" << std::endl; + DEBUG_OUTPUT_VAR(NETWORK_VERSION); DEBUG_OUTPUT_VAR(sizeof(Region::type_t)); DEBUG_OUTPUT_VAR(sizeof(Region)); DEBUG_OUTPUT_VAR(REGION_WIDTH); @@ -117,8 +118,10 @@ void ServerApplication::Init(int argc, char** argv) { DEBUG_OUTPUT_VAR(REGION_DEPTH); DEBUG_OUTPUT_VAR(REGION_TILE_FOOTPRINT); DEBUG_OUTPUT_VAR(REGION_SOLID_FOOTPRINT); + DEBUG_OUTPUT_VAR(PACKET_STRING_SIZE); DEBUG_OUTPUT_VAR(PACKET_BUFFER_SIZE); DEBUG_OUTPUT_VAR(MAX_PACKET_SIZE); + DEBUG_OUTPUT_VAR(static_cast(SerialPacketType::LAST)); #undef DEBUG_OUTPUT_VAR @@ -148,7 +151,7 @@ void ServerApplication::Proc() { //TODO: This could be checked only every few seconds //Check connections for (auto& it : clientMap) { - if (std::chrono::steady_clock::now() - it.second.GetLastBeat() > std::chrono::seconds(5)) { + if (std::chrono::steady_clock::now() - it.second.GetLastBeat() > std::chrono::seconds(3)) { ServerPacket newPacket; newPacket.type = SerialPacketType::PING; network.SendTo(it.second.GetAddress(), &newPacket); diff --git a/server/server_methods.cpp b/server/server_methods.cpp index bb3be96..2cc3b31 100644 --- a/server/server_methods.cpp +++ b/server/server_methods.cpp @@ -35,8 +35,6 @@ void ServerApplication::HandlePing(ServerPacket* const argPacket) { void ServerApplication::HandlePong(ServerPacket* const argPacket) { //find and update the specified client - - //BUGFIX: running multiple clients on one computer will result in matching host values; check the ports too for (auto& it : clientMap) { if (it.second.GetAddress().host == argPacket->srcAddress.host && it.second.GetAddress().port == argPacket->srcAddress.port @@ -60,16 +58,18 @@ void ServerApplication::HandleBroadcastRequest(ServerPacket* const argPacket) { } void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) { - //create the new client - ClientData newClient; - newClient.SetAddress(argPacket->srcAddress); - //load the user account //TODO: handle passwords int accountIndex = accountMgr.LoadAccount(argPacket->username, clientIndex); + + //Cannot load if (accountIndex < 0) { - //TODO: send rejection packet - std::cerr << "Error: Account already loaded: " << accountIndex << std::endl; + TextPacket newPacket; + newPacket.type = SerialPacketType::JOIN_REJECTION; + std::string msg = std::string() + "Account already loaded: " + argPacket->username; + memset(newPacket.name, 0, PACKET_STRING_SIZE); + strncpy(newPacket.text, msg.c_str(), PACKET_STRING_SIZE); //BUG: If the name is too long this would truncate it + network.SendTo(argPacket->srcAddress, static_cast(&newPacket)); return; } @@ -79,10 +79,14 @@ void ServerApplication::HandleJoinRequest(ClientPacket* const argPacket) { newPacket.clientIndex = clientIndex; newPacket.accountIndex = accountIndex; - network.SendTo(newClient.GetAddress(), static_cast(&newPacket)); + network.SendTo(argPacket->srcAddress, static_cast(&newPacket)); + + //register the client + ClientData newClient; + newClient.SetAddress(argPacket->srcAddress); + clientMap[clientIndex++] = newClient; //finished this routine - clientMap[clientIndex++] = newClient; std::cout << "New connection, " << clientMap.size() << " clients and " << accountMgr.GetContainer()->size() << " accounts total" << std::endl; } @@ -105,9 +109,9 @@ void ServerApplication::HandleDisconnect(ClientPacket* const argPacket) { ); //save and unload this account's characters - //pump the unload message to all remaining clients characterMgr.UnloadCharacterIf([&](std::map::iterator it) -> bool { if (argPacket->accountIndex == it->second.GetOwner()) { + //pump the unload message to all remaining clients PumpCharacterUnload(it->first); return true; } @@ -167,23 +171,32 @@ 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); - if (characterIndex == -1) { - //TODO: rejection packet - std::cerr << "Warning: Character already loaded" << std::endl; + //cannot load or create + if (characterIndex < 0) { + //build the error message + std::string msg; + if (characterIndex == -1) { + msg += "Character already loaded: "; + } + else if (characterIndex == -2) { + msg += "Character already exists: "; + } + msg += argPacket->handle; + + //create, fill and send the packet + TextPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_REJECTION; + memset(newPacket.name, 0, PACKET_STRING_SIZE); + strncpy(newPacket.text, msg.c_str(), PACKET_STRING_SIZE); + network.SendTo(argPacket->srcAddress, static_cast(&newPacket)); return; } - if (characterIndex == -2) { - //TODO: rejection packet - std::cerr << "Warning: Character already exists" << std::endl; - return; - } - - //TODO: Make sure that a character's owner's account is loaded before continuing - //send this new character to all clients CharacterPacket newPacket; newPacket.type = SerialPacketType::CHARACTER_NEW; @@ -198,9 +211,13 @@ void ServerApplication::HandleCharacterDelete(CharacterPacket* const argPacket) int characterIndex = characterMgr.LoadCharacter(argPacket->accountIndex, argPacket->handle, argPacket->avatar); //if this is not your character - if (characterIndex == -2) { - //TODO: rejection packet - std::cerr << "Warning: Character cannot be deleted" << std::endl; + if (characterIndex < 0 && characterMgr.GetCharacter(characterIndex)->GetOwner() != argPacket->accountIndex) { + //send the rejection packet + TextPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_REJECTION; + memset(newPacket.name, 0, PACKET_STRING_SIZE); + strncpy(newPacket.text, "Character cannot be deleted", PACKET_STRING_SIZE); + network.SendTo(argPacket->srcAddress, static_cast(&newPacket)); //unload an unneeded character if (characterIndex != -1) { @@ -223,8 +240,6 @@ void ServerApplication::HandleCharacterUpdate(CharacterPacket* const argPacket) //make a new character if this one doesn't exist if (!character) { - //this isn't normal - std::cerr << "Warning: HandleCharacterUpdate() is passing to HandleCharacterNew()" << std::endl; HandleCharacterNew(argPacket); return; } @@ -257,7 +272,6 @@ void ServerApplication::HandleSynchronize(ClientPacket* const argPacket) { newPacket.type = SerialPacketType::CHARACTER_UPDATE; for (auto& it : *characterMgr.GetContainer()) { - newPacket.characterIndex = it.first; CopyCharacterToPacket(&newPacket, it.first); network.SendTo(client.GetAddress(), static_cast(&newPacket)); } @@ -320,6 +334,7 @@ void ServerApplication::PumpPacket(SerialPacket* const argPacket) { void ServerApplication::PumpCharacterUnload(int uid) { //delete the client-side character(s) + //NOTE: This is a strange function CharacterPacket newPacket; newPacket.type = SerialPacketType::CHARACTER_DELETE; newPacket.characterIndex = uid; diff --git a/todo.txt b/todo.txt index e8af752..2098c57 100644 --- a/todo.txt +++ b/todo.txt @@ -1,9 +1,12 @@ -TODO: Rejection messages -TODO: The error handling is terrible -TODO: Move the statistics into their own SQL table, instead of duplicating the structure a dozen times +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 TODO: Move the map system into it's own namespace? TODO: The TileSheet class should implement the surface itself TODO: make the whole thing more fault tolerant