diff --git a/client/scenes/base_scene.cpp b/client/base_scene.cpp similarity index 100% rename from client/scenes/base_scene.cpp rename to client/base_scene.cpp diff --git a/client/scenes/base_scene.hpp b/client/base_scene.hpp similarity index 100% rename from client/scenes/base_scene.hpp rename to client/base_scene.hpp diff --git a/client/client_utilities/ip_operators.cpp b/client/client_utilities/ip_operators.cpp new file mode 100644 index 0000000..fe3ba48 --- /dev/null +++ b/client/client_utilities/ip_operators.cpp @@ -0,0 +1,30 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 "ip_operators.hpp" + +bool operator==(IPaddress lhs, IPaddress rhs) { + return lhs.host == rhs.host && lhs.port == rhs.port; +} + +bool operator!=(IPaddress lhs, IPaddress rhs) { + return !(lhs == rhs); +} \ No newline at end of file diff --git a/client/client_utilities/ip_operators.hpp b/client/client_utilities/ip_operators.hpp new file mode 100644 index 0000000..cdfff9e --- /dev/null +++ b/client/client_utilities/ip_operators.hpp @@ -0,0 +1,31 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 IPOPERATORS_HPP_ +#define IPOPERATORS_HPP_ + +#include "SDL/SDL_net.h" + +//these should've come standard +bool operator==(IPaddress lhs, IPaddress rhs); +bool operator!=(IPaddress lhs, IPaddress rhs); + +#endif \ No newline at end of file diff --git a/client/scenes/in_world.hpp b/client/gameplay_scenes/in_world.hpp similarity index 89% rename from client/scenes/in_world.hpp rename to client/gameplay_scenes/in_world.hpp index 7ce1288..7dfa1fc 100644 --- a/client/scenes/in_world.hpp +++ b/client/gameplay_scenes/in_world.hpp @@ -96,12 +96,18 @@ protected: void HandleCharacterCreate(CharacterPacket* const); void HandleCharacterDelete(CharacterPacket* const); void HandleCharacterQueryExists(CharacterPacket* const); - void HandleCharacterSetRoom(CharacterPacket* const); - void HandleCharacterSetOrigin(CharacterPacket* const); - void HandleCharacterSetMotion(CharacterPacket* const); + void HandleCharacterMovement(CharacterPacket* const); + void HandleCharacterAttack(CharacterPacket* const); + + //monster management + void HandleMonsterCreate(MonsterPacket* const); + void HandleMonsterDelete(MonsterPacket* const); + void HandleMonsterQueryExists(MonsterPacket* const); + void HandleMonsterMovement(MonsterPacket* const); + void HandleMonsterAttack(MonsterPacket* const); //player movement - void SendLocalCharacterMotion(); + void SendLocalCharacterMovement(); std::list GenerateCollisionGrid(Entity*, int tileWidth, int tileHeight); //indexes diff --git a/client/gameplay_scenes/in_world_entities.cpp b/client/gameplay_scenes/in_world_entities.cpp new file mode 100644 index 0000000..7fd7155 --- /dev/null +++ b/client/gameplay_scenes/in_world_entities.cpp @@ -0,0 +1,220 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 "in_world.hpp" + +#include "channels.hpp" + +#include +#include +#include + +//------------------------- +//character management +//------------------------- + +//DOCS: preexisting characters will result in query responses +//DOCS: new characters will result in create messages +//DOCS: this client's character will exist in both (skipped) + +void InWorld::HandleCharacterCreate(CharacterPacket* const argPacket) { + //prevent double message + if (characterMap.find(argPacket->characterIndex) != characterMap.end()) { + std::ostringstream msg; + msg << "Double character creation event; "; + msg << "Index: " << argPacket->characterIndex << "; "; + msg << "Handle: " << argPacket->handle; + throw(std::runtime_error(msg.str())); + } + + //implicity create and retrieve the entity + BaseCharacter* character = &characterMap[argPacket->characterIndex]; + + //fill the character's info + character->SetOrigin(argPacket->origin); + character->SetMotion(argPacket->motion); + character->SetBounds({CHARACTER_BOUNDS_X, CHARACTER_BOUNDS_Y, CHARACTER_BOUNDS_WIDTH, CHARACTER_BOUNDS_HEIGHT}); + character->SetHandle(argPacket->handle); + character->SetAvatar(argPacket->avatar); + character->SetOwner(argPacket->accountIndex); + character->CorrectSprite(); + + //check for this player's character + if (character->GetOwner() == accountIndex) { + localCharacter = static_cast(character); + + //focus the camera on this character + camera.marginX = (camera.width / 2 - localCharacter->GetSprite()->GetImage()->GetClipW() / 2); + camera.marginY = (camera.height/ 2 - localCharacter->GetSprite()->GetImage()->GetClipH() / 2); + + //focus on this character's info + characterIndex = argPacket->characterIndex; + roomIndex = argPacket->roomIndex; + } + + //debug + std::cout << "Create, total: " << characterMap.size() << std::endl; +} + +void InWorld::HandleCharacterDelete(CharacterPacket* const argPacket) { + //ignore if this character doesn't exist + std::map::iterator characterIt = characterMap.find(argPacket->characterIndex); + if (characterIt == characterMap.end()) { + return; + } + + //check for this player's character + if ((*characterIt).second.GetOwner() == accountIndex) { + localCharacter = nullptr; + + //clear the camera + camera.marginX = 0; + camera.marginY = 0; + + //clear the room + roomIndex = -1; + } + + //remove this character + characterMap.erase(characterIt); + + //debug + std::cout << "Delete, total: " << characterMap.size() << std::endl; +} + +void InWorld::HandleCharacterQueryExists(CharacterPacket* const argPacket) { + //prevent a double message about this player's character + if (argPacket->accountIndex == accountIndex) { + return; + } + + //ignore characters in a different room (sub-optimal) + if (argPacket->roomIndex != roomIndex) { + return; + } + + //implicitly construct the character if it doesn't exist + BaseCharacter* character = &characterMap[argPacket->characterIndex]; + + //set/update the character's info + character->SetOrigin(argPacket->origin); + character->SetMotion(argPacket->motion); + character->SetBounds({CHARACTER_BOUNDS_X, CHARACTER_BOUNDS_Y, CHARACTER_BOUNDS_WIDTH, CHARACTER_BOUNDS_HEIGHT}); + character->SetHandle(argPacket->handle); + character->SetAvatar(argPacket->avatar); + character->SetOwner(argPacket->accountIndex); + character->CorrectSprite(); + + //debug + std::cout << "Query, total: " << characterMap.size() << std::endl; +} + +void InWorld::HandleCharacterMovement(CharacterPacket* const argPacket) { + //TODO: Authentication + if (argPacket->characterIndex == characterIndex) { + return; + } + + //check that this character exists + std::map::iterator characterIt = characterMap.find(argPacket->characterIndex); + if (characterIt != characterMap.end()) { + //set the origin and motion + characterIt->second.SetOrigin(argPacket->origin); + characterIt->second.SetMotion(argPacket->motion); + characterIt->second.CorrectSprite(); + } +} + +void InWorld::HandleCharacterAttack(CharacterPacket* const argPacket) { + //TODO: attack animation +} + +//------------------------- +//monster management +//------------------------- + +void InWorld::HandleMonsterCreate(MonsterPacket* const argPacket) { + //TODO +} + +void InWorld::HandleMonsterDelete(MonsterPacket* const argPacket) { + //TODO +} + +void InWorld::HandleMonsterQueryExists(MonsterPacket* const argPacket) { + //TODO +} + +void InWorld::HandleMonsterMovement(MonsterPacket* const argPacket) { + //TODO +} + +void InWorld::HandleMonsterAttack(MonsterPacket* const argPacket) { + //TODO +} + +//------------------------- +//player movement +//------------------------- + +//TODO: add a "movement" packet type +void InWorld::SendLocalCharacterMovement() { + CharacterPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_MOVEMENT; + + newPacket.accountIndex = accountIndex; + newPacket.characterIndex = characterIndex; + newPacket.roomIndex = roomIndex; + newPacket.origin = localCharacter->GetOrigin(); + newPacket.motion = localCharacter->GetMotion(); + + network.SendTo(Channels::SERVER, &newPacket); +} + +std::list InWorld::GenerateCollisionGrid(Entity* ptr, int tileWidth, int tileHeight) { + //prepare for collisions + BoundingBox wallBounds = {0, 0, tileWidth, tileHeight}; + std::list boxList; + + //NOTE: for loops were too dense to work with, so I've just used while loops + + //outer loop + wallBounds.x = snapToBase((double)wallBounds.w, ptr->GetOrigin().x); + while(wallBounds.x < (ptr->GetOrigin() + ptr->GetBounds()).x + ptr->GetBounds().w) { + //inner loop + wallBounds.y = snapToBase((double)wallBounds.h, ptr->GetOrigin().y); + while(wallBounds.y < (ptr->GetOrigin() + ptr->GetBounds()).y + ptr->GetBounds().h) { + //check to see if this tile is solid + if (regionPager.GetSolid(wallBounds.x / wallBounds.w, wallBounds.y / wallBounds.h)) { + //push onto the box set + boxList.push_front(wallBounds); + } + + //increment + wallBounds.y += wallBounds.h; + } + + //increment + wallBounds.x += wallBounds.w; + } + + return std::move(boxList); +} \ No newline at end of file diff --git a/client/gameplay_scenes/in_world_networking.cpp b/client/gameplay_scenes/in_world_networking.cpp new file mode 100644 index 0000000..db23ce1 --- /dev/null +++ b/client/gameplay_scenes/in_world_networking.cpp @@ -0,0 +1,264 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 "in_world.hpp" + +#include "channels.hpp" +#include "ip_operators.hpp" +#include "terminal_error.hpp" + +#include +#include +#include + +//------------------------- +//Basic connections +//------------------------- + +void InWorld::HandlePacket(SerialPacket* const argPacket) { + switch(argPacket->type) { + //heartbeat system + case SerialPacketType::PING: + HandlePing(static_cast(argPacket)); + break; + case SerialPacketType::PONG: + HandlePong(static_cast(argPacket)); + break; + + //game server connections + case SerialPacketType::LOGOUT_RESPONSE: + HandleLogoutResponse(static_cast(argPacket)); + break; + case SerialPacketType::DISCONNECT_RESPONSE: + HandleDisconnectResponse(static_cast(argPacket)); + break; + case SerialPacketType::DISCONNECT_FORCED: + HandleDisconnectForced(static_cast(argPacket)); + break; + + //map management + case SerialPacketType::REGION_CONTENT: + HandleRegionContent(static_cast(argPacket)); + break; + + //character management + case SerialPacketType::CHARACTER_CREATE: + HandleCharacterCreate(static_cast(argPacket)); + break; + case SerialPacketType::CHARACTER_DELETE: + HandleCharacterDelete(static_cast(argPacket)); + break; + case SerialPacketType::QUERY_CHARACTER_EXISTS: + HandleCharacterQueryExists(static_cast(argPacket)); + break; + case SerialPacketType::CHARACTER_MOVEMENT: + HandleCharacterMovement(static_cast(argPacket)); + break; + case SerialPacketType::CHARACTER_ATTACK: + HandleCharacterAttack(static_cast(argPacket)); + break; + + //monster management + case SerialPacketType::MONSTER_CREATE: + HandleMonsterCreate(static_cast(argPacket)); + break; + case SerialPacketType::MONSTER_DELETE: + HandleMonsterDelete(static_cast(argPacket)); + break; + case SerialPacketType::QUERY_MONSTER_EXISTS: + HandleMonsterQueryExists(static_cast(argPacket)); + break; + case SerialPacketType::MONSTER_MOVEMENT: + HandleMonsterMovement(static_cast(argPacket)); + break; + case SerialPacketType::MONSTER_ATTACK: + HandleMonsterAttack(static_cast(argPacket)); + break; + + //rejection messages + case SerialPacketType::REGION_REJECTION: + case SerialPacketType::CHARACTER_REJECTION: + throw(terminal_error(static_cast(argPacket)->text)); + break; + case SerialPacketType::SHUTDOWN_REJECTION: + throw(std::runtime_error(static_cast(argPacket)->text)); + break; + + //errors + default: { + std::ostringstream msg; + msg << "Unknown SerialPacketType encountered in InWorld: " << static_cast(argPacket->type); + throw(std::runtime_error(msg.str())); + } + break; + } +} + +void InWorld::HandlePing(ServerPacket* const argPacket) { + ServerPacket newPacket; + newPacket.type = SerialPacketType::PONG; + network.SendTo(argPacket->srcAddress, &newPacket); +} + +void InWorld::HandlePong(ServerPacket* const argPacket) { + if (*network.GetIPAddress(Channels::SERVER) != argPacket->srcAddress) { + throw(std::runtime_error("Heartbeat message received from an unknown source")); + } + attemptedBeats = 0; + lastBeat = Clock::now(); +} + +//------------------------- +//Connection control +//------------------------- + +void InWorld::SendLogoutRequest() { + ClientPacket newPacket; + + //send a logout request + newPacket.type = SerialPacketType::LOGOUT_REQUEST; + newPacket.accountIndex = accountIndex; + + network.SendTo(Channels::SERVER, &newPacket); +} + +void InWorld::SendDisconnectRequest() { + ClientPacket newPacket; + + //send a disconnect request + newPacket.type = SerialPacketType::DISCONNECT_REQUEST; + newPacket.clientIndex = clientIndex; + + network.SendTo(Channels::SERVER, &newPacket); +} + +void InWorld::SendShutdownRequest() { + ClientPacket newPacket; + + //send a shutdown request + newPacket.type = SerialPacketType::SHUTDOWN_REQUEST; + newPacket.accountIndex = accountIndex; + + network.SendTo(Channels::SERVER, &newPacket); +} + +void InWorld::HandleLogoutResponse(ClientPacket* const argPacket) { + if (localCharacter) { + characterMap.erase(characterIndex); + localCharacter = nullptr; + } + + accountIndex = -1; + characterIndex = -1; + + //reset the camera + camera.marginX = camera.marginY = 0; + + //because, why not? I guess... + SendDisconnectRequest(); +} + +void InWorld::HandleDisconnectResponse(ClientPacket* const argPacket) { + HandleLogoutResponse(argPacket);//shortcut + SetNextScene(SceneList::DISCONNECTEDSCREEN); + ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have successfully logged out"; +} + +void InWorld::HandleDisconnectForced(ClientPacket* const argPacket) { + HandleDisconnectResponse(argPacket);//shortcut + SetNextScene(SceneList::DISCONNECTEDSCREEN); + ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have been forcibly disconnected by the server"; +} + +void InWorld::CheckHeartBeat() { + //check the connection (heartbeat) + if (Clock::now() - lastBeat > std::chrono::seconds(3)) { + if (attemptedBeats > 2) { + //escape to the disconnect screen + SendDisconnectRequest(); + SetNextScene(SceneList::DISCONNECTEDSCREEN); + ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server"; + } + else { + ServerPacket newPacket; + newPacket.type = SerialPacketType::PING; + network.SendTo(Channels::SERVER, &newPacket); + + attemptedBeats++; + lastBeat = Clock::now(); + } + } +} + +//------------------------- +//map management +//------------------------- + +void InWorld::SendRegionRequest(int roomIndex, int x, int y) { + RegionPacket packet; + + //pack the region's data + packet.type = SerialPacketType::REGION_REQUEST; + packet.roomIndex = roomIndex; + packet.x = x; + packet.y = y; + + network.SendTo(Channels::SERVER, &packet); +} + +void InWorld::HandleRegionContent(RegionPacket* const argPacket) { + //replace existing regions + regionPager.UnloadIf([&](Region const& region) -> bool { + return region.GetX() == argPacket->x && region.GetY() == argPacket->y; + }); + regionPager.PushRegion(argPacket->region); + + //clean up after the serial code + delete argPacket->region; + argPacket->region = nullptr; +} + +void InWorld::UpdateMap() { + if (roomIndex == -1) { + return; + } + + //these represent the zone of regions that the client needs loaded, including the mandatory buffers (+1/-1) + int xStart = snapToBase(REGION_WIDTH, camera.x/tileSheet.GetTileW()) - REGION_WIDTH; + int xEnd = snapToBase(REGION_WIDTH, (camera.x+camera.width)/tileSheet.GetTileW()) + REGION_WIDTH; + + int yStart = snapToBase(REGION_HEIGHT, camera.y/tileSheet.GetTileH()) - REGION_HEIGHT; + int yEnd = snapToBase(REGION_HEIGHT, (camera.y+camera.height)/tileSheet.GetTileH()) + REGION_HEIGHT; + + //prune distant regions + regionPager.GetContainer()->remove_if([&](Region const& region) -> bool { + return region.GetX() < xStart || region.GetX() > xEnd || region.GetY() < yStart || region.GetY() > yEnd; + }); + + //request empty regions within this zone + for (int i = xStart; i <= xEnd; i += REGION_WIDTH) { + for (int j = yStart; j <= yEnd; j += REGION_HEIGHT) { + if (!regionPager.FindRegion(i, j)) { + SendRegionRequest(roomIndex, i, j); + } + } + } +} \ No newline at end of file diff --git a/client/gameplay_scenes/in_world_scene_components.cpp b/client/gameplay_scenes/in_world_scene_components.cpp new file mode 100644 index 0000000..55ed44d --- /dev/null +++ b/client/gameplay_scenes/in_world_scene_components.cpp @@ -0,0 +1,305 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 "in_world.hpp" + +#include "channels.hpp" +#include "utility.hpp" + +#include "terminal_error.hpp" +#include +#include +#include +#include +#include + +//------------------------- +//Public access members +//------------------------- + +InWorld::InWorld(int* const argClientIndex, int* const argAccountIndex): + clientIndex(*argClientIndex), + accountIndex(*argAccountIndex) +{ + //setup the utility objects + buttonImage.LoadSurface(config["dir.interface"] + "button_menu.bmp"); + buttonImage.SetClipH(buttonImage.GetClipH()/3); + font.LoadSurface(config["dir.fonts"] + "pk_white_8.bmp"); + + //pass the utility objects + disconnectButton.SetImage(&buttonImage); + disconnectButton.SetFont(&font); + shutDownButton.SetImage(&buttonImage); + shutDownButton.SetFont(&font); + + //set the button positions + disconnectButton.SetX(50); + disconnectButton.SetY(50 + buttonImage.GetClipH() * 0); + shutDownButton.SetX(50); + shutDownButton.SetY(50 + buttonImage.GetClipH() * 1); + + //set the button texts + disconnectButton.SetText("Disconnect"); + shutDownButton.SetText("Shut Down"); + + //load the tilesheet + //TODO: add the tilesheet to the map system + //TODO: Tile size and tile sheet should be loaded elsewhere + tileSheet.Load(config["dir.tilesets"] + "overworld.bmp", 32, 32); + + //Send the character data + //TODO: login scene, prompt, etc. + CharacterPacket newPacket; + newPacket.type = SerialPacketType::CHARACTER_LOAD; + 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); + + //query the world state + memset(&newPacket, 0, MAX_PACKET_SIZE); + newPacket.type = SerialPacketType::QUERY_CHARACTER_EXISTS; + network.SendTo(Channels::SERVER, &newPacket); + + //set the camera's values + camera.width = GetScreen()->w; + camera.height = GetScreen()->h; + + //debug + // +} + +InWorld::~InWorld() { + //unload the local data + characterMap.clear(); + monsterMap.clear(); +} + +//------------------------- +//Frame loop +//------------------------- + +void InWorld::FrameStart() { + // +} + +void InWorld::Update() { + //create and zero the buffer + SerialPacket* packetBuffer = reinterpret_cast(new char[MAX_PACKET_SIZE]); + memset(packetBuffer, 0, MAX_PACKET_SIZE); + + try { + //suck in and process all waiting packets + while(network.Receive(packetBuffer)) { + HandlePacket(packetBuffer); + } + } + catch(terminal_error& e) { + throw(e); + } + catch(std::exception& e) { + std::cerr << "HandlePacket Error: " << e.what() << std::endl; + } + + //free the buffer + delete reinterpret_cast(packetBuffer); + + //heartbeat system + CheckHeartBeat(); + + //update all entities + for (auto& it : characterMap) { + it.second.Update(); + } + for (auto& it : monsterMap) { + it.second.Update(); + } + + //update the map + UpdateMap(); + + //skip the rest without a local character + if (!localCharacter) { + return; + } + + //get the collidable boxes + std::list boxList = GenerateCollisionGrid(localCharacter, tileSheet.GetTileW(), tileSheet.GetTileH()); + + //process the collisions + if (localCharacter->ProcessCollisionGrid(boxList)) { + localCharacter->CorrectSprite(); + SendLocalCharacterMovement(); + } + + //update the camera + camera.x = localCharacter->GetOrigin().x - camera.marginX; + camera.y = localCharacter->GetOrigin().y - camera.marginY; +} + +void InWorld::FrameEnd() { + // +} + +void InWorld::RenderFrame() { + SDL_FillRect(GetScreen(), 0, 0); + Render(GetScreen()); + SDL_Flip(GetScreen()); + fps.Calculate(); +} + +void InWorld::Render(SDL_Surface* const screen) { + //draw the map + for (std::list::iterator it = regionPager.GetContainer()->begin(); it != regionPager.GetContainer()->end(); it++) { + tileSheet.DrawRegionTo(screen, &(*it), camera.x, camera.y); + } + + //draw the entities + for (auto& it : characterMap) { + //TODO: depth ordering + it.second.DrawTo(screen, camera.x, camera.y); + } + for (auto& it : monsterMap) { + //TODO: depth ordering + it.second.DrawTo(screen, camera.x, camera.y); + } + + //draw UI + disconnectButton.DrawTo(screen); + shutDownButton.DrawTo(screen); + font.DrawStringTo(to_string_custom(fps.GetFrameRate()), screen, 0, 0); +} + +//------------------------- +//Event handlers +//------------------------- + +void InWorld::QuitEvent() { + //two-step logout + SendDisconnectRequest(); + SetNextScene(SceneList::QUIT); +} + +void InWorld::MouseMotion(SDL_MouseMotionEvent const& motion) { + disconnectButton.MouseMotion(motion); + shutDownButton.MouseMotion(motion); +} + +void InWorld::MouseButtonDown(SDL_MouseButtonEvent const& button) { + disconnectButton.MouseButtonDown(button); + shutDownButton.MouseButtonDown(button); +} + +void InWorld::MouseButtonUp(SDL_MouseButtonEvent const& button) { + if (disconnectButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) { + SendLogoutRequest(); + } + if (shutDownButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) { + SendShutdownRequest(); + } +} + +void InWorld::KeyDown(SDL_KeyboardEvent const& key) { + //hotkeys + switch(key.keysym.sym) { + case SDLK_ESCAPE: + //TODO: the escape key should actually control menus and stuff + SendLogoutRequest(); + return; + } + + //character movement + if (!localCharacter) { + return; + } + Vector2 motion = localCharacter->GetMotion(); + switch(key.keysym.sym) { + case SDLK_w: + motion.y -= CHARACTER_WALKING_SPEED; + break; + case SDLK_a: + motion.x -= CHARACTER_WALKING_SPEED; + break; + case SDLK_s: + motion.y += CHARACTER_WALKING_SPEED; + break; + case SDLK_d: + motion.x += CHARACTER_WALKING_SPEED; + break; + default: + //DOCS: prevents wrong keys screwing with character movement + return; + } + //handle diagonals + if (motion.x != 0 && motion.y != 0) { + motion *= CHARACTER_WALKING_MOD; + } + //set the info + localCharacter->SetMotion(motion); + localCharacter->CorrectSprite(); + SendLocalCharacterMovement(); +} + +void InWorld::KeyUp(SDL_KeyboardEvent const& key) { + //character movement + if (!localCharacter) { + return; + } + Vector2 motion = localCharacter->GetMotion(); + switch(key.keysym.sym) { + case SDLK_w: + motion.y = std::min(0.0, motion.y += CHARACTER_WALKING_SPEED); + break; + case SDLK_a: + motion.x = std::min(0.0, motion.x += CHARACTER_WALKING_SPEED); + break; + case SDLK_s: + motion.y = std::max(0.0, motion.y -= CHARACTER_WALKING_SPEED); + break; + case SDLK_d: + motion.x = std::max(0.0, motion.x -= CHARACTER_WALKING_SPEED); + break; + default: + //DOCS: prevents wrong keys screwing with character movement + return; + } + //BUGFIX: reset cardinal direction speed on key release + if (motion.x > 0) { + motion.x = CHARACTER_WALKING_SPEED; + } + else if (motion.x < 0) { + motion.x = -CHARACTER_WALKING_SPEED; + } + if (motion.y > 0) { + motion.y = CHARACTER_WALKING_SPEED; + } + else if (motion.y < 0) { + motion.y = -CHARACTER_WALKING_SPEED; + } + //handle diagonals + if (motion.x != 0 && motion.y != 0) { + motion *= CHARACTER_WALKING_MOD; + } + //set the info + localCharacter->SetMotion(motion); + localCharacter->CorrectSprite(); + SendLocalCharacterMovement(); +} \ No newline at end of file diff --git a/client/scenes/makefile b/client/gameplay_scenes/makefile similarity index 100% rename from client/scenes/makefile rename to client/gameplay_scenes/makefile diff --git a/client/makefile b/client/makefile index 68ce8d9..b931b13 100644 --- a/client/makefile +++ b/client/makefile @@ -1,5 +1,5 @@ #include directories -INCLUDES+=. client_utilities entities scenes ../common/debugging ../common/gameplay ../common/graphics ../common/map ../common/network ../common/network/packet_types ../common/ui ../common/utilities +INCLUDES+=. client_utilities entities gameplay_scenes menu_scenes ../common/debugging ../common/gameplay ../common/graphics ../common/map ../common/network ../common/network/packet_types ../common/ui ../common/utilities #libraries #the order of the $(LIBS) is important, at least for MinGW @@ -27,7 +27,8 @@ OUT=$(addprefix $(OUTDIR)/,client) all: $(OBJ) $(OUT) $(MAKE) -C client_utilities $(MAKE) -C entities - $(MAKE) -C scenes + $(MAKE) -C gameplay_scenes + $(MAKE) -C menu_scenes $(CXX) $(CXXFLAGS) -o $(OUT) $(OBJ) $(LIBS) $(OBJ): | $(OBJDIR) diff --git a/client/scenes/disconnected_screen.cpp b/client/menu_scenes/disconnected_screen.cpp similarity index 100% rename from client/scenes/disconnected_screen.cpp rename to client/menu_scenes/disconnected_screen.cpp diff --git a/client/scenes/disconnected_screen.hpp b/client/menu_scenes/disconnected_screen.hpp similarity index 100% rename from client/scenes/disconnected_screen.hpp rename to client/menu_scenes/disconnected_screen.hpp diff --git a/client/scenes/lobby_menu.cpp b/client/menu_scenes/lobby_menu.cpp similarity index 100% rename from client/scenes/lobby_menu.cpp rename to client/menu_scenes/lobby_menu.cpp diff --git a/client/scenes/lobby_menu.hpp b/client/menu_scenes/lobby_menu.hpp similarity index 100% rename from client/scenes/lobby_menu.hpp rename to client/menu_scenes/lobby_menu.hpp diff --git a/client/scenes/main_menu.cpp b/client/menu_scenes/main_menu.cpp similarity index 100% rename from client/scenes/main_menu.cpp rename to client/menu_scenes/main_menu.cpp diff --git a/client/scenes/main_menu.hpp b/client/menu_scenes/main_menu.hpp similarity index 100% rename from client/scenes/main_menu.hpp rename to client/menu_scenes/main_menu.hpp diff --git a/client/menu_scenes/makefile b/client/menu_scenes/makefile new file mode 100644 index 0000000..24dfb60 --- /dev/null +++ b/client/menu_scenes/makefile @@ -0,0 +1,37 @@ +#config +INCLUDES+=. .. ../../common/graphics ../../common/map ../../common/network ../../common/network/packet_types ../../common/ui ../../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)/,client.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/scenes/options_menu.cpp b/client/menu_scenes/options_menu.cpp similarity index 100% rename from client/scenes/options_menu.cpp rename to client/menu_scenes/options_menu.cpp diff --git a/client/scenes/options_menu.hpp b/client/menu_scenes/options_menu.hpp similarity index 100% rename from client/scenes/options_menu.hpp rename to client/menu_scenes/options_menu.hpp diff --git a/client/scenes/splash_screen.cpp b/client/menu_scenes/splash_screen.cpp similarity index 100% rename from client/scenes/splash_screen.cpp rename to client/menu_scenes/splash_screen.cpp diff --git a/client/scenes/splash_screen.hpp b/client/menu_scenes/splash_screen.hpp similarity index 100% rename from client/scenes/splash_screen.hpp rename to client/menu_scenes/splash_screen.hpp diff --git a/client/scenes/in_world.cpp b/client/scenes/in_world.cpp deleted file mode 100644 index 42f35a2..0000000 --- a/client/scenes/in_world.cpp +++ /dev/null @@ -1,756 +0,0 @@ -/* Copyright: (c) Kayne Ruse 2013-2015 - * - * 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 "in_world.hpp" - -#include "channels.hpp" - -#include "terminal_error.hpp" -#include -#include -#include -#include -#include - -//------------------------- -//these should've come standard -//------------------------- - -bool operator==(IPaddress lhs, IPaddress rhs) { - return lhs.host == rhs.host && lhs.port == rhs.port; -} - -bool operator!=(IPaddress lhs, IPaddress rhs) { - return !(lhs == rhs); -} - -//------------------------- -//Public access members -//------------------------- - -InWorld::InWorld(int* const argClientIndex, int* const argAccountIndex): - clientIndex(*argClientIndex), - accountIndex(*argAccountIndex) -{ - //setup the utility objects - buttonImage.LoadSurface(config["dir.interface"] + "button_menu.bmp"); - buttonImage.SetClipH(buttonImage.GetClipH()/3); - font.LoadSurface(config["dir.fonts"] + "pk_white_8.bmp"); - - //pass the utility objects - disconnectButton.SetImage(&buttonImage); - disconnectButton.SetFont(&font); - shutDownButton.SetImage(&buttonImage); - shutDownButton.SetFont(&font); - - //set the button positions - disconnectButton.SetX(50); - disconnectButton.SetY(50 + buttonImage.GetClipH() * 0); - shutDownButton.SetX(50); - shutDownButton.SetY(50 + buttonImage.GetClipH() * 1); - - //set the button texts - disconnectButton.SetText("Disconnect"); - shutDownButton.SetText("Shut Down"); - - //load the tilesheet - //TODO: add the tilesheet to the map system - //TODO: Tile size and tile sheet should be loaded elsewhere - tileSheet.Load(config["dir.tilesets"] + "overworld.bmp", 32, 32); - - //Send the character data - //TODO: login scene, prompt, etc. - CharacterPacket newPacket; - newPacket.type = SerialPacketType::CHARACTER_LOAD; - 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); - - //query the world state - memset(&newPacket, 0, MAX_PACKET_SIZE); - newPacket.type = SerialPacketType::QUERY_CHARACTER_EXISTS; - network.SendTo(Channels::SERVER, &newPacket); - - //set the camera's values - camera.width = GetScreen()->w; - camera.height = GetScreen()->h; - - //debug - // -} - -InWorld::~InWorld() { - //unload the local data - characterMap.clear(); - monsterMap.clear(); -} - -//------------------------- -//Frame loop -//------------------------- - -void InWorld::FrameStart() { - // -} - -void InWorld::Update() { - //create and zero the buffer - SerialPacket* packetBuffer = reinterpret_cast(new char[MAX_PACKET_SIZE]); - memset(packetBuffer, 0, MAX_PACKET_SIZE); - - try { - //suck in and process all waiting packets - while(network.Receive(packetBuffer)) { - HandlePacket(packetBuffer); - } - } - catch(terminal_error& e) { - throw(e); - } - catch(std::exception& e) { - std::cerr << "HandlePacket Error: " << e.what() << std::endl; - } - - //free the buffer - delete reinterpret_cast(packetBuffer); - - //heartbeat system - CheckHeartBeat(); - - //update all entities - for (auto& it : characterMap) { - it.second.Update(); - } - for (auto& it : monsterMap) { - it.second.Update(); - } - - //update the map - UpdateMap(); - - //skip the rest without a local character - if (!localCharacter) { - return; - } - - //get the collidable boxes - std::list boxList = GenerateCollisionGrid(localCharacter, tileSheet.GetTileW(), tileSheet.GetTileH()); - - //process the collisions - if (localCharacter->ProcessCollisionGrid(boxList)) { - localCharacter->CorrectSprite(); - SendLocalCharacterMotion(); - } - - //update the camera - camera.x = localCharacter->GetOrigin().x - camera.marginX; - camera.y = localCharacter->GetOrigin().y - camera.marginY; -} - -void InWorld::FrameEnd() { - // -} - -void InWorld::RenderFrame() { - SDL_FillRect(GetScreen(), 0, 0); - Render(GetScreen()); - SDL_Flip(GetScreen()); - fps.Calculate(); -} - -void InWorld::Render(SDL_Surface* const screen) { - //draw the map - for (std::list::iterator it = regionPager.GetContainer()->begin(); it != regionPager.GetContainer()->end(); it++) { - tileSheet.DrawRegionTo(screen, &(*it), camera.x, camera.y); - } - - //draw the entities - for (auto& it : characterMap) { - //TODO: depth ordering - it.second.DrawTo(screen, camera.x, camera.y); - } - for (auto& it : monsterMap) { - //TODO: depth ordering - it.second.DrawTo(screen, camera.x, camera.y); - } - - //draw UI - disconnectButton.DrawTo(screen); - shutDownButton.DrawTo(screen); - std::ostringstream msg; - msg << fps.GetFrameRate(); - font.DrawStringTo(msg.str(), screen, 0, 0); -} - -//------------------------- -//Event handlers -//------------------------- - -void InWorld::QuitEvent() { - //two-step logout - SendDisconnectRequest(); - SetNextScene(SceneList::QUIT); -} - -void InWorld::MouseMotion(SDL_MouseMotionEvent const& motion) { - disconnectButton.MouseMotion(motion); - shutDownButton.MouseMotion(motion); -} - -void InWorld::MouseButtonDown(SDL_MouseButtonEvent const& button) { - disconnectButton.MouseButtonDown(button); - shutDownButton.MouseButtonDown(button); -} - -void InWorld::MouseButtonUp(SDL_MouseButtonEvent const& button) { - if (disconnectButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) { - SendLogoutRequest(); - } - if (shutDownButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) { - SendShutdownRequest(); - } -} - -void InWorld::KeyDown(SDL_KeyboardEvent const& key) { - //hotkeys - switch(key.keysym.sym) { - case SDLK_ESCAPE: - //TODO: the escape key should actually control menus and stuff - SendLogoutRequest(); - return; - } - - //character movement - if (!localCharacter) { - return; - } - Vector2 motion = localCharacter->GetMotion(); - switch(key.keysym.sym) { - case SDLK_w: - motion.y -= CHARACTER_WALKING_SPEED; - break; - case SDLK_a: - motion.x -= CHARACTER_WALKING_SPEED; - break; - case SDLK_s: - motion.y += CHARACTER_WALKING_SPEED; - break; - case SDLK_d: - motion.x += CHARACTER_WALKING_SPEED; - break; - default: - //DOCS: prevents wrong keys screwing with character movement - return; - } - //handle diagonals - if (motion.x != 0 && motion.y != 0) { - motion *= CHARACTER_WALKING_MOD; - } - //set the info - localCharacter->SetMotion(motion); - localCharacter->CorrectSprite(); - SendLocalCharacterMotion(); -} - -void InWorld::KeyUp(SDL_KeyboardEvent const& key) { - //character movement - if (!localCharacter) { - return; - } - Vector2 motion = localCharacter->GetMotion(); - switch(key.keysym.sym) { - case SDLK_w: - motion.y = std::min(0.0, motion.y += CHARACTER_WALKING_SPEED); - break; - case SDLK_a: - motion.x = std::min(0.0, motion.x += CHARACTER_WALKING_SPEED); - break; - case SDLK_s: - motion.y = std::max(0.0, motion.y -= CHARACTER_WALKING_SPEED); - break; - case SDLK_d: - motion.x = std::max(0.0, motion.x -= CHARACTER_WALKING_SPEED); - break; - default: - //DOCS: prevents wrong keys screwing with character movement - return; - } - //BUGFIX: reset cardinal direction speed on key release - if (motion.x > 0) { - motion.x = CHARACTER_WALKING_SPEED; - } - else if (motion.x < 0) { - motion.x = -CHARACTER_WALKING_SPEED; - } - if (motion.y > 0) { - motion.y = CHARACTER_WALKING_SPEED; - } - else if (motion.y < 0) { - motion.y = -CHARACTER_WALKING_SPEED; - } - //handle diagonals - if (motion.x != 0 && motion.y != 0) { - motion *= CHARACTER_WALKING_MOD; - } - //set the info - localCharacter->SetMotion(motion); - localCharacter->CorrectSprite(); - SendLocalCharacterMotion(); -} - -//------------------------- -//Basic connections -//------------------------- - -void InWorld::HandlePacket(SerialPacket* const argPacket) { - switch(argPacket->type) { - //heartbeat system - case SerialPacketType::PING: - HandlePing(static_cast(argPacket)); - break; - case SerialPacketType::PONG: - HandlePong(static_cast(argPacket)); - break; - - //game server connections - case SerialPacketType::LOGOUT_RESPONSE: - HandleLogoutResponse(static_cast(argPacket)); - break; - case SerialPacketType::DISCONNECT_RESPONSE: - HandleDisconnectResponse(static_cast(argPacket)); - break; - case SerialPacketType::DISCONNECT_FORCED: - HandleDisconnectForced(static_cast(argPacket)); - break; - - //map management - case SerialPacketType::REGION_CONTENT: - HandleRegionContent(static_cast(argPacket)); - break; - - //character management - case SerialPacketType::CHARACTER_CREATE: - HandleCharacterCreate(static_cast(argPacket)); - break; - case SerialPacketType::CHARACTER_DELETE: - HandleCharacterDelete(static_cast(argPacket)); - break; - case SerialPacketType::QUERY_CHARACTER_EXISTS: - HandleCharacterQueryExists(static_cast(argPacket)); - break; - - //character movement - case SerialPacketType::CHARACTER_SET_ROOM: - HandleCharacterSetRoom(static_cast(argPacket)); - break; - case SerialPacketType::CHARACTER_SET_ORIGIN: - HandleCharacterSetOrigin(static_cast(argPacket)); - break; - case SerialPacketType::CHARACTER_SET_MOTION: - HandleCharacterSetMotion(static_cast(argPacket)); - break; - - //rejection messages - case SerialPacketType::REGION_REJECTION: - case SerialPacketType::CHARACTER_REJECTION: - throw(terminal_error(static_cast(argPacket)->text)); - break; - case SerialPacketType::SHUTDOWN_REJECTION: - throw(std::runtime_error(static_cast(argPacket)->text)); - break; - - //errors - default: { - std::ostringstream msg; - msg << "Unknown SerialPacketType encountered in InWorld: " << static_cast(argPacket->type); - throw(std::runtime_error(msg.str())); - } - break; - } -} - -void InWorld::HandlePing(ServerPacket* const argPacket) { - ServerPacket newPacket; - newPacket.type = SerialPacketType::PONG; - network.SendTo(argPacket->srcAddress, &newPacket); -} - -void InWorld::HandlePong(ServerPacket* const argPacket) { - if (*network.GetIPAddress(Channels::SERVER) != argPacket->srcAddress) { - throw(std::runtime_error("Heartbeat message received from an unknown source")); - } - attemptedBeats = 0; - lastBeat = Clock::now(); -} - -//------------------------- -//Connection control -//------------------------- - -void InWorld::SendLogoutRequest() { - ClientPacket newPacket; - - //send a logout request - newPacket.type = SerialPacketType::LOGOUT_REQUEST; - newPacket.accountIndex = accountIndex; - - network.SendTo(Channels::SERVER, &newPacket); -} - -void InWorld::SendDisconnectRequest() { - ClientPacket newPacket; - - //send a disconnect request - newPacket.type = SerialPacketType::DISCONNECT_REQUEST; - newPacket.clientIndex = clientIndex; - - network.SendTo(Channels::SERVER, &newPacket); -} - -void InWorld::SendShutdownRequest() { - ClientPacket newPacket; - - //send a shutdown request - newPacket.type = SerialPacketType::SHUTDOWN_REQUEST; - newPacket.accountIndex = accountIndex; - - network.SendTo(Channels::SERVER, &newPacket); -} - -void InWorld::HandleLogoutResponse(ClientPacket* const argPacket) { - if (localCharacter) { - characterMap.erase(characterIndex); - localCharacter = nullptr; - } - - accountIndex = -1; - characterIndex = -1; - - //reset the camera - camera.marginX = camera.marginY = 0; - - //because, why not? I guess... - SendDisconnectRequest(); -} - -void InWorld::HandleDisconnectResponse(ClientPacket* const argPacket) { - HandleLogoutResponse(argPacket);//shortcut - SetNextScene(SceneList::DISCONNECTEDSCREEN); - ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have successfully logged out"; -} - -void InWorld::HandleDisconnectForced(ClientPacket* const argPacket) { - HandleDisconnectResponse(argPacket);//shortcut - SetNextScene(SceneList::DISCONNECTEDSCREEN); - ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have been forcibly disconnected by the server"; -} - -void InWorld::CheckHeartBeat() { - //check the connection (heartbeat) - if (Clock::now() - lastBeat > std::chrono::seconds(3)) { - if (attemptedBeats > 2) { - //escape to the disconnect screen - SendDisconnectRequest(); - SetNextScene(SceneList::DISCONNECTEDSCREEN); - ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server"; - } - else { - ServerPacket newPacket; - newPacket.type = SerialPacketType::PING; - network.SendTo(Channels::SERVER, &newPacket); - - attemptedBeats++; - lastBeat = Clock::now(); - } - } -} - -//------------------------- -//map management -//------------------------- - -void InWorld::SendRegionRequest(int roomIndex, int x, int y) { - RegionPacket packet; - - //pack the region's data - packet.type = SerialPacketType::REGION_REQUEST; - packet.roomIndex = roomIndex; - packet.x = x; - packet.y = y; - - network.SendTo(Channels::SERVER, &packet); -} - -void InWorld::HandleRegionContent(RegionPacket* const argPacket) { - //replace existing regions - regionPager.UnloadIf([&](Region const& region) -> bool { - return region.GetX() == argPacket->x && region.GetY() == argPacket->y; - }); - regionPager.PushRegion(argPacket->region); - - //clean up after the serial code - delete argPacket->region; - argPacket->region = nullptr; -} - -void InWorld::UpdateMap() { - if (roomIndex == -1) { - return; - } - - //these represent the zone of regions that the client needs loaded, including the mandatory buffers (+1/-1) - int xStart = snapToBase(REGION_WIDTH, camera.x/tileSheet.GetTileW()) - REGION_WIDTH; - int xEnd = snapToBase(REGION_WIDTH, (camera.x+camera.width)/tileSheet.GetTileW()) + REGION_WIDTH; - - int yStart = snapToBase(REGION_HEIGHT, camera.y/tileSheet.GetTileH()) - REGION_HEIGHT; - int yEnd = snapToBase(REGION_HEIGHT, (camera.y+camera.height)/tileSheet.GetTileH()) + REGION_HEIGHT; - - //prune distant regions - regionPager.GetContainer()->remove_if([&](Region const& region) -> bool { - return region.GetX() < xStart || region.GetX() > xEnd || region.GetY() < yStart || region.GetY() > yEnd; - }); - - //request empty regions within this zone - for (int i = xStart; i <= xEnd; i += REGION_WIDTH) { - for (int j = yStart; j <= yEnd; j += REGION_HEIGHT) { - if (!regionPager.FindRegion(i, j)) { - SendRegionRequest(roomIndex, i, j); - } - } - } -} - -//------------------------- -//entity management -//------------------------- - -//DOCS: preexisting characters will result in query responses -//DOCS: new characters will result in create messages -//DOCS: this client's character will exist in both (skipped) - -void InWorld::HandleCharacterCreate(CharacterPacket* const argPacket) { - //prevent double message - if (characterMap.find(argPacket->characterIndex) != characterMap.end()) { - std::ostringstream msg; - msg << "Double character creation event; "; - msg << "Index: " << argPacket->characterIndex << "; "; - msg << "Handle: " << argPacket->handle; - throw(std::runtime_error(msg.str())); - } - - //implicity create and retrieve the entity - BaseCharacter* character = &characterMap[argPacket->characterIndex]; - - //fill the character's info - character->SetOrigin(argPacket->origin); - character->SetMotion(argPacket->motion); - character->SetBounds({CHARACTER_BOUNDS_X, CHARACTER_BOUNDS_Y, CHARACTER_BOUNDS_WIDTH, CHARACTER_BOUNDS_HEIGHT}); - character->SetHandle(argPacket->handle); - character->SetAvatar(argPacket->avatar); - character->SetOwner(argPacket->accountIndex); - character->CorrectSprite(); - - //check for this player's character - if (character->GetOwner() == accountIndex) { - localCharacter = static_cast(character); - - //focus the camera on this character - camera.marginX = (camera.width / 2 - localCharacter->GetSprite()->GetImage()->GetClipW() / 2); - camera.marginY = (camera.height/ 2 - localCharacter->GetSprite()->GetImage()->GetClipH() / 2); - - //focus on this character's info - characterIndex = argPacket->characterIndex; - roomIndex = argPacket->roomIndex; - } - - //debug - std::cout << "Create, total: " << characterMap.size() << std::endl; -} - -void InWorld::HandleCharacterDelete(CharacterPacket* const argPacket) { - //ignore if this character doesn't exist - std::map::iterator characterIt = characterMap.find(argPacket->characterIndex); - if (characterIt == characterMap.end()) { - return; - } - - //check for this player's character - if ((*characterIt).second.GetOwner() == accountIndex) { - localCharacter = nullptr; - - //clear the camera - camera.marginX = 0; - camera.marginY = 0; - - //clear the room - roomIndex = -1; - } - - //remove this character - characterMap.erase(characterIt); - - //debug - std::cout << "Delete, total: " << characterMap.size() << std::endl; -} - -void InWorld::HandleCharacterQueryExists(CharacterPacket* const argPacket) { - //prevent a double message about this player's character - if (argPacket->accountIndex == accountIndex) { - return; - } - - //ignore characters in a different room (sub-optimal) - if (argPacket->roomIndex != roomIndex) { - return; - } - - //implicitly construct the character if it doesn't exist - BaseCharacter* character = &characterMap[argPacket->characterIndex]; - - //set/update the character's info - character->SetOrigin(argPacket->origin); - character->SetMotion(argPacket->motion); - character->SetBounds({CHARACTER_BOUNDS_X, CHARACTER_BOUNDS_Y, CHARACTER_BOUNDS_WIDTH, CHARACTER_BOUNDS_HEIGHT}); - character->SetHandle(argPacket->handle); - character->SetAvatar(argPacket->avatar); - character->SetOwner(argPacket->accountIndex); - character->CorrectSprite(); - - //debug - std::cout << "Query, total: " << characterMap.size() << std::endl; -} - -void InWorld::HandleCharacterSetRoom(CharacterPacket* const argPacket) { - //someone else's character - if (argPacket->characterIndex != characterIndex) { - characterMap.erase(argPacket->characterIndex); - return; - } - - //this character is moving between rooms - roomIndex = argPacket->roomIndex; - - //set the character's info - localCharacter->SetOrigin(argPacket->origin); - localCharacter->SetMotion(argPacket->motion); - localCharacter->CorrectSprite(); - - //clear the old room's data - regionPager.UnloadAll(); - monsterMap.clear(); - - //use the jenky pattern for std::map to skip this player's character - for (std::map::iterator it = characterMap.begin(); it != characterMap.end(); /* EMPTY */ ) { - if (it->first != characterIndex) { - it = characterMap.erase(it); - } - else { - ++it; - } - } - - //request the info on characters in this room - CharacterPacket newPacket; - newPacket.type = SerialPacketType::QUERY_CHARACTER_EXISTS; - newPacket.roomIndex = roomIndex; - network.SendTo(Channels::SERVER, &newPacket); -} - -void InWorld::HandleCharacterSetOrigin(CharacterPacket* const argPacket) { - //TODO: Authentication - if (argPacket->characterIndex == characterIndex) { - return; - } - - //check that this character exists - std::map::iterator characterIt = characterMap.find(argPacket->characterIndex); - if (characterIt != characterMap.end()) { - //set the origin and motion - characterIt->second.SetOrigin(argPacket->origin); - characterIt->second.SetMotion(argPacket->motion); - characterIt->second.CorrectSprite(); - } -} - -void InWorld::HandleCharacterSetMotion(CharacterPacket* const argPacket) { - //TODO: Authentication - if (argPacket->characterIndex == characterIndex) { - return; - } - - //check that this character exists - std::map::iterator characterIt = characterMap.find(argPacket->characterIndex); - if (characterIt != characterMap.end()) { - //set the origin and motion - characterIt->second.SetOrigin(argPacket->origin); - characterIt->second.SetMotion(argPacket->motion); - characterIt->second.CorrectSprite(); - } -} - -//------------------------- -//player movement -//------------------------- - -//TODO: add a "movement" packet type -void InWorld::SendLocalCharacterMotion() { - CharacterPacket newPacket; - newPacket.type = SerialPacketType::CHARACTER_SET_MOTION; - - newPacket.accountIndex = accountIndex; - newPacket.characterIndex = characterIndex; - newPacket.roomIndex = roomIndex; - newPacket.origin = localCharacter->GetOrigin(); - newPacket.motion = localCharacter->GetMotion(); - - network.SendTo(Channels::SERVER, &newPacket); -} - -std::list InWorld::GenerateCollisionGrid(Entity* ptr, int tileWidth, int tileHeight) { - //prepare for collisions - BoundingBox wallBounds = {0, 0, tileWidth, tileHeight}; - std::list boxList; - - //NOTE: for loops were too dense to work with, so I've just used while loops - - //outer loop - wallBounds.x = snapToBase((double)wallBounds.w, ptr->GetOrigin().x); - while(wallBounds.x < (ptr->GetOrigin() + ptr->GetBounds()).x + ptr->GetBounds().w) { - //inner loop - wallBounds.y = snapToBase((double)wallBounds.h, ptr->GetOrigin().y); - while(wallBounds.y < (ptr->GetOrigin() + ptr->GetBounds()).y + ptr->GetBounds().h) { - //check to see if this tile is solid - if (regionPager.GetSolid(wallBounds.x / wallBounds.w, wallBounds.y / wallBounds.h)) { - //push onto the box set - boxList.push_front(wallBounds); - } - - //increment - wallBounds.y += wallBounds.h; - } - - //increment - wallBounds.x += wallBounds.w; - } - - return std::move(boxList); -} \ No newline at end of file diff --git a/common/network/packet_types/monster_packet.cpp b/common/network/packet_types/monster_packet.cpp new file mode 100644 index 0000000..5304550 --- /dev/null +++ b/common/network/packet_types/monster_packet.cpp @@ -0,0 +1,76 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 "monster_packet.hpp" + +#include "serial_utility.hpp" + +void serializeMonster(void* buffer, MonsterPacket* packet) { + serialCopy(&buffer, &packet->type, sizeof(SerialPacketType)); + + //identify the monster + serialCopy(&buffer, &packet->monsterIndex, sizeof(int)); + serialCopy(&buffer, packet->handle, PACKET_STRING_SIZE); + serialCopy(&buffer, packet->avatar, PACKET_STRING_SIZE); + + //bounds + serialCopy(&buffer, &packet->bounds.x, sizeof(int)); + serialCopy(&buffer, &packet->bounds.y, sizeof(int)); + serialCopy(&buffer, &packet->bounds.w, sizeof(int)); + serialCopy(&buffer, &packet->bounds.h, sizeof(int)); + + + //location + serialCopy(&buffer, &packet->roomIndex, sizeof(int)); + serialCopy(&buffer, &packet->origin.x, sizeof(double)); + serialCopy(&buffer, &packet->origin.y, sizeof(double)); + serialCopy(&buffer, &packet->motion.x, sizeof(double)); + serialCopy(&buffer, &packet->motion.y, sizeof(double)); + + //attack data + //TODO +} + +void deserializeMonster(void* buffer, MonsterPacket* packet) { + deserialCopy(&buffer, &packet->type, sizeof(SerialPacketType)); + + //identify the monster + deserialCopy(&buffer, &packet->monsterIndex, sizeof(int)); + deserialCopy(&buffer, packet->handle, PACKET_STRING_SIZE); + deserialCopy(&buffer, packet->avatar, PACKET_STRING_SIZE); + + //bounds + deserialCopy(&buffer, &packet->bounds.x, sizeof(int)); + deserialCopy(&buffer, &packet->bounds.y, sizeof(int)); + deserialCopy(&buffer, &packet->bounds.w, sizeof(int)); + deserialCopy(&buffer, &packet->bounds.h, sizeof(int)); + + + //location + deserialCopy(&buffer, &packet->roomIndex, sizeof(int)); + deserialCopy(&buffer, &packet->origin.x, sizeof(double)); + deserialCopy(&buffer, &packet->origin.y, sizeof(double)); + deserialCopy(&buffer, &packet->motion.x, sizeof(double)); + deserialCopy(&buffer, &packet->motion.y, sizeof(double)); + + //attack data + //TODO +} diff --git a/common/network/packet_types/monster_packet.hpp b/common/network/packet_types/monster_packet.hpp new file mode 100644 index 0000000..97ebd38 --- /dev/null +++ b/common/network/packet_types/monster_packet.hpp @@ -0,0 +1,48 @@ +/* Copyright: (c) Kayne Ruse 2013-2015 + * + * 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 MONSTERPACKET_HPP_ +#define MONSTERPACKET_HPP_ + +#include "serial_packet_base.hpp" + +#include "bounding_box.hpp" +#include "vector2.hpp" + +struct MonsterPacket : SerialPacketBase { + //identify the monster + int monsterIndex; + char handle[PACKET_STRING_SIZE]; + char avatar[PACKET_STRING_SIZE]; + BoundingBox bounds; + + //location + int roomIndex; + Vector2 origin; + Vector2 motion; + + //TODO: attack data +}; + +void serializeMonster(void* buffer, MonsterPacket* packet); +void deserializeMonster(void* buffer, MonsterPacket* packet); + +#endif \ No newline at end of file diff --git a/common/network/serial_packet.hpp b/common/network/serial_packet.hpp index 5d609f3..fad3dce 100644 --- a/common/network/serial_packet.hpp +++ b/common/network/serial_packet.hpp @@ -25,6 +25,7 @@ #include "serial_packet_base.hpp" #include "character_packet.hpp" #include "client_packet.hpp" +#include "monster_packet.hpp" #include "region_packet.hpp" #include "server_packet.hpp" #include "text_packet.hpp" @@ -33,14 +34,15 @@ typedef SerialPacketBase SerialPacket; //DOCS: NETWORK_VERSION is used to discern compatible servers and clients -constexpr int NETWORK_VERSION = 20141227; +constexpr int NETWORK_VERSION = -1; union MaxPacket { CharacterPacket a; ClientPacket b; - RegionPacket c; - ServerPacket d; - TextPacket e; + MonsterPacket c; + RegionPacket d; + ServerPacket e; + TextPacket f; }; constexpr int MAX_PACKET_SIZE = sizeof(MaxPacket); diff --git a/common/network/serial_packet_type.hpp b/common/network/serial_packet_type.hpp index c4e9842..bfd808c 100644 --- a/common/network/serial_packet_type.hpp +++ b/common/network/serial_packet_type.hpp @@ -30,7 +30,7 @@ //TODO: This needs to be smoothed out enum class SerialPacketType { //default: there is something wrong - NONE = 0, + NONE, //------------------------- //ServerPacket @@ -38,12 +38,12 @@ enum class SerialPacketType { //------------------------- //heartbeat - PING = 1, - PONG = 2, + PING, + PONG, //Used for finding available servers - BROADCAST_REQUEST = 3, - BROADCAST_RESPONSE = 4, + BROADCAST_REQUEST, + BROADCAST_RESPONSE, //------------------------- //ClientPacket @@ -51,24 +51,24 @@ enum class SerialPacketType { //------------------------- //Connecting to a server as a client - JOIN_REQUEST = 5, - JOIN_RESPONSE = 6, + JOIN_REQUEST, + JOIN_RESPONSE, //disconnect from the server - DISCONNECT_REQUEST = 7, - DISCONNECT_RESPONSE = 8, - DISCONNECT_FORCED = 9, + DISCONNECT_REQUEST, + DISCONNECT_RESPONSE, + DISCONNECT_FORCED, //load the account - LOGIN_REQUEST = 10, - LOGIN_RESPONSE = 11, + LOGIN_REQUEST, + LOGIN_RESPONSE, //unload the account - LOGOUT_REQUEST = 12, - LOGOUT_RESPONSE = 13, + LOGOUT_REQUEST, + LOGOUT_RESPONSE, //shut down the server - SHUTDOWN_REQUEST = 14, + SHUTDOWN_REQUEST, //------------------------- //RegionPacket @@ -76,8 +76,8 @@ enum class SerialPacketType { //------------------------- //map data - REGION_REQUEST = 15, //NOTE: technically a query - REGION_CONTENT = 16, + REGION_REQUEST, //NOTE: technically a query + REGION_CONTENT, //------------------------- //CharacterPacket @@ -89,22 +89,40 @@ enum class SerialPacketType { //------------------------- //character management - CHARACTER_CREATE = 17, - CHARACTER_DELETE = 18, - CHARACTER_LOAD = 19, - CHARACTER_UNLOAD = 20, + CHARACTER_CREATE, + CHARACTER_DELETE, + CHARACTER_LOAD, + CHARACTER_UNLOAD, //find out info from the server - QUERY_CHARACTER_EXISTS = 21, - QUERY_CHARACTER_STATS = 22, - QUERY_CHARACTER_LOCATION = 23, + QUERY_CHARACTER_EXISTS, + QUERY_CHARACTER_STATS, + QUERY_CHARACTER_LOCATION, //set the info in the server - CHARACTER_SET_ROOM = 24, - CHARACTER_SET_ORIGIN = 25, - CHARACTER_SET_MOTION = 26, + CHARACTER_MOVEMENT, + CHARACTER_ATTACK, - //TODO: enemy management + //admin control +// ADMIN_SET_CHARACTER_ORIGIN, + + //------------------------- + //MonsterPacket + // monster index, + // handle, avatar, hitbox + // room index, origin, motion + // TODO: attack data + //------------------------- + + MONSTER_CREATE, + MONSTER_DELETE, + + QUERY_MONSTER_EXISTS, //a list of monsters in a room + QUERY_MONSTER_STATS, //statistics of a specific monster type or instance + QUERY_MONSTER_LOCATION, //umm... + + MONSTER_MOVEMENT, //monster movement + MONSTER_ATTACK, //------------------------- //TextPacket @@ -112,20 +130,21 @@ enum class SerialPacketType { //------------------------- //general speech - TEXT_BROADCAST = 27, + TEXT_BROADCAST, //rejection/error messages - JOIN_REJECTION = 28, - LOGIN_REJECTION = 29, - REGION_REJECTION = 30, - CHARACTER_REJECTION = 31, - SHUTDOWN_REJECTION = 32, + JOIN_REJECTION, + LOGIN_REJECTION, + REGION_REJECTION, + CHARACTER_REJECTION, + MONSTER_REJECTION, + SHUTDOWN_REJECTION, //------------------------- //not used //------------------------- - LAST = 33 + LAST }; #endif \ No newline at end of file diff --git a/common/network/serial_utility.cpp b/common/network/serial_utility.cpp index a0d2c61..e258a9e 100644 --- a/common/network/serial_utility.cpp +++ b/common/network/serial_utility.cpp @@ -24,6 +24,7 @@ //packet types #include "character_packet.hpp" #include "client_packet.hpp" +#include "monster_packet.hpp" #include "region_packet.hpp" #include "server_packet.hpp" #include "text_packet.hpp" @@ -75,16 +76,25 @@ void serializePacket(void* buffer, SerialPacketBase* packet) { case SerialPacketType::QUERY_CHARACTER_EXISTS: case SerialPacketType::QUERY_CHARACTER_STATS: case SerialPacketType::QUERY_CHARACTER_LOCATION: - case SerialPacketType::CHARACTER_SET_ROOM: - case SerialPacketType::CHARACTER_SET_ORIGIN: - case SerialPacketType::CHARACTER_SET_MOTION: + case SerialPacketType::CHARACTER_MOVEMENT: + case SerialPacketType::CHARACTER_ATTACK: serializeCharacter(buffer, static_cast(packet)); break; + case SerialPacketType::MONSTER_CREATE: + case SerialPacketType::MONSTER_DELETE: + case SerialPacketType::QUERY_MONSTER_EXISTS: + case SerialPacketType::QUERY_MONSTER_STATS: + case SerialPacketType::QUERY_MONSTER_LOCATION: + case SerialPacketType::MONSTER_MOVEMENT: + case SerialPacketType::MONSTER_ATTACK: + serializeMonster(buffer, static_cast(packet)); + break; case SerialPacketType::TEXT_BROADCAST: case SerialPacketType::JOIN_REJECTION: case SerialPacketType::LOGIN_REJECTION: case SerialPacketType::REGION_REJECTION: case SerialPacketType::CHARACTER_REJECTION: + case SerialPacketType::MONSTER_REJECTION: case SerialPacketType::SHUTDOWN_REJECTION: serializeText(buffer, static_cast(packet)); break; @@ -126,16 +136,25 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) { case SerialPacketType::QUERY_CHARACTER_EXISTS: case SerialPacketType::QUERY_CHARACTER_STATS: case SerialPacketType::QUERY_CHARACTER_LOCATION: - case SerialPacketType::CHARACTER_SET_ROOM: - case SerialPacketType::CHARACTER_SET_ORIGIN: - case SerialPacketType::CHARACTER_SET_MOTION: + case SerialPacketType::CHARACTER_MOVEMENT: + case SerialPacketType::CHARACTER_ATTACK: deserializeCharacter(buffer, static_cast(packet)); break; + case SerialPacketType::MONSTER_CREATE: + case SerialPacketType::MONSTER_DELETE: + case SerialPacketType::QUERY_MONSTER_EXISTS: + case SerialPacketType::QUERY_MONSTER_STATS: + case SerialPacketType::QUERY_MONSTER_LOCATION: + case SerialPacketType::MONSTER_MOVEMENT: + case SerialPacketType::MONSTER_ATTACK: + deserializeMonster(buffer, static_cast(packet)); + break; case SerialPacketType::TEXT_BROADCAST: case SerialPacketType::JOIN_REJECTION: case SerialPacketType::LOGIN_REJECTION: case SerialPacketType::REGION_REJECTION: case SerialPacketType::CHARACTER_REJECTION: + case SerialPacketType::MONSTER_REJECTION: case SerialPacketType::SHUTDOWN_REJECTION: deserializeText(buffer, static_cast(packet)); break; diff --git a/server/server_application.hpp b/server/server_application.hpp index 8ed4f24..05a75a6 100644 --- a/server/server_application.hpp +++ b/server/server_application.hpp @@ -101,9 +101,8 @@ private: void HandleCharacterUnload(CharacterPacket* const); //character movement - void HandleCharacterSetRoom(CharacterPacket* const); - void HandleCharacterSetOrigin(CharacterPacket* const); - void HandleCharacterSetMotion(CharacterPacket* const); + void HandleCharacterMovement(CharacterPacket* const); + void HandleCharacterAttack(CharacterPacket* const); //utility methods void PumpPacket(SerialPacket* const); diff --git a/server/server_character_methods.cpp b/server/server_character_methods.cpp index 910a6cc..bf302b7 100644 --- a/server/server_character_methods.cpp +++ b/server/server_character_methods.cpp @@ -175,105 +175,15 @@ void ServerApplication::HandleCharacterUnload(CharacterPacket* const argPacket) //character movement //------------------------- -//TODO: ? Could replace this verbosity with a "verify" method, taking a client, account and character ptr as arguments +//TODO: Could replace this verbosity with a "verify" method, taking a client, account and character ptr as arguments -void ServerApplication::HandleCharacterSetRoom(CharacterPacket* const argPacket) { +void ServerApplication::HandleCharacterMovement(CharacterPacket* const argPacket) { //get the specified objects AccountData* accountData = accountMgr.Get(argPacket->accountIndex); CharacterData* characterData = characterMgr.Get(argPacket->characterIndex); if (!accountData || !characterData) { - throw(std::runtime_error("Failed to set character room, missing data")); - } - - //get this account's client - ClientData* clientData = clientMgr.Get(accountData->GetClientIndex()); - - //check for fraud - if (clientData->GetAddress() != argPacket->srcAddress) { - std::cerr << "Falsified set character origin targeting uid(" << argPacket->characterIndex << ")" << std::endl; - return; - } - - //check if allowed - if (characterData->GetOwner() != argPacket->accountIndex && !accountData->GetModerator() && !accountData->GetAdministrator()) { - //TODO: send to the client? - std::cerr << "Failed to set character room due to lack of permissions targeting uid(" << argPacket->characterIndex << ")" << std::endl; - return; - } - - //pop from the old room - roomMgr.PopCharacter(characterData); - - //set the character's room, zero it's origin, zero it's motion - characterData->SetRoomIndex(argPacket->roomIndex); - characterData->SetOrigin({0, 0}); - characterData->SetMotion({0, 0}); - - //push to the new room - roomMgr.PushCharacter(characterData); - - //update the clients - CharacterPacket newPacket; - CopyCharacterToPacket(&newPacket, argPacket->characterIndex); - newPacket.type = SerialPacketType::CHARACTER_SET_ROOM; - PumpPacket(&newPacket); -} - -void ServerApplication::HandleCharacterSetOrigin(CharacterPacket* const argPacket) { - //get the specified objects - AccountData* accountData = accountMgr.Get(argPacket->accountIndex); - CharacterData* characterData = characterMgr.Get(argPacket->characterIndex); - - if (!accountData || !characterData) { - throw(std::runtime_error("Failed to set character origin, missing data")); - } - - //get this account's client - ClientData* clientData = clientMgr.Get(accountData->GetClientIndex()); - - //check for fraud - if (clientData->GetAddress() != argPacket->srcAddress) { - std::cerr << "Falsified set character origin targeting uid(" << argPacket->characterIndex << ")" << std::endl; - return; - } - - //check if allowed - if (characterData->GetOwner() != argPacket->accountIndex && !accountData->GetModerator() && !accountData->GetAdministrator()) { - //TODO: send to the client? - std::cerr << "Failed to set character origin due to lack of permissions targeting uid(" << argPacket->characterIndex << ")" << std::endl; - return; - } - - //set the character's origin, zero it's motion - characterData->SetOrigin(argPacket->origin); - characterData->SetMotion({0, 0}); - - //update the clients - CharacterPacket newPacket; - CopyCharacterToPacket(&newPacket, argPacket->characterIndex); - newPacket.type = SerialPacketType::CHARACTER_SET_ORIGIN; - PumpPacket(&newPacket); -} - -void ServerApplication::HandleCharacterSetMotion(CharacterPacket* const argPacket) { - //get the specified objects - AccountData* accountData = accountMgr.Get(argPacket->accountIndex); - - if (!accountData) { - std::ostringstream msg; - msg << "Failed to set character motion, missing account: Index " << argPacket->accountIndex << "; "; - msg << "Number of accounts loaded: " << accountMgr.GetContainer()->size(); - throw(std::runtime_error(msg.str())); - } - - CharacterData* characterData = characterMgr.Get(argPacket->characterIndex); - - if (!characterData) { - std::ostringstream msg; - msg << "Failed to set character motion, missing character: Index " << argPacket->characterIndex << "; "; - msg << "Number of characters loaded: " << characterMgr.GetContainer()->size(); - throw(std::runtime_error(msg.str())); + throw(std::runtime_error("Failed to move a character, missing data")); } //get this account's client @@ -292,13 +202,38 @@ void ServerApplication::HandleCharacterSetMotion(CharacterPacket* const argPacke return; } - //set the character's origin and motion - characterData->SetOrigin(argPacket->origin); - characterData->SetMotion(argPacket->motion); + //check if moving rooms + if (characterData->GetRoomIndex() != argPacket->roomIndex) { + //delete from the old room + CharacterPacket newPacket; + CopyCharacterToPacket(&newPacket, argPacket->characterIndex); + newPacket.type = SerialPacketType::CHARACTER_DELETE; + PumpPacketProximity(&newPacket, characterData->GetRoomIndex(), characterData->GetOrigin(), -1); - //update the clients - CharacterPacket newPacket; - CopyCharacterToPacket(&newPacket, argPacket->characterIndex); - newPacket.type = SerialPacketType::CHARACTER_SET_MOTION; - PumpPacketProximity(&newPacket, characterData->GetRoomIndex(), characterData->GetOrigin(), -1); + //move the character between rooms + roomMgr.PopCharacter(characterData); + characterData->SetRoomIndex(argPacket->roomIndex); + roomMgr.PushCharacter(characterData); + + //create in the new room + CopyCharacterToPacket(&newPacket, argPacket->characterIndex); + newPacket.type = SerialPacketType::CHARACTER_CREATE; + PumpPacketProximity(&newPacket, characterData->GetRoomIndex(), characterData->GetOrigin(), -1); + } + //if not moving between rooms + else { + //set the character's origin and motion + characterData->SetOrigin(argPacket->origin); + characterData->SetMotion(argPacket->motion); + + //update the clients + CharacterPacket newPacket; + CopyCharacterToPacket(&newPacket, argPacket->characterIndex); + newPacket.type = SerialPacketType::CHARACTER_MOVEMENT; + PumpPacketProximity(&newPacket, characterData->GetRoomIndex(), characterData->GetOrigin(), -1); + } } + +void ServerApplication::HandleCharacterAttack(CharacterPacket* const) { + //TODO: bounce graphical attack data +} \ No newline at end of file diff --git a/server/server_logic.cpp b/server/server_logic.cpp index 96ca1fe..24192f2 100644 --- a/server/server_logic.cpp +++ b/server/server_logic.cpp @@ -276,27 +276,24 @@ void ServerApplication::HandlePacket(SerialPacket* const argPacket) { HandleCharacterUnload(static_cast(argPacket)); break; - //character movement - case SerialPacketType::CHARACTER_SET_ROOM: - HandleCharacterSetRoom(static_cast(argPacket)); +/* case SerialPacketType::QUERY_CHARACTER_EXISTS: + // break; - case SerialPacketType::CHARACTER_SET_ORIGIN: - HandleCharacterSetOrigin(static_cast(argPacket)); - break; - case SerialPacketType::CHARACTER_SET_MOTION: - HandleCharacterSetMotion(static_cast(argPacket)); - break; -/* case SerialPacketType::QUERY_CHARACTER_STATS: -// HandleCharacterStatsRequest(static_cast(argPacket)); + // break; case SerialPacketType::QUERY_CHARACTER_LOCATION: -// HandleCharacterStatsRequest(static_cast(argPacket)); + // break; - case SerialPacketType::TEXT_BROADCAST: -// HandleCharacterStatsRequest(static_cast(argPacket)); +*/ + //character movement + case SerialPacketType::CHARACTER_MOVEMENT: + HandleCharacterMovement(static_cast(argPacket)); break; - + case SerialPacketType::CHARACTER_ATTACK: + HandleCharacterAttack(static_cast(argPacket)); + break; +/* //enemy management //TODO: enemy management diff --git a/todo.txt b/todo.txt index 5e87dc8..5b9dc74 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,5 @@ TODO: In need of script APIs (list) * Characters - * Monsters - * Waypoints TODO: Account passwords (list) * backbone account server OR @@ -9,7 +7,6 @@ TODO: Account passwords (list) * ... * salts & hashes -TODO: Waypoints, with positions and trigger zones (collision areas) for doors, monster spawns, etc. TODO: Fix shoddy movement TODO: Periodic mass server saves TODO: Remove the big "Shut Down" button (currently broken...)