From f7f59e8ac87603054f5e993c855cf2d2beaa65c0 Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Tue, 21 Jan 2014 09:52:48 +0100 Subject: [PATCH 01/76] GameServer - Added more protocols and modified old ones a bit. Also added a new project that starts both server and client --- Code/DanBias.sln | 15 ++ .../Game/DanBiasGame/GameClientRecieverFunc.h | 32 +-- .../DanBiasGame/GameClientState/GameState.cpp | 16 +- .../GameSession/GameSession_Events.cpp | 38 ++- .../GameSession/GameSession_Logic.cpp | 1 - Code/Game/GameLogic/Game.cpp | 6 +- Code/Game/GameLogic/Game.h | 2 + Code/Game/GameLogic/GameAPI.h | 6 + Code/Game/GameLogic/Game_PlayerData.cpp | 5 + Code/Game/GameLogic/Player.cpp | 5 + Code/Game/GameLogic/Player.h | 1 + Code/Game/GameProtocols/ObjectProtocols.h | 196 +++++++++----- Code/Game/GameProtocols/PlayerProtocols.h | 96 +++++-- .../GameProtocols/ProtocolIdentificationID.h | 13 +- Code/Game/aDanBiasGameLauncher/Launcher.cpp | 62 +++++ .../aDanBiasGameLauncher.vcxproj | 187 +++++++++++++ .../aDanBiasGameLauncher.vcxproj.user | 22 ++ Code/Misc/DynamicArray.h | 7 + Code/Misc/Resource/ResourceManager.cpp | 247 +++++++++++++++++ Code/Misc/Resource/ResourceManager.h | 160 +++++++++++ Code/Tester/MainTest.cpp | 253 ------------------ Code/Tester/Tester.vcxproj | 99 ------- Code/Tester/Tester.vcxproj.filters | 22 -- Code/Tester/Tester.vcxproj.user | 6 - 24 files changed, 958 insertions(+), 539 deletions(-) create mode 100644 Code/Game/aDanBiasGameLauncher/Launcher.cpp create mode 100644 Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj create mode 100644 Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj.user create mode 100644 Code/Misc/Resource/ResourceManager.cpp create mode 100644 Code/Misc/Resource/ResourceManager.h delete mode 100644 Code/Tester/MainTest.cpp delete mode 100644 Code/Tester/Tester.vcxproj delete mode 100644 Code/Tester/Tester.vcxproj.filters delete mode 100644 Code/Tester/Tester.vcxproj.user diff --git a/Code/DanBias.sln b/Code/DanBias.sln index 3a606491..68c4cdfa 100644 --- a/Code/DanBias.sln +++ b/Code/DanBias.sln @@ -44,6 +44,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameProtocols", "Game\GameP EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasServerLauncher", "Game\DanBiasServerLauncher\DanBiasServerLauncher.vcxproj", "{060B1890-CBF3-4808-BA99-A4776222093B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aDanBiasGameLauncher", "Game\aDanBiasGameLauncher\aDanBiasGameLauncher.vcxproj", "{666FEA52-975F-41CD-B224-B19AF3C0ABBA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Mixed Platforms = Debug|Mixed Platforms @@ -270,6 +272,18 @@ Global {060B1890-CBF3-4808-BA99-A4776222093B}.Release|Win32.Build.0 = Release|Win32 {060B1890-CBF3-4808-BA99-A4776222093B}.Release|x64.ActiveCfg = Release|x64 {060B1890-CBF3-4808-BA99-A4776222093B}.Release|x64.Build.0 = Release|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Win32.ActiveCfg = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Win32.Build.0 = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|x64.ActiveCfg = Debug|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|x64.Build.0 = Debug|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Mixed Platforms.Build.0 = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.ActiveCfg = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.Build.0 = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.ActiveCfg = Release|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -285,5 +299,6 @@ Global {8690FDDF-C5B7-4C42-A337-BD5243F29B85} = {20720CA7-795C-45AD-A302-9383A6DD503A} {DA2AA800-ED64-4649-8B3B-E7F1E3968B78} = {20720CA7-795C-45AD-A302-9383A6DD503A} {060B1890-CBF3-4808-BA99-A4776222093B} = {20720CA7-795C-45AD-A302-9383A6DD503A} + {666FEA52-975F-41CD-B224-B19AF3C0ABBA} = {20720CA7-795C-45AD-A302-9383A6DD503A} EndGlobalSection EndGlobal diff --git a/Code/Game/DanBiasGame/GameClientRecieverFunc.h b/Code/Game/DanBiasGame/GameClientRecieverFunc.h index 68cb19d6..5e129fca 100644 --- a/Code/Game/DanBiasGame/GameClientRecieverFunc.h +++ b/Code/Game/DanBiasGame/GameClientRecieverFunc.h @@ -26,7 +26,7 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject } } break; - case protocol_Gameplay_PlayerNavigation: + case protocol_Gameplay_PlayerMovement: { Client::GameClientState::KeyInput* protocolData = new Client::GameClientState::KeyInput; for(int i = 0; i< 6; i++) @@ -40,21 +40,21 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject protocolData = NULL; } break; - case protocol_Gameplay_PlayerPosition: - { - Client::GameClientState::PlayerPos* protocolData = new Client::GameClientState::PlayerPos; - for(int i = 0; i< 3; i++) - { - protocolData->playerPos[i] = p[i].value.netFloat; - } - if(dynamic_cast(gameClientState)) - ((Client::GameState*)gameClientState)->Protocol(protocolData); - delete protocolData; - protocolData = NULL; - } - break; + //case protocol_Gameplay_PlayerPosition: + // { + // Client::GameClientState::PlayerPos* protocolData = new Client::GameClientState::PlayerPos; + // for(int i = 0; i< 3; i++) + // { + // protocolData->playerPos[i] = p[i].value.netFloat; + // } + // if(dynamic_cast(gameClientState)) + // ((Client::GameState*)gameClientState)->Protocol(protocolData); + // delete protocolData; + // protocolData = NULL; + // } + // break; - case protocol_Gameplay_CreateObject: + case protocol_Gameplay_ObjectCreate: { Client::GameClientState::NewObj* protocolData = new Client::GameClientState::NewObj; protocolData->object_ID = p[1].value.netInt; @@ -72,7 +72,7 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject protocolData = NULL; } break; - case protocol_Gameplay_RemoveObject: + case protocol_Gameplay_ObjectDisabled: { Client::GameClientState::RemoveObj* protocolData = new Client::GameClientState::RemoveObj; protocolData->object_ID = p[1].value.netInt; diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index e0b0054a..f1474eed 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -131,10 +131,8 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI GameLogic::Protocol_PlayerMovement movePlayer; movePlayer.bForward = false; movePlayer.bBackward = false; - movePlayer.bStrafeLeft = false; - movePlayer.bStrafeRight = false; - movePlayer.bTurnLeft = false; - movePlayer.bTurnRight = false; + movePlayer.bLeft = false; + movePlayer.bRight = false; if(KeyInput->IsKeyPressed(DIK_W)) { @@ -165,7 +163,7 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI { if(!key_strafeLeft) { - movePlayer.bStrafeLeft = true; + movePlayer.bLeft = true; send = true; key_strafeLeft = true; } @@ -177,7 +175,7 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI { if(!key_strafeRight) { - movePlayer.bStrafeRight = true; + movePlayer.bRight = true; send = true; key_strafeRight = true; } @@ -221,7 +219,7 @@ bool GameState::Render() Oyster::Graphics::API::SetView(privData->view); Oyster::Graphics::API::SetProjection(privData->proj); Oyster::Graphics::API::NewFrame(); - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { privData->object[i]->Render(); } @@ -230,7 +228,7 @@ bool GameState::Render() } bool GameState::Release() { - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { privData->object[i]->Release(); delete privData->object[i]; @@ -305,7 +303,7 @@ void GameState::Protocol( NewObj* newObj ) void DanBias::Client::GameState::Protocol( RemoveObj* obj ) { - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { if(privData->object[i]->GetId() == obj->object_ID) { diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index 164e5748..1fa526f5 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -42,35 +42,29 @@ namespace DanBias { switch (p[protocol_INDEX_ID].value.netShort) { - case protocol_Gameplay_PlayerNavigation: + case protocol_Gameplay_PlayerMovement: { - - //Oyster::Math::Float4x4 world = Oyster::Math::Matrix::identity; if(p[1].value.netBool) //bool bForward; - //world.v[3].x = 2; c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_FORWARD); if(p[2].value.netBool) //bool bBackward; - //world.v[3].x = -2; c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_BACKWARD); - if(p[5].value.netBool) //bool bStrafeRight; - //world.v[3].y = 2; - c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_RIGHT); - if(p[6].value.netBool) //bool bStrafeLeft; - //world.v[3].y = -2; + if(p[3].value.netBool) //bool bStrafeLeft; c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_LEFT); + if(p[4].value.netBool) //bool bStrafeRight; + c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_RIGHT); } break; case protocol_Gameplay_PlayerMouseMovement: + { + Protocol_PlayerMouse m; m = p; + c->GetPlayer()->Rotate(m.dxMouse, m.dyMouse); + } + break; + case protocol_Gameplay_PlayerChangeWeapon: break; - case protocol_Gameplay_PlayerPosition: - - break; - case protocol_Gameplay_CreateObject: - - break; - case protocol_Gameplay_ObjectPosition: - + case protocol_Gameplay_PlayerDamage: + break; } } @@ -110,11 +104,9 @@ namespace DanBias void GameSession::ObjectMove(GameLogic::IObjectData* movedObject) { - //if (movedObject->) - //{ - // - //} - //movedObject->GetOrientation(); + movedObject->GetID(); + movedObject->GetOrientation(); + } }//End namespace DanBias diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp index a0010f1f..7c47d4a7 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp @@ -67,7 +67,6 @@ namespace DanBias Oyster::Math::Float4x4 world = this->clients[0]->GetPlayer()->GetOrientation(); Protocol_ObjectPosition p(world, 1); Send(p.GetProtocol()); - //Sleep(100); } } diff --git a/Code/Game/GameLogic/Game.cpp b/Code/Game/GameLogic/Game.cpp index 5ab72dcc..4380777e 100644 --- a/Code/Game/GameLogic/Game.cpp +++ b/Code/Game/GameLogic/Game.cpp @@ -151,9 +151,9 @@ float Game::GetFrameTime() const void Game::PhysicsOnMove(const ICustomBody *object) { - IObjectData* temp = 0; - //IObjectData* temp = ((IObjectData*)object->GetDataTag()); - if(gameInstance.onMoveFnc) gameInstance.onMoveFnc(temp); + IObjectData* temp = (IObjectData*)object->GetCustomTag(); + + if(gameInstance.onMoveFnc && temp) gameInstance.onMoveFnc(temp); } void Game::PhysicsOnDestroy(::Utility::DynamicMemory::UniquePointer proto) { diff --git a/Code/Game/GameLogic/Game.h b/Code/Game/GameLogic/Game.h index b6f19eb8..11c8d325 100644 --- a/Code/Game/GameLogic/Game.h +++ b/Code/Game/GameLogic/Game.h @@ -39,6 +39,8 @@ namespace GameLogic Oyster::Math::Float4x4 GetOrientation() override; int GetID() const override; OBJECT_TYPE GetObjectType() const override; + void Rotate(const float x, const float y) override; + Player *player; }; diff --git a/Code/Game/GameLogic/GameAPI.h b/Code/Game/GameLogic/GameAPI.h index 8e05c8cd..12f5022f 100644 --- a/Code/Game/GameLogic/GameAPI.h +++ b/Code/Game/GameLogic/GameAPI.h @@ -75,6 +75,12 @@ namespace GameLogic ********************************************************/ virtual void Move(const PLAYER_MOVEMENT &movement) = 0; + /** Relative rotation around given axis + * @param x: The relative x axis + * @param y: The relative y axis + **/ + virtual void Rotate(const float x, const float y) = 0; + /******************************************************** * Uses the chosen players weapon based on input * @param Usage: enum value on what kind of action is to be taken diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index 58fe6616..e651a02f 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -6,6 +6,7 @@ using namespace GameLogic; Game::PlayerData::PlayerData() { this->player = new Player(); + this->player->GetRigidBody()->SetCustomTag(this); } Game::PlayerData::PlayerData(int playerID,int teamID) { @@ -47,4 +48,8 @@ int Game::PlayerData::GetTeamID() const OBJECT_TYPE Game::PlayerData::GetObjectType() const { return this->player->GetType(); +} +void Game::PlayerData::Rotate(const float x, const float y) +{ + this->player->Rotate(x, y); } \ No newline at end of file diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index a7ca4df4..96ee5cfd 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -88,6 +88,11 @@ void Player::Respawn(Oyster::Math::Float3 spawnPoint) this->lookDir = Oyster::Math::Float4(1,0,0); } +void Player::Rotate(float x, float y) +{ + this->setState.AddRotation(Oyster::Math::Float4(x, y)); +} + void Player::Jump() { diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index 5244f541..39e8f5f6 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -41,6 +41,7 @@ namespace GameLogic ********************************************************/ void Respawn(Oyster::Math::Float3 spawnPoint); + void Rotate(float x, float y); bool IsWalking(); bool IsJumping(); diff --git a/Code/Game/GameProtocols/ObjectProtocols.h b/Code/Game/GameProtocols/ObjectProtocols.h index 2a538714..e52a4d2f 100644 --- a/Code/Game/GameProtocols/ObjectProtocols.h +++ b/Code/Game/GameProtocols/ObjectProtocols.h @@ -5,9 +5,128 @@ #include "ProtocolIdentificationID.h" - namespace GameLogic { + struct Protocol_ObjectPosition :public Oyster::Network::CustomProtocolObject + { + int object_ID; + float worldMatrix[16]; + // look at dir + + Protocol_ObjectPosition() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + + for (int i = 2; i <= 17; i++) + { + this->protocol[i].type = Oyster::Network::NetAttributeType_Float; + } + object_ID = -1; + memset(&worldMatrix[0], 0, sizeof(float) * 16); + } + Protocol_ObjectPosition(float m[16], int id) + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + + for (int i = 2; i <= 17; i++) + { + this->protocol[i].type = Oyster::Network::NetAttributeType_Float; + } + + object_ID = id; + memcpy(&worldMatrix[0], &m[0], sizeof(float)*16); + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = object_ID; + for (int i = 2; i <= 17; i++) + { + this->protocol[i].value = worldMatrix[i-2]; + } + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + + struct Protocol_ObjectEnable :public Oyster::Network::CustomProtocolObject + { + int object_ID; + float worldMatrix[16]; + // look at dir + + Protocol_ObjectEnable() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectEnabled; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + + for (int i = 2; i <= 17; i++) + { + this->protocol[i].type = Oyster::Network::NetAttributeType_Float; + } + object_ID = -1; + memset(&worldMatrix[0], 0, sizeof(float) * 16); + } + Protocol_ObjectEnable(float m[16], int id) + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectEnabled; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + + for (int i = 2; i <= 17; i++) + { this->protocol[i].type = Oyster::Network::NetAttributeType_Float; } + + object_ID = id; + memcpy(&worldMatrix[0], &m[0], sizeof(float)*16); + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = object_ID; + for (int i = 2; i <= 17; i++) + { + this->protocol[i].value = worldMatrix[i-2]; + } + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + + struct Protocol_ObjectDisable :public Oyster::Network::CustomProtocolObject + { + int object_ID; + float timer; + + Protocol_ObjectDisable() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDisabled; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[2].type = Oyster::Network::NetAttributeType_Float; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = object_ID; + this->protocol[2].value = timer; + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + struct Protocol_CreateObject :public Oyster::Network::CustomProtocolObject { int object_ID; @@ -17,7 +136,7 @@ namespace GameLogic Protocol_CreateObject() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_CreateObject; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectCreate; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; @@ -30,7 +149,7 @@ namespace GameLogic } Protocol_CreateObject(float m[16], int id, char *path) { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_CreateObject; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectCreate; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Int; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; @@ -70,77 +189,6 @@ namespace GameLogic - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; - - struct Protocol_ObjectPosition :public Oyster::Network::CustomProtocolObject - { - int object_ID; - float worldMatrix[16]; - // look at dir - - Protocol_ObjectPosition() - { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Int; - - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - - for (int i = 2; i <= 17; i++) - { - this->protocol[i].type = Oyster::Network::NetAttributeType_Float; - } - object_ID = -1; - memset(&worldMatrix[0], 0, sizeof(float) * 16); - } - Protocol_ObjectPosition(float m[16], int id) - { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Int; - - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - - for (int i = 2; i <= 17; i++) - { - this->protocol[i].type = Oyster::Network::NetAttributeType_Float; - } - - object_ID = id; - memcpy(&worldMatrix[0], &m[0], sizeof(float)*16); - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - this->protocol[1].value = object_ID; - for (int i = 2; i <= 17; i++) - { - this->protocol[i].value = worldMatrix[i-2]; - } - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; - - struct Protocol_RemoveObject :public Oyster::Network::CustomProtocolObject - { - int object_ID; - - Protocol_RemoveObject() - { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_RemoveObject; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - - this->protocol[1].value = object_ID; return &protocol; } diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index 96686807..eb89b1c6 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -10,7 +10,6 @@ #include "ProtocolIdentificationID.h" - namespace GameLogic { struct Protocol_PlayerMovement :public Oyster::Network::CustomProtocolObject @@ -18,31 +17,25 @@ namespace GameLogic bool bForward; bool bBackward; - bool bTurnLeft; - bool bTurnRight; - bool bStrafeRight; - bool bStrafeLeft; + bool bLeft; + bool bRight; Protocol_PlayerMovement() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerNavigation; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerMovement; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; this->protocol[2].type = Oyster::Network::NetAttributeType_Bool; this->protocol[3].type = Oyster::Network::NetAttributeType_Bool; this->protocol[4].type = Oyster::Network::NetAttributeType_Bool; - this->protocol[5].type = Oyster::Network::NetAttributeType_Bool; - this->protocol[6].type = Oyster::Network::NetAttributeType_Bool; } const Protocol_PlayerMovement& operator=(Oyster::Network::CustomNetProtocol& val) { bForward = val[1].value.netBool; bBackward = val[2].value.netBool; - bTurnLeft = val[3].value.netBool; - bTurnRight = val[4].value.netBool; - bStrafeRight = val[5].value.netBool; - bStrafeLeft = val[6].value.netBool; + bLeft = val[3].value.netBool; + bRight = val[4].value.netBool; return *this; } @@ -50,10 +43,8 @@ namespace GameLogic { this->protocol[1].value = bForward; this->protocol[2].value = bBackward; - this->protocol[3].value = bTurnLeft; - this->protocol[4].value = bTurnRight; - this->protocol[5].value = bStrafeRight; - this->protocol[6].value = bStrafeLeft; + this->protocol[3].value = bLeft; + this->protocol[4].value = bRight; return &protocol; } @@ -97,15 +88,14 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_PlayerPosition :public Oyster::Network::CustomProtocolObject + struct Protocol_PlayerChangeWeapon :public Oyster::Network::CustomProtocolObject { - float position[3]; - // look at dir + int ID; - Protocol_PlayerPosition() + Protocol_PlayerChangeWeapon() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerPosition; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerChangeWeapon; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Float; @@ -113,19 +103,69 @@ namespace GameLogic this->protocol[3].type = Oyster::Network::NetAttributeType_Float; } - const Protocol_PlayerPosition& operator=(Oyster::Network::CustomNetProtocol& val) + const Protocol_PlayerChangeWeapon& operator=(Oyster::Network::CustomNetProtocol& val) { - position[0] = val[1].value.netFloat; - position[1] = val[2].value.netFloat; - position[2] = val[3].value.netFloat; + return *this; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + + struct Protocol_PlayerDamage :public Oyster::Network::CustomProtocolObject + { + + int hp; + // look at dir + + Protocol_PlayerDamage() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerDamage; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + + } + const Protocol_PlayerDamage& operator=(Oyster::Network::CustomNetProtocol& val) + { + hp = val[1].value.netInt; + return *this; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value =hp; + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + + struct Protocol_PlayerPickup :public Oyster::Network::CustomProtocolObject + { + int pickupID; + + Protocol_PlayerPickup() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerPickup; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + + } + const Protocol_PlayerPickup& operator=(Oyster::Network::CustomNetProtocol& val) + { + pickupID = val[3].value.netInt; return *this; } Oyster::Network::CustomNetProtocol* GetProtocol() override { - this->protocol[1].value = position[0]; - this->protocol[2].value = position[1]; - this->protocol[3].value = position[2]; + this->protocol[3].value = pickupID; return &protocol; } diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index fb54e403..a48d5594 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -45,13 +45,16 @@ /********* GAMEPLAY PROTOCOLS ***************************************************************************************************/ /***********[ 300 - 400 ]***********/ #define protocol_GameplayMIN 300 -#define protocol_Gameplay_PlayerNavigation 300 +#define protocol_Gameplay_PlayerMovement 300 #define protocol_Gameplay_PlayerMouseMovement 301 -#define protocol_Gameplay_PlayerPosition 302 -#define protocol_Gameplay_CreateObject 303 -#define protocol_Gameplay_RemoveObject 304 +#define protocol_Gameplay_PlayerChangeWeapon 302 +#define protocol_Gameplay_PlayerDamage 303 +#define protocol_Gameplay_PlayerPickup 304 #define protocol_Gameplay_ObjectPosition 305 -#define protocol_Gameplay_Initiate 306 +#define protocol_Gameplay_ObjectEnabled 306 +#define protocol_Gameplay_ObjectDisabled 307 +#define protocol_Gameplay_ObjectCreate 308 +#define protocol_Gameplay_Initiate 309 #define protocol_GameplayMAX 399 diff --git a/Code/Game/aDanBiasGameLauncher/Launcher.cpp b/Code/Game/aDanBiasGameLauncher/Launcher.cpp new file mode 100644 index 00000000..8f8e6252 --- /dev/null +++ b/Code/Game/aDanBiasGameLauncher/Launcher.cpp @@ -0,0 +1,62 @@ + +#define NOMINMAX +#include +#include + +#include "DanBiasGame.h" +#include + +#include + + +void ServerFnc() +{ + + if( DanBias::DanBiasServerAPI::Initiate() == DanBias::DanBiasServerReturn_Sucess) + { + DanBias::DanBiasServerAPI::Run(); + DanBias::DanBiasServerAPI::Release(); + } + Sleep(100); +} +void ClientFnc() +{ + // Game client starter code goes here + DanBias::DanBiasGameDesc gameDesc; + gameDesc.port = 15151; + //gameDesc.port = 15152; + //gameDesc.IP = "193.11.184.196"; + //gameDesc.IP = "193.11.184.31"; + //gameDesc.IP = "194.47.150.56"; + gameDesc.IP = "127.0.0.1"; + + if( DanBias::DanBiasGame::Initiate(gameDesc) == DanBias::DanBiasClientReturn_Sucess) + { + DanBias::DanBiasGame::Run(); + DanBias::DanBiasGame::Release(); + } + Sleep(100); +} + + +int WINAPI WinMain( HINSTANCE hinst, HINSTANCE prevInst, PSTR cmdLine, int cmdShow) +{ + std::thread serverThread; + std::thread clientThread; + + if(SetDllDirectory(L"..\\DLL") == FALSE) + { + return cmdShow; + } + + serverThread = std::thread(ServerFnc); + + Sleep(100); + + clientThread = std::thread(ClientFnc); + + if (serverThread.joinable()) serverThread.join(); + if (clientThread.joinable()) clientThread.join(); + + return cmdShow; +} \ No newline at end of file diff --git a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj new file mode 100644 index 00000000..1a9d892f --- /dev/null +++ b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {666FEA52-975F-41CD-B224-B19AF3C0ABBA} + Win32Proj + aDanBiasGameLauncher + + + + Application + true + v110 + Unicode + + + Application + true + v110 + Unicode + + + Application + false + v110 + true + Unicode + + + Application + false + v110 + true + Unicode + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)..\Bin\Executable\ + $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ + $(ProjectName)_$(PlatformShortName)D + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + + + true + $(SolutionDir)..\Bin\Executable\ + $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ + $(ProjectName)_$(PlatformShortName)D + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + + + false + $(SolutionDir)..\Bin\Executable\ + $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ + $(ProjectName)_$(PlatformShortName) + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + + + false + $(SolutionDir)..\Bin\Executable\ + $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ + $(ProjectName)_$(PlatformShortName) + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + $(SolutionDir)Game\DanBiasServer\Include;$(SolutionDir)Game\DanBiasGame\Include + + + Windows + true + DanBiasGame_$(PlatformShortName)D.dll;DanBiasServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName)D.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + $(SolutionDir)Game\DanBiasServer\Include;$(SolutionDir)Game\DanBiasGame\Include + + + Windows + true + DanBiasGame_$(PlatformShortName)D.dll;DanBiasServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName)D.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + $(SolutionDir)Game\DanBiasServer\Include;$(SolutionDir)Game\DanBiasGame\Include + + + Windows + true + true + true + DanBiasGame_$(PlatformShortName).dll;DanBiasServer_$(PlatformShortName).dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + $(SolutionDir)Game\DanBiasServer\Include;$(SolutionDir)Game\DanBiasGame\Include + + + Windows + true + true + true + DanBiasGame_$(PlatformShortName).dll;DanBiasServer_$(PlatformShortName).dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) + + + + + + + + {2a1bc987-af42-4500-802d-89cd32fc1309} + + + {52380daa-0f4a-4d97-8e57-98df39319caf} + + + + + + \ No newline at end of file diff --git a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj.user b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj.user new file mode 100644 index 00000000..2e28d6f7 --- /dev/null +++ b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj.user @@ -0,0 +1,22 @@ + + + + true + + + $(OutDir) + WindowsLocalDebugger + + + $(OutDir) + WindowsLocalDebugger + + + $(OutDir) + WindowsLocalDebugger + + + $(OutDir) + WindowsLocalDebugger + + \ No newline at end of file diff --git a/Code/Misc/DynamicArray.h b/Code/Misc/DynamicArray.h index 35df725f..e2041f99 100644 --- a/Code/Misc/DynamicArray.h +++ b/Code/Misc/DynamicArray.h @@ -35,6 +35,8 @@ namespace Utility void Resize(unsigned int size); + void Reserve(unsigned int size); + void Swap(unsigned int a, unsigned int b); unsigned int Size() const; @@ -201,6 +203,11 @@ namespace Utility this->data = temp; } + template void DynamicArray::Reserve(unsigned int size) + { + Expand(size); + } + template void DynamicArray::Swap(unsigned int a, unsigned int b) { T temp = this->data[a]; diff --git a/Code/Misc/Resource/ResourceManager.cpp b/Code/Misc/Resource/ResourceManager.cpp new file mode 100644 index 00000000..133ce00b --- /dev/null +++ b/Code/Misc/Resource/ResourceManager.cpp @@ -0,0 +1,247 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// + + +#include "ResourceManager.h" + +using namespace Oyster::Resource; + +struct Oyster::Resource::ResourceData +{ + +} + +ResourceData* FindResource(std::map resources, const HRESOURCE& h) const +{ + for (auto i = resources.begin(); i != resources.end() ; i++) + { + if(i->second->GetResourceHandle() == h) + { + return i->second; + } + } + return 0; +} +ResourceData* FindResource(std::map resources, const wchar_t c[]) const +{ + std::wstring temp = c; + auto t = this->resources.find(c); + if(t == this->resources.end()) return 0; + + return t->second; +} +void SaveResource( std::map resources, OResource* r, bool addNew ) +{ + if(!r) return; + + if(addNew) + { + this->resources[r->GetResourceFilename()] = r; + } + + r->resourceRef.Incref(); +} + + +ResourceManager::ResourceManager() +{ + +} +ResourceManager:: +HRESOURCE OysterResource::LoadResource(const wchar_t* filename, ResourceType type, int customID, bool force) +{ + if(!filename) return 0; + + OResource *resourceData = FindResource(filename); + + if(resourceData) + { + if(force) + { + return OysterResource::ReloadResource(filename); + } + else + { + //Add new reference + resourcePrivate.SaveResource(resourceData, false); + return resourceData->GetResourceHandle(); + } + } + else + { + resourceData = OResource::Load(filename, type); + if(resourceData) + { + resourceData->SetResourceID(customID); + resourcePrivate.SaveResource(resourceData); + } + } + + return resourceData->GetResourceHandle(); +} +HRESOURCE OysterResource::LoadResource(const wchar_t filename[], CustomLoadFunction loadFnc, int customId, bool force) +{ + if(!filename) + { + return 0; + } + if(!loadFnc) + { + return 0; + } + + OResource *resourceData = resourcePrivate.FindResource(filename); + if(resourceData) + { + if(force) + { + return OysterResource::ReloadResource(filename); + } + else + { + //Add new reference + resourcePrivate.SaveResource(resourceData, false); + return resourceData->GetResourceHandle(); + } + } + else + { + resourceData = OResource::Load(filename, loadFnc); + if(resourceData) + { + resourceData->SetResourceID(customId); + resourcePrivate.SaveResource(resourceData); + } + } + if(!resourceData) + { + return 0; + } + return (OHRESOURCE)resourceData->GetResourceHandle(); +} + +OHRESOURCE OysterResource::ReloadResource(const wchar_t filename[]) +{ + OResource *resourceData = resourcePrivate.FindResource(filename); + if(!resourceData) return 0; //The resource has not been loaded + + return OResource::Reload(resourceData)->GetResourceHandle(); +} +OHRESOURCE OysterResource::ReloadResource(OHRESOURCE resource) +{ + OResource *resourceData = resourcePrivate.FindResource(resource); + if(!resourceData) return 0; //The resource has not been loaded + + return OResource::Reload(resourceData)->GetResourceHandle(); +} + +void OysterResource::Clean() +{ + auto i = resourcePrivate.resources.begin(); + auto last = resourcePrivate.resources.end(); + + for (i; i != last; i++) + { + //Remove all the references + while (!OResource::Release(i->second)); + + std::wstring temp = i->second->GetResourceFilename(); + delete resourcePrivate.resources[temp]; + + + } + resourcePrivate.resources.clear(); +} +void OysterResource::ReleaseResource(const OHRESOURCE& resourceData) +{ + OResource* t = resourcePrivate.FindResource(resourceData); + if(t) + { + if(OResource::Release(t)) + { + std::wstring temp = t->GetResourceFilename(); + delete resourcePrivate.resources[temp]; + resourcePrivate.resources.erase(temp); + } + } +} +void OysterResource::ReleaseResource(const wchar_t filename[]) +{ + OResource* t = resourcePrivate.FindResource(filename); + if(t) + { + if(OResource::Release(t)) + { + std::wstring temp = t->GetResourceFilename(); + delete resourcePrivate.resources[temp]; + resourcePrivate.resources.erase(temp); + } + } +} + +void OysterResource::SetResourceId (const OHRESOURCE& resourceData, unsigned int id) +{ + OResource* t = resourcePrivate.FindResource(resourceData); + + if(t) t->SetResourceID(id); +} +void OysterResource::SetResourceId(const wchar_t c[], unsigned int id) +{ + OResource* t = resourcePrivate.FindResource(c); + + if(t) t->SetResourceID(id); +} +ResourceType OysterResource::GetResourceType (const OHRESOURCE& resourceData) +{ + OResource* t = resourcePrivate.FindResource(resourceData); + + if(t) return t->GetResourceType(); + + return ResourceType_INVALID; +} +ResourceType OysterResource::GetResourceType (const wchar_t c[]) +{ + OResource* t = resourcePrivate.FindResource(c); + + if(t) return t->GetResourceType(); + + return ResourceType_INVALID; +} +const wchar_t* OysterResource::GetResourceFilename (const OHRESOURCE& resourceData) +{ + OResource* t = resourcePrivate.FindResource(resourceData); + + if(t) return t->GetResourceFilename(); + + return 0; +} +OHRESOURCE OysterResource::GetResourceHandle(const wchar_t filename[]) +{ + OResource* t = resourcePrivate.FindResource(filename); + + if(t) return t->GetResourceHandle(); + + return 0; +} +int OysterResource::GetResourceId (const OHRESOURCE& resourceData) +{ + OResource* t = resourcePrivate.FindResource(resourceData); + + if(t) return t->GetResourceID(); + + return -1; +} +int OysterResource::GetResourceId(const wchar_t c[]) +{ + OResource* t = resourcePrivate.FindResource(c); + + if(t) return t->GetResourceID(); + + return -1; +} + + + + + diff --git a/Code/Misc/Resource/ResourceManager.h b/Code/Misc/Resource/ResourceManager.h new file mode 100644 index 00000000..0b24b3e4 --- /dev/null +++ b/Code/Misc/Resource/ResourceManager.h @@ -0,0 +1,160 @@ +#ifndef MISC_RESOURCELOADER_H +#define MISC_RESOURCELOADER_H + +#include +#include + +namespace Oyster +{ + namespace Resource + { + struct ResourceData; + + typedef void* HRESOURCE; + /** Typedef on a fuction required for custom unloading */ + typedef void(*UnloadFunction)(void* loadedData); + + /** Typedef on a fuction required for custom loading */ + typedef void*(*LoadFunction)(const wchar_t filename[]); + + /** An enum class representing all avalible resources that is supported. */ + enum ResourceType + { + //Byte + ResourceType_Byte_Raw, /**< Handle can be interpeted as char[] or char* */ + ResourceType_Byte_ANSI, /**< Handle can be interpeted as char[] or char* */ + ResourceType_Byte_UTF8, /**< Handle can be interpeted as char[] or char* */ + ResourceType_Byte_UNICODE, /**< Handle can be interpeted as char[] or char* */ + ResourceType_Byte_UTF16LE, /**< Handle can be interpeted as char[] or char* */ + + ResourceType_COUNT, /**< Handle can be interpeted as ? */ + + ResourceType_UNKNOWN = -1, /**< Handle can be interpeted as void* */ + ResourceType_INVALID = -2, /**< Invalid or non existing resource */ + }; + + /** A resource handler interface to interact with when loading resources. + * The resource handler uses the filename to make resources unuiqe. + */ + class ResourceManager + { + public: + ResourceManager(); + ~ResourceManager(); + ResourceManager(const ResourceManager&) = delete; + const ResourceManager& operator=(const ResourceManager&) = delete; + + /** + * Load a resource given a type. + * @param filename The path to the resource. + * @param customId An optional custom ID to associate with the resource. + * @param type The resource type to load. + * @param force If set to true, the resource will be reloaded if it already exists. If it does not, nothing happens. + * @return If function suceeds, a handle to the resource will be returned. If failed 0 is returned. + */ + char* LoadBytes(const wchar_t filename[], ResourceType type, int customId = -1, bool force = false); + + /** + * Load a resource with a custom loading function + * @param filename The path to the resource. + * @param loadFnc The function that will load the data. + * @param unloadFnc The function that will unload the loaded data. + * @param customId An optional custom ID to associate with the resource. + * @param force If set to true, the resource will be removed and loaded if exists. + * @return If function suceeds, a handle to the resource will be returned. If failed 0 is returned. + */ + HRESOURCE LoadResource(const wchar_t filename[], LoadFunction loadFnc = 0, UnloadFunction unloadFnc = 0, int customId = -1, bool force = false); + + /** + * Reload a resource + * @param filename The path to the resource. + * @return If function suceeds, the return value is true. + */ + bool ReloadResource(const wchar_t filename[]); + + /** + * Reload a resource + * @param filename The path to the resource. + * @return If function suceeds, a handle to the resource will be returned. If failed 0 is returned. + */ + bool ReloadResource(HRESOURCE resource); + + /** + * Releases all resources loaded by the resource handler. + * @return Nothing + */ + void Clean(); + + /** + * Release a reference to the resource handle + * @param resource The handle to release. + * @return Nothing + */ + void ReleaseResource(const HRESOURCE& resource); + + /** + * Release a reference to the resource handle + * @param resource The resource filename to release reference. + * @return Nothing + */ + void ReleaseResource(const wchar_t filename[]); + + /** Set a user defined ID + * @param resource A handle to accociate the id with. + * @param id A user defined identifier that the resource handler does not touch. + */ + void SetResourceId(const HRESOURCE& resource, unsigned int id); + + /** Set a user defined ID + * If the resource is not loaded the id will not be set. + * @param resource A filename to accociate the id with. + * @param id A user defined identifier that the resource handler does not touch. + */ + void SetResourceId(const wchar_t filename[], unsigned int id); + + /** Get a resource type given a OHRESOURCE handle + * @param resource The handle to check + * @return Returns the resource type of the handle + */ + ResourceType GetResourceType(const HRESOURCE& resource); + + /** Get a resource type given a filename + * If the resource is not loaded the id will not be set. + * @param resource The filename to check + * @return Returns the resource type of the handle + */ + ResourceType GetResourceType (const wchar_t filename[]); + + /** Get a resource filename given a OHRESOURCE handle + * @param resource The handle to check + * @return Returns the accociated filename + */ + const wchar_t* GetResourceFilename(const HRESOURCE& resource); + + /** Get a resource handle given a filename + * If the resource is not loaded function returns 0. + * @param resource The filename to check + * @return Returns the accociated handle + */ + HRESOURCE GetResourceHandle(const wchar_t filename[]); + + /** Get a user defined ID accociated with a handle + * @param resource The handle to check + * @return Returns the accociated ID + */ + int GetResourceId(const HRESOURCE& resource); + + /** Get a user defined ID accociated with a filename + * @param resource The filename to check + * @return Returns the accociated ID + */ + int GetResourceId(const wchar_t filename[]); + + private: + std::map resources; + + }; + } +} + +#endif // !MISC_RESOURCELOADER_H diff --git a/Code/Tester/MainTest.cpp b/Code/Tester/MainTest.cpp deleted file mode 100644 index d25a9adc..00000000 --- a/Code/Tester/MainTest.cpp +++ /dev/null @@ -1,253 +0,0 @@ -//-------------------------------------------------------------------------------------- -// File: TemplateMain.cpp -// -// BTH-D3D-Template -// -// Copyright (c) Stefan Petersson 2011. All rights reserved. -//-------------------------------------------------------------------------------------- -#define NOMINMAX -#include -#include -#include "DllInterfaces\GFXAPI.h" - - - -//-------------------------------------------------------------------------------------- -// Global Variables -//-------------------------------------------------------------------------------------- -HINSTANCE g_hInst = NULL; -HWND g_hWnd = NULL; -Oyster::Graphics::Model::Model* m = NULL; -Oyster::Graphics::Model::Model* m2 = NULL; -Oyster::Graphics::Model::Model* m3 = NULL; -Oyster::Math::Float4x4 V; -Oyster::Math::Float4x4 P; - - -//-------------------------------------------------------------------------------------- -// Forward declarations -//-------------------------------------------------------------------------------------- -HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ); -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -HRESULT Render(float deltaTime); -HRESULT Update(float deltaTime); -HRESULT InitDirect3D(); - - - - -//-------------------------------------------------------------------------------------- -// Entry point to the program. Initializes everything and goes into a message processing -// loop. Idle time is used to render the scene. -//-------------------------------------------------------------------------------------- -int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) -{ - - BOOL b = SetDllDirectoryW(L"..\\DLL"); - - if( FAILED( InitWindow( hInstance, nCmdShow ) ) ) - return 0; - - if( FAILED( InitDirect3D() ) ) - return 0; - - __int64 cntsPerSec = 0; - QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec); - float secsPerCnt = 1.0f / (float)cntsPerSec; - - __int64 prevTimeStamp = 0; - QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp); - - std::string fps = "FPS:"; - char count[100]; - // Main message loop - MSG msg = {0}; - float fpsCounter = 0; - while(WM_QUIT != msg.message) - { - if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE) ) - { - TranslateMessage( &msg ); - DispatchMessage( &msg ); - } - else - { - __int64 currTimeStamp = 0; - QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp); - float dt = (currTimeStamp - prevTimeStamp) * secsPerCnt; - - //render - Update(dt); - Render(dt); - fpsCounter += dt; - if(fpsCounter>0.1f) - { - sprintf_s(count, "%f",1/dt); - SetWindowTextA(g_hWnd, (fps + count).c_str()); - fpsCounter = 0; - } - prevTimeStamp = currTimeStamp; - } - } - - Oyster::Graphics::API::DeleteModel(m); - Oyster::Graphics::API::DeleteModel(m2); - Oyster::Graphics::API::DeleteModel(m3); - Oyster::Graphics::API::Clean(); - return (int) msg.wParam; -} - -//-------------------------------------------------------------------------------------- -// Register class and create window -//-------------------------------------------------------------------------------------- -HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ) -{ - // Register class - WNDCLASSEX wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInstance; - wcex.hIcon = 0; - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wcex.lpszMenuName = NULL; - wcex.lpszClassName = L"BTH_D3D_Template"; - wcex.hIconSm = 0; - if( !RegisterClassEx(&wcex) ) - return E_FAIL; - - // Adjust and create window - g_hInst = hInstance; - RECT rc = { 0, 0, 1024, 768 }; - AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE ); - - if(!(g_hWnd = CreateWindow( - L"BTH_D3D_Template", - L"BTH - Direct3D 11.0 Template", - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, - CW_USEDEFAULT, - rc.right - rc.left, - rc.bottom - rc.top, - NULL, - NULL, - hInstance, - NULL))) - { - return E_FAIL; - } - - ShowWindow( g_hWnd, nCmdShow ); - - return S_OK; -} - - - -//-------------------------------------------------------------------------------------- -// Create Direct3D device and swap chain -//-------------------------------------------------------------------------------------- -HRESULT InitDirect3D() -{ - HRESULT hr = S_OK;; - - if(Oyster::Graphics::API::Init(g_hWnd,false,false, Oyster::Math::Float2( 1024, 768 )) == Oyster::Graphics::API::Fail) - { - return E_FAIL; - } - - m = Oyster::Graphics::API::CreateModel(L"cube_tri.dan"); - m2 = Oyster::Graphics::API::CreateModel(L"cube_tri.dan"); - m2->WorldMatrix = Oyster::Math3D::OrientationMatrix(Oyster::Math::Float3::null,Oyster::Math::Float3(0,5,0),Oyster::Math::Float3::null); - m3 = Oyster::Graphics::API::CreateModel(L"cube_tri.dan"); - m3->WorldMatrix = Oyster::Math3D::OrientationMatrix(Oyster::Math::Float3::null,Oyster::Math::Float3(0,5,0),Oyster::Math::Float3::null); - - - P = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/2,1024.0f/768.0f,.1f,100); - Oyster::Graphics::API::SetProjection(P); - - V = Oyster::Math3D::OrientationMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),Oyster::Math::Float3(0,0,15.4f)); - V = V.GetInverse(); - - - Oyster::Graphics::Definitions::Pointlight pl; - pl.Color = Oyster::Math::Float3(1,1,1); - pl.Bright = 1; - pl.Pos = Oyster::Math::Float3(0,5,5.4f); - pl.Radius = 15; - - Oyster::Graphics::API::AddLight(pl); - - - return S_OK; -} -float angle = 0; -HRESULT Update(float deltaTime) -{ - - angle += Oyster::Math::pi/8 * deltaTime; - m->WorldMatrix = Oyster::Math3D::RotationMatrix_AxisY(angle); - m2->WorldMatrix = Oyster::Math3D::OrientationMatrix(Oyster::Math::Float3(1,0,0)*-angle,Oyster::Math::Float3(0,-4,0),Oyster::Math::Float3::null); - m3->WorldMatrix = Oyster::Math3D::OrientationMatrix(Oyster::Math::Float3(1,0,0)*-0,Oyster::Math::Float3(3,4,-1*angle),Oyster::Math::Float3::null); - return S_OK; -} - -HRESULT Render(float deltaTime) -{ - Oyster::Graphics::API::SetView(V); - Oyster::Graphics::API::NewFrame(); - - Oyster::Graphics::API::RenderModel(*m); - Oyster::Graphics::API::RenderModel(*m2); - Oyster::Graphics::API::RenderModel(*m3); - - Oyster::Graphics::API::EndFrame(); - - return S_OK; -} - -//-------------------------------------------------------------------------------------- -// Called every time the application receives a message -//-------------------------------------------------------------------------------------- -LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) -{ - PAINTSTRUCT ps; - HDC hdc; - - switch (message) - { - case WM_PAINT: - hdc = BeginPaint(hWnd, &ps); - EndPaint(hWnd, &ps); - break; - - case WM_DESTROY: - PostQuitMessage(0); - break; - - case WM_KEYDOWN: - - switch(wParam) - { - case VK_ESCAPE: - PostQuitMessage(0); - break; - //R - case 0x52: -#ifdef _DEBUG - Oyster::Graphics::API::ReloadShaders(); -#endif - break; - } - break; - - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - - return 0; -} - diff --git a/Code/Tester/Tester.vcxproj b/Code/Tester/Tester.vcxproj deleted file mode 100644 index 3c61ce46..00000000 --- a/Code/Tester/Tester.vcxproj +++ /dev/null @@ -1,99 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {1B3BEA4C-CF75-438A-9693-60FB8444BBF3} - Win32Proj - Tester - - - - Application - true - v110 - Unicode - - - Application - false - v110 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - ..\OysterGraphics;..\OysterMath;..\Misc;%(AdditionalIncludeDirectories) - - - Windows - true - OysterGraphics_$(PlatformShortName)D.dll; - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - true - - - Windows - true - true - true - - - - - - - - {2ec4dded-8f75-4c86-a10b-e1e8eb29f3ee} - - - {0ec83e64-230e-48ef-b08c-6ac9651b4f82} - - - {f10cbc03-9809-4cba-95d8-327c287b18ee} - - - - - - \ No newline at end of file diff --git a/Code/Tester/Tester.vcxproj.filters b/Code/Tester/Tester.vcxproj.filters deleted file mode 100644 index 5657bd66..00000000 --- a/Code/Tester/Tester.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - \ No newline at end of file diff --git a/Code/Tester/Tester.vcxproj.user b/Code/Tester/Tester.vcxproj.user deleted file mode 100644 index 9a0b0ae0..00000000 --- a/Code/Tester/Tester.vcxproj.user +++ /dev/null @@ -1,6 +0,0 @@ - - - - true - - \ No newline at end of file From 06d468299abb987bb0e7a834a9991136982ab0f0 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Tue, 21 Jan 2014 10:37:45 +0100 Subject: [PATCH 02/76] GL - linking problem fix --- Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj index 1a9d892f..b2b5b766 100644 --- a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj +++ b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj @@ -105,13 +105,13 @@ Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - $(SolutionDir)Game\DanBiasServer\Include;$(SolutionDir)Game\DanBiasGame\Include + $(SolutionDir)Game\DanBiasServer\;$(SolutionDir)Game\DanBiasGame\Include Windows true DanBiasGame_$(PlatformShortName)D.dll;DanBiasServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) - DanBiasGame_$(PlatformShortName)D.lib;%(AdditionalDependencies) + DanBiasGame_$(PlatformShortName)D.lib;DanBiasServer_$(PlatformShortName)D.lib;%(AdditionalDependencies) From 561418c4274a425386e6907d8c9ad8d06e6be1f6 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Tue, 21 Jan 2014 14:28:27 +0100 Subject: [PATCH 03/76] GL - gravity working --- .../DanBiasGame/GameClientState/GameState.cpp | 25 ++++++++++-- Code/Game/GameLogic/Game.cpp | 24 ++++++++++++ Code/Game/GameLogic/Level.cpp | 38 ++++++++++--------- Code/Game/GameLogic/Object.cpp | 1 - Code/Game/GameLogic/Player.cpp | 12 ++++-- .../Implementation/PhysicsAPI_Impl.cpp | 2 +- 6 files changed, 76 insertions(+), 26 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index f1474eed..9bb3d620 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -47,13 +47,23 @@ bool GameState::Init(Oyster::Network::NetworkClient* nwClient) GameState::gameStateState GameState::LoadGame() { Oyster::Graphics::Definitions::Pointlight plight; - plight.Pos = Oyster::Math::Float3(0,3,0); + plight.Pos = Oyster::Math::Float3(0,15,5); plight.Color = Oyster::Math::Float3(0,1,0); - plight.Radius = 5; + plight.Radius = 50; + plight.Bright = 2; + Oyster::Graphics::API::AddLight(plight); + plight.Pos = Oyster::Math::Float3(10,15,5); + plight.Color = Oyster::Math::Float3(1,0,0); + plight.Radius = 50; + plight.Bright = 2; + Oyster::Graphics::API::AddLight(plight); + plight.Pos = Oyster::Math::Float3(10,-15,5); + plight.Color = Oyster::Math::Float3(0,0,1); + plight.Radius = 50; plight.Bright = 2; Oyster::Graphics::API::AddLight(plight); LoadModels(L"map"); - InitCamera(Oyster::Math::Float3(0,0,5.4f)); + InitCamera(Oyster::Math::Float3(0,0,20.0f)); return gameStateState_playing; } bool GameState::LoadModels(std::wstring mapFile) @@ -89,6 +99,15 @@ bool GameState::LoadModels(std::wstring mapFile) modelData.world = modelData.world * translate; modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; modelData.id ++; + + translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(0,0,0)); + Oyster::Math3D::Float4x4 scale = Oyster::Math3D::Float4x4::identity; + scale.v[0].x = 8; + scale.v[1].y = 8; + scale.v[2].z = 8; + modelData.world = scale; //modelData.world * translate + modelData.modelPath = L"ball.dan"; + modelData.id ++; obj = new C_DynamicObj(); privData->object.push_back(obj); diff --git a/Code/Game/GameLogic/Game.cpp b/Code/Game/GameLogic/Game.cpp index 4380777e..b22b3522 100644 --- a/Code/Game/GameLogic/Game.cpp +++ b/Code/Game/GameLogic/Game.cpp @@ -100,6 +100,30 @@ bool Game::NewFrame() API::Instance().Update(); + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->EndFrame(); + } + + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->BeginFrame(); + } + + API::Instance().Update(); + + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->EndFrame(); + } + + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->BeginFrame(); + } + + API::Instance().Update(); + for (unsigned int i = 0; i < this->players.Size(); i++) { if(this->players[i]->player) this->players[i]->player->EndFrame(); diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 90dc1865..b2bbe60c 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -19,24 +19,28 @@ void Level::InitiateLevel(std::string levelPath) } void Level::InitiateLevel(float radius) { - //API::SphericalBodyDescription sbDesc; - //sbDesc.centerPosition = Oyster::Math::Float4(0,0,0,1); - //sbDesc.ignoreGravity = true; - //sbDesc.radius = radius; - //sbDesc.mass = 1e16f; //10^16 + API::SphericalBodyDescription sbDesc; + sbDesc.centerPosition = Oyster::Math::Float4(0,0,0,1); + sbDesc.ignoreGravity = true; + sbDesc.radius = 8; //radius; + sbDesc.mass = 10e12f; //sbDesc.mass = 0; //10^16 - //sbDesc.subscription_onCollision = CollisionManager::LevelCollision; - // - //ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); - //API::Instance().AddObject(rigidBody); - - //API::Gravity gravityWell; - // - //gravityWell.gravityType = API::Gravity::GravityType_Well; - //gravityWell.well.mass = 1e16f; - //gravityWell.well.position = Oyster::Math::Float4(0,0,0,1); - // - //API::Instance().AddGravity(gravityWell); + sbDesc.subscription_onCollision = CollisionManager::LevelCollision; + + ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); + API::Instance().AddObject(rigidBody); + ICustomBody::State state; + rigidBody->GetState(state); + state.SetRestitutionCoeff(0.1); + rigidBody->SetState(state); + + API::Gravity gravityWell; + + gravityWell.gravityType = API::Gravity::GravityType_Well; + gravityWell.well.mass = 10e12f; + gravityWell.well.position = Oyster::Math::Float4(0,0,0,1); + + API::Instance().AddGravity(gravityWell); } diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 7e137c37..2937c605 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -34,7 +34,6 @@ Object::Object() Object::Object(void* collisionFunc, OBJECT_TYPE type) { API::SimpleBodyDescription sbDesc; - //sbDesc.centerPosition = //poi this->rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index 96ee5cfd..dd65c375 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -16,6 +16,10 @@ Player::Player() teamID = -1; playerState = PLAYER_STATE::PLAYER_STATE_IDLE; lookDir = Oyster::Math::Float4(0,0,-1,0); + setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); + setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); + + } Player::~Player(void) @@ -53,18 +57,18 @@ void Player::Move(const PLAYER_MOVEMENT &movement) void Player::MoveForward() { - setState.ApplyLinearImpulse(this->lookDir * (100 * this->gameInstance->GetFrameTime())); + setState.ApplyLinearImpulse(this->lookDir * (20 * this->gameInstance->GetFrameTime())); } void Player::MoveBackwards() { - setState.ApplyLinearImpulse(-this->lookDir * 100 * this->gameInstance->GetFrameTime()); + setState.ApplyLinearImpulse(-this->lookDir * 20 * this->gameInstance->GetFrameTime()); } void Player::MoveRight() { //Do cross product with forward vector and negative gravity vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r = (-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); - setState.ApplyLinearImpulse(r * 100 * this->gameInstance->GetFrameTime()); + setState.ApplyLinearImpulse(r * 20 * this->gameInstance->GetFrameTime()); } void Player::MoveLeft() @@ -72,7 +76,7 @@ void Player::MoveLeft() //Do cross product with forward vector and negative gravity vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r1 = -(-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); //Still get zero - setState.ApplyLinearImpulse(-r * 100 * this->gameInstance->GetFrameTime()); + setState.ApplyLinearImpulse(-r * 20 * this->gameInstance->GetFrameTime()); } void Player::UseWeapon(const WEAPON_FIRE &usage) diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp index 7bec7852..46346099 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp @@ -36,7 +36,7 @@ namespace // calc from perspective of deuter Float4 normal; deuter->GetNormalAt( worldPointOfContact, normal ); - Float protoG_Magnitude = protoG.Dot( normal ), + Float protoG_Magnitude = protoG.Dot( normal ), deuterG_Magnitude = deuterG.Dot( normal ); // if they are not relatively moving towards eachother, there is no collision From 50c09547f344298e2d3b6d28aa3fd01a0aa300c1 Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Tue, 21 Jan 2014 14:32:42 +0100 Subject: [PATCH 04/76] GameServer - Added protocols and stuff --- .../DanBiasGame/GameClientState/GameState.cpp | 10 +- .../GameClientState/LobbyState.cpp | 2 +- .../GameSession/GameSession_Events.cpp | 2 +- .../GameSession/GameSession_Logic.cpp | 2 +- .../DanBiasServer/LobbySessions/MainLobby.cpp | 2 +- Code/Game/GameProtocols/GameplayProtocols.h | 50 -------- Code/Game/GameProtocols/LobbyProtocols.h | 29 +++-- Code/Game/GameProtocols/ObjectProtocols.h | 112 ++++++++++++++++-- Code/Game/GameProtocols/PlayerProtocols.h | 4 +- .../GameProtocols/ProtocolIdentificationID.h | 21 ++-- Code/Misc/OysterCallback.h | 4 +- 11 files changed, 145 insertions(+), 93 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index f1474eed..109308a1 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -47,10 +47,10 @@ bool GameState::Init(Oyster::Network::NetworkClient* nwClient) GameState::gameStateState GameState::LoadGame() { Oyster::Graphics::Definitions::Pointlight plight; - plight.Pos = Oyster::Math::Float3(0,3,0); - plight.Color = Oyster::Math::Float3(0,1,0); - plight.Radius = 5; - plight.Bright = 2; + plight.Pos = Oyster::Math::Float3(0,8,8); + plight.Color = Oyster::Math::Float3(1,1,1); + plight.Radius = 20; + plight.Bright = 0.5; Oyster::Graphics::API::AddLight(plight); LoadModels(L"map"); InitCamera(Oyster::Math::Float3(0,0,5.4f)); @@ -264,7 +264,7 @@ void GameState::Protocol( ObjPos* pos ) world[i] = pos->worldPos[i]; } - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { if(privData->object[i]->GetId() == pos->object_ID) { diff --git a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp index 69e3504a..89d8b1b7 100644 --- a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp @@ -43,7 +43,7 @@ bool LobbyState::LoadModels(std::wstring file) plight.Pos = Oyster::Math::Float3(-2,3,0); plight.Color = Oyster::Math::Float3(0,1,0); plight.Radius = 10; - plight.Bright = 3; + plight.Bright = 1; Oyster::Graphics::API::AddLight(plight); // open file // read file diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index 1fa526f5..c14b8e41 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -63,7 +63,7 @@ namespace DanBias case protocol_Gameplay_PlayerChangeWeapon: break; - case protocol_Gameplay_PlayerDamage: + case protocol_Gameplay_ObjectDamage: break; } diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp index 7c47d4a7..ddf41582 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp @@ -37,7 +37,7 @@ namespace DanBias if(GetAsyncKeyState(VK_DOWN)) { Oyster::Math::Float4x4 world = Oyster::Math::Matrix::identity; - Protocol_CreateObject p(world, 2, "../Content/crate"); + Protocol_ObjectCreate p(world, 2, "../Content/crate"); Send(p.GetProtocol()); Sleep(100); } diff --git a/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp b/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp index 9b8899ca..ffdfa62e 100644 --- a/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp +++ b/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp @@ -104,7 +104,7 @@ namespace DanBias void MainLobby::SendUpdate() { //Send Lobbys - GameLogic::Protocol_LobbyUpdate(); + GameLogic::Protocol_LobbyRefresh(); } }//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/GameProtocols/GameplayProtocols.h b/Code/Game/GameProtocols/GameplayProtocols.h index b595ce36..8ffcd827 100644 --- a/Code/Game/GameProtocols/GameplayProtocols.h +++ b/Code/Game/GameProtocols/GameplayProtocols.h @@ -7,57 +7,7 @@ namespace GameLogic { - struct Protocol_GameplayInitiateSession :public Oyster::Network::CustomProtocolObject - { - struct SessionInitData - { - int id; - int type; - //std::string text; - }; - std::vector element; - Protocol_GameplayInitiateSession() - { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_Initiate; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - } - Protocol_GameplayInitiateSession(Oyster::Network::CustomNetProtocol& p) - { - int size = p[1].value.netInt; - for (int i = 0; i < size; i++) - { - SessionInitData d = { p[i+2].value.netInt, p[i+3].value.netInt /*, p[i+4].value.netCharPtr */ }; - element.push_back(d); - } - } - void Add(int id, int type, std::string text) - { - SessionInitData d = { id, type /*, text*/ }; - element.push_back(d); - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - //Store the elements count - this->protocol[1].value = element.size(); - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - - for (unsigned int i = 0; i < element.size(); i+=3) - { - this->protocol[i+2].value = element[i].id; - this->protocol[i+3].value = element[i].type; - //element[i].text.copy(this->protocol[i+4].value.netCharPtr, element[i].text.size()); - - this->protocol[i+2].type = Oyster::Network::NetAttributeType_Int; - this->protocol[i+3].type = Oyster::Network::NetAttributeType_Int; - //this->protocol[i+4].type = Oyster::Network::NetAttributeType_CharArray; - } - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; } #endif // !GAMEPROTOCOLS_GAMEPLAYPROTOCOLS_H diff --git a/Code/Game/GameProtocols/LobbyProtocols.h b/Code/Game/GameProtocols/LobbyProtocols.h index 71e6e3b6..ae175691 100644 --- a/Code/Game/GameProtocols/LobbyProtocols.h +++ b/Code/Game/GameProtocols/LobbyProtocols.h @@ -9,6 +9,13 @@ #include #include "ProtocolIdentificationID.h" +//#define protocol_Lobby_CreateGame 200 +//#define protocol_Lobby_StartGame 201 +//#define protocol_Lobby_JoinGame 202 +//#define protocol_Lobby_JoinLobby 203 +//#define protocol_Lobby_LeaveLobby 204 +//#define protocol_Lobby_Refresh 205 + namespace GameLogic { struct Protocol_LobbyCreateGame :public Oyster::Network::CustomProtocolObject @@ -40,13 +47,13 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_LobbyJoinGame :public Oyster::Network::CustomProtocolObject + struct Protocol_LobbyStartGame :public Oyster::Network::CustomProtocolObject { char gameId; - Protocol_LobbyJoinGame() + Protocol_LobbyStartGame() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_JoinGame; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_StartGame; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Char; @@ -61,13 +68,13 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_LobbyStartGame :public Oyster::Network::CustomProtocolObject + struct Protocol_LobbyJoinGame :public Oyster::Network::CustomProtocolObject { char gameId; - Protocol_LobbyStartGame() + Protocol_LobbyJoinGame() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_StartGame; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_JoinGame; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Char; @@ -125,7 +132,7 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_LobbyUpdate :public Oyster::Network::CustomProtocolObject + struct Protocol_LobbyRefresh :public Oyster::Network::CustomProtocolObject { struct LobbyUpdateData { @@ -134,14 +141,14 @@ namespace GameLogic }; int count; LobbyUpdateData* data; - Protocol_LobbyUpdate() + Protocol_LobbyRefresh() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_LeaveLobby; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Refresh; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; } - Protocol_LobbyUpdate( Oyster::Network::CustomNetProtocol* p ) + Protocol_LobbyRefresh( Oyster::Network::CustomNetProtocol* p ) { count = (*p)[1].value.netInt; data = new LobbyUpdateData[count]; @@ -150,7 +157,7 @@ namespace GameLogic //data[i].mapName = (*p)[i].value. } } - ~Protocol_LobbyUpdate() + ~Protocol_LobbyRefresh() { delete [] data; data = 0; diff --git a/Code/Game/GameProtocols/ObjectProtocols.h b/Code/Game/GameProtocols/ObjectProtocols.h index e52a4d2f..31452d68 100644 --- a/Code/Game/GameProtocols/ObjectProtocols.h +++ b/Code/Game/GameProtocols/ObjectProtocols.h @@ -4,14 +4,99 @@ #include #include "ProtocolIdentificationID.h" +//protocol_Gameplay_PlayerMovement 300 +//protocol_Gameplay_PlayerMouseMovement 301 +//protocol_Gameplay_PlayerChangeWeapon 302 + +//#define protocol_Gameplay_ObjectPickup 303 +//#define protocol_Gameplay_ObjectDamage 304 +//#define protocol_Gameplay_ObjectPosition 305 +//#define protocol_Gameplay_ObjectEnabled 306 +//#define protocol_Gameplay_ObjectDisabled 307 +//#define protocol_Gameplay_ObjectCreate 308 namespace GameLogic { + struct Protocol_ObjectPickup :public Oyster::Network::CustomProtocolObject + { + int object_ID; + short pickup_ID; + + Protocol_ObjectPickup() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPickup; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[2].type = Oyster::Network::NetAttributeType_Short; + + object_ID = -1; + pickup_ID = -1; + } + Protocol_ObjectPickup(int objectID, short pickupID) + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[2].type = Oyster::Network::NetAttributeType_Short; + + object_ID = objectID; + pickup_ID = pickupID; + + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = object_ID; + this->protocol[2].value = pickup_ID; + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + + struct Protocol_ObjectDamage :public Oyster::Network::CustomProtocolObject + { + int object_ID; + float health; //Precentage% + + Protocol_ObjectDamage() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDamage; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[2].type = Oyster::Network::NetAttributeType_Float; + + object_ID = -1; + health = 0.0f; + } + Protocol_ObjectDamage(int id, float hp) + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDamage; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[2].type = Oyster::Network::NetAttributeType_Float; + + object_ID = id; + health = hp; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = object_ID; + this->protocol[2].value = health; + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + struct Protocol_ObjectPosition :public Oyster::Network::CustomProtocolObject { int object_ID; float worldMatrix[16]; - // look at dir Protocol_ObjectPosition() { @@ -60,7 +145,6 @@ namespace GameLogic { int object_ID; float worldMatrix[16]; - // look at dir Protocol_ObjectEnable() { @@ -116,6 +200,17 @@ namespace GameLogic this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; } + Protocol_ObjectDisable(int id, float time) + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDisabled; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[2].type = Oyster::Network::NetAttributeType_Float; + + object_ID = id; + timer = time; + } Oyster::Network::CustomNetProtocol* GetProtocol() override { this->protocol[1].value = object_ID; @@ -127,14 +222,13 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_CreateObject :public Oyster::Network::CustomProtocolObject + struct Protocol_ObjectCreate :public Oyster::Network::CustomProtocolObject { int object_ID; - char *path; + char *name; float worldMatrix[16]; - - Protocol_CreateObject() + Protocol_ObjectCreate() { this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectCreate; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; @@ -147,7 +241,7 @@ namespace GameLogic this->protocol[i].type = Oyster::Network::NetAttributeType_Float; } } - Protocol_CreateObject(float m[16], int id, char *path) + Protocol_ObjectCreate(float m[16], int id, char *path) { this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectCreate; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Int; @@ -161,14 +255,14 @@ namespace GameLogic } object_ID = id; - this->path = path; + this->name = path; memcpy(&worldMatrix[0], &m[0], sizeof(float)*16); } Oyster::Network::CustomNetProtocol* GetProtocol() override { this->protocol[1].value = object_ID; - this->protocol[2].value = path; + this->protocol[2].value = name; this->protocol[3].value = worldMatrix[0]; this->protocol[4].value = worldMatrix[1]; this->protocol[5].value = worldMatrix[2]; diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index eb89b1c6..36ccf497 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -124,7 +124,7 @@ namespace GameLogic Protocol_PlayerDamage() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerDamage; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDamage; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; @@ -151,7 +151,7 @@ namespace GameLogic Protocol_PlayerPickup() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerPickup; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPickup; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index a48d5594..5293093b 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -15,46 +15,47 @@ /***********************************/ /********* RESERVERD PROTOCOLS ***************************************************************************************************/ -/********** [ 0 - 100 ] *********/ +/********** [ 0 - 99 ] *********/ #define protocol_RESERVED_MIN 0 #define protocol_RESERVED_MAX 99 /***********************************/ /********* GENERAL PROTOCOLS ***************************************************************************************************/ -/***********[ 100 - 200 ]***********/ +/***********[ 100 - 199 ]***********/ #define protocol_GeneralMIN 100 #define protocol_General_Status 100 #define protocol_General_Text 101 +#define protocol_General_Disconnect 102 +#define protocol_General_Login 110 #define protocol_GeneralMAX 199 /***********************************/ /********* LOBBY PROTOCOLS ***************************************************************************************************/ -/***********[ 200 - 300 ]***********/ +/***********[ 200 - 299 ]***********/ #define protocol_LobbyMIN 200 #define protocol_Lobby_CreateGame 200 -#define protocol_Lobby_JoinGame 201 -#define protocol_Lobby_StartGame 202 +#define protocol_Lobby_StartGame 201 +#define protocol_Lobby_JoinGame 202 #define protocol_Lobby_JoinLobby 203 #define protocol_Lobby_LeaveLobby 204 -#define protocol_Lobby_CreateGameLobby 205 +#define protocol_Lobby_Refresh 205 #define protocol_LobbyMAX 299 /***********************************/ /********* GAMEPLAY PROTOCOLS ***************************************************************************************************/ -/***********[ 300 - 400 ]***********/ +/***********[ 300 - 399 ]***********/ #define protocol_GameplayMIN 300 #define protocol_Gameplay_PlayerMovement 300 #define protocol_Gameplay_PlayerMouseMovement 301 #define protocol_Gameplay_PlayerChangeWeapon 302 -#define protocol_Gameplay_PlayerDamage 303 -#define protocol_Gameplay_PlayerPickup 304 +#define protocol_Gameplay_ObjectPickup 303 +#define protocol_Gameplay_ObjectDamage 304 #define protocol_Gameplay_ObjectPosition 305 #define protocol_Gameplay_ObjectEnabled 306 #define protocol_Gameplay_ObjectDisabled 307 #define protocol_Gameplay_ObjectCreate 308 -#define protocol_Gameplay_Initiate 309 #define protocol_GameplayMAX 399 diff --git a/Code/Misc/OysterCallback.h b/Code/Misc/OysterCallback.h index 258408cb..48e967ed 100644 --- a/Code/Misc/OysterCallback.h +++ b/Code/Misc/OysterCallback.h @@ -59,11 +59,11 @@ namespace Oyster switch (callbackType) { case CallbackType_Function: - value.callbackFunction(e); + if(value.callbackFunction) value.callbackFunction(e); return true; break; case CallbackType_Object: - value.callbackObject->ObjectCallback(e); + if(value.callbackObject) value.callbackObject->ObjectCallback(e); return true; break; case CallbackType_PostBox: From ebaa6683825b4c0fc69715e722b17c04b37053d1 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Tue, 21 Jan 2014 15:46:54 +0100 Subject: [PATCH 05/76] Major updates to Collision handling with the class CollitionManager. changes started on the massdriver --- Code/Game/GameLogic/AttatchmentMassDriver.cpp | 42 +++++------ Code/Game/GameLogic/AttatchmentMassDriver.h | 11 ++- Code/Game/GameLogic/CollisionManager.cpp | 67 +++++++++--------- Code/Game/GameLogic/CollisionManager.h | 9 +-- Code/Game/GameLogic/GameLogicStates.h | 3 +- Code/Game/GameLogic/IAttatchment.h | 2 +- Code/Game/GameLogic/Level.h | 8 +++ Code/Game/GameLogic/Player.cpp | 4 +- Code/Game/GameLogic/Player.h | 14 ++++ Code/Game/GameLogic/Weapon.cpp | 69 +++++++------------ Code/Game/GameLogic/Weapon.h | 16 +++-- 11 files changed, 117 insertions(+), 128 deletions(-) diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index 6ec70e1b..a820ca46 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -3,52 +3,38 @@ using namespace GameLogic; -struct AttatchmentMassDriver::PrivateData -{ - PrivateData() - { - - } - - ~PrivateData() - { - - } - -}myData; AttatchmentMassDriver::AttatchmentMassDriver(void) { - myData = new PrivateData(); this->owner = 0; } AttatchmentMassDriver::AttatchmentMassDriver(Player &owner) { - myData = new PrivateData(); + this->owner = &owner; } AttatchmentMassDriver::~AttatchmentMassDriver(void) { - delete myData; + } /******************************************************** * Uses the attatchment and will from here switch case the different WEAPON_FIRE's that are to be used ********************************************************/ -void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage) +void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage, float dt) { //switch case to determin what functionallity to use in the attatchment switch (usage) { case WEAPON_FIRE::WEAPON_USE_PRIMARY_PRESS: - ForcePush(usage); + ForcePush(usage,dt); break; case WEAPON_FIRE::WEAPON_USE_SECONDARY_PRESS: - ForcePull(usage); + ForcePull(usage,dt); break; } @@ -57,17 +43,27 @@ void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage) /******************************************************** * Pushes objects in a cone in front of the weapon when fired ********************************************************/ -void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage) +void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float dt) { - //create coneRigidBody that will then collide with object and push them in the aimed direction + Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt); + + + //create frustum that will then collide with object and push them in the aimed direction + + //sample with frustum using visitor pattern(needs a function ptr sent with it that idicates what happens when a collision has been made) } /******************************************************** * Pulls the player in the direction he is looking, used for fast movement(kinda like a jetpack) ********************************************************/ -void AttatchmentMassDriver::ForcePull(const WEAPON_FIRE &usage) +void AttatchmentMassDriver::ForcePull(const WEAPON_FIRE &usage, float dt) { - //Oyster::Physics::API::Instance().ApplyForceAt(owner->GetRigidBody(), owner->GetRigidBody()->GetCenter(), owner->GetLookDir() * 100); + Oyster::Physics::Struct::CustomBodyState state = this->owner->GetRigidBody()->GetState(); + + //do something with state + state.ApplyLinearImpulse(Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt)); + + this->owner->GetRigidBody()->SetState(state); } diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.h b/Code/Game/GameLogic/AttatchmentMassDriver.h index a1ededd0..14332525 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.h +++ b/Code/Game/GameLogic/AttatchmentMassDriver.h @@ -15,30 +15,29 @@ namespace GameLogic ~AttatchmentMassDriver(void); - void UseAttatchment(const WEAPON_FIRE &usage); + void UseAttatchment(const WEAPON_FIRE &usage, float dt); private: /******************************************************** * Pushes objects and players in a cone in front of the player * @param fireInput: allows switching on different functionality in this specific function ********************************************************/ - void ForcePush(const WEAPON_FIRE &usage); + void ForcePush(const WEAPON_FIRE &usage, float dt); /******************************************************** * Pulls the player forward, this is a movement tool * @param fireInput: allows switching on different functionality in this specific function ********************************************************/ - void ForcePull(const WEAPON_FIRE &usage); + void ForcePull(const WEAPON_FIRE &usage, float dt); /******************************************************** * Sucks objects towards the player, the player can then pick up an object and throw it as a projectile * @param fireInput: allows switching on different functionality in this specific function ********************************************************/ - void ForceSuck(const WEAPON_FIRE &usage); + void ForceSuck(const WEAPON_FIRE &usage, float dt); private: - struct PrivateData; - PrivateData *myData; + }; } #endif diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 4cdb3689..4dfebedf 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -1,62 +1,59 @@ -#include "CollisionManager.h" #include "PhysicsAPI.h" #include "Object.h" #include "DynamicObject.h" #include "Player.h" +#include "Level.h" using namespace Oyster; using namespace GameLogic; void PlayerVBox(Player &player, DynamicObject &box); + void PlayerVObject(Player &player, Object &obj); - Physics::ICustomBody::SubscriptMessage CollisionManager::PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj) + Physics::ICustomBody::SubscriptMessage Player::PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj) { - //Player *player = ((Player*)(rigidBodyPlayer->gameObjectRef)); - //Object *realObj = (Object*)obj->gameObjectRef; + Player *player = ((Player*)(rigidBodyPlayer->GetCustomTag())); + Object *realObj = (Object*)obj->GetCustomTag(); - //switch (realObj->GetType()) - //{ - //case OBJECT_TYPE::OBJECT_TYPE_BOX: - // PlayerVBox(*player,(*(DynamicObject*) realObj)); - // break; - //case OBJECT_TYPE::OBJECT_TYPE_PLAYER: - // - // break; - //} + switch (realObj->GetType()) + { + case OBJECT_H::OBJECT_TYPE_GENERIC: + PlayerVObject(*player,*realObj); + Physics::ICustomBody::SubscriptMessage_none; + break; + + case OBJECT_TYPE::OBJECT_TYPE_BOX: + PlayerVBox(*player,(*(DynamicObject*) realObj)); + Physics::ICustomBody::SubscriptMessage_none; + break; + case OBJECT_TYPE::OBJECT_TYPE_PLAYER: + Physics::ICustomBody::SubscriptMessage_none; + break; + + } return Physics::ICustomBody::SubscriptMessage_none; } void PlayerVBox(Player &player, DynamicObject &box) { + //use kinetic energyloss of the collision in order too determin how much damage to take + //use as part of the damage algorithm + player.DamageLife(20); + } + + void PlayerVObject(Player &player, Object &obj) + { + //Collision between a player and a general static or dynamic object + //use kinetic energyloss of the collision in order too determin how much damage to take + //use as part of the damage algorithm player.DamageLife(20); } - Physics::ICustomBody::SubscriptMessage CollisionManager::BoxCollision(const Oyster::Physics::ICustomBody *rigidBodyBox, const Oyster::Physics::ICustomBody *obj) - { - if(rigidBodyBox == 0) - { - return Physics::ICustomBody::SubscriptMessage::SubscriptMessage_none; - } - //DynamicObject *box = (DynamicObject*)rigidBodyBox->gameObjectRef; - //Object *realObj = (Object*)obj->gameObjectRef; - //switch (realObj->GetType()) - //{ - //case OBJECT_TYPE::OBJECT_TYPE_BOX: - // - // break; - //case OBJECT_TYPE::OBJECT_TYPE_PLAYER: - // //PlayerVBox(*(Player*)realObj,*box); - // break; - //} - - return Physics::ICustomBody::SubscriptMessage_none; - } - - Oyster::Physics::ICustomBody::SubscriptMessage CollisionManager::LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj) + Oyster::Physics::ICustomBody::SubscriptMessage Level::LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj) { return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; } diff --git a/Code/Game/GameLogic/CollisionManager.h b/Code/Game/GameLogic/CollisionManager.h index 21723885..6179333f 100644 --- a/Code/Game/GameLogic/CollisionManager.h +++ b/Code/Game/GameLogic/CollisionManager.h @@ -10,14 +10,7 @@ namespace GameLogic class CollisionManager { public: - //these are the main collision functions - //typedef SubscriptMessage (*EventAction_Collision)( const ICustomBody *proto, const ICustomBody *deuter ); - static Oyster::Physics::ICustomBody::SubscriptMessage PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj); - static Oyster::Physics::ICustomBody::SubscriptMessage BoxCollision(const Oyster::Physics::ICustomBody *rigidBodyBox, const Oyster::Physics::ICustomBody *obj); - static Oyster::Physics::ICustomBody::SubscriptMessage LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj); - //these are the specific collision case functions - //void PlayerVBox(Player &player, DynamicObject &box); - //void BoxVBox(DynamicObject &box1, DynamicObject &box2); + //put general collision functions here that are not part of a specific object }; diff --git a/Code/Game/GameLogic/GameLogicStates.h b/Code/Game/GameLogic/GameLogicStates.h index 4c7c4197..2b0d0b8b 100644 --- a/Code/Game/GameLogic/GameLogicStates.h +++ b/Code/Game/GameLogic/GameLogicStates.h @@ -24,7 +24,8 @@ namespace GameLogic { OBJECT_TYPE_PLAYER = 0, OBJECT_TYPE_BOX = 1, - OBJECT_TYPE_WORLD = 2, + OBJECT_TYPE_WORLD = 2, + OBJECT_TYPE_GENERIC = 4, OBJECT_TYPE_UNKNOWN = -1, }; diff --git a/Code/Game/GameLogic/IAttatchment.h b/Code/Game/GameLogic/IAttatchment.h index 5bc400e6..e458fdbf 100644 --- a/Code/Game/GameLogic/IAttatchment.h +++ b/Code/Game/GameLogic/IAttatchment.h @@ -19,7 +19,7 @@ namespace GameLogic IAttatchment(void); ~IAttatchment(void); - virtual void UseAttatchment(const WEAPON_FIRE &usage) = 0; + virtual void UseAttatchment(const WEAPON_FIRE &usage, float dt) = 0; private: diff --git a/Code/Game/GameLogic/Level.h b/Code/Game/GameLogic/Level.h index 2242a8b2..4a28fabe 100644 --- a/Code/Game/GameLogic/Level.h +++ b/Code/Game/GameLogic/Level.h @@ -51,6 +51,14 @@ namespace GameLogic ********************************************************/ void RespawnPlayer(Player *player); + /******************************************************** + * Collision function for level, this is to be sent to physics through the subscribe function with the rigidbody + * Will be called when the physics detect a collision + * @param rigidBodyLevel: physics object of the level + * @param obj: physics object for the object that collided with the level + ********************************************************/ + static Oyster::Physics::ICustomBody::SubscriptMessage LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj); + private: TeamManager teamManager; Utility::DynamicMemory::DynamicArray> staticObjects; diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index dd65c375..7eca9f25 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -8,7 +8,7 @@ using namespace GameLogic; using namespace Oyster::Physics; Player::Player() - :DynamicObject(CollisionManager::PlayerCollision, OBJECT_TYPE::OBJECT_TYPE_PLAYER) + :DynamicObject(Player::PlayerCollision, OBJECT_TYPE::OBJECT_TYPE_PLAYER) { weapon = new Weapon(); @@ -81,7 +81,7 @@ void Player::MoveLeft() void Player::UseWeapon(const WEAPON_FIRE &usage) { - this->weapon->Use(usage); + this->weapon->Use(usage,gameInstance->GetFrameTime()); } void Player::Respawn(Oyster::Math::Float3 spawnPoint) diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index 39e8f5f6..48caa017 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -41,7 +41,18 @@ namespace GameLogic ********************************************************/ void Respawn(Oyster::Math::Float3 spawnPoint); +<<<<<<< HEAD void Rotate(float x, float y); +======= + /******************************************************** + * Collision function for player, this is to be sent to physics through the subscribe function with the rigidbody + * Will be called when the physics detect a collision + * @param rigidBodyPlayer: physics object of the player + * @param obj: physics object for the object that collided with the player + ********************************************************/ + static Oyster::Physics::ICustomBody::SubscriptMessage PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj); + +>>>>>>> Major updates to Collision handling with the class CollitionManager. changes started on the massdriver bool IsWalking(); bool IsJumping(); @@ -65,6 +76,9 @@ namespace GameLogic PLAYER_STATE playerState; Oyster::Math::Float4 lookDir; + bool hasTakenDamage; + float invincibleCooldown; + }; } #endif \ No newline at end of file diff --git a/Code/Game/GameLogic/Weapon.cpp b/Code/Game/GameLogic/Weapon.cpp index 28765cf3..994ccac5 100644 --- a/Code/Game/GameLogic/Weapon.cpp +++ b/Code/Game/GameLogic/Weapon.cpp @@ -1,60 +1,39 @@ #include "Weapon.h" -#include "AttatchmentSocket.h" #include "AttatchmentMassDriver.h" -#include "DynamicArray.h" + using namespace GameLogic; using namespace Utility::DynamicMemory; -struct Weapon::PrivateData -{ - PrivateData() - { - weaponState = WEAPON_STATE_IDLE; - selectedAttatchment = 0; - currentNrOfAttatchments = 0; - selectedSocketID = 0; - attatchmentSockets = 0; - } - - ~PrivateData() - { - } - - WEAPON_STATE weaponState; - - DynamicArray> attatchmentSockets; - int currentNrOfAttatchments; - SmartPointer selectedAttatchment; - int selectedSocketID; - -}myData; Weapon::Weapon() { - myData = new PrivateData(); + weaponState = WEAPON_STATE_IDLE; + selectedAttatchment = 0; + currentNrOfAttatchments = 0; + selectedSocketID = 0; + attatchmentSockets = 0; } Weapon::Weapon(int MaxNrOfSockets) { - myData = new PrivateData(); - myData->attatchmentSockets.Resize(MaxNrOfSockets); + attatchmentSockets.Resize(MaxNrOfSockets); } Weapon::~Weapon(void) { - delete myData; + } /******************************************************** * Uses the weapon based on the input given and the current chosen attatchment ********************************************************/ -void Weapon::Use(const WEAPON_FIRE &usage) +void Weapon::Use(const WEAPON_FIRE &usage, float dt) { - if (myData->selectedAttatchment) + if (selectedAttatchment) { - myData->selectedAttatchment->UseAttatchment(usage); + selectedAttatchment->UseAttatchment(usage, dt); } } @@ -68,24 +47,24 @@ void Weapon::Use(const WEAPON_FIRE &usage) ********************************************************/ bool Weapon::IsFireing() { - return (myData->weaponState == WEAPON_STATE::WEAPON_STATE_FIRING); + return (weaponState == WEAPON_STATE::WEAPON_STATE_FIRING); } bool Weapon::IsIdle() { - return (myData->weaponState == WEAPON_STATE::WEAPON_STATE_IDLE); + return (weaponState == WEAPON_STATE::WEAPON_STATE_IDLE); } bool Weapon::IsReloading() { - return (myData->weaponState == WEAPON_STATE::WEAPON_STATE_RELOADING); + return (weaponState == WEAPON_STATE::WEAPON_STATE_RELOADING); } bool Weapon::IsValidSocket(int socketID) { - if(socketID < (int)myData->attatchmentSockets.Size() && socketID >= 0) + if(socketID < (int)attatchmentSockets.Size() && socketID >= 0) { - if (myData->attatchmentSockets[socketID]->GetAttatchment() != 0) + if (attatchmentSockets[socketID]->GetAttatchment() != 0) { return true; } @@ -96,16 +75,16 @@ bool Weapon::IsValidSocket(int socketID) int Weapon::GetCurrentSocketID() { - return myData->selectedSocketID; + return selectedSocketID; } void Weapon::AddNewAttatchment(IAttatchment *attatchment, Player *owner) { - if(myData->currentNrOfAttatchments < (int)myData->attatchmentSockets.Size()) + if(currentNrOfAttatchments < (int)attatchmentSockets.Size()) { - myData->attatchmentSockets[myData->currentNrOfAttatchments]->SetAttatchment(attatchment); - myData->currentNrOfAttatchments++; + attatchmentSockets[currentNrOfAttatchments]->SetAttatchment(attatchment); + currentNrOfAttatchments++; } } @@ -113,7 +92,7 @@ void Weapon::SwitchAttatchment(IAttatchment *attatchment, int socketID, Player * { if (IsValidSocket(socketID)) { - myData->attatchmentSockets[socketID]->SetAttatchment(attatchment); + attatchmentSockets[socketID]->SetAttatchment(attatchment); } } @@ -121,7 +100,7 @@ void Weapon::RemoveAttatchment(int socketID) { if (IsValidSocket(socketID)) { - myData->attatchmentSockets[socketID]->RemoveAttatchment(); + attatchmentSockets[socketID]->RemoveAttatchment(); } } @@ -129,8 +108,8 @@ void Weapon::SelectAttatchment(int socketID) { if (IsValidSocket(socketID)) { - myData->selectedAttatchment = myData->attatchmentSockets[socketID]->GetAttatchment(); - myData->selectedSocketID = socketID; + selectedAttatchment = attatchmentSockets[socketID]->GetAttatchment(); + selectedSocketID = socketID; } } \ No newline at end of file diff --git a/Code/Game/GameLogic/Weapon.h b/Code/Game/GameLogic/Weapon.h index 719a853c..0d3c09d8 100644 --- a/Code/Game/GameLogic/Weapon.h +++ b/Code/Game/GameLogic/Weapon.h @@ -6,21 +6,20 @@ #include "GameLogicStates.h" #include "IAttatchment.h" #include "Player.h" +#include "AttatchmentSocket.h" +#include "DynamicArray.h" namespace GameLogic { class Weapon { - public: - - Weapon(void); Weapon(int nrOfAttatchmentSockets); ~Weapon(void); - void Use(const WEAPON_FIRE &fireInput); + void Use(const WEAPON_FIRE &usage, float dt); void AddNewAttatchment(IAttatchment *attatchment, Player *owner); void SwitchAttatchment(IAttatchment *attatchment, int socketID, Player *owner); @@ -35,9 +34,12 @@ namespace GameLogic int GetCurrentSocketID(); - private: - struct PrivateData; - PrivateData *myData; + private: + WEAPON_STATE weaponState; + Utility::DynamicMemory::DynamicArray attatchmentSockets; + int currentNrOfAttatchments; + IAttatchment *selectedAttatchment; + int selectedSocketID; }; } From 0aae9de92b4d4f59b9fd492ec0f6c3e7e5f8bc82 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Tue, 21 Jan 2014 16:08:55 +0100 Subject: [PATCH 06/76] blah --- Code/Game/GameLogic/Player.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index 48caa017..ae2a40b4 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -41,9 +41,14 @@ namespace GameLogic ********************************************************/ void Respawn(Oyster::Math::Float3 spawnPoint); +<<<<<<< HEAD <<<<<<< HEAD void Rotate(float x, float y); ======= +======= + void rotate(float x, float y); + +>>>>>>> blah /******************************************************** * Collision function for player, this is to be sent to physics through the subscribe function with the rigidbody * Will be called when the physics detect a collision From 954de26b437d881e022b21590b84f731485aaf38 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Tue, 21 Jan 2014 16:11:02 +0100 Subject: [PATCH 07/76] GL - mergeproblems --- Code/Game/GameLogic/Player.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index ae2a40b4..14359554 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -41,14 +41,9 @@ namespace GameLogic ********************************************************/ void Respawn(Oyster::Math::Float3 spawnPoint); -<<<<<<< HEAD -<<<<<<< HEAD - void Rotate(float x, float y); -======= -======= - void rotate(float x, float y); ->>>>>>> blah + void Rotate(float x, float y); + /******************************************************** * Collision function for player, this is to be sent to physics through the subscribe function with the rigidbody * Will be called when the physics detect a collision @@ -57,7 +52,6 @@ namespace GameLogic ********************************************************/ static Oyster::Physics::ICustomBody::SubscriptMessage PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj); ->>>>>>> Major updates to Collision handling with the class CollitionManager. changes started on the massdriver bool IsWalking(); bool IsJumping(); From 1b86500ab06b3cb5d8986e4caba421521beb46f6 Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Wed, 22 Jan 2014 10:39:16 +0100 Subject: [PATCH 08/76] GameLogic - Uml for LevelLoader --- Code/Dokumentation/LevelLoader.uxf | 276 +++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 Code/Dokumentation/LevelLoader.uxf diff --git a/Code/Dokumentation/LevelLoader.uxf b/Code/Dokumentation/LevelLoader.uxf new file mode 100644 index 00000000..1bdd8e14 --- /dev/null +++ b/Code/Dokumentation/LevelLoader.uxf @@ -0,0 +1,276 @@ + + + 8 + + com.umlet.element.Class + + 560 + 360 + 232 + 136 + + LevelLoader +<<API>><Interface>> +-- +Functions: +vector<struct> LoadLevel(String fileName); +- +Privates: + + + + + + com.umlet.element.Class + + 440 + 88 + 128 + 40 + + GameLogic +<<Erik>> + + + + com.umlet.element.Relation + + 768 + 472 + 136 + 104 + + lt=<<<<- + 120;24;120;88;24;88 + + + com.umlet.element.Relation + + 544 + 64 + 136 + 160 + + lt=lt=->>>> +m1=1..1 +m2=1..1 +Uses> + 24;40;80;40;120;40;120;144 + + + com.umlet.element.Relation + + 384 + 176 + 256 + 56 + + lt=->>>> +m1=1..1 +m2=1..1 +<Knows about + 240;40;24;40 + + + com.umlet.element.Relation + + 680 + 176 + 152 + 56 + + lt=- +m1=1..1 +m2=1..1 +Uses> + 24;40;136;40 + + + com.umlet.element.Class + + 816 + 192 + 128 + 40 + + Resource Loader +<<Dennis>><<Singleton> + + + + com.umlet.element.Relation + + 656 + 472 + 40 + 88 + + lt=<<<<- + 24;72;24;24 + + + com.umlet.element.Class + + 256 + 360 + 232 + 104 + + Defines.h +<<Header file>> +-- +Enum ObjectType(static, dynamic, specials); +. +Struct static; +Struct dynamic; +Struct specials + + + + com.umlet.element.Relation + + 640 + 208 + 40 + 168 + + lt=<<. + 24;24;24;152 + + + com.umlet.element.Class + + 800 + 360 + 208 + 136 + + <<Interface>> +Loader +-- +Functions: +wchar* LoadFile(string fileName); +Model* LoadModel(string modelName); +Model* LoadModel(int modelID); +- +Privates: + + + + com.umlet.element.Class + + 328 + 208 + 80 + 24 + + Defines + + + + com.umlet.element.Class + + 560 + 544 + 232 + 136 + + <<Interface>> +Parser +-- +Functions: +vector<struct> Parse(); +- +Privates: +enum headerType; +const int FileHeaderSize; +const int FileVersion; + + + + + com.umlet.element.Package + + 248 + 320 + 248 + 160 + + Defines + + + + com.umlet.element.Package + + 552 + 320 + 584 + 368 + + LevelLoader + + + + com.umlet.element.Relation + + 768 + 576 + 176 + 56 + + lt=- +m1=1..1 +m2=1..1 +Uses> + 24;40;160;40 + + + com.umlet.element.Class + + 624 + 208 + 80 + 24 + + LevelLoader + + + + + com.umlet.element.Class + + 928 + 560 + 200 + 120 + + Collection of functions +<<lots of functions>> +-- +functions for creating the right structs + + + + com.umlet.element.Relation + + 344 + 208 + 40 + 168 + + lt=<<. + 24;24;24;152 + + + com.umlet.element.Relation + + 840 + 208 + 88 + 168 + + lt=. +<Uses + 24;24;24;64;72;64;72;152 + + From 0443e753e82e5444e63324f7dfe8b2cf4301ddbe Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Wed, 22 Jan 2014 11:31:56 +0100 Subject: [PATCH 09/76] GameLogic, Uml for levelLoader updated --- Code/Dokumentation/LevelLoader.uxf | 255 +++++++++++++++-------------- 1 file changed, 128 insertions(+), 127 deletions(-) diff --git a/Code/Dokumentation/LevelLoader.uxf b/Code/Dokumentation/LevelLoader.uxf index 1bdd8e14..723857bb 100644 --- a/Code/Dokumentation/LevelLoader.uxf +++ b/Code/Dokumentation/LevelLoader.uxf @@ -2,22 +2,14 @@ 8 - com.umlet.element.Class + com.umlet.element.Package - 560 - 360 - 232 - 136 + 552 + 320 + 584 + 368 - LevelLoader -<<API>><Interface>> --- -Functions: -vector<struct> LoadLevel(String fileName); -- -Privates: - - + LevelLoader @@ -43,19 +35,49 @@ Privates: lt=<<<<- 120;24;120;88;24;88 + + com.umlet.element.Class + + 560 + 544 + 232 + 136 + + <<Interface>> +Parser +-- +Functions: +vector<struct> Parse(); +- +Privates: +enum headerType; +const int FileHeaderSize; +const int FileVersion; + + + + + com.umlet.element.Class + + 624 + 208 + 80 + 24 + + LevelLoader + + + com.umlet.element.Relation - 544 - 64 - 136 - 160 + 640 + 208 + 40 + 168 - lt=lt=->>>> -m1=1..1 -m2=1..1 -Uses> - 24;40;80;40;120;40;120;144 + lt=<<. + 24;24;24;152 com.umlet.element.Relation @@ -72,71 +94,16 @@ m2=1..1 240;40;24;40 - com.umlet.element.Relation + com.umlet.element.Package - 680 - 176 - 152 - 56 + 248 + 320 + 248 + 160 - lt=- -m1=1..1 -m2=1..1 -Uses> - 24;40;136;40 - - - com.umlet.element.Class - - 816 - 192 - 128 - 40 - - Resource Loader -<<Dennis>><<Singleton> + Defines - - com.umlet.element.Relation - - 656 - 472 - 40 - 88 - - lt=<<<<- - 24;72;24;24 - - - com.umlet.element.Class - - 256 - 360 - 232 - 104 - - Defines.h -<<Header file>> --- -Enum ObjectType(static, dynamic, specials); -. -Struct static; -Struct dynamic; -Struct specials - - - - com.umlet.element.Relation - - 640 - 208 - 40 - 168 - - lt=<<. - 24;24;24;152 - com.umlet.element.Class @@ -170,44 +137,59 @@ Privates: com.umlet.element.Class - 560 - 544 + 256 + 360 232 - 136 + 104 - <<Interface>> -Parser + Defines.h +<<Header file>> -- -Functions: -vector<struct> Parse(); -- -Privates: -enum headerType; -const int FileHeaderSize; -const int FileVersion; - +Enum ObjectType(static, dynamic, specials); +. +Struct static; +Struct dynamic; +Struct specials - com.umlet.element.Package + com.umlet.element.Relation - 248 - 320 - 248 - 160 + 680 + 176 + 152 + 56 - Defines + lt=- +m1=1..1 +m2=1..1 +Uses> + 24;40;136;40 + + + com.umlet.element.Class + + 816 + 192 + 128 + 40 + + Resource Loader +<<Dennis>><<Singleton> - com.umlet.element.Package + com.umlet.element.Class - 552 - 320 - 584 - 368 + 928 + 560 + 200 + 120 - LevelLoader + Collection of functions +<<lots of functions>> +-- +functions for creating the right structs @@ -227,27 +209,21 @@ Uses> com.umlet.element.Class - 624 - 208 - 80 - 24 + 560 + 360 + 232 + 136 LevelLoader - - - - - com.umlet.element.Class - - 928 - 560 - 200 - 120 - - Collection of functions -<<lots of functions>> +<<API>><Interface>> -- -functions for creating the right structs +Functions: +vector<struct> LoadLevel(String fileName); +struct LoadLevelHeader(String fileName); +- +Privates: + + @@ -273,4 +249,29 @@ functions for creating the right structs <Uses 24;24;24;64;72;64;72;152 + + com.umlet.element.Relation + + 656 + 472 + 40 + 88 + + lt=<<<<- + 24;72;24;24 + + + com.umlet.element.Relation + + 544 + 64 + 136 + 160 + + lt=lt=->>>> +m1=1..1 +m2=1..1 +Uses> + 24;40;80;40;120;40;120;144 + From 80204ecbe3108fb731e27256e9a5d6c0375093af Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Wed, 22 Jan 2014 13:02:13 +0100 Subject: [PATCH 10/76] GL - Massdriver usage of applyeffect --- Code/Game/GameLogic/AttatchmentMassDriver.cpp | 13 ++++++++++++- Code/Game/GameLogic/AttatchmentMassDriver.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index a820ca46..b3fdc844 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -46,13 +46,24 @@ void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage, void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float dt) { Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt); - + Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), owner->GetPosition()); + Oyster::Math::Float4x4 hitSpace = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/4,1,1,20); + Oyster::Collision3D::Frustrum hitFrustum = Oyster::Collision3D::Frustrum(Oyster::Math3D::ViewProjectionMatrix(aim,hitSpace)); + //Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,ForcePushAction); //create frustum that will then collide with object and push them in the aimed direction //sample with frustum using visitor pattern(needs a function ptr sent with it that idicates what happens when a collision has been made) } +void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) +{ + Oyster::Physics::ICustomBody::State state = obj->GetState(); + Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500); + state.ApplyLinearImpulse(pushForce); + obj->SetState(state); +} + /******************************************************** * Pulls the player in the direction he is looking, used for fast movement(kinda like a jetpack) ********************************************************/ diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.h b/Code/Game/GameLogic/AttatchmentMassDriver.h index 14332525..3fb8932a 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.h +++ b/Code/Game/GameLogic/AttatchmentMassDriver.h @@ -36,6 +36,8 @@ namespace GameLogic ********************************************************/ void ForceSuck(const WEAPON_FIRE &usage, float dt); + void ForcePushAction(Oyster::Physics::ICustomBody *obj); + private: }; From 43eac4cbe3f8389d54cd1ebdffd1beefb96fdb34 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Wed, 22 Jan 2014 14:26:45 +0100 Subject: [PATCH 11/76] moved ForcePushAction for massdriver to collisionManager --- Code/Game/GameLogic/AttatchmentMassDriver.cpp | 12 +----------- Code/Game/GameLogic/CollisionManager.cpp | 7 +++++++ Code/Game/GameLogic/Object.cpp | 5 +++++ Code/Game/GameLogic/Object.h | 1 + 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index b3fdc844..11ec5ea8 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -37,7 +37,7 @@ void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage, ForcePull(usage,dt); break; } - + } /******************************************************** @@ -51,17 +51,7 @@ void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float Oyster::Collision3D::Frustrum hitFrustum = Oyster::Collision3D::Frustrum(Oyster::Math3D::ViewProjectionMatrix(aim,hitSpace)); //Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,ForcePushAction); - //create frustum that will then collide with object and push them in the aimed direction - //sample with frustum using visitor pattern(needs a function ptr sent with it that idicates what happens when a collision has been made) -} - -void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) -{ - Oyster::Physics::ICustomBody::State state = obj->GetState(); - Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500); - state.ApplyLinearImpulse(pushForce); - obj->SetState(state); } /******************************************************** diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 4dfebedf..73c7b7ed 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -3,6 +3,7 @@ #include "DynamicObject.h" #include "Player.h" #include "Level.h" +#include "AttatchmentMassDriver.h" using namespace Oyster; @@ -57,3 +58,9 @@ using namespace GameLogic; { return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; } + + void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) +{ + Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500); + ((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); +} diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 2937c605..034ded3d 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -49,6 +49,11 @@ Object::Object(void* collisionFunc, OBJECT_TYPE type) this->type = type; } +void Object::ApplyLinearImpulse(Oyster::Math::Float4 force) +{ + setState.ApplyLinearImpulse(force); +} + Object::~Object(void) { diff --git a/Code/Game/GameLogic/Object.h b/Code/Game/GameLogic/Object.h index 1316b287..26bf17db 100644 --- a/Code/Game/GameLogic/Object.h +++ b/Code/Game/GameLogic/Object.h @@ -23,6 +23,7 @@ namespace GameLogic OBJECT_TYPE GetType() const; int GetID() const; Oyster::Physics::ICustomBody* GetRigidBody(); + void ApplyLinearImpulse(Oyster::Math::Float4 force); void BeginFrame(); void EndFrame(); From ce80e8155c4e5f2d07003e478922b29df91b9d01 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Wed, 22 Jan 2014 14:39:10 +0100 Subject: [PATCH 12/76] GL - formatting mistake --- Code/Game/GameLogic/CollisionManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 73c7b7ed..73681b8e 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -60,7 +60,7 @@ using namespace GameLogic; } void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) -{ - Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500); - ((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); -} + { + Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500); + ((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); + } From 22a5db97f8e7913c8ebd71798a13927bb71577d5 Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Wed, 22 Jan 2014 15:22:52 +0100 Subject: [PATCH 13/76] Added final protocols, and a bit of support for them on server side --- .../DanBiasGame/GameClientState/GameState.cpp | 6 +- Code/Game/DanBiasServer/DanBiasServer.vcxproj | 2 + .../GameSession/GameSession_Events.cpp | 15 +- .../LobbyGeneralProtocolParser.cpp | 64 ++++++++ .../LobbySessions/LobbyProtocolParser.cpp | 37 +++++ .../DanBiasServer/LobbySessions/MainLobby.cpp | 70 ++------ .../DanBiasServer/LobbySessions/MainLobby.h | 15 +- Code/Game/GameProtocols/GameProtocols.vcxproj | 2 +- ...{ControlProtocols.h => GeneralProtocols.h} | 27 ++-- Code/Game/GameProtocols/LobbyProtocols.h | 152 +++++++++--------- Code/Game/GameProtocols/ObjectProtocols.h | 11 -- Code/Game/GameProtocols/PlayerProtocols.h | 62 +------ .../GameProtocols/ProtocolIdentificationID.h | 15 +- Code/Game/GameProtocols/Protocols.h | 2 +- Code/Game/aDanBiasGameLauncher/Launcher.cpp | 2 +- Code/Network/NetworkAPI/CustomNetProtocol.cpp | 40 ++++- Code/Network/NetworkAPI/CustomNetProtocol.h | 3 + 17 files changed, 288 insertions(+), 237 deletions(-) create mode 100644 Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp create mode 100644 Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp rename Code/Game/GameProtocols/{ControlProtocols.h => GeneralProtocols.h} (63%) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index e2e81cf5..9093654c 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -107,7 +107,7 @@ bool GameState::LoadModels(std::wstring mapFile) scale.v[1].y = 8; scale.v[2].z = 8; modelData.world = scale; //modelData.world * translate - modelData.modelPath = L"ball.dan"; + modelData.modelPath = L"..\\Content\\Models\\ball.dan"; modelData.id ++; obj = new C_DynamicObj(); @@ -162,6 +162,10 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI movePlayer.bForward = true; send = true; key_forward = true; + + GameLogic::Protocol_General_Text tp; + tp.text = "What!?"; + this->privData->nwClient->Send(tp); } } else diff --git a/Code/Game/DanBiasServer/DanBiasServer.vcxproj b/Code/Game/DanBiasServer/DanBiasServer.vcxproj index b491c65e..74223981 100644 --- a/Code/Game/DanBiasServer/DanBiasServer.vcxproj +++ b/Code/Game/DanBiasServer/DanBiasServer.vcxproj @@ -186,6 +186,8 @@ + + diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index c14b8e41..5dac3c3e 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -76,10 +76,6 @@ namespace DanBias case protocol_General_Status: switch (p[1].value.netInt) { - case GameLogic::Protocol_General_Status::States_bussy: - - break; - case GameLogic::Protocol_General_Status::States_disconected: printf("Client with ID [%i] dissconnected\n", c->GetClient()->GetID()); this->RemoveClient(c); @@ -92,12 +88,19 @@ namespace DanBias case GameLogic::Protocol_General_Status::States_ready: break; + + case GameLogic::Protocol_General_Status::States_leave: + + break; } break; - case protocol_General_Text: - + { + GameLogic::Protocol_General_Text temp(p); + printf("Message recieved from (%i):\t %s\n", c->GetID(), temp.text.c_str()); + } break; + } } diff --git a/Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp b/Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp new file mode 100644 index 00000000..cb19336f --- /dev/null +++ b/Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp @@ -0,0 +1,64 @@ +#include "MainLobby.h" +#include "LobbyClient.h" + +using namespace DanBias; +using namespace GameLogic; + +void MainLobby::ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c) +{ + switch (p[0].value.netShort) + { + case protocol_General_Status: + { + GeneralStatus(GameLogic::Protocol_General_Status(p), c); + } break; + case protocol_General_Text: + { + GameLogic::Protocol_General_Text(p); + } break; + case protocol_Lobby_Login: + { + + } break; + case protocol_Lobby_Join: + { + JoinLobby(GameLogic::Protocol_LobbyJoin(p), c); + } break; + } +} + +void MainLobby::GeneralStatus(GameLogic::Protocol_General_Status& p, DanBias::LobbyClient* c) +{ + switch (p.status) + { + case Protocol_General_Status::States_ready: + { + + } + case Protocol_General_Status::States_idle: + { + + } + case Protocol_General_Status::States_leave: + { + + } + case Protocol_General_Status::States_disconected: + { + Detach(c)->Disconnect(); + } + } +} + +void MainLobby::JoinLobby(GameLogic::Protocol_LobbyJoin& p, DanBias::LobbyClient* c) +{ + for (unsigned int i = 0; i < this->gameLobby.Size(); i++) + { + if (this->gameLobby[i]->GetID() == p.value) + { + this->gameLobby[i]->Attach(Detach(c)); + return; + } + } +} + diff --git a/Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp b/Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp new file mode 100644 index 00000000..27aade71 --- /dev/null +++ b/Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp @@ -0,0 +1,37 @@ +#include "MainLobby.h" + +using namespace DanBias; + +void MainLobby::ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c) +{ + switch (p[0].value.netShort) + { + case protocol_Lobby_Create: + CreateGame(GameLogic::Protocol_LobbyCreateGame(p), c); + break; + + case protocol_Lobby_Start: + + break; + + case protocol_Lobby_Refresh: + GameLogic::Protocol_LobbyRefresh(); + break; + } +} + +void MainLobby::CreateGame(GameLogic::Protocol_LobbyCreateGame& p, DanBias::LobbyClient* c) +{ + for (unsigned int i = 0; i < this->gameLobby.Size(); i++) + { + if(!gameLobby[i]) + { + gameLobby[i] = new GameLobby(NetworkSession::Detach(c)); + return; + } + } + + this->gameLobby.Push(new GameLobby(NetworkSession::Detach(c))); +} + + diff --git a/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp b/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp index ffdfa62e..ef5765c7 100644 --- a/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp +++ b/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp @@ -36,7 +36,15 @@ namespace DanBias { return this->box; } + void MainLobby::SetRefreshFrequency(float delta) + { + this->refreshFrequency = delta; + } + float MainLobby::GetRefreshFrequency() const + { + return this->refreshFrequency; + } //////// Private void MainLobby::ParseEvents() { @@ -44,67 +52,11 @@ namespace DanBias { NetEvent &e = this->box->Fetch(); - ParseProtocol(e.protocol, e.sender); - - } - } - void MainLobby::ParseProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c) - { - bool update = false; - switch (p[0].value.netShort) - { - case protocol_Lobby_CreateGame: - { - GameLogic::Protocol_LobbyCreateGame val(p); - CreateGame(val, c); - update = true; - } - break; - case protocol_Lobby_JoinLobby: - { - GameLogic::Protocol_LobbyJoinLobby val(p); - JoinLobby(val, c); - } - break; - case protocol_Lobby_LeaveLobby: - { - Detach(c)->Disconnect(); - } - break; - } + short type = e.protocol[0].value.netShort; - if(update) SendUpdate(); - } - - void MainLobby::CreateGame(GameLogic::Protocol_LobbyCreateGame& p, DanBias::LobbyClient* c) - { - for (unsigned int i = 0; i < this->gameLobby.Size(); i++) - { - if(!gameLobby[i]) - { - gameLobby[i] = new GameLobby(NetworkSession::Detach(c)); - return; - } + if(ProtocolIsLobby(type)) ParseLobbyProtocol(e.protocol, e.sender); + else if(ProtocolIsGeneral(type)) ParseGeneralProtocol(e.protocol, e.sender); } - - this->gameLobby.Push(new GameLobby(NetworkSession::Detach(c))); - } - void MainLobby::JoinLobby(GameLogic::Protocol_LobbyJoinLobby& p, DanBias::LobbyClient* c) - { - for (unsigned int i = 0; i < this->gameLobby.Size(); i++) - { - if (this->gameLobby[i]->GetID() == p.LobbyID) - { - this->gameLobby[i]->Attach(Detach(c)); - return; - } - } - } - - void MainLobby::SendUpdate() - { - //Send Lobbys - GameLogic::Protocol_LobbyRefresh(); } }//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/DanBiasServer/LobbySessions/MainLobby.h b/Code/Game/DanBiasServer/LobbySessions/MainLobby.h index a28d90e9..13e1bab5 100644 --- a/Code/Game/DanBiasServer/LobbySessions/MainLobby.h +++ b/Code/Game/DanBiasServer/LobbySessions/MainLobby.h @@ -8,6 +8,7 @@ #include "GameLobby.h" #include #include +#include namespace DanBias { @@ -20,20 +21,26 @@ namespace DanBias void Frame(); + void SetPostbox(Oyster::IPostBox* box); Oyster::IPostBox* GetPostbox(); + void SetRefreshFrequency(float delta); + float GetRefreshFrequency() const; + private: void ParseEvents(); - void ParseProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c); + void ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c); + void ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c); + void GeneralStatus(GameLogic::Protocol_General_Status& p, DanBias::LobbyClient* c); void CreateGame(GameLogic::Protocol_LobbyCreateGame& p, DanBias::LobbyClient* c); - void JoinLobby(GameLogic::Protocol_LobbyJoinLobby& p, DanBias::LobbyClient* c); - - void SendUpdate(); + void JoinLobby(GameLogic::Protocol_LobbyJoin& p, DanBias::LobbyClient* c); private: Oyster::IPostBox *box; Utility::DynamicMemory::DynamicArray> gameLobby; + Utility::WinTimer timer; + float refreshFrequency; private: friend class AdminInterface; diff --git a/Code/Game/GameProtocols/GameProtocols.vcxproj b/Code/Game/GameProtocols/GameProtocols.vcxproj index e01959b8..a5d5b19f 100644 --- a/Code/Game/GameProtocols/GameProtocols.vcxproj +++ b/Code/Game/GameProtocols/GameProtocols.vcxproj @@ -154,7 +154,7 @@ - + diff --git a/Code/Game/GameProtocols/ControlProtocols.h b/Code/Game/GameProtocols/GeneralProtocols.h similarity index 63% rename from Code/Game/GameProtocols/ControlProtocols.h rename to Code/Game/GameProtocols/GeneralProtocols.h index 0aba4dc6..98b7c060 100644 --- a/Code/Game/GameProtocols/ControlProtocols.h +++ b/Code/Game/GameProtocols/GeneralProtocols.h @@ -1,5 +1,5 @@ -#ifndef GAMELOGIC_CONTROL_PROTOCOLS_H -#define GAMELOGIC_CONTROL_PROTOCOLS_H +#ifndef GAMELOGIC_GENERAL_PROTOCOLS_H +#define GAMELOGIC_GENERAL_PROTOCOLS_H #include #include "ProtocolIdentificationID.h" @@ -12,9 +12,8 @@ namespace GameLogic { States_ready, States_idle, - States_bussy, - State_waiting, States_disconected, + States_leave }; States status; @@ -25,6 +24,11 @@ namespace GameLogic this->protocol[1].type = Oyster::Network::NetAttributeType_Short; } + Protocol_General_Status(Oyster::Network::CustomNetProtocol& p) + { + this->protocol = p; + status = (States)p[1].value.netShort; + } Protocol_General_Status(States state) { this->protocol[protocol_INDEX_ID].value = protocol_General_Status; @@ -45,18 +49,21 @@ namespace GameLogic struct Protocol_General_Text :public Oyster::Network::CustomProtocolObject { - char* text; - int destination; + std::string text; //The text to send + int destination; //The destination if any (Ie a whisper to a player) Protocol_General_Text() + : destination(-1) {} + Protocol_General_Text(Oyster::Network::CustomNetProtocol& p) { - this->protocol[protocol_INDEX_ID].value = protocol_General_Text; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_CharArray; + destination = p.Get(1).value.netInt; + text = p.Get(2).value.netCharPtr; } Oyster::Network::CustomNetProtocol* GetProtocol() override { - this->protocol[1].value.netCharPtr = text; + this->protocol.Set(protocol_INDEX_ID, protocol_General_Text, Oyster::Network::NetAttributeType_Short); + this->protocol.Set(1, destination, Oyster::Network::NetAttributeType_Int); + this->protocol.Set(2, text); return &protocol; } diff --git a/Code/Game/GameProtocols/LobbyProtocols.h b/Code/Game/GameProtocols/LobbyProtocols.h index ae175691..f043a7a2 100644 --- a/Code/Game/GameProtocols/LobbyProtocols.h +++ b/Code/Game/GameProtocols/LobbyProtocols.h @@ -9,12 +9,8 @@ #include #include "ProtocolIdentificationID.h" -//#define protocol_Lobby_CreateGame 200 -//#define protocol_Lobby_StartGame 201 -//#define protocol_Lobby_JoinGame 202 -//#define protocol_Lobby_JoinLobby 203 -//#define protocol_Lobby_LeaveLobby 204 -//#define protocol_Lobby_Refresh 205 +#include + namespace GameLogic { @@ -25,7 +21,7 @@ namespace GameLogic Protocol_LobbyCreateGame() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_CreateGame; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Create; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_CharArray; @@ -53,7 +49,7 @@ namespace GameLogic Protocol_LobbyStartGame() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_StartGame; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Start; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Char; @@ -68,20 +64,18 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_LobbyJoinGame :public Oyster::Network::CustomProtocolObject + struct Protocol_LobbyLogin :public Oyster::Network::CustomProtocolObject { - char gameId; - - Protocol_LobbyJoinGame() + // Login stuff + Protocol_LobbyLogin() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_JoinGame; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_Char; + this->protocol[1].type = Oyster::Network::NetAttributeType_Short; } Oyster::Network::CustomNetProtocol* GetProtocol() override { - protocol[1].value = gameId; return &protocol; } @@ -89,42 +83,26 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_LobbyJoinLobby :public Oyster::Network::CustomProtocolObject + struct Protocol_LobbyJoin :public Oyster::Network::CustomProtocolObject { - int LobbyID; - Protocol_LobbyJoinLobby(int id = -1) - { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_JoinLobby; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + short value; - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - LobbyID = id; - } - Protocol_LobbyJoinLobby(Oyster::Network::CustomNetProtocol& o) + Protocol_LobbyJoin() { - LobbyID = o[1].value.netInt; - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - this->protocol[1].value = LobbyID; - - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; - - struct Protocol_LobbyLeaveLobby :public Oyster::Network::CustomProtocolObject - { - - Protocol_LobbyLeaveLobby() - { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_LeaveLobby; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + } + Protocol_LobbyJoin(Oyster::Network::CustomNetProtocol& p) + { + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + value = p[1].value.netShort; } Oyster::Network::CustomNetProtocol* GetProtocol() override { + protocol[1].value = value; return &protocol; } @@ -134,48 +112,74 @@ namespace GameLogic struct Protocol_LobbyRefresh :public Oyster::Network::CustomProtocolObject { - struct LobbyUpdateData - { - std::string mapName; - int LobbyId; - }; - int count; - LobbyUpdateData* data; Protocol_LobbyRefresh() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Refresh; + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Login; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { return &protocol; } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + + /** + * A protocol that contains all data to send to client when update game lobby + */ + struct Protocol_LobbyGameData :public Oyster::Network::CustomProtocolObject + { + // Player list + struct PlayerData + { + std::string name; + int id; + }; + Utility::DynamicMemory::DynamicArray list; + + Protocol_LobbyGameData() + { + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_GameData; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - } - Protocol_LobbyRefresh( Oyster::Network::CustomNetProtocol* p ) - { - count = (*p)[1].value.netInt; - data = new LobbyUpdateData[count]; - for (int i = 0; i < count; i++) - { - //data[i].mapName = (*p)[i].value. - } - } - ~Protocol_LobbyRefresh() - { - delete [] data; - data = 0; + list.Reserve(10); } Oyster::Network::CustomNetProtocol* GetProtocol() override { - this->protocol[1].value.netInt = count; - for (int i = 2; i < count; i++) + int a = 1; + for (unsigned int i = 0; i < list.Size(); i++) { - protocol[i].type = Oyster::Network::NetAttributeType_CharArray; - protocol[i+1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[a].type = Oyster::Network::NetAttributeType_Int; + this->protocol[a].type = Oyster::Network::NetAttributeType_CharArray; - protocol[i].value.netCharPtr = const_cast(data[i-2].mapName.c_str()); - protocol[i+1].value.netInt = data[i-1].LobbyId; + this->protocol[a].value = list[i].id; + this->protocol.Set(a, list[i].name); } return &protocol; } + private: + Oyster::Network::CustomNetProtocol protocol; + }; + /** + * A protocol that contains all data to send to client when update main lobby + */ + struct Protocol_LobbyMainData :public Oyster::Network::CustomProtocolObject + { + // Game instance list + + Protocol_LobbyMainData() + { + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_MainData; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + return &protocol; + } + private: Oyster::Network::CustomNetProtocol protocol; }; diff --git a/Code/Game/GameProtocols/ObjectProtocols.h b/Code/Game/GameProtocols/ObjectProtocols.h index 31452d68..d37aee44 100644 --- a/Code/Game/GameProtocols/ObjectProtocols.h +++ b/Code/Game/GameProtocols/ObjectProtocols.h @@ -4,17 +4,6 @@ #include #include "ProtocolIdentificationID.h" -//protocol_Gameplay_PlayerMovement 300 -//protocol_Gameplay_PlayerMouseMovement 301 -//protocol_Gameplay_PlayerChangeWeapon 302 - -//#define protocol_Gameplay_ObjectPickup 303 -//#define protocol_Gameplay_ObjectDamage 304 -//#define protocol_Gameplay_ObjectPosition 305 -//#define protocol_Gameplay_ObjectEnabled 306 -//#define protocol_Gameplay_ObjectDisabled 307 -//#define protocol_Gameplay_ObjectCreate 308 - namespace GameLogic { struct Protocol_ObjectPickup :public Oyster::Network::CustomProtocolObject diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index 36ccf497..b91cb46d 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -8,13 +8,16 @@ #include #include "ProtocolIdentificationID.h" +#include +//protocol_Gameplay_PlayerMovement 300 +//protocol_Gameplay_PlayerMouseMovement 301 +//protocol_Gameplay_PlayerChangeWeapon 302 namespace GameLogic { struct Protocol_PlayerMovement :public Oyster::Network::CustomProtocolObject { - bool bForward; bool bBackward; bool bLeft; @@ -116,63 +119,6 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_PlayerDamage :public Oyster::Network::CustomProtocolObject - { - - int hp; - // look at dir - - Protocol_PlayerDamage() - { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDamage; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - - } - const Protocol_PlayerDamage& operator=(Oyster::Network::CustomNetProtocol& val) - { - hp = val[1].value.netInt; - return *this; - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - this->protocol[1].value =hp; - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; - - struct Protocol_PlayerPickup :public Oyster::Network::CustomProtocolObject - { - int pickupID; - - Protocol_PlayerPickup() - { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPickup; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; - - } - const Protocol_PlayerPickup& operator=(Oyster::Network::CustomNetProtocol& val) - { - pickupID = val[3].value.netInt; - - return *this; - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - this->protocol[3].value = pickupID; - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; - } #endif // !GAMELOGIC_PLAYER_PROTOCOLS_H diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index 5293093b..7b742eb9 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -25,8 +25,6 @@ #define protocol_GeneralMIN 100 #define protocol_General_Status 100 #define protocol_General_Text 101 -#define protocol_General_Disconnect 102 -#define protocol_General_Login 110 #define protocol_GeneralMAX 199 @@ -34,12 +32,13 @@ /********* LOBBY PROTOCOLS ***************************************************************************************************/ /***********[ 200 - 299 ]***********/ #define protocol_LobbyMIN 200 -#define protocol_Lobby_CreateGame 200 -#define protocol_Lobby_StartGame 201 -#define protocol_Lobby_JoinGame 202 -#define protocol_Lobby_JoinLobby 203 -#define protocol_Lobby_LeaveLobby 204 -#define protocol_Lobby_Refresh 205 +#define protocol_Lobby_Create 200 +#define protocol_Lobby_Start 201 +#define protocol_Lobby_Join 202 +#define protocol_Lobby_Login 203 +#define protocol_Lobby_Refresh 204 +#define protocol_Lobby_MainData 205 +#define protocol_Lobby_GameData 206 #define protocol_LobbyMAX 299 diff --git a/Code/Game/GameProtocols/Protocols.h b/Code/Game/GameProtocols/Protocols.h index 6e084b75..1e7236c8 100644 --- a/Code/Game/GameProtocols/Protocols.h +++ b/Code/Game/GameProtocols/Protocols.h @@ -4,7 +4,7 @@ #include "ObjectProtocols.h" #include "PlayerProtocols.h" #include "LobbyProtocols.h" -#include "ControlProtocols.h" +#include "GeneralProtocols.h" #include "GameplayProtocols.h" #endif // !GAMEPROTOCOLS_GAMEPROTOCOLS_H diff --git a/Code/Game/aDanBiasGameLauncher/Launcher.cpp b/Code/Game/aDanBiasGameLauncher/Launcher.cpp index 8f8e6252..c4ff4194 100644 --- a/Code/Game/aDanBiasGameLauncher/Launcher.cpp +++ b/Code/Game/aDanBiasGameLauncher/Launcher.cpp @@ -51,7 +51,7 @@ int WINAPI WinMain( HINSTANCE hinst, HINSTANCE prevInst, PSTR cmdLine, int cmdSh serverThread = std::thread(ServerFnc); - Sleep(100); + Sleep(200); clientThread = std::thread(ClientFnc); diff --git a/Code/Network/NetworkAPI/CustomNetProtocol.cpp b/Code/Network/NetworkAPI/CustomNetProtocol.cpp index d34832d7..6d4ea1bf 100644 --- a/Code/Network/NetworkAPI/CustomNetProtocol.cpp +++ b/Code/Network/NetworkAPI/CustomNetProtocol.cpp @@ -23,7 +23,6 @@ struct CustomNetProtocol::PrivateData if(size == 0) continue; attributes[i->first].value.netCharPtr = new char[size + 1]; - //strcpy_s(attributes[i->first].value.netCharPtr, size + 1, i->second.value.netCharPtr); memcpy(&attributes[i->first].value.netCharPtr[0], &i->second.value.netCharPtr[0], size + 1); attributes[i->first].type = NetAttributeType_CharArray; } @@ -49,8 +48,8 @@ struct CustomNetProtocol::PrivateData switch (i->second.type) { case NetAttributeType_CharArray: - //delete [] i->second.value.netCharPtr; - i->second.value.netCharPtr = 0; + delete [] i->second.value.netCharPtr; + //i->second.value.netCharPtr = 0; break; } } @@ -87,4 +86,39 @@ NetAttributeContainer& CustomNetProtocol::operator[](int ID) } return this->privateData->attributes[ID]; +} + +void CustomNetProtocol::Set(int ID, Oyster::Network::NetAttributeValue val, Oyster::Network::NetAttributeType type) +{ + this->privateData->attributes[ID].type = type; + + switch (type) + { + case Oyster::Network::NetAttributeType_Bool: + case Oyster::Network::NetAttributeType_Char: + case Oyster::Network::NetAttributeType_UnsignedChar: + case Oyster::Network::NetAttributeType_Short: + case Oyster::Network::NetAttributeType_UnsignedShort: + case Oyster::Network::NetAttributeType_Int: + case Oyster::Network::NetAttributeType_UnsignedInt: + case Oyster::Network::NetAttributeType_Int64: + case Oyster::Network::NetAttributeType_UnsignedInt64: + case Oyster::Network::NetAttributeType_Float: + case Oyster::Network::NetAttributeType_Double: + this->privateData->attributes[ID].value = val; + break; + } +} +void CustomNetProtocol::Set(int ID, std::string s) +{ + if(s.size() == 0) return; + + this->privateData->attributes[ID].type = Oyster::Network::NetAttributeType_CharArray; + + this->privateData->attributes[ID].value.netCharPtr = new char[s.size() + 1]; + memcpy(&this->privateData->attributes[ID].value.netCharPtr[0], &s[0], s.size() + 1); +} +const NetAttributeContainer& CustomNetProtocol::Get(int id) +{ + return this->privateData->attributes[id]; } \ No newline at end of file diff --git a/Code/Network/NetworkAPI/CustomNetProtocol.h b/Code/Network/NetworkAPI/CustomNetProtocol.h index 82d92c99..85997c51 100644 --- a/Code/Network/NetworkAPI/CustomNetProtocol.h +++ b/Code/Network/NetworkAPI/CustomNetProtocol.h @@ -87,6 +87,9 @@ namespace Oyster const CustomNetProtocol& operator=(const CustomNetProtocol& o); NetAttributeContainer& operator[](int ID); + void Set(int id, Oyster::Network::NetAttributeValue val, Oyster::Network::NetAttributeType type); + void Set(int ID, std::string s); + const NetAttributeContainer& Get(int id); private: struct PrivateData; From 6a888cb15462b7236f2a78a6038a53b3ef835d32 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Wed, 22 Jan 2014 15:47:44 +0100 Subject: [PATCH 14/76] GL - adjusting collisionManager --- Code/Game/GameLogic/CollisionManager.cpp | 31 ++++++++++++------------ Code/Game/GameLogic/Level.cpp | 2 +- Code/Game/GameLogic/Level.h | 2 +- Code/Game/GameLogic/Player.h | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 73681b8e..eaafd59a 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -9,11 +9,11 @@ using namespace Oyster; using namespace GameLogic; - void PlayerVBox(Player &player, DynamicObject &box); - void PlayerVObject(Player &player, Object &obj); + void PlayerVBox(Player &player, DynamicObject &box, Oyster::Math::Float kineticEnergyLoss); + void PlayerVObject(Player &player, Object &obj, Oyster::Math::Float kineticEnergyLoss); - - Physics::ICustomBody::SubscriptMessage Player::PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj) + //Physics::ICustomBody::SubscriptMessage + void Player::PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) { Player *player = ((Player*)(rigidBodyPlayer->GetCustomTag())); Object *realObj = (Object*)obj->GetCustomTag(); @@ -21,31 +21,30 @@ using namespace GameLogic; switch (realObj->GetType()) { case OBJECT_H::OBJECT_TYPE_GENERIC: - PlayerVObject(*player,*realObj); - Physics::ICustomBody::SubscriptMessage_none; + PlayerVObject(*player,*realObj, kineticEnergyLoss); + //return Physics::ICustomBody::SubscriptMessage_none; break; case OBJECT_TYPE::OBJECT_TYPE_BOX: - PlayerVBox(*player,(*(DynamicObject*) realObj)); - Physics::ICustomBody::SubscriptMessage_none; + PlayerVBox(*player,(*(DynamicObject*) realObj), kineticEnergyLoss); + //return Physics::ICustomBody::SubscriptMessage_none; break; case OBJECT_TYPE::OBJECT_TYPE_PLAYER: - Physics::ICustomBody::SubscriptMessage_none; + //return Physics::ICustomBody::SubscriptMessage_none; break; - } - return Physics::ICustomBody::SubscriptMessage_none; + //return Physics::ICustomBody::SubscriptMessage_none; } - void PlayerVBox(Player &player, DynamicObject &box) + void PlayerVBox(Player &player, DynamicObject &box, Oyster::Math::Float kineticEnergyLoss) { //use kinetic energyloss of the collision in order too determin how much damage to take //use as part of the damage algorithm player.DamageLife(20); } - void PlayerVObject(Player &player, Object &obj) + void PlayerVObject(Player &player, Object &obj, Oyster::Math::Float kineticEnergyLoss) { //Collision between a player and a general static or dynamic object //use kinetic energyloss of the collision in order too determin how much damage to take @@ -53,10 +52,10 @@ using namespace GameLogic; player.DamageLife(20); } - - Oyster::Physics::ICustomBody::SubscriptMessage Level::LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj) + //Oyster::Physics::ICustomBody::SubscriptMessage + void Level::LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) { - return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; + //return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; } void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index b2bbe60c..8335ea8e 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -25,7 +25,7 @@ void Level::InitiateLevel(float radius) sbDesc.radius = 8; //radius; sbDesc.mass = 10e12f; //sbDesc.mass = 0; //10^16 - sbDesc.subscription_onCollision = CollisionManager::LevelCollision; + sbDesc.subscription_onCollisionResponse = Level::LevelCollision; ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); API::Instance().AddObject(rigidBody); diff --git a/Code/Game/GameLogic/Level.h b/Code/Game/GameLogic/Level.h index 4a28fabe..71e7ed06 100644 --- a/Code/Game/GameLogic/Level.h +++ b/Code/Game/GameLogic/Level.h @@ -57,7 +57,7 @@ namespace GameLogic * @param rigidBodyLevel: physics object of the level * @param obj: physics object for the object that collided with the level ********************************************************/ - static Oyster::Physics::ICustomBody::SubscriptMessage LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj); + static void LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); private: TeamManager teamManager; diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index 14359554..6d1f0424 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -50,7 +50,7 @@ namespace GameLogic * @param rigidBodyPlayer: physics object of the player * @param obj: physics object for the object that collided with the player ********************************************************/ - static Oyster::Physics::ICustomBody::SubscriptMessage PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj); + static void PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); bool IsWalking(); From f657a58a4fe0295e2a67318964111833f6bbbf08 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Thu, 23 Jan 2014 08:24:35 +0100 Subject: [PATCH 15/76] GL - adding a camera on the client --- Code/Game/DanBiasGame/DanBiasGame.vcxproj | 2 + .../GameClientState/C_obj/C_Player.cpp | 3 +- .../DanBiasGame/GameClientState/Camera.cpp | 192 ++++++++++++++++++ .../Game/DanBiasGame/GameClientState/Camera.h | 63 ++++++ .../DanBiasGame/GameClientState/GameState.cpp | 181 ++++++++++------- .../DanBiasGame/GameClientState/GameState.h | 3 + Code/Game/GameLogic/Object.cpp | 17 +- Code/Game/GameLogic/Player.cpp | 8 +- .../Implementation/PhysicsAPI_Impl.cpp | 4 +- 9 files changed, 390 insertions(+), 83 deletions(-) create mode 100644 Code/Game/DanBiasGame/GameClientState/Camera.cpp create mode 100644 Code/Game/DanBiasGame/GameClientState/Camera.h diff --git a/Code/Game/DanBiasGame/DanBiasGame.vcxproj b/Code/Game/DanBiasGame/DanBiasGame.vcxproj index 0e856428..25696efd 100644 --- a/Code/Game/DanBiasGame/DanBiasGame.vcxproj +++ b/Code/Game/DanBiasGame/DanBiasGame.vcxproj @@ -192,6 +192,7 @@ + @@ -205,6 +206,7 @@ + diff --git a/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp b/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp index 4cd6fbd3..49c450b5 100644 --- a/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp +++ b/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp @@ -8,6 +8,7 @@ struct C_Player::myData Oyster::Math3D::Float4x4 view; Oyster::Math3D::Float4x4 proj; Oyster::Graphics::Model::Model *model; + Oyster::Math3D::Float4 lookDir; int ID; }privData; @@ -29,7 +30,7 @@ void C_Player::Init(ModelInitData modelInit) privData->model->WorldMatrix = modelInit.world; privData->model->Visible = modelInit.visible; privData->ID = modelInit.id; - + privData->lookDir = Oyster::Math3D::Float4 (0,0,1,0); } void C_Player::setPos(Oyster::Math::Float4x4 world) { diff --git a/Code/Game/DanBiasGame/GameClientState/Camera.cpp b/Code/Game/DanBiasGame/GameClientState/Camera.cpp new file mode 100644 index 00000000..13b5a70f --- /dev/null +++ b/Code/Game/DanBiasGame/GameClientState/Camera.cpp @@ -0,0 +1,192 @@ +#include "Camera.h" + +Camera::Camera() +{ + this->m_position = Oyster::Math::Float3(0, 50, 0); + this->mRight = Oyster::Math::Float3(1, 0, 0); + this->mUp = Oyster::Math::Float3(0, 1, 0); + this->mLook = Oyster::Math::Float3(0, 0, 1); +} + +Camera::~Camera() +{ +} + +void Camera::SetPosition(const Oyster::Math::Float3& v) +{ + this->m_position = v; +} + +Oyster::Math::Float3 Camera::GetPosition()const +{ + return this->m_position; +} + +Oyster::Math::Float3 Camera::GetRight()const +{ + return this->mRight; +} + +Oyster::Math::Float3 Camera::GetUp()const +{ + return this->mUp; +} + +Oyster::Math::Float3 Camera::GetLook()const +{ + return this->mLook; +} + +float Camera::GetNearZ()const +{ + return this->mNearZ; +} + +float Camera::GetFarZ()const +{ + return this->mFarZ; +} + +float Camera::GetAspect()const +{ + return this->mAspect; +} + +Oyster::Math::Float3 Camera::CrossMatrix(const Oyster::Math::Float3& vector, const Oyster::Math::Float4x4& matrix) +{ + Oyster::Math::Float3 vec; + vec.x = matrix.m11*vector.x + matrix.m12*vector.y + matrix.m13*vector.z; + vec.y = matrix.m21*vector.x + matrix.m22*vector.y + matrix.m23*vector.z; + vec.z = matrix.m31*vector.x + matrix.m32*vector.y + matrix.m33*vector.z; + return vec; +} + +void Camera::SetLens(float fovY, float aspect, float zn, float zf) +{ + this->mFovY = fovY; + this->mAspect = aspect; + this->mNearZ = zn; + this->mFarZ = zf; + + /*float yScale = tan((Oyster::Math::pi*0.5f) - (mFovY*0.5f)); + float xScale = yScale/this->mAspect; + + mProj = Oyster::Math::Float4x4(xScale, 0, 0, 0, + 0, yScale, 0, 0, + 0, 0, zf/(zf-zn), 1, + 0, 0, -zn*zf/(zf-zn), 0); + mProj.Transpose();*/ + mProj = Oyster::Math3D::ProjectionMatrix_Perspective(fovY,aspect,zn,zf); +} + +void Camera::LookAt(Oyster::Math::Float3 pos, Oyster::Math::Float3 target, Oyster::Math::Float3 worldUp) +{ + Oyster::Math::Float3 L; + + L = target - pos; + L.Normalize(); + + Oyster::Math::Float3 R; + R = worldUp.Cross(L); + R.Normalize(); + + Oyster::Math::Float3 U; + U = L.Cross(R); + + this->m_position = pos; + this->mLook = L; + this->mRight = R; + this->mUp = U; +} + +Oyster::Math::Float4x4 Camera::View()const +{ + return this->mView; +} + +Oyster::Math::Float4x4 Camera::Proj()const +{ + return this->mProj; +} + +Oyster::Math::Float4x4 Camera::ViewsProj()const +{ + Oyster::Math::Float4x4 M; + M = mView * mProj; + return M; +} + +void Camera::Walk(float dist) +{ + this->m_position += dist*this->mLook; +} + +void Camera::Strafe(float dist) +{ + this->m_position += dist*this->mRight; +} + +void Camera::Pitch(float angle) +{ + float radians = angle * 0.0174532925f; + + Oyster::Math::Float4x4 R; + + Oyster::Math3D::RotationMatrix(radians,-mRight,R); + this->mUp = CrossMatrix(this->mUp, R); + this->mLook = CrossMatrix(this->mLook, R); +} + +void Camera::Yaw(float angle) +{ + float radians = angle * 0.0174532925f; + + Oyster::Math::Float4x4 R; + + Oyster::Math::Float3 up(0,1,0); + Oyster::Math3D::RotationMatrix(radians,-up,R); + + this->mRight = CrossMatrix(this->mRight, R); + this->mUp = CrossMatrix(mUp, R); + this->mLook = CrossMatrix(this->mLook, R); +} + +void Camera::UpdateViewMatrix() +{ + mLook.Normalize(); + mUp = mLook.Cross(mRight); + mUp.Normalize(); + mRight = mUp.Cross(mLook); + mView = Oyster::Math3D::ViewMatrix_LookAtDirection(mLook, mUp, m_position); + /* + mLook.Normalize(); + mUp = mLook.Cross(mRight); + mUp.Normalize(); + mRight = mUp.Cross(mLook); + + float x = -m_position.Dot(mRight); + float y = -m_position.Dot(mUp); + float z = -m_position.Dot(mLook); + + mView.m11 = mRight.x; + mView.m21 = mRight.y; + mView.m31 = mRight.z; + mView.m41 = x; + + mView.m12 = mUp.x; + mView.m22 = mUp.y; + mView.m32 = mUp.z; + mView.m42 = y; + + mView.m13 = mLook.x; + mView.m23 = mLook.y; + mView.m33 = mLook.z; + mView.m43 = z; + + mView.m14 = 0.0f; + mView.m24 = 0.0f; + mView.m34 = 0.0f; + mView.m44 = 1.0f; + + mView.Transpose();*/ +} \ No newline at end of file diff --git a/Code/Game/DanBiasGame/GameClientState/Camera.h b/Code/Game/DanBiasGame/GameClientState/Camera.h new file mode 100644 index 00000000..56ea5569 --- /dev/null +++ b/Code/Game/DanBiasGame/GameClientState/Camera.h @@ -0,0 +1,63 @@ +#ifndef CAMERA__H +#define CAMERA__H + +#include "OysterMath.h" + +class Camera +{ +private: + + Oyster::Math::Float3 m_position; + Oyster::Math::Float3 mRight; + Oyster::Math::Float3 mUp; + Oyster::Math::Float3 mLook; + + + + float mNearZ; + float mFarZ; + float mAspect; + float mFovY; + + Oyster::Math::Float4x4 mView; + Oyster::Math::Float4x4 mProj; + +public: + Camera(); + virtual ~Camera(); + + void SetPosition(const Oyster::Math::Float3& v); + + Oyster::Math::Float3 GetPosition()const; + + Oyster::Math::Float3 GetRight()const; + Oyster::Math::Float3 GetUp()const; + Oyster::Math::Float3 GetLook()const; + + float GetNearZ()const; + float GetFarZ()const; + float GetAspect()const; + + Oyster::Math::Float3 CrossMatrix(const Oyster::Math::Float3& v, const Oyster::Math::Float4x4& m); + + void SetLens(float fovY, float aspect, float zn, float zf); + + void LookAt(Oyster::Math::Float3 pos, Oyster::Math::Float3 target, Oyster::Math::Float3 worldUp); + + void setLook(Oyster::Math::Float3 look) { mLook = look; } + void setUp(Oyster::Math::Float3 up) { mUp = up; } + void setRight(Oyster::Math::Float3 right) { mRight = right; } + + Oyster::Math::Float4x4 View()const; + Oyster::Math::Float4x4 Proj()const; + Oyster::Math::Float4x4 ViewsProj()const; + + void Walk(float dist); + void Strafe(float dist); + + void Pitch(float angle); + void Yaw(float angle); + + void UpdateViewMatrix(); +}; +#endif \ No newline at end of file diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index e2e81cf5..665ba668 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -4,7 +4,7 @@ #include "C_obj/C_DynamicObj.h" #include #include "NetworkClient.h" - +#include "Camera.h" using namespace DanBias::Client; @@ -17,7 +17,6 @@ struct GameState::myData int modelCount; Oyster::Network::NetworkClient* nwClient; gameStateState state; - }privData; @@ -38,10 +37,12 @@ GameState::~GameState(void) bool GameState::Init(Oyster::Network::NetworkClient* nwClient) { // load models + camera = new Camera; privData = new myData(); privData->state = gameStateState_loading; privData->nwClient = nwClient; privData->state = LoadGame(); + return true; } GameState::gameStateState GameState::LoadGame() @@ -119,10 +120,19 @@ bool GameState::LoadModels(std::wstring mapFile) } bool GameState::InitCamera(Oyster::Math::Float3 startPos) { + Oyster::Math::Float3 dir = Oyster::Math::Float3(0,0,-1); + Oyster::Math::Float3 up =Oyster::Math::Float3(0,1,0); + Oyster::Math::Float3 pos = Oyster::Math::Float3(0, 0, 20); + + camera->LookAt(pos, dir, up); + camera->SetLens(3.14f/2, 1024/768, 1, 1000); + privData->proj = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/2,1024.0f/768.0f,.1f,1000); //privData->proj = Oyster::Math3D::ProjectionMatrix_Orthographic(1024, 768, 1, 1000); Oyster::Graphics::API::SetProjection(privData->proj); - + camera->UpdateViewMatrix(); + privData->view = camera->View(); + privData->view = Oyster::Math3D::ViewMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); privData->view = Oyster::Math3D::OrientationMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); return true; @@ -147,81 +157,9 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI // read server data // update objects { - bool send = false; - GameLogic::Protocol_PlayerMovement movePlayer; - movePlayer.bForward = false; - movePlayer.bBackward = false; - movePlayer.bLeft = false; - movePlayer.bRight = false; + readKeyInput(KeyInput); + camera->UpdateViewMatrix(); - if(KeyInput->IsKeyPressed(DIK_W)) - { - - if(!key_forward) - { - movePlayer.bForward = true; - send = true; - key_forward = true; - } - } - else - key_forward = false; - - if(KeyInput->IsKeyPressed(DIK_S)) - { - if(!key_backward) - { - movePlayer.bBackward = true; - send = true; - key_backward = true; - } - } - else - key_backward = false; - - if(KeyInput->IsKeyPressed(DIK_A)) - { - if(!key_strafeLeft) - { - movePlayer.bLeft = true; - send = true; - key_strafeLeft = true; - } - } - else - key_strafeLeft = false; - - if(KeyInput->IsKeyPressed(DIK_D)) - { - if(!key_strafeRight) - { - movePlayer.bRight = true; - send = true; - key_strafeRight = true; - } - } - else - key_strafeRight = false; - - - if (privData->nwClient->IsConnected() && send) - { - privData->nwClient->Send(movePlayer); - } - - //send delta mouse movement - if (KeyInput->IsMousePressed()) - { - GameLogic::Protocol_PlayerMouse deltaMouseMove; - deltaMouseMove.dxMouse = KeyInput->GetYaw(); - deltaMouseMove.dyMouse = KeyInput->GetPitch(); - //privData->nwClient->Send(deltaMouseMove); - } - - // send event data - // - if(KeyInput->IsKeyPressed(DIK_L)) - privData->state = GameState::gameStateState_end; } break; case gameStateState_end: @@ -236,7 +174,9 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI } bool GameState::Render() { - Oyster::Graphics::API::SetView(privData->view); + Oyster::Graphics::API::SetView(camera->View()); + //Oyster::Graphics::API::SetProjection(camera->Proj()); + //Oyster::Graphics::API::SetView(privData->view); Oyster::Graphics::API::SetProjection(privData->proj); Oyster::Graphics::API::NewFrame(); for (unsigned int i = 0; i < privData->object.size(); i++) @@ -259,6 +199,88 @@ bool GameState::Release() privData = NULL; return true; } +void GameState::readKeyInput(InputClass* KeyInput) +{ + + bool send = false; + GameLogic::Protocol_PlayerMovement movePlayer; + movePlayer.bForward = false; + movePlayer.bBackward = false; + movePlayer.bLeft = false; + movePlayer.bRight = false; + + if(KeyInput->IsKeyPressed(DIK_W)) + { + + if(!key_forward) + { + movePlayer.bForward = true; + send = true; + key_forward = true; + } + } + else + key_forward = false; + + if(KeyInput->IsKeyPressed(DIK_S)) + { + if(!key_backward) + { + movePlayer.bBackward = true; + send = true; + key_backward = true; + } + } + else + key_backward = false; + + if(KeyInput->IsKeyPressed(DIK_A)) + { + if(!key_strafeLeft) + { + movePlayer.bLeft = true; + send = true; + key_strafeLeft = true; + } + } + else + key_strafeLeft = false; + + if(KeyInput->IsKeyPressed(DIK_D)) + { + if(!key_strafeRight) + { + movePlayer.bRight = true; + send = true; + key_strafeRight = true; + } + } + else + key_strafeRight = false; + + + if (privData->nwClient->IsConnected() && send) + { + privData->nwClient->Send(movePlayer); + } + + //send delta mouse movement + if (KeyInput->IsMousePressed()) + { + GameLogic::Protocol_PlayerMouse deltaMouseMove; + deltaMouseMove.dxMouse = KeyInput->GetYaw(); + deltaMouseMove.dyMouse = KeyInput->GetPitch(); + //privData->nwClient->Send(deltaMouseMove); + camera->Yaw(KeyInput->GetYaw()); + camera->Pitch(KeyInput->GetPitch()); + } + + + // send event data + // + if(KeyInput->IsKeyPressed(DIK_L)) + privData->state = GameState::gameStateState_end; +} void GameState::Protocol(ProtocolStruct* pos) { @@ -290,6 +312,9 @@ void GameState::Protocol( ObjPos* pos ) { privData->object[i]->setPos(world); + + camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); + camera->UpdateViewMatrix(); //privData->view = world; //privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.h b/Code/Game/DanBiasGame/GameClientState/GameState.h index 3942afba..a6ed7d3f 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.h +++ b/Code/Game/DanBiasGame/GameClientState/GameState.h @@ -3,6 +3,7 @@ #include "GameClientState.h" #include "OysterMath.h" #include +#include "Camera.h" namespace DanBias { namespace Client @@ -21,6 +22,7 @@ private: bool key_backward; bool key_strafeRight; bool key_strafeLeft; + Camera* camera; struct myData; myData* privData; @@ -33,6 +35,7 @@ public: bool InitCamera(Oyster::Math::Float3 startPos) ; gameStateState LoadGame(); + void readKeyInput(InputClass* KeyInput); bool Render()override; bool Release()override; diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 2937c605..fb0e8994 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -72,10 +72,25 @@ Oyster::Physics::ICustomBody* Object::GetRigidBody() void Object::BeginFrame() { + this->rigidBody->SetState(this->setState); + } +// update physic void Object::EndFrame() { - this->rigidBody->GetState(this->getState); + + //Oyster::Math::Float rot = (setState.GetGravityNormal().xyz).Dot(getState.GetGravityNormal().xyz); + //Oyster::Math::Float3 axis = (setState.GetGravityNormal().xyz).Cross(getState.GetGravityNormal().xyz); + Oyster::Math::Float4x4 rotMatrix = setState.GetOrientation(); //Oyster::Math3D::RotationMatrix(rot, axis); + Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); + setState.SetOrientation(rotMatrix); + + + this->getState = this->rigidBody->GetState(); + + + this->setState = this->getState; + } \ No newline at end of file diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index dd65c375..d21422d8 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -92,9 +92,13 @@ void Player::Respawn(Oyster::Math::Float3 spawnPoint) this->lookDir = Oyster::Math::Float4(1,0,0); } -void Player::Rotate(float x, float y) +void Player::Rotate(float dx, float dy) { - this->setState.AddRotation(Oyster::Math::Float4(x, y)); + + //this->lookDir = lookDir; + + //this->setState.AddRotation(Oyster::Math::Float4(x, y)); + //this->setState.SetRotation(); } void Player::Jump() diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp index d2799b2f..7615216c 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp @@ -194,8 +194,10 @@ void API_Impl::Update() if( gravityImpulse != Float4::null ) { state.ApplyLinearImpulse( gravityImpulse ); - (*proto)->SetGravityNormal( gravityImpulse.GetNormalized().xyz ); + //state.SetGravityNormal( gravityImpulse.GetNormalized()); + //(*proto)->SetGravityNormal( gravityImpulse.GetNormalized().xyz ); (*proto)->SetState( state ); + (*proto)->SetGravityNormal( gravityImpulse.GetNormalized().xyz ); } // Step 2: Apply Collision Response From 121fd51c4520ddd8cb6fec8c90acb44d0b9d3bc3 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Thu, 23 Jan 2014 08:57:46 +0100 Subject: [PATCH 16/76] GL collision with level should worm --- Code/Game/GameLogic/CollisionManager.cpp | 3 +++ Code/Game/GameLogic/Level.cpp | 3 +++ Code/Game/GameLogic/Level.h | 1 + Code/Game/GameLogic/StaticObject.cpp | 5 +++++ Code/Game/GameLogic/StaticObject.h | 1 + 5 files changed, 13 insertions(+) diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index eaafd59a..5be071fe 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -32,6 +32,9 @@ using namespace GameLogic; case OBJECT_TYPE::OBJECT_TYPE_PLAYER: //return Physics::ICustomBody::SubscriptMessage_none; break; + case OBJECT_TYPE::OBJECT_TYPE_WORLD: + int test = 5; + break; } //return Physics::ICustomBody::SubscriptMessage_none; diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 8335ea8e..bda72073 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -26,8 +26,11 @@ void Level::InitiateLevel(float radius) sbDesc.mass = 10e12f; //sbDesc.mass = 0; //10^16 sbDesc.subscription_onCollisionResponse = Level::LevelCollision; + levelObj = new StaticObject(OBJECT_TYPE::OBJECT_TYPE_WORLD); + ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); + rigidBody->SetCustomTag(levelObj); API::Instance().AddObject(rigidBody); ICustomBody::State state; rigidBody->GetState(state); diff --git a/Code/Game/GameLogic/Level.h b/Code/Game/GameLogic/Level.h index 71e7ed06..ce299b78 100644 --- a/Code/Game/GameLogic/Level.h +++ b/Code/Game/GameLogic/Level.h @@ -65,6 +65,7 @@ namespace GameLogic Utility::DynamicMemory::DynamicArray> dynamicObjects; GameMode gameMode; Utility::DynamicMemory::SmartPointer rigidBodyLevel; + StaticObject *levelObj; }; diff --git a/Code/Game/GameLogic/StaticObject.cpp b/Code/Game/GameLogic/StaticObject.cpp index 155834fe..03f33e10 100644 --- a/Code/Game/GameLogic/StaticObject.cpp +++ b/Code/Game/GameLogic/StaticObject.cpp @@ -14,6 +14,11 @@ StaticObject::StaticObject(void* collisionFunc, OBJECT_TYPE type) :Object(collisionFunc,type) { +} +StaticObject::StaticObject(OBJECT_TYPE type) + :Object(NULL,type) +{ + } diff --git a/Code/Game/GameLogic/StaticObject.h b/Code/Game/GameLogic/StaticObject.h index 31121447..1a6fd63b 100644 --- a/Code/Game/GameLogic/StaticObject.h +++ b/Code/Game/GameLogic/StaticObject.h @@ -17,6 +17,7 @@ namespace GameLogic public: StaticObject(); StaticObject(void* collisionFunc, OBJECT_TYPE type); + StaticObject(OBJECT_TYPE type); ~StaticObject(void); private: From bd739f8acb684c54356345a06957a8146caebec5 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Thu, 23 Jan 2014 09:14:04 +0100 Subject: [PATCH 17/76] GL - Creation of objects kinda sorted --- Code/Game/GameLogic/Level.cpp | 7 ++++--- Code/Game/GameLogic/Object.cpp | 11 +++++++++++ Code/Game/GameLogic/Object.h | 1 + Code/Game/GameLogic/Player.cpp | 2 -- Code/Game/GameLogic/StaticObject.cpp | 5 +++++ Code/Game/GameLogic/StaticObject.h | 1 + 6 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index bda72073..1249dafa 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -25,18 +25,19 @@ void Level::InitiateLevel(float radius) sbDesc.radius = 8; //radius; sbDesc.mass = 10e12f; //sbDesc.mass = 0; //10^16 - sbDesc.subscription_onCollisionResponse = Level::LevelCollision; - levelObj = new StaticObject(OBJECT_TYPE::OBJECT_TYPE_WORLD); + ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); rigidBody->SetCustomTag(levelObj); - API::Instance().AddObject(rigidBody); + ICustomBody::State state; rigidBody->GetState(state); state.SetRestitutionCoeff(0.1); rigidBody->SetState(state); + levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); + API::Gravity gravityWell; gravityWell.gravityType = API::Gravity::GravityType_Well; diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index bb36246f..35c1765b 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -49,6 +49,17 @@ Object::Object(void* collisionFunc, OBJECT_TYPE type) this->type = type; } +Object::Object(ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type) +{ + Oyster::Physics::API::Instance().AddObject(rigidBody); + + rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); + + this->objectID = GID(); + + this->type = type; +} + void Object::ApplyLinearImpulse(Oyster::Math::Float4 force) { setState.ApplyLinearImpulse(force); diff --git a/Code/Game/GameLogic/Object.h b/Code/Game/GameLogic/Object.h index 26bf17db..a4385345 100644 --- a/Code/Game/GameLogic/Object.h +++ b/Code/Game/GameLogic/Object.h @@ -18,6 +18,7 @@ namespace GameLogic public: Object(); Object(void* collisionFunc, OBJECT_TYPE type); + Object(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type); ~Object(void); OBJECT_TYPE GetType() const; diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index a6320bd5..fae9b086 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -18,8 +18,6 @@ Player::Player() lookDir = Oyster::Math::Float4(0,0,-1,0); setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); - - } Player::~Player(void) diff --git a/Code/Game/GameLogic/StaticObject.cpp b/Code/Game/GameLogic/StaticObject.cpp index 03f33e10..c9fda9cf 100644 --- a/Code/Game/GameLogic/StaticObject.cpp +++ b/Code/Game/GameLogic/StaticObject.cpp @@ -19,6 +19,11 @@ StaticObject::StaticObject(OBJECT_TYPE type) :Object(NULL,type) { +} +StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type) + :Object(rigidBody,collisionFunc,type) +{ + } diff --git a/Code/Game/GameLogic/StaticObject.h b/Code/Game/GameLogic/StaticObject.h index 1a6fd63b..d033f6e8 100644 --- a/Code/Game/GameLogic/StaticObject.h +++ b/Code/Game/GameLogic/StaticObject.h @@ -17,6 +17,7 @@ namespace GameLogic public: StaticObject(); StaticObject(void* collisionFunc, OBJECT_TYPE type); + StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type); StaticObject(OBJECT_TYPE type); ~StaticObject(void); From 8c297d1e9b3818ec914c99c08e213a7a42607d8e Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Thu, 23 Jan 2014 09:24:55 +0100 Subject: [PATCH 18/76] GL - starting to send lookdir to server --- .../DanBiasGame/GameClientState/GameState.cpp | 10 +++++--- .../GameSession/GameSession_Events.cpp | 10 +++++--- Code/Game/GameLogic/Game.h | 2 +- Code/Game/GameLogic/GameAPI.h | 2 +- Code/Game/GameLogic/Game_PlayerData.cpp | 4 +-- Code/Game/GameLogic/Player.cpp | 2 +- Code/Game/GameLogic/Player.h | 2 +- Code/Game/GameProtocols/PlayerProtocols.h | 25 +++++++++++-------- .../GameProtocols/ProtocolIdentificationID.h | 2 +- 9 files changed, 34 insertions(+), 25 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index 665ba668..bb2703c4 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -267,12 +267,14 @@ void GameState::readKeyInput(InputClass* KeyInput) //send delta mouse movement if (KeyInput->IsMousePressed()) { - GameLogic::Protocol_PlayerMouse deltaMouseMove; - deltaMouseMove.dxMouse = KeyInput->GetYaw(); - deltaMouseMove.dyMouse = KeyInput->GetPitch(); - //privData->nwClient->Send(deltaMouseMove); + + camera->Yaw(KeyInput->GetYaw()); camera->Pitch(KeyInput->GetPitch()); + camera->UpdateViewMatrix(); + GameLogic::Protocol_PlayerLook playerLookDir; + //deltaMouseMove.camera->GetLook(); + //privData->nwClient->Send(playerLookDir); } diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index 5dac3c3e..44392da0 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -54,10 +54,14 @@ namespace DanBias c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_RIGHT); } break; - case protocol_Gameplay_PlayerMouseMovement: + case protocol_Gameplay_PlayerLookDir: { - Protocol_PlayerMouse m; m = p; - c->GetPlayer()->Rotate(m.dxMouse, m.dyMouse); + Protocol_PlayerLook m; m = p; + Oyster::Math3D::Float3 lookDir; + lookDir.x = p.Get(1).value.netFloat; + lookDir.y = p.Get(2).value.netFloat; + lookDir.z = p.Get(3).value.netFloat; + c->GetPlayer()->Rotate(lookDir); } break; case protocol_Gameplay_PlayerChangeWeapon: diff --git a/Code/Game/GameLogic/Game.h b/Code/Game/GameLogic/Game.h index 11c8d325..7d40a3c5 100644 --- a/Code/Game/GameLogic/Game.h +++ b/Code/Game/GameLogic/Game.h @@ -39,7 +39,7 @@ namespace GameLogic Oyster::Math::Float4x4 GetOrientation() override; int GetID() const override; OBJECT_TYPE GetObjectType() const override; - void Rotate(const float x, const float y) override; + void Rotate(const Oyster::Math3D::Float3 lookDir) override; Player *player; }; diff --git a/Code/Game/GameLogic/GameAPI.h b/Code/Game/GameLogic/GameAPI.h index 12f5022f..889466c7 100644 --- a/Code/Game/GameLogic/GameAPI.h +++ b/Code/Game/GameLogic/GameAPI.h @@ -79,7 +79,7 @@ namespace GameLogic * @param x: The relative x axis * @param y: The relative y axis **/ - virtual void Rotate(const float x, const float y) = 0; + virtual void Rotate(const const Oyster::Math3D::Float3 lookDir) = 0; /******************************************************** * Uses the chosen players weapon based on input diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index e651a02f..1c7de849 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -49,7 +49,7 @@ OBJECT_TYPE Game::PlayerData::GetObjectType() const { return this->player->GetType(); } -void Game::PlayerData::Rotate(const float x, const float y) +void Game::PlayerData::Rotate(const Oyster::Math3D::Float3 lookDir) { - this->player->Rotate(x, y); + this->player->Rotate(lookDir); } \ No newline at end of file diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index a6320bd5..cdd32090 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -92,7 +92,7 @@ void Player::Respawn(Oyster::Math::Float3 spawnPoint) this->lookDir = Oyster::Math::Float4(1,0,0); } -void Player::Rotate(float dx, float dy) +void Player::Rotate(const Oyster::Math3D::Float3 lookDir) { //this->lookDir = lookDir; diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index 6d1f0424..371460c8 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -42,7 +42,7 @@ namespace GameLogic void Respawn(Oyster::Math::Float3 spawnPoint); - void Rotate(float x, float y); + void Rotate(const Oyster::Math3D::Float3 lookDir); /******************************************************** * Collision function for player, this is to be sent to physics through the subscribe function with the rigidbody diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index b91cb46d..9ad819e8 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -56,33 +56,36 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_PlayerMouse :public Oyster::Network::CustomProtocolObject + struct Protocol_PlayerLook :public Oyster::Network::CustomProtocolObject { - float dxMouse; - float dyMouse; - + float lookDirX; + float lookDirY; + float lookDirZ; - Protocol_PlayerMouse() + Protocol_PlayerLook() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerMouseMovement; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerLookDir; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Float; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; + this->protocol[3].type = Oyster::Network::NetAttributeType_Float; } - const Protocol_PlayerMouse& operator=(Oyster::Network::CustomNetProtocol& val) + const Protocol_PlayerLook& operator=(Oyster::Network::CustomNetProtocol& val) { - dxMouse = val[1].value.netFloat; - dyMouse = val[2].value.netFloat; + lookDirX = val[1].value.netFloat; + lookDirY = val[2].value.netFloat; + lookDirZ = val[3].value.netFloat; return *this; } Oyster::Network::CustomNetProtocol* GetProtocol() override { - this->protocol[1].value = dxMouse; - this->protocol[2].value = dyMouse; + this->protocol[1].value = lookDirX; + this->protocol[2].value = lookDirY; + this->protocol[3].value = lookDirZ; return &protocol; } diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index 7b742eb9..353338b6 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -47,7 +47,7 @@ /***********[ 300 - 399 ]***********/ #define protocol_GameplayMIN 300 #define protocol_Gameplay_PlayerMovement 300 -#define protocol_Gameplay_PlayerMouseMovement 301 +#define protocol_Gameplay_PlayerLookDir 301 #define protocol_Gameplay_PlayerChangeWeapon 302 #define protocol_Gameplay_ObjectPickup 303 #define protocol_Gameplay_ObjectDamage 304 From fb2cf714c698ac68e9e7da7faf6ea3ddf58d6ef0 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Thu, 23 Jan 2014 14:28:53 +0100 Subject: [PATCH 19/76] GL - sending dir to server, forward and backward walking works --- .../DanBiasGame/GameClientState/GameState.cpp | 14 ++++++++------ Code/Game/DanBiasLauncher/Launcher.cpp | 2 +- Code/Game/GameLogic/Level.cpp | 6 +++--- Code/Game/GameLogic/Object.cpp | 10 ++++++---- Code/Game/GameLogic/Player.cpp | 16 +++++++++++++--- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index bb2703c4..e141c8be 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -273,8 +273,11 @@ void GameState::readKeyInput(InputClass* KeyInput) camera->Pitch(KeyInput->GetPitch()); camera->UpdateViewMatrix(); GameLogic::Protocol_PlayerLook playerLookDir; - //deltaMouseMove.camera->GetLook(); - //privData->nwClient->Send(playerLookDir); + Oyster::Math::Float3 look = camera->GetLook(); + playerLookDir.lookDirX = look.x; + playerLookDir.lookDirY = look.y; + playerLookDir.lookDirZ = look.z; + privData->nwClient->Send(playerLookDir); } @@ -313,12 +316,11 @@ void GameState::Protocol( ObjPos* pos ) if(privData->object[i]->GetId() == pos->object_ID) { privData->object[i]->setPos(world); - - + //camera->setRight((Oyster::Math::Float3(world[0], world[1], world[2]))); + //camera->setUp((Oyster::Math::Float3(world[4], world[5], world[6]))); + //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); camera->UpdateViewMatrix(); - //privData->view = world; - //privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); } } diff --git a/Code/Game/DanBiasLauncher/Launcher.cpp b/Code/Game/DanBiasLauncher/Launcher.cpp index 38f499eb..95bad91c 100644 --- a/Code/Game/DanBiasLauncher/Launcher.cpp +++ b/Code/Game/DanBiasLauncher/Launcher.cpp @@ -18,7 +18,7 @@ int WINAPI WinMain( HINSTANCE hinst, HINSTANCE prevInst, PSTR cmdLine, int cmdSh DanBias::DanBiasGameDesc gameDesc; gameDesc.port = 15151; //gameDesc.port = 15152; - //gameDesc.IP = "193.11.184.196"; + //gameDesc.IP = "193.11.184.109"; //gameDesc.IP = "193.11.184.31"; //gameDesc.IP = "194.47.150.56"; gameDesc.IP = "127.0.0.1"; diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 1249dafa..ab25b059 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -29,7 +29,7 @@ void Level::InitiateLevel(float radius) ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); - rigidBody->SetCustomTag(levelObj); + //rigidBody->SetCustomTag(levelObj); ICustomBody::State state; rigidBody->GetState(state); @@ -38,14 +38,14 @@ void Level::InitiateLevel(float radius) levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); - API::Gravity gravityWell; + /*API::Gravity gravityWell; gravityWell.gravityType = API::Gravity::GravityType_Well; gravityWell.well.mass = 10e12f; gravityWell.well.position = Oyster::Math::Float4(0,0,0,1); API::Instance().AddGravity(gravityWell); - + */ } void Level::AddPlayerToTeam(Player *player, int teamID) diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 35c1765b..2ee2a613 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -54,7 +54,7 @@ Object::Object(ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type) Oyster::Physics::API::Instance().AddObject(rigidBody); rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); - + rigidBody->SetCustomTag(this); this->objectID = GID(); this->type = type; @@ -98,9 +98,11 @@ void Object::EndFrame() //Oyster::Math::Float rot = (setState.GetGravityNormal().xyz).Dot(getState.GetGravityNormal().xyz); //Oyster::Math::Float3 axis = (setState.GetGravityNormal().xyz).Cross(getState.GetGravityNormal().xyz); - Oyster::Math::Float4x4 rotMatrix = setState.GetOrientation(); //Oyster::Math3D::RotationMatrix(rot, axis); - Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); - setState.SetOrientation(rotMatrix); + + // align with gravity normal + //Oyster::Math::Float4x4 rotMatrix = setState.GetOrientation(); //Oyster::Math3D::RotationMatrix(rot, axis); + //Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); + //setState.SetOrientation(rotMatrix); this->getState = this->rigidBody->GetState(); diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index b24b5bf3..8c604a05 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -18,6 +18,7 @@ Player::Player() lookDir = Oyster::Math::Float4(0,0,-1,0); setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); + //setState.SetRotation(Oyster::Math::Float4(0,0,0,0).Normalize()); } Player::~Player(void) @@ -64,6 +65,7 @@ void Player::MoveBackwards() void Player::MoveRight() { //Do cross product with forward vector and negative gravity vector + // temp up vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r = (-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); setState.ApplyLinearImpulse(r * 20 * this->gameInstance->GetFrameTime()); @@ -72,6 +74,7 @@ void Player::MoveRight() void Player::MoveLeft() { //Do cross product with forward vector and negative gravity vector + // temp up vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r1 = -(-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); //Still get zero setState.ApplyLinearImpulse(-r * 20 * this->gameInstance->GetFrameTime()); @@ -92,9 +95,16 @@ void Player::Respawn(Oyster::Math::Float3 spawnPoint) void Player::Rotate(const Oyster::Math3D::Float3 lookDir) { - - //this->lookDir = lookDir; - + + this->lookDir = lookDir; + + Oyster::Math::Float4 up(0,1,0,0);//-setState.GetGravityNormal(); + Oyster::Math::Float4 pos = setState.GetCenterPosition(); + Oyster::Math::Float4x4 world = Oyster::Math3D::OrientationMatrix_LookAtDirection(lookDir, up.xyz, pos.xyz); + // cant set rotation + //setState.SetOrientation(world); + //this->lookDir = lookDir - up.xyz; + //this->setState.AddRotation(Oyster::Math::Float4(x, y)); //this->setState.SetRotation(); } From 76e4b23f195bb6447bb87f308f93544f6b8e6c6b Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Thu, 23 Jan 2014 14:54:53 +0100 Subject: [PATCH 20/76] Added documentation Mathematical exercise on how to convert angular momentum to angular velocity --- .../angular momentum to angular velocity.odt | Bin 0 -> 174508 bytes .../angular_momentum_to_angular_velocity.pdf | Bin 0 -> 112800 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Code/Dokumentation/Other/angular momentum to angular velocity.odt create mode 100644 Code/Dokumentation/Other/angular_momentum_to_angular_velocity.pdf diff --git a/Code/Dokumentation/Other/angular momentum to angular velocity.odt b/Code/Dokumentation/Other/angular momentum to angular velocity.odt new file mode 100644 index 0000000000000000000000000000000000000000..3abd5446fb580dce6e6d257641319a21c934ffc5 GIT binary patch literal 174508 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 -J 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 literal 0 HcmV?d00001 diff --git a/Code/Dokumentation/Other/angular_momentum_to_angular_velocity.pdf b/Code/Dokumentation/Other/angular_momentum_to_angular_velocity.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c6facc1e14d758e8a850a2c8b4531bc4e49e8f8b GIT binary patch literal 112800 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 sQ~v4W{aN6pbd52e(*hRAq1FtwxJ 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 z zPBnVRqls#pqgfksT%I}DCE2M}cXxm@^nK`LwYPyCnYK~OY(`qMhQmkkb`=*;E-_D$ zFAGN}SQZ?qjs*>U0+5T!ydA{$fFYZ2$9-qtpeR@@now;|Lv$A)C_K}hV8XPpb+x@~ zfaw6S$_B?d2J!4Zw=w3mup02U>X%V$Pag!jt91$oARiwVKC-oc-c; zSTZ>9cVy0XMz$%X*cC$FlRBY-4iXlwR}!jE?9G)K=CYIqk)k9}kH*C6N0>C>ePk2V*x18`i0%#ObCBQ^+FP2`uQs4J3I2m6NpHWIlIf684 z`K&5lNr#8+hvI6VW$ zdlru+nDZ#OUyJsjCjLB&w}%t=b2f-FE!0X{7) zvc&pO!Y>z_CybA(Hpfs-67jb^lww~3*=60BvXYW4_R^Hq^U(g&K!U5I>Zkkx!}a7eMH+8B)__?_#22dR}xlDfRzM^dqvg`79b ziHA9E;fuo(%6kwC`=lY24dJM+Doer=bp3T_;((AiaqqJjqQt9n_^Hz8BiOq`ct<4^ zcX7sYgWxG8<+-jkLA)k0xmoj0a-hp_eA$^YOqDIDv2T9&G7zY50g>*r5;g|eAR|b4aqff|Rgvy&}x(ZKQ_W@aJVL&(_ zCEbk5i=O=x-&j-#OHw@FvlIO~ZP_5ZN2co&C*9V`V{kp@anOs&6XJPh7KTZa>9pWl zEx%Vv`ZT*-cwlfK19s?~x$IRe1!xgoI~t!^uT9S=*(H!l3`ea@z5+G?950NloFq^z z%iJG^+)#hr6U5CqbrT|<7KQ}j*Pv8D-R#;g0I->l#G5_bW5-fpu#97*0 z8pDdb$O1WOA3g$t_huZgpLcDqE-zXw7_O~QhTPTRup%E{K)3H+8Y01AnYZNcficZ2 z2w$oD9wM?SV)Lx{n9?+B_#Kr1ot6T)maI7E*WUnjIo4j_w}K90W*mziq5akL zmK_Fm$ROuNuW5_iL5D#DmcFMY3>Fh@Pj^b=>yjPm!rq>$E*gGf-Te zUkPuqDb;^1&v8D`J+Puv0~Xo<7u!Rx293k(?KBNU`n+}JSKZ=pTTk2Hx__-yZJmJS)>*zA>xOLNL z`7xGNau15@XDfda9KB$~CfWjr7b*g-o)K4X3O0qGy?&D0{E{34P$kHa`U9zqK2-5O zjavKngrdZ_Q4;}^DodhNlhzr_b`ZxH4f{sh2#3U$sIy#Vh(=XQ*_ZOoz}GS@xlC{2 zIqY7!gm3poS913XPxUWYZ@io^hjS(@7>w{U^LRgs?dA=%ixh{i@)AXj)97yBbo;NX zvVqvY5`}x31F>f+*8#`5CIG&@3dSyBx^pHbY2ZK(Fn;<_#Bm|KLuFJ&%a;crY;)YgJ!@Z4k!)x)tAu5_mB;2OBB!vjs;HXx4_+g z*GO9f);c~(2As6Y=Gap%)w}A8NFiz{R+Ztwks|MpJ(qn3Y7pf$d%$It6jwZv3ve!k zyzcODbE7LNW}7f`b{nG_1^VQPl2q3ozt<5+Y& zXc27^0TCo|l{XTeKx2>_@%*;VK&@y zY9xTFXj}83Pl2dg=!Q5VQUJ{ml`V5YJ}#&O7K-9jo4AagJ5#ZRPpNNRg zV#R~452#K9d)7yFg$4e<&Mz=_Z1VPDwPhm)tGyigBrbZJ=%VwRfi7xbS7YFV`&$?u zJA%YJyyhpYo*vqsws!e_93WaRP%Vv{&jy}#k50vz&COZf_Oh$_Ud|(-ZaY8LtF5NG z%_>wrx)ujKgUF9bs0z`q_ei4M@xyNTeY#ldX~H1Vb{n3rtD3(Yc1;L!A(#l?C#s*` zsyM3?Gnt=sd@&hp8iTRXQyv58G7=I9XO8LNO{RYfp#314S!)wWNgkKeE8!=f&2P;k zCgj-A=4gXAVguKuMXAEr@UF#iu}9bd22?uK0_H71CEXOy;M|nX;9S-Taq*jY-}-qS zKNL{46dd%@&4uHvJs5(Ox|BgiQ&UB4LNw|BdPG07Esd1B|F%o+|46%HFk1?hNgn57 zSdxCtFfq&O6mqb`WrRS#xAzP5@WCJC(UABMdgA%nIi@>A6YketP8k zg_&HX)8<7p=l;Un>l{!+VLkpuZ}DY`RiWn*$}E>ugxhT_^7S1;DzmWte3PXu zSVdXL1@Qzn)-6)R_3(a;nKQ(|*6;X~Sh%~Q;v}2p>lF05;;)b>W#@Y7oGDoWKyzF? ztX;T$4m~#>RKpkopHWB&y+JH$I~$!tm~##uKMcBAglq1gs4Q4*bn2Y`eDtix)@@Dd zyogr*jBj;nWEbp07_o%sGfbXLU@diHqHI~cwv+G&yuuP&jtC-^JMVN6)rQ5>u z%mK^T?H?I4J>EKSm^VA+06nAu$7d3w84%rYFh;gGpMi70vt(4m)-z?~YFM+O;IyBLAK@KkvOOK? zjMA1Vvv0QJ_5wOJ_yx_ul1Rl01vNlXFa_&ooX2h9SaVX z&Xu;Ex8D|NkCWWTZtN204e}C=AV?P&gd3Pr#~Rl~E~-fTGsydb33bh^J^j`SH;mju z2hOOMH0?k5+`c~G@3}EM7e9(zV=D}5{RSgOVVoS21*wVosZfi6a58i*1P=0~|6sITHLIM!_6kCZxsFO4QLMgM^5 z0Nkl?n+gU!#=Uwe1b67gChhDKQ}xlc#0+z#)m*E^tIv7Wj!#+0)I`Q5Z;SAe`Xu}z z!*btqxj(2hgxlt^i;&_KG_YAqfuRLHkL>C4>Ex9Yz~wKz`{{i-W9yvuP6@25(t?2{ zz=4<8tN6llhWM>!e^3W|BogldzqyY%EA_ns8f4qUS<4x^GT>2UaRf;`;HJPK%5}uD zUg>&49hNVw8U`3DblVo-8f;aXXJ_X5_xpZY*J$OA`c}KVi`14CYxZIvU|A4#sOCV0lVuMYROQPjVUM=#?RP9Lg=ak65&kp=3h0k^FKSzKfA_c6pNxY zE8V(m+l@YBezT>o-6?OR!h-ZGNTfxX)k7(3gWYo?i=%B@lwQRB_=gonAVPG`sa+R4XAD z_QfP0LWL=xC}?uo2H?%h0IUqN{yM~ZCp;_Oak9SiCreQ3RX}!F#@w^Z)f8?~xli`> z=cXIwbWX!pwy}W}Bb`qNlAe?m9ry%CT^C~W0klGilGEFbwk+AG-OM#dc5G4lU)0zR z5DQiDkGrP7tC2pWDIPjs2e?nlGk6%)?T}3^`N@x+8&|?%Y5~d9C(aka&|R9L#a%?v z#gZ7^w9!TqZWCHuMA@-V$@bV~I{WJqBNfSlT#g8Ty4!&sBttpswfIfa)JmnAl#aJm zwyQ6Mv;~Z|I=i(tQ>wJHiM6<7CLIvnrYq8Kj+ve8lv@dwYibT`tPGtVF`j?Ts_a$; zsNt*#UZ*)l8^hcLAs(FJt?+fCG}<=D@G?0A1u~8;dlnq6!|JRvKi$NunAup!_})pA z=%km8A6{!uHy*rbA0X-*CB{6MwQPo)C0r+sB5;;pxF5{A^H_E9MZ@zG8x!xu6}@!J zC`p$4g*S$k2Dxwj55f%F<0qvGhv9R@wlI@UchnEt*IBp6LtK*#K~3N2u~V*-H0kUX zC_Eh0N;Nl|%WIvSlu9+%m)GZ)*Vk4znk@n~=cltP%{ONjR&y)O&rQxZmRIOyjmA=nk>?w&PA<-l*Vfnr%p|Gyd|&268du%! z4BH!9oAVva>>lS`4-Y0Umktpnh7{=)XV=?GJywxCmJi0yQ&sPeUq&V(&R6g9&r?^} z*i+mJjz?47T34CtnNLeooXpL-J&$Wg4&w#boQ~8|oleL&gKs$EZSYI3O8mp zX6Jo?$5&PFDlQxmEPG-VB~sn)Di$uv&)eI}DPIQGpHxdXd@L;W6tusU=BA3bwy>)? znxD*9W4EqU;m4_V)p@KIL}F4J>vJxLOjuyH-se1!x}GjXC_fUMTYT0zRBZGJZLY8M z3Js6hWq*5k5zv5m3JzVVj^9`)A4^z2UosUxtgygLAE!h{&t|2)Td>3wo(qx0-JD*n z&sDNASsxw?XR|2B)Ie4<2C*zjcdur;HSiw&%`yd5$tUa{WVJe6qi~cIlHZ>o6L#fI zgY%}$63T#wft2y|1?TPSw~t(31$Fw~rka4aIb5Ekd}=`;hBd&>l)5_Uby!>doDR?jv90+OKKY{z zDKwBTxdUDrLT-xy4zU0-(9Q#&FFTsOsmrL(zbl%!yib8qL)&XoW~@1~4gOd(FINQ5 z9QH=00=M&9G0b1b#L@a^B3RV(F>Emp`04tDd=*=ma~`S++0g-)HLFG9L*6jaJ0gHj z(a89e{43t}#50}*$)~iU#4}JL!Z8y!aQs6kZLJ8e8MMym(t#BxB-_ge0*)H^lh7JJ zMf2{-;Y`N!!RnvE>QxNDuEPaG>a11q?!Sw_D3`E_MJnV&9J7cxj-T^M9ocQ+eRsc# zjs}q>AR8*^gXQ1{&W?Q-2L2h$1MF*e7KRN!l+~;8D}RRN9e`si_i~m#1kTlP=$L9* z>t65$i6g`wK{U&1`8dNBk`i$GCYUaIUDfP@;v@c<+G6h)E&(IF!P_ zjzW09zBr;_0+F6eW{$=0xN(3exx}=#j;W7sIo?^{Nu|94O|S~6XAVJc-DM1VZM&Qp zBp4;as?~Eox#YL5`%BmENLD6-+E9w&Oi*V)^4MeaU4I|IG(2%A5C3$Q{Wgc;@8taA z;VkfNmn`#Vi4f={LtPKINko^q;gyCre-E zd6`j#U=)b*b?@NnshauV=>8)=X+(sV1FF`PBL%7!i1NCBZUQY~oLALDFAwA0#d6cS zFkeae+$g=Um^Ul!szXpV1VJ2+;HqC4>Qu~KrPP1e!Q0`$gRY&4fGawbJ1%r-}k#;V2b3E&cust@S z+c`)gm{#F<9Os-nuMt@A!&#S$B+uQro1+_Q9^B-Sq$DnE2>3}D3(Y6N zP8pX$)VA~J)muKin^*}LHyW23JFP3D5UTLNr*1SxP449UW*&*!9$$_-u3XSlj=j>m zibn;4I>TBg=x0M+pVsR()qh70buemJ2^c$C&c=Gm>HKJW3u~9c{wVsm!W8$j)1br$FJ7d;eY_f7!i$d z^@)2PVlN@iyCiNqPGBJVSs;yJ@gvUYDTtldBBd_4xms1+p?hCN@`SM0VS2Th4fJ+|op_5M@Acn)eM+~uu(GR@d=1**=h zU*;uiY}vNtR-!n__FrcB!Qs5fU4j$$jZMk7-a-di7KuP40j6@jPsl}tA`3y&(2a&# zO8S*X*+oz$!LiR+2u6#wxq+F31!ICKRrqy;qG?d1ve)n(hcaj0EJJ{W`3g%EWuv+U zNGcknIgk`HJbv+n#PRl`ay8-=GPMuXcuJ%A`09p)vG?$u<}zn6Gc&5cwvcN7M&Px< zL}o~mDw1FAR58%9si7Y$zjvc3#1>nSY!5ZICfDNZP^8I0zm^3MIQJ2h1%)F|(PV57 zMchu&6_k3wjxv-z$N>IG>)rEK$;)2CRQ|rbfyv2^Q`^u^bUo{bjc|XV4I+JIJ$dL+ zRmNn>uy}U|I44+GQ4*$Dp}b0?U7b}I0!7NkOW)uZAoyUuyr2oOjOnxfj3y&r+4(4B3&2ez>5kx(rZxQu z-72Aqmblm{C`3Z-F2!U=(mkKpBsK(wUeUQnw_O?cjTsJ2WbW@U$3Gn3Ux+OYEiKc3 zF$DR1wr-VIlf(t0 zdu{6t%1Nq>V}iUnPK#<~%SRP(b}%pmu8e@;^7!i>bLpk63a8XbJSu2$HFLIaL0#2{kgSuO*fCMgo_t*L8Mic`jE7m$oLYdH97;ol1GjopS{#eK zerw+cJTiLTK&Wjum$ZhBKHw4eOKGWy+)>5`iSZHNI?^-howgsL*2cqe{zg0W|CUYj z{|1r$A6ofaI+pee&HjJUv45!6Kjkk{_KRZutNwNT7v=gzY_t56lx6&?|5d(d*uNoN z|7iL@NY}q)@}I)_pGif&?)(2hy3#WGJ`oL zegBs8-WxJ~6spDta76@Q^SIfl6{%>;xk01k>5hVTbCyu$8>VX|HX46EfTh z5G)Mv+lO@*yyGybrAH|3 zHYr!U4!p@=ryQcCWCT9oz}R3SUOynhk-*23?kc7v zmrlyrO}tllr3Peo!_YKsg$&vS_SguJ_arHOw(fur$5lTUh4TyMy2=6Sh%}&hy}EvW z_9<`d6Hq|3t~|orbsFqlTl0CVoYwO^Ejur4dk zShA=_6yGRf_PdNlChc1BnewDVEUzBEi$({8jjOjc@-#DU*k|4E%ro%TA??oUY2D8o zySr{^t0AZ!7i+s9vf!G!;%328GxKTHlN2!lE9M;huFU9G&Y1-8$S|2)9#3%6SNZ%# zQrI2F97CIkw}~bKt`O&qsSg2HfUc%|M&k<9^&fA$O5MBUWQJS%`l@0OH(r85g(fLe zJl=UBus^Dd5qX_B2_%LpvFUZg@?a|Cvp;GFbXUl{56qaR6gy#PTez8ON${WJsD zQw*Qp9l6e=qGjaM6SrfLm(?s7dYN&(i7a>qtWQxyS8o2ju%2&L(x5S$kiwWB;xr|P z0ve*!+;#e!236tKRE*r^6QNRG`iOM>Y3?a8%086#Wg!61b}G#a)@J2r;dr0PU{0FO zf_b1Ss}$LR4ciUrK27R~__{N@NZUXF1Q6#yunlN291>W6A3B#&8Y368C*z~^E}`5b zg*O9k2JiZp-Gp(B5}~eM8SI0+OrSbeMfG=UON?q!JEv1TQvD&pvfKzMXT3VR-D__r^{O9nalV3S3E_N}5e?h!-B~2=-+Dr9RbEv@RLp3SUVClMq*E4t0HnUNf=LC4DX&wW3AM zR@2d|!@YBNSto1smF_3D_ z*%>tU=DVC#HGg#>9I+-?azDMY_S*FJTu%MSh3y4se;vvaK93Ech^0S4K@M@9QhOs5 zJW7i(;0^~Vq`Lo9=6H6iz=4f57F0o2(rPWuUD(=;lG$*5J;+0z%na!Mpwho#jP0u-*?aI_z_- z&Cp|B5x!+FNJIJSgOSigQqg_<_lf*snE*t+yoj_$tBCaBi$Jca;JkT7V4y9r|Gbva zX{{zMS=j^H;g3DngN`OLzh>$JKfm25ulLn-LbFb9)v#u6_j7l(bEww-ep{dlT>yxV z*Bj6YxrTk{^{RY=%@y5*X_#yc`E2Qneos$wiS1=VhG^dBAnilCW5$|Dp{chpYen~z zHvUXtsAIpkWEsfzH-^D?5UDM<2qy$?^FZ7rUy~>Hu^76V1jratI!UT&T~`QBL8xr? zB|?4hjo-W;-C7+~B){#dVX2>#AP*~E)os_k00oC)dMJRA5RKg(Zmipju zs(D69IuV88rbE}@mgKX*Ty3R&3XGj{cpyl)WwQkHGO4EEF2poRrszg$M_-kSvK2~| zik%_}UxH$tB8eU?SZUUn>E`0rJT7H)&(OP>=Nk5wo;dMe=VY>IVLyZ*M{Oz_0JHtI z5iPHja2<%lc>(kJqXk?MDqpoBFuZk+dSQHV-*7~FESr#vzelz$2d54sJFFM9Aui(FIf%N~k)<(g9#9h+X29|Oz7P?mEe`{={ zWoG>fy}t72(b9ZPiCJlx|F_7CnU(e5eT_6;QCx|Pp$GAcB`vG?{b35XO#s9OGfpI>SvfZDK3M@CU&mk^A}v&v?o_B2S% z&6u!G%%j%yV}9si!d5`uxj0!v4%bE=h^x^@3!FMEcA9+uLE79<4sB3dwyOOM-&P5o zl&9P$JC~>9*bcb43PAvTNJySiAklT=#TX+z>LQcb4)g10UhLv4T@~b*iz2>JUn~5L zhyKW=47EYvn>U4RNLyhW4wpbd2CIvKpL@7F4_8dV3a(PpB#X;w(QpiQ$nMt%BG%zg z4!Q|^fjK$NU(%89f=EWNb^fbgS;w3!Ga2a}?zzg6BE?$!%h4b@ot)zMkv%q(zvlUW zC#(ODw#C2G(cgzLbt`=6#n`v0RTk%5k$ z;Xh1?YhU>vG5V8f)aTb{*X`NQJaSl^)W{SZql!_}`g+$b~^;(Azr z_k4XURCH2UoYqiyUz?PxwQ-(R81fc^4VzBNyr$1ki~^cx3HZ$W*+&OGc3DNp(UZj)qV`ibCAKXUzCQ)dBU18ZZUW5@fdJh)~H-KlSa-Yz?Xzt4{DX z;Q^op%hdzD^-AD+eJ67&iY2_lm)PUmIu!m!5K$6Z1Ai?jDM=s|Z(@#u%q#01Q^U5Z zBIQ6>ZD?!OUF#9HM662yqBe#o15Ei>I>=4`d1v@^K5RYCi5;u0!tE#EkJ1B56gB1* ze@o!C7m%_Ir|HLO9rZ9XRODUXdE4yos(=pc)4Nw6M$fmXsTg*JUFU+CC;KO(vH>ZWcSE(TN77z@K5dshag zC{SMcjN5-?c)+;ldl+LxGV3HDm32|1T<1U8wn1O^!R*+)F?ptLC&SYBxerBFS5|lK zqA{lELU0U+0A6j-s0J?dB0q7IJ-pLw5>H1>%bs=7szcGc!oPU+jA;!< z8NM#JE`u&9Eoa*GuFbBoe=YW@kWv_+5P%NRN#cmy8T=!DOGn-2Hy>0H*w&$`cfqN) zyPq{=?&=h#F=z|S*1l&QC@0jd=TA(*P9fqc;VWQR=Q-_ zWAkgd&s3i&GNfaOiecRB?5qlm^W68ZfMm|#Gyr{&5K|ZiI}$0w-4g6c ztYdr!wk!*@=vyfIfyv`n0Ue_pqZo!8`WEKD%MF#P`Ol~xK+i_c(9b$q0>y1iD&o~f z%D)y^(>(O-ZtwRf05fG{JY|eB(L88(2kP|iOz=wb3jFluCcomVtWs@~bmk`smCaJ* z_Hn^(3f{|qUc8n>V|RkMrP>M@gEnp0Caluss7-3mXpef&KQMJqQMh7v26cmS)3{zW zN4_O({YGt|*@tK@GuxvXaUm%9#&8RI0C`J1;e$-?H4wk1bcJgOwAy|3yW%6%&f3Y8 zizoOQ%2da45PmFbnSGf(j5-TxT<=L^TyKmzx+h)V{79qg#A1wp>iBSsag4Jl%52I# zu)@c|OUw?U>`~&tQoX;Un=^1juXoIAYxapZ3vKYqovKHDc<*YK$EMI4Kyn9{A-FS8 z{*8mnXYl8Mbj@Zz1PYLQIqg7js`ovn9{&@RV$jPlicPr9mgWINt^Yf&SK7EPr2J0e zk>c16WY@%<*0=Q^C|3aRpeYMrb9+in(8EW#S&v5G+yn?}w+RPW-nCXWr1q>MSsb@$ zrr*|_VIsNi2<3w^fKb}d4U&SUE&6}9Asun_ld*`OrZDytji!)|0?EMdiFc1EpwX2P_4^79wB) zk$hsdz&THR`aHVHkcV5bz7Qq)%98UW2W1WB1~iKV?1~>Tj%BUtjM0CabZc(ralxk0 zgrIK&ah?O*3K5=W><^Lwb#EOFb3(f0B@f%A(DYfz7VIbX`LLwA;Eym)Eyz?U5w(Tl z(Tm!_?~3vayZqL|iQEo!M-pmqhl|1OeLXqH5#?yz`E6xcv90mRY|s0?$Qw}=O8pL@ z<=Z>rf&~@3&j~~#om-g38IE<4)3ibVC6b?V&;oMhS4wLByQZvfEA+mADTCh}FSjcU z>1Z~-+wL&@*34dxh-Nbz)~&ZVAS|a>wH=p?SPpz?S&?WdV2dvklONEV7HPuE`wrxp zxwxC4A=Nr=8Z-7+Zp%=>%57zkTzR7=Gal41sBoL=tF`9&J+3xza77In8kzzg*~NZ~ zf#2U&B$}J@!6_?0s43@viQlV>Q;?Py)wAW>2gXz>IKGDHX9D$+$hQN5Ww? zmX;?e1L`r3vPnxcwMeoUy`1ItyS+FB^D$D~_?~B`imA_cCy}R9*|E4U92#r$hWl5|_U*N!9hYx;)~zb;i)*S=dnHS0S@)XE<&432VdkoH zbu>qtlU(=Z=hhXI5ryXgIE*Q46c|Zg80OSWE>|_99c!AQ8T|EQ=ry($zQ;9V+Kz!8 zQVWzzr2m*EDutCDA0znw;mMCKE?HGB;iJ;Wh-3+roC7qdpU#gZlVep?1VDri_@Xez zne;weo!UIkjD-g!1rk_3)C+67s=p--iysP+&wJ0fidMtT*vMslrF#Xf_U=P!I@v$Q z43L_M=^DopQZq7LbFTY97~pt6)l(c}V(i?%S#sJs*SCye=l@6bRi*mwMd9wtmiH;0 zI|j;Y+r9XryHb7gxw>)=mnvYv$G9l0)Vwf&-sQ{tNCEt7qFv$6YX;^5s=*k?Y#n{9 zm+3F%7%xj71@L>txhIpGJ_C6c7v(AZuN@ae0AN~zm98;qU~cK!uM36#mz~Cor%5lG zobOhi(^yOL;`&|Os51;lE{j;^rMwPRq%s`28;>aZe( zcN}K>*uEWuodeUKf4unIJ>Iddd;4J$>v0Q!HiKsE4dc5n+pFTc7cK8QEP*O|_u{^N z3}!k8X7%rlKwaNq1?V#^Wk@M0BsU7V0iFj~v5ygHd+%Wtp%Gfk(E@tEW*j40CQg@O zv}T+sl}XcO43u%DGL@E*KSC)w7WZ8U0~E3Ps|mMfFzC`6p9X=(FQA^i41S*Z5y-;= z^Wr|F;l`OC?PrlrAejRgxkX;{BGfa8#R;Uv*WoLXoRmIkooc;QI@8)S)r#66FP(); zZ6;(1S)y>kJ`SB#*om{M27Lxn&;;}k!&|q27V1PH%&Pr*uipP9>1)!jCCz4OvwfTN zux*#)sQ0M<4M~(OR=YpI3J$!@b9aEL6#1YRg(`%g*BA=BLhfLt$z){Ql};xTeXR?v z7+EE&V$E1ptHwHB{QW_rky&D$7alK8s59HLY8WeFJQ&^+J{e}iAs276F-u$qvzc5; zQ&)w+8zhNY!qYtrSU8*w zDV)_I34AE-tgk~B356?!aQ)JV(--flXDUJCxa6hpzf{=t+pSAKz;%V6pI&}bv?+8G zd;L}=72Q$z(XR^c{pgL=KHLTJz8lZ>!*@pWij(Yi_8E|mE@9{GWe7~DN}Eb1*QSy+ zaCn89sHoR7OQLigqR3DWNT=$pR%S`PlQe<*-|r_&1DP|bgDg)yZCjrcXew0KkY!f1 z(50px1-L{i(_m1w@KA&5_misystn`BH`D-WWH4}BWh~QDnYNInXh3L9aVS06HhCnO z%v*D5vQ)>?Qx) zMhf5n)WgZ1$upBlX(~w|ymczsDwU2;@_^xIrcULh$)UE%;i>#k(vl->lF9U5p1{;T z)__sIe-geG`CJqe=45C1!Wvssb7M2ZYek*N@SzH&g5k6JoPsQVn-5uS=AhAsE5b80 zIUi~k8x)*v&|7_y51T6B(!#g;5J5Vr6)*=b@Sm)zs=DKjJMb`QLPL0H809|q+`+a*zQ{|o#C_UTxQAOXdS$|wgo?w zS~z=Y^MWtlU~zxp+K$@Uw=9ipzq+C#)lyT}P`z|C5uBC0t8nzr8FsMGeWvKDMfS|2t{xLA=pn^IHySiWd5qgNOLFh)6RbSAU}PCKOvJxBuu1p0-Dl3 zkU34C0+~QqogKcj?@XB6eFOv z$+mP_nw>IM?@fW{1TBNH6m{|!_~=0L6D6fILy92tk3*zI zB9U#dlxQ6tX*o|uiqdJA)`?>!gd&W?tF~m-s;rnDFyrTG!K z8_V)z+2dJeEQ<%AU?R)(hQvU_6I+R#K*$M^52i{fVVZEj+**Ncemr>=e@s3Or5RS)&GKB- z&ANP8!^;|v4<|7}dO&z!nfW1<$3S-a`!Qfy3CUc5WsVRq(niWj2`*k(NQy{VJX(*t zzj=T6^@`J^ubowxVN>gMTzk*WoAsvJ?!pXvSFJhd@x{&9o@6Fp?q1q@TVczxpxYOT z#AD`#_~zS(zEJSzoqlg5GG{Gb{PB-nC3dio8+;0yk4K|=R@!f_C>QKc{bD6K+|Lm zP4^{A#Y(JH>XLj|2{z4@)n24hLQ{|5SR_|I@&4&2LG6F@GR^5crq* zQ|Vv+e+5!nbGMfX1~6G9^O9m=0$7$E5Nf z8_?i1oMjpe;@T03(qJO7O_SWqtawL~%mWYd!4+_Fu0W0Yy-5lLg-NW2> ztM;UYiS+3tqbNuTJy$%5^dM7owkiviE0wEt*DJRwyU}jtd%|Jil`4Hqw8ahW)_`rc z!yk9H`vU&iLFlIEQVzOm!4yuVf~-D>^dW;n#5Jp9z&YZ4#>obqqfW;8mqag5P_?qA zfux7#WciMoj;&?Aq-Sz!I1gl%`~mn*4#Si!KsP91MWuw&R!=O+iScMmOeiSH!KG4& zD!59M0xh+qm=L|B8U6rzL9`ksFx(HaPD!k>f>@J1M@!4XjY|{GT51?7|4`B9Cmkd6 zA3XW#zi(M+R%B1oh%MFTkkebOFPyI7Ti2xfI#-NczhYh2C7=EH$9PW9Q{R~Dk-~$Y zo%rS)pCvqW6u&ilFt>31t3Uk>K=gv*DRvP%hV01C-d=|2O3`U|AcGk|5SeIVqAG~V zkpb;kK^f==B$+|5Q2T9mXhpuV#bSXR(tD#80SS`82&9_yCYb}I9VolSH)#*Z{PYmP z2Ci1G2LJ+H1N0GqAOcnSe4c{!2@oodO#nERq1o>kL3_{`V#yFxP^;-6N^ca&OVkJf zVM$PgF#(kB0T9FY2%K;~_bqN8$C6hGFg1dSF#^tZdoTd=Bw3g(fIXQlBqk>*GMR#b z5|p2qmKlIyz*zG~^7&*P9V?8KfV=9pu9Wlc0W@HLlhwEt9|*m^oKyAXf@D;>j`UcpO}+|GdjC?T);>;E-*tRC2Kbdi#8_k!tIUvU_|;PHL-97hCx8%f?$=UQ`Qjj!-x@}G4Z6xi{CRjIJBL>6`NA6857|m zEHX(Z#l)UAVG|X4U@c7@fsXDHW`aW1Gc`oTo;)OzpfyRrUYNcyk#O$HoKQ*# zjo9Zc-FZuO8f)BbZajHbbIPi@Ts7k;BJtr8c=#x)zgMpVr8fMd0=5OiUH>`EsSr7Z;i9@Np-a4->&;}w=f!1bkY zJg&6I0`b{Mr?0Zu6$x|lh!$&ek^vj~S)eeoOSh88Dv$Y^U=^-HmPjxdRPcy0sxU~A zlrd#OIj(4wfy5K%EC8)NAoEDWr$DQ2BSO&R@MIo%2`0lTvl1ex18V>!xF0)+5bmIA zFW~^^C!SzA2C{2Jh;mTM@(OT3=KOT9|$R}7|d3YN+D z#%8Zaj2lRKJY)uypK6?DLSO4hy}4Ept%D zBZSiD$tfK&LO8ZUf5yn(;N$k;swgZ^Caz-cG&Gy&rx*^5>M^+Lp?vZmhmNwG;1TcCsUm z5zmNs#J9bAXN}Q}&5VxK8hD>B^=ie@u;^o*b}Q&I?u0kBOS?<=i1O9&S0j3B(pZ_A zpITVIvi_FDEvYY=o(w-z{{j2H&yWyn1Ly@NfP*+qMfl`C^g_)z_NbOBIpBW58}I}@ zSn?>aa-`Dzf|FEMSgnz;QO}vd`Pf$&|suq+mSX9sF-ENs!(Cp4MSy<+0R*bE$ zhu#hSEyRY#S-YwqG~)sDpn25Hn#XaY>W+KdH9--Jsoilr5Fd<>#91Yti8Jx%K$@t- z%HHm2;J($S~tK%b5n z^>(9CPgDvMs1*I6NPKi=GCz!^$upCsJk6GXXn&2OH8!ARf61y`nFuP9g%^UBkPq_- z(T9j~goH|s50|93JBU#XMy6^%6F!kXv3yp^f!;BUhKPYob??TznceJe{nw47j#1C3 zchtA5;-T>FYD2z1pTt85I1Dt?r^D&UJ*h_`kEAsDewdtUsZ`uKZKXShRb7sO(_3O8 z9>P1^x?BwurRb^68Ki)<&7_dac=0rF9h$gvkrE>gml6Xc2W`2OY%4v>T6&h*3a_xj zE3CPcVkLb}t7bDaGv`>z2=6hH2b@-|MtF}A+Q2DW==onwbQk~OlFhCkTf&rGIe<#J zTqU{{Cj=^NsVBlLsA4hD^~(l4!tR*SQ0(R_x|S-zmG{5)!luR7ha4_rDCB!=b?35G zh2K|KKk~W8o_dR9HL%YVUj6E|^Q)UH6E$w&a!#J?t`@ zWDp-ZL8d(nG|Y_rjG;`$e0Bp*sFQ(eTLvo68YDux3?7bfI#Nk8s*MzKv>E4$ih3!E zsO_tyX_d_5$Ic6-T2fEic}Qn3AtoNqPMMV5AQ6@XD+xCSj#~Ulmg52jLrJ&<0VOFB zQQR$~x^!Ac9QeD=gU%o0quL|>x3#?WPdYqT+v!~9xC`H_-EMx{D+JZLEJuaP z-9h|{aG6TlZ(_ z&)y4eC#A{$@`M1j8nNFRHN}{yFQ$v~F^k!*AV2mf*r|n_EI`g?loae`;o{J{6y(;x zr3_iqf7BigI5dQb+@@MKF`M{Xb+?*swcg^~ByaMGfM$S*fKXbWWXXBq1nKqkUOfd~ z5V3F_1xX5%0D_IK3Sup`Qm~B27+83!yZvX2ve(Vp{e@MZzh(~m%wyZ0 z`P|u&$M5;K&;RS@_O{1vd$sWUJ^y~@-T}fdZ!A2GA4abt7h0so`#+KgJewxzzd>!y|K_L9*pM2Njr zb1zCVVi^yj)zn&1trGaIramff5=$l?i>3soQXsaPh_%{EL_Cv~Dt;6jOgu48XhE*i ziIHf)g^)P`^xeD9$`gy)QC?yc#@dF z6k{;8`eJI)rNelugfS*s4)^a++0JCDPE8visPsr>yJ_^9d5^Nou`9;TW@j7by6=t)xACn%lj8$tT9<Pc-chi1%0E{Q+T|71dxkusiS~ z2o9D=)PJD%97NsdX$yzzy)iQSZP{_@XlxO`S+o&T{YD*LPs(n!c8XfUACfFc_;* zeIKz4rk)t7NJS!*vsW|A=eBPD(Wcqe&4Fy_2Ai#R-J7%K5*R!UFvx4xBPV)DwX5=g zyhlDMb4XTYW;6N{VodEed<||#TD%8UAeQDu$cLfxrvNM0ASaX{{14TH0SvVa)_{i0 zu;@AbPiQ_*wVF(3)soGaw}Qm6$IO}CuIHEtK2=^r;!U1BCAHEuvk>&kp>t9;0wl}Z zN~RWKE1bztM=?RT(|CT!)_O&O8EAIugs3Mvi~GqpKif9k9AKhR#$S6Y^ScMClt7T! z1y>cigp}q!Gz%@pC)8`7Ku@%P)XpL|@=0!ouh+fAx7jIR2~~c8K5Rd^^pj4mulEVZ z6VBsHIi***Ou2le%!QDGK_gkntw&ce>-^ggw*~D$pS81l#r9dV>Sv*am)Fi}XAr07 zJXH(Z>lv=Y3!3sQ;6O9+%=M@Pcfj4DS>~=7nkD$2XJ^6tz3iL=3+`wPbh*aa%hX1p ztEQn*clkQ5xwdx6Qhirdo9EvYFR^p8UeD58vw7agJZ9dLwicx#Q=wM$R&W*WB}>Qg z+s}p`y;2^>O?M@emjbPt1Y&im^huuhG6^D1y+?ePXF#)iUwW^tZBqI;Kb3!v3TH%Y zb`jB8I=W3VwNf~pF}u6z5=~~#+^)Hub6I{yOKS_mr(#-wTqSCWMq-swV{G;%^ERUH zrg?tEr#U|otMwbPGiV#fo1R6oALP|dhp^A<_DE4uruva74lU-kbZo@)n-}|(j55>ddG9gL0EYy%Ss zS8xn)5UyT9wt5DIDk4l)vZDGr&g!r;W&h>0aj%K`QN0Y9~|WqBc1_{Cof>;L#Ee(|%{anBFFeRlhT9_s`9X3x3t zg-35%(a|r3UheK0>YXts)iQFg(map-QDNxhEfFPkKc2hqX^pSD z?zOWn1uV7~|IV~>FCj0Un&u>a)e3r{pYjvEfjS=z4jb0kXx2uJ1Ge$vr__jGBQh4@ zB{m`kQS-P>FQ&{+J4aXu*+kNIZ0h*2^yHDUUEsuo^h)|-ozLx}zC$OyIxfhAjQaug z`FW-@Zpf(AqCKd`db1ZhuCe2Jc1&NVf_M+_)_bvrvKkF_Bx)!N(%4F_Q=X2L+7ux3 zpNB}D&F1%A;7Fv_z_zL5`TT@*OgfS;IS`Y?vg19Bj4*(9LvAHr$uMpHU6x($7acD; z$K4;eh24I<-Gdhz78+L?RvJH+HN5PQ<1*`X$Zijd$;Iy7gIPzWJX$uxFqk)F$)KID zJKlBt4aA76?B1Ut{W$(eO(_O!s7d?B{EQ!A%yF8Cz1N0EY#7-j+n8;_cHDN-#@hyb zPj9F0n3AuM9QiYOVA;eIgW9G}5p#qz300@C#g#*FT0s&acCw*i;2*>%XsLIEEfjW} z>M3By2oGtb9+&xVzFA)xnrVrLM`qXbReh!DrfOG$d#UiNuBjjN&rDRVUQ@qv4YNMv zyk>6fD#9BWkkqHx2T+vBl%dKQr~Y%XEQ;urO6oH?XJb+VWxvTO)kZxg9@@}j^^tbg zX>hZa3_~i=B#|Plxm=qx`B4cD zk0sO7{*?=CQ_F;s;gS_~dPOgrKumzAmL93cB9&isRF)~pHHwG&RXo&e<{*G;jZ~2gg*~e^9xKBhQ5wiaqQ3THQ~-iuw*e4R<5t^=l2tm3 zsQ1K|LRm5m?IH&aC^*NLfk{YSpV{IwdwfW;cqP9NQ71KVpWr+N#Pi;fX*g_% z#e{6A44rTn&$4UGA!jgdD*Uy2^KG3yLn&X=T-@H@mb{@mx14=o>esuePvqs1S^f8p z;9c!?UL2izWTdxo0V7=6#6$r`zfwGfH=vi19wk*DQh7bAYSoskR&C3!)Z*RRXS9s= zF2l9A62v0f36bJ!Gtp_I9SBz#rPcPDn)ddW>9r=Ug3M6-jA*GKGdNKM|FQ)AdjlGx zL`no+HFg7%sq;_BjT?WK?ok$Adb0et@YDP88fM!JVjE2?x#o$Cpju=3@?lle?M~&PFT*iznGD=3p zn1Sqs@wibi5^r8>);JJ-x$HAI2Zsa_hbQSshlxAyyc@65t+09_rmz78dvvl7xn;e< zC&FEj3n|#G_xg~Z_fn@G4Vt2I1Hk4GiI2+T;d1bV!!DHKlFs||EYoHXqSN6ScYXcK zzx>ucPxn5))U3$9DigL<*WZv^@r`e+&1Nf^PY(b67iYdU($c~nc$7NvrYfg?S6Bb) zi(@Z%?Ir6z`yT*9jm*w6{q#3e`YGuwI!YLIG7T(}X<(5&O&(p^O0(%iY0wsHC8xtyABAhwG>B$ zD&dTy27pa-mcnMX5ydV36h|O4ohFb;LyIo<3lO(Jophu`f{AVRA_qZVgOxyEmR{kF z@dkJ(d88$-qoAszYNL(hSLH6h2O0y1YQ6|Sys zZu;Mb5_#2hvs#taH4`Fij+$d5&7;lSSo1{l@n$y3UEQ2u`(I%N0wG$J4M#(WyL z4-2A4@J|O15harZ3gY|_jUBQy{*Tc^M86a$`7XIJF7#sbZ1_U^rGs9ZNtaOzGaYK3 zjt$Pv)LuK^k?Sh7ToSg+=Ag%sHesvgzNyu>b}qd_{a)dR%K&#s43Tsxp8epJ>4t>` z-<37NNW`XVUdmol@_!R~BL(>KH0YuMyk7l+AnS83*>_2Utir`jF6MyKnGjlqdBRfy zuPQ6J<>Ct0a``%Oqh+J@QT;beyDZ<=f8TUebJX>!{I=_D`K0nM?q4p41N%9*#_Mo9 z-7dc@XkB_)?{9F=ac_6+R0P@0FfNbV;O332n*m{usM>;!GmgVBv|80}Xd`Z=akd_C zQsdd_#=G6mxEc3zY&|HrUw#%dhQK)fvT8*99~atI+BVp>+Bi^p1l2~O)jdd2l@Wy< zQ1*cS=zbpm3jq&(_nc}4H7-8{iX0UK zNoWIM&TUgeB@{cfQ;T2JzOH3J^XX5XDr<7oRmDhU8~W|;FT0_>-_*KI(%gQ;bc96a zfV7y$R20T+D2ouKcf1g8oTiEbBZP?h!#=(8>`A-|E05i{c6Ti5e*KZhPh{pl@voV9 z_4UiTJXllsEQ)90ho8FRiA_U?UwLD6-MVibD4cGVYO6u-o?rZcJp|uVLJ{=bzEvU! zDf~W-#z7LJ5p+beYSnsBOpP&WY#_ELb~47rETq`95^X?R(N44nX^=bm91fgcmC~B@ zOrE!2F2Ip+1av!|!7OHYA?ow`y#X)F+hXRZJ|??eZiWwWmenZ8dsbt+335&YjCj3}A^nECf^Tt4vVZI9gj ztJN>xal?_$+)(4jKuso+OSH_+&TU{G`v5PzynXkN3(tI1IQX^qUixR@gS}r{HT->? z`{0qAGNDTr6&{5Z1C|9+$9kj#-til?(rLtI)M-@BtZK$p2J8@EdYIK}c+45}M#LG6 z91m1+9Q#zOAld~%6j@H-4I&B}vGIBQDB_VG?^cZ(%xguS7d5~*xaaXafI$&ot?IQ} zGmCe#&#(+Tj{m93xQ&7=@eu7XpER>(UKOz5HeCRhL#-4hTLCB``@TeIQX7c7R7{kn zhJh|wC=hO|NpiPKM@ZIeHiM!vOvukL1`Q=_31x9TB$z#X@bRganN2r7UWni`_Z7a5 zuO4B)aQ0s2n^QD~H-Qj$NV5P|W=!=;NWuzIuv)Q`--5BYqFhke4E)gen9ZncgR=kMz`PBVi+z9E=PR5R6oEATujT4)V1AMoJEqgpEk} z0%@V-WQMVyIM#No6y{$R8r-81JP;X-j7IiEPDeCKq&LE-n@hC8IQm z(!|zxK8&GcTgwlXgyIXdBHK9$pt13s(G?_sMAeKLRaLDsKJTt=FLZR&c(p>nmga!vds>&^RbNXfZW6wL(Io)2W`J4;{>n7Wy5N&hn6(>tER@X( zG|mOnI2UXnLS9+B`jE=EhH0=g5a6ZC)p+su2dKWKc>x4jk;E7y8vv-@c23h0$j409 zopXSczVi{{1GzwEjoT>X*{NxYC|U>!Wg9`s>%o<*2Gf9KD2doKQvxJ|0rU9mc23@1BJVcr&{^IXF{kcEqZkSVOYpd~?g0iR5f-Rc+K5Jjv?5E$BRUesClHHi%nnD-eso4n> zDS?lawHjhY{AL4uoS_7gQlJ8fyyP7@U^@gvzvw5LS3bfnlA94{7V7sz}x9pUu5 z>6oXtR4om4LVO}lXpy0k?vbcWIK4lYPL^8kq%EQ ztfJ5;-|^zds*xI<_EjZ+5h5Qkc`0CVSvMw3=TfQjs@d=NoB_<1CiB$5RD$6#{+M?^ zr6rTY{ltD#=El}!*|AbG-}nM1vp?!H2YonT0(}{k0w_u$uV^U$Ux~p58+)Af|Ai)0 zCRpFuOwJk-xVq<+Eb?0qP|T z)Js`=V{nD$yW;%DHZO9rjiLEoNBcU4et56v>W%jM?mX%f%zrKbXv9twL+DS z7u=WO%Tg(G&yUy)NH_tw=1>otQG-$jZ^-P-?9Q+mnZ)^)cJ^elxf*t4FM)logp3>WV$E=9(3xt50ueSYg`vU>EM9d-J+#6(=@32c54wStkklqA`n30|`(r z`3syxLALE`!)>)0wwGmkS&UeTh^VlXZhr$!;i>Gjq(U->$Q;>gmaljY--1F|w|6@r z%_juu0c$3Qr)Xv}Iebb=4t+vzlEWklH8~`mBB(V?4DF@h)T#GLsA`)8sC6cp&qG4m zL*sl$-+mHG#pL^w!)GSR1_x3SeK!gv!$blu>tN^fu<4zEw{|aU>5OE3ewU0jv1naw zeQiT6%eTiC#%iKfv8B<)KJ1$j@S*N(kAi05HU(XxY4f4p>K-4uJh@oGv*j)yUJ_sC z!%LU>TfETRI|D7Kov+~T`PoL5=}>^f&g5Es_|o)cKC~!tnSwf99X@IVD{UM+f4MMj zgJ?N-&_4`%A#ocOOwTyw>H> zY52K=BM0rVoZX@b9IMe92QMF5bN{lsMp=%|(yj^C2E$h}+cw|&*s@u}x9(m(>+Bu% zebG#0=GM6lPAA91npzOzGeDP?vFCOX_ERffKaJ0Dm4kDaEXYh_LxzYd1gJ@gY9;Za5)sv0SE*g~ThDygz+S#5_xin@VMDMK zI^j&inJIp7z$3e5S#B=<^?OxcgK)h6Z%%dugtvZT#Y`IUXg^j&B@vLvnX~&Vni7Fp z$f){?1&P3%`7~~NoHgwaCli6pIBVP=ZchZdAfwI5NhMw3ekK?s8df~kN{|vEr zo+>4+mlA0Qwb3m;ZS=oqk_b2D2-9;hBj;_Q6%m8k7!AiFhL8`N%oV2Sg|^UPL_$7E zzzt9*Y6|^t45H-_ZW?hT2(I%I^0{K+0!!&dGEqJ5U2CmgU%&J=$GZEv=M9CNMqT42 zg;v{)kW0sT<4d#GEnpZ&OIM+GK~Ap;r4}}37gf8-Hq5p<50#us`!GLRYmQZ|U3p7) z_mY;|3Y(WI&R`_sqH*Cn25Z#pTz#_8O|7Ysh~;u9t5yA}rh;R6qc;-q&RBx4d?*zv z>tB}QDRz>504dB%B}#Tst3@6wPj_Zo;8fsb*gx(C38Rge``gmM#?t3KP_?p7>lL-y ziV*bE+vz4@yN$P0QGBxU3@u?Wuh5J_o^vdD{=dW&(i037R4Ho6}RGP(dKk5)|{Mmw|M4@^R%;dopPspp643zQE`{{ z8=kLwcUL@xo)jO~e#`wW&y(ICi3hX?bqD1`?&mzudnYP>Eq|i>ME=ZEy<3YbsHQ&9 zK+|MxDGek_>6|&GG#)Rd;czLnSZJ!MZeN4B;x;slhnYdmZOR>*yDU2^v@K$Tu0hUu zU*RW0Z+V2fb=&1_ZnnufS7vOoJzzs#C4j6tOTao_{F0i|dfbZac4xFYyH>07dOQ)W z2s!$5RGi2Ju+3`481Wvr9x%hNT370@q>Jcw>kjJP&}nqHYrRCtkW@asM?5V4Tx7-D zweC$G^1}=Y(!zMn)&^}^Tq5bdx-3Z!8L~(_p=Gq=_{D=#1sCr=|QutAw+@@ z+#vA`4WFSqI6YJH`)=t>UOwZQB4+OlXUmC-E$4+3m!datX+KWLQ3_iDfLZ7$KC#=(K){LN$(0=camE+1u^qxlBgy-O;@)F<5@PItv+bj?J zc3AJTjao+KC-8TeXTs0om+()7pSb@lp7MRDoWU~B%(pJH-V?k>83~^b3l;_cuy_(F za0X$;kspzb0~slV2SOtu28E=M66y_+d5wnlgvLS>q2r;Gq0=E_=xYDFW^De6GpZH* z1b6K@lBmtr986Ll`dQF`7aDdN7(-g3cGm$kh(^&Enm{L!mJ~4PdpCLR^f0|1yxW64 zA9a*em7A z=6~P|L%6)t3r+9viO0&uei|GG%z>RBee2FgKfrkZwjb1{W&|wyaCqk0OD_B7_SKg* zHQ*}_{5$5~eG8j*_QcXL$L3&Q{_1aj=d+F)^2?P5(00!NEv{oK%8&*4pR8ihEpZ(8 z15Kv3TRX^22Q4&fAz6lI8IrA|*}Cy*a-Ez^egsEb`dFUE5S*vl0c{fb?H1dS%d2&AqfAcCC8A3r`(@{*28Acwqwyv0wEyLcLTKnu`GW#Qv# zEzu54$*(0CTSJX=ZR4GqyLd*c)mTNh=+P$ao|rabjd)_oX547adgoZzYuD?pabN9O z<6WP+Mcks>;=b9l(R)j3yKcMtVf3)}A<2Zpl>-Ye79{kxfGgEe7; zWB}ZqTl|CO7R`0YfV^n0TKkaoN#QBoQxbPG-Xd(ncMDvHXskr6qmtLktwA=;GDLw_ z*bJ+(8g`zank?J0ieL3JeoL#QRkVy*8wAEQ&$o6>Il8)MvPT+9ep33CscBzd9rhBR zo+oCDnGBX{%Q=r8`MU$y5LU=fONGd_sgeBZ zm4!O=tQILAVAOj^aRowLlKj>*O5s}$qZ*j%S6i~_noaUX??&Hk zm4h{3^9ftzLy_kyf9w6N@0|$mj!QL_v0OA4pHZ2qSsuS8K3FqSqkjcqk1yft_Wj2F zTd(HHO8nEv+pc#aZ^z%N{7Z!QsbPPmXbO78Pz4Tpg;3ZW^g2RerxS!Y~sK z3y!KvP{tWi5Ut1~c`_c=Gw9KH=GByc6%5s2wPvh_*=!<4&W%H42< z*;M+eoYF=E>IpMFR$ViWZ+J8Ul@QY#d7H?6dKt(7Rp21ZFlo{{|g9uZT z21?#Rx9ksN8sw0?FpmBL2V9-AY}AZT$N3O~j=$rJ^)yU9U%xbJ_r`nb@!t>ra`d;a z)(*F4FZZv1Xzmvm*Y`4?D{LAGrlQg2;70a(lIz}g>l4RKb9B0Ij`Tg$Z3CJPU!MO5 zeEAGMs(xUWu?dMTliOHnPME8>j9|S4Uy|;ZH{kX14e2fNL-_0I*W`EP5Aa`Pqfy2> z7oX|Mu#IwKW{%7{GjTbVVR>1Tak;2tzjM7UAFrEqeJFqEK3Vq(`cKzCGts$to~tXp9QV7H zrmuC~;(kT`apq0=&CGl9dl?gekTz7I1ihY6MNQCK8LD6ifQ6-?*BJ_jg5G#2?2_dQ zgzZRnBMb^D`8|@EnY11B!c1CD<1~yN2&~)100~AYlZjV~nH7K-?sQFsqFf+H@S(>5 zo$D||9$}QsirK;}F+Wzj>USj%EBRSH04CyUo@#9c7F8e$pfvsSBybS&N{K7|ff5mj zE6K>yY9OHc5d5Jby6ecBmh6T$T)Id(S+?Y42{cDh&bh{m#}Bx2u8cib3X7m;KL)uW z^xr@dA-@^dRfHji}t3cCNj&y z&Z&>NO=maX9;}K+8Xmi6&1;0>%8&HzC*Pg^OACjGtReSWV`Zspm6`W3o{fOl2}mXYHbx+o)5L znP@axtK7<7zb@pq$~1rW0h%W(G!y9V7+s;it1Zj%a%Q=IdEh#H9dn)kx`3DtwS^Xj z9@aeMeNyv;S75L|;3Sa&75}%rZ;x-{x)Q#3W~3QuG}1`2Ey=PpBkPT1Nq$?lgJrNu zz z>sOKuZNnyPX-nFsiOqNJm1Imp_VssVpL@ ztU4S)DVwpuVuIk>&=JI;X(p5^6m+z8)X|1eM_Wf-v8_Ofxak;!_&{#i-Q_;y=G-Tk z5GuIxZf2bx?%h&R+0cFMN<_B3 zu2{crFm}OzA@pbepF;*BFlnu%a^H6<6`oiu+*{>xK)vzGF-{+F1)_mu;1=6cwx{e* z1q9Jw?W+#9qUCr6zf`!4+O?z zwmrK0ZF>T&r0c+U%6FPM7!SkT!|&H)9>&A6&Fq#{ug9&TaMX=VdZXK+5)5IFuI-hoF4;g9$;GCkQ%wzTJ`U)BE%EdcW7_OV}NGcDo}S z@H*@^q7+*X@8KkV7pcgN-chuA z4)4sa-Nl(7>Y-`5o13?JgN#u5-kl^S;-~vMfZ0&d;OV{0zR#eWSNhuQ0XYacOeJax z5OHC&A?OV%E-;;;aJR-DgR7`R`v%g^4Hdh}?u6c)-mDX0Y^f8QjcwvfSU4eQW>b3I zEA2!b{eV@1`u>fd-n$~Ni~-r2)P+RnhC)26RAUvP6EkQ`t((v0M==i}8-?NYdC0HE zpBl?~UHYav?D1L`s(@UA@8fKIn zB3T$@t8E>)33cQcZ?>yQXd}ZNBtX)&BTa*35bE@gMxcuR8ez~a^ei;+#Y%EKX+p9? zc;M8nl`~K3{A=Dy3cI@gs1#TObi>{!;;&=}tWZ)JO*ijif9@=I+5~7L93Dv^G=fP9 z!-WJoB}bXoGbrt8bUq1m?RA@(Y3TjomE zqx4O2OFN(wlIM6u`AxBc9Y*cTQ`maO*XrUoxSmF6~w^6#*++63%Prt^Q5&n^hqm zvR34{uA$Um5F{sX>_MY*m}WnYw3Do~AT*yOdr0yVITLPJk6fG+A|hg&qboP${yIgd z|Ld<#puDtbbcB0F>T!gWLgBBf5>+YsUx5*ThQeLC2<|$Y`)dOwH&diaDJ8{!Yf8vb z$R(7CC5NS+(;BKvbV{(Kz$*@8=A=plPuKhHoGXQo>jiH?iV6ik(vroM=WoqCgf>z-A0Zt?}nHup_ zOm}K;Cul&G@GVG`v8C5Ym)r>!09EwKz_0*@8n%YRX1vOQD_mvjW$Ww^H#2rX#cQDT zF6q-VUFS^?Hc4IHgDYfdXPPPkjeQ*?Q+?AARzEL9xB(X#t4tMf$ttW$JD^JyUH0SZR#d)C-js zH%UIh_&wo8lYa~}ECXYaJtlHM^qO2y9;R`5m@WR&)XyAgM0;_e}U8YiU zmb$p{NzdYzB%0G9QPfZn9>Qb^?SwWY+%bTAk5$Rp#LWW<0!-?N1Yv-i-~|nfwvSd} zxL{SYl%1-_LzEYxR}^>6PT6s{%fLuWF`vYv^jlf3wze{4VU|f>^$(S~3KLe{L-v$k zakg`r5NJjG5_&L20wNMo;zB&gj2Elp(HdZGN3auXWgWF3X06wQi}55I1zn0=%5!G> zdr=$hn*1_S7mH`d>(fbH-FPRJXP$j}4}NED5|YdL7#AfNWt%ciEVMPbb^ULH*8ML- zs@doR5dk@?nV3v;DdIs#hIXi=WO{CiqybxMDwT)#(ZJ=lukhT(o+8GFx{Z(}-+CyVnVpH2t*x8crDA)t+0-Gyr0;}ZFZtR?~%3DcYqz@E8A^hFi zgK=f-v|jRMm5Mgy=t+vTDC1hQ{hJIEl>$bs1`|~mW$^0PhGm=QeW)CV^|P%IHaqcZsX-rW#}l%S zKj2sIw||F+?qoQ~L?w$@yz*zmsjxK!6@io*giup49R z2~b&zl|`AZ&Z;rz^`p_5`x?R%9I&>+pZA4csF(Wn%l6PQkJpxyva?Q!pHKD38rS+#k69p^Fx#{_GPP1(A9L%z9t4PsMfg_3hXSE0HZn#?kcT4<@O zo9J5X;J26q5GkAL1;O7b_iJXDvIro*32 zGVNfcUH14^fKpyCqujoqPYxBm-9MJF8f_ZC7&fJ0OFlQ&1Fmf2FA6#pAPkY9on&m5 zTtAVIAa<~z zgXJ~YAu`4u<4|CE0F~W?^0C4~%t*;-$wo;GoxOIqsYC=dG-PPowYV#N?Qe4tp(ojO zVjI*p30{QSX5j4~SOt`^`QQxUu1bkg>4}E`8TXu~bPBQ=3a|U7j#wBEf5O@~CXH45 z0s-~(zbF~stG4G}H$to7g;8~XP?MUz?~XblcR7M#QP1zt%Q#popYkp?_D|>VbbkA< z0Xi%#f^oV|!`8;r1?xk$y6RgEUiU3xw!HSTWfu0sw|stI1u@UnbnDnIer5-amwDd` zB|;HJ6BU?K`LEc&P326y!o&%5jGg6`N}Q~mOg|GcdkKztpu;inpGP2*5Gurv47FA0 zOLs0(*#CI$7MQ7VGk6};sPen+7`8Vu)o)m2s>`CdMN`|3B$F65_JcmO2>OMK3d6M_ zHhTanLw-h;MCMY|{1yciQ&mD-jhn?F_DoOtY%O}b@Rfer+kkOqE319tUG1=_9W&D< z83>yb799rU(ed3#R!)o-p)z{$Q-3b^?P=S-Nk8>}Wf%uM)OVDumE%kd(Q!;`m%@mj zKWe%|FY+4pQ7{;CdB)T;(fc8G(WcX8`dJ}jPQX5am5ADi(=x!GipdCOYv5PAoF=iP$9?PMG$wzsz0D35BjJnHly!f z-pKY=PAMlBy^%xmq<+QfN9~ryhbpx+F^?cF&iL{zV5TaCuhI{!;TmP#AH8bWJO)=X za~U48*{5HhJKO$v;Ho>Lg%ZuC`lm8e9y>x+EwtvP;_ld0x2ZmHbwax!BS!&wSq)KfumxIAZHBWsSG@pW7plr_hMsp%bKZ^ zQ3zAk5_c*dK=s`D%V?)HE#BWq^^g6GK|7E;)75KBFH>Auk;uHm{i}Zg&CH+JVpk$p zSCH(w%^81(yh2Z?FL_@L;PBZtpu>33imGI&Z%&(jKhnx=ls~`PcQ2~Owoc?;me(8Q zO;fLNvo%J+ZJjWCjaH;Bs%@Dro3|7)c7CwW4P317=aH+}iL;(%K1vOy8eX~Jz=c`$ zS4763)T(tV+>-IcQQ8;TPQ0_#n|TlWkQvtgo${D|{kFTkl?u6G{<=^mCoZx$ z`dC;6k(F*l8CH+c_J=OL?K7%TCf41}{5dRB;{TL_|5YN>r!^Qf-u`0Vw@BaCx& z?@Ex=DbLgDa`rt}z2ju?>b7rsy1TKJ?8@O+CZEYY-O7iET>;W-zhv{{j?A;Iw>4F( z^>tBeYmMK*?Ht;5X4~^=X+9ODIcC$|K5#V+OO1SymfyNhwboEBafc07%|~Q)5TY_e zWI=1(DTq>_!AN^Trm%?!_iVhmj5EpAl8>m{ihbJ+WEm|!Ix!IyS{-Iwzd)MRViJ7s zDEENp3cqtS?v~$ex^F~~h_yhgM&0QG`Y3WeLAz z3~8bC12K^Ra8mE0Qf`l)99bGuPb!^WGTrrx09&X21^vAkI>*2azLRkJ$u<6Qunk9u zp)gN#wKzTdXQ7TGfeyiSKUmgQNC+YQ(SlM^S2fOx6tkysO-0f?#Or7eNqlJx~&qQPXYYLg)ijUHMH9X7FFcunO2{^mU^lq5jg(mw`hzjI4e?qPE zV*}3jO@)gaHBa5+xfdPVP=#)==pW4>gmWtwEwW3dE2eA6i;9GR4q^`KG#Tl!@XOu^Ae;Y-DO6_edB?-1Eyot{uQ<{B?ze zigcJThpt_*$2b9zW07}~Jtler8_|;~^w#9`ZLVBv8!I@^WnD(PLmksmU2uHPUWY%s z7{Y=MZQ|EEs2oZ}2i|eGuPYypRaD<#LHqOzH{#WFZT*++mNBdk1$u<4BET;^m*o4@ zG9&F1fb0lyiE8^|s7;wTKoIvO?Ij}CFXx1!=Dn$5X;dcoVq%nj19OQxna5Kp#X=5a z#Y(}&(vLtg4oj3mr4ZrTWmH9jmetdTj5)kY9IcZ_SZ1Io<>FmTQWqm^Dk7pY=yhhl z`}3P!1*0pB(<=E87N3w+>}6ieWYm+7fMx_A`coKm7R{5wWKvEQ@^w_6kPZWk9vZaRET|VMaW@$QpHD*+z_2A@ z1(5ol^EU3A_qWI|y?uVP7^g8Ii?}>4CvY9#ny@faKKIEm+}{#pJA-2VxV7ptD~?JI z0BR|3l6I>;-Jhig)>;t*nSZ`_OA`}U{cbsc&*i=c8nW#4@ov#pFH9V7m#TR}UTJ&s zn$h5SSh%_>lOJF2qFJ?$GniU4F9n8<=MUPiI`4M*Sr$uQP<#V%_!-x78*UP1pd=UP zV&*g@#vPI9q#O5&b?IaqACu@U!PlY1m(EHoj?gH8J#}k#^5me6fQYgO>H3882725j ze!;?;a>kKK+MSz;GxTJC&R~5`mnW_W@EHrVtp5JZlakAiU*7EdmcqhAcBSL6p8DKV zS*@k3Hw?%9AmS&nH~U!&`{1(O!sJw&f#dJWjORy$(h8BC!!?4F42Rj%C*$-X@Dm$T zq-ddnrDsOyk5~@V@cnt zxt>)>x=I+v6dSaq)-)3a9k{m5+m~c@15eYT{k{{%kJu>etRg|m=!tX^*PPNduYTa; zO^3KAO{D2}QGqzwRq^A7`=PZYcyp$VzrbRpB!9Z%;1LzR(W+jjdi;T)NASmKl!t`v zjNr&8on~S-PhPxTPda>YM536?vMQ1%uK%O5A-qoTqpH_keONX~ttneL-dFM}`}_Wt zXJrz!g$A<|K<$NH{V1X9#MjU;t-bqr>f{^WM?KqbTLPoh=}F$7Q(20{g>@H_96dj_ zS?wx37oAJf8N_9+x~rHv&DqSA!l$!L)k=T4jnOt#Ox%`JmT6nvk>@(VpTBm`?-E7E z=p%?PB#kv)<`6*V3Ja)T-63uvX85JVeS+kaw8cN%0<`)ldr){Wi#!tkRrGruA6`s1 zJP63>EYygVya_BScwu%e`~BIa!YNyp7v88BnH}DV`HJygGy-}R_yv3g++{1zRF1Ph z9PBXJ=8D%Nhv4323pn5fGs6I?n7DJG6FCpIdJ&L-ZQqYswj@5C*M?%&yB{Mg_ihI}?!`7%8zBclC5w=U6xMAKWNa5eKQB~1X+0)ooZ*j6~_=5!wZJNU) zxI7e9OdQ#>OG}WogVHKZvwtz{o>qijhvP)lBe;C`?)8hp(&|gSoEc%w$c13yJ;N3((RghicJcdX%-4BS`xjJrCVcg($;!pRaxo!(^;Ls zQ?T8u$|&LW6?>;r$a8%<$5a&*71h*15ks#ci!INQzQRoXLQlkWSdQG(MlZzED!zF7 z2xj2z{Vr*isoLWllHLbDf=%(_IVt7%4q_0lGi*eyj=NW5W}AB)ZII9`y2Cmzg3Kwx z;sXd2eBej<*#PZ0?qL$u(au1y{Kl=Ue@K=l7v0@nR@-gh17TfG=ai_sDb6% zTjkM>&j4-p6V*KZ-GWd>nfxaao{*6623?TZq9Z-fC`0SU`=$kVBtC*hAPu_VI~R9& zWj_F_!2GZPxCp>C14wuM;5K;Zz1uMob;D?xdiLR*!dTE=RQ`y(J%*;0Y-3@`_*r>Q zcdp*WKUQ-q`C9Z@wG=&>JmGQJQgL5-|2}z_%r#3>dt!OPgBK3komrb%-wh)&lZ+j_ z-;Y8n8>3C@QMpxLTwjD`VO#L#@o>A-Vn!RE^}HKJy)#ih5!g3}71ZQ=hMSAGYwOj& z)`BqEv@0W(a@v-$p?)+GhBvlAg)N{nwt#D|k1t^vi3QfsjEWouM3%faC)-Ld0ZkYF zB$hg#`Fx+fbA^W|7l?& z^V-w`chSN(u>{n3IkT||j%F#eu_^dx;SJw-q8=eFfKS#UQHb$dN79PoTaSEF=_@N4 zmHd-S@?0Djid{+w7s}1pVnoQmE;W?(k zz?1i*@CdJ{DK|%9(vK*yR_rK=MefH?5|eCO!u*;Tr|>6bPBouKk+0;wd_pIAs%-b7 zz!~t+Z0SBO2}jwz!ZnnZE>p(o#%I0dEI|Jf;OWI$y^;-Vrs(2EyFX6}Tr)aT+u9o)EhsGufA*88#0XR{*#Yo8L#(A96n4+BH70mW7FKfd{T51}LUTS-Wuu z_zpCH>abrU^DqErYOG5HZ^pz6%r&_5wRm0Jvx@u~ef%qi;?=lGLx(hxkWb&PUy zmho@(WODNx8zNFxe95vYsgW{H4mxx?>wKtBetc;)p<7(fxnbrNI0Y2f8B`R@es9tj zcyel+o!t0xi5{tln0hrZi8oBsYtY$Tr%7cNGHqW;rjCE!Gqo>^>{xSHnB-Cl#QIpp zWl~CAkF`f9gN2B}`Sddn- z!x;QdsTXd>)STUBO2(D7!-ez{_U%S@0YW>QY4H?!7aO?3Q|(wxD7f0{w%HVND?gpg z`ykIdpdYp(6P=fT&iPz#Ew9dEdwzbXAUQirxVi-P>F7O||F-w^Qr4N@W=h0WOml;O ze6qn)kV-l7q+@nxc*C|&kd~wII7*xd76mheic*wDgkg|SkEKF={d6y5D#8KeHQg=XK95Kel$dL}F)uKU~?B-fHoV07fn{?T$CJDVF;;M>YXW?=sO_ zgxd0cjY-nG&1p|8oL9R8`K5?FZ2C6jozJf}!#e*3?$bGT!)|W8maT_Q4juA-;$9ok zwRmV880HN3Wr-{3P=vbHwqB zIPctWS_YN9Vw{c)jb;oOl)2^pnI34hr*;1tl^?$1>SJVAU%g z(x^CJ=;B)l830DIo-gkXbPO)n@EdaDJHZDQH^$w|Mv*a1z<=9FPY|`Zj;>+2fp)kw z`iOCXeS$Zfo)h*+!=DX+7SNu2k~d@s;0X{La3>y88WbOHMw0!a@uD$)YQR_rY=WI1 zQy&iiTti&d;0mAgo!RSWF`*}q#0%e_Z-7_&b{P;2=nL~^)4vDGH4xtPG7gaR3cTTi zE~pFOCavKms@DzZ3upz>m)UB2G*;1%NW(iEC)b(Eu?TBJ#UBI!dVz?a2T0=?f&x+B z@X2l!5OZFk*Tlcogx+}hN+K?;yJ%C1Rcw`DWNtJ5v4h~)RcPvx6wk=$dTMkdCxOD? z8Z4tj;K(+<9m|oV4^GH?z}N*?8iOBHpI|%e=XFXkZxD|M=F#5+Kk*3=79P`ue8&J& zu{OTNi=mhwZ<8TeI)*9RkVR0bba6xxR%yMhk|iq#MW}CtniX!1=W>}fhXK)Q_j@L# z58p+sDB)7T4UnE3UK)=dlph&j_(}S0Zw%6vWQ{aGSpmanXobO6dVnOe?(mmZ=o$!! z$XtJFz9#|%mCj?EXOXQuTaD3Z&UG+tiPgNt+y#0^oXIH9Em%v+r6G%PQc^7qSqD3t zrPcUzRM;ToZIip|g0#7u)wJdIjP9HzqbK276IOH3n!}~Uj4E`w)ZDf0yUJ-qyG*B%F)v^MN!WCbR^cTdBLCDsdWDP}t-o%^z0=R&a^dzP=y`t) zy$`UfY|p5tQi2T_);oK2eCQO^3}~-qJ(UTa4ro`lE^cF5@=UO}V|~CDgbrwLU_B2D z-40~$seDL^b4*-n`5M1g9M;ku|4A)Ni=#2C9EA)V6)%k5Gvw}Jcoj;n_0PB;5Cm?`Wpz3) zc&iQ@^5Z!JLI;`2UtVpV;iw`Z2&w>p0s3*DmB*XbRGo2dc)w|e>H67Z<;>drK%f&d zYEuE*WDMYBKuA8BNje25Iu@+*Qz?uz2|sO8AFB-KZ$=6T5ahtyykNY7QZf=&Njhf9 z>KMf~g}@o>ksxByyi85QJ-71q#LQXcTrtmWqR&2}&nY6GzeJZe;%x5WJ%r^QNJ(Nc z3F$A;3)-TS{F2JWYA6=fgep{uMZtK(2wa-NGHfh4gM&SOeGA5df0ACF>1R^Wxi#=q zOEH8IyBRgMAN#Oh)Kp98MZ(O z-~qZB_S-!eCB0b^AWCodBwL#eIsL5gpuhA z3?oA%uDcK-pFqDo4&@%nMuUo;>n!dZ4y)64UzhwJw{PY~%~Je$WgD!1jmrs!32a;;Nzo|+$n2egHyu@c# z*rnJcv!}tbkaUibplS2Z75Px!Ilwx|eJ``<<$1n8(o$ItGOD8**8WQwo->m7a~a;n zNiPj@nV0-HsmoqmydF+0Vfh%(@q4x)y433jwc{&CN zgR&d~VwT`vVHlKs3d)x3NC5^KTU$;|?QBF(P&yJpd2ihUK`lmuS9~al${2hEqm2=< zfjb6ULa;*u%=gYotgcBP04y@zMyE<2 zL+_;DxR;-xKOuiBy+blQT98*)SuY{2wHAfT>&3X2SM9M*@D5i`tT-Y(lb7eQPsMMU zq#a6|lUrA%u@z*)sIA(_%UkVjsBt=Ebqq)=hDggvG|LXQ1XFT}ZJOfRG{iMzN^)~; zKlrbH8k_T2D09SDOxC|j(dA)pb+f0Z>dN@=w8be!Q${_*9)UtLHW3N2kU?&aPAOfA;Ndqc#@FXWl@3WM-LX7y0kzsf&q*jlv6n z3;<-D5Edo;J;#hvfRA=%h@Oy&N9l!@hpWTn59$U!iUZgJdI1Iq`$C-v1Iz_TC&X?E zrm<*wW=lZlA}pS*Z!}Sg7;DJ1=-td>a({e-vDMA!dyFx0?M;F`d=o{6>15ypJmLfT zz~0#g`~u217++z;d@(OwaD^$_g$O-J0`?^uM30TucU*x?y1>PV6`rv_dcg~2<4^gk zr*(yhiz^b3aw7;3fN%(hY*z$w>^lgUx%Ukp0Cku7KT#9^fdl^o<78)LW%%DT&VR)C ze`0QKy8jz+FKpsu;#YhYtSC-}c;oXQq9CQh;@Zi;p`2Db7_GRprA z<^17i{?io{v~$;@VPR$efpXISVC?CCc=ZhQ^#6}kPIeB4|6eNSKMd#prgHw=`2U3E z{6|LoKUj&(^#A*fNSoN2Ih*4%Gqe7GVL6k=W7p_^g+6;g(UnCh7;>cv2qvJT6Qg#O zOXJ}ahej~7CqP1Pb$?A?w=tcJi5)MOU$?yjj?{wx8>q zeEc;zFEn5D?6G4y4*Q<^9Y|5;^gSDb{@ci0A=w0L6=E6Fdem-5apl)e zBd9N;EsVQJ%MPs3(~jEGMqukkZ6B8b)#LVx)Z3K?c$c$)D4mQSuw2f^tQy%j8vSSW z#r$?*X=zVN<^$bs(Wzc-??bH#->cnEf@OcV9dNq6L!0@NLJBoL0x?}eMb#0o+ zcV&BOr!ZXl+UG;_Sp)?}Cxb4EusxDDtNO_k%)WF-Fv@L|D0cnjYjw{|B<0%!7y-1S z`9Ih=GsFM3f&bf>>6BayojvSL=#(8@O#UMkG;lKcXRH5jHvK;WLRo&8vHt}KW&44C z{)2!rGc)0{{Ma}n3;ln}|MLH2OiVu)`wtxSzv}*#|K&Lte{|W{p#EF_Fa7_&SQ-AK z^B=(IKgj66`hWfXOaH4ssDFL4u>1f?fBN{>_P=}n$vFP&dH&PiPyK(l{ol{|<5K@= z1nNHx|0po9)BjI&(tpFk|3N4HkAMB29`_H8`(H!He|y|N;;-WW;B<8VOW_Bk%KS4V z@bW@AIXjvd*g&~wUwV7!D=#j-bX|9?KKn?SZcP}IHWFv>j1m)g1b`q38bj&_3PTX{ zuK*F`mqT13P-&u^$F{gs=tDy8_(LyfYBhgZ_t>sInbqTIR+p}+R#%%Ixn1Q*09f&@ z`}%#o>0WUiZ+TsLPVpRXyl)ws+gsr&7V3nH^pC;vWTDbzt_LE#5eTaHIIbh~+$&VT z-^@cuT&Xu(Jcjb|G+$UjjM9pTKir)?uB+XW!1IL1gN-HYw3ODCxrMoE2gzSL$%`Fg zim#+@k_5b%A<$_w8l24a80Y4`Blw{Z-1}sKwZOc>p*KRfXiMI)!9Ll%eY%Xp34X{1AMD46?vkb<$umSS zQIjD_6*IOARw!CE{Rd5|Rdw>6Ce?)2k9`m*KWHmTf3tywutiG1#r2$Fj&oE|;tlsNDYRB@o;*QK0O|O=kg*9i}7%mumrjG+=4+y)N#$YKjSYlFR1W$in z;kht)>S^CTG(Etb^g%oNz#$gUZTmC_w08lwzck(16nL<4{$(-)KXW|`ej=1U+WaEw zjgI%%&mekYe`30ATDUfncVsi|9fm@`#itxbWLLHqW=gPnCN1W~c4qY z34q3kzt#-rCQ_@L*->1*pXu)0ZRo?pN|xzjP|7M{F5J(BR!De*34)haTle6?~u1 zn8uG9I77ctZp8JX56vJxV2f|W&H5EA&)V(yLU+Mt`=rI55WP#a_+K^Ov z^96Dm0F2wnPP=zt*|5CAylNr`xL=^sq*{Z2ZZr&WuYQSlm;Ok6v58PeiflE6)k zmUOIBge%Z8NXc~y803iznh=?hLiJ1I2(%lZV=CHUFB;N$VMJv()W#Tb=kknN9eA_n z@uG0;b9C#|TnSmfp->m9%$S_GZl6}C5kJAYu)=4v>syoC;~JzZD3L}l zw!vJBn;?h%@W!zj=2i+w&F@&*?6qKPC_re{1>wa0j=&L#V{oJQN+)VZLl52p?rIIr z3PE(keDH;6zv(zxpJ_$Q$fNhVQIL(s8+0mw7peMnu>W2j+z$U(uf;HYVfZ!C6{%}9 zH-Xdw^BoaYPn`+w{Rhlt*RB2+&Y&B8o<7@Jm|Ne;_TV+6UE$Bxm24x@m8=Yw7x0(& zm5Jb6XV%Du2`qQehfLnzpxa}%2rfro z$(sa!LMy~7g|Ed+3oa*MW)N!qX*h!gUwnq(?ymxqG1Doa-NwuE81EGr?g76bbRz9S z7ZrerG$1nf5j11)Ks6CpO2O)Y{EXoIm#ocN19Le-bAc^-fSfx5G$36~VRik{>yWUU z!QFzGKN8Nx*874o!*ao_JOSv-P7130zu-^0{??)Th1dnFEcX|;%Ps)6JXjtGh?{GZ zGcU+hR#r=>sg)!~xKm?Rkkdhj1qLYg)DE6I+|!ylBn;R#YW^>rsO17>i6Cu~!I&Q8eRTvhSYH&Z7`X=w!mj-dHay`t1xGGX;4xJtqG%*)uO@yeh%nDt&+nPS#k zq;o-tOUoPcT#RYo@&wf*AIv?>52ImO&N%{(TI>Xq1R+Z0&I6- z1AbJwRa-BU5@D~qe)nY=`RDm(Hb zg=vYI{1aw|Ju&4{(rm`*{cddM?f~@PW;$ibmsF4gINUlQ)$Ar9%t3`kT_>+6hCQ14 z86tzl+pK~$WF2K?j?z&RvUQR;l>Vl933$iN89^Y(aB*80bwn>shZmHIQlr5*(m)8-J8CdeK_90&0+q#SHT z3quu(E79{g33qgod|xDXYKYjC1INbLTIlpnEXvr3z``M7>X`!#QV;sU ze_$|w)}dYJ`lMIAXZ2Mnp1(id9HjWS28nx=bark3`p$a({Tp#}VTA>BqbMzds{)UV4r#yGIBei%DYuTlCnZqwPdf*KhTCDeea zJ+H2%O=ML3m$7n7(Hhnh_-~(c=O6a!`SToZqu~A+-C8D8wjo1@Xl`4(U}^v>8BUtC z@pO55^G1yl247GA-F=4YZiUf6r%7~#xMzI29fuZ-Qw3_bvj|}mM$#9>dg-eL^)^BY z1AOrU{ku}bz{!LS6s(Ax$WL-t&L;tB?C~9%K<_`%t%KCw)_<@I!@#R{>so4PvEc@c zUBga6fqa=s(|ur@C^Zsl2uRr_!Lto8v1TSO;A#teGDp97i@yWaewg%_EeC zA41>K^QFS3+yB_VXWYV1M+=Rw{i%CepxD6|xR>+coP+(S>JZRDRK1?z1)q^bq?(I0 zx~%YfxcCYrhK#-hBY)GTj_la&PXgwSWo};hs-M)_A>!fLtRaDI*1lqy@=4<$bHVK(EU3Ieg=I3*tO}m zIvA6NM&?z5d|h$01`O8m9b~BWTW3`L7t>4lsz!fj+;L5L)B6K2-ctHTf=ymm=kEzU z%G+1;lJ4>c%uc@bc`b$%gw9Br2U6$*QMXkSe1)xz^+Hxxc&L3q0Rxs*?C1qzCwcF9mZ%1|i+q_6(Xy#P&^uA_C;_ z+P|VX?lS}z6qB36bbD7Kxd*zB2Yh=pV~9oIz(*Paafu_GS;ba#4-|!e!9uAf^u5PL z)-0XW(GI9WN`92utavu_a7S-7nWc9HZZK#cWN9^21yhXHCCtubi72|b$Sz6=hwq4oJ20^V&qj*ev>%x^7Gh&`xPD zBu-yqr_+8HO{rz*XpmMcyj?^3d$^)fhUWBw>pC6$6=w8_*eHASn4!IE5n=qgx4Avi ztu%?9ksbv9VZvgEm^R@~{C>lNdt~64i3a4t3HyR4?1zGhQOS52N{>fpNSCTZt%u+p z%~S3%@fNS+n&XqlgJ$|{@8%Faf7oBX`uWaJAGyYGLOBF_MqAiVxs$fT@HS{j4XKas z*zV3&&S>9!2*_3&GX}ECjX}lL2Ql)uuBZ>qy}tlTDibYnKCx3ibuR=70vCc77b!9_ zDSsBBCy#IRJKQgC`quC!%nc9rKJ^8=Fp>9SOIse*%-=4bydTzuEu6AnQGLR|3i$WD zAR}1!=^RAvTHMe;-fo;Nz9k{5$YyVt{mPEfqe>F>jps+mC>ok=L>jerW)qy-rsK)c zD`e}o5lw3whbW=S3GQByy=tEQ8%y0Qx4ZImN)5TifBDXgVN)%7HADm4VF@s?iZS}&T1 zI5kEmB{Qpzc5ZpCz+95 zu)Iy7+wWN;=r5|i2)GHyz=gYDI&{FoWPfiNbS<5zQv5;QiqCJp?%IASoaP)>HLBEK zKQn8!>pl*g4&D6O{m^?_#}@M9?nH=~abFpII6U$6j?@qsvI^0-)yE9IyhgvImbOp3 zDl%sB8#+d+vcu|+6+=aV<2!@~2KoX`MK~3f#BI$AB(;6zG#K-zm_|qv z!}sEbO{>~{zd8E z25w-}=#3aYvoN%X$-R6Cm4|)XAQ}3h61Z69ePO+q`F|dpdP`F%=I8hAqnaP@kE|V0KBcK+SkA^#O%f z!J75z_v5KldH&Xzrc}Fx1|@^C-Y~5T36uq8ErA09Dq_ddC&36HmHDs$(8kU8HWb4} zSofIFuX=WeIsOx@li#30+3FFIB28htXNBkk$@Y=vjl;{8}hVG{awWrI3aXKR7JtCYd(Rkm4^mLa)0 z9Y`-itZTM*ZwRnNz)q=hfNT3>%}e?jYGz|;4tq^A<&&e)t3bf?iPEPIo&&U{9rTGn z)_WZhW-BOz-2~`@D_&}VDT>++V+i|#7MF>!B|YJ^e#?@4$q0*bh_k&#-K=>vo3UMi zH0j(02Ka0OC4RhVsZ2jfKL|E3n*oBeOY|sF#F)#3ZCse~X2ir}IKrO{i6Ud?XO*_x z8&t8wxXhR@F(=grAbF>393-Yj7fdV2h&E$_&}md|hi1he3Q4S5T=$?HBHcJpcNQNx zTGBmT?`%Mex_(1j>TN7H`{`SYEx^2 zZbjEI%2?OLJRt4R+La3#D&#!mjgw&KiQct?3LhO9uz=TipIyiD?PlT_@n!iPB&|Y} zRxEMh{&X%9swWV7^=xzCou}3veHxnbNctX<8yd3Bc){1jpWX9s3)7>ZjaGv$x$NTd z^qxxKndu?{N?AkepiLv=in#U#bdy<=ol6i3&?Io=(Ln2v07?upcwl)(1k*#or+v2j zdXiIIGo5(x8YcyP(A5aSYFvPqK$WVB$stFLrhx_4L`VqT6j_5buHrzd8dQqP&VoW3 zW^L^1L!Yc&?Y1Vita+rQ^iufJS&d$2ufAGFjXPll~g@@@9RCq zVh%=5@wsB#&b{$hel>Q~Cr|>@lRn$9g*Kl;F%^X6iHIG%!&`JIMlW@sx z?l)P#L3SQm-{EGCA>PUy30Fu)TTJh98aIk0D`}AkPm63wSQn_oS3`TYgc1IK}HYFeoE6Gi9fiv%Nr$f9{H*JB1NlZ(e~y}l91GeMCWYL3zCCU zVZbGrFK6LIhhGRw(W)`3&@*)B7o2XoW{h25*F^?nURB&&((!17Zc~<_*<2M-Jzl3H zIMj`eI?X8#Gz4lhOIr_Va6Pi>Ua!p<5-Vyb6cvq3PQiNy=6sT!XM;qFl8*~~qfNd| z!iV&;AQ{nIx^DRvy-US4JJ>E1YZTwghU z4pDhLf@a`sZQfK=<>UPn+Vi&l_wSdYIa?)3c!hY8s&WhMzv{Ur$EE@dRer(Mb9vR_ zI?M9-RwG-;TB0rR_V3y@skP!edSBPlC|H--s!BNn24- zZrA$VREh7^dC!Za$s)9Mvb{I0#-$ax z>O(r?R)*J#+oc|1?s3ZKZ1q&>b5;s>Oi~JCRB6&zO?tpt@mA>k%c zAJz#zz*wk<^i`W2@TENEl2Qt?a=jP@f>7HqBTw0*VR6sl*Wf~qJPmNEfx#`ueodz6m{ax-V1^i(x^E@R?0ohbQX-q{@ z7IB5N(ZJw48@O5W-wc4t`|K%6FN~qcseONf+G-Zd7LxZB%+%B)K%oR}QD(ClI1coa z74YCjv)?a@e~5CFDX%F`>#IT#@SE@|CNSpQ@(+k18Q2H`uPOxJCKlf2f`z-S;z@#t zM=JF7uaO7mlLiNL{X}mf3ru+-Sq?Zza7`rNCidKgURCV|M*y)fBVRthI0JF(OG{(a z-nNtEcLhm*4xV`=D5=76zjBlP#ACJ9R@7`{7!HPCDU~?9*Vr%U&IR|p z$~9CRYjWtxrG9KhpD}MgKk@gQ-qGK1%zbn{vc`zpGZTv>%{_o;TV-f+2mYU$iSXbE@5iLUf%ssHW6 z#u3rj?$NrG`y<6;f#XP-p}%&MZzr_MGT7%|01?9U$nu#gG>6Ud=+4=CRaUE zP+!z*GxFhP)4Na)q&vSXYarjgZ!9_ChpKl$JOqcPtz-Fg+|%@L(nYgB%}$L4)id0& zTWb5Xc$<=^>h$^;GztxXF(&XD`CWP}tH>a`=wCngAK@@CpJ;;8i+$*{KEm`Dj<2g_ zu%$F|y&2M1zounWKkAtAU9{90zcZp$KOd7@j*c$1X)|sAW|g!sRwVqBvm@X7vX`lA zp{YC3Qe7UU)#=e3K)-MvWux8BpKhU3*V)~|j(+8WW0D*MDn%vZNx$j2KDNVdd*-XV z{ky^TX+hcPa0D{bTG>%fu1#7!C-Cy5Csla zb;O(yvjfeEOzo?f(vyu-<%umok(c~sFyVJBnUT|~fi^_O1u>Uzh1|Ng4TGr~_;eS6)AxRFyzP99=3P9;Lr#ybXCRazDG7_kby8RV(2nEx48 z6^9rOxwgd%oT*}fGK|~HbeOy4t|{vhs^hC4(PyCOEtxG+OQb(N^Kj^28O#+ud0p}< zV!8fDGty9X#{FYGC35S{A<{NOj;GB*Hnqi!#(n|FR<(9HI8xvB`R*~5RdmZa+0zzz zndekqWw(U8R!fQCX7SU>y5htGSrbPPE+PQq3xU@du(twM8I>Af+=iixyB>P&w29Ht zxUaXbriM)B$r(AndLwhsZYXVR#x_I2+mfVk2~E00N)Im)Xk25Q9-NIa1OP4o^rdjw z_)LAbUP%@CSxsqSV9Tg4 zvCg_4&%`@*A#7X+@>quTU?$3>F0IYFN`rBIE^$fk)00LSqnbfe0&xH6e8tz_0}IHo zQ!vxX^GCyp+_;<72pbrV5t1Ro=Mu^%BH*`5UmtRA?zz`bt8!@QKYJD3yD;G4X-~L^ z?5%sKr;~~GgDvf?>&xTYT|@wNC0VUONVtEuS!6>wH^VJY?`B=)r9jFfZ?vbM&d-k~b#W-d)jOy$kE z4$vLLu8ju0G`h;huHaZu07cH|Y$SQ{V=Zq?EBwIO=u^lr!jZjxMy??+{St7Iiq z)@dxk2y6G+&KAH;#JnP6qb)|L3h^I2lcNtf?8B%C&6sZHCYj#A2~OvPUkF1~`!gPb zuiAltVM&MOx%hQ) zlP|;?(jC%1=Y};(t^<_NXHh(Zi$qH9N1Suu<`Ocw-g!218;}UZK%kqQLa$)DzP+H! zSAAHv<8ZP;xU*mr)INp@0qy*llE(~>+=O*N>7@GGk`zYM)Y`{ncWP}-S8tCHk-UYp zjnp_Jx3}@r=NY=CU9{PCY4=37L-%;c=Ax%)6HWI=#6qN{E!KJ{W|g*5O0SgnvENqF zvZNQPW)N({M6P*eWGWa)?fMvBtBy$k^NvE$S2`mZ6_}76$N3?9|M!sDLh)wA-lNH1={@f^AeVuz6>p0y? zuh?Gru1B|`T~U?@+2Z2QY;gW*NyK1*>8!(M$p>DInP6^yi+&z7n!-_EU91tEm zvzs7a97{J>Tqlz5U09Ae9!`w~F`z6KJ27#Wj<)zqz$j1%DZ1eUBQFXjbSlTI2QGE3 z0xyLrvhe&u{VOAhqPyBe5H!@uV4(^8mZKj_d?Cv zYHOmxDUGl3z{s~B1`zC;lQyzi0clyx+?k)iqfwYc_Mx*aP&-9ka<6T7l*ts<7Q)O+ z%rdn3qWgT0DvvJjeZBT)ihT$9m>1{RIqqil8PNqr;sP`($%FeAtV|oU@{N+#Q5juR zF>pR2zaV+!ZMmuXkomPJq{d0Ohk`xHjUXn!YijM(xgz4mKr0xO^0OEu!J$>KFkXVkc?Wk)2yMAQSvW?rf;l>Ck_ zSQkH~KPyX@6kf(4AcWrXnDk^a4r)e{{T@NMSV|d>&%mSx{5;c?2F8e!4XSS)v=B`(dAtTaE-T)u=(pikL4`#qS4jm}E0r%^p_r~~k4q|< zUXZ~Ak7NF-Y?_8s(~SnJx}HijSj(MySLG@dVhvXUvd58 zG4e^a>3RCM^GM*@b(GC9&4!Fivd;eZ2D!58-b=UC%0(waQzO?iM5|g8!bgi}zw=76 zHo88Yigwj+wMG@zydSf0X0Bx}6|NR^D_2xI%J#MAe2?8Y)Z1J)5&1{M2GD+6unMWcOyhqLdNagul|ZS+>r_ z;48zVQg|a^>a?f*WY|0Pal$EztCAxW2_OrW-32IxQX^hdc%{y6`Iu;NBLH3lcmiSV z--P@F>^qbTdUqa zjK}LZ%h;+JMO4L5{&?6sZ?hDPR;#8)K!06vYlEy&cXWnCaJ6~ad+J~fEPm2xh5asO z?Ks&D{FGV3=f8@cx?X=T5W1*msA5j3uD!SJd2euMb~V{?yD3=OQMW{}JoYd7WE9$5 z5p~pC!5SFoM<`cU(ZIA2bQEeFkebzS;_Mh~?*2fy-8za5j?t72kySbUfk6<@V8C$g zIj-Kqf^m=ppiE7dC4wK)luKt`nY805llUH1gGF$LT=X;@BhYu{n0^ z-#+&oeQk5kM0a+y^De*AurB(!Mozrii{-=xa8C2TId!2UzDjGsY-bhl1^5vh8u#Zv zGLL4whsnzwPIh{*^f2lAf`##rFj@|pIqzFEBBrQ~P<>SW43j(dN6D-V1l@!EykoE0kDK=|QZ%`T z_>NT@5w+V&`jxXo5u0~Mwa3-d)daoV zkV&TWix5azJ&J^6mz)~!I&1G7ngT`zPtjg9B9^ki_@4AT3X}RZhu_}F=L3+s* zNx2Nbx8wm{_4sT|m*2mZMX?LCiagLLcxQCp0mZciTbtvNgpxWT>-k#?~L99t98)BeLB%OX+aCa9)GOKI|b!kAx>@1AcP91hjAAF?q~ zYm$aEw8bKwupE(W`5Uktq2mmyy`rNLh2UCI1#QKCQnPGc=D?{!Zv-%HaN08*ux%0T znXku-R;413;CLj_W>UbWacWM^z^3xeX0E^|aq3PLevxqs5BDH+Vk&dCux(&V5nn33 z#iLZR=yuo{L^kxhRGW0a!7YaIV6Dr^zFYkq%Y^RehD1rVEvR?n=xB@E(k5e_fA3Ihhh~@NI~0hPn#fpT3Cj^BzXh-dFT!0)MYBU7TVhwx zk|YIqH~2YHRQLq%TS%l{uuaWC>4 zaAP$@W0!y1L9-L$m$he>T;YP@D6$MhYmN74!o;vE6YoseM*#Rb0+T;H%Xg*C_bBy3 zf6`N25ub6j_z8E&3g4nA#Mb=n@^*Z51)IH*foBz+ysF9m!KU7=3vqHnD0ccPwG;Yg z8aiup%EhpgSV5KS1psPm|Gp&IciNJ#1v?78{`7B_v#m(6`xFrdNN%%GmcIykU0D3s zgs_A*^o*IUUAK+ zV(Ea~po?+*ntqg(qmMrd@P3*d?KJD$lNrC>16J~enzm?z`R*vdtHtHf^?=JVXW@jV zF`z2*v~F}OJ_(k*TVlN6O($lA@yk9O4>Vtjt)CXMen1I34SV{B9Kshk$BB7-UEBT9 zrM-Q2f-SB432=x0Wdrv6-i+?S937uvj5c&uaHI=aYzwmZt{P!JK3-#j(3r89px}%r zLwH17tOI%8EpChj;OFEb_@xq-c$?_aqA*6x+*raOS7{dg_e2t2s7LbSwe~OS)zH#`!fLr_`s{bNu{wz+9Od<)qJnhj&U;D$>o5qMz$8uFl{0 zjZQ63+mw8W3g!3(yt z`|-!79VcDtmxWpQ6g>BZ;|H2VE#Z9b>l@aJ37NH_ziJ>OzT?Gr_7CRNl62cjVr>al zIT)I}6$$ta_3RdU^=5v3_YTASw*U0-5&S-971n$K_tt!aXnlNCp}E+ZZ4K_pyGHTp z>kY|?t`_F~^R6n3QTwVzmpfCVzRBJOZgHrHK1anjyWQhpr#I1Ck-v+Xm@6ei>F0Mm z^M``IsrBZb?7j7d_hVL1&UPmxr_cGLx5AgF$~TEIxh0M#1HzN;Y^NC)7rt47!*{kS!s6-WX8VLU z*iMl#&#^Sf_ZB$#{!=}LY4aZua!blmo=B&mDj{!daoBhlp(>t`bZc!sc_*7<`3txEW1sk^XMD4Azd887?R~xuK0a`# z5vwwMhdQ$4ylYAb@_1I?YRx{%nM_$HOu`;NVb^Koc7jfXtxlyTWaf`phl~h*drh7w zju5;G({;cJ9Gc~Y*g_|$e3Kh1+V@*GUScTXmn)e@V7jyXn#pf=80{& z)V<@HF8yLZff6q@GT8d(!MOau>E(5LwE7@FKJhw_Uer7l?=)=qG}z71+&dCXa^&8Q>ek6Dl4B9~=>%LeQXM=JNIc zF0Yyqko#mn&c(tU|zzKnMXO^x>kguxIPQBV;Rf zWv?@TnS0J!frdhxyFHuOguF4)IE8mCDlV76V4}=gIjbx4Ph)L>&vn11ME}A?^QsV6 z688`uPBd8%oF5F#uwD+uL+WwavO09*3_FB*o9CQPU=hT}>JEFu8b!Z(~J`s5sgcZH+|X$_hCJ$(c< zbeEpbrtognQ(J}-W1`oZeKO(wOvE2=W-iJ(GakY5s1OtnAq`gA8ebHutPqrTMDRpg zjs9~fZM2oQkFmB@OaMjxr!*aP(#tsNc!mVAPwF+}4)hKzpX^~}(59R7rV46Rd5wNG zAFPra>2Bqdyave)@hrp~S4vKL9bTlTqBzQHz6*MI{kn(E&;GZ=cXHBATY; z&_Urvz1ejjNJ&G`vHUo(o*&^BX5XlSSE0kkKB3%vA8;(eYuSflK3HnOmE&YFVYq`A zKF3Io3GTsrPmQ6gi5Ju~>_AHFja8A+b?_HjF4)xbEG237dRI(tSD`n!A$ZH;yK6xD ze!~dq+VL9r7`Vq{m=5e>d*Pd*o~WV<)Oz8DB|@-C zMKNG0V7g_gSg6t_u;~HB=s9A!$`5Bq7m`a;l1t5!OBqEK)a4p!O3kFo#*)f}~p|4-&_&$ zFM7b+?C08uLsw*Z@*&rPI`dF0ae2{JxR0s=Pj7Rpd6n{^D}tG%?(kIkMpJ~uU~Fg+ z?&8_cojLtBhei@;__wyMG6L>9Wq)kLqtIeIzBCf_5u!S_pG+ABw}ZMm#kEZhkWkTT zzGJfOKiOJF9(BLq0%O~K4t>`i8|Vjs_fQ!DFV_6khk2+d{$F3KLp8Op*RESBwVrK3 zDB7Tj?g9*LH6cXSk2&_%0Tv$Jfi;o$%FdqVh#1|XhWN7tK#r&^w;=rGtGgNKnwJ@8 z{JR;*o@>h2-K)w60S|^XPGi`r$*C>Md&NV-vRHTzq^G>QRMGf$v=tsO{IR5NSVC%L zaTiHeDGyRAlIx@aMH&i}l&Nl{HsSIWsTRc+CSngZnx<+*&Hm$ zEHwLKy*Brz^j<&6NQkeUcnWNVtzkRO(9Ms!vEkM_PqKd5A-{;b3$Eo zT$R+&8Er4?SBUKTSK=1_k?b?Nhn#}NLUQU@_kD6ZRt>Mm?kl(Gv9yJmKUeLcxz4tgj z%6v0L9Dl`}wOQ+&=u>8Fvl^QJ$Y{n4MA1$<*?^@2KHRi_YOdE-J>n?{q@6QVx>NKxf6U5=*{;y zU>D@yIpcqr>p$|9k(Gh*UzVPQA#FGGc(J-0lxn$>D5k{u?b{=8@}cdjoHc%_ytuN6sNHc z;Jt_)ega})#Gi+f&x73dso%1mN65>)oH-_u)yhRQ-I%$pBya|8JoT{Tkv^ERM8a%) zVoNAz=+3!D?tYx@pg`!JVwz&evSv>)M(YV)XL%R>TUWcQR~W6w4fJ=bYW-VTCK5aQ z$!|R%?nu%KD*ZfI#~h94O#~#xl{YJ)_S{rwvsctEdIS+by%Dts3q!j~pzS4k0h1(^ ze2%O#*z!kZF?OpK*XP6#%r2N@mAA-rQmR(;4F=LvcPu?pm20ITMq`10%| zt-S`xHHTaV?>$1h$rSQyFZ1)LxXIg9_+w@-(_WW|$v2g-$v4c2)KjfpiP5)cz~mSD zSE|uB=hWZJ(OS>=8PGL(uy}aqmwNmx`}KgwsnD#B9Z7fa4)=jrsP=;wXr_Fj-oJ}J zwtu}R|1-?~AIkmzA8r2@yEC!;U)Y_EjqN{B`+vjtf7*Y7@gH3N4}kxt{Rh4OcmE%J z{`dBOVE2Fe|Bc!I8>Ro#{sY(lIZpdOWBp$rm%m&8BO2+IY?W=y4Q-8#30eM$#{WG& z|H%XKe;D+C5?%bi4LTz`BisKPu$NxudRjyAc8{L6HdB+Cb6WEf%(k+U%n9Pl`hOZ8 zoYvJ+XagA&>g$Z~Q>@mwtm}v*j-d#lp6G%mh_HnG1YCG={NwGR#)SxtBpLKkB}Uf~ zIG~Y&*7Qap;*Kk?uyvt3r>~w5-fxX_Ix9LlJ39Wkew!LC9N6e20w4B=Y;#U|1uANNc5!v; zc6^70@SF8WsxQNYU7o&H(*Kg3eD`dP(K%ANs_y+@IX?RGpN=Vot)cAu@HN=(Y(A*y zBi0FQ7{E?TZE?1`^r1KO20pHjuC}w&GkS2p83C?

VSd)^I1*S$6Lp%<`VO+ofL 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 TO`%XI6b?gxczG4IltBLnzzKBO literal 0 HcmV?d00001 From ff1fbdffa1eec9481f398fe971ca0ff265a0b3eb Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Thu, 23 Jan 2014 17:13:50 +0100 Subject: [PATCH 21/76] Vector::PiecewiseMultiplication --- Code/OysterMath/OysterMath.h | 6 +- Code/OysterMath/Vector.h | 119 +++++++++++++++++++++-------------- 2 files changed, 76 insertions(+), 49 deletions(-) diff --git a/Code/OysterMath/OysterMath.h b/Code/OysterMath/OysterMath.h index 3770bf02..dd4655c4 100644 --- a/Code/OysterMath/OysterMath.h +++ b/Code/OysterMath/OysterMath.h @@ -67,7 +67,7 @@ inline ::Oyster::Math::Float2 & operator *= ( ::Oyster::Math::Float2 &left, cons } inline ::Oyster::Math::Float2 operator * ( const ::Oyster::Math::Float2 &left, const ::Oyster::Math::Float2 &right ) -{ return ::Oyster::Math::Float2(left) *= right; } +{ return left.PiecewiseMultiplication( right; } inline ::Oyster::Math::Float2 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float2 &right ) { return ::Oyster::Math::Float2(right) *= left; } @@ -81,7 +81,7 @@ inline ::Oyster::Math::Float3 & operator *= ( ::Oyster::Math::Float3 &left, cons } inline ::Oyster::Math::Float3 operator * ( const ::Oyster::Math::Float3 &left, const ::Oyster::Math::Float3 &right ) -{ return ::Oyster::Math::Float3(left) *= right; } +{ return left.PiecewiseMultiplication( right ); } inline ::Oyster::Math::Float3 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float3 &right ) { return ::Oyster::Math::Float3(right) *= left; } @@ -96,7 +96,7 @@ inline ::Oyster::Math::Float4 & operator *= ( ::Oyster::Math::Float4 &left, cons } inline ::Oyster::Math::Float4 operator * ( const ::Oyster::Math::Float4 &left, const ::Oyster::Math::Float4 &right ) -{ return ::Oyster::Math::Float4(left) *= right; } +{ return left.PiecewiseMultiplication( right ); } inline ::Oyster::Math::Float4 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float4 &right ) { return ::Oyster::Math::Float4(right) *= left; } diff --git a/Code/OysterMath/Vector.h b/Code/OysterMath/Vector.h index 901ea5e4..d6ab69d9 100644 --- a/Code/OysterMath/Vector.h +++ b/Code/OysterMath/Vector.h @@ -57,6 +57,9 @@ namespace LinearAlgebra ScalarType GetMagnitude( ) const; ScalarType Dot( const Vector2 &vector ) const; + //! @return (a.x * b.x, a.y * b.y) + Vector2 PiecewiseMultiplication( const Vector2 &vector ) const; + Vector2 & Normalize( ); Vector2 GetNormalized( ) const; }; @@ -112,6 +115,9 @@ namespace LinearAlgebra ScalarType Dot( const Vector3 &vector ) const; Vector3 Cross( const Vector3 &vector ) const; + //! @return (a.x * b.x, a.y * b.y, a.z * b.z) + Vector3 PiecewiseMultiplication( const Vector3 &vector ) const; + Vector3 & Normalize( ); Vector3 GetNormalized( ) const; }; @@ -169,6 +175,9 @@ namespace LinearAlgebra ScalarType GetMagnitude( ) const; ScalarType Dot( const Vector4 &vector ) const; + //! @return (a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w ) + Vector4 PiecewiseMultiplication( const Vector4 &vector ) const; + Vector4 & Normalize( ); Vector4 GetNormalized( ) const; }; @@ -184,22 +193,22 @@ namespace LinearAlgebra template const Vector2 Vector2::standard_unit_y = Vector2( 0, 1 ); template - Vector2::Vector2( ) : x(), y() {} + inline Vector2::Vector2( ) : x(), y() {} template - Vector2::Vector2( const Vector2 &vector ) + inline Vector2::Vector2( const Vector2 &vector ) { this->x = vector.x; this->y = vector.y; } template - Vector2::Vector2( const ScalarType &_element ) + inline Vector2::Vector2( const ScalarType &_element ) { this->x = this->y = _element; } template - Vector2::Vector2( const ScalarType _element[2] ) + inline Vector2::Vector2( const ScalarType _element[2] ) { this->x = _element[0]; this->y = _element[1]; } template - Vector2::Vector2( const ScalarType &_x, const ScalarType &_y ) + inline Vector2::Vector2( const ScalarType &_x, const ScalarType &_y ) { this->x = _x; this->y = _y; } template @@ -227,7 +236,7 @@ namespace LinearAlgebra { return this->element[i]; } template - Vector2 & Vector2::operator = ( const Vector2 &vector ) + inline Vector2 & Vector2::operator = ( const Vector2 &vector ) { this->element[0] = vector.element[0]; this->element[1] = vector.element[1]; @@ -235,7 +244,7 @@ namespace LinearAlgebra } template - Vector2 & Vector2::operator = ( const ScalarType _element[2] ) + inline Vector2 & Vector2::operator = ( const ScalarType _element[2] ) { this->element[0] = _element[0]; this->element[1] = _element[1]; @@ -243,7 +252,7 @@ namespace LinearAlgebra } template - Vector2 & Vector2::operator *= ( const ScalarType &scalar ) + inline Vector2 & Vector2::operator *= ( const ScalarType &scalar ) { this->element[0] *= scalar; this->element[1] *= scalar; @@ -251,7 +260,7 @@ namespace LinearAlgebra } template - Vector2 & Vector2::operator /= ( const ScalarType &scalar ) + inline Vector2 & Vector2::operator /= ( const ScalarType &scalar ) { this->element[0] /= scalar; this->element[1] /= scalar; @@ -259,7 +268,7 @@ namespace LinearAlgebra } template - Vector2 & Vector2::operator += ( const Vector2 &vector ) + inline Vector2 & Vector2::operator += ( const Vector2 &vector ) { this->element[0] += vector.element[0]; this->element[1] += vector.element[1]; @@ -267,7 +276,7 @@ namespace LinearAlgebra } template - Vector2 & Vector2::operator -= ( const Vector2 &vector ) + inline Vector2 & Vector2::operator -= ( const Vector2 &vector ) { this->element[0] -= vector.element[0]; this->element[1] -= vector.element[1]; @@ -295,7 +304,7 @@ namespace LinearAlgebra { return Vector2(-this->x, -this->y); } template - bool Vector2::operator == ( const Vector2 &vector ) const + inline bool Vector2::operator == ( const Vector2 &vector ) const { if( this->x != vector.x ) return false; if( this->y != vector.y ) return false; @@ -303,7 +312,7 @@ namespace LinearAlgebra } template - bool Vector2::operator != ( const Vector2 &vector ) const + inline bool Vector2::operator != ( const Vector2 &vector ) const { if( this->x != vector.x ) return true; if( this->y != vector.y ) return true; @@ -319,7 +328,7 @@ namespace LinearAlgebra { return (ScalarType) ::sqrt( this->Dot(*this) ); } template - ScalarType Vector2::Dot( const Vector2 &vector ) const + inline ScalarType Vector2::Dot( const Vector2 &vector ) const { ScalarType value = 0; value += this->element[0] * vector.element[0]; @@ -327,6 +336,12 @@ namespace LinearAlgebra return value; } + template + inline Vector2 Vector2::PiecewiseMultiplication( const Vector2 &vector ) const + { + return Vector2( this->x * vector.x, this->y * vector.y ); + } + template inline Vector2 & Vector2::Normalize( ) { return (*this) /= this->GetLength(); } @@ -343,26 +358,26 @@ namespace LinearAlgebra template const Vector3 Vector3::standard_unit_z = Vector3( 0, 0, 1 ); template - Vector3::Vector3( ) : x(), y(), z() {} + inline Vector3::Vector3( ) : x(), y(), z() {} template - Vector3::Vector3( const Vector3 &vector ) + inline Vector3::Vector3( const Vector3 &vector ) { this->x = vector.x; this->y = vector.y; this->z = vector.z; } template - Vector3::Vector3( const Vector2 &vector, const ScalarType &_z ) + inline Vector3::Vector3( const Vector2 &vector, const ScalarType &_z ) { this->x = vector.x; this->y = vector.y; this->z = _z; } template - Vector3::Vector3( const ScalarType &_element ) + inline Vector3::Vector3( const ScalarType &_element ) { this->x = this->y = this->z = _element; } template - Vector3::Vector3( const ScalarType _element[3] ) + inline Vector3::Vector3( const ScalarType _element[3] ) { this->x = _element[0]; this->y = _element[1]; this->z = _element[2]; } template - Vector3::Vector3( const ScalarType &_x, const ScalarType &_y, const ScalarType &_z ) + inline Vector3::Vector3( const ScalarType &_x, const ScalarType &_y, const ScalarType &_z ) { this->x = _x; this->y = _y; this->z = _z; } template @@ -382,7 +397,7 @@ namespace LinearAlgebra { return this->element[i]; } template - Vector3 & Vector3::operator = ( const Vector3 &vector ) + inline Vector3 & Vector3::operator = ( const Vector3 &vector ) { this->element[0] = vector.element[0]; this->element[1] = vector.element[1]; @@ -391,7 +406,7 @@ namespace LinearAlgebra } template - Vector3 & Vector3::operator = ( const ScalarType element[3] ) + inline Vector3 & Vector3::operator = ( const ScalarType element[3] ) { this->element[0] = element[0]; this->element[1] = element[1]; @@ -400,7 +415,7 @@ namespace LinearAlgebra } template - Vector3 & Vector3::operator *= ( const ScalarType &scalar ) + inline Vector3 & Vector3::operator *= ( const ScalarType &scalar ) { this->element[0] *= scalar; this->element[1] *= scalar; @@ -409,7 +424,7 @@ namespace LinearAlgebra } template - Vector3 & Vector3::operator /= ( const ScalarType &scalar ) + inline Vector3 & Vector3::operator /= ( const ScalarType &scalar ) { this->element[0] /= scalar; this->element[1] /= scalar; @@ -418,7 +433,7 @@ namespace LinearAlgebra } template - Vector3 & Vector3::operator += ( const Vector3 &vector ) + inline Vector3 & Vector3::operator += ( const Vector3 &vector ) { this->element[0] += vector.element[0]; this->element[1] += vector.element[1]; @@ -427,7 +442,7 @@ namespace LinearAlgebra } template - Vector3 & Vector3::operator -= ( const Vector3 &vector ) + inline Vector3 & Vector3::operator -= ( const Vector3 &vector ) { this->element[0] -= vector.element[0]; this->element[1] -= vector.element[1]; @@ -456,7 +471,7 @@ namespace LinearAlgebra { return Vector3(-this->x, -this->y, -this->z); } template - bool Vector3::operator == ( const Vector3 &vector ) const + inline bool Vector3::operator == ( const Vector3 &vector ) const { if( this->x != vector.x ) return false; if( this->y != vector.y ) return false; @@ -465,7 +480,7 @@ namespace LinearAlgebra } template - bool Vector3::operator != ( const Vector3 &vector ) const + inline bool Vector3::operator != ( const Vector3 &vector ) const { if( this->x != vector.x ) return true; if( this->y != vector.y ) return true; @@ -482,7 +497,7 @@ namespace LinearAlgebra { return (ScalarType) ::sqrt( this->Dot(*this) ); } template - ScalarType Vector3::Dot( const Vector3 &vector ) const + inline ScalarType Vector3::Dot( const Vector3 &vector ) const { ScalarType value = 0; value += this->element[0] * vector.element[0]; @@ -492,13 +507,19 @@ namespace LinearAlgebra } template - Vector3 Vector3::Cross( const Vector3 &vector ) const + inline Vector3 Vector3::Cross( const Vector3 &vector ) const { return Vector3( (this->y*vector.z) - (this->z*vector.y), (this->z*vector.x) - (this->x*vector.z), (this->x*vector.y) - (this->y*vector.x) ); } + template + inline Vector3 Vector3::PiecewiseMultiplication( const Vector3 &vector ) const + { + return Vector3( this->x * vector.x, this->y * vector.y, this->z * vector.z ); + } + template inline Vector3 & Vector3::Normalize( ) { return (*this) /= this->GetLength(); } @@ -516,30 +537,30 @@ namespace LinearAlgebra template const Vector4 Vector4::standard_unit_w = Vector4( 0, 0, 0, 1 ); template - Vector4::Vector4( ) : x(), y(), z(), w() {} + inline Vector4::Vector4( ) : x(), y(), z(), w() {} template - Vector4::Vector4( const Vector4 &vector ) + inline Vector4::Vector4( const Vector4 &vector ) { this->x = vector.x; this->y = vector.y; this->z = vector.z; this->w = vector.w; } template - Vector4::Vector4( const Vector3 &vector, const ScalarType &_w ) + inline Vector4::Vector4( const Vector3 &vector, const ScalarType &_w ) { this->x = vector.x; this->y = vector.y; this->z = vector.z; this->w = _w; } template - Vector4::Vector4( const Vector2 &vector, const ScalarType &_z, const ScalarType &_w ) + inline Vector4::Vector4( const Vector2 &vector, const ScalarType &_z, const ScalarType &_w ) { this->x = vector.x; this->y = vector.y; this->z = _z; this->w = _w; } template - Vector4::Vector4( const ScalarType &_element ) + inline Vector4::Vector4( const ScalarType &_element ) { this->x = this->y = this->z = this->w = _element; } template - Vector4::Vector4( const ScalarType _element[4] ) + inline Vector4::Vector4( const ScalarType _element[4] ) { this->x = _element[0]; this->y = _element[1]; this->z = _element[2]; this->w = _element[3]; } template - Vector4::Vector4( const ScalarType &_x, const ScalarType &_y, const ScalarType &_z, const ScalarType &_w ) + inline Vector4::Vector4( const ScalarType &_x, const ScalarType &_y, const ScalarType &_z, const ScalarType &_w ) { this->x = _x; this->y = _y; this->z = _z; this->w = _w; } template @@ -559,7 +580,7 @@ namespace LinearAlgebra { return this->element[i]; } template - Vector4 & Vector4::operator = ( const Vector4 &vector ) + inline Vector4 & Vector4::operator = ( const Vector4 &vector ) { this->element[0] = vector.element[0]; this->element[1] = vector.element[1]; @@ -569,7 +590,7 @@ namespace LinearAlgebra } template - Vector4 & Vector4::operator = ( const ScalarType element[4] ) + inline Vector4 & Vector4::operator = ( const ScalarType element[4] ) { this->element[0] = element[0]; this->element[1] = element[1]; @@ -579,7 +600,7 @@ namespace LinearAlgebra } template - Vector4 & Vector4::operator *= ( const ScalarType &scalar ) + inline Vector4 & Vector4::operator *= ( const ScalarType &scalar ) { this->element[0] *= scalar; this->element[1] *= scalar; @@ -589,7 +610,7 @@ namespace LinearAlgebra } template - Vector4 & Vector4::operator /= ( const ScalarType &scalar ) + inline Vector4 & Vector4::operator /= ( const ScalarType &scalar ) { this->element[0] /= scalar; this->element[1] /= scalar; @@ -599,7 +620,7 @@ namespace LinearAlgebra } template - Vector4 & Vector4::operator += ( const Vector4 &vector ) + inline Vector4 & Vector4::operator += ( const Vector4 &vector ) { this->element[0] += vector.element[0]; this->element[1] += vector.element[1]; @@ -609,7 +630,7 @@ namespace LinearAlgebra } template - Vector4 & Vector4::operator -= ( const Vector4 &vector ) + inline Vector4 & Vector4::operator -= ( const Vector4 &vector ) { this->element[0] -= vector.element[0]; this->element[1] -= vector.element[1]; @@ -639,7 +660,7 @@ namespace LinearAlgebra { return Vector4(-this->x, -this->y, -this->z, -this->w); } template - bool Vector4::operator == ( const Vector4 &vector ) const + inline bool Vector4::operator == ( const Vector4 &vector ) const { if( this->x != vector.x ) return false; if( this->y != vector.y ) return false; @@ -649,7 +670,7 @@ namespace LinearAlgebra } template - bool Vector4::operator != ( const Vector4 &vector ) const + inline bool Vector4::operator != ( const Vector4 &vector ) const { if( this->x != vector.x ) return true; if( this->y != vector.y ) return true; @@ -667,7 +688,7 @@ namespace LinearAlgebra { return (ScalarType) ::sqrt( this->Dot(*this) ); } template - ScalarType Vector4::Dot( const Vector4 &vector ) const + inline ScalarType Vector4::Dot( const Vector4 &vector ) const { ScalarType value = 0; value += this->element[0] * vector.element[0]; @@ -677,6 +698,12 @@ namespace LinearAlgebra return value; } + template + inline Vector4 Vector4::PiecewiseMultiplication( const Vector4 &vector ) const + { + return Vector4( this->x * vector.x, this->y * vector.y, this->z * vector.z, this->w * vector.w ); + } + template inline Vector4 & Vector4::Normalize( ) { return (*this) /= this->GetLength(); } From 163481ce4f3e59d5ad960ac9197a34d17ee26666 Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Thu, 23 Jan 2014 18:33:48 +0100 Subject: [PATCH 22/76] Documentation edit Were missing a final step about the ang. momentum toang. velocity conversion documentation. --- .../angular momentum to angular velocity.odt | Bin 174508 -> 201605 bytes .../angular_momentum_to_angular_velocity.pdf | Bin 112800 -> 115170 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Code/Dokumentation/Other/angular momentum to angular velocity.odt b/Code/Dokumentation/Other/angular momentum to angular velocity.odt index 3abd5446fb580dce6e6d257641319a21c934ffc5..9cb227205c1dd9f3cb8867ccba5d73170f01e7ef 100644 GIT binary patch delta 46244 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 SJYW=(eV2!)*$woNdHxR!Bhyj< delta 30719 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 diff --git a/Code/Dokumentation/Other/angular_momentum_to_angular_velocity.pdf b/Code/Dokumentation/Other/angular_momentum_to_angular_velocity.pdf index c6facc1e14d758e8a850a2c8b4531bc4e49e8f8b..820bf412beb25d03ce78c3d20a5433a52c2a7e77 100644 GIT binary patch delta 36632 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 chn>s*5mCn?J_`sW;cDT+a?BbSmc!K1_-hN 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 Date: Thu, 23 Jan 2014 18:34:48 +0100 Subject: [PATCH 23/76] Vector::PiecewiseMultiplicationAdd and a few other minor changes in the math lib --- Code/OysterMath/LinearMath.h | 18 +++++----- Code/OysterMath/Matrix.h | 24 +++++++++++++ Code/OysterMath/OysterMath.h | 44 +++++------------------- Code/OysterMath/Vector.h | 65 ++++++++++++++++++++++++++++++++++-- 4 files changed, 105 insertions(+), 46 deletions(-) diff --git a/Code/OysterMath/LinearMath.h b/Code/OysterMath/LinearMath.h index d32ea04f..35adef43 100644 --- a/Code/OysterMath/LinearMath.h +++ b/Code/OysterMath/LinearMath.h @@ -35,7 +35,7 @@ namespace std // x2 template -::LinearAlgebra::Matrix2x2 operator * ( const ::LinearAlgebra::Matrix2x2 &left, const ::LinearAlgebra::Matrix2x2 &right ) +inline ::LinearAlgebra::Matrix2x2 operator * ( const ::LinearAlgebra::Matrix2x2 &left, const ::LinearAlgebra::Matrix2x2 &right ) { return ::LinearAlgebra::Matrix2x2( (left.m11 * right.m11) + (left.m12 * right.m21), (left.m11 * right.m12) + (left.m12 * right.m22), @@ -44,14 +44,14 @@ template } template -::LinearAlgebra::Vector2 operator * ( const ::LinearAlgebra::Matrix2x2 &matrix, const ::LinearAlgebra::Vector2 &vector ) +inline ::LinearAlgebra::Vector2 operator * ( const ::LinearAlgebra::Matrix2x2 &matrix, const ::LinearAlgebra::Vector2 &vector ) { return ::LinearAlgebra::Vector2( (matrix.m11 * vector.x) + (matrix.m12 * vector.y), (matrix.m21 * vector.x) + (matrix.m22 * vector.y) ); } template -::LinearAlgebra::Vector2 operator * ( const ::LinearAlgebra::Vector2 &vector, const ::LinearAlgebra::Matrix2x2 &left ) +inline ::LinearAlgebra::Vector2 operator * ( const ::LinearAlgebra::Vector2 &vector, const ::LinearAlgebra::Matrix2x2 &left ) { return ::LinearAlgebra::Vector2( (vector.x * matrix.m11) + (vector.y * matrix.m21), (vector.x * matrix.m12) + (vector.y * matrix.m22) ); @@ -60,7 +60,7 @@ template // x3 template -::LinearAlgebra::Matrix3x3 operator * ( const ::LinearAlgebra::Matrix3x3 &left, const ::LinearAlgebra::Matrix3x3 &right ) +inline ::LinearAlgebra::Matrix3x3 operator * ( const ::LinearAlgebra::Matrix3x3 &left, const ::LinearAlgebra::Matrix3x3 &right ) { return ::LinearAlgebra::Matrix3x3( (left.m11 * right.m11) + (left.m12 * right.m21) + (left.m13 * right.m31), (left.m11 * right.m12) + (left.m12 * right.m22) + (left.m13 * right.m32), (left.m11 * right.m13) + (left.m12 * right.m23) + (left.m13 * right.m33), (left.m21 * right.m11) + (left.m22 * right.m21) + (left.m23 * right.m31), (left.m21 * right.m12) + (left.m22 * right.m22) + (left.m23 * right.m32), (left.m21 * right.m13) + (left.m22 * right.m23) + (left.m23 * right.m33), @@ -68,7 +68,7 @@ template } template -::LinearAlgebra::Vector3 operator * ( const ::LinearAlgebra::Matrix3x3 &matrix, const ::LinearAlgebra::Vector3 &vector ) +inline ::LinearAlgebra::Vector3 operator * ( const ::LinearAlgebra::Matrix3x3 &matrix, const ::LinearAlgebra::Vector3 &vector ) { return ::LinearAlgebra::Vector3( (matrix.m11 * vector.x) + (matrix.m12 * vector.y) + (matrix.m13 * vector.z), (matrix.m21 * vector.x) + (matrix.m22 * vector.y) + (matrix.m23 * vector.z), @@ -76,7 +76,7 @@ template } template -::LinearAlgebra::Vector3 operator * ( const ::LinearAlgebra::Vector3 &vector, const ::LinearAlgebra::Matrix3x3 &left ) +inline ::LinearAlgebra::Vector3 operator * ( const ::LinearAlgebra::Vector3 &vector, const ::LinearAlgebra::Matrix3x3 &left ) { return ::LinearAlgebra::Vector3( (vector.x * matrix.m11) + (vector.y * matrix.m21) + (vector.z * matrix.m31), (vector.x * matrix.m12) + (vector.y * matrix.m22) + (vector.z * matrix.m32), @@ -86,7 +86,7 @@ template // x4 template -::LinearAlgebra::Matrix4x4 operator * ( const ::LinearAlgebra::Matrix4x4 &left, const ::LinearAlgebra::Matrix4x4 &right ) +inline ::LinearAlgebra::Matrix4x4 operator * ( const ::LinearAlgebra::Matrix4x4 &left, const ::LinearAlgebra::Matrix4x4 &right ) { return ::LinearAlgebra::Matrix4x4( (left.m11 * right.m11) + (left.m12 * right.m21) + (left.m13 * right.m31) + (left.m14 * right.m41), (left.m11 * right.m12) + (left.m12 * right.m22) + (left.m13 * right.m32) + (left.m14 * right.m42), (left.m11 * right.m13) + (left.m12 * right.m23) + (left.m13 * right.m33) + (left.m14 * right.m43), (left.m11 * right.m14) + (left.m12 * right.m24) + (left.m13 * right.m34) + (left.m14 * right.m44), (left.m21 * right.m11) + (left.m22 * right.m21) + (left.m23 * right.m31) + (left.m24 * right.m41), (left.m21 * right.m12) + (left.m22 * right.m22) + (left.m23 * right.m32) + (left.m24 * right.m42), (left.m21 * right.m13) + (left.m22 * right.m23) + (left.m23 * right.m33) + (left.m24 * right.m43), (left.m21 * right.m14) + (left.m22 * right.m24) + (left.m23 * right.m34) + (left.m24 * right.m44), @@ -95,7 +95,7 @@ template } template -::LinearAlgebra::Vector4 operator * ( const ::LinearAlgebra::Matrix4x4 &matrix, const ::LinearAlgebra::Vector4 &vector ) +inline ::LinearAlgebra::Vector4 operator * ( const ::LinearAlgebra::Matrix4x4 &matrix, const ::LinearAlgebra::Vector4 &vector ) { return ::LinearAlgebra::Vector4( (matrix.m11 * vector.x) + (matrix.m12 * vector.y) + (matrix.m13 * vector.z) + (matrix.m14 * vector.w), (matrix.m21 * vector.x) + (matrix.m22 * vector.y) + (matrix.m23 * vector.z) + (matrix.m24 * vector.w), @@ -104,7 +104,7 @@ template } template -::LinearAlgebra::Vector4 operator * ( const ::LinearAlgebra::Vector4 &vector, const ::LinearAlgebra::Matrix4x4 &matrix ) +inline ::LinearAlgebra::Vector4 operator * ( const ::LinearAlgebra::Vector4 &vector, const ::LinearAlgebra::Matrix4x4 &matrix ) { return ::LinearAlgebra::Vector4( (vector.x * matrix.m11) + (vector.y * matrix.m21) + (vector.z * matrix.m31) + (vector.w * matrix.m41), (vector.x * matrix.m12) + (vector.y * matrix.m22) + (vector.z * matrix.m32) + (vector.w * matrix.m42), diff --git a/Code/OysterMath/Matrix.h b/Code/OysterMath/Matrix.h index c7b6b9e0..5d129431 100644 --- a/Code/OysterMath/Matrix.h +++ b/Code/OysterMath/Matrix.h @@ -163,12 +163,18 @@ namespace LinearAlgebra Vector4 GetRowVector( unsigned int rowID ) const; const Vector4 & GetColumnVector( unsigned int colID ) const; }; +} +template LinearAlgebra::Matrix2x2 operator * ( const ScalarType &left, const LinearAlgebra::Matrix2x2 &right ); +template LinearAlgebra::Matrix3x3 operator * ( const ScalarType &left, const LinearAlgebra::Matrix3x3 &right ); +template LinearAlgebra::Matrix4x4 operator * ( const ScalarType &left, const LinearAlgebra::Matrix4x4 &right ); /////////////////////////////////////////////////////////////////////////////////// // Body /////////////////////////////////////////////////////////////////////////////////// +namespace LinearAlgebra +{ // Matrix2x2 /////////////////////////////////////// template @@ -753,4 +759,22 @@ namespace LinearAlgebra { return this->v[colID]; } } +template +inline LinearAlgebra::Matrix2x2 operator * ( const ScalarType &left, const LinearAlgebra::Matrix2x2 &right ) +{ + return right * left; +} + +template +inline LinearAlgebra::Matrix3x3 operator * ( const ScalarType &left, const LinearAlgebra::Matrix3x3 &right ) +{ + return right * left; +} + +template +inline LinearAlgebra::Matrix4x4 operator * ( const ScalarType &left, const LinearAlgebra::Matrix4x4 &right ) +{ + return right * left; +} + #endif \ No newline at end of file diff --git a/Code/OysterMath/OysterMath.h b/Code/OysterMath/OysterMath.h index dd4655c4..ef6dc65e 100644 --- a/Code/OysterMath/OysterMath.h +++ b/Code/OysterMath/OysterMath.h @@ -61,55 +61,29 @@ namespace Oyster { namespace Math //! Oyster's native math library inline ::Oyster::Math::Float2 & operator *= ( ::Oyster::Math::Float2 &left, const ::Oyster::Math::Float2 &right ) { - left.x *= right.x; - left.y *= right.y; - return left; + return left.PiecewiseMultiplicationAdd( right ); } inline ::Oyster::Math::Float2 operator * ( const ::Oyster::Math::Float2 &left, const ::Oyster::Math::Float2 &right ) -{ return left.PiecewiseMultiplication( right; } - -inline ::Oyster::Math::Float2 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float2 &right ) -{ return ::Oyster::Math::Float2(right) *= left; } +{ + return left.PiecewiseMultiplication( right ); +} inline ::Oyster::Math::Float3 & operator *= ( ::Oyster::Math::Float3 &left, const ::Oyster::Math::Float3 &right ) { - left.x *= right.x; - left.y *= right.y; - left.z *= right.z; - return left; + return left.PiecewiseMultiplicationAdd( right ); } inline ::Oyster::Math::Float3 operator * ( const ::Oyster::Math::Float3 &left, const ::Oyster::Math::Float3 &right ) -{ return left.PiecewiseMultiplication( right ); } - -inline ::Oyster::Math::Float3 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float3 &right ) -{ return ::Oyster::Math::Float3(right) *= left; } +{ + return left.PiecewiseMultiplication( right ); +} inline ::Oyster::Math::Float4 & operator *= ( ::Oyster::Math::Float4 &left, const ::Oyster::Math::Float4 &right ) { - left.x *= right.x; - left.y *= right.y; - left.z *= right.z; - left.w *= right.w; - return left; + return left.PiecewiseMultiplicationAdd( right ); } -inline ::Oyster::Math::Float4 operator * ( const ::Oyster::Math::Float4 &left, const ::Oyster::Math::Float4 &right ) -{ return left.PiecewiseMultiplication( right ); } - -inline ::Oyster::Math::Float4 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float4 &right ) -{ return ::Oyster::Math::Float4(right) *= left; } - -inline ::Oyster::Math::Float2x2 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float2x2 &right ) -{ return ::Oyster::Math::Float2x2(right) *= left; } - -inline ::Oyster::Math::Float3x3 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float3x3 &right ) -{ return ::Oyster::Math::Float3x3(right) *= left; } - -inline ::Oyster::Math::Float4x4 operator * ( const ::Oyster::Math::Float &left, const ::Oyster::Math::Float4x4 &right ) -{ return ::Oyster::Math::Float4x4(right) *= left; } - namespace Oyster { namespace Math2D //! Oyster's native math library specialized for 2D { using namespace ::Oyster::Math; // deliberate inheritance from ::Oyster::Math namespace diff --git a/Code/OysterMath/Vector.h b/Code/OysterMath/Vector.h index d6ab69d9..ea36cfc9 100644 --- a/Code/OysterMath/Vector.h +++ b/Code/OysterMath/Vector.h @@ -58,7 +58,10 @@ namespace LinearAlgebra ScalarType Dot( const Vector2 &vector ) const; //! @return (a.x * b.x, a.y * b.y) - Vector2 PiecewiseMultiplication( const Vector2 &vector ) const; + Vector2 PiecewiseMultiplication( const Vector2 &vector ) const; + + //! @return a = (a.x * b.x, a.y * b.y) + Vector2 & PiecewiseMultiplicationAdd( const Vector2 &vector ); Vector2 & Normalize( ); Vector2 GetNormalized( ) const; @@ -118,6 +121,9 @@ namespace LinearAlgebra //! @return (a.x * b.x, a.y * b.y, a.z * b.z) Vector3 PiecewiseMultiplication( const Vector3 &vector ) const; + //! @return a = (a.x * b.x, a.y * b.y, a.z * b.z) + Vector3 & PiecewiseMultiplicationAdd( const Vector3 &vector ); + Vector3 & Normalize( ); Vector3 GetNormalized( ) const; }; @@ -175,17 +181,27 @@ namespace LinearAlgebra ScalarType GetMagnitude( ) const; ScalarType Dot( const Vector4 &vector ) const; - //! @return (a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w ) + //! @return (a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w) Vector4 PiecewiseMultiplication( const Vector4 &vector ) const; + //! @return a = (a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w) + Vector4 & PiecewiseMultiplicationAdd( const Vector4 &vector ); + Vector4 & Normalize( ); Vector4 GetNormalized( ) const; }; +} + +template ::LinearAlgebra::Vector2 operator * ( const ScalarType &left, const ::LinearAlgebra::Vector2 &right ); +template ::LinearAlgebra::Vector3 operator * ( const ScalarType &left, const ::LinearAlgebra::Vector3 &right ); +template ::LinearAlgebra::Vector4 operator * ( const ScalarType &left, const ::LinearAlgebra::Vector4 &right ); /////////////////////////////////////////////////////////////////////////////////// // Body /////////////////////////////////////////////////////////////////////////////////// +namespace LinearAlgebra +{ // Vector2 /////////////////////////////////////// template const Vector2 Vector2::null = Vector2( 0, 0 ); @@ -342,6 +358,14 @@ namespace LinearAlgebra return Vector2( this->x * vector.x, this->y * vector.y ); } + template + inline Vector2 & Vector2::PiecewiseMultiplicationAdd( const Vector2 &vector ) + { + this->x *= vector.x; + this->y *= vector.y; + return *this; + } + template inline Vector2 & Vector2::Normalize( ) { return (*this) /= this->GetLength(); } @@ -520,6 +544,15 @@ namespace LinearAlgebra return Vector3( this->x * vector.x, this->y * vector.y, this->z * vector.z ); } + template + inline Vector3 & Vector3::PiecewiseMultiplicationAdd( const Vector3 &vector ) + { + this->x *= vector.x; + this->y *= vector.y; + this->z *= vector.z; + return *this; + } + template inline Vector3 & Vector3::Normalize( ) { return (*this) /= this->GetLength(); } @@ -704,6 +737,16 @@ namespace LinearAlgebra return Vector4( this->x * vector.x, this->y * vector.y, this->z * vector.z, this->w * vector.w ); } + template + inline Vector4 & Vector4::PiecewiseMultiplicationAdd( const Vector4 &vector ) + { + this->x *= vector.x; + this->y *= vector.y; + this->z *= vector.z; + this->w *= vector.w; + return *this; + } + template inline Vector4 & Vector4::Normalize( ) { return (*this) /= this->GetLength(); } @@ -713,4 +756,22 @@ namespace LinearAlgebra { return Vector4(*this).Normalize(); } } +template +inline ::LinearAlgebra::Vector2 operator * ( const ScalarType &left, const ::LinearAlgebra::Vector2 &right ) +{ + return right * left; +} + +template +inline ::LinearAlgebra::Vector3 operator * ( const ScalarType &left, const ::LinearAlgebra::Vector3 &right ) +{ + return right * left; +} + +template +inline ::LinearAlgebra::Vector4 operator * ( const ScalarType &left, const ::LinearAlgebra::Vector4 &right ) +{ + return right * left; +} + #endif \ No newline at end of file From c3ed5e78acaddcc6640a7b71258e390684b6bb61 Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Thu, 23 Jan 2014 18:45:33 +0100 Subject: [PATCH 24/76] struct ::Oyster::Physics3D::MomentOfInertia --- Code/OysterPhysics3D/Inertia.cpp | 51 ++++++++ Code/OysterPhysics3D/Inertia.h | 119 ++++++++++++++++++ Code/OysterPhysics3D/OysterPhysics3D.vcxproj | 2 + .../OysterPhysics3D.vcxproj.filters | 6 + 4 files changed, 178 insertions(+) create mode 100644 Code/OysterPhysics3D/Inertia.cpp create mode 100644 Code/OysterPhysics3D/Inertia.h diff --git a/Code/OysterPhysics3D/Inertia.cpp b/Code/OysterPhysics3D/Inertia.cpp new file mode 100644 index 00000000..69df0e01 --- /dev/null +++ b/Code/OysterPhysics3D/Inertia.cpp @@ -0,0 +1,51 @@ +/******************************************************************** + * Created by Dan Andersson 2014 + ********************************************************************/ + +#include "Inertia.h" + +using namespace ::Oyster::Math3D; +using namespace ::Oyster::Physics3D; + +MomentOfInertia::MomentOfInertia() +{ + this->rotation = Quaternion::identity; + this->magnitude = Float3( 1.0f ); +} + +MomentOfInertia::MomentOfInertia( const Quaternion &r, const Float3 &m ) +{ + this->rotation = r; + this->magnitude = m; +} + +MomentOfInertia & MomentOfInertia::operator = ( const MomentOfInertia &i ) +{ + this->rotation = i.rotation; + this->magnitude = i.magnitude; + return *this; +} + +Float4 MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, const Float4 &h ) const +{ + return this->CalculateAngularVelocity( externR, h, Float4() ); +} + +Float4 & MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, const Float4 &h, Float4 &targetMem ) const +{ // w = (R * I_R) * I_M^-1 * (R * I_R)^-1 * h + Float4x4 rotation = RotationMatrix( externR ) * RotationMatrix( this->rotation ); + Float4 w = rotation.GetInverse() * h; + return targetMem = rotation * w.PiecewiseMultiplicationAdd( Float4(1.0f / this->magnitude.x, 1.0f / this->magnitude.y, 1.0f / this->magnitude.z, 0.0f) ); +} + +Float4 MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float4 &w ) const +{ + return this->CalculateAngularMomentum( externR, w, Float4() ); +} + +Float4 & MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float4 &w, Float4 &targetMem ) const +{ // h = (R * I_R) * I_M * (R * I_R)^-1 * w + Float4x4 rotation = RotationMatrix( externR ) * RotationMatrix( this->rotation ); + Float4 h = rotation.GetInverse() * w; + return targetMem = rotation * h.PiecewiseMultiplicationAdd( Float4(this->magnitude.x, this->magnitude.y, this->magnitude.z, 0.0f) ); +} \ No newline at end of file diff --git a/Code/OysterPhysics3D/Inertia.h b/Code/OysterPhysics3D/Inertia.h new file mode 100644 index 00000000..c7ffc49f --- /dev/null +++ b/Code/OysterPhysics3D/Inertia.h @@ -0,0 +1,119 @@ +/******************************************************************** + * Created by Dan Andersson & Robin Engman 2014 + ********************************************************************/ + +#ifndef OYSTER_PHYSICS_3D_INERTIA_H +#define OYSTER_PHYSICS_3D_INERTIA_H + +#include "OysterMath.h" + +namespace Oyster { namespace Physics3D +{ + struct MomentOfInertia + { + ::Oyster::Math::Quaternion rotation; + ::Oyster::Math::Float3 magnitude; + + MomentOfInertia(); + MomentOfInertia( const ::Oyster::Math::Quaternion &r, const ::Oyster::Math::Float3 &m ); + + MomentOfInertia & operator = ( const MomentOfInertia &i ); + + ::Oyster::Math::Float4 CalculateAngularVelocity( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularMomentum ) const; + ::Oyster::Math::Float4 & CalculateAngularVelocity( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularMomentum, ::Oyster::Math::Float4 &targetMem ) const; + + ::Oyster::Math::Float4 CalculateAngularMomentum( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularVelocity ) const; + ::Oyster::Math::Float4 & CalculateAngularMomentum( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularVelocity, ::Oyster::Math::Float4 &targetMem ) const; + + static ::Oyster::Math::Float CalculateSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ); + static ::Oyster::Math::Float CalculateHollowSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ); + static ::Oyster::Math::Float CalculateCuboidX( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float depth ); + static ::Oyster::Math::Float CalculateCuboidY( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float width, const ::Oyster::Math::Float depth ); + static ::Oyster::Math::Float CalculateCuboidZ( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float width, const ::Oyster::Math::Float height ); + static ::Oyster::Math::Float CalculateRodCenter( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float length ); + static ::Oyster::Math::Float CalculateCylinderXY( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float radius ); + static ::Oyster::Math::Float CalculateCylinderZ( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ); + + static MomentOfInertia Sphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ); + static MomentOfInertia HollowSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ); + static MomentOfInertia Cuboid( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float width, const ::Oyster::Math::Float depth ); + static MomentOfInertia RodCenter( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float length ); + static MomentOfInertia Cylinder( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float radius ); + }; + + inline ::Oyster::Math::Float MomentOfInertia::CalculateSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ) + { + return (2.0f / 5.0f) * mass * radius * radius; + } + + inline ::Oyster::Math::Float MomentOfInertia::CalculateHollowSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ) + { + return (2.0f / 3.0f) * mass * radius * radius; + } + + inline ::Oyster::Math::Float MomentOfInertia::CalculateCuboidX( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float depth ) + { + return (1.0f / 12.0f) * mass * (height * height + depth * depth); + } + + inline ::Oyster::Math::Float MomentOfInertia::CalculateCuboidY( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float width, const ::Oyster::Math::Float depth ) + { + return (1.0f / 12.0f) * mass * (width * width + depth * depth); + } + + inline ::Oyster::Math::Float MomentOfInertia::CalculateCuboidZ( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float width, const ::Oyster::Math::Float height ) + { + return (1.0f / 12.0f) * mass * (height * height + width * width); + } + + inline ::Oyster::Math::Float MomentOfInertia::CalculateRodCenter( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float length ) + { + return (1.0f / 12.0f) * mass * length * length; + } + + inline ::Oyster::Math::Float MomentOfInertia::CalculateCylinderXY( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float radius ) + { + return (1.0f / 12.0f) * mass * (3.0f * radius * radius + height * height); + } + + inline ::Oyster::Math::Float MomentOfInertia::CalculateCylinderZ( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ) + { + return 0.5f * mass * radius * radius; + } + + inline MomentOfInertia MomentOfInertia::Sphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ) + { + return MomentOfInertia( ::Oyster::Math::Quaternion::identity, + ::Oyster::Math::Float3(MomentOfInertia::CalculateSphere(mass, radius)) ); + } + + inline MomentOfInertia MomentOfInertia::HollowSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ) + { + return MomentOfInertia( ::Oyster::Math::Quaternion::identity, + ::Oyster::Math::Float3(MomentOfInertia::CalculateHollowSphere(mass, radius)) ); + } + + inline MomentOfInertia MomentOfInertia::Cuboid( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float width, const ::Oyster::Math::Float depth ) + { + return MomentOfInertia( ::Oyster::Math::Quaternion::identity, + ::Oyster::Math::Float3(MomentOfInertia::CalculateCuboidX(mass, height, depth), + MomentOfInertia::CalculateCuboidY(mass, width, depth), + MomentOfInertia::CalculateCuboidZ(mass, height, width)) ); + } + + inline MomentOfInertia MomentOfInertia::RodCenter( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float length ) + { + return MomentOfInertia( ::Oyster::Math::Quaternion::identity, + ::Oyster::Math::Float3(MomentOfInertia::CalculateRodCenter(mass , length)) ); + } + + inline MomentOfInertia MomentOfInertia::Cylinder( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float height, const ::Oyster::Math::Float radius ) + { + ::Oyster::Math::Float cylinderXY = MomentOfInertia::CalculateCylinderXY( mass , height, radius ); + return MomentOfInertia( ::Oyster::Math::Quaternion::identity, + ::Oyster::Math::Float3(cylinderXY, cylinderXY, + MomentOfInertia::CalculateCylinderZ(mass, radius)) ); + } +} } + +#endif \ No newline at end of file diff --git a/Code/OysterPhysics3D/OysterPhysics3D.vcxproj b/Code/OysterPhysics3D/OysterPhysics3D.vcxproj index 186ea535..dffeca4f 100644 --- a/Code/OysterPhysics3D/OysterPhysics3D.vcxproj +++ b/Code/OysterPhysics3D/OysterPhysics3D.vcxproj @@ -155,6 +155,7 @@ + @@ -174,6 +175,7 @@ + diff --git a/Code/OysterPhysics3D/OysterPhysics3D.vcxproj.filters b/Code/OysterPhysics3D/OysterPhysics3D.vcxproj.filters index 02919d28..da336ce5 100644 --- a/Code/OysterPhysics3D/OysterPhysics3D.vcxproj.filters +++ b/Code/OysterPhysics3D/OysterPhysics3D.vcxproj.filters @@ -78,6 +78,9 @@ Header Files\Physics + + Header Files\Physics + @@ -125,5 +128,8 @@ Source Files\Physics + + Source Files\Physics + \ No newline at end of file From ff936133fc0849c1cac96a82b4b1bb31403e3bda Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Thu, 23 Jan 2014 19:13:02 +0100 Subject: [PATCH 25/76] Physics engine now using struct MomentOfInertia Some API impact for the other modules. * Desc. structs * State struct --- .../Implementation/SphericalRigidBody.cpp | 2 +- Code/GamePhysics/PhysicsStructs-Impl.h | 21 ++++---- Code/GamePhysics/PhysicsStructs.h | 13 ++--- Code/OysterPhysics3D/RigidBody.cpp | 48 ++++++++----------- Code/OysterPhysics3D/RigidBody.h | 32 +++++++------ 5 files changed, 55 insertions(+), 61 deletions(-) diff --git a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp index 9fd8d219..bb497851 100644 --- a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp +++ b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp @@ -28,7 +28,7 @@ SphericalRigidBody::SphericalRigidBody( const API::SphericalBodyDescription &des this->rigid.centerPos = desc.centerPosition; this->rigid.boundingReach = Float4( desc.radius, desc.radius, desc.radius, 0.0f ); this->rigid.SetMass_KeepMomentum( desc.mass ); - this->rigid.SetMomentOfInertia_KeepMomentum( Formula::MomentOfInertia::CreateSphereMatrix( desc.mass, desc.radius ) ); + this->rigid.SetMomentOfInertia_KeepMomentum( MomentOfInertia::Sphere(desc.mass, desc.radius) ); this->deltaPos = Float4::null; this->deltaAxis = Float4::null; diff --git a/Code/GamePhysics/PhysicsStructs-Impl.h b/Code/GamePhysics/PhysicsStructs-Impl.h index 46de90cc..8918ab98 100644 --- a/Code/GamePhysics/PhysicsStructs-Impl.h +++ b/Code/GamePhysics/PhysicsStructs-Impl.h @@ -19,7 +19,7 @@ namespace Oyster this->restitutionCoeff = 1.0f; this->frictionCoeff_Dynamic = 0.5f; this->frictionCoeff_Static = 0.5f; - this->inertiaTensor = ::Oyster::Math::Float4x4::identity; + this->inertiaTensor = ::Oyster::Physics3D::MomentOfInertia(); this->subscription_onCollision = NULL; this->subscription_onCollisionResponse = NULL; this->subscription_onMovement = NULL; @@ -41,7 +41,7 @@ namespace Oyster this->ignoreGravity = false; } - inline CustomBodyState::CustomBodyState( ::Oyster::Math::Float mass, ::Oyster::Math::Float restitutionCoeff, ::Oyster::Math::Float staticFrictionCoeff, ::Oyster::Math::Float kineticFrictionCoeff, const ::Oyster::Math::Float4x4 &inertiaTensor, const ::Oyster::Math::Float4 &reach, const ::Oyster::Math::Float4 ¢erPos, const ::Oyster::Math::Float4 &rotation, const ::Oyster::Math::Float4 &linearMomentum, const ::Oyster::Math::Float4 &angularMomentum, const ::Oyster::Math::Float4 &gravityNormal ) + inline CustomBodyState::CustomBodyState( ::Oyster::Math::Float mass, ::Oyster::Math::Float restitutionCoeff, ::Oyster::Math::Float staticFrictionCoeff, ::Oyster::Math::Float kineticFrictionCoeff, const ::Oyster::Physics3D::MomentOfInertia &inertiaTensor, const ::Oyster::Math::Float4 &reach, const ::Oyster::Math::Float4 ¢erPos, const ::Oyster::Math::Float4 &rotation, const ::Oyster::Math::Float4 &linearMomentum, const ::Oyster::Math::Float4 &angularMomentum, const ::Oyster::Math::Float4 &gravityNormal ) { this->mass = mass; this->restitutionCoeff = restitutionCoeff; @@ -102,7 +102,7 @@ namespace Oyster return this->kineticFrictionCoeff; } - inline const ::Oyster::Math::Float4x4 & CustomBodyState::GetMomentOfInertia() const + inline const ::Oyster::Physics3D::MomentOfInertia & CustomBodyState::GetMomentOfInertia() const { return this->inertiaTensor; } @@ -219,20 +219,17 @@ namespace Oyster this->kineticFrictionCoeff = kineticU; } - inline void CustomBodyState::SetMomentOfInertia_KeepMomentum( const ::Oyster::Math::Float4x4 &tensor ) + inline void CustomBodyState::SetMomentOfInertia_KeepMomentum( const ::Oyster::Physics3D::MomentOfInertia &tensor ) { this->inertiaTensor = tensor; } - inline void CustomBodyState::SetMomentOfInertia_KeepVelocity( const ::Oyster::Math::Float4x4 &tensor ) + inline void CustomBodyState::SetMomentOfInertia_KeepVelocity( const ::Oyster::Physics3D::MomentOfInertia &tensor ) { - if( tensor.GetDeterminant() != 0.0f ) - { // sanity block! - ::Oyster::Math::Float4x4 rotation = ::Oyster::Math3D::RotationMatrix(this->angularAxis.xyz); - ::Oyster::Math::Float4 w = ::Oyster::Physics3D::Formula::AngularVelocity( (rotation * this->inertiaTensor).GetInverse(), this->angularMomentum ); - this->inertiaTensor = tensor; - this->angularMomentum = ::Oyster::Physics3D::Formula::AngularMomentum( rotation * tensor, w ); - } + ::Oyster::Math::Quaternion rotation = ::Oyster::Math3D::Rotation(this->angularAxis); + ::Oyster::Math::Float4 w = this->inertiaTensor.CalculateAngularVelocity( rotation, this->angularMomentum ); + this->inertiaTensor = tensor; + this->angularMomentum = this->inertiaTensor.CalculateAngularMomentum( rotation, w ); } inline void CustomBodyState::SetSize( const ::Oyster::Math::Float4 &size ) diff --git a/Code/GamePhysics/PhysicsStructs.h b/Code/GamePhysics/PhysicsStructs.h index 1bc1736f..4eaaf46d 100644 --- a/Code/GamePhysics/PhysicsStructs.h +++ b/Code/GamePhysics/PhysicsStructs.h @@ -3,6 +3,7 @@ #include "OysterMath.h" #include "PhysicsAPI.h" +#include "Inertia.h" namespace Oyster { namespace Physics { @@ -17,7 +18,7 @@ namespace Oyster { namespace Physics ::Oyster::Math::Float restitutionCoeff; ::Oyster::Math::Float frictionCoeff_Static; ::Oyster::Math::Float frictionCoeff_Dynamic; - ::Oyster::Math::Float4x4 inertiaTensor; + ::Oyster::Physics3D::MomentOfInertia inertiaTensor; ::Oyster::Physics::ICustomBody::EventAction_Collision subscription_onCollision; ::Oyster::Physics::ICustomBody::EventAction_CollisionResponse subscription_onCollisionResponse; ::Oyster::Physics::ICustomBody::EventAction_Move subscription_onMovement; @@ -50,7 +51,7 @@ namespace Oyster { namespace Physics ::Oyster::Math::Float restitutionCoeff = 1.0f, ::Oyster::Math::Float staticFrictionCoeff = 1.0f, ::Oyster::Math::Float kineticFrictionCoeff = 1.0f, - const ::Oyster::Math::Float4x4 &inertiaTensor = ::Oyster::Math::Float4x4::identity, + const ::Oyster::Physics3D::MomentOfInertia &inertiaTensor = ::Oyster::Physics3D::MomentOfInertia(), const ::Oyster::Math::Float4 &reach = ::Oyster::Math::Float4::null, const ::Oyster::Math::Float4 ¢erPos = ::Oyster::Math::Float4::standard_unit_w, const ::Oyster::Math::Float4 &rotation = ::Oyster::Math::Float4::null, @@ -64,7 +65,7 @@ namespace Oyster { namespace Physics const ::Oyster::Math::Float GetRestitutionCoeff() const; const ::Oyster::Math::Float GetFrictionCoeff_Static() const; const ::Oyster::Math::Float GetFrictionCoeff_Kinetic() const; - const ::Oyster::Math::Float4x4 & GetMomentOfInertia() const; + const ::Oyster::Physics3D::MomentOfInertia & GetMomentOfInertia() const; const ::Oyster::Math::Float4 & GetReach() const; ::Oyster::Math::Float4 GetSize() const; const ::Oyster::Math::Float4 & GetCenterPosition() const; @@ -87,8 +88,8 @@ namespace Oyster { namespace Physics void SetMass_KeepVelocity( ::Oyster::Math::Float m ); void SetRestitutionCoeff( ::Oyster::Math::Float e ); void SetFrictionCoeff( ::Oyster::Math::Float staticU, ::Oyster::Math::Float kineticU ); - void SetMomentOfInertia_KeepMomentum( const ::Oyster::Math::Float4x4 &tensor ); - void SetMomentOfInertia_KeepVelocity( const ::Oyster::Math::Float4x4 &tensor ); + void SetMomentOfInertia_KeepMomentum( const ::Oyster::Physics3D::MomentOfInertia &tensor ); + void SetMomentOfInertia_KeepVelocity( const ::Oyster::Physics3D::MomentOfInertia &tensor ); void SetSize( const ::Oyster::Math::Float4 &size ); void SetReach( const ::Oyster::Math::Float4 &halfSize ); void SetCenterPosition( const ::Oyster::Math::Float4 ¢erPos ); @@ -115,7 +116,7 @@ namespace Oyster { namespace Physics private: ::Oyster::Math::Float mass, restitutionCoeff, staticFrictionCoeff, kineticFrictionCoeff; - ::Oyster::Math::Float4x4 inertiaTensor; + ::Oyster::Physics3D::MomentOfInertia inertiaTensor; ::Oyster::Math::Float4 reach, centerPos, angularAxis; ::Oyster::Math::Float4 linearMomentum, angularMomentum; ::Oyster::Math::Float4 linearImpulse, angularImpulse; diff --git a/Code/OysterPhysics3D/RigidBody.cpp b/Code/OysterPhysics3D/RigidBody.cpp index 80c800a6..d4da0d00 100644 --- a/Code/OysterPhysics3D/RigidBody.cpp +++ b/Code/OysterPhysics3D/RigidBody.cpp @@ -23,7 +23,7 @@ RigidBody::RigidBody( ) this->frictionCoeff_Static = 0.5f; this->frictionCoeff_Kinetic = 1.0f; this->mass = 10; - this->momentOfInertiaTensor = Float4x4::identity; + this->momentOfInertiaTensor = MomentOfInertia(); this->rotation = Quaternion::identity; } @@ -51,17 +51,18 @@ void RigidBody::Update_LeapFrog( Float updateFrameLength ) // updating the linear // ds = dt * Formula::LinearVelocity( m, avg_G ) = dt * avg_G / m = (dt / m) * avg_G this->centerPos += ( updateFrameLength / this->mass ) * AverageWithDelta( this->momentum_Linear, this->impulse_Linear ); - + // updating the angular - Float4x4 rotationMatrix; ::Oyster::Math3D::RotationMatrix( this->rotation, rotationMatrix ); + //Float4x4 rotationMatrix; ::Oyster::Math3D::RotationMatrix( this->rotation, rotationMatrix ); // Important! The member data is all world data except the Inertia tensor. Thus a new InertiaTensor needs to be created to be compatible with the rest of the world data. - Float4x4 wMomentOfInertiaTensor = TransformMatrix( rotationMatrix, this->momentOfInertiaTensor ); // RI + //Float4x4 wMomentOfInertiaTensor = TransformMatrix( rotationMatrix, this->momentOfInertiaTensor ); // RI // dO = dt * Formula::AngularVelocity( (RI)^-1, avg_H ) = dt * (RI)^-1 * avg_H //! HACK: @todo Rotation temporary disabled //this->axis += Radian( Formula::AngularVelocity(wMomentOfInertiaTensor.GetInverse(), AverageWithDelta(this->momentum_Angular, this->impulse_Angular)) ); - //this->rotation = Rotation( this->axis ); + this->axis += this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, AverageWithDelta(this->momentum_Angular, this->impulse_Angular) ); + this->rotation = Rotation( this->axis ); // update momentums and clear impulse_Linear and impulse_Angular this->momentum_Linear += this->impulse_Linear; @@ -78,11 +79,12 @@ void RigidBody::Predict_LeapFrog( Float4 &outDeltaPos, Float4 &outDeltaAxis, con outDeltaPos = ( deltaTime / this->mass ) * AverageWithDelta( this->momentum_Linear, actingLinearImpulse ); // updating the angular - Float4x4 rotationMatrix; ::Oyster::Math3D::RotationMatrix( this->rotation, rotationMatrix ); - Float4x4 wMomentOfInertiaTensor = TransformMatrix( rotationMatrix, this->momentOfInertiaTensor ); // RI + //Float4x4 rotationMatrix; ::Oyster::Math3D::RotationMatrix( this->rotation, rotationMatrix ); + //Float4x4 wMomentOfInertiaTensor = TransformMatrix( rotationMatrix, this->momentOfInertiaTensor ); // RI // dO = dt * Formula::AngularVelocity( (RI)^-1, avg_H ) = dt * (RI)^-1 * avg_H - outDeltaAxis = Formula::AngularVelocity( wMomentOfInertiaTensor.GetInverse(), AverageWithDelta(this->momentum_Angular, actingAngularImpulse) ); + //outDeltaAxis = Formula::AngularVelocity( wMomentOfInertiaTensor.GetInverse(), AverageWithDelta(this->momentum_Angular, actingAngularImpulse) ); + outDeltaAxis = this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, AverageWithDelta(this->momentum_Angular, this->impulse_Angular) ); } void RigidBody::Move( const Float4 &deltaPos, const Float4 &deltaAxis ) @@ -106,7 +108,7 @@ void RigidBody::ApplyImpulse( const Float4 &worldJ, const Float4 &atWorldPos ) } } -const Float4x4 & RigidBody::GetMomentOfInertia() const +const MomentOfInertia & RigidBody::GetMomentOfInertia() const { // by Dan Andersson return this->momentOfInertiaTensor; } @@ -143,7 +145,7 @@ Float4 RigidBody::GetVelocity_Linear() const Float4 RigidBody::GetVelocity_Angular() const { // by Dan Andersson - return Formula::AngularVelocity( this->momentOfInertiaTensor.GetInverse(), this->momentum_Angular ); + return this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, this->momentum_Angular ); } Float4 RigidBody::GetLinearMomentum( const Float4 &atWorldPos ) const @@ -151,24 +153,16 @@ Float4 RigidBody::GetLinearMomentum( const Float4 &atWorldPos ) const return this->momentum_Linear + Formula::TangentialLinearMomentum( this->momentum_Angular, atWorldPos - this->centerPos ); } -void RigidBody::SetMomentOfInertia_KeepVelocity( const Float4x4 &localTensorI ) +void RigidBody::SetMomentOfInertia_KeepVelocity( const MomentOfInertia &localTensorI ) { // by Dan Andersson - if( localTensorI.GetDeterminant() != 0.0f ) - { // insanity check! MomentOfInertiaTensor must be invertable - Float4x4 rotationMatrix; RotationMatrix( this->rotation, rotationMatrix ); - - Float4 w = Formula::AngularVelocity( (rotationMatrix * this->momentOfInertiaTensor).GetInverse(), this->momentum_Angular ); - this->momentOfInertiaTensor = localTensorI; - this->momentum_Angular = Formula::AngularMomentum( rotationMatrix * localTensorI, w ); - } + Float4 w = this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, this->momentum_Angular ); + this->momentOfInertiaTensor = localTensorI; + this->momentum_Angular = this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, w ); } -void RigidBody::SetMomentOfInertia_KeepMomentum( const Float4x4 &localTensorI ) +void RigidBody::SetMomentOfInertia_KeepMomentum( const MomentOfInertia &localTensorI ) { // by Dan Andersson - if( localTensorI.GetDeterminant() != 0.0f ) - { // insanity check! MomentOfInertiaTensor must be invertable - this->momentOfInertiaTensor = localTensorI; - } + this->momentOfInertiaTensor = localTensorI; } void RigidBody::SetMass_KeepVelocity( const Float &m ) @@ -217,13 +211,13 @@ void RigidBody::SetVelocity_Linear( const Float4 &worldV ) void RigidBody::SetVelocity_Linear( const Float4 &worldV, const Float4 &atWorldPos ) { // by Dan Andersson Float4 worldOffset = atWorldPos - this->centerPos; - this->momentum_Linear = Formula::LinearMomentum( this->mass, VectorProjection(worldV, worldOffset) ); - this->momentum_Angular = Formula::AngularMomentum( RotationMatrix(this->rotation) * this->momentOfInertiaTensor, Formula::AngularVelocity(worldV, worldOffset) ); + this->momentum_Linear = Formula::LinearMomentum( this->mass, VectorProjection(worldV, worldOffset) ); + this->momentum_Angular = this->momentOfInertiaTensor.CalculateAngularMomentum( this->rotation, Formula::AngularVelocity(worldV, worldOffset) ); } void RigidBody::SetVelocity_Angular( const Float4 &worldW ) { // by Dan Andersson - this->momentum_Angular = Formula::AngularMomentum( this->momentOfInertiaTensor, worldW ); + this->momentum_Angular = this->momentOfInertiaTensor.CalculateAngularMomentum( this->rotation, worldW ); } void RigidBody::SetImpulse_Linear( const Float4 &worldJ, const Float4 &atWorldPos ) diff --git a/Code/OysterPhysics3D/RigidBody.h b/Code/OysterPhysics3D/RigidBody.h index 51c5d2d8..ad619180 100644 --- a/Code/OysterPhysics3D/RigidBody.h +++ b/Code/OysterPhysics3D/RigidBody.h @@ -8,6 +8,7 @@ #include "OysterMath.h" #include "OysterCollision3D.h" #include "OysterPhysics3D.h" +#include "Inertia.h" namespace Oyster { namespace Physics3D { @@ -42,23 +43,23 @@ namespace Oyster { namespace Physics3D // GET METHODS //////////////////////////////// - const ::Oyster::Math::Float4x4 & GetMomentOfInertia() const; - ::Oyster::Math::Float GetMass() const; - const ::Oyster::Math::Quaternion & GetRotation() const; - ::Oyster::Math::Float4x4 GetRotationMatrix() const; - ::Oyster::Math::Float4x4 GetOrientation() const; - ::Oyster::Math::Float4x4 GetView() const; - ::Oyster::Math::Float4x4 GetToWorldMatrix() const; - ::Oyster::Math::Float4x4 GetToLocalMatrix() const; - ::Oyster::Math::Float4 GetSize() const; - ::Oyster::Math::Float4 GetVelocity_Linear() const; - ::Oyster::Math::Float4 GetVelocity_Angular() const; - ::Oyster::Math::Float4 GetLinearMomentum( const ::Oyster::Math::Float4 &atWorldPos ) const; + const ::Oyster::Physics3D::MomentOfInertia & GetMomentOfInertia() const; + ::Oyster::Math::Float GetMass() const; + const ::Oyster::Math::Quaternion & GetRotation() const; + ::Oyster::Math::Float4x4 GetRotationMatrix() const; + ::Oyster::Math::Float4x4 GetOrientation() const; + ::Oyster::Math::Float4x4 GetView() const; + ::Oyster::Math::Float4x4 GetToWorldMatrix() const; + ::Oyster::Math::Float4x4 GetToLocalMatrix() const; + ::Oyster::Math::Float4 GetSize() const; + ::Oyster::Math::Float4 GetVelocity_Linear() const; + ::Oyster::Math::Float4 GetVelocity_Angular() const; + ::Oyster::Math::Float4 GetLinearMomentum( const ::Oyster::Math::Float4 &atWorldPos ) const; // SET METHODS //////////////////////////////// - void SetMomentOfInertia_KeepVelocity( const ::Oyster::Math::Float4x4 &localTensorI ); - void SetMomentOfInertia_KeepMomentum( const ::Oyster::Math::Float4x4 &localTensorI ); + void SetMomentOfInertia_KeepVelocity( const ::Oyster::Physics3D::MomentOfInertia &localTensorI ); + void SetMomentOfInertia_KeepMomentum( const ::Oyster::Physics3D::MomentOfInertia &localTensorI ); void SetMass_KeepVelocity( const ::Oyster::Math::Float &m ); void SetMass_KeepMomentum( const ::Oyster::Math::Float &m ); @@ -78,7 +79,8 @@ namespace Oyster { namespace Physics3D private: ::Oyster::Math::Float mass; //!< m (kg) - ::Oyster::Math::Float4x4 momentOfInertiaTensor; //!< I (Nm*s) Tensor matrix ( only need to be 3x3 matrix, but is 4x4 for future hardware acceleration ) (localValue) + //::Oyster::Math::Float4x4 momentOfInertiaTensor; //!< I (Nm*s) Tensor matrix ( only need to be 3x3 matrix, but is 4x4 for future hardware acceleration ) (localValue) + ::Oyster::Physics3D::MomentOfInertia momentOfInertiaTensor; ::Oyster::Math::Quaternion rotation; //!< RotationAxis of the body. }; } } From 0f5517398dbb4b72c5395d13b918fcc8d5acdf11 Mon Sep 17 00:00:00 2001 From: Pontus Fransson Date: Fri, 24 Jan 2014 09:00:59 +0100 Subject: [PATCH 26/76] GL - Update uml, LevelParser.h.cpp ObjectDefines.h --- Code/Dokumentation/LevelLoader.uxf | 46 +++++++++++++-------------- Code/Game/GameLogic/GameLogic.vcxproj | 3 ++ Code/Game/GameLogic/LevelParser.cpp | 2 ++ Code/Game/GameLogic/LevelParser.h | 28 ++++++++++++++++ Code/Game/GameLogic/ObjectDefines.h | 38 ++++++++++++++++++++++ 5 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 Code/Game/GameLogic/LevelParser.cpp create mode 100644 Code/Game/GameLogic/LevelParser.h create mode 100644 Code/Game/GameLogic/ObjectDefines.h diff --git a/Code/Dokumentation/LevelLoader.uxf b/Code/Dokumentation/LevelLoader.uxf index 723857bb..fd3e9c79 100644 --- a/Code/Dokumentation/LevelLoader.uxf +++ b/Code/Dokumentation/LevelLoader.uxf @@ -1,11 +1,11 @@ - + 8 com.umlet.element.Package 552 - 320 + 360 584 368 @@ -16,7 +16,7 @@ com.umlet.element.Class 440 - 88 + 128 128 40 @@ -28,7 +28,7 @@ com.umlet.element.Relation 768 - 472 + 512 136 104 @@ -39,12 +39,12 @@ com.umlet.element.Class 560 - 544 + 584 232 136 <<Interface>> -Parser +LevelParser -- Functions: vector<struct> Parse(); @@ -60,7 +60,7 @@ const int FileVersion; com.umlet.element.Class 624 - 208 + 248 80 24 @@ -72,7 +72,7 @@ const int FileVersion; com.umlet.element.Relation 640 - 208 + 248 40 168 @@ -83,7 +83,7 @@ const int FileVersion; com.umlet.element.Relation 384 - 176 + 216 256 56 @@ -97,7 +97,7 @@ m2=1..1 com.umlet.element.Package 248 - 320 + 360 248 160 @@ -108,7 +108,7 @@ m2=1..1 com.umlet.element.Class 800 - 360 + 400 208 136 @@ -127,7 +127,7 @@ Privates: com.umlet.element.Class 328 - 208 + 248 80 24 @@ -138,11 +138,11 @@ Privates: com.umlet.element.Class 256 - 360 + 400 232 104 - Defines.h + ObjectDefines.h <<Header file>> -- Enum ObjectType(static, dynamic, specials); @@ -156,7 +156,7 @@ Struct specials com.umlet.element.Relation 680 - 176 + 216 152 56 @@ -170,7 +170,7 @@ Uses> com.umlet.element.Class 816 - 192 + 232 128 40 @@ -182,7 +182,7 @@ Uses> com.umlet.element.Class 928 - 560 + 600 200 120 @@ -196,7 +196,7 @@ functions for creating the right structs com.umlet.element.Relation 768 - 576 + 616 176 56 @@ -210,7 +210,7 @@ Uses> com.umlet.element.Class 560 - 360 + 400 232 136 @@ -230,7 +230,7 @@ Privates: com.umlet.element.Relation 344 - 208 + 248 40 168 @@ -241,7 +241,7 @@ Privates: com.umlet.element.Relation 840 - 208 + 248 88 168 @@ -253,7 +253,7 @@ Privates: com.umlet.element.Relation 656 - 472 + 512 40 88 @@ -264,7 +264,7 @@ Privates: com.umlet.element.Relation 544 - 64 + 104 136 160 diff --git a/Code/Game/GameLogic/GameLogic.vcxproj b/Code/Game/GameLogic/GameLogic.vcxproj index 6b66a9d5..da052b70 100644 --- a/Code/Game/GameLogic/GameLogic.vcxproj +++ b/Code/Game/GameLogic/GameLogic.vcxproj @@ -185,6 +185,8 @@ + + @@ -202,6 +204,7 @@ + diff --git a/Code/Game/GameLogic/LevelParser.cpp b/Code/Game/GameLogic/LevelParser.cpp new file mode 100644 index 00000000..65b63aef --- /dev/null +++ b/Code/Game/GameLogic/LevelParser.cpp @@ -0,0 +1,2 @@ +#include "LevelParser.h" + diff --git a/Code/Game/GameLogic/LevelParser.h b/Code/Game/GameLogic/LevelParser.h new file mode 100644 index 00000000..f1904d9c --- /dev/null +++ b/Code/Game/GameLogic/LevelParser.h @@ -0,0 +1,28 @@ +#ifndef LEVEL_PARSER_H +#define LEVEL_PARSER_H + +#include +#include "ObjectDefines.h" + +namespace GameLogic +{ + namespace LevelLoader + { + class LevelParser + { + public: + LevelParser(); + ~LevelParser(); + + // + std::vector Parse(); + + // + ObjectTypeHeader ParseHeader(); + + private: + + }; + } +} +#endif \ No newline at end of file diff --git a/Code/Game/GameLogic/ObjectDefines.h b/Code/Game/GameLogic/ObjectDefines.h new file mode 100644 index 00000000..74bca42c --- /dev/null +++ b/Code/Game/GameLogic/ObjectDefines.h @@ -0,0 +1,38 @@ +#ifndef OBJECT_DEFINES_H +#define OBJECT_DEFINES_H + +namespace GameLogic +{ + enum ObjectType + { + ObjectType_Static, + ObjectType_Dynamic, + + + ObjectType_NUM_OF_TYPES, + + ObjectType_Unknow = -1, + }; + + struct ObjectTypeHeader + { + ObjectType typeID; + + }; + + struct ObjectHeader : public ObjectTypeHeader + { + //Model, + + //Texture + + //Position + float position[3]; + //Rotation + float rotation[3]; + //Scale + float scale[3]; + }; +} + +#endif \ No newline at end of file From aaf3bf30f9a19644dc6480cbcc3d0097dec073fd Mon Sep 17 00:00:00 2001 From: Pontus Fransson Date: Fri, 24 Jan 2014 10:01:58 +0100 Subject: [PATCH 27/76] GL - LevelParser half way implemented. --- Code/Game/GameLogic/LevelParser.cpp | 80 +++++++++++++++++++++++++++++ Code/Game/GameLogic/LevelParser.h | 27 +++++++++- Code/Game/GameLogic/ObjectDefines.h | 3 +- 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/Code/Game/GameLogic/LevelParser.cpp b/Code/Game/GameLogic/LevelParser.cpp index 65b63aef..21e6c020 100644 --- a/Code/Game/GameLogic/LevelParser.cpp +++ b/Code/Game/GameLogic/LevelParser.cpp @@ -1,2 +1,82 @@ #include "LevelParser.h" +using namespace GameLogic; +using namespace ::LevelLoader; + +LevelParser::LevelParser() +{ +} + +LevelParser::~LevelParser() +{ +} + +// +std::vector LevelParser::Parse(std::string filename) +{ + //Read entire level file. + + std::vector objects; + + unsigned int counter = 0; + unsigned int stringSize = 0; + while(counter < stringSize) + { + //Get typeID + int typeID = 0; + + + switch(typeID) + { + case TypeID_LevelHeader: + //Call function + counter += LevelHeaderSize; + break; + + case TypeID_Object: + //Call function + counter += ObjectHeaderSize; + break; + default: + //Couldn't find typeID. FAIL!!!!!! + break; + } + } + + return objects; +} + +// +ObjectTypeHeader LevelParser::ParseHeader(std::string filename) +{ + //Read entire level file. + + //Find the header in the returned string. + unsigned int counter = 0; + unsigned int stringSize = 0; + + ObjectTypeHeader header; + header.typeID = ObjectType_Level; + + while(counter < stringSize) + { + int typeID = 0; + switch(typeID) + { + case TypeID_LevelHeader: + //Call function + + break; + case TypeID_Object: + //Call function + counter += ObjectHeaderSize; + break; + + default: + //Couldn't find typeID. FAIL!!!!!! + break; + } + } + + return header; +} \ No newline at end of file diff --git a/Code/Game/GameLogic/LevelParser.h b/Code/Game/GameLogic/LevelParser.h index f1904d9c..f261004b 100644 --- a/Code/Game/GameLogic/LevelParser.h +++ b/Code/Game/GameLogic/LevelParser.h @@ -1,6 +1,7 @@ #ifndef LEVEL_PARSER_H #define LEVEL_PARSER_H +#include #include #include "ObjectDefines.h" @@ -15,12 +16,34 @@ namespace GameLogic ~LevelParser(); // - std::vector Parse(); + std::vector Parse(std::string filename); // - ObjectTypeHeader ParseHeader(); + ObjectTypeHeader ParseHeader(std::string filename); private: + static const int TypeHeaderSize = 4; //Including the TypeID + static const int ObjectHeaderSize = 10; //Including modelID, textureID, position, rotation, scale. + static const int LevelHeaderSize = 10; //Including Format version, Name, Map version etc. + + static const int FormatVersionMajor = 1; + static const int FormatVersionMinor = 0; + + /************************************* + Question + *************************************/ + //Could we use the IDs in "ObjectDefines.h" or do we have to use our own??? + enum TypeID + { + TypeID_LevelHeader, + TypeID_Player, + TypeID_Object, + + + TypeID_NUM_OF_TYPES, + + ObjectType_Unknown = -1, + }; }; } diff --git a/Code/Game/GameLogic/ObjectDefines.h b/Code/Game/GameLogic/ObjectDefines.h index 74bca42c..eae1b0c9 100644 --- a/Code/Game/GameLogic/ObjectDefines.h +++ b/Code/Game/GameLogic/ObjectDefines.h @@ -5,13 +5,14 @@ namespace GameLogic { enum ObjectType { + ObjectType_Level, ObjectType_Static, ObjectType_Dynamic, ObjectType_NUM_OF_TYPES, - ObjectType_Unknow = -1, + ObjectType_Unknown = -1, }; struct ObjectTypeHeader From c84ed645f00d0b256599462e093b9b3f29e64710 Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Fri, 24 Jan 2014 10:22:18 +0100 Subject: [PATCH 28/76] GameLogic - implementation of LevelLoader --- Code/Dokumentation/LevelLoader.uxf | 300 +++++++++++++------------- Code/Game/GameLogic/GameLogic.vcxproj | 4 + Code/Game/GameLogic/LevelLoader.cpp | 17 ++ Code/Game/GameLogic/LevelLoader.h | 29 +++ Code/Game/GameLogic/Loader.cpp | 18 ++ Code/Game/GameLogic/Loader.h | 28 +++ 6 files changed, 246 insertions(+), 150 deletions(-) create mode 100644 Code/Game/GameLogic/LevelLoader.cpp create mode 100644 Code/Game/GameLogic/LevelLoader.h create mode 100644 Code/Game/GameLogic/Loader.cpp create mode 100644 Code/Game/GameLogic/Loader.h diff --git a/Code/Dokumentation/LevelLoader.uxf b/Code/Dokumentation/LevelLoader.uxf index 723857bb..6cfb9c29 100644 --- a/Code/Dokumentation/LevelLoader.uxf +++ b/Code/Dokumentation/LevelLoader.uxf @@ -1,61 +1,6 @@ 8 - - com.umlet.element.Package - - 552 - 320 - 584 - 368 - - LevelLoader - - - - com.umlet.element.Class - - 440 - 88 - 128 - 40 - - GameLogic -<<Erik>> - - - - com.umlet.element.Relation - - 768 - 472 - 136 - 104 - - lt=<<<<- - 120;24;120;88;24;88 - - - com.umlet.element.Class - - 560 - 544 - 232 - 136 - - <<Interface>> -Parser --- -Functions: -vector<struct> Parse(); -- -Privates: -enum headerType; -const int FileHeaderSize; -const int FileVersion; - - - com.umlet.element.Class @@ -71,85 +16,39 @@ const int FileVersion; com.umlet.element.Relation - 640 + 840 208 - 40 + 88 168 - lt=<<. - 24;24;24;152 - - - com.umlet.element.Relation - - 384 - 176 - 256 - 56 - - lt=->>>> -m1=1..1 -m2=1..1 -<Knows about - 240;40;24;40 - - - com.umlet.element.Package - - 248 - 320 - 248 - 160 - - Defines - + lt=. +<Uses + 24;24;24;64;72;64;72;152 com.umlet.element.Class - 800 - 360 - 208 - 136 + 928 + 560 + 200 + 120 - <<Interface>> -Loader + Collection of functions +<<lots of functions>> -- -Functions: -wchar* LoadFile(string fileName); -Model* LoadModel(string modelName); -Model* LoadModel(int modelID); -- -Privates: +functions for creating the right structs com.umlet.element.Class - 328 - 208 - 80 - 24 + 440 + 88 + 128 + 40 - Defines - - - - com.umlet.element.Class - - 256 - 360 - 232 - 104 - - Defines.h -<<Header file>> --- -Enum ObjectType(static, dynamic, specials); -. -Struct static; -Struct dynamic; -Struct specials + GameLogic +<<Erik>> @@ -169,28 +68,32 @@ Uses> com.umlet.element.Class - 816 - 192 - 128 - 40 + 800 + 360 + 208 + 136 - Resource Loader -<<Dennis>><<Singleton> + <<Interface>> +Loader +-- +Functions: +wchar* LoadFile(string fileName); +//Model* LoadHitBoxes(string modelName); +//Model* LoadHitBoxes(int modelID); +- +Privates: - com.umlet.element.Class + com.umlet.element.Relation - 928 - 560 - 200 - 120 + 344 + 208 + 40 + 168 - Collection of functions -<<lots of functions>> --- -functions for creating the right structs - + lt=<<. + 24;24;24;152 com.umlet.element.Relation @@ -206,6 +109,61 @@ m2=1..1 Uses> 24;40;160;40 + + com.umlet.element.Class + + 816 + 192 + 128 + 40 + + Resource Loader +<<Dennis>><<Singleton> + + + + com.umlet.element.Relation + + 768 + 472 + 136 + 104 + + lt=<<<<- + 120;24;120;88;24;88 + + + com.umlet.element.Relation + + 384 + 176 + 256 + 56 + + lt=->>>> +m1=1..1 +m2=1..1 +<Knows about + 240;40;24;40 + + + com.umlet.element.Class + + 256 + 360 + 232 + 104 + + Defines.h +<<Header file>> +-- +Enum ObjectType(static, dynamic, specials); +. +Struct static; +Struct dynamic; +Struct specials + + com.umlet.element.Class @@ -229,7 +187,7 @@ Privates: com.umlet.element.Relation - 344 + 640 208 40 168 @@ -238,27 +196,26 @@ Privates: 24;24;24;152 - com.umlet.element.Relation + com.umlet.element.Class - 840 + 328 208 - 88 - 168 + 80 + 24 - lt=. -<Uses - 24;24;24;64;72;64;72;152 + Defines + - com.umlet.element.Relation + com.umlet.element.Package - 656 - 472 - 40 - 88 + 248 + 320 + 248 + 160 - lt=<<<<- - 24;72;24;24 + Defines + com.umlet.element.Relation @@ -274,4 +231,47 @@ m2=1..1 Uses> 24;40;80;40;120;40;120;144 + + com.umlet.element.Relation + + 656 + 472 + 40 + 88 + + lt=<<<<- + 24;72;24;24 + + + com.umlet.element.Class + + 560 + 544 + 232 + 136 + + <<Interface>> +Parser +-- +Functions: +vector<struct> Parse(); +- +Privates: +enum headerType; +const int FileHeaderSize; +const int FileVersion; + + + + + com.umlet.element.Package + + 552 + 320 + 584 + 368 + + LevelLoader + + diff --git a/Code/Game/GameLogic/GameLogic.vcxproj b/Code/Game/GameLogic/GameLogic.vcxproj index 6b66a9d5..bfa96d35 100644 --- a/Code/Game/GameLogic/GameLogic.vcxproj +++ b/Code/Game/GameLogic/GameLogic.vcxproj @@ -184,6 +184,8 @@ + + @@ -202,6 +204,8 @@ + + diff --git a/Code/Game/GameLogic/LevelLoader.cpp b/Code/Game/GameLogic/LevelLoader.cpp new file mode 100644 index 00000000..0e302d37 --- /dev/null +++ b/Code/Game/GameLogic/LevelLoader.cpp @@ -0,0 +1,17 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#include "LevelLoader.h" +using namespace GameLogic; +using namespace GameLogic::LevelLoader; + +std::vector LevelLoader::LoadLevel(std::string fileName) +{ + Parser->parse(fileName); +} + +std::vector LevelLoader::LoadLevelHeader(std::string fileName) +{ + parser->parseHeader(fileName); +} \ No newline at end of file diff --git a/Code/Game/GameLogic/LevelLoader.h b/Code/Game/GameLogic/LevelLoader.h new file mode 100644 index 00000000..c33cd09b --- /dev/null +++ b/Code/Game/GameLogic/LevelLoader.h @@ -0,0 +1,29 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#ifndef LEVELLOADER_H +#define LEVELLOADER_H + +#include +#include +#include "ObjectDefines.h" +#include "LevelParser.h" + +namespace GameLogic +{ + class LevelLoader + { + + public: + LevelLoader(){this->parser = new Parser()}; + ~LevelLoader(){}; + std::vector LoadLevel(std::string fileName); //loads the level and objects from file + std::vector LoadLevelHeader(std::string fileName); //just for fast access for the meta information about the level. + + private: + LevelParser parser; + }; +} + +#endif \ No newline at end of file diff --git a/Code/Game/GameLogic/Loader.cpp b/Code/Game/GameLogic/Loader.cpp new file mode 100644 index 00000000..3cc68c50 --- /dev/null +++ b/Code/Game/GameLogic/Loader.cpp @@ -0,0 +1,18 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#include "Loader.h" + +using namespace GameLogic::LevelLoader; +using namespace Oyster::Resource; +using namespace std; + +char* Loader::LoadFile(std::string fileName) +{ + //convert from string to wstring + std::wstring temp(fileName.begin(), fileName.end()); + + //convert from wstring to wchar then loads the file + return OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); +} \ No newline at end of file diff --git a/Code/Game/GameLogic/Loader.h b/Code/Game/GameLogic/Loader.h new file mode 100644 index 00000000..0d0a662f --- /dev/null +++ b/Code/Game/GameLogic/Loader.h @@ -0,0 +1,28 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#ifndef LOADER_H +#define LOADER_H + +#include "Resource\OysterResource.h" +#include + +namespace GameLogic +{ + namespace LevelLoader + { + class Loader + { + public: + Loader(){}; + ~Loader(){}; + char* LoadFile(std::string fileName); + + //TODO: + //Add functionality to load physicsObjects (hitboxes) + }; + } +} + +#endif; \ No newline at end of file From 74ac5e2d31b5d6d0ba4a6f89e0850d58729b3603 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Mon, 27 Jan 2014 08:54:25 +0100 Subject: [PATCH 29/76] Updated creation of objects --- Code/Game/GameLogic/AttatchmentMassDriver.cpp | 2 +- Code/Game/GameLogic/AttatchmentMassDriver.h | 2 +- Code/Game/GameLogic/CollisionManager.cpp | 17 +++++++++---- Code/Game/GameLogic/DynamicObject.cpp | 6 +++++ Code/Game/GameLogic/DynamicObject.h | 1 + Code/Game/GameLogic/Game_PlayerData.cpp | 11 ++++++++- Code/Game/GameLogic/Object.cpp | 15 +++++++----- Code/Game/GameLogic/Object.h | 1 + Code/Game/GameLogic/Player.cpp | 24 ++++++++----------- Code/Game/GameLogic/Player.h | 5 ++-- Code/Game/GameLogic/StaticObject.cpp | 5 ++++ Code/Game/GameLogic/StaticObject.h | 1 + 12 files changed, 60 insertions(+), 30 deletions(-) diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index 11ec5ea8..692e4ef4 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -50,7 +50,7 @@ void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float Oyster::Math::Float4x4 hitSpace = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/4,1,1,20); Oyster::Collision3D::Frustrum hitFrustum = Oyster::Collision3D::Frustrum(Oyster::Math3D::ViewProjectionMatrix(aim,hitSpace)); - //Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,ForcePushAction); + Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,ForcePushAction); } diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.h b/Code/Game/GameLogic/AttatchmentMassDriver.h index 3fb8932a..0ced186a 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.h +++ b/Code/Game/GameLogic/AttatchmentMassDriver.h @@ -36,7 +36,7 @@ namespace GameLogic ********************************************************/ void ForceSuck(const WEAPON_FIRE &usage, float dt); - void ForcePushAction(Oyster::Physics::ICustomBody *obj); + static void ForcePushAction(Oyster::Physics::ICustomBody *obj); private: diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 5be071fe..f01dac1c 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -13,14 +13,14 @@ using namespace GameLogic; void PlayerVObject(Player &player, Object &obj, Oyster::Math::Float kineticEnergyLoss); //Physics::ICustomBody::SubscriptMessage - void Player::PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) + void Player::PlayerCollision(Oyster::Physics::ICustomBody *rigidBodyPlayer, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) { Player *player = ((Player*)(rigidBodyPlayer->GetCustomTag())); Object *realObj = (Object*)obj->GetCustomTag(); switch (realObj->GetType()) { - case OBJECT_H::OBJECT_TYPE_GENERIC: + case OBJECT_TYPE::OBJECT_TYPE_GENERIC: PlayerVObject(*player,*realObj, kineticEnergyLoss); //return Physics::ICustomBody::SubscriptMessage_none; break; @@ -52,9 +52,16 @@ using namespace GameLogic; //Collision between a player and a general static or dynamic object //use kinetic energyloss of the collision in order too determin how much damage to take //use as part of the damage algorithm - player.DamageLife(20); - } + int damageDone = 0; + int forceThreashHold = 200; + + if(kineticEnergyLoss > forceThreashHold) //should only take damage if the force is high enough + { + damageDone = kineticEnergyLoss * 0.10f; + player.DamageLife(damageDone); + } + } //Oyster::Physics::ICustomBody::SubscriptMessage void Level::LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) { @@ -63,6 +70,6 @@ using namespace GameLogic; void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) { - Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500); + Oyster::Math::Float4 pushForce = Oyster::Math::Float4(owner->GetLookDir()) * (500); ((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); } diff --git a/Code/Game/GameLogic/DynamicObject.cpp b/Code/Game/GameLogic/DynamicObject.cpp index a8ea1ab4..1ccc12ef 100644 --- a/Code/Game/GameLogic/DynamicObject.cpp +++ b/Code/Game/GameLogic/DynamicObject.cpp @@ -16,6 +16,12 @@ DynamicObject::DynamicObject(void* collisionFunc, OBJECT_TYPE type) } +DynamicObject::DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) + :Object(rigidBody, collisionFunc, type) +{ + +} + DynamicObject::~DynamicObject(void) { diff --git a/Code/Game/GameLogic/DynamicObject.h b/Code/Game/GameLogic/DynamicObject.h index 70b5efe7..8912d455 100644 --- a/Code/Game/GameLogic/DynamicObject.h +++ b/Code/Game/GameLogic/DynamicObject.h @@ -15,6 +15,7 @@ namespace GameLogic public: DynamicObject(); DynamicObject(void* collisionFunc, OBJECT_TYPE type); + DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); ~DynamicObject(void); private: diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index 1c7de849..8e4b7707 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -5,7 +5,16 @@ using namespace GameLogic; Game::PlayerData::PlayerData() { - this->player = new Player(); + Oyster::Physics::API::SimpleBodyDescription sbDesc; + + //create rigidbody + Oyster::Physics::ICustomBody *rigidBody = Oyster::Physics::API::Instance().CreateRigidBody(sbDesc).Release(); + + + //create player with this rigidbody + + + this->player = new Player(rigidBody,Player::PlayerCollision,OBJECT_TYPE::OBJECT_TYPE_PLAYER); this->player->GetRigidBody()->SetCustomTag(this); } Game::PlayerData::PlayerData(int playerID,int teamID) diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 2ee2a613..46695ddf 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -54,12 +54,17 @@ Object::Object(ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type) Oyster::Physics::API::Instance().AddObject(rigidBody); rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); - rigidBody->SetCustomTag(this); + this->objectID = GID(); this->type = type; } +Object::Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) +{ + +} + void Object::ApplyLinearImpulse(Oyster::Math::Float4 force) { setState.ApplyLinearImpulse(force); @@ -98,11 +103,9 @@ void Object::EndFrame() //Oyster::Math::Float rot = (setState.GetGravityNormal().xyz).Dot(getState.GetGravityNormal().xyz); //Oyster::Math::Float3 axis = (setState.GetGravityNormal().xyz).Cross(getState.GetGravityNormal().xyz); - - // align with gravity normal - //Oyster::Math::Float4x4 rotMatrix = setState.GetOrientation(); //Oyster::Math3D::RotationMatrix(rot, axis); - //Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); - //setState.SetOrientation(rotMatrix); + Oyster::Math::Float4x4 rotMatrix = setState.GetOrientation(); //Oyster::Math3D::RotationMatrix(rot, axis); + Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); + setState.SetOrientation(rotMatrix); this->getState = this->rigidBody->GetState(); diff --git a/Code/Game/GameLogic/Object.h b/Code/Game/GameLogic/Object.h index a4385345..f39e2a41 100644 --- a/Code/Game/GameLogic/Object.h +++ b/Code/Game/GameLogic/Object.h @@ -19,6 +19,7 @@ namespace GameLogic Object(); Object(void* collisionFunc, OBJECT_TYPE type); Object(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type); + Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); ~Object(void); OBJECT_TYPE GetType() const; diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index 8c604a05..92e5b61e 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -18,7 +18,12 @@ Player::Player() lookDir = Oyster::Math::Float4(0,0,-1,0); setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); - //setState.SetRotation(Oyster::Math::Float4(0,0,0,0).Normalize()); +} + +Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) + :DynamicObject(rigidBody, collisionFunc, type) +{ + } Player::~Player(void) @@ -65,7 +70,6 @@ void Player::MoveBackwards() void Player::MoveRight() { //Do cross product with forward vector and negative gravity vector - // temp up vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r = (-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); setState.ApplyLinearImpulse(r * 20 * this->gameInstance->GetFrameTime()); @@ -74,7 +78,6 @@ void Player::MoveRight() void Player::MoveLeft() { //Do cross product with forward vector and negative gravity vector - // temp up vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r1 = -(-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); //Still get zero setState.ApplyLinearImpulse(-r * 20 * this->gameInstance->GetFrameTime()); @@ -93,18 +96,11 @@ void Player::Respawn(Oyster::Math::Float3 spawnPoint) this->lookDir = Oyster::Math::Float4(1,0,0); } -void Player::Rotate(const Oyster::Math3D::Float3 lookDir) +void Player::Rotate(float dx, float dy) { - - this->lookDir = lookDir; - - Oyster::Math::Float4 up(0,1,0,0);//-setState.GetGravityNormal(); - Oyster::Math::Float4 pos = setState.GetCenterPosition(); - Oyster::Math::Float4x4 world = Oyster::Math3D::OrientationMatrix_LookAtDirection(lookDir, up.xyz, pos.xyz); - // cant set rotation - //setState.SetOrientation(world); - //this->lookDir = lookDir - up.xyz; - + + //this->lookDir = lookDir; + //this->setState.AddRotation(Oyster::Math::Float4(x, y)); //this->setState.SetRotation(); } diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index 371460c8..2b5a64fe 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -16,6 +16,7 @@ namespace GameLogic { public: Player(void); + Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); ~Player(void); /******************************************************** @@ -42,7 +43,7 @@ namespace GameLogic void Respawn(Oyster::Math::Float3 spawnPoint); - void Rotate(const Oyster::Math3D::Float3 lookDir); + void Rotate(float x, float y); /******************************************************** * Collision function for player, this is to be sent to physics through the subscribe function with the rigidbody @@ -50,7 +51,7 @@ namespace GameLogic * @param rigidBodyPlayer: physics object of the player * @param obj: physics object for the object that collided with the player ********************************************************/ - static void PlayerCollision(const Oyster::Physics::ICustomBody *rigidBodyPlayer, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); + static void PlayerCollision(Oyster::Physics::ICustomBody *rigidBodyPlayer, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); bool IsWalking(); diff --git a/Code/Game/GameLogic/StaticObject.cpp b/Code/Game/GameLogic/StaticObject.cpp index c9fda9cf..293e05ee 100644 --- a/Code/Game/GameLogic/StaticObject.cpp +++ b/Code/Game/GameLogic/StaticObject.cpp @@ -24,6 +24,11 @@ StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisi :Object(rigidBody,collisionFunc,type) { +} +StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) + :Object(rigidBody, collisionFunc, type) +{ + } diff --git a/Code/Game/GameLogic/StaticObject.h b/Code/Game/GameLogic/StaticObject.h index d033f6e8..13d7f73e 100644 --- a/Code/Game/GameLogic/StaticObject.h +++ b/Code/Game/GameLogic/StaticObject.h @@ -18,6 +18,7 @@ namespace GameLogic StaticObject(); StaticObject(void* collisionFunc, OBJECT_TYPE type); StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type); + StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); StaticObject(OBJECT_TYPE type); ~StaticObject(void); From e188794aa3f5c8572517662d1e8cacf8c884de05 Mon Sep 17 00:00:00 2001 From: Pontus Fransson Date: Mon, 27 Jan 2014 10:14:02 +0100 Subject: [PATCH 30/76] GL - Load level in LevelParser --- Code/Game/GameLogic/LevelParser.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Code/Game/GameLogic/LevelParser.cpp b/Code/Game/GameLogic/LevelParser.cpp index 1f6a9398..5a709316 100644 --- a/Code/Game/GameLogic/LevelParser.cpp +++ b/Code/Game/GameLogic/LevelParser.cpp @@ -1,5 +1,7 @@ #include "LevelParser.h" +#include "Loader.h" + using namespace GameLogic; using namespace ::LevelFileLoader; @@ -15,6 +17,8 @@ LevelParser::~LevelParser() std::vector LevelParser::Parse(std::string filename) { //Read entire level file. + Loader loader; + unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str()); std::vector objects; @@ -25,6 +29,7 @@ std::vector LevelParser::Parse(std::string filename) //Get typeID int typeID = 0; + //Unpack ID switch(typeID) { @@ -50,6 +55,8 @@ std::vector LevelParser::Parse(std::string filename) ObjectTypeHeader LevelParser::ParseHeader(std::string filename) { //Read entire level file. + Loader loader; + unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str()); //Find the header in the returned string. unsigned int counter = 0; @@ -65,7 +72,8 @@ ObjectTypeHeader LevelParser::ParseHeader(std::string filename) { case TypeID_LevelHeader: //Call function - + + counter += LevelHeaderSize; break; case TypeID_Object: //Call function From 1748fe323a5d531ed73a3651f24110260475e2ef Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Mon, 27 Jan 2014 10:15:39 +0100 Subject: [PATCH 31/76] GL - Added Parsing functions for levelLoader and moved Packing classes to misc --- Code/Game/GameLogic/GameLogic.vcxproj | 2 + Code/Game/GameLogic/Loader.cpp | 5 +- Code/Game/GameLogic/Loader.h | 4 +- Code/Game/GameLogic/ObjectDefines.h | 4 +- Code/Game/GameLogic/ParseFunctions.cpp | 83 +++ Code/Game/GameLogic/ParserFunctions.h | 22 + Code/Misc/Misc.vcxproj | 2 + Code/Misc/Misc.vcxproj.filters | 6 + Code/Misc/Packing/Packing.cpp | 346 +++++++++++++ Code/Misc/Packing/Packing.h | 81 +++ .../Messages/MessageHeader.cpp | 3 +- .../NetworkDependencies.vcxproj | 2 - .../NetworkDependencies.vcxproj.filters | 2 - Code/Network/NetworkDependencies/Packing.cpp | 486 ------------------ Code/Network/NetworkDependencies/Packing.h | 107 ---- 15 files changed, 551 insertions(+), 604 deletions(-) create mode 100644 Code/Game/GameLogic/ParseFunctions.cpp create mode 100644 Code/Game/GameLogic/ParserFunctions.h create mode 100644 Code/Misc/Packing/Packing.cpp create mode 100644 Code/Misc/Packing/Packing.h delete mode 100644 Code/Network/NetworkDependencies/Packing.cpp delete mode 100644 Code/Network/NetworkDependencies/Packing.h diff --git a/Code/Game/GameLogic/GameLogic.vcxproj b/Code/Game/GameLogic/GameLogic.vcxproj index ba7603a7..54ec2d7e 100644 --- a/Code/Game/GameLogic/GameLogic.vcxproj +++ b/Code/Game/GameLogic/GameLogic.vcxproj @@ -189,6 +189,7 @@ + @@ -210,6 +211,7 @@ + diff --git a/Code/Game/GameLogic/Loader.cpp b/Code/Game/GameLogic/Loader.cpp index ec537765..01047b2f 100644 --- a/Code/Game/GameLogic/Loader.cpp +++ b/Code/Game/GameLogic/Loader.cpp @@ -8,11 +8,12 @@ using namespace GameLogic::LevelFileLoader; using namespace Oyster::Resource; using namespace std; -char* Loader::LoadFile(std::string fileName) +unsigned char* Loader::LoadFile(std::string fileName, int &size) { //convert from string to wstring std::wstring temp(fileName.begin(), fileName.end()); + size = temp.size(); //convert from wstring to wchar then loads the file - return (char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); + return (unsigned char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); } \ No newline at end of file diff --git a/Code/Game/GameLogic/Loader.h b/Code/Game/GameLogic/Loader.h index 72d53854..d77921c5 100644 --- a/Code/Game/GameLogic/Loader.h +++ b/Code/Game/GameLogic/Loader.h @@ -15,9 +15,9 @@ namespace GameLogic class Loader { public: - Loader(){}; + Loader (){}; ~Loader(){}; - char* LoadFile(std::string fileName); + unsigned char* LoadFile(std::string fileName, int &size); //TODO: //Add functionality to load physicsObjects (hitboxes) diff --git a/Code/Game/GameLogic/ObjectDefines.h b/Code/Game/GameLogic/ObjectDefines.h index eae1b0c9..7f2307d1 100644 --- a/Code/Game/GameLogic/ObjectDefines.h +++ b/Code/Game/GameLogic/ObjectDefines.h @@ -24,9 +24,9 @@ namespace GameLogic struct ObjectHeader : public ObjectTypeHeader { //Model, - + int ModelID; //Texture - + int TextureID; //Position float position[3]; //Rotation diff --git a/Code/Game/GameLogic/ParseFunctions.cpp b/Code/Game/GameLogic/ParseFunctions.cpp new file mode 100644 index 00000000..28199e6b --- /dev/null +++ b/Code/Game/GameLogic/ParseFunctions.cpp @@ -0,0 +1,83 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#include "ParserFunctions.h" +#include "../../Misc/Packing/Packing.h" +#include + +using namespace Oyster::Packing; +using namespace GameLogic; +using namespace std; + +ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer) +{ + int i = Unpacki(buffer); + + struct ObjectTypeHeader header; + header.typeID = (ObjectType)i; + + return header; +} +ObjectHeader parseObjectHeader (unsigned char* buffer) +{ + struct ObjectHeader header; + int x, y,z; + string s; + int start = 0; + + + //ModelID + x = Unpacki(buffer); + header.ModelID = (ObjectType)x; + + //TextureID + start += 4; + x = Unpacki(&buffer[start]); + header.TextureID = x; + + //Position + start += 4; + x = Unpacki(&buffer[start]); + + start += 4; + y = Unpacki(&buffer[start]); + + start += 4; + z = Unpacki(&buffer[start]); + + header.position[0] = x; + header.position[1] = y; + header.position[2] = z; + + //Rotation + start += 4; + x = Unpacki(&buffer[start]); + + start += 4; + y = Unpacki(&buffer[start]); + + start += 4; + z = Unpacki(&buffer[start]); + + header.rotation[0] = x; + header.rotation[1] = y; + header.rotation[2] = z; + + //Scale + start += 4; + x = Unpacki(&buffer[start]); + + start += 4; + y = Unpacki(&buffer[start]); + + start += 4; + z = Unpacki(&buffer[start]); + + header.scale[0] = x; + header.scale[1] = y; + header.scale[2] = z; + + + return header; +} \ No newline at end of file diff --git a/Code/Game/GameLogic/ParserFunctions.h b/Code/Game/GameLogic/ParserFunctions.h new file mode 100644 index 00000000..0608e77a --- /dev/null +++ b/Code/Game/GameLogic/ParserFunctions.h @@ -0,0 +1,22 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#ifndef PARSERFUNCTIONS_H +#define PARSERFUNCTIONS_H +#include "ObjectDefines.h" + +namespace GameLogic +{ + namespace LevelFileLoader + { + namespace parseFunctions + { + ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. + ObjectHeader parseObjectHeader (unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. + } + } +} + + +#endif \ No newline at end of file diff --git a/Code/Misc/Misc.vcxproj b/Code/Misc/Misc.vcxproj index 0b7bf576..6c3db1ec 100644 --- a/Code/Misc/Misc.vcxproj +++ b/Code/Misc/Misc.vcxproj @@ -146,6 +146,7 @@ + @@ -164,6 +165,7 @@ + diff --git a/Code/Misc/Misc.vcxproj.filters b/Code/Misc/Misc.vcxproj.filters index 44348332..8413642a 100644 --- a/Code/Misc/Misc.vcxproj.filters +++ b/Code/Misc/Misc.vcxproj.filters @@ -48,6 +48,9 @@ Source Files + + Source Files + @@ -110,5 +113,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Code/Misc/Packing/Packing.cpp b/Code/Misc/Packing/Packing.cpp new file mode 100644 index 00000000..30064b0a --- /dev/null +++ b/Code/Misc/Packing/Packing.cpp @@ -0,0 +1,346 @@ +#include "Packing.h" + +/*************************** + Packing +***************************/ + +#include + +namespace Oyster +{ + namespace Packing + { + //bool (1-bit) + void Pack(unsigned char buffer[], bool i) + { + *buffer++ = i; + } + + //char (8-bit) + void Pack(unsigned char buffer[], char i) + { + *buffer++ = i; + } + + void Pack(unsigned char buffer[], unsigned char i) + { + *buffer++ = i; + } + + //short (16-bit) + void Pack(unsigned char buffer[], short i) + { + *buffer++ = i >> 8; + *buffer++ = (char)i; + } + + void Pack(unsigned char buffer[], unsigned short i) + { + *buffer++ = i >> 8; + *buffer++ = (char)i; + } + + //int (32-bit) + void Pack(unsigned char buffer[], int i) + { + *buffer++ = i >> 24; + *buffer++ = i >> 16; + *buffer++ = i >> 8; + *buffer++ = i; + } + + void Pack(unsigned char buffer[], unsigned int i) + { + *buffer++ = i >> 24; + *buffer++ = i >> 16; + *buffer++ = i >> 8; + *buffer++ = i; + } + + //__int64 (64-bit) + void Pack(unsigned char buffer[], __int64 i) + { + *buffer++ = (char)(i >> 56); + *buffer++ = (char)(i >> 48); + *buffer++ = (char)(i >> 40); + *buffer++ = (char)(i >> 32); + *buffer++ = (char)(i >> 24); + *buffer++ = (char)(i >> 16); + *buffer++ = (char)(i >> 8); + *buffer++ = (char)i; + } + + void Pack(unsigned char buffer[], unsigned __int64 i) + { + *buffer++ = (char)(i >> 56); + *buffer++ = (char)(i >> 48); + *buffer++ = (char)(i >> 40); + *buffer++ = (char)(i >> 32); + *buffer++ = (char)(i >> 24); + *buffer++ = (char)(i >> 16); + *buffer++ = (char)(i >> 8); + *buffer++ = (char)i; + } + + //floating point (32, 64-bit) + void Pack(unsigned char buffer[], float i) + { + int tempFloat = (int)Pack754(i, 32, 8); + Pack(buffer, tempFloat); + } + + void Pack(unsigned char buffer[], double i) + { + __int64 tempDouble = Pack754(i, 64, 11); + Pack(buffer, tempDouble); + } + + //string + void Pack(unsigned char buffer[], char str[]) + { + short len = (short)strlen(str); + Pack(buffer, len); + buffer += 2; + memcpy(buffer, str, len); + } + + void Pack(unsigned char buffer[], std::string& str) + { + short len = (short)str.length(); + Pack(buffer, len); + buffer += 2; + memcpy(buffer, str.c_str(), len); + } + + unsigned __int64 Pack754(long double f, unsigned bits, unsigned expbits) + { + long double fnorm; + int shift; + long long sign, exp, significand; + unsigned significandbits = bits - expbits - 1; // -1 for sign bit + + if (f == 0.0) + return 0; // get this special case out of the way + + // check sign and begin normalization + if (f < 0) + { + sign = 1; + fnorm = -f; + } + else + { + sign = 0; + fnorm = f; + } + + // get the normalized form of f and track the exponent + shift = 0; + while(fnorm >= 2.0) + { + fnorm /= 2.0; + shift++; + } + + while(fnorm < 1.0) + { + fnorm *= 2.0; + shift--; + } + + fnorm = fnorm - 1.0; + + // calculate the binary form (non-float) of the significand data + significand = (long long)(fnorm * ((1LL << significandbits) + 0.5f)); + + // get the biased exponent + exp = shift + ((1 << (expbits - 1)) - 1); // shift + bias + + // return the final answer + return (sign << (bits - 1)) | (exp << (bits - expbits - 1)) | significand; + } + + /****************************** + Unpacking + ******************************/ + + //bool (1-bit) + bool Unpackb(unsigned char buffer[]) + { + return *buffer; + } + + //char (8-bit) + char Unpackc(unsigned char buffer[]) + { + if(*buffer <= 0x7f) + { + return *buffer; + } + else + { + return (-1 - (unsigned char)(0xffu - *buffer)); + } + } + + unsigned char UnpackC(unsigned char buffer[]) + { + return *buffer; + } + + //short (16-bit) + short Unpacks(unsigned char buffer[]) + { + short i = ((short)buffer[0] << 8) | buffer[1]; + + if(i > 0x7fffu) + { + i = -1 - (unsigned short)(0xffffu - i); + } + + return i; + } + + unsigned short UnpackS(unsigned char buffer[]) + { + return ((unsigned int)buffer[0] << 8) | buffer[1]; + } + + //int (32-bit) + int Unpacki(unsigned char buffer[]) + { + int i = ((int)buffer[0] << 24) | + ((int)buffer[1] << 16) | + ((int)buffer[2] << 8) | + ((int)buffer[3]); + + if(i > 0x7fffffffu) + { + i = -1 - (int)(0xffffffffu - i); + } + + return i; + } + + unsigned int UnpackI(unsigned char buffer[]) + { + return ((unsigned int)buffer[0] << 24) | + ((unsigned int)buffer[1] << 16) | + ((unsigned int)buffer[2] << 8) | + ((unsigned int)buffer[3]); + } + + //__int64 (64-bit) + __int64 Unpacki64(unsigned char buffer[]) + { + __int64 i = ((__int64)buffer[0] << 56) | + ((__int64)buffer[1] << 48) | + ((__int64)buffer[2] << 40) | + ((__int64)buffer[3] << 32) | + ((__int64)buffer[4] << 24) | + ((__int64)buffer[5] << 16) | + ((__int64)buffer[6] << 8) | + (buffer[7]); + + if(i > 0x7fffffffffffffffu) + { + i = -1 - (__int64)(0xffffffffffffffffu - i); + } + + return i; + } + + unsigned __int64 UnpackI64(unsigned char buffer[]) + { + + return ((__int64)buffer[0] << 56) | + ((__int64)buffer[1] << 48) | + ((__int64)buffer[2] << 40) | + ((__int64)buffer[3] << 32) | + ((__int64)buffer[4] << 24) | + ((__int64)buffer[5] << 16) | + ((__int64)buffer[6] << 8) | + ((__int64)buffer[7]); + } + + //floating point (32, 64-bit) + float Unpackf(unsigned char buffer[]) + { + int tempFloat = Unpacki(buffer); + return (float)Unpack754(tempFloat, 32, 8); + } + + double Unpackd(unsigned char buffer[]) + { + __int64 tempDouble = Unpacki64(buffer); + return Unpack754(tempDouble, 64, 11); + } + + //string + char* UnpackCStr(unsigned char buffer[]) + { + short len = UnpackS(buffer); + char* str = new char[len+1]; + + buffer += 2; + for(int i = 0; i < len; i++) + { + str[i] = buffer[i]; + } + + str[len] = '\0'; + + return str; + } + + std::string UnpackStr(unsigned char buffer[]) + { + short len = UnpackS(buffer); + std::string temp; + temp.resize(len); + + buffer += 2; + for(int i = 0; i < len; i++) + { + temp[i] = buffer[i]; + } + + return temp; + } + + long double Unpack754(unsigned __int64 i, unsigned bits, unsigned expbits) + { + long double result; + long long shift; + unsigned bias; + unsigned significandbits = bits - expbits - 1; // -1 for sign bit + + if (i == 0) + return 0.0; + + // pull the significand + result = (long double)(i&((1LL << significandbits) - 1)); // mask + result /= (1LL << significandbits); // convert back to float + result += 1.0f; // add the one back on + + // deal with the exponent + bias = (1 << (expbits - 1)) - 1; + shift = ((i >> significandbits) & ((1LL << expbits) - 1)) - bias; + while(shift > 0) + { + result *= 2.0; + shift--; + } + while(shift < 0) + { + result /= 2.0; + shift++; + } + + // sign it + result *= (i >> (bits - 1)) & 1 ? -1.0 : 1.0; + + return result; + } + } +} diff --git a/Code/Misc/Packing/Packing.h b/Code/Misc/Packing/Packing.h new file mode 100644 index 00000000..47a65c51 --- /dev/null +++ b/Code/Misc/Packing/Packing.h @@ -0,0 +1,81 @@ +#ifndef PACKING_H +#define PACKING_H + +///////////////////////////////////// +// Created by Pontus Fransson 2013 // +///////////////////////////////////// + +#include + +/****************************** + Packing +******************************/ +namespace Oyster +{ + namespace Packing + { + //bool (1-bit) + void Pack(unsigned char buffer[], bool i); + + //char (8-bit) + void Pack(unsigned char buffer[], char i); + void Pack(unsigned char buffer[], unsigned char i); // unsigned + + //short (16-bit) + void Pack(unsigned char buffer[], short i); + void Pack(unsigned char buffer[], unsigned short i); // unsigned + + //int (32-bit) + void Pack(unsigned char buffer[], int i); + void Pack(unsigned char buffer[], unsigned int i); // unsigned + + //__int64 (64-bit) + void Pack(unsigned char buffer[], __int64 i); + void Pack(unsigned char buffer[], unsigned __int64 i); // unsigned + + //floating point (32, 64-bit) + void Pack(unsigned char buffer[], float i); + void Pack(unsigned char buffer[], double i); + + //string + void Pack(unsigned char buffer[], char str[]); + void Pack(unsigned char buffer[], std::string& str); + + unsigned __int64 Pack754(long double f, unsigned bits, unsigned expbits); + + /****************************** + Unpacking + ******************************/ + + //bool (1-bit) + bool Unpackb(unsigned char buffer[]); + + //char (8-bit) + char Unpackc(unsigned char buffer[]); + unsigned char UnpackC(unsigned char buffer[]); // unsigned + + //short (16-bit) + short Unpacks(unsigned char buffer[]); + unsigned short UnpackS(unsigned char buffer[]); // unsigned + + //int (32-bit) + int Unpacki(unsigned char buffer[]); + unsigned int UnpackI(unsigned char buffer[]); // unsigned + + //__int64 (64-bit) + __int64 Unpacki64(unsigned char buffer[]); + unsigned __int64 UnpackI64(unsigned char buffer[]); // unsigned + + //floating point (32, 64-bit) + float Unpackf(unsigned char buffer[]); + double Unpackd(unsigned char buffer[]); + + //string + char* UnpackCStr(unsigned char buffer[]); + std::string UnpackStr(unsigned char buffer[]); + + long double Unpack754(unsigned __int64 i, unsigned bits, unsigned expbits); + } +} + +#endif \ No newline at end of file diff --git a/Code/Network/NetworkDependencies/Messages/MessageHeader.cpp b/Code/Network/NetworkDependencies/Messages/MessageHeader.cpp index d2823b62..8f315797 100644 --- a/Code/Network/NetworkDependencies/Messages/MessageHeader.cpp +++ b/Code/Network/NetworkDependencies/Messages/MessageHeader.cpp @@ -1,8 +1,9 @@ #include "MessageHeader.h" -#include "../Packing.h" +#include "../../../Misc/Packing/Packing.h" #include using namespace std; +using namespace Oyster::Packing; using namespace Oyster::Network; using namespace Oyster::Network::Messages; using namespace Oyster::Network::Protocols; diff --git a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj index 4d2ad291..c0048016 100644 --- a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj +++ b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj @@ -157,7 +157,6 @@ - @@ -172,7 +171,6 @@ - diff --git a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters index eadbbeb3..cce2fe94 100644 --- a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters +++ b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters @@ -7,7 +7,6 @@ - @@ -22,7 +21,6 @@ - diff --git a/Code/Network/NetworkDependencies/Packing.cpp b/Code/Network/NetworkDependencies/Packing.cpp deleted file mode 100644 index 41d059a3..00000000 --- a/Code/Network/NetworkDependencies/Packing.cpp +++ /dev/null @@ -1,486 +0,0 @@ -#include "Packing.h" - -/*************************** - Packing -***************************/ - -#include - -namespace Oyster -{ - namespace Network - { - namespace Packing - { - //bool (1-bit) - void Pack(unsigned char buffer[], bool i) - { - *buffer++ = i; - } - - //char (8-bit) - void Pack(unsigned char buffer[], char i) - { - *buffer++ = i; - } - - void Pack(unsigned char buffer[], unsigned char i) - { - *buffer++ = i; - } - - //short (16-bit) - void Pack(unsigned char buffer[], short i) - { - *buffer++ = i >> 8; - *buffer++ = (char)i; - } - - void Pack(unsigned char buffer[], unsigned short i) - { - *buffer++ = i >> 8; - *buffer++ = (char)i; - } - - //int (32-bit) - void Pack(unsigned char buffer[], int i) - { - *buffer++ = i >> 24; - *buffer++ = i >> 16; - *buffer++ = i >> 8; - *buffer++ = i; - } - - void Pack(unsigned char buffer[], unsigned int i) - { - *buffer++ = i >> 24; - *buffer++ = i >> 16; - *buffer++ = i >> 8; - *buffer++ = i; - } - - //__int64 (64-bit) - void Pack(unsigned char buffer[], __int64 i) - { - *buffer++ = (char)(i >> 56); - *buffer++ = (char)(i >> 48); - *buffer++ = (char)(i >> 40); - *buffer++ = (char)(i >> 32); - *buffer++ = (char)(i >> 24); - *buffer++ = (char)(i >> 16); - *buffer++ = (char)(i >> 8); - *buffer++ = (char)i; - } - - void Pack(unsigned char buffer[], unsigned __int64 i) - { - *buffer++ = (char)(i >> 56); - *buffer++ = (char)(i >> 48); - *buffer++ = (char)(i >> 40); - *buffer++ = (char)(i >> 32); - *buffer++ = (char)(i >> 24); - *buffer++ = (char)(i >> 16); - *buffer++ = (char)(i >> 8); - *buffer++ = (char)i; - } - - //floating point (32, 64-bit) - void Pack(unsigned char buffer[], float i) - { - int tempFloat = (int)Pack754(i, 32, 8); - Pack(buffer, tempFloat); - } - - void Pack(unsigned char buffer[], double i) - { - __int64 tempDouble = Pack754(i, 64, 11); - Pack(buffer, tempDouble); - } - - //string - void Pack(unsigned char buffer[], char str[]) - { - short len = (short)strlen(str); - Pack(buffer, len); - buffer += 2; - memcpy(buffer, str, len); - } - - void Pack(unsigned char buffer[], std::string& str) - { - short len = (short)str.length(); - Pack(buffer, len); - buffer += 2; - memcpy(buffer, str.c_str(), len); - } - - unsigned __int64 Pack754(long double f, unsigned bits, unsigned expbits) - { - long double fnorm; - int shift; - long long sign, exp, significand; - unsigned significandbits = bits - expbits - 1; // -1 for sign bit - - if (f == 0.0) - return 0; // get this special case out of the way - - // check sign and begin normalization - if (f < 0) - { - sign = 1; - fnorm = -f; - } - else - { - sign = 0; - fnorm = f; - } - - // get the normalized form of f and track the exponent - shift = 0; - while(fnorm >= 2.0) - { - fnorm /= 2.0; - shift++; - } - - while(fnorm < 1.0) - { - fnorm *= 2.0; - shift--; - } - - fnorm = fnorm - 1.0; - - // calculate the binary form (non-float) of the significand data - significand = (long long)(fnorm * ((1LL << significandbits) + 0.5f)); - - // get the biased exponent - exp = shift + ((1 << (expbits - 1)) - 1); // shift + bias - - // return the final answer - return (sign << (bits - 1)) | (exp << (bits - expbits - 1)) | significand; - } - - /****************************** - Unpacking - ******************************/ - - //bool (1-bit) - bool Unpackb(unsigned char buffer[]) - { - return *buffer; - } - - //char (8-bit) - char Unpackc(unsigned char buffer[]) - { - if(*buffer <= 0x7f) - { - return *buffer; - } - else - { - return (-1 - (unsigned char)(0xffu - *buffer)); - } - } - - unsigned char UnpackC(unsigned char buffer[]) - { - return *buffer; - } - - //short (16-bit) - short Unpacks(unsigned char buffer[]) - { - short i = ((short)buffer[0] << 8) | buffer[1]; - - if(i > 0x7fffu) - { - i = -1 - (unsigned short)(0xffffu - i); - } - - return i; - } - - unsigned short UnpackS(unsigned char buffer[]) - { - return ((unsigned int)buffer[0] << 8) | buffer[1]; - } - - //int (32-bit) - int Unpacki(unsigned char buffer[]) - { - int i = ((int)buffer[0] << 24) | - ((int)buffer[1] << 16) | - ((int)buffer[2] << 8) | - ((int)buffer[3]); - - if(i > 0x7fffffffu) - { - i = -1 - (int)(0xffffffffu - i); - } - - return i; - } - - unsigned int UnpackI(unsigned char buffer[]) - { - return ((unsigned int)buffer[0] << 24) | - ((unsigned int)buffer[1] << 16) | - ((unsigned int)buffer[2] << 8) | - ((unsigned int)buffer[3]); - } - - //__int64 (64-bit) - __int64 Unpacki64(unsigned char buffer[]) - { - __int64 i = ((__int64)buffer[0] << 56) | - ((__int64)buffer[1] << 48) | - ((__int64)buffer[2] << 40) | - ((__int64)buffer[3] << 32) | - ((__int64)buffer[4] << 24) | - ((__int64)buffer[5] << 16) | - ((__int64)buffer[6] << 8) | - (buffer[7]); - - if(i > 0x7fffffffffffffffu) - { - i = -1 - (__int64)(0xffffffffffffffffu - i); - } - - return i; - } - - unsigned __int64 UnpackI64(unsigned char buffer[]) - { - - return ((__int64)buffer[0] << 56) | - ((__int64)buffer[1] << 48) | - ((__int64)buffer[2] << 40) | - ((__int64)buffer[3] << 32) | - ((__int64)buffer[4] << 24) | - ((__int64)buffer[5] << 16) | - ((__int64)buffer[6] << 8) | - ((__int64)buffer[7]); - } - - //floating point (32, 64-bit) - float Unpackf(unsigned char buffer[]) - { - int tempFloat = Unpacki(buffer); - return (float)Unpack754(tempFloat, 32, 8); - } - - double Unpackd(unsigned char buffer[]) - { - __int64 tempDouble = Unpacki64(buffer); - return Unpack754(tempDouble, 64, 11); - } - - //string - char* UnpackCStr(unsigned char buffer[]) - { - short len = UnpackS(buffer); - char* str = new char[len+1]; - - buffer += 2; - for(int i = 0; i < len; i++) - { - str[i] = buffer[i]; - } - - str[len] = '\0'; - - return str; - } - - std::string UnpackStr(unsigned char buffer[]) - { - short len = UnpackS(buffer); - std::string temp; - temp.resize(len); - - buffer += 2; - for(int i = 0; i < len; i++) - { - temp[i] = buffer[i]; - } - - return temp; - } - - long double Unpack754(unsigned __int64 i, unsigned bits, unsigned expbits) - { - long double result; - long long shift; - unsigned bias; - unsigned significandbits = bits - expbits - 1; // -1 for sign bit - - if (i == 0) - return 0.0; - - // pull the significand - result = (long double)(i&((1LL << significandbits) - 1)); // mask - result /= (1LL << significandbits); // convert back to float - result += 1.0f; // add the one back on - - // deal with the exponent - bias = (1 << (expbits - 1)) - 1; - shift = ((i >> significandbits) & ((1LL << expbits) - 1)) - bias; - while(shift > 0) - { - result *= 2.0; - shift--; - } - while(shift < 0) - { - result /= 2.0; - shift++; - } - - // sign it - result *= (i >> (bits - 1)) & 1 ? -1.0 : 1.0; - - return result; - } - } - } -} - -/* -int32_t pack(unsigned char* buffer, char* format, ...) -{ - va_list ap; - int16_t h; - int32_t l; - int8_t c; - float f; - double d; - char* s; - int32_t size = 0, len; - - va_start(ap, format); - - for(; *format != '\0'; format++) - { - switch(*format) - { - case 'h': // 16-bit - size += 2; - h = (int16_t)va_arg(ap, int); - packi16(buffer, h); - buffer += 2; - break; - case 'l': // 32-bit - size += 4; - l = va_arg(ap, int32_t); - packi32(buffer, l); - buffer += 4; - break; - case 'c': // 8-bit - size += 1; - c = (int8_t)va_arg(ap, int); - *buffer++ = (c >> 0)&0xff; - break; - case 'f': // float (32-bit) - size += 4; - f = (float)va_arg(ap, double); - //l = pack754(f, 32, 8); - packi32(buffer, l); - buffer += 4; - break; - case 'd': // double (64-bit) - size += 8; - d = (float)va_arg(ap, double); - //l = pack754(f, 64, 11); - packi32(buffer, l); - buffer += 4; - break; - case 's': // string - s = va_arg(ap, char*); - len = strlen(s); - size += len + 2; - packi16(buffer, len); - buffer += 2; - memcpy(buffer, s, len); - buffer += len; - break; - } - } - - va_end(ap); - - return size; -} -*/ - -/* -void unpack(unsigned char* buffer, char* format, ...) -{ - va_list ap; - int16_t* h; - int32_t* l; - int32_t pf; - int64_t pd; - int8_t* c; - float* f; - double* d; - char* s; - int32_t len, count, maxstrlen = 0; - - va_start(ap, format); - - for(; *format != '\0'; format++) - { - switch(*format) - { - case 'h': // 16-bit - h = va_arg(ap, int16_t*); - *h = unpacki16(buffer); - buffer += 2; - break; - case 'l': // 32-bit - l = va_arg(ap, int32_t*); - *l = unpacki32(buffer); - buffer += 4; - break; - case 'c': // 8-bit - c = va_arg(ap, int8_t*); - *c = *buffer++; - break; - case 'f': // float - f = va_arg(ap, float*); - pf = unpacki32(buffer); - buffer += 4; - //*f = unpack754(pf, 32, 8); - break; - case 'd': // double (64-bit) - d = va_arg(ap, double*); - pd = unpacki64(buffer); - buffer += 8; - //*d = unpack754(pf, 64, 11); - break; - case 's': // string - s = va_arg(ap, char*); - len = unpacki16(buffer); - buffer += 2; - if (maxstrlen > 0 && len > maxstrlen) count = maxstrlen - 1; - else count = len; - memcpy(s, buffer, count); - s[count] = '\0'; - buffer += len; - break; - default: - if (isdigit(*format)) // track max str len - { - maxstrlen = maxstrlen * 10 + (*format-'0'); - } - } - - if(!isdigit(*format)) - maxstrlen = 0; - } - - va_end(ap); -}*/ \ No newline at end of file diff --git a/Code/Network/NetworkDependencies/Packing.h b/Code/Network/NetworkDependencies/Packing.h deleted file mode 100644 index aaf3a1b8..00000000 --- a/Code/Network/NetworkDependencies/Packing.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef PACKING_H -#define PACKING_H - -///////////////////////////////////// -// Created by Pontus Fransson 2013 // -///////////////////////////////////// - -#include - -/****************************** - Packing -******************************/ -namespace Oyster -{ - namespace Network - { - namespace Packing - { - //bool (1-bit) - void Pack(unsigned char buffer[], bool i); - - //char (8-bit) - void Pack(unsigned char buffer[], char i); - void Pack(unsigned char buffer[], unsigned char i); // unsigned - - //short (16-bit) - void Pack(unsigned char buffer[], short i); - void Pack(unsigned char buffer[], unsigned short i); // unsigned - - //int (32-bit) - void Pack(unsigned char buffer[], int i); - void Pack(unsigned char buffer[], unsigned int i); // unsigned - - //__int64 (64-bit) - void Pack(unsigned char buffer[], __int64 i); - void Pack(unsigned char buffer[], unsigned __int64 i); // unsigned - - //floating point (32, 64-bit) - void Pack(unsigned char buffer[], float i); - void Pack(unsigned char buffer[], double i); - - //string - void Pack(unsigned char buffer[], char str[]); - void Pack(unsigned char buffer[], std::string& str); - - unsigned __int64 Pack754(long double f, unsigned bits, unsigned expbits); - - /****************************** - Unpacking - ******************************/ - - //bool (1-bit) - bool Unpackb(unsigned char buffer[]); - - //char (8-bit) - char Unpackc(unsigned char buffer[]); - unsigned char UnpackC(unsigned char buffer[]); // unsigned - - //short (16-bit) - short Unpacks(unsigned char buffer[]); - unsigned short UnpackS(unsigned char buffer[]); // unsigned - - //int (32-bit) - int Unpacki(unsigned char buffer[]); - unsigned int UnpackI(unsigned char buffer[]); // unsigned - - //__int64 (64-bit) - __int64 Unpacki64(unsigned char buffer[]); - unsigned __int64 UnpackI64(unsigned char buffer[]); // unsigned - - //floating point (32, 64-bit) - float Unpackf(unsigned char buffer[]); - double Unpackd(unsigned char buffer[]); - - //string - char* UnpackCStr(unsigned char buffer[]); - std::string UnpackStr(unsigned char buffer[]); - - long double Unpack754(unsigned __int64 i, unsigned bits, unsigned expbits); - } - } -} - - -//int32_t pack(unsigned char* buffer, char* format, ...); - -//void unpack(unsigned char* buffer, char* format, ...); - - -/*********************************************** -* This table is used for naming pack/unpack functions. -* It's also used to identify types in the 'format' string in function pack()/unpack() -* -* bits |signed unsigned float string -* -----+---------------------------------- -* 1 | b -* 8 | c C -* 16 | s S f -* 32 | i I d -* 64 | q Q g -* - | str -* -* (16-bit unsigned length is automatically added in front of strings) -* -*/ - -#endif \ No newline at end of file From 5b984db9ba90e0ee9c9a4c206bcc0c729e0d26b0 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Mon, 27 Jan 2014 13:25:09 +0100 Subject: [PATCH 32/76] GL - gameState merge --- Code/DanBias.sln | 27 +-- .../DanBiasGame/GameClientState/GameState.cpp | 228 +++++++----------- 2 files changed, 104 insertions(+), 151 deletions(-) diff --git a/Code/DanBias.sln b/Code/DanBias.sln index 68c4cdfa..5fc5d0db 100644 --- a/Code/DanBias.sln +++ b/Code/DanBias.sln @@ -44,7 +44,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameProtocols", "Game\GameP EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasServerLauncher", "Game\DanBiasServerLauncher\DanBiasServerLauncher.vcxproj", "{060B1890-CBF3-4808-BA99-A4776222093B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aDanBiasGameLauncher", "Game\aDanBiasGameLauncher\aDanBiasGameLauncher.vcxproj", "{666FEA52-975F-41CD-B224-B19AF3C0ABBA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Physics lab", "Physics lab\Physics lab.vcxproj", "{5128BD77-6472-4C4A-BE6F-724AD0E589C2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -272,18 +272,18 @@ Global {060B1890-CBF3-4808-BA99-A4776222093B}.Release|Win32.Build.0 = Release|Win32 {060B1890-CBF3-4808-BA99-A4776222093B}.Release|x64.ActiveCfg = Release|x64 {060B1890-CBF3-4808-BA99-A4776222093B}.Release|x64.Build.0 = Release|x64 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Mixed Platforms.Build.0 = Debug|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Win32.ActiveCfg = Debug|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Win32.Build.0 = Debug|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|x64.ActiveCfg = Debug|x64 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|x64.Build.0 = Debug|x64 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Mixed Platforms.ActiveCfg = Release|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Mixed Platforms.Build.0 = Release|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.ActiveCfg = Release|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.Build.0 = Release|Win32 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.ActiveCfg = Release|x64 - {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.Build.0 = Release|x64 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Debug|Win32.ActiveCfg = Debug|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Debug|Win32.Build.0 = Debug|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Debug|x64.ActiveCfg = Debug|x64 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Debug|x64.Build.0 = Debug|x64 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|Mixed Platforms.Build.0 = Release|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|Win32.ActiveCfg = Release|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|Win32.Build.0 = Release|Win32 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|x64.ActiveCfg = Release|x64 + {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -299,6 +299,5 @@ Global {8690FDDF-C5B7-4C42-A337-BD5243F29B85} = {20720CA7-795C-45AD-A302-9383A6DD503A} {DA2AA800-ED64-4649-8B3B-E7F1E3968B78} = {20720CA7-795C-45AD-A302-9383A6DD503A} {060B1890-CBF3-4808-BA99-A4776222093B} = {20720CA7-795C-45AD-A302-9383A6DD503A} - {666FEA52-975F-41CD-B224-B19AF3C0ABBA} = {20720CA7-795C-45AD-A302-9383A6DD503A} EndGlobalSection EndGlobal diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index e141c8be..b86fe931 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -4,7 +4,7 @@ #include "C_obj/C_DynamicObj.h" #include #include "NetworkClient.h" -#include "Camera.h" + using namespace DanBias::Client; @@ -17,6 +17,7 @@ struct GameState::myData int modelCount; Oyster::Network::NetworkClient* nwClient; gameStateState state; + }privData; @@ -37,35 +38,22 @@ GameState::~GameState(void) bool GameState::Init(Oyster::Network::NetworkClient* nwClient) { // load models - camera = new Camera; privData = new myData(); privData->state = gameStateState_loading; privData->nwClient = nwClient; privData->state = LoadGame(); - return true; } GameState::gameStateState GameState::LoadGame() { Oyster::Graphics::Definitions::Pointlight plight; - plight.Pos = Oyster::Math::Float3(0,15,5); + plight.Pos = Oyster::Math::Float3(0,3,0); plight.Color = Oyster::Math::Float3(0,1,0); - plight.Radius = 50; + plight.Radius = 5; plight.Bright = 2; - Oyster::Graphics::API::AddLight(plight); - plight.Pos = Oyster::Math::Float3(10,15,5); - plight.Color = Oyster::Math::Float3(1,0,0); - plight.Radius = 50; - plight.Bright = 2; - Oyster::Graphics::API::AddLight(plight); - plight.Pos = Oyster::Math::Float3(10,-15,5); - plight.Color = Oyster::Math::Float3(0,0,1); - plight.Radius = 50; - plight.Bright = 2; - Oyster::Graphics::API::AddLight(plight); LoadModels(L"map"); - InitCamera(Oyster::Math::Float3(0,0,20.0f)); + InitCamera(Oyster::Math::Float3(0,0,5.4f)); return gameStateState_playing; } bool GameState::LoadModels(std::wstring mapFile) @@ -101,15 +89,6 @@ bool GameState::LoadModels(std::wstring mapFile) modelData.world = modelData.world * translate; modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; modelData.id ++; - - translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(0,0,0)); - Oyster::Math3D::Float4x4 scale = Oyster::Math3D::Float4x4::identity; - scale.v[0].x = 8; - scale.v[1].y = 8; - scale.v[2].z = 8; - modelData.world = scale; //modelData.world * translate - modelData.modelPath = L"ball.dan"; - modelData.id ++; obj = new C_DynamicObj(); privData->object.push_back(obj); @@ -120,19 +99,10 @@ bool GameState::LoadModels(std::wstring mapFile) } bool GameState::InitCamera(Oyster::Math::Float3 startPos) { - Oyster::Math::Float3 dir = Oyster::Math::Float3(0,0,-1); - Oyster::Math::Float3 up =Oyster::Math::Float3(0,1,0); - Oyster::Math::Float3 pos = Oyster::Math::Float3(0, 0, 20); - - camera->LookAt(pos, dir, up); - camera->SetLens(3.14f/2, 1024/768, 1, 1000); - privData->proj = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/2,1024.0f/768.0f,.1f,1000); //privData->proj = Oyster::Math3D::ProjectionMatrix_Orthographic(1024, 768, 1, 1000); Oyster::Graphics::API::SetProjection(privData->proj); - camera->UpdateViewMatrix(); - privData->view = camera->View(); - privData->view = Oyster::Math3D::ViewMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); + privData->view = Oyster::Math3D::OrientationMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); return true; @@ -157,9 +127,84 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI // read server data // update objects { - readKeyInput(KeyInput); - camera->UpdateViewMatrix(); + bool send = false; + GameLogic::Protocol_PlayerMovement movePlayer; + movePlayer.bForward = false; + movePlayer.bBackward = false; + movePlayer.bStrafeLeft = false; + movePlayer.bStrafeRight = false; + movePlayer.bTurnLeft = false; + movePlayer.bTurnRight = false; + if(KeyInput->IsKeyPressed(DIK_W)) + { + + if(!key_forward) + { + movePlayer.bForward = true; + send = true; + key_forward = true; + } + } + else + key_forward = false; + + if(KeyInput->IsKeyPressed(DIK_S)) + { + if(!key_backward) + { + movePlayer.bBackward = true; + send = true; + key_backward = true; + } + } + else + key_backward = false; + + if(KeyInput->IsKeyPressed(DIK_A)) + { + if(!key_strafeLeft) + { + movePlayer.bStrafeLeft = true; + send = true; + key_strafeLeft = true; + } + } + else + key_strafeLeft = false; + + if(KeyInput->IsKeyPressed(DIK_D)) + { + if(!key_strafeRight) + { + movePlayer.bStrafeRight = true; + send = true; + key_strafeRight = true; + } + } + else + key_strafeRight = false; + + + if (privData->nwClient->IsConnected() && send) + { + privData->nwClient->Send(movePlayer); + } + + //send delta mouse movement + if (KeyInput->IsMousePressed()) + { + GameLogic::Protocol_PlayerMouse deltaMouseMove; + deltaMouseMove.dxMouse = KeyInput->GetYaw(); + deltaMouseMove.dyMouse = KeyInput->GetPitch(); + //privData->nwClient->Send(deltaMouseMove); + + } + + // send event data + // + if(KeyInput->IsKeyPressed(DIK_L)) + privData->state = GameState::gameStateState_end; } break; case gameStateState_end: @@ -174,12 +219,10 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI } bool GameState::Render() { - Oyster::Graphics::API::SetView(camera->View()); - //Oyster::Graphics::API::SetProjection(camera->Proj()); - //Oyster::Graphics::API::SetView(privData->view); + Oyster::Graphics::API::SetView(privData->view); Oyster::Graphics::API::SetProjection(privData->proj); Oyster::Graphics::API::NewFrame(); - for (unsigned int i = 0; i < privData->object.size(); i++) + for (int i = 0; i < privData->object.size(); i++) { privData->object[i]->Render(); } @@ -188,7 +231,7 @@ bool GameState::Render() } bool GameState::Release() { - for (unsigned int i = 0; i < privData->object.size(); i++) + for (int i = 0; i < privData->object.size(); i++) { privData->object[i]->Release(); delete privData->object[i]; @@ -199,93 +242,6 @@ bool GameState::Release() privData = NULL; return true; } -void GameState::readKeyInput(InputClass* KeyInput) -{ - - bool send = false; - GameLogic::Protocol_PlayerMovement movePlayer; - movePlayer.bForward = false; - movePlayer.bBackward = false; - movePlayer.bLeft = false; - movePlayer.bRight = false; - - if(KeyInput->IsKeyPressed(DIK_W)) - { - - if(!key_forward) - { - movePlayer.bForward = true; - send = true; - key_forward = true; - } - } - else - key_forward = false; - - if(KeyInput->IsKeyPressed(DIK_S)) - { - if(!key_backward) - { - movePlayer.bBackward = true; - send = true; - key_backward = true; - } - } - else - key_backward = false; - - if(KeyInput->IsKeyPressed(DIK_A)) - { - if(!key_strafeLeft) - { - movePlayer.bLeft = true; - send = true; - key_strafeLeft = true; - } - } - else - key_strafeLeft = false; - - if(KeyInput->IsKeyPressed(DIK_D)) - { - if(!key_strafeRight) - { - movePlayer.bRight = true; - send = true; - key_strafeRight = true; - } - } - else - key_strafeRight = false; - - - if (privData->nwClient->IsConnected() && send) - { - privData->nwClient->Send(movePlayer); - } - - //send delta mouse movement - if (KeyInput->IsMousePressed()) - { - - - camera->Yaw(KeyInput->GetYaw()); - camera->Pitch(KeyInput->GetPitch()); - camera->UpdateViewMatrix(); - GameLogic::Protocol_PlayerLook playerLookDir; - Oyster::Math::Float3 look = camera->GetLook(); - playerLookDir.lookDirX = look.x; - playerLookDir.lookDirY = look.y; - playerLookDir.lookDirZ = look.z; - privData->nwClient->Send(playerLookDir); - } - - - // send event data - // - if(KeyInput->IsKeyPressed(DIK_L)) - privData->state = GameState::gameStateState_end; -} void GameState::Protocol(ProtocolStruct* pos) { @@ -311,16 +267,14 @@ void GameState::Protocol( ObjPos* pos ) world[i] = pos->worldPos[i]; } - for (unsigned int i = 0; i < privData->object.size(); i++) + for (int i = 0; i < privData->object.size(); i++) { if(privData->object[i]->GetId() == pos->object_ID) { privData->object[i]->setPos(world); - //camera->setRight((Oyster::Math::Float3(world[0], world[1], world[2]))); - //camera->setUp((Oyster::Math::Float3(world[4], world[5], world[6]))); - //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); - camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); - camera->UpdateViewMatrix(); + + //privData->view = world; + //privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); } } @@ -352,7 +306,7 @@ void GameState::Protocol( NewObj* newObj ) void DanBias::Client::GameState::Protocol( RemoveObj* obj ) { - for (unsigned int i = 0; i < privData->object.size(); i++) + for (int i = 0; i < privData->object.size(); i++) { if(privData->object[i]->GetId() == obj->object_ID) { From 51a1b3ecff054787361f4021826a5c6b0de97f4b Mon Sep 17 00:00:00 2001 From: Pontus Fransson Date: Mon, 27 Jan 2014 13:54:56 +0100 Subject: [PATCH 33/76] GL - Resource get size --- Code/Misc/Resource/OResourceHandler.cpp | 7 +++++++ Code/Misc/Resource/OysterResource.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/Code/Misc/Resource/OResourceHandler.cpp b/Code/Misc/Resource/OResourceHandler.cpp index 918ecd1d..52d93af0 100644 --- a/Code/Misc/Resource/OResourceHandler.cpp +++ b/Code/Misc/Resource/OResourceHandler.cpp @@ -214,7 +214,14 @@ int OysterResource::GetResourceId(const wchar_t c[]) return -1; } +int OysterResource::GetResourceSize(const OHRESOURCE& resource) +{ + OResource* t = resourcePrivate.FindResource(resource); + if(t) return t->GetResourceSize(); + + return -1; +} OResource* ResourcePrivate::FindResource(const OHRESOURCE& h) const { diff --git a/Code/Misc/Resource/OysterResource.h b/Code/Misc/Resource/OysterResource.h index d6509b54..8adce704 100644 --- a/Code/Misc/Resource/OysterResource.h +++ b/Code/Misc/Resource/OysterResource.h @@ -150,6 +150,11 @@ namespace Oyster * @return Returns the accociated ID */ static int GetResourceId(const wchar_t filename[]); + + + static int GetResourceSize(const OHRESOURCE& resource); + + }; } From 1ed1bab7514b7d5d666fdb205a5c8fd34fcd129c Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Mon, 27 Jan 2014 13:54:57 +0100 Subject: [PATCH 34/76] Created test box that should now be shot at by the player --- .../GameSession/GameSession_Logic.cpp | 43 +++++++++++++------ Code/Game/GameLogic/AttatchmentMassDriver.cpp | 2 +- Code/Game/GameLogic/AttatchmentSocket.cpp | 23 ++-------- Code/Game/GameLogic/AttatchmentSocket.h | 3 +- Code/Game/GameLogic/CollisionManager.cpp | 10 +++-- Code/Game/GameLogic/Game_PlayerData.cpp | 3 +- Code/Game/GameLogic/Level.cpp | 14 ++++++ Code/Game/GameLogic/Level.h | 2 +- Code/Game/GameLogic/Object.cpp | 6 +++ Code/Game/GameLogic/Player.cpp | 18 ++++++-- Code/Game/GameLogic/StaticObject.h | 2 +- Code/Game/GameLogic/Weapon.cpp | 15 ++++++- Code/Game/GameLogic/Weapon.h | 2 +- 13 files changed, 95 insertions(+), 48 deletions(-) diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp index ddf41582..08bb39c9 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp @@ -24,24 +24,39 @@ using namespace GameLogic; namespace DanBias { + //TEST SHIT + GameAPI *game = &GameAPI::Instance(); + DynamicArray players; + + //TEST SHIT + bool GameSession::DoWork( ) { if(this->isRunning) { - if(GetAsyncKeyState(VK_UP)) - { - Protocol_General_Status p(Protocol_General_Status::States_ready); - Send(p.GetProtocol()); - Sleep(100); - } - if(GetAsyncKeyState(VK_DOWN)) - { - Oyster::Math::Float4x4 world = Oyster::Math::Matrix::identity; - Protocol_ObjectCreate p(world, 2, "../Content/crate"); - Send(p.GetProtocol()); - Sleep(100); - } - + //TEST SHIT + //player creation and testing + //players.Resize(10); + + //for(int i = 0; i < 10; i++) + //{ + // players[i] = game->CreatePlayer();WWW + // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_BACKWARD); + // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_FORWARD); + // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_JUMP); + // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_LEFT); + // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_RIGHT); + + // //using weapon testing + // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_PRIMARY_PRESS); + // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_PRIMARY_RELEASE); + // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_SECONDARY_PRESS); + // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_SECONDARY_RELEASE); + // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_UTILLITY_PRESS); + //} + + + //TEST SHIT double dt = this->timer.getElapsedSeconds(); gameInstance.SetFrameTimeLength((float)dt); diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index 692e4ef4..d110eb37 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -45,7 +45,7 @@ void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage, ********************************************************/ void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float dt) { - Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt); + //Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt); Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), owner->GetPosition()); Oyster::Math::Float4x4 hitSpace = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/4,1,1,20); Oyster::Collision3D::Frustrum hitFrustum = Oyster::Collision3D::Frustrum(Oyster::Math3D::ViewProjectionMatrix(aim,hitSpace)); diff --git a/Code/Game/GameLogic/AttatchmentSocket.cpp b/Code/Game/GameLogic/AttatchmentSocket.cpp index a3d9097c..4a663bef 100644 --- a/Code/Game/GameLogic/AttatchmentSocket.cpp +++ b/Code/Game/GameLogic/AttatchmentSocket.cpp @@ -5,25 +5,10 @@ using namespace GameLogic; using namespace Utility::DynamicMemory; -struct AttatchmentSocket::PrivateData -{ - PrivateData() - { - - } - - ~PrivateData() - { - - } - - SmartPointer attatchment; - - -}myData; AttatchmentSocket::AttatchmentSocket(void) { + this->attatchment = 0; } @@ -34,17 +19,17 @@ AttatchmentSocket::~AttatchmentSocket(void) IAttatchment* AttatchmentSocket::GetAttatchment() { - return myData->attatchment; + return this->attatchment; } void AttatchmentSocket::SetAttatchment(IAttatchment *attatchment) { - myData->attatchment = attatchment; + this->attatchment = attatchment; } void AttatchmentSocket::RemoveAttatchment() { - myData->attatchment = 0; + this->attatchment = 0; } diff --git a/Code/Game/GameLogic/AttatchmentSocket.h b/Code/Game/GameLogic/AttatchmentSocket.h index 2257dd7a..8c5745c5 100644 --- a/Code/Game/GameLogic/AttatchmentSocket.h +++ b/Code/Game/GameLogic/AttatchmentSocket.h @@ -19,8 +19,7 @@ namespace GameLogic void RemoveAttatchment(); private: - struct PrivateData; - PrivateData *myData; + IAttatchment *attatchment; }; } #endif \ No newline at end of file diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index f01dac1c..41908167 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -4,6 +4,7 @@ #include "Player.h" #include "Level.h" #include "AttatchmentMassDriver.h" +#include "Game.h" using namespace Oyster; @@ -15,8 +16,9 @@ using namespace GameLogic; //Physics::ICustomBody::SubscriptMessage void Player::PlayerCollision(Oyster::Physics::ICustomBody *rigidBodyPlayer, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) { - Player *player = ((Player*)(rigidBodyPlayer->GetCustomTag())); - Object *realObj = (Object*)obj->GetCustomTag(); + + Player *player = ((Game::PlayerData*)(rigidBodyPlayer->GetCustomTag()))->player; + Object *realObj = (Object*)obj->GetCustomTag(); //needs to be changed? switch (realObj->GetType()) { @@ -63,13 +65,13 @@ using namespace GameLogic; } //Oyster::Physics::ICustomBody::SubscriptMessage - void Level::LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) + void Level::LevelCollision(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) { //return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; } void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) { - Oyster::Math::Float4 pushForce = Oyster::Math::Float4(owner->GetLookDir()) * (500); + Oyster::Math::Float4 pushForce = Oyster::Math::Float4(1,0,0,0) * (500); ((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); } diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index 8e4b7707..fd0d9b89 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -6,6 +6,7 @@ using namespace GameLogic; Game::PlayerData::PlayerData() { Oyster::Physics::API::SimpleBodyDescription sbDesc; + //set some stats that are appropriate to a player //create rigidbody Oyster::Physics::ICustomBody *rigidBody = Oyster::Physics::API::Instance().CreateRigidBody(sbDesc).Release(); @@ -60,5 +61,5 @@ OBJECT_TYPE Game::PlayerData::GetObjectType() const } void Game::PlayerData::Rotate(const Oyster::Math3D::Float3 lookDir) { - this->player->Rotate(lookDir); + //this->player->Rotate(lookDir); } \ No newline at end of file diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index ab25b059..0e99d160 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -38,6 +38,20 @@ void Level::InitiateLevel(float radius) levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); + + API::SphericalBodyDescription sbDesc_TestBox; + sbDesc.centerPosition = Oyster::Math::Float4(3,15,0,0); + sbDesc.ignoreGravity = true; + sbDesc.radius = 8; //radius; + sbDesc.mass = 10e12f; + //sbDesc.mass = 0; //10^16 + + + + ICustomBody* rigidBody_TestBox = API::Instance().CreateRigidBody(sbDesc_TestBox).Release(); + + DynamicObject *testBox = new DynamicObject(rigidBody_TestBox,LevelCollision,OBJECT_TYPE::OBJECT_TYPE_BOX); + /*API::Gravity gravityWell; gravityWell.gravityType = API::Gravity::GravityType_Well; diff --git a/Code/Game/GameLogic/Level.h b/Code/Game/GameLogic/Level.h index ce299b78..6aa54002 100644 --- a/Code/Game/GameLogic/Level.h +++ b/Code/Game/GameLogic/Level.h @@ -57,7 +57,7 @@ namespace GameLogic * @param rigidBodyLevel: physics object of the level * @param obj: physics object for the object that collided with the level ********************************************************/ - static void LevelCollision(const Oyster::Physics::ICustomBody *rigidBodyLevel, const Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); + static void LevelCollision(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); private: TeamManager teamManager; diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 46695ddf..e156f262 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -62,6 +62,12 @@ Object::Object(ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type) Object::Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) { + Oyster::Physics::API::Instance().AddObject(rigidBody); + rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); + + this->rigidBody = rigidBody; + this->type = type; + this->objectID = GID(); } diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index 92e5b61e..407117bc 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -23,13 +23,25 @@ Player::Player() Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) :DynamicObject(rigidBody, collisionFunc, type) { - + weapon = new Weapon(2,this); + + this->life = 100; + this->teamID = -1; + this->playerState = PLAYER_STATE_IDLE; + lookDir = Oyster::Math::Float4(0,0,-1,0); + + setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); + setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); } Player::~Player(void) { - delete weapon; - weapon = NULL; + if(weapon) + { + delete weapon; + weapon = NULL; + } + } diff --git a/Code/Game/GameLogic/StaticObject.h b/Code/Game/GameLogic/StaticObject.h index 13d7f73e..5306d43b 100644 --- a/Code/Game/GameLogic/StaticObject.h +++ b/Code/Game/GameLogic/StaticObject.h @@ -17,7 +17,7 @@ namespace GameLogic public: StaticObject(); StaticObject(void* collisionFunc, OBJECT_TYPE type); - StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type); + //StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type); StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); StaticObject(OBJECT_TYPE type); ~StaticObject(void); diff --git a/Code/Game/GameLogic/Weapon.cpp b/Code/Game/GameLogic/Weapon.cpp index 994ccac5..a4c87e9e 100644 --- a/Code/Game/GameLogic/Weapon.cpp +++ b/Code/Game/GameLogic/Weapon.cpp @@ -1,5 +1,6 @@ #include "Weapon.h" #include "AttatchmentMassDriver.h" +#include "Player.h" using namespace GameLogic; @@ -15,9 +16,21 @@ Weapon::Weapon() attatchmentSockets = 0; } -Weapon::Weapon(int MaxNrOfSockets) +Weapon::Weapon(int MaxNrOfSockets,Player *owner) { attatchmentSockets.Resize(MaxNrOfSockets); + attatchmentSockets[0] = new AttatchmentSocket(); + + weaponState = WEAPON_STATE_IDLE; + currentNrOfAttatchments = 0; + selectedAttatchment = 0; + + //give the weapon a massdriver on socket 0 + IAttatchment *mD = new AttatchmentMassDriver(*owner); + attatchmentSockets[0]->SetAttatchment(mD); + this->currentNrOfAttatchments = 1; + SelectAttatchment(0); + //give the weapon a massdriver on socket 0 } diff --git a/Code/Game/GameLogic/Weapon.h b/Code/Game/GameLogic/Weapon.h index 0d3c09d8..5138b2ac 100644 --- a/Code/Game/GameLogic/Weapon.h +++ b/Code/Game/GameLogic/Weapon.h @@ -16,7 +16,7 @@ namespace GameLogic { public: Weapon(void); - Weapon(int nrOfAttatchmentSockets); + Weapon(int nrOfAttatchmentSockets, Player *owner); ~Weapon(void); void Use(const WEAPON_FIRE &usage, float dt); From 4085a5f5e2efcccb11a913e78c313f7cb0b4d2e0 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Mon, 27 Jan 2014 13:56:31 +0100 Subject: [PATCH 35/76] GL - added shot protocol --- .../DanBiasGame/GameClientState/GameState.cpp | 239 +++++++++++------- .../DanBiasGame/GameClientState/GameState.h | 1 + .../GameSession/GameSession_Events.cpp | 2 + Code/Game/GameLogic/Player.cpp | 24 +- Code/Game/GameLogic/Player.h | 2 +- Code/Game/GameProtocols/PlayerProtocols.h | 26 ++ .../GameProtocols/ProtocolIdentificationID.h | 13 +- 7 files changed, 201 insertions(+), 106 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index b86fe931..f133a394 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -4,7 +4,7 @@ #include "C_obj/C_DynamicObj.h" #include #include "NetworkClient.h" - +#include "Camera.h" using namespace DanBias::Client; @@ -17,7 +17,6 @@ struct GameState::myData int modelCount; Oyster::Network::NetworkClient* nwClient; gameStateState state; - }privData; @@ -38,22 +37,35 @@ GameState::~GameState(void) bool GameState::Init(Oyster::Network::NetworkClient* nwClient) { // load models + camera = new Camera; privData = new myData(); privData->state = gameStateState_loading; privData->nwClient = nwClient; privData->state = LoadGame(); + return true; } GameState::gameStateState GameState::LoadGame() { Oyster::Graphics::Definitions::Pointlight plight; - plight.Pos = Oyster::Math::Float3(0,3,0); + plight.Pos = Oyster::Math::Float3(0,15,5); plight.Color = Oyster::Math::Float3(0,1,0); - plight.Radius = 5; + plight.Radius = 50; plight.Bright = 2; + Oyster::Graphics::API::AddLight(plight); + plight.Pos = Oyster::Math::Float3(10,15,5); + plight.Color = Oyster::Math::Float3(1,0,0); + plight.Radius = 50; + plight.Bright = 2; + Oyster::Graphics::API::AddLight(plight); + plight.Pos = Oyster::Math::Float3(10,-15,5); + plight.Color = Oyster::Math::Float3(0,0,1); + plight.Radius = 50; + plight.Bright = 2; + Oyster::Graphics::API::AddLight(plight); LoadModels(L"map"); - InitCamera(Oyster::Math::Float3(0,0,5.4f)); + InitCamera(Oyster::Math::Float3(0,0,20.0f)); return gameStateState_playing; } bool GameState::LoadModels(std::wstring mapFile) @@ -89,6 +101,15 @@ bool GameState::LoadModels(std::wstring mapFile) modelData.world = modelData.world * translate; modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; modelData.id ++; + + translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(0,0,0)); + Oyster::Math3D::Float4x4 scale = Oyster::Math3D::Float4x4::identity; + scale.v[0].x = 8; + scale.v[1].y = 8; + scale.v[2].z = 8; + modelData.world = scale; //modelData.world * translate + modelData.modelPath = L"ball.dan"; + modelData.id ++; obj = new C_DynamicObj(); privData->object.push_back(obj); @@ -99,10 +120,19 @@ bool GameState::LoadModels(std::wstring mapFile) } bool GameState::InitCamera(Oyster::Math::Float3 startPos) { + Oyster::Math::Float3 dir = Oyster::Math::Float3(0,0,-1); + Oyster::Math::Float3 up =Oyster::Math::Float3(0,1,0); + Oyster::Math::Float3 pos = Oyster::Math::Float3(0, 0, 20); + + camera->LookAt(pos, dir, up); + camera->SetLens(3.14f/2, 1024/768, 1, 1000); + privData->proj = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/2,1024.0f/768.0f,.1f,1000); //privData->proj = Oyster::Math3D::ProjectionMatrix_Orthographic(1024, 768, 1, 1000); Oyster::Graphics::API::SetProjection(privData->proj); - + camera->UpdateViewMatrix(); + privData->view = camera->View(); + privData->view = Oyster::Math3D::ViewMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); privData->view = Oyster::Math3D::OrientationMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); return true; @@ -127,84 +157,9 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI // read server data // update objects { - bool send = false; - GameLogic::Protocol_PlayerMovement movePlayer; - movePlayer.bForward = false; - movePlayer.bBackward = false; - movePlayer.bStrafeLeft = false; - movePlayer.bStrafeRight = false; - movePlayer.bTurnLeft = false; - movePlayer.bTurnRight = false; + readKeyInput(KeyInput); + camera->UpdateViewMatrix(); - if(KeyInput->IsKeyPressed(DIK_W)) - { - - if(!key_forward) - { - movePlayer.bForward = true; - send = true; - key_forward = true; - } - } - else - key_forward = false; - - if(KeyInput->IsKeyPressed(DIK_S)) - { - if(!key_backward) - { - movePlayer.bBackward = true; - send = true; - key_backward = true; - } - } - else - key_backward = false; - - if(KeyInput->IsKeyPressed(DIK_A)) - { - if(!key_strafeLeft) - { - movePlayer.bStrafeLeft = true; - send = true; - key_strafeLeft = true; - } - } - else - key_strafeLeft = false; - - if(KeyInput->IsKeyPressed(DIK_D)) - { - if(!key_strafeRight) - { - movePlayer.bStrafeRight = true; - send = true; - key_strafeRight = true; - } - } - else - key_strafeRight = false; - - - if (privData->nwClient->IsConnected() && send) - { - privData->nwClient->Send(movePlayer); - } - - //send delta mouse movement - if (KeyInput->IsMousePressed()) - { - GameLogic::Protocol_PlayerMouse deltaMouseMove; - deltaMouseMove.dxMouse = KeyInput->GetYaw(); - deltaMouseMove.dyMouse = KeyInput->GetPitch(); - //privData->nwClient->Send(deltaMouseMove); - - } - - // send event data - // - if(KeyInput->IsKeyPressed(DIK_L)) - privData->state = GameState::gameStateState_end; } break; case gameStateState_end: @@ -219,10 +174,12 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI } bool GameState::Render() { - Oyster::Graphics::API::SetView(privData->view); + Oyster::Graphics::API::SetView(camera->View()); + //Oyster::Graphics::API::SetProjection(camera->Proj()); + //Oyster::Graphics::API::SetView(privData->view); Oyster::Graphics::API::SetProjection(privData->proj); Oyster::Graphics::API::NewFrame(); - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { privData->object[i]->Render(); } @@ -231,7 +188,7 @@ bool GameState::Render() } bool GameState::Release() { - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { privData->object[i]->Release(); delete privData->object[i]; @@ -242,6 +199,104 @@ bool GameState::Release() privData = NULL; return true; } +void GameState::readKeyInput(InputClass* KeyInput) +{ + + bool send = false; + GameLogic::Protocol_PlayerMovement movePlayer; + movePlayer.bForward = false; + movePlayer.bBackward = false; + movePlayer.bLeft = false; + movePlayer.bRight = false; + + if(KeyInput->IsKeyPressed(DIK_W)) + { + + if(!key_forward) + { + movePlayer.bForward = true; + send = true; + key_forward = true; + } + } + else + key_forward = false; + + if(KeyInput->IsKeyPressed(DIK_S)) + { + if(!key_backward) + { + movePlayer.bBackward = true; + send = true; + key_backward = true; + } + } + else + key_backward = false; + + if(KeyInput->IsKeyPressed(DIK_A)) + { + if(!key_strafeLeft) + { + movePlayer.bLeft = true; + send = true; + key_strafeLeft = true; + } + } + else + key_strafeLeft = false; + + if(KeyInput->IsKeyPressed(DIK_D)) + { + if(!key_strafeRight) + { + movePlayer.bRight = true; + send = true; + key_strafeRight = true; + } + } + else + key_strafeRight = false; + + + if (privData->nwClient->IsConnected() && send) + { + privData->nwClient->Send(movePlayer); + } + + //send delta mouse movement + if (KeyInput->IsMousePressed()) + { + + + camera->Yaw(KeyInput->GetYaw()); + camera->Pitch(KeyInput->GetPitch()); + camera->UpdateViewMatrix(); + GameLogic::Protocol_PlayerLook playerLookDir; + Oyster::Math::Float3 look = camera->GetLook(); + playerLookDir.lookDirX = look.x; + playerLookDir.lookDirY = look.y; + playerLookDir.lookDirZ = look.z; + privData->nwClient->Send(playerLookDir); + } + if(KeyInput->IsKeyPressed(DIK_Z)) + { + if(!key_Shoot) + { + GameLogic::Protocol_PlayerShot playerShot; + playerShot.hasShot = true; + privData->nwClient->Send(playerShot); + key_Shoot = true; + } + } + else + key_Shoot = false; + + // send event data + // + if(KeyInput->IsKeyPressed(DIK_L)) + privData->state = GameState::gameStateState_end; +} void GameState::Protocol(ProtocolStruct* pos) { @@ -267,14 +322,16 @@ void GameState::Protocol( ObjPos* pos ) world[i] = pos->worldPos[i]; } - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { if(privData->object[i]->GetId() == pos->object_ID) { privData->object[i]->setPos(world); - - //privData->view = world; - //privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); + //camera->setRight((Oyster::Math::Float3(world[0], world[1], world[2]))); + //camera->setUp((Oyster::Math::Float3(world[4], world[5], world[6]))); + //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); + camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); + camera->UpdateViewMatrix(); } } @@ -306,7 +363,7 @@ void GameState::Protocol( NewObj* newObj ) void DanBias::Client::GameState::Protocol( RemoveObj* obj ) { - for (int i = 0; i < privData->object.size(); i++) + for (unsigned int i = 0; i < privData->object.size(); i++) { if(privData->object[i]->GetId() == obj->object_ID) { diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.h b/Code/Game/DanBiasGame/GameClientState/GameState.h index a6ed7d3f..30884043 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.h +++ b/Code/Game/DanBiasGame/GameClientState/GameState.h @@ -22,6 +22,7 @@ private: bool key_backward; bool key_strafeRight; bool key_strafeLeft; + bool key_Shoot; Camera* camera; struct myData; diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index 44392da0..1c1e5127 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -67,6 +67,8 @@ namespace DanBias case protocol_Gameplay_PlayerChangeWeapon: break; + case protocol_Gameplay_PlayerShot: + c->GetPlayer()->UseWeapon(GameLogic::WEAPON_USE_PRIMARY_PRESS); case protocol_Gameplay_ObjectDamage: break; diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index 92e5b61e..f2f2f02b 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -18,14 +18,13 @@ Player::Player() lookDir = Oyster::Math::Float4(0,0,-1,0); setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); + //setState.SetRotation(Oyster::Math::Float4(0,0,0,0).Normalize()); } - Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) - :DynamicObject(rigidBody, collisionFunc, type) + :DynamicObject(rigidBody, collisionFunc, type) { - -} +} Player::~Player(void) { delete weapon; @@ -70,6 +69,7 @@ void Player::MoveBackwards() void Player::MoveRight() { //Do cross product with forward vector and negative gravity vector + // temp up vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r = (-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); setState.ApplyLinearImpulse(r * 20 * this->gameInstance->GetFrameTime()); @@ -78,6 +78,7 @@ void Player::MoveRight() void Player::MoveLeft() { //Do cross product with forward vector and negative gravity vector + // temp up vector Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); //Oyster::Math::Float4 r1 = -(-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); //Still get zero setState.ApplyLinearImpulse(-r * 20 * this->gameInstance->GetFrameTime()); @@ -96,11 +97,18 @@ void Player::Respawn(Oyster::Math::Float3 spawnPoint) this->lookDir = Oyster::Math::Float4(1,0,0); } -void Player::Rotate(float dx, float dy) +void Player::Rotate(const Oyster::Math3D::Float3 lookDir) { - - //this->lookDir = lookDir; - + + this->lookDir = lookDir; + + Oyster::Math::Float4 up(0,1,0,0);//-setState.GetGravityNormal(); + Oyster::Math::Float4 pos = setState.GetCenterPosition(); + Oyster::Math::Float4x4 world = Oyster::Math3D::OrientationMatrix_LookAtDirection(lookDir, up.xyz, pos.xyz); + // cant set rotation + //setState.SetOrientation(world); + //this->lookDir = lookDir - up.xyz; + //this->setState.AddRotation(Oyster::Math::Float4(x, y)); //this->setState.SetRotation(); } diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index 2b5a64fe..ac799670 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -43,7 +43,7 @@ namespace GameLogic void Respawn(Oyster::Math::Float3 spawnPoint); - void Rotate(float x, float y); + void Rotate(const Oyster::Math3D::Float3 lookDir); /******************************************************** * Collision function for player, this is to be sent to physics through the subscribe function with the rigidbody diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index 9ad819e8..ffb009cf 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -122,6 +122,32 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; + struct Protocol_PlayerShot :public Oyster::Network::CustomProtocolObject + { + bool hasShot; + + Protocol_PlayerShot() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerShot; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; + } + const Protocol_PlayerShot& operator=(Oyster::Network::CustomNetProtocol& val) + { + hasShot = val[1].value.netBool; + return *this; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = hasShot; + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + } #endif // !GAMELOGIC_PLAYER_PROTOCOLS_H diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index 353338b6..bb0414f8 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -49,12 +49,13 @@ #define protocol_Gameplay_PlayerMovement 300 #define protocol_Gameplay_PlayerLookDir 301 #define protocol_Gameplay_PlayerChangeWeapon 302 -#define protocol_Gameplay_ObjectPickup 303 -#define protocol_Gameplay_ObjectDamage 304 -#define protocol_Gameplay_ObjectPosition 305 -#define protocol_Gameplay_ObjectEnabled 306 -#define protocol_Gameplay_ObjectDisabled 307 -#define protocol_Gameplay_ObjectCreate 308 +#define protocol_Gameplay_PlayerShot 303 +#define protocol_Gameplay_ObjectPickup 304 +#define protocol_Gameplay_ObjectDamage 305 +#define protocol_Gameplay_ObjectPosition 306 +#define protocol_Gameplay_ObjectEnabled 307 +#define protocol_Gameplay_ObjectDisabled 308 +#define protocol_Gameplay_ObjectCreate 309 #define protocol_GameplayMAX 399 From 91bf6ce901e4b56d1e7ac3fba9a23541be924c30 Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Mon, 27 Jan 2014 13:57:18 +0100 Subject: [PATCH 36/76] GL- LevelFormat fix --- Bin/map.txt | Bin 0 -> 48 bytes Code/Game/GameLogic/GameLogic.vcxproj | 2 +- Code/Game/GameLogic/LevelParser.cpp | 26 +++--- Code/Game/GameLogic/ParseFunctions.cpp | 110 +++++++++++++------------ Code/Game/GameLogic/ParseFunctions.h | 19 +++++ Code/Game/GameLogic/ParserFunctions.h | 22 ----- 6 files changed, 94 insertions(+), 85 deletions(-) create mode 100644 Bin/map.txt create mode 100644 Code/Game/GameLogic/ParseFunctions.h delete mode 100644 Code/Game/GameLogic/ParserFunctions.h diff --git a/Bin/map.txt b/Bin/map.txt new file mode 100644 index 0000000000000000000000000000000000000000..c9d1861205c2960ed93f42a764db732d9c326d2e GIT binary patch literal 48 gcmZQzU|?imU|<4bW~XmUqMWnxW;m}o+K7t{08$7NVE_OC literal 0 HcmV?d00001 diff --git a/Code/Game/GameLogic/GameLogic.vcxproj b/Code/Game/GameLogic/GameLogic.vcxproj index 54ec2d7e..df1d09aa 100644 --- a/Code/Game/GameLogic/GameLogic.vcxproj +++ b/Code/Game/GameLogic/GameLogic.vcxproj @@ -189,7 +189,7 @@ - + diff --git a/Code/Game/GameLogic/LevelParser.cpp b/Code/Game/GameLogic/LevelParser.cpp index 5a709316..a03da6eb 100644 --- a/Code/Game/GameLogic/LevelParser.cpp +++ b/Code/Game/GameLogic/LevelParser.cpp @@ -1,10 +1,12 @@ #include "LevelParser.h" #include "Loader.h" +#include "ParseFunctions.h" using namespace GameLogic; using namespace ::LevelFileLoader; + LevelParser::LevelParser() { } @@ -16,22 +18,23 @@ LevelParser::~LevelParser() // std::vector LevelParser::Parse(std::string filename) { + int stringSize = 0; //Read entire level file. Loader loader; - unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str()); + unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), stringSize); std::vector objects; unsigned int counter = 0; - unsigned int stringSize = 0; + while(counter < stringSize) { //Get typeID - int typeID = 0; - + ObjectTypeHeader typeID; + typeID = parseObjectTypeHeader(buffer); //Unpack ID - switch(typeID) + switch((int)typeID.typeID) { case TypeID_LevelHeader: //Call function @@ -51,24 +54,27 @@ std::vector LevelParser::Parse(std::string filename) return objects; } -// +//för meta information om leveln. Måste göra så den returnerar rätt strukt så fort vi +//vi definierat en! ObjectTypeHeader LevelParser::ParseHeader(std::string filename) { + int stringSize = 0; //Read entire level file. Loader loader; - unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str()); + unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), stringSize); //Find the header in the returned string. unsigned int counter = 0; - unsigned int stringSize = 0; + ObjectTypeHeader header; header.typeID = ObjectType_Level; while(counter < stringSize) { - int typeID = 0; - switch(typeID) + ObjectTypeHeader typeID; + typeID = parseObjectTypeHeader(buffer); + switch(typeID.typeID) { case TypeID_LevelHeader: //Call function diff --git a/Code/Game/GameLogic/ParseFunctions.cpp b/Code/Game/GameLogic/ParseFunctions.cpp index 28199e6b..11e7b0ab 100644 --- a/Code/Game/GameLogic/ParseFunctions.cpp +++ b/Code/Game/GameLogic/ParseFunctions.cpp @@ -2,82 +2,88 @@ // Created by Sam Svensson 2013 // ////////////////////////////////// -#include "ParserFunctions.h" +#include "ParseFunctions.h" #include "../../Misc/Packing/Packing.h" #include using namespace Oyster::Packing; +using namespace GameLogic::LevelFileLoader; using namespace GameLogic; using namespace std; -ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer) +namespace GameLogic { - int i = Unpacki(buffer); + namespace LevelFileLoader + { + ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer) + { + struct ObjectTypeHeader header; + int i = Unpacki(buffer); + header.typeID = (ObjectType)i; + return header; + } - struct ObjectTypeHeader header; - header.typeID = (ObjectType)i; - - return header; -} -ObjectHeader parseObjectHeader (unsigned char* buffer) -{ - struct ObjectHeader header; - int x, y,z; - string s; - int start = 0; + ObjectHeader parseObjectHeader (unsigned char* buffer) + { + struct ObjectHeader header; + int x, y,z; + string s; + int start = 0; - //ModelID - x = Unpacki(buffer); - header.ModelID = (ObjectType)x; + //ModelID + x = Unpacki(buffer); + header.ModelID = (ObjectType)x; - //TextureID - start += 4; - x = Unpacki(&buffer[start]); - header.TextureID = x; + //TextureID + start += 4; + x = Unpacki(&buffer[start]); + header.TextureID = x; - //Position - start += 4; - x = Unpacki(&buffer[start]); + //Position + start += 4; + x = Unpacki(&buffer[start]); - start += 4; - y = Unpacki(&buffer[start]); + start += 4; + y = Unpacki(&buffer[start]); - start += 4; - z = Unpacki(&buffer[start]); + start += 4; + z = Unpacki(&buffer[start]); - header.position[0] = x; - header.position[1] = y; - header.position[2] = z; + header.position[0] = x; + header.position[1] = y; + header.position[2] = z; - //Rotation - start += 4; - x = Unpacki(&buffer[start]); + //Rotation + start += 4; + x = Unpacki(&buffer[start]); - start += 4; - y = Unpacki(&buffer[start]); + start += 4; + y = Unpacki(&buffer[start]); - start += 4; - z = Unpacki(&buffer[start]); + start += 4; + z = Unpacki(&buffer[start]); - header.rotation[0] = x; - header.rotation[1] = y; - header.rotation[2] = z; + header.rotation[0] = x; + header.rotation[1] = y; + header.rotation[2] = z; - //Scale - start += 4; - x = Unpacki(&buffer[start]); + //Scale + start += 4; + x = Unpacki(&buffer[start]); - start += 4; - y = Unpacki(&buffer[start]); + start += 4; + y = Unpacki(&buffer[start]); - start += 4; - z = Unpacki(&buffer[start]); + start += 4; + z = Unpacki(&buffer[start]); - header.scale[0] = x; - header.scale[1] = y; - header.scale[2] = z; + header.scale[0] = x; + header.scale[1] = y; + header.scale[2] = z; - return header; + return header; + } + } } \ No newline at end of file diff --git a/Code/Game/GameLogic/ParseFunctions.h b/Code/Game/GameLogic/ParseFunctions.h new file mode 100644 index 00000000..180f82c3 --- /dev/null +++ b/Code/Game/GameLogic/ParseFunctions.h @@ -0,0 +1,19 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#ifndef PARSERFUNCTIONS_H +#define PARSERFUNCTIONS_H +#include "ObjectDefines.h" + +namespace GameLogic +{ + namespace LevelFileLoader + { + ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. + ObjectHeader parseObjectHeader (unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. + } +} + + +#endif \ No newline at end of file diff --git a/Code/Game/GameLogic/ParserFunctions.h b/Code/Game/GameLogic/ParserFunctions.h deleted file mode 100644 index 0608e77a..00000000 --- a/Code/Game/GameLogic/ParserFunctions.h +++ /dev/null @@ -1,22 +0,0 @@ -////////////////////////////////// -// Created by Sam Svensson 2013 // -////////////////////////////////// - -#ifndef PARSERFUNCTIONS_H -#define PARSERFUNCTIONS_H -#include "ObjectDefines.h" - -namespace GameLogic -{ - namespace LevelFileLoader - { - namespace parseFunctions - { - ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. - ObjectHeader parseObjectHeader (unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. - } - } -} - - -#endif \ No newline at end of file From a8dbf2a4c947824f5e077636d07893e5dba9d14b Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Mon, 27 Jan 2014 14:43:39 +0100 Subject: [PATCH 37/76] GL - added test box to level --- Code/Game/DanBiasServer/GameSession/GameSession.h | 2 +- .../DanBiasServer/GameSession/GameSession_Events.cpp | 6 ++++-- Code/Game/GameLogic/CollisionManager.cpp | 6 +++++- Code/Game/GameLogic/Level.cpp | 12 ++++++------ Code/Game/GameLogic/Level.h | 1 + 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Code/Game/DanBiasServer/GameSession/GameSession.h b/Code/Game/DanBiasServer/GameSession/GameSession.h index aee9b29d..0e64a821 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession.h +++ b/Code/Game/DanBiasServer/GameSession/GameSession.h @@ -30,7 +30,7 @@ namespace DanBias NetworkSession* owner; Utility::DynamicMemory::DynamicArray> clients; }; - + public: GameSession(); virtual~GameSession(); diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index 1c1e5127..d7cb2f39 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -113,8 +113,10 @@ namespace DanBias void GameSession::ObjectMove(GameLogic::IObjectData* movedObject) { - movedObject->GetID(); - movedObject->GetOrientation(); + /*int id= movedObject->GetID(); + Oyster::Math::Float4x4 world = movedObject->GetOrientation(); + Protocol_ObjectPosition p(world, 2); + Send(p.GetProtocol());*/ } diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 41908167..887d1f13 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -73,5 +73,9 @@ using namespace GameLogic; void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) { Oyster::Math::Float4 pushForce = Oyster::Math::Float4(1,0,0,0) * (500); - ((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); + Oyster::Physics::ICustomBody::State state; + state = obj->GetState(); + state.ApplyLinearImpulse(pushForce); + obj->SetState(state); + //((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); } diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 0e99d160..406b81e9 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -39,18 +39,18 @@ void Level::InitiateLevel(float radius) levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); - API::SphericalBodyDescription sbDesc_TestBox; - sbDesc.centerPosition = Oyster::Math::Float4(3,15,0,0); - sbDesc.ignoreGravity = true; - sbDesc.radius = 8; //radius; - sbDesc.mass = 10e12f; + API::SimpleBodyDescription sbDesc_TestBox; + sbDesc_TestBox.centerPosition = Oyster::Math::Float4(3,15,0,0); + sbDesc_TestBox.ignoreGravity = false; + sbDesc_TestBox.mass = 10; + sbDesc_TestBox.size = Oyster::Math::Float4(2,2,2,0); //sbDesc.mass = 0; //10^16 ICustomBody* rigidBody_TestBox = API::Instance().CreateRigidBody(sbDesc_TestBox).Release(); - DynamicObject *testBox = new DynamicObject(rigidBody_TestBox,LevelCollision,OBJECT_TYPE::OBJECT_TYPE_BOX); + testBox = new DynamicObject(rigidBody_TestBox,LevelCollision,OBJECT_TYPE::OBJECT_TYPE_BOX); /*API::Gravity gravityWell; diff --git a/Code/Game/GameLogic/Level.h b/Code/Game/GameLogic/Level.h index 6aa54002..7d280761 100644 --- a/Code/Game/GameLogic/Level.h +++ b/Code/Game/GameLogic/Level.h @@ -66,6 +66,7 @@ namespace GameLogic GameMode gameMode; Utility::DynamicMemory::SmartPointer rigidBodyLevel; StaticObject *levelObj; + DynamicObject *testBox; }; From e11dc6551c8e598c2874b434275fbbfba2a03f4f Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Mon, 27 Jan 2014 14:45:10 +0100 Subject: [PATCH 38/76] GL - ? --- Code/Game/GameLogic/GameAPI.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Code/Game/GameLogic/GameAPI.h b/Code/Game/GameLogic/GameAPI.h index 889466c7..62094675 100644 --- a/Code/Game/GameLogic/GameAPI.h +++ b/Code/Game/GameLogic/GameAPI.h @@ -100,7 +100,6 @@ namespace GameLogic class ILevelData :public IObjectData { public: - }; class DANBIAS_GAMELOGIC_DLL GameAPI From dd2b214122186dd1797ccd2d7af1b6388e14c8ab Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Mon, 27 Jan 2014 14:57:18 +0100 Subject: [PATCH 39/76] =?UTF-8?q?GL-=20la=20in=20memcpy=20f=C3=B6r=20parsi?= =?UTF-8?q?ng=20i=20parseFunctions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Code/Game/GameLogic/LevelParser.cpp | 8 +-- Code/Game/GameLogic/Loader.cpp | 5 +- Code/Game/GameLogic/ParseFunctions.cpp | 70 +++----------------------- Code/Game/GameLogic/ParseFunctions.h | 4 +- 4 files changed, 17 insertions(+), 70 deletions(-) diff --git a/Code/Game/GameLogic/LevelParser.cpp b/Code/Game/GameLogic/LevelParser.cpp index a03da6eb..a957b88a 100644 --- a/Code/Game/GameLogic/LevelParser.cpp +++ b/Code/Game/GameLogic/LevelParser.cpp @@ -31,7 +31,8 @@ std::vector LevelParser::Parse(std::string filename) { //Get typeID ObjectTypeHeader typeID; - typeID = parseObjectTypeHeader(buffer); + typeID = ParseObjectTypeHeader(&buffer[counter]); + //counter += 4; //Unpack ID switch((int)typeID.typeID) @@ -43,7 +44,8 @@ std::vector LevelParser::Parse(std::string filename) case TypeID_Object: //Call function - counter += ObjectHeaderSize; + objects.push_back(ParseObjectHeader(&buffer[counter])); + counter += sizeof(ObjectHeader); break; default: //Couldn't find typeID. FAIL!!!!!! @@ -73,7 +75,7 @@ ObjectTypeHeader LevelParser::ParseHeader(std::string filename) while(counter < stringSize) { ObjectTypeHeader typeID; - typeID = parseObjectTypeHeader(buffer); + typeID = ParseObjectTypeHeader(buffer); switch(typeID.typeID) { case TypeID_LevelHeader: diff --git a/Code/Game/GameLogic/Loader.cpp b/Code/Game/GameLogic/Loader.cpp index 01047b2f..0afb48de 100644 --- a/Code/Game/GameLogic/Loader.cpp +++ b/Code/Game/GameLogic/Loader.cpp @@ -13,7 +13,8 @@ unsigned char* Loader::LoadFile(std::string fileName, int &size) //convert from string to wstring std::wstring temp(fileName.begin(), fileName.end()); - size = temp.size(); //convert from wstring to wchar then loads the file - return (unsigned char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); + unsigned char* buffer = (unsigned char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); + size = OysterResource::GetResourceSize(buffer); + return buffer; } \ No newline at end of file diff --git a/Code/Game/GameLogic/ParseFunctions.cpp b/Code/Game/GameLogic/ParseFunctions.cpp index 11e7b0ab..64405ff5 100644 --- a/Code/Game/GameLogic/ParseFunctions.cpp +++ b/Code/Game/GameLogic/ParseFunctions.cpp @@ -15,75 +15,19 @@ namespace GameLogic { namespace LevelFileLoader { - ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer) + ObjectTypeHeader ParseObjectTypeHeader(unsigned char* buffer) { struct ObjectTypeHeader header; - int i = Unpacki(buffer); - header.typeID = (ObjectType)i; + memcpy(&header, buffer, sizeof(ObjectTypeHeader)); return header; } - ObjectHeader parseObjectHeader (unsigned char* buffer) + ObjectHeader ParseObjectHeader (unsigned char* buffer) { - struct ObjectHeader header; - int x, y,z; - string s; - int start = 0; - - - //ModelID - x = Unpacki(buffer); - header.ModelID = (ObjectType)x; - - //TextureID - start += 4; - x = Unpacki(&buffer[start]); - header.TextureID = x; - - //Position - start += 4; - x = Unpacki(&buffer[start]); - - start += 4; - y = Unpacki(&buffer[start]); - - start += 4; - z = Unpacki(&buffer[start]); - - header.position[0] = x; - header.position[1] = y; - header.position[2] = z; - - //Rotation - start += 4; - x = Unpacki(&buffer[start]); - - start += 4; - y = Unpacki(&buffer[start]); - - start += 4; - z = Unpacki(&buffer[start]); - - header.rotation[0] = x; - header.rotation[1] = y; - header.rotation[2] = z; - - //Scale - start += 4; - x = Unpacki(&buffer[start]); - - start += 4; - y = Unpacki(&buffer[start]); - - start += 4; - z = Unpacki(&buffer[start]); - - header.scale[0] = x; - header.scale[1] = y; - header.scale[2] = z; - - - return header; + struct ObjectHeader header; + memcpy(&header, buffer, sizeof(ObjectHeader)); + + return header; } } } \ No newline at end of file diff --git a/Code/Game/GameLogic/ParseFunctions.h b/Code/Game/GameLogic/ParseFunctions.h index 180f82c3..5638f419 100644 --- a/Code/Game/GameLogic/ParseFunctions.h +++ b/Code/Game/GameLogic/ParseFunctions.h @@ -10,8 +10,8 @@ namespace GameLogic { namespace LevelFileLoader { - ObjectTypeHeader parseObjectTypeHeader(unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. - ObjectHeader parseObjectHeader (unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. + ObjectTypeHeader ParseObjectTypeHeader(unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. + ObjectHeader ParseObjectHeader (unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. } } From 87793b3926a2877bec3fa28bcd787e04f1e147de Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Mon, 27 Jan 2014 15:04:48 +0100 Subject: [PATCH 40/76] Added documentation Timestep Impulse fix treatise / proposition --- .../Other/Timestep_Impulse_Fix.odt | Bin 0 -> 69508 bytes .../Other/Timestep_Impulse_Fix.pdf | Bin 0 -> 123976 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Code/Dokumentation/Other/Timestep_Impulse_Fix.odt create mode 100644 Code/Dokumentation/Other/Timestep_Impulse_Fix.pdf diff --git a/Code/Dokumentation/Other/Timestep_Impulse_Fix.odt b/Code/Dokumentation/Other/Timestep_Impulse_Fix.odt new file mode 100644 index 0000000000000000000000000000000000000000..25ac526e38e7cf2ec810df86c0d02cc34b0075c2 GIT binary patch literal 69508 zcmb@u19)WHwl*9m9oy%< zs;V`{9BU5E@s3$bRst9V82|tR007E@Lr`mw9-0&Y0O0rY^D6*LQ%ge!R~tiJ8ygE# zeO(7rYbzQjD+6k4U3*h|YHJ%qD+6nNM@vI12WkgH7YEt@!t9g&KOcCXM*`MXMyAG& zc7L$3r>8Ns)HOD=r{Onsu++7&r}-N(1O&t%lKdy-=fQ7j{yVX*zP_P_;U_O^I~sjQ zJGtAvC*Cz9qV-;6oFIQgjE2^iDzKI+iW01G8rN{z(^#_s0Vd{omwooSzooF!SH zkdMR@pCXbnq|{@Y2oaPG>nU9;#^k5_r>LXZ?Hq41EvD9= z{zYP2x5XQ}N*m0bCG%4)G5z?LRnwePA!=QM^ucY*q`8D|M=bSnZM4ES&UHA?GpdCU z;X6O4U1KZl=V)bA@)fH}&z~AhRtG=JQ)ziX5z(pHjk*F2GDE%B%iY`;Cr*M>I~vB^ zUNA^2TSHrpE-4Q957wKPe+7Huyg^bt@1OK-R4o-+Yl~{gB@FrHKhlCWjN=X2l2gzJ zk4z2}1nM5|sO4@n#9pdKKT8rks*iB7+=)jFrI8c>;>`H+-&;@bkBR*V6^){m{jQkQ zf4pNd=iT<}YDVJ)%UK570jZ2H-pHzcQL^<*?}jqK3Ze9=FrT#w@5;)&?h&*Ilhx_C zj9y{_nEC@8&@|LH$NEX;ZCi-G6+edo)G%Rr_@Q~gNdU?DwC+t$h7!Rdr=fH_R+6A! z)Tq)#Lw?F&dPkxP(zD%gP=aJ)_6g5TAyYu}iCDRFWogLRASC?h>0)cW(riV@eaCyb z5KDS~g>GtFR(0qVXC>ISb79UvuFpyck7WJN1~JiMTGb9ceHYoH{j^NBD(`|kldXD8 zKT+)C9Jf#gLdIw#w%|&2LaV$VIxNr(PF>Qz`O~H&^y~0k69woKW$YZ$sA#{?Gm4Wzkne3a zpVduedx|zbAT!&-lHS$*F_b!PHP*f z>aWcCW0Dxj$T4-p&WV7;%;n_n*unXr)@ke4KVpKY+an1Ymc(eFk3l|S8L1tLX(&HE z3#dH=sm}uB*|*&7tBxYNGG^-{*F-~-y!c3)13r0WxN#pC1OKWRXjgba;cS-l{pi4(Grc^L(TCp54k_42FIv6L# zBf{@(P@0}lB}6k#*<^$J*75F1lpQ8CGV-s`^%eZp5l=sJ$|+rCt7#p2SiK#3us$46 zT&SdX4GUk_Juks?tA$WNo!yd*-U};Za=oh1LV%PM;Nihhh*azVCd|tSlU zXp1dv>+4Xsp?*orLlu@`9O9z2sTZ;eQ#YG23mb&yW12_`J#Y8Ow_!@9U{U&3#onX% z=3Y^;8uZA{8t|&9k^gYyAlW}CXd&63=|rHs8i|2avq_&j?-YI!b)Hv)yQ+Gt&7y!+ zu`s0m8b1AAATgl#x-gcBe`ozli?-{A#eb$GNSr7@l^iT0SFn>4eLM2;ep@x$D^D1k z!3KAM5p=;cA5TMFXvv`x^z$RR5A_>pgBb`Ya2m&v^L z;5beE)hnX`cXD8`a8^NJIrx0^EftdiPjXflqJWEW z`XH<~*_GIy5CXA@LU2K+Zopqe{Py2wr@r$qxB9Y*O?n|mkVWi(s=mNYTs8Vl^a>_NKjTpl$L6Rw@2s3Q73%O zN|4*kODT<6Mji3ZR5s{3qJ~{J_?XPKG?Wo0m`mENNK*AB3;Oj-CyssZx};jGA)A7i z_~Q4U`4!O7WaNhoU9!mOxmE0=a#5gP`{|xCO2)~$YA_LF@go$b;NBD`Q~}VnyhaHM zNbH;=OurNo)rJpt#6?yj=aL^!A9vUL4_)KtG$%Uq8e11zZ4e&2gpr^pTOq4SCrM-8 zz_cllH!$WMRXAtrlD)`qU8b3E?zA2td(bhau?IZ?1=NjpVlNm?w)@eLXg_g&^B|WU zAW~4!gtXX(>#rGC1GTt8^h(GPjmK92zB$uKTB79L4bqK{C&29AID{Xm5qR(MfIYxWNwhrc%Fo1k=*1dNU*j)?fs?I&FO>TWN{Bw1KDi( zqMH7kI7D}yg*gx~e!a5=ineA@`l`eh4HOy^Jh$bR9K6raaMi1kzB4c=LXaMZdW5^}Pi*MAeywzvk(d(b2JR9zJZr=3d$C)m3EtDV zK+81#M8FD0%bjU23d@xqx|bSeR-EHQ$NZjiQJ$_mrY=f5zB;G^GcOTI_op8POr*#Y z(DfLWSNtn9b44(fA}=XvKVM<+RSN2)JHg=c9(0_GU^W6LpclUTAI^Af31D$XJqX~{ z9k}A(YK(ck35%}xe826yLrd&%yN48L38j0#$2xw$*U;MCiX%6>d7<7DYN9$ppSPa>xsEw;`wO+S=7EeIi`}zZmWJy8ea8k67du6SjatO)jCCS4BM<3yq zoXUQv$!b@liEH!e=ed}5@dU&<5T-&}>@fr*;dD4J1JHy%P(;D<;D3H3=T+eNmUEO&~xF z8{XfB>om)@l?F+je8Uh)TdhOQZW&sPnB3bbeY_+czg8H2+>HW=fM6k5QwgytDwJPu zi9bmGnJoT?c=n*FJ54$&vv9Eb2p{CS9XA4fhK^^JEZiRxyJY@dXdmFe@fP)*@KHV( z008okzvC@`2IHXoV+m$&=-^;#Wo-W&v}sUXS6JbN_rBEbG59sjt;1_7LeDQ!uUu5$ z0QBH0mT0rPPi>Ev`{Bv*g%#az5GfP=!pghrt#!R4-NRkfWfwQWNdB(n`T?TduqE1mZ6pb~4cIwfEiNCR;LMiVrKPCEP7Lt3Xn8JP+m?|J(4I^V&6jTNZ1Y5?P45*&29&pibarve!;oZ^QoW_DJ>pOiiE(U`-p8S zKv4lzh%B>LC8L`S2u1h)sr5ntz9zth@Ft+@m0hk8Bd+fC8yQ%xChW-=9IcVw@Q*go zO`*G752bUp+pETNLkR7|j~skGkc0Hb8jTe#zc1^TqiYd&;va&<(o6%dE1}a%?fBMZ zWei&@HPu?j=JY+#`R%)^(pBq7(D?1Ep3W|lwmEx)Ld!T`?wf9KmU7allUp{Z7&wTU z$0_v(a~=~wKPPjxzR2w^_?=BlxHqz{vs1jRECvYk~)GXe$xs73rMM@9NCj_TlQVfcreu4}B@tumu_ZWe7- z&voQI>$BA~#BiKXUYt$NA5XG(ES13u`Vr_wZ~0^!>=QrKUM`Xz_uzqw=Swo#rL(7m zOxy~j-)^S^Z^64huIr0+Fde|?F7!sx5N!4-jnd0kpuSJxy<=#;Y_7lW@6}T`Qq)uC4tDUByUuvU40Oi5;DVSD*qn?|mYGt*{6IO*PAkca$n!u?u-NFp1^` zuT!^Q^MPol+_hT?)6CHFs8ET^Gw;CkW%~x7g>5X}j7O2Qd?(v7uDOmFqG#Y$)yJVz z>V4t0Hh@A?6iIC8E?ba3AGba~Tgk0o&Y(nbWT0|DBF6}IUq{%%M$x$~sB;lqBr>2N zI0tL14C@PKXA6M{s5KUS`AYpIVa<0Ql8Am0KQOTnN=eNy zv&P?)2dgb)zw7mLq*4&rB72&X(JDZ7x$`fGp9mhxVo6A5?~H+D~>% z6>HkF!SPg!$X*f9gtU)|&gllzgQ>$ysK*Fq?Y%fmOxQ%Kn<+cU%-Ml_?fm{k8F}KD z-p=8M977LqO?`QK4|Sj- zMq&$rGvmp(S@?$A5d=${Ll!~Yt-cemcF3Xill}s2@Qp?oSRJV78mVkB84N?(Ccmbw zFWtq#sLx%wsZ8E4y!d$?k1o4!NeRWX{h8qE+^XA<{<~0F5-pj6Qi70ULzuR?w?nWC z(J}zy^ko6La={&_6+ZLjC|?aNiCLHIKB98I=ImvUV*b)ki-mT5j0W47Lh*eg^qN(D z2{c-T(8OJ_c;*@N7-E-2of-}I1A zT%-Q*;Z9MAPNGX-I5g z7wg$TG^Mbvf>47^wFUV=@0_Gqkm1x~R4U4_)J^Zl`QnE&;9+}z2wQy1W+l*x=*$;OepkQXTRE}# zYa(w|f~L>HA-;Es|EQb{l(nOCPk`uP!{Se>Ld^n^Od3;*;mC?Px@z+5(M8_mM zj)<}U(aDrjF-{{}O@KalRo*%>I+ipC<#9E)P%`0OYS873Tu>WK38rxT-XiAt{Ozw4W=v6nnB9Pl^5IAUz(s?_Zt zIo`TA`(bYc?;LY|?lLOV&ecIcAOy>Qifx7}v$SQ7?=Hx%_}hj=m0(ExNh$ zY=t0)z}J001-2Q8bZ*R&^relDLL%Wcy!uO)-~k&{oa4Z;1RB3ma;&4q`8#zVK15RQ zl*K&A<-bGM2epzs#SG4_?nV%2=;nDANPIt#7E(|NIQ%gepTT4r40&v93i<{(m6JRc zxN_V4u4r*|kU1JZQuqN<$OG{lQ3MLLH{~HwLe+KS3{AQc}4vEPohYJM{1 z^!ENZVs-MiyLh_y1?%M9YbleSq?ZrGbEcP;ING=_4oIyPx$B2ap0+)~W7`~>27Qrn z%*GGNjL5yaW6?}coC_!MnF|IbYuFo3DQm0FO19+KJ-7Hb`&%JSJG%>X;9K{V?G~Qn zB+_prpu#DfNbMemu0U@72UHmT3?W7O4(fWU;ad5M9oV98R)Lm0R2UP|K^G~bqbgAA z7TE}bAVjXeT7nbNnZA7>KD)VGagSZQ9qkNcZwx!a7+YD21^I0#3nte+&$EdKn(;O} zuTOD~wi(~R8eysi4!8yYvo7^~ZDf)7XbF1-Od-r+sg*p7xd%dqqvlO49c1Vx=E}94 zCShqE(LLJ*;nht)vyvMt%fm)M!y5{_-fAB^@h337r~|(N#UY`-#s^3u^HjC{+5380 zGTVejiVe~?b4(wO$iyG8=j5D%lpag8Y`n?U_DIsOxIQon{64usPTzVv6CPFwpO-#E zh*$_V+*_L8>&e4NHmDY2Yn^!UXyw=f*b1e6Xl1rFZq+Q{3O8=^)aAAI4*Ppia_3T# zuK3xrWvBo9qU2xn;Mo4DQ}|zdaDP^O{6nMhpUMBp;MS3J0T8C2j6+A z@MCRVHGf(>c7t;{?w(3NyvKj*MRtjqjzg5qHPRkk}b# zVsuUfQS2&z&ZnM|pFKn|3C8xl^m=B7MQ>uCU;yMGA)%t#L}*(3IHFV_&defdM@nrJi^ z-py{eXV^Fya&Gf7OVVi7XX2boxMXh2TNS@Rl2>z1jn5fmNgCCjdh+I{fSDzGPZ>{p zBV4EB_o9WWG~lULxpSKr;NMP(q()bD@>vH20{-18|IDlYp`>S{`NxvJBSp%7g%&>W z@(m^YGUQowW#@|lzNxwq{y9i&dO(K{N$pR<5x`%kl)94g)l#7qndla#S)M9o3y_tS zDqqVw{GyRvhfLEKGcaxNfJYB;4sGq69~N3Jyjpvg!+&jo9u0l9aU|g*=MwyR=Rn=D z=QtDs`WP)r!6nTfoq59*?o%|^{+QPRL*-hl7vJTO9v~lc*gCd<7bJo+KEqdABVhBNr)X9;>m`;M9%!kBb5gs}qnBEifFnZ~O7N*m z^Nmpv<~SmR9V(Dx-{;fIUA?DVXLsF!hCv8PJ@bCXcnP%Fwcb-cPWKK~!0HxKC3_b7 zsv9g@vaIe?eE_g)8?<^XTN$KHx|pDD?Mfta27}761HK!cyM<4-i1l)L?*j!qunukN zE58W#;@Bl%gw8wMI;;weex>Y2tDqsp6e^&_MxZO=TwO<+J10q8sSX+Fc=r-ffj?VS z;jc=V>*!IHg>P_IRrbY1qP7?1;`>te1JG~M4j-wMG6O-uy*v79&IYVLWSRFN=3K-o zblAtHh=Wj)-?Wz(LD*9xJo&}QTD(nsgG_nB!j8>wSsDPiEvm1Cd;J~U45-XM)X@J{ z@Qyj1zM6(2Bx9j}m9D%nqr_C%kO-g+Q*xr7lHjYVk}h+I62h16(!h1FdWt=q0@Vn_ z>yYG2psjr)zJAq;WiM^TN>7a88X5FE^i!yvE}xYuWu+gc_!DMPv`dd2kh=%JWak%| zPDlfdf{9!J5b=%8p}an3i+q4|r9;nvd%3K30h2r7+k2hPtx6Psys%-|xu#~hhg(zP zc(E26PS#qMWN?fF zQfAAU(3vdCCyi^Uipb`X`srTHp=T$v*7)7jHBX!i`Hu7??9p+%pz)pgV z0|zQ%P2;d@I6;}lCaYBeLZ*&GFI-Uzk82zFheosO$C1OwAN!pwVl~0dKFfNf&rLu7 z_ia>I|5?_{8`@at>KlG$0|$GWzi+=ePlvHlP(mG&nbm`8fQ)N_#Zvf=soKE?K!{utY?%I+kuJK_SxCWebDUf3N} zu&*t{BG<$xST^GZkygI6l;Ty;As_F4MbTiI?Xq&~pKH+s0w?GUu-;Ra8xlH6E*=wf z6zE`vIVxRNpHqG!tL#YmxTl56Hrcmo%8)vfs?${neti-nEm&|!XN&UC;iN9-7tiHJ z9ODcC$LB!G>^KXjna6}I^mUA#aqS3CNSf2Rc26nEozn9`&-$L=r9LFHJr&Zrz*F5( zX!+GHtD|~}!PHhws6}O!CXI`D!}YeTJ=3-`t)n1p;d^HL=<>#i;X`{b;(0;%9(ieT zYKKb=L&&#gmG(=k#DP~U)4%ef7z6h zDwRI+iy6g%3#3Pa1m`a8`)f-ouVY+QWA6AV2FH|!0yCa{R7TLj+H-r^s0tRX+;y!` zLnlR)H?Qsdektj)LhEUKaUn`UN4rKC(u{hS3d%~b@L~7;;@&Nr{V*arOls8bW#mrE zL1|67GyyxfG;or(@E#ym53-FdwD6JC9f}LGCcH#F)+=5!MtN`g!WNniw~IX*0hS9k z=S-LhuB00VCzut$;h=7fUzVSL58Jh%Hfb(QC&aH-0)g%iKctp*m==IU{ejKRxMC_m zHIT^*K$)#el6G!?OO{+2Vi&GK4>ZprzCJP=dJ7k5eaU8^K!26To>z2^G2uI)gYoXz zNO1SZf!TmU>9=4f-c#~wq$e*(xi1#a65hEL)ioQ^4v3ALA+MMylw+Pa9=ErQ<4{?S zc%t#J@8FssSC!0MPF{bzc)Y@2%67OH-gR}#8Q~JBA6989%zzJ5HDQv7VlR;sP4s1m3VExv~I8~ zk}JY1@oJ9H;gm~cL*yqgRX}bqZh)VDZQbTQa6OE)qe2dia&ogh0$Z$CQ(G2SEL-PW z6mhRAHOx2ku`*VJXmE6i^U)tp5@Lp#Fy!W3(>Pct| z()J%U&p~pc(n=>}lrdUZCdfvz|E>V!oTDzU zcXcyLgIVNkrruc1@>I{L)j*q=`Qw#Ewl*b%tV*99sx4a(&Y#%YIl;-XJ@Kmn91&+_^xYP$8U|R7DbuMedOGjm=LHjsVKNeiFguciDElK`KYIv z6VrHwaD~%v`9DupeyH~_YkvJQ#2E@THBPjx(hTqhLbtY{vQjYOb@Db*suRfcBhL>5 znrR9Bu|4dXRD^JXJgyh1GRY*0ev6CzNbkXxQZ{B=Q;O@MF zQ(XqtQAW1bphUjS^w!G)Nz3LMJQ7JWot~eGq2HGOijQmr9eMVoQGi|ToP`cQpUNXo z3Ocx9E~-k?5;Sk0=s=NRcd)KW4UNHKuqk~nmb_0U#of~p^0|XT$YN^@xyY?pvpQYY za;`}$9}3m}WOIu#x7Em?-hg}|+PxS&Po)Y6;kx%iIsZn<7JL~~W*Ud#g z;>|lwKsW44<~y9K;smj|q24;P-?#PG82SVMSSF{(kcUY26%8O){a2J(?(y#`#Tw)9bGM2E7xNJWX zaMDaUqq9@XbjYeQtEwqvmfG=bwA_uvI@79c@m0fnz}@-c`8N5Ic7u20Juz_5!X{~O z^dMg`O@rbBL3PXYq;)HNdb;x#Npx*UCU{;COONzcl6qU1)B{|j*oQ4Mn^&ULpoUr8 zb<6y`#ST?ZJ^X0PEE)Ljb(1v49SCP>_F#|K@AJHTJAi`>oD=TC-w4&d(z$J zS?umJ2M(@vroF!gtRwUZ#;ZqG4woGHx?%0D}`~B0H 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 literal 0 HcmV?d00001 diff --git a/Code/Dokumentation/Other/Timestep_Impulse_Fix.pdf b/Code/Dokumentation/Other/Timestep_Impulse_Fix.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8ee3faeab7ba99ea6cca47a7bdd96d5413671a31 GIT binary patch literal 123976 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�RjorcdT6;QM@D`PB*JvznM~b+`_hdBlgdk#`ua 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 literal 0 HcmV?d00001 From cbdce81b201570d2a4b4cdbad0a1f0520137bbdd Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Tue, 28 Jan 2014 08:54:20 +0100 Subject: [PATCH 41/76] Inactivated code Matrix to Angular Axis --- Code/OysterMath/LinearMath.h | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Code/OysterMath/LinearMath.h b/Code/OysterMath/LinearMath.h index 4d8b4efd..947f1d0f 100644 --- a/Code/OysterMath/LinearMath.h +++ b/Code/OysterMath/LinearMath.h @@ -325,23 +325,24 @@ namespace LinearAlgebra2D namespace LinearAlgebra3D { - template - inline ::LinearAlgebra::Vector4 AngularAxis( const ::LinearAlgebra::Matrix3x3 &rotationMatrix ) - { - return ::std::asin( ::LinearAlgebra::Vector4(rotationMatrix.v[1].z, rotationMatrix.v[2].x, rotationMatrix.v[0].y, 1) ); - } + // All Matrix to AngularAxis conversions here is incorrect + //template + //inline ::LinearAlgebra::Vector4 AngularAxis( const ::LinearAlgebra::Matrix3x3 &rotationMatrix ) + //{ + // return ::std::asin( ::LinearAlgebra::Vector4(rotationMatrix.v[1].z, rotationMatrix.v[2].x, rotationMatrix.v[0].y, 1) ); + //} - template - inline ::LinearAlgebra::Vector4 AngularAxis( const ::LinearAlgebra::Matrix4x4 &rotationMatrix ) - { - return ::std::asin( ::LinearAlgebra::Vector4(rotationMatrix.v[1].z, rotationMatrix.v[2].x, rotationMatrix.v[0].y, 1) ); - } + //template + //inline ::LinearAlgebra::Vector4 AngularAxis( const ::LinearAlgebra::Matrix4x4 &rotationMatrix ) + //{ + // return ::std::asin( ::LinearAlgebra::Vector4(rotationMatrix.v[1].z, rotationMatrix.v[2].x, rotationMatrix.v[0].y, 1) ); + //} - template - inline ::LinearAlgebra::Vector4 ExtractAngularAxis( const ::LinearAlgebra::Matrix4x4 &orientationMatrix ) - { - return ::std::asin( ::LinearAlgebra::Vector4(orientationMatrix.v[1].z, orientationMatrix.v[2].x, orientationMatrix.v[0].y, 0) ); - } + //template + //inline ::LinearAlgebra::Vector4 ExtractAngularAxis( const ::LinearAlgebra::Matrix4x4 &orientationMatrix ) + //{ + // return ::std::asin( ::LinearAlgebra::Vector4(orientationMatrix.v[1].z, orientationMatrix.v[2].x, orientationMatrix.v[0].y, 0) ); + //} template inline ::LinearAlgebra::Matrix4x4 & TranslationMatrix( const ::LinearAlgebra::Vector3 &position, ::LinearAlgebra::Matrix4x4 &targetMem = ::LinearAlgebra::Matrix4x4() ) From 4e4aaf881a902a1fc91e63eca5ba485b34dc062b Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Tue, 28 Jan 2014 08:56:29 +0100 Subject: [PATCH 42/76] Inactivated code part 2 Matrix to angularAxis conversions in OysterMath --- Code/OysterMath/OysterMath.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Code/OysterMath/OysterMath.cpp b/Code/OysterMath/OysterMath.cpp index f44de9fd..bbaccf11 100644 --- a/Code/OysterMath/OysterMath.cpp +++ b/Code/OysterMath/OysterMath.cpp @@ -81,20 +81,20 @@ namespace Oyster { namespace Math2D namespace Oyster { namespace Math3D { - Float4 AngularAxis( const Float3x3 &rotationMatrix ) - { - return ::LinearAlgebra3D::AngularAxis( rotationMatrix ); - } + //Float4 AngularAxis( const Float3x3 &rotationMatrix ) + //{ + // return ::LinearAlgebra3D::AngularAxis( rotationMatrix ); + //} - Float4 AngularAxis( const Float4x4 &rotationMatrix ) - { - return ::LinearAlgebra3D::AngularAxis( rotationMatrix ); - } + //Float4 AngularAxis( const Float4x4 &rotationMatrix ) + //{ + // return ::LinearAlgebra3D::AngularAxis( rotationMatrix ); + //} - Float4 ExtractAngularAxis( const Float4x4 &orientationMatrix ) - { - return ::LinearAlgebra3D::ExtractAngularAxis( orientationMatrix ); - } + //Float4 ExtractAngularAxis( const Float4x4 &orientationMatrix ) + //{ + // return ::LinearAlgebra3D::ExtractAngularAxis( orientationMatrix ); + //} Float4x4 & TranslationMatrix( const Float3 &position, Float4x4 &targetMem ) { From 4d6cfaf0462fddb45d393ca016d31c1af92c9cca Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Tue, 28 Jan 2014 08:57:14 +0100 Subject: [PATCH 43/76] ... part 3 -_- --- Code/OysterMath/OysterMath.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Code/OysterMath/OysterMath.h b/Code/OysterMath/OysterMath.h index 559ba0d3..62438e47 100644 --- a/Code/OysterMath/OysterMath.h +++ b/Code/OysterMath/OysterMath.h @@ -167,13 +167,13 @@ namespace Oyster { namespace Math3D //! Oyster's native math library specialized using namespace ::Oyster::Math; // deliberate inheritance from ::Oyster::Math namespace //! Extracts the angularAxis from rotationMatrix - Float4 AngularAxis( const Float3x3 &rotationMatrix ); + //Float4 AngularAxis( const Float3x3 &rotationMatrix ); - //! Extracts the angularAxis from rotationMatrix - Float4 AngularAxis( const Float4x4 &rotationMatrix ); + ////! Extracts the angularAxis from rotationMatrix + //Float4 AngularAxis( const Float4x4 &rotationMatrix ); - //! Extracts the angularAxis from orientationMatrix - Float4 ExtractAngularAxis( const Float4x4 &orientationMatrix ); + ////! Extracts the angularAxis from orientationMatrix + //Float4 ExtractAngularAxis( const Float4x4 &orientationMatrix ); //! Sets and returns targetMem to a translationMatrix with position as translation. Float4x4 & TranslationMatrix( const Float3 &position, Float4x4 &targetMem = Float4x4() ); From 91e825dbfba5e627aedc36dafa96de17d36a0d14 Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Tue, 28 Jan 2014 09:00:02 +0100 Subject: [PATCH 44/76] Broekn stuff to tobias --- Code/DanBias.sln | 45 ++- Code/Dokumentation/GameServer.uxf | 181 +++++------ .../DanBiasGame/GameClientState/GameState.cpp | 2 +- .../AdminInterface/AdminInterface.cpp | 68 ---- .../AdminInterface/AdminInterface.h | 20 -- Code/Game/DanBiasServer/DanBiasServerAPI.cpp | 28 -- Code/Game/DanBiasServer/GameServer.cpp | 161 ---------- Code/Game/DanBiasServer/GameServer.h | 50 --- .../DanBiasServer/GameSession/GameClient.cpp | 67 ---- .../DanBiasServer/GameSession/GameClient.h | 43 --- Code/Game/DanBiasServer/Helpers/MapManager.h | 30 -- .../DanBiasServer/Helpers/ServerDataReader.h | 44 --- .../DanBiasServer/LobbySessions/GameLobby.h | 24 -- .../LobbySessions/INetworkSession.cpp | 18 -- .../LobbySessions/INetworkSession.h | 22 -- .../LobbySessions/LobbyClient.cpp | 51 --- .../DanBiasServer/LobbySessions/LobbyClient.h | 38 --- .../LobbyGeneralProtocolParser.cpp | 64 ---- .../LobbySessions/LobbyProtocolParser.cpp | 37 --- .../DanBiasServer/LobbySessions/MainLobby.cpp | 62 ---- .../DanBiasServer/LobbySessions/MainLobby.h | 49 --- .../LobbySessions/NetworkSession.cpp | 219 ------------- .../LobbySessions/NetworkSession.h | 91 ------ .../DanBiasServerLauncher.vcxproj | 8 +- .../DanBiasServerLauncher/ServerLauncher.cpp | 12 +- Code/Game/GameProtocols/LobbyProtocols.h | 16 +- Code/Game/GameServer/GameClient.h | 34 ++ Code/Game/GameServer/GameLobby.h | 48 +++ Code/Game/GameServer/GameServer.h | 33 ++ .../GameServer.vcxproj} | 77 +++-- .../GameServer.vcxproj.user} | 0 .../GameServerAPI.h} | 14 +- .../GameSession => GameServer}/GameSession.h | 16 +- .../GameSessionManager.h | 12 +- .../Implementation}/DLLMain.cpp | 0 .../GameServer/Implementation/GameClient.cpp | 50 +++ .../GameServer/Implementation/GameLobby.cpp | 51 +++ .../GameServer/Implementation/GameServer.cpp | 139 +++++++++ .../Implementation}/GameSessionManager.cpp | 10 +- .../Implementation}/GameSession_Events.cpp | 5 +- .../Implementation}/GameSession_General.cpp | 6 +- .../Implementation}/GameSession_Logic.cpp | 4 +- .../Implementation}/GameSession_Network.cpp | 4 +- .../LobbyGeneralProtocolParser.cpp | 52 ++++ .../Implementation/LobbyProtocolParser.cpp | 58 ++++ Code/Game/aDanBiasGameLauncher/Launcher.cpp | 1 + .../aDanBiasGameLauncher.vcxproj | 3 - Code/Misc/DynamicArray.h | 2 + Code/Misc/Thread/OysterThread.h | 3 +- Code/Misc/Thread/OysterThread_Impl.cpp | 7 +- Code/Misc/ThreadSafeQueue.h | 8 + Code/Network/NetworkAPI/CustomNetProtocol.h | 9 +- Code/Network/NetworkAPI/NetworkAPI.vcxproj | 13 +- .../NetworkAPI/NetworkAPI_Preprocessor.h | 10 + .../NetworkAPI/NetworkCallbackHelper.h | 80 ----- Code/Network/NetworkAPI/NetworkClient.cpp | 294 ++++++++---------- Code/Network/NetworkAPI/NetworkClient.h | 102 ++++-- Code/Network/NetworkAPI/NetworkServer.cpp | 276 ++++++++-------- Code/Network/NetworkAPI/NetworkServer.h | 72 ++++- .../NetworkAPI/NetworkServerEventStruct.h | 17 + Code/Network/NetworkAPI/NetworkSession.cpp | 184 +++++++++++ Code/Network/NetworkAPI/NetworkSession.h | 83 +++++ Code/Network/NetworkAPI/Translator.cpp | 1 + Code/Network/NetworkAPI/Translator.h | 11 +- .../NetworkDependencies/Connection.cpp | 15 +- Code/Network/NetworkDependencies/Connection.h | 3 +- Code/Network/NetworkDependencies/Listener.cpp | 2 +- 67 files changed, 1412 insertions(+), 1847 deletions(-) delete mode 100644 Code/Game/DanBiasServer/AdminInterface/AdminInterface.cpp delete mode 100644 Code/Game/DanBiasServer/AdminInterface/AdminInterface.h delete mode 100644 Code/Game/DanBiasServer/DanBiasServerAPI.cpp delete mode 100644 Code/Game/DanBiasServer/GameServer.cpp delete mode 100644 Code/Game/DanBiasServer/GameServer.h delete mode 100644 Code/Game/DanBiasServer/GameSession/GameClient.cpp delete mode 100644 Code/Game/DanBiasServer/GameSession/GameClient.h delete mode 100644 Code/Game/DanBiasServer/Helpers/MapManager.h delete mode 100644 Code/Game/DanBiasServer/Helpers/ServerDataReader.h delete mode 100644 Code/Game/DanBiasServer/LobbySessions/GameLobby.h delete mode 100644 Code/Game/DanBiasServer/LobbySessions/INetworkSession.cpp delete mode 100644 Code/Game/DanBiasServer/LobbySessions/INetworkSession.h delete mode 100644 Code/Game/DanBiasServer/LobbySessions/LobbyClient.cpp delete mode 100644 Code/Game/DanBiasServer/LobbySessions/LobbyClient.h delete mode 100644 Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp delete mode 100644 Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp delete mode 100644 Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp delete mode 100644 Code/Game/DanBiasServer/LobbySessions/MainLobby.h delete mode 100644 Code/Game/DanBiasServer/LobbySessions/NetworkSession.cpp delete mode 100644 Code/Game/DanBiasServer/LobbySessions/NetworkSession.h create mode 100644 Code/Game/GameServer/GameClient.h create mode 100644 Code/Game/GameServer/GameLobby.h create mode 100644 Code/Game/GameServer/GameServer.h rename Code/Game/{DanBiasServer/DanBiasServer.vcxproj => GameServer/GameServer.vcxproj} (78%) rename Code/Game/{DanBiasServer/DanBiasServer.vcxproj.user => GameServer/GameServer.vcxproj.user} (100%) rename Code/Game/{DanBiasServer/DanBiasServerAPI.h => GameServer/GameServerAPI.h} (73%) rename Code/Game/{DanBiasServer/GameSession => GameServer}/GameSession.h (85%) rename Code/Game/{DanBiasServer/GameSession => GameServer}/GameSessionManager.h (90%) rename Code/Game/{DanBiasServer => GameServer/Implementation}/DLLMain.cpp (100%) create mode 100644 Code/Game/GameServer/Implementation/GameClient.cpp create mode 100644 Code/Game/GameServer/Implementation/GameLobby.cpp create mode 100644 Code/Game/GameServer/Implementation/GameServer.cpp rename Code/Game/{DanBiasServer/GameSession => GameServer/Implementation}/GameSessionManager.cpp (93%) rename Code/Game/{DanBiasServer/GameSession => GameServer/Implementation}/GameSession_Events.cpp (96%) rename Code/Game/{DanBiasServer/GameSession => GameServer/Implementation}/GameSession_General.cpp (96%) rename Code/Game/{DanBiasServer/GameSession => GameServer/Implementation}/GameSession_Logic.cpp (96%) rename Code/Game/{DanBiasServer/GameSession => GameServer/Implementation}/GameSession_Network.cpp (92%) create mode 100644 Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp create mode 100644 Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp create mode 100644 Code/Network/NetworkAPI/NetworkAPI_Preprocessor.h delete mode 100644 Code/Network/NetworkAPI/NetworkCallbackHelper.h create mode 100644 Code/Network/NetworkAPI/NetworkServerEventStruct.h create mode 100644 Code/Network/NetworkAPI/NetworkSession.cpp create mode 100644 Code/Network/NetworkAPI/NetworkSession.h diff --git a/Code/DanBias.sln b/Code/DanBias.sln index 68c4cdfa..540348f9 100644 --- a/Code/DanBias.sln +++ b/Code/DanBias.sln @@ -31,8 +31,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Game", "Game", "{20720CA7-7 EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameLogic", "Game\GameLogic\GameLogic.vcxproj", "{B1195BB9-B3A5-47F0-906C-8DEA384D1520}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasServer", "Game\DanBiasServer\DanBiasServer.vcxproj", "{52380DAA-0F4A-4D97-8E57-98DF39319CAF}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasLauncher", "Game\DanBiasLauncher\DanBiasLauncher.vcxproj", "{8690FDDF-C5B7-4C42-A337-BD5243F29B85}" ProjectSection(ProjectDependencies) = postProject {52380DAA-0F4A-4D97-8E57-98DF39319CAF} = {52380DAA-0F4A-4D97-8E57-98DF39319CAF} @@ -46,6 +44,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasServerLauncher", "Ga EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aDanBiasGameLauncher", "Game\aDanBiasGameLauncher\aDanBiasGameLauncher.vcxproj", "{666FEA52-975F-41CD-B224-B19AF3C0ABBA}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerDependencies", "Game\ServerDependencies\ServerDependencies.vcxproj", "{52380DAA-0F4A-4D97-8E57-98DF39319CAF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameServer", "Game\GameServer\GameServer.vcxproj", "{143BD516-20A1-4890-A3E4-F8BFD02220E7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Mixed Platforms = Debug|Mixed Platforms @@ -212,18 +214,6 @@ Global {B1195BB9-B3A5-47F0-906C-8DEA384D1520}.Release|Win32.Build.0 = Release|Win32 {B1195BB9-B3A5-47F0-906C-8DEA384D1520}.Release|x64.ActiveCfg = Release|x64 {B1195BB9-B3A5-47F0-906C-8DEA384D1520}.Release|x64.Build.0 = Release|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Mixed Platforms.Build.0 = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Win32.ActiveCfg = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Win32.Build.0 = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|x64.ActiveCfg = Debug|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|x64.Build.0 = Debug|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Mixed Platforms.ActiveCfg = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Mixed Platforms.Build.0 = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Win32.ActiveCfg = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Win32.Build.0 = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|x64.ActiveCfg = Release|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|x64.Build.0 = Release|x64 {8690FDDF-C5B7-4C42-A337-BD5243F29B85}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {8690FDDF-C5B7-4C42-A337-BD5243F29B85}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {8690FDDF-C5B7-4C42-A337-BD5243F29B85}.Debug|Win32.ActiveCfg = Debug|Win32 @@ -284,6 +274,30 @@ Global {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.Build.0 = Release|Win32 {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.ActiveCfg = Release|x64 {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.Build.0 = Release|x64 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Win32.ActiveCfg = Debug|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Win32.Build.0 = Debug|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|x64.ActiveCfg = Debug|x64 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|x64.Build.0 = Debug|x64 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Mixed Platforms.Build.0 = Release|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Win32.ActiveCfg = Release|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Win32.Build.0 = Release|Win32 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|x64.ActiveCfg = Release|x64 + {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|x64.Build.0 = Release|x64 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|Win32.ActiveCfg = Debug|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|Win32.Build.0 = Debug|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|x64.ActiveCfg = Debug|x64 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|x64.Build.0 = Debug|x64 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Release|Mixed Platforms.Build.0 = Release|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Release|Win32.ActiveCfg = Release|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Release|Win32.Build.0 = Release|Win32 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Release|x64.ActiveCfg = Release|x64 + {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -295,10 +309,11 @@ Global {460D625F-2AC9-4559-B809-0BA89CEAEDF4} = {C27B926E-B3EF-4990-8822-47580E43A0BE} {2A1BC987-AF42-4500-802D-89CD32FC1309} = {20720CA7-795C-45AD-A302-9383A6DD503A} {B1195BB9-B3A5-47F0-906C-8DEA384D1520} = {20720CA7-795C-45AD-A302-9383A6DD503A} - {52380DAA-0F4A-4D97-8E57-98DF39319CAF} = {20720CA7-795C-45AD-A302-9383A6DD503A} {8690FDDF-C5B7-4C42-A337-BD5243F29B85} = {20720CA7-795C-45AD-A302-9383A6DD503A} {DA2AA800-ED64-4649-8B3B-E7F1E3968B78} = {20720CA7-795C-45AD-A302-9383A6DD503A} {060B1890-CBF3-4808-BA99-A4776222093B} = {20720CA7-795C-45AD-A302-9383A6DD503A} {666FEA52-975F-41CD-B224-B19AF3C0ABBA} = {20720CA7-795C-45AD-A302-9383A6DD503A} + {52380DAA-0F4A-4D97-8E57-98DF39319CAF} = {20720CA7-795C-45AD-A302-9383A6DD503A} + {143BD516-20A1-4890-A3E4-F8BFD02220E7} = {20720CA7-795C-45AD-A302-9383A6DD503A} EndGlobalSection EndGlobal diff --git a/Code/Dokumentation/GameServer.uxf b/Code/Dokumentation/GameServer.uxf index 9b416e02..039b4dda 100644 --- a/Code/Dokumentation/GameServer.uxf +++ b/Code/Dokumentation/GameServer.uxf @@ -4,8 +4,8 @@ UMLClass - 530 - 320 + 380 + 360 100 30 @@ -16,20 +16,8 @@ UMLClass - 530 - 250 - 100 - 30 - - MainLobby - - - - - UMLClass - - 510 - 500 + 360 + 540 160 80 @@ -43,8 +31,8 @@ UMLClass - 710 - 320 + 560 + 360 100 30 @@ -54,19 +42,8 @@ com.umlet.element.Relation - 550 - 250 - 50 - 90 - - lt=->>>> - 30;70;30;30 - - - com.umlet.element.Relation - - 550 - 420 + 400 + 460 50 100 @@ -76,8 +53,8 @@ com.umlet.element.Relation - 600 - 300 + 450 + 340 130 50 @@ -87,19 +64,19 @@ com.umlet.element.Relation - 600 - 230 - 180 + 460 + 270 + 170 110 lt=>>>- - 160;90;160;30;30;30 + 150;90;150;30;30;30 com.umlet.element.Relation - 640 - 410 + 490 + 450 140 140 @@ -109,8 +86,8 @@ com.umlet.element.Relation - 610 - 140 + 460 + 180 400 150 @@ -120,10 +97,10 @@ UMLClass - 520 - 160 + 370 + 200 120 - 30 + 120 GameServer @@ -131,19 +108,19 @@ com.umlet.element.Relation - 550 - 160 + 400 + 290 50 - 110 + 90 lt=>>>>- - 30;90;30;30 + 30;70;30;30 com.umlet.element.Relation - 750 - 260 + 600 + 300 260 80 @@ -153,8 +130,8 @@ com.umlet.element.Package - 990 - 250 + 840 + 290 120 50 @@ -165,8 +142,8 @@ bg=#a21aff UMLClass - 510 - 20 + 360 + 120 130 40 @@ -177,20 +154,20 @@ DanBiasServerAPI com.umlet.element.Relation - 550 - 30 + 400 + 130 50 - 150 + 90 lt=>>>>- - 30;130;30;30 + 30;70;30;30 UMLClass - 300 - 320 + 150 + 360 120 30 @@ -200,19 +177,19 @@ DanBiasServerAPI com.umlet.element.Relation - 330 - 230 - 220 + 180 + 270 + 210 110 lt=->>>>> - 200;30;30;30;30;90 + 190;30;30;30;30;90 com.umlet.element.Relation - 390 - 300 + 240 + 340 160 50 @@ -222,8 +199,8 @@ DanBiasServerAPI com.umlet.element.Relation - 330 - 320 + 180 + 360 200 210 @@ -233,8 +210,8 @@ DanBiasServerAPI com.umlet.element.Package - 990 - 370 + 840 + 410 120 50 @@ -245,8 +222,8 @@ bg=blue com.umlet.element.Relation - 750 - 320 + 600 + 360 260 90 @@ -256,8 +233,8 @@ bg=blue com.umlet.element.Package - 990 - 310 + 840 + 350 120 40 @@ -270,8 +247,8 @@ bg=blue com.umlet.element.Relation - 780 - 300 + 630 + 340 230 50 @@ -281,8 +258,8 @@ bg=blue com.umlet.element.Relation - 640 - 380 + 490 + 420 370 190 @@ -292,8 +269,8 @@ bg=blue UMLClass - 320 - 120 + 170 + 160 130 90 @@ -308,8 +285,8 @@ elementstyle=wordwrap com.umlet.element.Relation - 420 - 140 + 270 + 180 120 50 @@ -320,8 +297,8 @@ elementstyle=wordwrap UMLClass - 510 - 400 + 360 + 440 160 50 @@ -333,8 +310,8 @@ elementstyle=wordwrap com.umlet.element.Relation - 550 - 320 + 400 + 360 50 100 @@ -344,8 +321,8 @@ elementstyle=wordwrap com.umlet.element.Package - 990 - 430 + 840 + 470 120 40 @@ -356,8 +333,8 @@ bg=#aaaaa com.umlet.element.Relation - 310 - 320 + 160 + 360 770 300 @@ -367,8 +344,8 @@ bg=#aaaaa com.umlet.element.Relation - 1080 - 260 + 930 + 300 70 210 @@ -378,8 +355,8 @@ bg=#aaaaa UMLClass - 160 - 310 + 10 + 350 120 40 @@ -390,8 +367,8 @@ INetworkSession com.umlet.element.Relation - 250 - 300 + 100 + 340 70 50 @@ -401,8 +378,8 @@ INetworkSession UMLClass - 710 - 410 + 560 + 450 100 30 @@ -412,8 +389,8 @@ INetworkSession com.umlet.element.Relation - 730 - 320 + 580 + 360 50 110 @@ -423,8 +400,8 @@ INetworkSession UMLClass - 150 - 420 + 0 + 460 160 50 diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index 9093654c..70385e07 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -290,7 +290,7 @@ void GameState::Protocol( ObjPos* pos ) for (unsigned int i = 0; i < privData->object.size(); i++) { - if(privData->object[i]->GetId() == pos->object_ID) + if(privData->object[i] && privData->object[i]->GetId() == pos->object_ID) { privData->object[i]->setPos(world); diff --git a/Code/Game/DanBiasServer/AdminInterface/AdminInterface.cpp b/Code/Game/DanBiasServer/AdminInterface/AdminInterface.cpp deleted file mode 100644 index 80e2f9fe..00000000 --- a/Code/Game/DanBiasServer/AdminInterface/AdminInterface.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#define NOMINMAX -#include "AdminInterface.h" -#include -#include -#include "..\GameServer.h" -#include "..\GameSession\GameSessionManager.h" -#include "..\GameSession\GameSession.h" -#include "..\Helpers\ServerDataReader.h" -#include "..\LobbySessions\LobbyClient.h" -#include "..\LobbySessions\NetworkSession.h" -#include "..\LobbySessions\GameLobby.h" -#include "..\LobbySessions\MainLobby.h" - -using namespace Oyster::Network; -using namespace Oyster::Thread; -using namespace DanBias; - -struct AdminInstanceData :public IThreadObject -{ - bool isCreated; - Oyster::Network::NetworkServer adminServer; - NetworkClient* admin; - NetworkSession* parentInstance; - OysterThread worker; - - bool DoWork() override - { - //((GameServer*)parentInstance)->serve - return true; - } - -}adminInstanceData; - -void AdminArrived(NetworkClient* adm) -{ - if(adminInstanceData.admin) - { - delete adm; - return; - } - - adminInstanceData.admin = adm; - adminInstanceData.worker.Create(&adminInstanceData, true, true); -} - -void AdminInterface::Toggle(bool toggle, NetworkSession* parent) -{ - if(toggle) - { - if(!parent) return; - - if(adminInstanceData.isCreated) return; - - NetworkServer::INIT_DESC desc; - desc.port = 15152; - desc.callbackType = NetworkClientCallbackType_Function; - desc.recvObj.clientConnectFnc = AdminArrived; - - if(!adminInstanceData.adminServer.Init(desc)) return; - adminInstanceData.parentInstance = parent; - - adminInstanceData.adminServer.Start(); - } - else - { - adminInstanceData.adminServer.Shutdown(); - } -} \ No newline at end of file diff --git a/Code/Game/DanBiasServer/AdminInterface/AdminInterface.h b/Code/Game/DanBiasServer/AdminInterface/AdminInterface.h deleted file mode 100644 index 4acc20a2..00000000 --- a/Code/Game/DanBiasServer/AdminInterface/AdminInterface.h +++ /dev/null @@ -1,20 +0,0 @@ -///////////////////////////////////////// -// Created by [Dennis Andersen] [2013] // -///////////////////////////////////////// -#ifndef DANBIASSERVER_ADMIN_INTERFACE_H -#define DANBIASSERVER_ADMIN_INTERFACE_H - -#include - -namespace DanBias -{ - class NetworkSession; - //Global admin - class AdminInterface - { - public: - static void Toggle(bool toggle, NetworkSession* parent); - - }; -} -#endif // !DANBIASSERVER_ADMIN_INTERFACE_H diff --git a/Code/Game/DanBiasServer/DanBiasServerAPI.cpp b/Code/Game/DanBiasServer/DanBiasServerAPI.cpp deleted file mode 100644 index dfaf0b75..00000000 --- a/Code/Game/DanBiasServer/DanBiasServerAPI.cpp +++ /dev/null @@ -1,28 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#include "DanBiasServerAPI.h" -#include "GameServer.h" - -namespace DanBias -{ - -#pragma region Server Data - static GameServer server; -#pragma endregion - - - DanBiasServerReturn DanBiasServerAPI::Initiate() - { - return server.Create(); - } - DanBiasServerReturn DanBiasServerAPI::Run() - { - return server.Run(); - } - DanBiasServerReturn DanBiasServerAPI::Release() - { - return server.Release(); - } - -} //End namspace DanBias \ No newline at end of file diff --git a/Code/Game/DanBiasServer/GameServer.cpp b/Code/Game/DanBiasServer/GameServer.cpp deleted file mode 100644 index 1da2b5ba..00000000 --- a/Code/Game/DanBiasServer/GameServer.cpp +++ /dev/null @@ -1,161 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#define NOMINMAX -#include -#include -#include - -#include "GameServer.h" -#include "Helpers\ServerDataReader.h" -#include "GameSession\GameSessionManager.h" -#include "LobbySessions\LobbyClient.h" -#include "GameSession\GameSession.h" -#include "AdminInterface\AdminInterface.h" - -#include -#include - -#include - -namespace DanBias -{ - using namespace Oyster::Network; - - GameServer* GameServer::instance = 0; - - void GameServer::NetworkCallback(NetworkClient* client) - { - static bool myTest = false; - static int sessionId = -1; - printf("Client with ID [%i] connected.\n", client->GetID()); - - if(!myTest) - { - Utility::DynamicMemory::SmartPointer c = new LobbyClient(client); - - GameSessionDescription desc; - desc.mapName = L"test"; - desc.clients.Push(c); - desc.exitDestionation = this->mainLobby; - if((sessionId = GameSessionManager::AddSession(desc, true)) == 0) - printf("Failed to create a game session\n"); - myTest = true; - //myTest = new GameSession(); - // - //DanBias::GameSession::GameSessionDescription desc; - //desc.owner = 0; - //desc.clients.Push(c); - // - //if(!myTest->Create(desc)) return; - //myTest->Run(); - } - else - { - Utility::DynamicMemory::SmartPointer c = new LobbyClient(client); - GameSessionManager::JoinSession(sessionId, c); - } - - - //Utility::DynamicMemory::SmartPointer c = new LobbyClient(client); - //this->mainLobby->Attach(c, this->mainLobby->GetPostbox()); - } - - - GameServer::GameServer() - : initiated(0) - , running(0) - , released(0) - , maxClients(0) - , mainLobby(0) - , server(0) - { this->instance = this; } - GameServer::~GameServer() - { - - } - DanBiasServerReturn GameServer::Create() - { - this->server = new NetworkServer(); - this->mainLobby = new MainLobby(); - - InitData data; - if(!LoadIniFile(data)) return DanBiasServerReturn_Error; - - NetworkServer::INIT_DESC serverDesc; - this->maxClients = data.clients; - serverDesc.port = data.port; - serverDesc.recvObj = this; - serverDesc.callbackType = Oyster::Network::NetworkClientCallbackType_Object; - - if(!this->server->Init(serverDesc)) return DanBiasServerReturn_Error; - if(!WindowShell::CreateConsoleWindow()) return DanBiasServerReturn_Error; - - this->initiated = true; - return DanBiasServerReturn_Sucess; - } - DanBiasServerReturn GameServer::Run() - { - if(this->running) return DanBiasServerReturn_Error; - if(this->released) return DanBiasServerReturn_Error; - if(!this->initiated) return DanBiasServerReturn_Error; - - if(!this->server->Start()) return DanBiasServerReturn_Error; - //Oyster::Thread::OysterThread ioThread; - //ioThread.Create(this, true, - - while (true) - { - if(!WindowShell::Frame()) break; - - this->mainLobby->Frame(); - - if(GetAsyncKeyState(0x51)) //Q for exit - break; - } - - return DanBiasServerReturn_Sucess; - } - DanBiasServerReturn GameServer::Release() - { - GameSessionManager::CloseSession(); - this->mainLobby->Release(); - delete this->mainLobby; - this->server->Shutdown(); - delete this->server; - this->released = true; - return DanBiasServerReturn_Sucess; - } - - NetworkSession* GameServer::MainLobbyInstance() - { - return GameServer::instance->mainLobby; - } - - bool GameServer::LoadIniFile(InitData& ini) - { - std::ifstream in; - std::string f = GetInitPath(InitPath_ServerIni); - in.open(f, std::ios::in); - if(!in.is_open()) return false; - - std::string buffer; - while (!in.eof()) - { - in >> buffer; - - if(buffer == "port") - { - in >> ini.port; - } - else if(buffer == "clients") - { - in >> ini.clients; - } - - } - - in.close(); - return true; - } -}//End namespace DanBias diff --git a/Code/Game/DanBiasServer/GameServer.h b/Code/Game/DanBiasServer/GameServer.h deleted file mode 100644 index ccdb6e05..00000000 --- a/Code/Game/DanBiasServer/GameServer.h +++ /dev/null @@ -1,50 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_GAME_SERVER_H -#define DANBIASSERVER_GAME_SERVER_H - -#include "DanBiasServerAPI.h" -#include "LobbySessions\MainLobby.h" -#include -#include - -namespace DanBias -{ - class GameServer :public Oyster::Network::ClientConnectedObject - { - public: - GameServer(); - virtual~GameServer(); - - DanBiasServerReturn Create(); - DanBiasServerReturn Run(); - DanBiasServerReturn Release(); - - static NetworkSession* MainLobbyInstance(); - - private: - //static void ClientConnectCallbackFunction(Oyster::Network::NetworkClient& connectedClient); - void NetworkCallback(Oyster::Network::NetworkClient* client) override; - - bool initiated; - bool running; - bool released; - int maxClients; - MainLobby *mainLobby; - Oyster::Network::NetworkServer *server; - static GameServer* instance; - - private: - struct InitData - { - int port; - int clients; - }; - bool LoadIniFile(InitData&); - - private: - friend class AdminInterface; - }; -}// End namspace DanBias -#endif // !DANBIASSERVER_DBSERVER_H diff --git a/Code/Game/DanBiasServer/GameSession/GameClient.cpp b/Code/Game/DanBiasServer/GameSession/GameClient.cpp deleted file mode 100644 index 23f0f3e2..00000000 --- a/Code/Game/DanBiasServer/GameSession/GameClient.cpp +++ /dev/null @@ -1,67 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// - -#include "GameClient.h" -#include "..\LobbySessions\NetworkSession.h" -#include - -using namespace Utility::DynamicMemory; -using namespace DanBias; -using namespace GameLogic; - -static int gameClientIDCount = 1; - -GameClient::GameClient(SmartPointer client, IPlayerData* player, Oyster::Callback::OysterCallback value) -{ - this->callbackValue = value; - this->client = client; - this->id = gameClientIDCount++; - this->player = player; - Oyster::Callback::OysterCallback c; - c.callbackType = Oyster::Callback::CallbackType_Object; - c.value = this; - this->client->SetCallback(c); - -} -GameClient::~GameClient() -{ - if(this->client) this->client->Disconnect(); - this->player = 0; - this->id = -1; -} - -void GameClient::SetCallback(Oyster::Callback::OysterCallback value) -{ - this->callbackValue = value; -} - -GameLogic::IPlayerData* GameClient::GetPlayer() -{ - return this->player; -} -GameLogic::IPlayerData* GameClient::ReleasePlayer() -{ - GameLogic::IPlayerData *temp = this->player; - this->player = 0; - return temp; -} -LobbyClient* GameClient::GetClient() const -{ - return this->client; -} -Utility::DynamicMemory::SmartPointer GameClient::ReleaseClient() -{ - SmartPointer temp = this->client; - this->client = 0; - return temp; -} -int GameClient::GetID() const -{ - return this->id; -} -void GameClient::ObjectCallback(NetworkSession::NetEvent e) -{ - e.gameClient = this; - this->callbackValue(e); -} diff --git a/Code/Game/DanBiasServer/GameSession/GameClient.h b/Code/Game/DanBiasServer/GameSession/GameClient.h deleted file mode 100644 index 0a74479a..00000000 --- a/Code/Game/DanBiasServer/GameSession/GameClient.h +++ /dev/null @@ -1,43 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_CLIENT_OBJECT_H -#define DANBIASSERVER_CLIENT_OBJECT_H - -#include "..\LobbySessions\LobbyClient.h" -#include -#include - -namespace DanBias -{ - class GameClient: Oyster::Callback::CallbackObject - { - public: - GameClient(Utility::DynamicMemory::SmartPointer client, GameLogic::IPlayerData* player, Oyster::Callback::OysterCallback value); - virtual~GameClient(); - - void SetCallback(Oyster::Callback::OysterCallback value); - - /* */ - GameLogic::IPlayerData* GetPlayer(); - - GameLogic::IPlayerData* ReleasePlayer(); - - LobbyClient* GetClient() const; - Utility::DynamicMemory::SmartPointer ReleaseClient(); - int GetID() const; - - private: - //Utility::DynamicMemory::SmartPointer player; - GameLogic::IPlayerData* player; - Utility::DynamicMemory::SmartPointer client; - Oyster::Callback::OysterCallback callbackValue; - int id; - void ObjectCallback(NetworkSession::NetEvent) override; - - private: - friend class AdminInterface; - }; - -}//End namespace DanBias -#endif // !DANBIASSERVER_CLIENT_OBJECT_H diff --git a/Code/Game/DanBiasServer/Helpers/MapManager.h b/Code/Game/DanBiasServer/Helpers/MapManager.h deleted file mode 100644 index 49342a56..00000000 --- a/Code/Game/DanBiasServer/Helpers/MapManager.h +++ /dev/null @@ -1,30 +0,0 @@ -///////////////////////////////////////// -// Created by [Dennis Andersen] [2013] // -///////////////////////////////////////// -#ifndef DANBIASSERVER_LEVELMANAGER_H -#define DANBIASSERVER_LEVELMANAGER_H - -#include "ServerDataReader.h" -#include "..\LobbySessions\LobbyClient.h" -#include "..\LobbySessions\NetworkSession.h" -#include "..\LobbySessions\GameLobby.h" -#include - -namespace DanBias -{ - class MapManager - { - public: - struct MapInitDesc - { - const wchar_t* map; - Utility::DynamicMemory::DynamicArray> clients; - }; - - public: - static bool InitiateMapPack(const MapInitDesc& desc); - - }; -} - -#endif // !DANBIASSERVER_LEVELMANAGER_H diff --git a/Code/Game/DanBiasServer/Helpers/ServerDataReader.h b/Code/Game/DanBiasServer/Helpers/ServerDataReader.h deleted file mode 100644 index 73048bd7..00000000 --- a/Code/Game/DanBiasServer/Helpers/ServerDataReader.h +++ /dev/null @@ -1,44 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_SERVER_INIT_READER_H -#define DANBIASSERVER_SERVER_INIT_READER_H - -#include -#include - -namespace DanBias -{ - enum InitPath - { - InitPath_ServerIni, - }; - static std::string GetInitPath(InitPath file) - { - std::string type = ""; - std::string path = ""; - std::string flag = ""; - - switch (file) - { - case DanBias::InitPath_ServerIni: - flag = "ServerInit"; - break; - } - - std::fstream in; - in.open("..\\Settings\\serversearchpath.ini", std::ios::in); - if(!in.is_open()) return ""; - - while (!in.eof() && type != flag) - { - in >> type; - in >> path; - } - - in.close(); - return path; - } -} - -#endif // !DANBIASSERVER_SERVER_INIT_READER_H diff --git a/Code/Game/DanBiasServer/LobbySessions/GameLobby.h b/Code/Game/DanBiasServer/LobbySessions/GameLobby.h deleted file mode 100644 index b6c6d4f9..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/GameLobby.h +++ /dev/null @@ -1,24 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_GAMELOBBY_H -#define DANBIASSERVER_GAMELOBBY_H - -#include "NetworkSession.h" - -namespace DanBias -{ - class GameLobby :public NetworkSession - { - public: - GameLobby(Utility::DynamicMemory::SmartPointer owner); - virtual~GameLobby(); - - void Release(); - - private: - friend class AdminInterface; - }; -}//End namespace DanBias - -#endif // !DANBIASSERVER_GAME_LOBBY_H \ No newline at end of file diff --git a/Code/Game/DanBiasServer/LobbySessions/INetworkSession.cpp b/Code/Game/DanBiasServer/LobbySessions/INetworkSession.cpp deleted file mode 100644 index 899e1d53..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/INetworkSession.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "INetworkSession.h" -#include - -INetworkSession::INetworkSession() - :id(GID()) -{} -INetworkSession::INetworkSession(const INetworkSession& orig) -{ - id = orig.id; -} -const INetworkSession& INetworkSession::operator=(const INetworkSession& orig) -{ - id = orig.id; - return *this; -} -INetworkSession::~INetworkSession() -{} - diff --git a/Code/Game/DanBiasServer/LobbySessions/INetworkSession.h b/Code/Game/DanBiasServer/LobbySessions/INetworkSession.h deleted file mode 100644 index dfad66ae..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/INetworkSession.h +++ /dev/null @@ -1,22 +0,0 @@ -///////////////////////////////////////// -// Created by [Dennis Andersen] [2013] // -///////////////////////////////////////// -#ifndef DANBIASSERVER_INETWORKSESSION_H -#define DANBIASSERVER_INETWORKSESSION_H - -class INetworkSession -{ -public: - INetworkSession(); - INetworkSession(const INetworkSession& orig); - const INetworkSession& operator=(const INetworkSession& orig); - virtual~INetworkSession(); - - inline int GetID() const { return this->id; } - -private: - int id; - -}; - -#endif // !DANBIASSERVER_INETWORKSESSION_H diff --git a/Code/Game/DanBiasServer/LobbySessions/LobbyClient.cpp b/Code/Game/DanBiasServer/LobbySessions/LobbyClient.cpp deleted file mode 100644 index 95f18e68..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/LobbyClient.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "LobbyClient.h" -#include - -using namespace Utility::DynamicMemory; -using namespace Oyster::Network; -using namespace Oyster::Callback; -using namespace GameLogic; - -namespace DanBias -{ - LobbyClient::LobbyClient(SmartPointer client) - { - this->client = client; - this->client->SetRecieverObject(this, NetworkProtocolCallbackType_Object); - } - LobbyClient::~LobbyClient() - { - this->callbackValue.callbackType = CallbackType_Unknown; - } - - void LobbyClient::Disconnect() - { - this->client->Disconnect(); - } - void LobbyClient::SetCallback(OysterCallback value) - { - this->callbackValue = value; - } - - /** This method is NOT threadsafe. */ - void LobbyClient::NetworkCallback(CustomNetProtocol& protocol) - { - if(this->callbackValue.callbackType == CallbackType_Unknown) return; - - NetworkSession::NetEvent e; - e.sender = this; - e.protocol = protocol; - - this->callbackValue(e); - } - void LobbyClient::Disconnected() - { - if(this->callbackValue.callbackType == CallbackType_Unknown) return; - - NetworkSession::NetEvent e; - e.sender = this; - e.protocol = *GameLogic::Protocol_General_Status(Protocol_General_Status::States_disconected).GetProtocol(); - - this->callbackValue(e); - } -}//End namsapce DanBias \ No newline at end of file diff --git a/Code/Game/DanBiasServer/LobbySessions/LobbyClient.h b/Code/Game/DanBiasServer/LobbySessions/LobbyClient.h deleted file mode 100644 index 33b99d78..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/LobbyClient.h +++ /dev/null @@ -1,38 +0,0 @@ -///////////////////////////////////////// -// Created by [Dennis Andersen] [2013] // -///////////////////////////////////////// -#ifndef DANBIASSERVER_LOBBYCLIENT_H -#define DANBIASSERVER_LOBBYCLIENT_H - -#include "NetworkSession.h" -#include -#include - -namespace DanBias -{ - class LobbyClient :public Oyster::Network::NetClientEvent - { - public: - LobbyClient(Utility::DynamicMemory::SmartPointer client); - virtual~LobbyClient(); - - void Disconnect(); - void SetCallback(Oyster::Callback::OysterCallback value); - - inline void Send(Oyster::Network::CustomProtocolObject& protocol) { this->client->Send(protocol); } - inline void Send(Oyster::Network::CustomNetProtocol* protocol) { this->client->Send(protocol); } - inline int GetID() const { return this->client->GetID(); } - - private: - Utility::DynamicMemory::SmartPointer client; - Oyster::Callback::OysterCallback callbackValue; - - private: - void NetworkCallback(Oyster::Network::CustomNetProtocol& protocol) override; - void Disconnected() override; - - private: - friend class AdminInterface; - }; -}//End namspace DanBias -#endif // !DANBIASSERVER_LOBBYCLIENT_H diff --git a/Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp b/Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp deleted file mode 100644 index cb19336f..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/LobbyGeneralProtocolParser.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "MainLobby.h" -#include "LobbyClient.h" - -using namespace DanBias; -using namespace GameLogic; - -void MainLobby::ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c) -{ - switch (p[0].value.netShort) - { - case protocol_General_Status: - { - GeneralStatus(GameLogic::Protocol_General_Status(p), c); - } break; - case protocol_General_Text: - { - GameLogic::Protocol_General_Text(p); - } break; - case protocol_Lobby_Login: - { - - } break; - case protocol_Lobby_Join: - { - JoinLobby(GameLogic::Protocol_LobbyJoin(p), c); - } break; - } -} - -void MainLobby::GeneralStatus(GameLogic::Protocol_General_Status& p, DanBias::LobbyClient* c) -{ - switch (p.status) - { - case Protocol_General_Status::States_ready: - { - - } - case Protocol_General_Status::States_idle: - { - - } - case Protocol_General_Status::States_leave: - { - - } - case Protocol_General_Status::States_disconected: - { - Detach(c)->Disconnect(); - } - } -} - -void MainLobby::JoinLobby(GameLogic::Protocol_LobbyJoin& p, DanBias::LobbyClient* c) -{ - for (unsigned int i = 0; i < this->gameLobby.Size(); i++) - { - if (this->gameLobby[i]->GetID() == p.value) - { - this->gameLobby[i]->Attach(Detach(c)); - return; - } - } -} - diff --git a/Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp b/Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp deleted file mode 100644 index 27aade71..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/LobbyProtocolParser.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "MainLobby.h" - -using namespace DanBias; - -void MainLobby::ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c) -{ - switch (p[0].value.netShort) - { - case protocol_Lobby_Create: - CreateGame(GameLogic::Protocol_LobbyCreateGame(p), c); - break; - - case protocol_Lobby_Start: - - break; - - case protocol_Lobby_Refresh: - GameLogic::Protocol_LobbyRefresh(); - break; - } -} - -void MainLobby::CreateGame(GameLogic::Protocol_LobbyCreateGame& p, DanBias::LobbyClient* c) -{ - for (unsigned int i = 0; i < this->gameLobby.Size(); i++) - { - if(!gameLobby[i]) - { - gameLobby[i] = new GameLobby(NetworkSession::Detach(c)); - return; - } - } - - this->gameLobby.Push(new GameLobby(NetworkSession::Detach(c))); -} - - diff --git a/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp b/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp deleted file mode 100644 index ef5765c7..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/MainLobby.cpp +++ /dev/null @@ -1,62 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#include "MainLobby.h" -#include "LobbyClient.h" -#include -#include - -using namespace Utility::DynamicMemory; -using namespace Oyster::Network; -using namespace Oyster; - -namespace DanBias -{ - MainLobby::MainLobby() - :gameLobby(5) - { - this->box = new PostBox(); - } - MainLobby::~MainLobby() - { - - } - void MainLobby::Release() - { - delete this->box; - this->box = 0; - this->CloseSession(true); - } - - void MainLobby::Frame() - { - ParseEvents(); - } - IPostBox* MainLobby::GetPostbox() - { - return this->box; - } - void MainLobby::SetRefreshFrequency(float delta) - { - this->refreshFrequency = delta; - } - - float MainLobby::GetRefreshFrequency() const - { - return this->refreshFrequency; - } -//////// Private - void MainLobby::ParseEvents() - { - if(this->box && !this->box->IsEmpty()) - { - NetEvent &e = this->box->Fetch(); - - short type = e.protocol[0].value.netShort; - - if(ProtocolIsLobby(type)) ParseLobbyProtocol(e.protocol, e.sender); - else if(ProtocolIsGeneral(type)) ParseGeneralProtocol(e.protocol, e.sender); - } - } - -}//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/DanBiasServer/LobbySessions/MainLobby.h b/Code/Game/DanBiasServer/LobbySessions/MainLobby.h deleted file mode 100644 index 13e1bab5..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/MainLobby.h +++ /dev/null @@ -1,49 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_MAINLOBBY_H -#define DANBIASSERVER_MAINLOBBY_H - -#include "NetworkSession.h" -#include "GameLobby.h" -#include -#include -#include - -namespace DanBias -{ - class MainLobby :public NetworkSession - { - public: - MainLobby(); - virtual~MainLobby(); - void Release(); - - void Frame(); - - void SetPostbox(Oyster::IPostBox* box); - Oyster::IPostBox* GetPostbox(); - - void SetRefreshFrequency(float delta); - float GetRefreshFrequency() const; - - private: - void ParseEvents(); - void ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c); - void ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::LobbyClient* c); - - void GeneralStatus(GameLogic::Protocol_General_Status& p, DanBias::LobbyClient* c); - void CreateGame(GameLogic::Protocol_LobbyCreateGame& p, DanBias::LobbyClient* c); - void JoinLobby(GameLogic::Protocol_LobbyJoin& p, DanBias::LobbyClient* c); - - private: - Oyster::IPostBox *box; - Utility::DynamicMemory::DynamicArray> gameLobby; - Utility::WinTimer timer; - float refreshFrequency; - - private: - friend class AdminInterface; - }; -}//End namespace DanBias -#endif // !DANBIASGAME_GAMELOBBY_H diff --git a/Code/Game/DanBiasServer/LobbySessions/NetworkSession.cpp b/Code/Game/DanBiasServer/LobbySessions/NetworkSession.cpp deleted file mode 100644 index 0eecf59a..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/NetworkSession.cpp +++ /dev/null @@ -1,219 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#include "LobbyClient.h" -#include "NetworkSession.h" -#include - - - -namespace DanBias -{ - NetworkSession::NetworkSession() - : owner(0) - , clientCount(0) - {} - NetworkSession::NetworkSession(const NetworkSession& orig) - { - this->clients = orig.clients; - this->owner = orig.owner; - this->clientCount = orig.clientCount; - } - const NetworkSession& NetworkSession::operator=(const NetworkSession& orig) - { - this->clients = orig.clients; - this->owner = orig.owner; - this->clientCount = orig.clientCount; - return *this; - } - NetworkSession::~NetworkSession() - { - this->clients.Clear(); - this->clientCount = 0; - } - - bool NetworkSession::Attach(Utility::DynamicMemory::SmartPointer client) - { - clientListLock.lock(); - - int k = -1; - for (unsigned int i = 0; (k == -1) && i < this->clients.Size(); i++) - { - if(!this->clients[i]) - k = i; - } - - if(k == -1) - { - this->clients.Push(client); - } - else - { - this->clients[k] = client; - } - this->clientCount++; - clientListLock.unlock(); - - return true; - } - - Utility::DynamicMemory::SmartPointer NetworkSession::Detach(Oyster::Network::NetworkClient* client) - { - Utility::DynamicMemory::SmartPointer val; - - clientListLock.lock(); - - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[0]->GetID() == client->GetID()) - { - val = this->clients[i]; - this->clients[i] = 0; - this->clientCount--; - } - } - - clientListLock.unlock(); - - return val; - } - Utility::DynamicMemory::SmartPointer NetworkSession::Detach(const LobbyClient* client) - { - Utility::DynamicMemory::SmartPointer val; - - clientListLock.lock(); - - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[0]->GetID() == client->GetID()) - { - val = this->clients[i]; - this->clients[i] = 0; - this->clientCount--; - } - } - - clientListLock.unlock(); - - return val; - } - Utility::DynamicMemory::SmartPointer NetworkSession::Detach(const LobbyClient& client) - { - Utility::DynamicMemory::SmartPointer val; - - clientListLock.lock(); - - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[0]->GetID() == client.GetID()) - { - val = this->clients[i]; - this->clients[i] = 0; - this->clientCount--; - } - } - - clientListLock.unlock(); - - return val; - } - Utility::DynamicMemory::SmartPointer NetworkSession::Detach(short ID) - { - Utility::DynamicMemory::SmartPointer val; - - clientListLock.lock(); - - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[0]->GetID() == ID) - { - val = this->clients[i]; - this->clients[i] = 0; - this->clientCount--; - } - } - - clientListLock.unlock(); - - return val; - } - - bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol) - { - bool returnValue = false; - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[i]) - { - this->clients[i]->Send(&protocol); - returnValue = true; - } - } - - return returnValue; - } - bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol, int ID) - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[i] && this->clients[i]->GetID() == ID) - { - this->clients[i]->Send(&protocol); - return true; - } - } - return false; - } - - void NetworkSession::SetCallback(Oyster::Callback::OysterCallback value) - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - this->clients[i]->SetCallback(value); - } - } - - void NetworkSession::CloseSession(bool dissconnectClients) - { - clientListLock.lock(); - - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(dissconnectClients) this->clients[i]->Disconnect(); - else if(this->owner) this->owner->Attach(this->clients[i]); - } - - this->clients.Clear(); - - clientListLock.unlock(); - } - Utility::DynamicMemory::SmartPointer NetworkSession::FindClient(int ID) - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[i]->GetID() == ID) - return this->clients[i]; - } - return Utility::DynamicMemory::SmartPointer(); - } - Utility::DynamicMemory::SmartPointer NetworkSession::FindClient(LobbyClient& obj) - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[i]->GetID() == obj.GetID()) - return this->clients[i]; - } - return Utility::DynamicMemory::SmartPointer(); - } - Utility::DynamicMemory::SmartPointer NetworkSession::FindClient(LobbyClient* obj) - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[i]->GetID() == obj->GetID()) - return this->clients[i]; - } - return Utility::DynamicMemory::SmartPointer(); - } - - -}//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/DanBiasServer/LobbySessions/NetworkSession.h b/Code/Game/DanBiasServer/LobbySessions/NetworkSession.h deleted file mode 100644 index 41fb0a59..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/NetworkSession.h +++ /dev/null @@ -1,91 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_NETWORK_SESSION_H -#define DANBIASSERVER_NETWORK_SESSION_H - -//warning C4150: deletion of pointer to incomplete type, no destructor called -#pragma warning(disable : 4150) - -#define NOMINMAX - -#include "INetworkSession.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace DanBias -{ - class LobbyClient; - class GameClient; - - class NetworkSession :public INetworkSession - { - public: - struct NetEvent - { - LobbyClient* sender; - GameClient* gameClient; - Oyster::Network::CustomNetProtocol protocol; - NetEvent():sender(0), gameClient(0){} - }; - - public: - NetworkSession(); - NetworkSession(const NetworkSession& orig); - const NetworkSession& operator=(const NetworkSession& orig); - virtual~NetworkSession(); - - virtual bool Attach(Utility::DynamicMemory::SmartPointer client); - - virtual Utility::DynamicMemory::SmartPointer Detach(Oyster::Network::NetworkClient* client); - virtual Utility::DynamicMemory::SmartPointer Detach(const LobbyClient* client); - virtual Utility::DynamicMemory::SmartPointer Detach(const LobbyClient& client); - virtual Utility::DynamicMemory::SmartPointer Detach(short ID); - - Utility::DynamicMemory::SmartPointer FindClient(LobbyClient& obj); - Utility::DynamicMemory::SmartPointer FindClient(LobbyClient* obj); - Utility::DynamicMemory::SmartPointer FindClient(int ID); - - /** - * Sends a message to all clients in this session. - */ - virtual bool Send(Oyster::Network::CustomNetProtocol& message); - /** - * Sends a message to a specific client in this session. - */ - virtual bool Send(Oyster::Network::CustomNetProtocol& protocol, int ID); - - /** - * Set the callback to all clients to where a messages is recieved. - */ - virtual void SetCallback(Oyster::Callback::OysterCallback value); - - /** - * Closes the session and sends the clients to given owner session if any. - * If session is null, clients is assumed to already be elsewhere and only releases a reference. - */ - virtual void CloseSession(bool dissconnectClients = false); - - - /** Set where the clients is returned on closed session. */ - inline void SetOwner(NetworkSession* owner) { this->owner = owner; } - - protected: - Utility::DynamicMemory::DynamicArray> clients; - NetworkSession* owner; //Where clients end up when session is closed. - - private: - std::mutex clientListLock; - int clientCount; - - private: - friend class AdminInterface; - }; -}//End namespace DanBias -#endif // !DANBIASSERVER_NETWORK_SESSION_H diff --git a/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj b/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj index ec88a915..62344a57 100644 --- a/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj +++ b/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj @@ -71,7 +71,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)Game\DanBiasServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -79,7 +79,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)Game\DanBiasServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -87,7 +87,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)Game\DanBiasServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -95,7 +95,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)Game\DanBiasServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) diff --git a/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp b/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp index 17d1a045..7781717d 100644 --- a/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp +++ b/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp @@ -7,7 +7,7 @@ #include -#include +#include int WINAPI WinMain( HINSTANCE hinst, HINSTANCE prevInst, PSTR cmdLine, int cmdShow) { @@ -15,10 +15,14 @@ int WINAPI WinMain( HINSTANCE hinst, HINSTANCE prevInst, PSTR cmdLine, int cmdSh { return cmdShow; } - if( DanBias::DanBiasServerAPI::Initiate() == DanBias::DanBiasServerReturn_Sucess) + + DanBias::GameServerAPI::GameInitDesc desc; + desc.connectionPort = 15151; + desc.maxNumberOfClients = 0; + desc.threaded = false; + if( !DanBias::GameServerAPI::Create(desc) == DanBias::DanBiasServerReturn_Sucess) { - DanBias::DanBiasServerAPI::Run(); - DanBias::DanBiasServerAPI::Release(); + } return cmdShow; } \ No newline at end of file diff --git a/Code/Game/GameProtocols/LobbyProtocols.h b/Code/Game/GameProtocols/LobbyProtocols.h index f043a7a2..4a262404 100644 --- a/Code/Game/GameProtocols/LobbyProtocols.h +++ b/Code/Game/GameProtocols/LobbyProtocols.h @@ -45,14 +45,18 @@ namespace GameLogic struct Protocol_LobbyStartGame :public Oyster::Network::CustomProtocolObject { - char gameId; + short gameId; Protocol_LobbyStartGame() { this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Start; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_Char; + this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + } + Protocol_LobbyStartGame(Oyster::Network::CustomNetProtocol& o) + { + gameId = o[1].value.netInt; } Oyster::Network::CustomNetProtocol* GetProtocol() override { @@ -73,6 +77,10 @@ namespace GameLogic this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + } + Protocol_LobbyLogin(Oyster::Network::CustomNetProtocol& p) + { + } Oyster::Network::CustomNetProtocol* GetProtocol() override { @@ -116,6 +124,10 @@ namespace GameLogic { this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Login; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + } + Protocol_LobbyRefresh(Oyster::Network::CustomNetProtocol& o) + { + } Oyster::Network::CustomNetProtocol* GetProtocol() override { return &protocol; } diff --git a/Code/Game/GameServer/GameClient.h b/Code/Game/GameServer/GameClient.h new file mode 100644 index 00000000..3fa0a6f6 --- /dev/null +++ b/Code/Game/GameServer/GameClient.h @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// +#ifndef DANBIASSERVER_CLIENT_OBJECT_H +#define DANBIASSERVER_CLIENT_OBJECT_H + +#include +#include +#include + +namespace DanBias +{ + class GameClient + { + public: + GameClient(Oyster::Network::NetworkClient client, GameLogic::IPlayerData* player); + virtual~GameClient(); + + //void SetCallback(Oyster::Callback::OysterCallback value); + + GameLogic::IPlayerData* GetPlayer(); + GameLogic::IPlayerData* ReleasePlayer(); + Oyster::Network::NetworkClient* GetClient(); + Oyster::Network::NetworkClient ReleaseClient(); + int GetID() const; + + private: + GameLogic::IPlayerData* player; + Oyster::Network::NetworkClient client; + int id; + }; + +}//End namespace DanBias +#endif // !DANBIASSERVER_CLIENT_OBJECT_H diff --git a/Code/Game/GameServer/GameLobby.h b/Code/Game/GameServer/GameLobby.h new file mode 100644 index 00000000..7a47638d --- /dev/null +++ b/Code/Game/GameServer/GameLobby.h @@ -0,0 +1,48 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// +#ifndef DANBIASSERVER_MAINLOBBY_H +#define DANBIASSERVER_MAINLOBBY_H + +#include +#include +#include +#include + +namespace DanBias +{ + class GameLobby :public Oyster::Network::NetworkSession + { + public: + GameLobby(); + virtual~GameLobby(); + + void Release(); + + void Frame(); + + //void ClientEventCallback(NetEvent e) override; + + private: + void ParseEvents(); + void ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, Oyster::Network::NetworkClient* c); + void ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, Oyster::Network::NetworkClient* c); + + //Lobby events + void LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Network::NetworkClient* c); + void LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); + + //General events + void GeneralStatus(GameLogic::Protocol_General_Status& p, Oyster::Network::NetworkClient* c); + void GeneralText(GameLogic::Protocol_General_Text& p, Oyster::Network::NetworkClient* c); + + private: + void ClientEventCallback(Oyster::Network::NetEvent e) override; + void ClientConnectedEvent(Oyster::Network::NetEvent e) override; + + private: + Utility::WinTimer timer; + float refreshFrequency; + }; +}//End namespace DanBias +#endif // !DANBIASGAME_GAMELOBBY_H diff --git a/Code/Game/GameServer/GameServer.h b/Code/Game/GameServer/GameServer.h new file mode 100644 index 00000000..73857b2d --- /dev/null +++ b/Code/Game/GameServer/GameServer.h @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// +#ifndef DANBIASSERVER_GAME_SERVER_H +#define DANBIASSERVER_GAME_SERVER_H + +#include "GameServerAPI.h" +#include "GameLobby.h" +#include +#include + +namespace DanBias +{ + class GameServer + { + public: + GameServer(); + virtual~GameServer(); + + DanBiasServerReturn Create(const GameServerAPI::GameInitDesc& desc); + + private: + static void Run(GameServer* owner); + void Run(); + void Release(); + + private: + int maxClients; + Utility::DynamicMemory::SmartPointer lobby; + Utility::DynamicMemory::SmartPointer server; + }; +}// End namspace DanBias +#endif // !DANBIASSERVER_DBSERVER_H diff --git a/Code/Game/DanBiasServer/DanBiasServer.vcxproj b/Code/Game/GameServer/GameServer.vcxproj similarity index 78% rename from Code/Game/DanBiasServer/DanBiasServer.vcxproj rename to Code/Game/GameServer/GameServer.vcxproj index 74223981..e736a111 100644 --- a/Code/Game/DanBiasServer/DanBiasServer.vcxproj +++ b/Code/Game/GameServer/GameServer.vcxproj @@ -19,9 +19,10 @@ - {52380DAA-0F4A-4D97-8E57-98DF39319CAF} + {143BD516-20A1-4890-A3E4-F8BFD02220E7} Win32Proj - DanBiasServer + GameServer + GameServer @@ -73,32 +74,32 @@ $(SolutionDir)..\Bin\DLL\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)OysterPhysics3D\;$(IncludePath) - $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;$(SolutionDir)OysterPhysics3D\;$(SolutionDir)Game\ServerDependencies;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(SolutionDir)..\External\Lib\ServerDependencies\;$(LibraryPath) true $(SolutionDir)..\Bin\DLL\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)OysterPhysics3D\;$(IncludePath) - $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;$(SolutionDir)OysterPhysics3D\;$(SolutionDir)Game\ServerDependencies;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(SolutionDir)..\External\Lib\ServerDependencies\;$(LibraryPath) false $(SolutionDir)..\Bin\DLL\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)OysterPhysics3D\;$(IncludePath) - $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;$(SolutionDir)OysterPhysics3D\;$(SolutionDir)Game\ServerDependencies;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(SolutionDir)..\External\Lib\ServerDependencies\;$(LibraryPath) false $(SolutionDir)..\Bin\DLL\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)OysterPhysics3D\;$(IncludePath) - $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Game\GameLogic\;$(SolutionDir)Network\NetworkAPI\;$(SolutionDir)OysterMath\;$(SolutionDir)GamePhysics\;$(SolutionDir)Misc\;$(SolutionDir)WindowManager\;$(SolutionDir)OysterPhysics3D\;$(SolutionDir)Game\ServerDependencies;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(OutDir);$(SolutionDir)..\External\Lib\WindowManager\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(SolutionDir)..\External\Lib\ServerDependencies\;$(LibraryPath) @@ -173,39 +174,35 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + true + true + true + true + + + true + true + true + true + - - + + - - - - - - - - - - - + + + diff --git a/Code/Game/DanBiasServer/DanBiasServer.vcxproj.user b/Code/Game/GameServer/GameServer.vcxproj.user similarity index 100% rename from Code/Game/DanBiasServer/DanBiasServer.vcxproj.user rename to Code/Game/GameServer/GameServer.vcxproj.user diff --git a/Code/Game/DanBiasServer/DanBiasServerAPI.h b/Code/Game/GameServer/GameServerAPI.h similarity index 73% rename from Code/Game/DanBiasServer/DanBiasServerAPI.h rename to Code/Game/GameServer/GameServerAPI.h index 1d514053..08e25114 100644 --- a/Code/Game/DanBiasServer/DanBiasServerAPI.h +++ b/Code/Game/GameServer/GameServerAPI.h @@ -24,12 +24,18 @@ namespace DanBias extern "C" { - class DANBIAS_SERVER_DLL DanBiasServerAPI + class DANBIAS_SERVER_DLL GameServerAPI { public: - static DanBiasServerReturn Initiate(); - static DanBiasServerReturn Run(); - static DanBiasServerReturn Release(); + struct GameInitDesc + { + //stuff + int connectionPort; + int maxNumberOfClients; + bool threaded; + }; + public: + static DanBiasServerReturn Create(const GameInitDesc& desc); };//End class DanBiasServer }//End Extern "C" } //End namspace DanBias diff --git a/Code/Game/DanBiasServer/GameSession/GameSession.h b/Code/Game/GameServer/GameSession.h similarity index 85% rename from Code/Game/DanBiasServer/GameSession/GameSession.h rename to Code/Game/GameServer/GameSession.h index aee9b29d..eed2b4b4 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession.h +++ b/Code/Game/GameServer/GameSession.h @@ -7,18 +7,18 @@ //warning C4150: deletion of pointer to incomplete type, no destructor called, because of forward decleration and the use of smartpointer. #pragma warning(disable: 4150) -#include "..\LobbySessions\NetworkSession.h" + +#include "GameClient.h" #include #include #include #include #include +#include namespace DanBias { - class LobbyClient; - class GameClient; - class GameSession : public Oyster::Thread::IThreadObject, public INetworkSession + class GameSession : public Oyster::Thread::IThreadObject { public: /** @@ -28,7 +28,7 @@ namespace DanBias { std::wstring mapName; NetworkSession* owner; - Utility::DynamicMemory::DynamicArray> clients; + Utility::DynamicMemory::DynamicArray clients; }; public: @@ -44,7 +44,7 @@ namespace DanBias /** Join an existing/running game session * @param client The client to attach to the session */ - bool Join(Utility::DynamicMemory::SmartPointer client); + bool Join(Utility::DynamicMemory::SmartPointer client); /** * Closes the game session @@ -82,7 +82,7 @@ namespace DanBias private: Utility::DynamicMemory::DynamicArray> clients; - Oyster::IPostBox *box; + //Oyster::PostBox *box; Oyster::Thread::OysterThread worker; GameLogic::GameAPI& gameInstance; GameLogic::ILevelData *levelData; @@ -93,8 +93,6 @@ namespace DanBias static void ObjectMove(GameLogic::IObjectData* movedObject); - private: - friend class AdminInterface; };//End GameSession }//End namespace DanBias diff --git a/Code/Game/DanBiasServer/GameSession/GameSessionManager.h b/Code/Game/GameServer/GameSessionManager.h similarity index 90% rename from Code/Game/DanBiasServer/GameSession/GameSessionManager.h rename to Code/Game/GameServer/GameSessionManager.h index 75b0e64d..a2c8a3ee 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSessionManager.h +++ b/Code/Game/GameServer/GameSessionManager.h @@ -4,19 +4,19 @@ #ifndef DANBIASSERVER_GAME_SEESION_MANAGER_H #define DANBIASSERVER_GAME_SEESION_MANAGER_H +#include #include #include #include +using namespace Oyster::Network; + namespace DanBias { - class LobbyClient; - class NetworkSession; - struct GameSessionDescription { std::wstring mapName; - Utility::DynamicMemory::DynamicArray> clients; + Utility::DynamicMemory::DynamicArray clients; NetworkSession* exitDestionation; //The new owner when session dies }; struct GameSessionInfo @@ -49,7 +49,7 @@ namespace DanBias * @param client The client that is to join a game session * @return Returns false on failure. */ - static bool JoinSession(int session, Utility::DynamicMemory::SmartPointer client); + static bool JoinSession(int session, Utility::DynamicMemory::SmartPointer client); /** * Gets information about a given session @@ -67,7 +67,7 @@ namespace DanBias /** * Close all sessions. */ - static void CloseSession(); + static void CloseSessions(); /** * Get total sessions running diff --git a/Code/Game/DanBiasServer/DLLMain.cpp b/Code/Game/GameServer/Implementation/DLLMain.cpp similarity index 100% rename from Code/Game/DanBiasServer/DLLMain.cpp rename to Code/Game/GameServer/Implementation/DLLMain.cpp diff --git a/Code/Game/GameServer/Implementation/GameClient.cpp b/Code/Game/GameServer/Implementation/GameClient.cpp new file mode 100644 index 00000000..94ac807a --- /dev/null +++ b/Code/Game/GameServer/Implementation/GameClient.cpp @@ -0,0 +1,50 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// + +#include "..\GameClient.h" +#include +#include + +using namespace Utility::DynamicMemory; +using namespace DanBias; +using namespace GameLogic; + +static int gameClientIDCount = 1; + +GameClient::GameClient(Oyster::Network::NetworkClient client, GameLogic::IPlayerData* player) +{ + this->client = client; + this->id = gameClientIDCount++; + this->player = player; +} +GameClient::~GameClient() +{ + this->client.Disconnect(); + this->player = 0; + this->id = -1; +} + +GameLogic::IPlayerData* GameClient::GetPlayer() +{ + return this->player; +} +GameLogic::IPlayerData* GameClient::ReleasePlayer() +{ + GameLogic::IPlayerData *temp = this->player; + this->player = 0; + return temp; +} +Oyster::Network::NetworkClient* GameClient::GetClient() +{ + return &this->client; +} +Oyster::Network::NetworkClient GameClient::ReleaseClient() +{ + Oyster::Network::NetworkClient temp = this->client; + return temp; +} +int GameClient::GetID() const +{ + return this->id; +} diff --git a/Code/Game/GameServer/Implementation/GameLobby.cpp b/Code/Game/GameServer/Implementation/GameLobby.cpp new file mode 100644 index 00000000..ad3de1c7 --- /dev/null +++ b/Code/Game/GameServer/Implementation/GameLobby.cpp @@ -0,0 +1,51 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// +#include "..\GameLobby.h" +#include +#include + +using namespace Utility::DynamicMemory; +using namespace Oyster::Network; +using namespace Oyster; + +namespace DanBias +{ + GameLobby::GameLobby() + { } + + GameLobby::~GameLobby() + { } + + void GameLobby::Release() + { } + + void GameLobby::Frame() + { + ParseEvents(); + } + + void GameLobby::ClientEventCallback(NetEvent e) + { + + } + void GameLobby::ClientConnectedEvent(NetEvent e) + { + + } + +//////// Private + void GameLobby::ParseEvents() + { + //if(this->box && !this->box->IsEmpty()) + //{ + // NetEvent &e = this->box->Fetch(); + // + // short type = e.protocol[0].value.netShort; + // + // if(ProtocolIsLobby(type)) ParseLobbyProtocol(e.protocol, e.sender); + // else if(ProtocolIsGeneral(type)) ParseGeneralProtocol(e.protocol, e.sender); + //} + } + +}//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/GameServer/Implementation/GameServer.cpp b/Code/Game/GameServer/Implementation/GameServer.cpp new file mode 100644 index 00000000..90620c18 --- /dev/null +++ b/Code/Game/GameServer/Implementation/GameServer.cpp @@ -0,0 +1,139 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// +#define NOMINMAX +#include +#include +#include + + +#include "..\GameServer.h" +#include "..\GameSessionManager.h" +#include "..\GameSession.h" + +#include +#include +#include +#include +#include + +namespace DanBias +{ + using namespace Oyster::Network; + + GameServer* instance = 0; + std::thread workerThread; + typedef void(*WorkerThreadFnc)(GameServer*); + + DanBiasServerReturn GameServerAPI::Create(const GameInitDesc& desc) + { + if(!instance) + instance = new GameServer(); + + return instance->Create(desc); + } + + + + GameServer::GameServer() + : maxClients(0) + { } + GameServer::~GameServer() + { } + DanBiasServerReturn GameServer::Create(const GameServerAPI::GameInitDesc& desc) + { + this->maxClients = desc.maxNumberOfClients; + this->server = new NetworkServer(); + this->lobby = new GameLobby(); + + if(desc.threaded) + { + if(!this->server->Init(desc.connectionPort, this->lobby)) + return DanBiasServerReturn_Error; + + if(!this->server->Start()) + return DanBiasServerReturn_Error; + + WorkerThreadFnc temp = GameServer::Run; + workerThread = std::thread(temp, this); + } + else + { + if(!this->server->Init(desc.connectionPort, this->lobby)) + return DanBiasServerReturn_Error; + + if(!this->server->Start()) + return DanBiasServerReturn_Error; + + Run(); + } + + return DanBiasServerReturn_Sucess; + } + void GameServer::Run(GameServer* owner) + { + while (true) + { + owner->server->ProcessConnectedClients(); + owner->lobby->ProcessClients(); + + if(GetAsyncKeyState(0x51)) //Q for exit + break; + } + } + void GameServer::Run() + { + while (true) + { + this->server->ProcessConnectedClients(); + this->lobby->Frame(); + + if(GetAsyncKeyState(0x51)) //Q for exit + break; + } + } + void GameServer::Release() + { + GameSessionManager::CloseSessions(); + } + + + + + //void GameServer::ClientConnected(NetworkClient* client) + //{ + // static bool myTest = false; + // static int sessionId = -1; + // printf("Client with ID [%i] connected.\n", client->GetID()); + // + // if(!myTest) + // { + // Utility::DynamicMemory::SmartPointer c = new Client(client); + // + // GameSessionDescription desc; + // desc.mapName = L"test"; + // desc.clients.Push(c); + // desc.exitDestionation = this->lobby; + // if((sessionId = GameSessionManager::AddSession(desc, true)) == 0) + // printf("Failed to create a game session\n"); + // myTest = true; + // //myTest = new GameSession(); + // // + // //DanBias::GameSession::GameSessionDescription desc; + // //desc.owner = 0; + // //desc.clients.Push(c); + // // + // //if(!myTest->Create(desc)) return; + // //myTest->Run(); + // } + // else + // { + // Utility::DynamicMemory::SmartPointer c = new NetworkSession(client); + // GameSessionManager::JoinSession(sessionId, c); + // } + // + // + // //Utility::DynamicMemory::SmartPointer c = new NetworkSession(client); + // //this->mainLobby->Attach(c, this->mainLobby->GetPostbox()); + //} +}//End namespace DanBias diff --git a/Code/Game/DanBiasServer/GameSession/GameSessionManager.cpp b/Code/Game/GameServer/Implementation/GameSessionManager.cpp similarity index 93% rename from Code/Game/DanBiasServer/GameSession/GameSessionManager.cpp rename to Code/Game/GameServer/Implementation/GameSessionManager.cpp index 23dadb65..5eb8d664 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSessionManager.cpp +++ b/Code/Game/GameServer/Implementation/GameSessionManager.cpp @@ -1,10 +1,8 @@ ///////////////////////////////////////////////////////////////////// // Created by [Dennis Andersen] [2013] ///////////////////////////////////////////////////////////////////// -#include "GameSessionManager.h" - -#include "..\LobbySessions\LobbyClient.h" -#include "GameSession.h" +#include "..\GameSessionManager.h" +#include "..\GameSession.h" #include using namespace DanBias; @@ -75,7 +73,7 @@ bool GameSessionManager::StartSession(int session) return true; } -bool GameSessionManager::JoinSession(int session, Utility::DynamicMemory::SmartPointer client) +bool GameSessionManager::JoinSession(int session, Utility::DynamicMemory::SmartPointer client) { int i = -1; if((i = gameSessionData.Existst(session)) == -1) return false; @@ -97,7 +95,7 @@ void GameSessionManager::GetSessionInfo(int session, GameSessionInfo& data) //data.numberOfPlayers = gameSessionData.sessions[i]-> } -void GameSessionManager::CloseSession() +void GameSessionManager::CloseSessions() { for (unsigned int i = 0; i < gameSessionData.sessions.Size(); i++) { diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/GameServer/Implementation/GameSession_Events.cpp similarity index 96% rename from Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp rename to Code/Game/GameServer/Implementation/GameSession_Events.cpp index 5dac3c3e..b3b831f6 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_Events.cpp @@ -1,9 +1,10 @@ ///////////////////////////////////////////////////////////////////// // Created by [Dennis Andersen] [2013] ///////////////////////////////////////////////////////////////////// -#include "GameSession.h" -#include "GameClient.h" +#include "..\GameSession.h" +#include "..\GameClient.h" +#include #include #include #include diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_General.cpp b/Code/Game/GameServer/Implementation/GameSession_General.cpp similarity index 96% rename from Code/Game/DanBiasServer/GameSession/GameSession_General.cpp rename to Code/Game/GameServer/Implementation/GameSession_General.cpp index 0f9f3c57..e68ac8dc 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_General.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_General.cpp @@ -1,8 +1,8 @@ ///////////////////////////////////////////////////////////////////// // Created by [Dennis Andersen] [2013] ///////////////////////////////////////////////////////////////////// -#include "GameSession.h" -#include "GameClient.h" +#include "..\GameSession.h" +#include "..\GameClient.h" #include "..\GameServer.h" #include #include @@ -105,7 +105,7 @@ namespace DanBias } } - bool GameSession::Join(Utility::DynamicMemory::SmartPointer client) + bool GameSession::Join(Utility::DynamicMemory::SmartPointer client) { if(!this->isCreated) return false; diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp b/Code/Game/GameServer/Implementation/GameSession_Logic.cpp similarity index 96% rename from Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp rename to Code/Game/GameServer/Implementation/GameSession_Logic.cpp index ddf41582..1388f6fe 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_Logic.cpp @@ -1,8 +1,8 @@ ///////////////////////////////////////////////////////////////////// // Created by [Dennis Andersen] [2013] ///////////////////////////////////////////////////////////////////// -#include "GameSession.h" -#include "GameClient.h" +#include "..\GameSession.h" +#include "..\GameClient.h" #include #include diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Network.cpp b/Code/Game/GameServer/Implementation/GameSession_Network.cpp similarity index 92% rename from Code/Game/DanBiasServer/GameSession/GameSession_Network.cpp rename to Code/Game/GameServer/Implementation/GameSession_Network.cpp index 729ef417..df546b35 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Network.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_Network.cpp @@ -1,8 +1,8 @@ ///////////////////////////////////////////////////////////////////// // Created by [Dennis Andersen] [2013] ///////////////////////////////////////////////////////////////////// -#include "GameSession.h" -#include "GameClient.h" +#include "..\GameSession.h" +#include "..\GameClient.h" #include #include diff --git a/Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp b/Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp new file mode 100644 index 00000000..23195087 --- /dev/null +++ b/Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp @@ -0,0 +1,52 @@ +#include "GameLobby.h" +#include "NetworkSession.h" + +using namespace DanBias; +using namespace GameLogic; + +void GameLobby::ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::NetworkSession* c) +{ + switch (p[0].value.netShort) + { + case protocol_General_Status: + { + GeneralStatus(GameLogic::Protocol_General_Status(p), c); + } break; + case protocol_General_Text: + { + GeneralText(GameLogic::Protocol_General_Text(p), c); + } break; + } +} + +////////////////////////////////////////////////////// + +void GameLobby::GeneralStatus(GameLogic::Protocol_General_Status& p, DanBias::NetworkSession* c) +{ + switch (p.status) + { + case Protocol_General_Status::States_ready: + { + + } + case Protocol_General_Status::States_idle: + { + + } + case Protocol_General_Status::States_leave: + { + + } + case Protocol_General_Status::States_disconected: + { + Detach(c)->Disconnect(); + } + } +} + +void GameLobby::GeneralText(GameLogic::Protocol_General_Text& p, DanBias::NetworkSession* c) +{ + +} + + diff --git a/Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp b/Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp new file mode 100644 index 00000000..4063db18 --- /dev/null +++ b/Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp @@ -0,0 +1,58 @@ +#include "..\GameLobby.h" + +using namespace DanBias; + +void GameLobby::ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::NetworkSession* c) +{ + switch (p[0].value.netShort) + { + case protocol_Lobby_Start: + LobbyStartGame(GameLogic::Protocol_LobbyStartGame(p), c); + break; + + case protocol_Lobby_Refresh: + LobbyRefresh(GameLogic::Protocol_LobbyRefresh(p), c); + break; + case protocol_Lobby_Login: + { + LobbyLogin(GameLogic::Protocol_LobbyLogin(p), c); + } break; + case protocol_Lobby_Join: + { + LobbyJoin(GameLogic::Protocol_LobbyJoin(p), c); + } break; + } +} + +void GameLobby::LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, DanBias::NetworkSession* c) +{ + +} + +void GameLobby::LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, DanBias::NetworkSession* c) +{ + double now = this->timer.getElapsedSeconds() + c->lastPoll; + + if(now > this->refreshFrequency) + { + c->lastPoll = (float)now; + } +} + +void GameLobby::LobbyLogin(GameLogic::Protocol_LobbyLogin& p, DanBias::NetworkSession* c) +{ + +} + +void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyJoin& p, DanBias::NetworkSession* c) +{ + for (unsigned int i = 0; i < this->gameLobby.Size(); i++) + { + if (this->gameLobby[i]->GetID() == p.value) + { + this->gameLobby[i]->Attach(Detach(c)); + return; + } + } +} + diff --git a/Code/Game/aDanBiasGameLauncher/Launcher.cpp b/Code/Game/aDanBiasGameLauncher/Launcher.cpp index c4ff4194..7e533171 100644 --- a/Code/Game/aDanBiasGameLauncher/Launcher.cpp +++ b/Code/Game/aDanBiasGameLauncher/Launcher.cpp @@ -29,6 +29,7 @@ void ClientFnc() //gameDesc.IP = "193.11.184.31"; //gameDesc.IP = "194.47.150.56"; gameDesc.IP = "127.0.0.1"; + //gameDesc.IP = "194.47.150.184"; if( DanBias::DanBiasGame::Initiate(gameDesc) == DanBias::DanBiasClientReturn_Sucess) { diff --git a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj index b2b5b766..5f33ada6 100644 --- a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj +++ b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj @@ -177,9 +177,6 @@ {2a1bc987-af42-4500-802d-89cd32fc1309} - - {52380daa-0f4a-4d97-8e57-98df39319caf} - diff --git a/Code/Misc/DynamicArray.h b/Code/Misc/DynamicArray.h index e2041f99..1db30719 100644 --- a/Code/Misc/DynamicArray.h +++ b/Code/Misc/DynamicArray.h @@ -4,6 +4,8 @@ #ifndef MISC_DYNAMIC_ARRAY_H #define MISC_DYNAMIC_ARRAY_H +#include + namespace Utility { namespace DynamicMemory diff --git a/Code/Misc/Thread/OysterThread.h b/Code/Misc/Thread/OysterThread.h index cd4bfd3f..739487d9 100644 --- a/Code/Misc/Thread/OysterThread.h +++ b/Code/Misc/Thread/OysterThread.h @@ -72,8 +72,7 @@ namespace Oyster //OYSTER_THREAD_ERROR Create(Oyster::Callback::CallbackFunction::FNC worker, bool start, bool detach = false); OYSTER_THREAD_ERROR Start(); OYSTER_THREAD_ERROR Stop(); - OYSTER_THREAD_ERROR Pause(); - OYSTER_THREAD_ERROR Pause(int mSec); + OYSTER_THREAD_ERROR Stop(int msec); OYSTER_THREAD_ERROR Resume(); OYSTER_THREAD_ERROR SetWorker(IThreadObject* worker = 0); OYSTER_THREAD_ERROR SetWorker(ThreadFnc worker = 0); diff --git a/Code/Misc/Thread/OysterThread_Impl.cpp b/Code/Misc/Thread/OysterThread_Impl.cpp index 6c89567c..a09574df 100644 --- a/Code/Misc/Thread/OysterThread_Impl.cpp +++ b/Code/Misc/Thread/OysterThread_Impl.cpp @@ -229,6 +229,7 @@ OYSTER_THREAD_ERROR OysterThread::Create(IThreadObject* worker, bool start, bool { if(!this->privateData) this->privateData = new PrivateData(); + if(this->privateData->data->isCreated) return OYSTER_THREAD_ERROR_ThreadAlreadyCreated; OwnerContainer c; c.type = Oyster::Callback::CallbackType_Object; c.value = worker; @@ -257,15 +258,11 @@ OYSTER_THREAD_ERROR OysterThread::Start() return OYSTER_THREAD_ERROR_SUCCESS; } OYSTER_THREAD_ERROR OysterThread::Stop() -{ - return this->Terminate(); -} -OYSTER_THREAD_ERROR OysterThread::Pause() { this->privateData->data->threadData->state = OYSTER_THREAD_STATE_IDLE; return OYSTER_THREAD_ERROR_SUCCESS; } -OYSTER_THREAD_ERROR OysterThread::Pause(int msec) +OYSTER_THREAD_ERROR OysterThread::Stop(int msec) { this->privateData->data->threadData->msec = msec; return OYSTER_THREAD_ERROR_SUCCESS; diff --git a/Code/Misc/ThreadSafeQueue.h b/Code/Misc/ThreadSafeQueue.h index 3fab7c70..3afb1cb3 100644 --- a/Code/Misc/ThreadSafeQueue.h +++ b/Code/Misc/ThreadSafeQueue.h @@ -10,6 +10,7 @@ ///////////////////////////////////////////// #include "IQueue.h" +#include namespace Utility { @@ -20,6 +21,7 @@ namespace Utility { public: ThreadSafeQueue(); + ThreadSafeQueue(const ThreadSafeQueue& obj); virtual ~ThreadSafeQueue(); virtual void Push( Type item ); @@ -64,6 +66,12 @@ namespace Utility } + template < typename Type > + ThreadSafeQueue::ThreadSafeQueue(const ThreadSafeQueue& obj) + { + + } + template < typename Type > ThreadSafeQueue::~ThreadSafeQueue() { diff --git a/Code/Network/NetworkAPI/CustomNetProtocol.h b/Code/Network/NetworkAPI/CustomNetProtocol.h index 85997c51..8e531f17 100644 --- a/Code/Network/NetworkAPI/CustomNetProtocol.h +++ b/Code/Network/NetworkAPI/CustomNetProtocol.h @@ -6,12 +6,7 @@ #include //#include - -#ifdef CUSTOM_NET_PROTOCOL_EXPORT - #define NET_PROTOCOL_EXPORT __declspec(dllexport) -#else - #define NET_PROTOCOL_EXPORT __declspec(dllimport) -#endif +#include "NetworkAPI_Preprocessor.h" namespace Oyster { @@ -78,7 +73,7 @@ namespace Oyster virtual CustomNetProtocol* GetProtocol() = 0; }; - class NET_PROTOCOL_EXPORT CustomNetProtocol + class NET_API_EXPORT CustomNetProtocol { public: CustomNetProtocol(); diff --git a/Code/Network/NetworkAPI/NetworkAPI.vcxproj b/Code/Network/NetworkAPI/NetworkAPI.vcxproj index fe2f5c09..b4b8a31f 100644 --- a/Code/Network/NetworkAPI/NetworkAPI.vcxproj +++ b/Code/Network/NetworkAPI/NetworkAPI.vcxproj @@ -105,7 +105,7 @@ Level3 Disabled - CUSTOM_NET_PROTOCOL_EXPORT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + NETWORKAPI_EXPORT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -119,7 +119,7 @@ Level3 Disabled - CUSTOM_NET_PROTOCOL_EXPORT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + NETWORKAPI_EXPORT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -135,7 +135,7 @@ MaxSpeed true true - CUSTOM_NET_PROTOCOL_EXPORT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + NETWORKAPI_EXPORT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -153,7 +153,7 @@ MaxSpeed true true - CUSTOM_NET_PROTOCOL_EXPORT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + NETWORKAPI_EXPORT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -167,13 +167,16 @@ + - + + + diff --git a/Code/Network/NetworkAPI/NetworkAPI_Preprocessor.h b/Code/Network/NetworkAPI/NetworkAPI_Preprocessor.h new file mode 100644 index 00000000..351dbaf0 --- /dev/null +++ b/Code/Network/NetworkAPI/NetworkAPI_Preprocessor.h @@ -0,0 +1,10 @@ +#ifndef NETWORK_API_NETWORK_API_PREPROCESSOR_H +#define NETWORK_API_NETWORK_API_PREPROCESSOR_H + +#ifdef NETWORKAPI_EXPORT + #define NET_API_EXPORT __declspec(dllexport) +#else + #define NET_API_EXPORT __declspec(dllimport) +#endif + +#endif // !NETWORK_API_NETWORK_API_PREPROCESSOR_H diff --git a/Code/Network/NetworkAPI/NetworkCallbackHelper.h b/Code/Network/NetworkAPI/NetworkCallbackHelper.h deleted file mode 100644 index 963dc6de..00000000 --- a/Code/Network/NetworkAPI/NetworkCallbackHelper.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef NETWORK_API_NETWORK_CALLBACK_HELPER_H -#define NETWORK_API_NETWORK_CALLBACK_HELPER_H - -///////////////////////////////////// -// Created by Dennis Andersen 2013 // -///////////////////////////////////// - -#include - - -namespace Oyster -{ - namespace Network - { - enum NetworkProtocolCallbackType - { - NetworkProtocolCallbackType_Function, - NetworkProtocolCallbackType_Object, - NetworkProtocolCallbackType_Unknown = -1, - }; - enum NetworkClientCallbackType - { - NetworkClientCallbackType_Function, - NetworkClientCallbackType_Object, - NetworkClientCallbackType_Unknown = -1, - }; - - class NetworkClient; - class CustomNetProtocol; - - - template - struct NetRecieverObject - { - virtual void NetworkCallback(Param) = 0; - }; - struct NetClientEvent :public NetRecieverObject - { - virtual void NetworkCallback(CustomNetProtocol& protocol) = 0; - virtual void Disconnected() { }; - }; - typedef NetRecieverObject ClientConnectedObject ; - typedef NetClientEvent ProtocolRecieverObject; - - - template - struct NetCallbackFunction - { - typedef void (*FNC)(Param); - }; - typedef NetCallbackFunction::FNC ClientConnectCallbackMethod; - typedef NetCallbackFunction::FNC ProtocolRecFunction; - struct NetClientMethods - { - typedef void(*Dissconnected)(void); - ProtocolRecFunction recieved; - Dissconnected dissconnected; - void operator()(CustomNetProtocol& obj) { if(recieved) recieved(obj); } - void operator()() { if(dissconnected) dissconnected(); } - }; - typedef NetClientMethods ProtocolRecieverFunction; - - union RecieverObject - { - ClientConnectCallbackMethod clientConnectFnc; // !< A function pointer for sending or recieving NetworkClient - ProtocolRecieverFunction protocolRecieverFnc; // !< A function pointer for sending or recieving CustomNetProtocol - - ClientConnectedObject *clientConnectObject; // !< An object for sending or recieving NetworkClient - ProtocolRecieverObject *protocolRecievedObject; // !< An object with collected client events methods. - - RecieverObject() { memset(this, 0, sizeof(RecieverObject)); } - RecieverObject(ClientConnectCallbackMethod o) { clientConnectFnc = o; } - RecieverObject(ProtocolRecieverFunction o) { protocolRecieverFnc = o; } - RecieverObject(ClientConnectedObject* o) { clientConnectObject = o; } - RecieverObject(ProtocolRecieverObject* o) { protocolRecievedObject = o; } - }; - } -} - -#endif \ No newline at end of file diff --git a/Code/Network/NetworkAPI/NetworkClient.cpp b/Code/Network/NetworkAPI/NetworkClient.cpp index b55e4d7e..71614f77 100644 --- a/Code/Network/NetworkAPI/NetworkClient.cpp +++ b/Code/Network/NetworkAPI/NetworkClient.cpp @@ -7,6 +7,7 @@ #include "Translator.h" #include "CustomNetProtocol.h" +#include "NetworkSession.h" #include "../NetworkDependencies/Connection.h" #include "../NetworkDependencies/PostBox.h" @@ -19,73 +20,53 @@ using namespace Oyster::Network; using namespace Oyster::Thread; using namespace Utility::DynamicMemory; +using namespace Utility::Container; /************************************* PrivateData *************************************/ -struct ClientDataContainer -{ +typedef NetworkClient::ClientEventArgs CEA; +struct NetDataContainer : public IThreadObject +{ //This struct is contained within a smart pointer. To avoide dependencies in link its implemented here.. + NetworkSession *owner; + NetworkClient *parent; Connection connection; - - SmartPointer> sendPostBox; - - RecieverObject recvObj; - NetworkProtocolCallbackType callbackType; - - Oyster::Thread::OysterThread thread; - std::mutex recvObjMutex; - std::mutex postBoxMutex; - Translator translator; + OysterThread thread; + + //Message queue for sending and recieving + ThreadSafeQueue sendQueue; + ThreadSafeQueue> recieveQueue; + //ID static unsigned int currID; const unsigned int ID; - ClientDataContainer(IThreadObject* o) - : ID(currID++) - { - InitWinSock(); - callbackType = NetworkProtocolCallbackType_Unknown; - sendPostBox = new PostBox(); - connection.InitiateClient(); - connection.SetBlockingMode(false); + NetDataContainer() + : ID(currID++) + , parent(0) + , owner(0) + { - } - ClientDataContainer(IThreadObject* o, unsigned int socket ) - :connection(socket), ID(currID++) - { InitWinSock(); - callbackType = NetworkProtocolCallbackType_Unknown; - sendPostBox = new PostBox(); - connection.InitiateClient(); - connection.SetBlockingMode(false); + this->thread.Create(this, true); + this->thread.SetPriority(Oyster::Thread::OYSTER_THREAD_PRIORITY_1); } - ~ClientDataContainer() - { - connection.Disconnect(); - thread.Stop(); - callbackType = NetworkProtocolCallbackType_Unknown; - + NetDataContainer(const NetDataContainer& obj) + :ID(obj.ID) { } + ~NetDataContainer() + { ShutdownWinSock(); + this->connection.Disconnect(); + this->thread.Terminate(); + this->owner = 0; + this->parent = 0; } - -}; -unsigned int ClientDataContainer::currID = 0; - -struct NetworkClient::PrivateData : public IThreadObject -{ - Utility::DynamicMemory::SmartPointer data; - - PrivateData() { this->data = new ClientDataContainer(this); } - PrivateData(unsigned int socket) { this->data = new ClientDataContainer(this, socket); } - ~PrivateData() { } - - bool DoWork() + bool DoWork() override { - if(!this->data) return false; - if(!this->data->connection.IsConnected()) return false; + if(!this->connection.IsConnected()) return false; Send(); Recv(); @@ -93,45 +74,25 @@ struct NetworkClient::PrivateData : public IThreadObject return true; } - - void Send(CustomNetProtocol* protocol) - { - if(!data) return; - - this->data->postBoxMutex.lock(); - this->data->sendPostBox->PostMessage(*protocol); - this->data->postBoxMutex.unlock(); - } - int Send() { int errorCode = 0; - if(!data) return -1; - this->data->postBoxMutex.lock(); - if(this->data->sendPostBox->IsFull()) + if(!this->sendQueue.IsEmpty()) { SmartPointer temp = new OysterByte(); - this->data->translator.Pack(temp, this->data->sendPostBox->FetchMessage()); - errorCode = this->data->connection.Send(temp); + CustomNetProtocol p = this->sendQueue.Pop(); + this->translator.Pack(temp, p); + errorCode = this->connection.Send(temp); if(errorCode != 0) { - //Failed - this->data->connection.Disconnect(); - - this->data->recvObjMutex.lock(); - if(this->data->callbackType == NetworkProtocolCallbackType_Function) - { - this->data->recvObj.protocolRecieverFnc(); - } - else if(this->data->callbackType == NetworkProtocolCallbackType_Object) - { - this->data->recvObj.protocolRecievedObject->Disconnected(); - } - this->data->recvObjMutex.unlock(); + CEA parg; + parg.type = CEA::EventType_ProtocolFailedToSend; + parg.data.protocol = p; + NetEvent e = { this->parent, parg }; + this->recieveQueue.Push(e); } } - this->data->postBoxMutex.unlock(); return errorCode; } @@ -141,105 +102,129 @@ struct NetworkClient::PrivateData : public IThreadObject int errorCode = -1; OysterByte temp = OysterByte(); - errorCode = this->data->connection.Recieve(temp); + errorCode = this->connection.Recieve(temp); if(errorCode == 0 && temp.GetSize()) { CustomNetProtocol protocol; - bool ok = this->data->translator.Unpack(protocol, temp); + bool ok = this->translator.Unpack(protocol, temp); //Check if the protocol was unpacked correctly if(ok) { - this->data->recvObjMutex.lock(); - if(this->data->callbackType == NetworkProtocolCallbackType_Function) - { - this->data->recvObj.protocolRecieverFnc(protocol); - } - else if(this->data->callbackType == NetworkProtocolCallbackType_Object) - { - this->data->recvObj.protocolRecievedObject->NetworkCallback(protocol); - } - this->data->recvObjMutex.unlock(); + CEA parg; + parg.type = CEA::EventType_ProtocolRecieved; + parg.data.protocol = protocol; + NetEvent e = { this->parent, parg }; + this->recieveQueue.Push(e); } } + else + { + CEA parg; + parg.type = CEA::EventType_ProtocolFailedToRecieve; + parg.data.nothing = 0; + NetEvent e = { this->parent, parg }; + this->recieveQueue.Push(e); + } return errorCode; } - }; +struct NetworkClient::PrivateData +{ + SmartPointer dat; + +public: + PrivateData() + { this->dat = new NetDataContainer(); } + PrivateData(const PrivateData& obj) + { this->dat = obj.dat; } + ~PrivateData() + { this->dat = 0; } +}; +unsigned int NetDataContainer::currID = 0; + /************************************* NetworkClient *************************************/ NetworkClient::NetworkClient() -{ - privateData = new PrivateData(); - this->privateData->data->thread.SetPriority(Oyster::Thread::OYSTER_THREAD_PRIORITY_1); -} - -NetworkClient::NetworkClient(unsigned int socket) -{ - privateData = new PrivateData(socket); - this->privateData->data->thread.Create(this->privateData, true); - this->privateData->data->thread.SetPriority(Oyster::Thread::OYSTER_THREAD_PRIORITY_1); -} - -NetworkClient::NetworkClient(RecieverObject recvObj, NetworkProtocolCallbackType type) -{ - privateData = new PrivateData(); - this->privateData->data->callbackType = type; - this->privateData->data->recvObj = recvObj; -} - -NetworkClient::NetworkClient(RecieverObject recvObj, NetworkProtocolCallbackType type, unsigned int socket) -{ - privateData = new PrivateData(socket); - this->privateData->data->callbackType = type; - this->privateData->data->recvObj = recvObj; - this->privateData->data->thread.Create(this->privateData, true); - this->privateData->data->thread.SetPriority(Oyster::Thread::OYSTER_THREAD_PRIORITY_1); -} + : privateData(0) +{ } NetworkClient::NetworkClient(const NetworkClient& obj) { - this->privateData = new PrivateData(*obj.privateData); + if(obj.privateData) this->privateData = new PrivateData(*obj.privateData); + else this->privateData = 0; } NetworkClient& NetworkClient::operator =(const NetworkClient& obj) { delete privateData; - this->privateData = new PrivateData(*obj.privateData); + this->privateData = 0; + if(obj.privateData) this->privateData = new PrivateData(*obj.privateData); + return *this; } NetworkClient::~NetworkClient() { - if(privateData) + if(this->privateData) { - delete privateData; - privateData = NULL; + delete this->privateData; + this->privateData = NULL; } } +bool NetworkClient::operator ==(const NetworkClient& obj) +{ + return (this->privateData->dat->ID == obj.privateData->dat->ID); +} + +bool NetworkClient::operator ==(const int& ID) +{ + return this->privateData->dat->ID == ID; +} + +void NetworkClient::ProcessMessages() +{ + while (!this->privateData->dat->recieveQueue.IsEmpty()) + { + if(this->privateData->dat->owner) + { + this->privateData->dat->owner->ClientEventCallback(this->privateData->dat->recieveQueue.Pop()); + } + } +} + +bool NetworkClient::Connect(int socket) +{ + if(this->IsConnected()) return true; + if(this->privateData) return false; + if(!this->privateData) this->privateData = new PrivateData(); + + int result = this->privateData->dat->connection.Connect(socket, true); + + //Connect has succeeded + if(result == 0) return true; + + //Connect has failed + return false; +} + bool NetworkClient::Connect(unsigned short port, const char serverIP[]) { - privateData->data->connection.SetBlockingMode(true); - int result = this->privateData->data->connection.Connect(port, serverIP); + if(this->IsConnected()) return false; + if(this->privateData) return false; + if(!this->privateData) this->privateData = new PrivateData(); + + int result = this->privateData->dat->connection.Connect(port, serverIP, false); //Connect has succeeded - if(result == 0) - { - if(this->privateData->data->thread.IsCreated()) return false; - - this->privateData->data->thread.Create(this->privateData, true); - privateData->data->connection.SetBlockingMode(false); - return true; - } - - + if(result == 0) return true; //Connect has failed return false; @@ -247,46 +232,35 @@ bool NetworkClient::Connect(unsigned short port, const char serverIP[]) void NetworkClient::Disconnect() { - privateData->data->connection.Disconnect(); - privateData->data->thread.Terminate(); -} - -bool NetworkClient::IsConnected() -{ - return privateData->data->connection.IsConnected(); + privateData->dat->connection.Disconnect(); + privateData->dat->thread.Terminate(); } void NetworkClient::Send(CustomProtocolObject& protocol) { - this->privateData->Send(protocol.GetProtocol()); + this->privateData->dat->sendQueue.Push(*protocol.GetProtocol()); } void NetworkClient::Send(CustomNetProtocol* protocol) { - this->privateData->Send(protocol); + this->privateData->dat->sendQueue.Push(*protocol); } -void NetworkClient::SetRecieverObject(RecieverObject recvObj, NetworkProtocolCallbackType type) +void NetworkClient::SetOwner(NetworkSession* owner) { - if (type == NetworkProtocolCallbackType_Unknown) return; //It should probably still be set even if it is unknown. - - privateData->data->recvObjMutex.lock(); - privateData->data->recvObj = recvObj; - privateData->data->callbackType = type; - privateData->data->recvObjMutex.unlock(); + this->privateData->dat->owner = owner; } -bool NetworkClient::operator ==(const NetworkClient& obj) +bool NetworkClient::IsConnected() { - return (this->privateData->data->ID == obj.privateData->data->ID); -} - -bool NetworkClient::operator ==(const int& ID) -{ - return this->privateData->data->ID == ID; + if(!this->privateData) return false; + return privateData->dat->connection.IsConnected(); } int NetworkClient::GetID() const { - return this->privateData->data->ID; -} \ No newline at end of file + return this->privateData->dat->ID; +} + + + diff --git a/Code/Network/NetworkAPI/NetworkClient.h b/Code/Network/NetworkAPI/NetworkClient.h index 5a8dd5e5..39350537 100644 --- a/Code/Network/NetworkAPI/NetworkClient.h +++ b/Code/Network/NetworkAPI/NetworkClient.h @@ -1,59 +1,101 @@ #ifndef NETWORK_API_NETWORK_CLIENT_H #define NETWORK_API_NETWORK_CLIENT_H -///////////////////////////////////// -// Created by Pontus Fransson 2013 // -///////////////////////////////////// +////////////////////////////////////// +// Created by Pontus Fransson 2013 // +// Modified by Dennis Andersen 2014 // +////////////////////////////////////// -#ifdef CUSTOM_NET_PROTOCOL_EXPORT - #define NET_PROTOCOL_EXPORT __declspec(dllexport) -#else - #define NET_PROTOCOL_EXPORT __declspec(dllimport) -#endif - -#include "NetworkCallbackHelper.h" -//#include +#include "CustomNetProtocol.h" +#include "NetworkServerEventStruct.h" +#include "NetworkAPI_Preprocessor.h" namespace Oyster { namespace Network { + class NetworkSession; + extern "C" { - struct CustomProtocolObject; - class NET_PROTOCOL_EXPORT NetworkClient + class NET_API_EXPORT NetworkClient { + public: + struct ClientEventArgs + { + enum EventType + { + EventType_ProtocolFailedToRecieve, // No data + EventType_ProtocolFailedToSend, // Data in data.protocol + EventType_ProtocolRecieved, // Data in data.protocol + EventType_Disconnect, // No data + } type; + + union EventData + { + struct { Oyster::Network::CustomNetProtocol protocol; }; + void * nothing; + } data; + }; + typedef void(*ClientEventFunction)(NetEvent e); + public: NetworkClient(); - NetworkClient(unsigned int socket); - NetworkClient(RecieverObject recvObj, NetworkProtocolCallbackType type); - NetworkClient(RecieverObject recvObj, NetworkProtocolCallbackType type, unsigned int socket); NetworkClient(const NetworkClient& obj); - NetworkClient& operator =(const NetworkClient& obj); virtual ~NetworkClient(); - bool Connect(unsigned short port, const char serverIP[]); - void Disconnect(); - - bool IsConnected(); - - //Adds the protocol to the queue of protocols to be sent. - void Send(CustomProtocolObject& protocol); - void Send(CustomNetProtocol* protocol); - - void SetRecieverObject(RecieverObject recvObj, NetworkProtocolCallbackType type); - - //Compares the internal ID. bool operator ==(const NetworkClient& obj); bool operator ==(const int& ID); + /** + * + */ + void ProcessMessages(); + + /** + * + */ + bool Connect(int socket); + + /** + * + */ + bool Connect(unsigned short port, const char serverIP[]); + + /** + * + */ + void Disconnect(); + + /** + * + */ + void Send(CustomProtocolObject& protocol); + + /** + * + */ + void Send(CustomNetProtocol* protocol); + + /** + * + */ + void SetOwner(NetworkSession* owner); + + /** + * + */ + bool IsConnected(); + + /** + * + */ int GetID() const; private: struct PrivateData; PrivateData* privateData; - }; } } diff --git a/Code/Network/NetworkAPI/NetworkServer.cpp b/Code/Network/NetworkAPI/NetworkServer.cpp index ec4da231..de341f81 100644 --- a/Code/Network/NetworkAPI/NetworkServer.cpp +++ b/Code/Network/NetworkAPI/NetworkServer.cpp @@ -23,146 +23,50 @@ using namespace Oyster::Thread; struct NetworkServer::PrivateData : public IThreadObject { - PrivateData(); - ~PrivateData(); - - bool Init(INIT_DESC& initDesc); - bool Start(); - void Stop(); - void Shutdown(); - - void CheckForNewClient(); +public: + PrivateData() + : listener(0) + , mainSession(0) + , isInitiated(0) + , isReleased(0) + , isRunning(0) + { } + ~PrivateData() + { } bool DoWork(); - // +public: IListener* listener; - INIT_DESC initDesc; - bool started; - - //Postbox for new clients - PostBox postBox; - - //Server thread - OysterThread thread; + PostBox postBox; //Postbox for new clients + OysterThread thread; //Server thread + NetworkSession *mainSession; + Utility::Container::ThreadSafeQueue clientQueue; + bool isInitiated; + bool isReleased; + bool isRunning; + int port; }; -NetworkServer::PrivateData::PrivateData() -{ - listener = 0; - started = false; - //postBox = new PostBox; -} - -NetworkServer::PrivateData::~PrivateData() -{ - Shutdown(); -} - -bool NetworkServer::PrivateData::Init(INIT_DESC& initDesc) -{ - //Check if it's a valid port - if(initDesc.port == 0) - { - return false; - } - - if(!InitWinSock()) - return false; - - this->initDesc = initDesc; - - //Initiate listener - listener = new Listener(&postBox); - if(!((Listener*)listener)->Init(this->initDesc.port, false)) - { - return false; - } - - if(thread.Create(this, false) == OYSTER_THREAD_ERROR_FAILED) - { - return false; - } - - return true; -} - -bool NetworkServer::PrivateData::Start() -{ - //Start listener - if(!((Listener*)listener)->Start()) - { - return false; - } - - started = true; - - if(thread.Start() == OYSTER_THREAD_ERROR_FAILED) - { - return false; - } - - return true; -} - -void NetworkServer::PrivateData::Stop() -{ - if(listener) - { - ((Listener*)listener)->Stop(); - } - - started = false; - - thread.Stop(); -} - -void NetworkServer::PrivateData::Shutdown() -{ - if(listener) - { - listener->Shutdown(); - delete listener; - listener = NULL; - } - - started = false; - thread.Terminate(); - - ShutdownWinSock(); -} - -//Checks for new clients and sends them to the proc function. -void NetworkServer::PrivateData::CheckForNewClient() +bool NetworkServer::PrivateData::DoWork() { + /** Check for new clients **/ if(postBox.IsFull()) { int clientSocketNum = postBox.FetchMessage(); - //Safety check that is probably not needed. if(clientSocketNum == -1) { - return; + //Something went wrong somewhere... do we care? } - //Create client and Proc function if the pointer is not NULL - if(initDesc.callbackType == NetworkClientCallbackType_Function) - { - Oyster::Network::NetworkClient *client = new Oyster::Network::NetworkClient(clientSocketNum); - initDesc.recvObj.clientConnectFnc(client); - } - else if(initDesc.callbackType == NetworkClientCallbackType_Object) - { - Oyster::Network::NetworkClient *client = new Oyster::Network::NetworkClient(clientSocketNum); - initDesc.recvObj.clientConnectObject->NetworkCallback(client); - } + + Oyster::Network::NetworkClient client; + client.Connect(clientSocketNum); + if(this->mainSession) + this->clientQueue.Push(client); } -} - -bool NetworkServer::PrivateData::DoWork() -{ - CheckForNewClient(); return true; } @@ -173,48 +77,142 @@ bool NetworkServer::PrivateData::DoWork() NetworkServer::NetworkServer() { - privateData = new PrivateData(); + this->privateData = new PrivateData(); +} +NetworkServer::NetworkServer(const NetworkServer& obj) +{ + delete this->privateData; + this->privateData = new PrivateData(*obj.privateData); +} +const NetworkServer& NetworkServer::operator=(const NetworkServer& obj) +{ + delete this->privateData; + this->privateData = new PrivateData(*obj.privateData); + return *this; } - NetworkServer::~NetworkServer() { - if(privateData) + if(this->privateData) { - delete privateData; + delete this->privateData; + this->privateData = 0; } } -bool NetworkServer::Init(INIT_DESC& initDesc) +NetworkServer::ServerReturnCode NetworkServer::Init(const int& port, NetworkSession const* mainSession) { - if(!privateData->Init(initDesc)) + //Check if it's a valid port + if(port == 0 || port == -1) { - return false; + return NetworkServer::ServerReturnCode_Error; + } + else if(this->privateData->port != 0 && this->privateData->port != -1) + { + return NetworkServer::ServerReturnCode_Error; + } + if(!InitWinSock()) + { + return NetworkServer::ServerReturnCode_Error; } - return true; + //Initiate listener + this->privateData->listener = new Listener(&this->privateData->postBox); + if(!((Listener*)this->privateData->listener)->Init(port, false)) + { + return NetworkServer::ServerReturnCode_Error; + } + + if(this->privateData->thread.Create(this->privateData, false) == OYSTER_THREAD_ERROR_FAILED) + { + return NetworkServer::ServerReturnCode_Error; + } + + this->privateData->isInitiated = true; + this->privateData->isReleased = false; + this->privateData->mainSession = 0; + return NetworkServer::ServerReturnCode_Sucess; } -bool NetworkServer::Start() +NetworkServer::ServerReturnCode NetworkServer::Start() { - if(!privateData->Start()) + //Start listener + if(!((Listener*)this->privateData->listener)->Start()) { - return false; + return NetworkServer::ServerReturnCode_Error; } - return true; + if(this->privateData->thread.Start() == OYSTER_THREAD_ERROR_FAILED) + { + return NetworkServer::ServerReturnCode_Error; + } + + this->privateData->isRunning = true; + return NetworkServer::ServerReturnCode_Sucess; } void NetworkServer::Stop() { - privateData->Stop(); + if(this->privateData->listener) + { + ((Listener*)this->privateData->listener)->Stop(); + } + + this->privateData->thread.Stop(); + + this->privateData->isRunning = false; } void NetworkServer::Shutdown() { - privateData->Shutdown(); + if(this->privateData->listener) + { + this->privateData->listener->Shutdown(); + delete this->privateData->listener; + this->privateData->listener = NULL; + } + + this->privateData->thread.Terminate(); + + ShutdownWinSock(); + + this->privateData->isRunning = false; + this->privateData->mainSession = 0; + this->privateData->isReleased = true; +} + +void NetworkServer::ProcessConnectedClients() +{ + while(!this->privateData->clientQueue.IsEmpty()) + { + if(this->privateData->mainSession) this->privateData->mainSession->Attach(this->privateData->clientQueue.Pop()); + } +} + +void NetworkServer::SetSession(NetworkSession const* mainSession) +{ + this->privateData->mainSession = const_cast(mainSession); +} + +NetworkSession const* NetworkServer::GetMainSession() +{ + return this->privateData->mainSession; +} + +NetworkSession const* NetworkServer::ReleaseMainSessionSession() +{ + NetworkSession const * temp; + temp = this->privateData->mainSession; + this->privateData->mainSession = 0; + return temp; } bool NetworkServer::IsStarted() const { - return privateData->started; -} \ No newline at end of file + return this->privateData->isRunning; +} + + + + + + diff --git a/Code/Network/NetworkAPI/NetworkServer.h b/Code/Network/NetworkAPI/NetworkServer.h index 8edc59ff..c3385f86 100644 --- a/Code/Network/NetworkAPI/NetworkServer.h +++ b/Code/Network/NetworkAPI/NetworkServer.h @@ -1,18 +1,15 @@ #ifndef NETWORK_API_NETWORK_SERVER_H #define NETWORK_API_NETWORK_SERVER_H -///////////////////////////////////// -// Created by Pontus Fransson 2013 // -///////////////////////////////////// +////////////////////////////////////// +// Created by Pontus Fransson 2013 // +// Modified by Dennis Andersen 2014 // +////////////////////////////////////// -#ifdef CUSTOM_NET_PROTOCOL_EXPORT - #define NET_PROTOCOL_EXPORT __declspec(dllexport) -#else - #define NET_PROTOCOL_EXPORT __declspec(dllimport) -#endif +#include "NetworkAPI_Preprocessor.h" #include "NetworkClient.h" -#include "NetworkCallbackHelper.h" +#include "NetworkSession.h" #include namespace Oyster @@ -21,27 +18,68 @@ namespace Oyster { extern "C" { - class NET_PROTOCOL_EXPORT NetworkServer + class NET_API_EXPORT NetworkServer { public: - struct INIT_DESC + enum ServerReturnCode { - unsigned short port; //Port the server should be accepting clients on. - - NetworkClientCallbackType callbackType; //The recieverObject type. Function or object. - RecieverObject recvObj; //The functions that is called when a new client has connected. + ServerReturnCode_Error, + ServerReturnCode_Sucess }; + public: NetworkServer(); + NetworkServer(const NetworkServer&); + const NetworkServer& operator=(const NetworkServer&); virtual ~NetworkServer(); - bool Init(INIT_DESC& initDesc); - bool Start(); + + /** Creates a server that clients can connect to + * @param port The port the server will be listening for clients. + * @param mainSession The main session the server will send connected clients to. + * @return The server returncode + */ + ServerReturnCode Init(const int& port, NetworkSession const* mainSession); + + /** Starts the server allowing clients to connect + * @return The server returncode + */ + ServerReturnCode Start(); + + /** + * + */ void Stop(); + + /** Shutdown the server and return all resources. + */ void Shutdown(); + /** Parses asynchronous connected clients. + */ + void ProcessConnectedClients(); + + /** Set the main session connected clients will enter when connected to server. + * @param mainSession The session to connect as main server session. + */ + void SetSession(NetworkSession const* mainSession); + + /** Get the main session connected with the server + * @return Returns the main session + */ + NetworkSession const* GetMainSession(); + + /** Sets the main session to NULL and returns it + * @return Returns the main session + */ + NetworkSession const* ReleaseMainSessionSession(); + + /** + * + */ bool IsStarted() const; + private: struct PrivateData; PrivateData* privateData; diff --git a/Code/Network/NetworkAPI/NetworkServerEventStruct.h b/Code/Network/NetworkAPI/NetworkServerEventStruct.h new file mode 100644 index 00000000..a48d97de --- /dev/null +++ b/Code/Network/NetworkAPI/NetworkServerEventStruct.h @@ -0,0 +1,17 @@ +#ifndef NETWORK_API_SERVEREVENT_H +#define NETWORK_API_SERVEREVENT_H + +namespace Oyster +{ + namespace Network + { + template + struct NetEvent + { + Sender sender; + Args args; + }; + } +} + +#endif // !SERVERDEPENDENCIES_SERVEREVENT_H diff --git a/Code/Network/NetworkAPI/NetworkSession.cpp b/Code/Network/NetworkAPI/NetworkSession.cpp new file mode 100644 index 00000000..e026413e --- /dev/null +++ b/Code/Network/NetworkAPI/NetworkSession.cpp @@ -0,0 +1,184 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// + +#include "NetworkSession.h" +#include "..\..\Misc\Utilities.h" +#include "..\..\Misc\DynamicArray.h" +#include "..\..\Misc\GID.h" +#include "CustomNetProtocol.h" +#include +#include + + +using namespace Oyster::Network; + + +struct NetworkSession::PrivateSessionData +{ + Utility::DynamicMemory::DynamicArray clients; + NetworkClient::ClientEventFunction messageCallback; + std::mutex clientListLock; + NetworkSession* owner; //Where clients end up when session is closed. + int clientCount; + int id; + NetworkSession::PrivateSessionData() + : clientCount(0) + , owner(0) + , id(GID()) + {} +}; + + + +NetworkSession::NetworkSession() + : data(0) +{} +NetworkSession::NetworkSession(const NetworkSession& orig) +{ + this->data->clients = orig.data->clients; + this->data->owner = orig.data->owner; + this->data->clientCount = orig.data->clientCount; + this->data->id = orig.data->id; + this->data->messageCallback = orig.data->messageCallback; +} + +const NetworkSession& NetworkSession::operator=(const NetworkSession& orig) +{ + this->data->clients = orig.data->clients; + this->data->owner = orig.data->owner; + this->data->clientCount = orig.data->clientCount; + this->data->id = orig.data->id; + this->data->messageCallback = orig.data->messageCallback; + + return *this; +} + +NetworkSession::~NetworkSession() +{ + this->data->clients.Clear(); + this->data->clientCount = 0; + this->data->messageCallback = 0; +} + +void NetworkSession::ProcessClients() +{ + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + this->data->clients[i].ProcessMessages(); + } +} + +bool NetworkSession::Attach(NetworkClient client) +{ + this->data->clientListLock.lock(); + + int k = -1; + for (unsigned int i = 0; (k == -1) && i < this->data->clients.Size(); i++) + { + if(!this->data->clients[i].IsConnected()) //TODO: Dont check connection status, check more general status.. + k = i; + } + + if(k == -1) + { + this->data->clients.Push(client); + } + else + { + this->data->clients[k] = client; + } + + this->data->clientCount++; + + this->data->clientListLock.unlock(); + + return true; +} + +NetworkClient NetworkSession::Detach(const NetworkClient& client) +{ + NetworkClient val; + + this->data->clientListLock.lock(); + + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + if(this->data->clients[0].GetID() == client.GetID()) + { + val = this->data->clients[i]; + this->data->clients[i] = NetworkClient(); + this->data->clientCount--; + } + } + + this->data->clientListLock.unlock(); + + return val; +} + +NetworkClient NetworkSession::Detach(short ID) +{ + NetworkClient val; + + this->data->clientListLock.lock(); + + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + if(this->data->clients[0].GetID() == ID) + { + val = this->data->clients[i]; + this->data->clients[i] = NetworkClient(); + this->data->clientCount--; + } + } + + this->data->clientListLock.unlock(); + + return val; +} + +bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol) +{ + bool returnValue = false; + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + this->data->clients[i].Send(&protocol); + returnValue = true; + } + + return returnValue; +} + +bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol, int ID) +{ + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + if(this->data->clients[i].GetID() == ID) + { + this->data->clients[i].Send(&protocol); + return true; + } + } + return false; +} + +void NetworkSession::CloseSession(bool dissconnectClients) +{ + this->data->clientListLock.lock(); + + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + if(dissconnectClients) this->data->clients[i].Disconnect(); + else if(this->data->owner) this->data->owner->Attach(this->data->clients[i]); + else this->data->clients[i].Disconnect(); + } + + this->data->clients.Clear(); + this->data->clientCount = 0; + + this->data->clientListLock.unlock(); +} + + + diff --git a/Code/Network/NetworkAPI/NetworkSession.h b/Code/Network/NetworkAPI/NetworkSession.h new file mode 100644 index 00000000..7c50afd0 --- /dev/null +++ b/Code/Network/NetworkAPI/NetworkSession.h @@ -0,0 +1,83 @@ +///////////////////////////////////////////////////////////////////// +// Created by [Dennis Andersen] [2013] +///////////////////////////////////////////////////////////////////// +#ifndef NETWORK_API_NETWORK_SESSION_H +#define NETWORK_API_NETWORK_SESSION_H + +//warning C4150: deletion of pointer to incomplete type, no destructor called +#pragma warning(disable : 4150) + +#include "NetworkAPI_Preprocessor.h" +#include "NetworkServerEventStruct.h" +#include "NetworkClient.h" + +namespace Oyster +{ + namespace Network + { + class NET_API_EXPORT NetworkSession + { + public: + NetworkSession(); + NetworkSession(const NetworkSession& orig); + const NetworkSession& operator=(const NetworkSession& orig); + virtual~NetworkSession(); + + /** Parse session events such as protocols recieved etc. + */ + void ProcessClients(); + + /** + * + */ + bool Attach(NetworkClient client); + + /** + * + */ + NetworkClient Detach(const NetworkClient& client); + + /** + * + */ + NetworkClient Detach(short ID); + + /** Send a message to all clients in this session + * @param message The message + */ + bool Send(Oyster::Network::CustomNetProtocol& message); + + /** Send a message to a specific client in this session + * @param message The message + */ + bool Send(Oyster::Network::CustomNetProtocol& protocol, int ID); + + /** + * + */ + void CloseSession( bool dissconnectClients = false ); + + /** + * Set the owner that clients will be returned to. + * @param owner If owner is NULL, clients will be disconnected when session is over. + */ + void SetOwner(NetworkSession* owner); + + /** + * + */ + virtual void ClientEventCallback(NetEvent e) = 0; + + /** + * + */ + virtual void ClientConnectedEvent(NetEvent e) = 0; + + private: + struct PrivateSessionData; + PrivateSessionData* data; + }; + } +}//End namespace DanBias + +#endif // !DANBIASSERVER_NETWORK_SESSION_H diff --git a/Code/Network/NetworkAPI/Translator.cpp b/Code/Network/NetworkAPI/Translator.cpp index 0829913e..b1196e88 100644 --- a/Code/Network/NetworkAPI/Translator.cpp +++ b/Code/Network/NetworkAPI/Translator.cpp @@ -5,6 +5,7 @@ #include "CustomNetProtocol.h" +#include "../../Misc/Utilities.h" #include "../NetworkDependencies/Messages/MessageHeader.h" #include "../NetworkDependencies/OysterByte.h" diff --git a/Code/Network/NetworkAPI/Translator.h b/Code/Network/NetworkAPI/Translator.h index f413492e..7913c16a 100644 --- a/Code/Network/NetworkAPI/Translator.h +++ b/Code/Network/NetworkAPI/Translator.h @@ -27,13 +27,7 @@ "100F" */ -#ifdef CUSTOM_NET_PROTOCOL_EXPORT - #define NET_PROTOCOL_EXPORT __declspec(dllexport) -#else - #define NET_PROTOCOL_EXPORT __declspec(dllimport) -#endif - -#include "../../Misc/Utilities.h" +#include "NetworkAPI_Preprocessor.h" namespace Oyster { @@ -43,7 +37,8 @@ namespace Oyster { class OysterByte; class CustomNetProtocol; - class NET_PROTOCOL_EXPORT Translator + + class NET_API_EXPORT Translator { public: Translator (); diff --git a/Code/Network/NetworkDependencies/Connection.cpp b/Code/Network/NetworkDependencies/Connection.cpp index 5ff4eeda..c79d5fe6 100644 --- a/Code/Network/NetworkDependencies/Connection.cpp +++ b/Code/Network/NetworkDependencies/Connection.cpp @@ -40,7 +40,18 @@ Connection::~Connection() CloseSocket( this->socket ); } -int Connection::Connect(unsigned short port , const char serverName[]) +int Connection::Connect(int socket, bool blocking) +{ + this->socket = socket; + this->stillSending = true; + this->closed = false; + + SetBlockingMode(blocking); + //connection succesfull! + return 0; +} + +int Connection::Connect(unsigned short port , const char serverName[], bool blocking) { struct hostent *hostEnt; if((hostEnt = gethostbyname(serverName)) == NULL) @@ -61,6 +72,8 @@ int Connection::Connect(unsigned short port , const char serverName[]) closed = false; stillSending = true; + SetBlockingMode(blocking); + //connection succesfull! return 0; } diff --git a/Code/Network/NetworkDependencies/Connection.h b/Code/Network/NetworkDependencies/Connection.h index ae76a3f7..d2e815d3 100644 --- a/Code/Network/NetworkDependencies/Connection.h +++ b/Code/Network/NetworkDependencies/Connection.h @@ -27,7 +27,8 @@ namespace Oyster virtual int Recieve( OysterByte &bytes ); virtual int Disconnect(); - virtual int Connect( unsigned short port , const char serverName[] ); + virtual int Connect(int socket, bool blocking = false); + virtual int Connect(unsigned short port , const char serverName[], bool blocking = false); virtual int Listen(); diff --git a/Code/Network/NetworkDependencies/Listener.cpp b/Code/Network/NetworkDependencies/Listener.cpp index deb0d992..360ccebb 100644 --- a/Code/Network/NetworkDependencies/Listener.cpp +++ b/Code/Network/NetworkDependencies/Listener.cpp @@ -120,7 +120,7 @@ void Listener::StopListen() this->isListening = false; Connection c; c.InitiateClient(); - c.Connect(this->port, "127.0.0.1"); + c.Connect(this->port, "127.0.0.1", false); } } bool Listener::DoWork() From 5bf92757a510c789a6b03ea50e6529e4d85afeed Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Tue, 28 Jan 2014 09:01:35 +0100 Subject: [PATCH 45/76] State struct fixed since last inactivation. --- Code/GamePhysics/PhysicsStructs-Impl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/GamePhysics/PhysicsStructs-Impl.h b/Code/GamePhysics/PhysicsStructs-Impl.h index 46de90cc..b6168900 100644 --- a/Code/GamePhysics/PhysicsStructs-Impl.h +++ b/Code/GamePhysics/PhysicsStructs-Impl.h @@ -259,7 +259,7 @@ namespace Oyster this->isSpatiallyAltered = this->isDisturbed = true; } - inline void CustomBodyState::SetRotation( const ::Oyster::Math::Float4x4 &rotation ) + /*inline void CustomBodyState::SetRotation( const ::Oyster::Math::Float4x4 &rotation ) { this->SetRotation( ::Oyster::Math3D::AngularAxis(rotation) ); } @@ -268,7 +268,7 @@ namespace Oyster { this->SetRotation( ::Oyster::Math3D::ExtractAngularAxis(orientation) ); this->SetCenterPosition( orientation.v[3] ); - } + }*/ inline void CustomBodyState::SetLinearMomentum( const ::Oyster::Math::Float4 &g ) { From 6be72da03aeaf8ff54c3fc6a68f9b88e13b0217a Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Tue, 28 Jan 2014 09:23:58 +0100 Subject: [PATCH 46/76] OysterPhysics3D updated * changed Float4 in header API to Float3 * removed all Set...( Float4x4 ) methods --- Code/OysterPhysics3D/Inertia.cpp | 16 +++---- Code/OysterPhysics3D/Inertia.h | 8 ++-- Code/OysterPhysics3D/RigidBody.cpp | 62 ++++++++++++------------- Code/OysterPhysics3D/RigidBody.h | 46 +++++++++--------- Code/OysterPhysics3D/RigidBody_Inline.h | 29 ++++++------ 5 files changed, 80 insertions(+), 81 deletions(-) diff --git a/Code/OysterPhysics3D/Inertia.cpp b/Code/OysterPhysics3D/Inertia.cpp index 69df0e01..2e0e436b 100644 --- a/Code/OysterPhysics3D/Inertia.cpp +++ b/Code/OysterPhysics3D/Inertia.cpp @@ -26,26 +26,26 @@ MomentOfInertia & MomentOfInertia::operator = ( const MomentOfInertia &i ) return *this; } -Float4 MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, const Float4 &h ) const +Float3 MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, const Float3 &h ) const { - return this->CalculateAngularVelocity( externR, h, Float4() ); + return this->CalculateAngularVelocity( externR, h, Float3() ); } -Float4 & MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, const Float4 &h, Float4 &targetMem ) const +Float3 & MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, const Float3 &h, Float3 &targetMem ) const { // w = (R * I_R) * I_M^-1 * (R * I_R)^-1 * h Float4x4 rotation = RotationMatrix( externR ) * RotationMatrix( this->rotation ); - Float4 w = rotation.GetInverse() * h; + Float4 w = rotation.GetInverse() * Float4( h, 0.0f ); return targetMem = rotation * w.PiecewiseMultiplicationAdd( Float4(1.0f / this->magnitude.x, 1.0f / this->magnitude.y, 1.0f / this->magnitude.z, 0.0f) ); } -Float4 MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float4 &w ) const +Float3 MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float3 &w ) const { - return this->CalculateAngularMomentum( externR, w, Float4() ); + return this->CalculateAngularMomentum( externR, w, Float3() ); } -Float4 & MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float4 &w, Float4 &targetMem ) const +Float3 & MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float3 &w, Float3 &targetMem ) const { // h = (R * I_R) * I_M * (R * I_R)^-1 * w Float4x4 rotation = RotationMatrix( externR ) * RotationMatrix( this->rotation ); - Float4 h = rotation.GetInverse() * w; + Float4 h = rotation.GetInverse() * Float4( w, 0.0f ); return targetMem = rotation * h.PiecewiseMultiplicationAdd( Float4(this->magnitude.x, this->magnitude.y, this->magnitude.z, 0.0f) ); } \ No newline at end of file diff --git a/Code/OysterPhysics3D/Inertia.h b/Code/OysterPhysics3D/Inertia.h index c7ffc49f..502074a6 100644 --- a/Code/OysterPhysics3D/Inertia.h +++ b/Code/OysterPhysics3D/Inertia.h @@ -19,11 +19,11 @@ namespace Oyster { namespace Physics3D MomentOfInertia & operator = ( const MomentOfInertia &i ); - ::Oyster::Math::Float4 CalculateAngularVelocity( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularMomentum ) const; - ::Oyster::Math::Float4 & CalculateAngularVelocity( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularMomentum, ::Oyster::Math::Float4 &targetMem ) const; + ::Oyster::Math::Float3 CalculateAngularVelocity( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float3 &angularMomentum ) const; + ::Oyster::Math::Float3 & CalculateAngularVelocity( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float3 &angularMomentum, ::Oyster::Math::Float3 &targetMem ) const; - ::Oyster::Math::Float4 CalculateAngularMomentum( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularVelocity ) const; - ::Oyster::Math::Float4 & CalculateAngularMomentum( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float4 &angularVelocity, ::Oyster::Math::Float4 &targetMem ) const; + ::Oyster::Math::Float3 CalculateAngularMomentum( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float3 &angularVelocity ) const; + ::Oyster::Math::Float3 & CalculateAngularMomentum( const ::Oyster::Math::Quaternion &externR, const ::Oyster::Math::Float3 &angularVelocity, ::Oyster::Math::Float3 &targetMem ) const; static ::Oyster::Math::Float CalculateSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ); static ::Oyster::Math::Float CalculateHollowSphere( const ::Oyster::Math::Float mass, const ::Oyster::Math::Float radius ); diff --git a/Code/OysterPhysics3D/RigidBody.cpp b/Code/OysterPhysics3D/RigidBody.cpp index d4da0d00..8e5f5f2b 100644 --- a/Code/OysterPhysics3D/RigidBody.cpp +++ b/Code/OysterPhysics3D/RigidBody.cpp @@ -72,7 +72,7 @@ void RigidBody::Update_LeapFrog( Float updateFrameLength ) this->impulse_Angular = Float4::null; } -void RigidBody::Predict_LeapFrog( Float4 &outDeltaPos, Float4 &outDeltaAxis, const Float4 &actingLinearImpulse, const Float4 &actingAngularImpulse, Float deltaTime ) +void RigidBody::Predict_LeapFrog( Float3 &outDeltaPos, Float3 &outDeltaAxis, const Float3 &actingLinearImpulse, const Float3 &actingAngularImpulse, Float deltaTime ) { // updating the linear // ds = dt * Formula::LinearVelocity( m, avg_G ) = dt * avg_G / m = (dt / m) * avg_G @@ -87,17 +87,17 @@ void RigidBody::Predict_LeapFrog( Float4 &outDeltaPos, Float4 &outDeltaAxis, con outDeltaAxis = this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, AverageWithDelta(this->momentum_Angular, this->impulse_Angular) ); } -void RigidBody::Move( const Float4 &deltaPos, const Float4 &deltaAxis ) +void RigidBody::Move( const Float3 &deltaPos, const Float3 &deltaAxis ) { this->centerPos += deltaPos; this->axis += deltaAxis; this->rotation = Rotation( this->axis ); } -void RigidBody::ApplyImpulse( const Float4 &worldJ, const Float4 &atWorldPos ) +void RigidBody::ApplyImpulse( const Float3 &worldJ, const Float3 &atWorldPos ) { // by Dan Andersson - Float4 worldOffset = atWorldPos - this->centerPos; - if( worldOffset != Float4::null ) + Float3 worldOffset = atWorldPos - this->centerPos; + if( worldOffset != Float3::null ) { this->impulse_Linear += VectorProjection( worldJ, atWorldPos ); this->impulse_Angular += Formula::ImpulseTorque( worldJ, atWorldPos ); @@ -118,7 +118,7 @@ Float RigidBody::GetMass() const return this->mass; } -const Quaternion & RigidBody::GetRotation() const +const Quaternion & RigidBody::GetRotationQuaternion() const { // by Dan Andersson return this->rotation; } @@ -138,24 +138,24 @@ Float4x4 RigidBody::GetView() const return ViewMatrix( this->rotation, this->centerPos ); } -Float4 RigidBody::GetVelocity_Linear() const +Float3 RigidBody::GetVelocity_Linear() const { // by Dan Andersson return Formula::LinearVelocity( this->mass, this->momentum_Linear ); } -Float4 RigidBody::GetVelocity_Angular() const +Float3 RigidBody::GetVelocity_Angular() const { // by Dan Andersson return this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, this->momentum_Angular ); } -Float4 RigidBody::GetLinearMomentum( const Float4 &atWorldPos ) const +Float3 RigidBody::GetLinearMomentum( const Float3 &atWorldPos ) const { // by Dan Andersson return this->momentum_Linear + Formula::TangentialLinearMomentum( this->momentum_Angular, atWorldPos - this->centerPos ); } void RigidBody::SetMomentOfInertia_KeepVelocity( const MomentOfInertia &localTensorI ) { // by Dan Andersson - Float4 w = this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, this->momentum_Angular ); + Float3 w = this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, this->momentum_Angular ); this->momentOfInertiaTensor = localTensorI; this->momentum_Angular = this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, w ); } @@ -169,7 +169,7 @@ void RigidBody::SetMass_KeepVelocity( const Float &m ) { // by Dan Andersson if( m != 0.0f ) { // insanity check! Mass must be invertable - Float4 v = Formula::LinearVelocity( this->mass, this->momentum_Linear ); + Float3 v = Formula::LinearVelocity( this->mass, this->momentum_Linear ); this->mass = m; this->momentum_Linear = Formula::LinearMomentum( this->mass, v ); } @@ -183,46 +183,46 @@ void RigidBody::SetMass_KeepMomentum( const Float &m ) } } -void RigidBody::SetOrientation( const Float4x4 &o ) -{ // by Dan Andersson - this->axis = ExtractAngularAxis( o ); - this->rotation = Rotation( this->axis ); - this->centerPos = o.v[3].xyz; -} +//void RigidBody::SetOrientation( const Float4x4 &o ) +//{ // by Dan Andersson +// this->axis = ExtractAngularAxis( o ); +// this->rotation = Rotation( this->axis ); +// this->centerPos = o.v[3].xyz; +//} +// +//void RigidBody::SetRotation( const Float4x4 &r ) +//{ // by Dan Andersson +// this->axis = ExtractAngularAxis( r ); +// this->rotation = Rotation( this->axis ); +//} -void RigidBody::SetRotation( const Float4x4 &r ) +void RigidBody::SetMomentum_Linear( const Float3 &worldG, const Float3 &atWorldPos ) { // by Dan Andersson - this->axis = ExtractAngularAxis( r ); - this->rotation = Rotation( this->axis ); -} - -void RigidBody::SetMomentum_Linear( const Float4 &worldG, const Float4 &atWorldPos ) -{ // by Dan Andersson - Float4 worldOffset = atWorldPos - this->centerPos; + Float3 worldOffset = atWorldPos - this->centerPos; this->momentum_Linear = VectorProjection( worldG, worldOffset ); this->momentum_Angular = Formula::AngularMomentum( worldG, worldOffset ); } -void RigidBody::SetVelocity_Linear( const Float4 &worldV ) +void RigidBody::SetVelocity_Linear( const Float3 &worldV ) { // by Dan Andersson this->momentum_Linear = Formula::LinearMomentum( this->mass, worldV ); } -void RigidBody::SetVelocity_Linear( const Float4 &worldV, const Float4 &atWorldPos ) +void RigidBody::SetVelocity_Linear( const Float3 &worldV, const Float3 &atWorldPos ) { // by Dan Andersson - Float4 worldOffset = atWorldPos - this->centerPos; + Float3 worldOffset = atWorldPos - this->centerPos; this->momentum_Linear = Formula::LinearMomentum( this->mass, VectorProjection(worldV, worldOffset) ); this->momentum_Angular = this->momentOfInertiaTensor.CalculateAngularMomentum( this->rotation, Formula::AngularVelocity(worldV, worldOffset) ); } -void RigidBody::SetVelocity_Angular( const Float4 &worldW ) +void RigidBody::SetVelocity_Angular( const Float3 &worldW ) { // by Dan Andersson this->momentum_Angular = this->momentOfInertiaTensor.CalculateAngularMomentum( this->rotation, worldW ); } -void RigidBody::SetImpulse_Linear( const Float4 &worldJ, const Float4 &atWorldPos ) +void RigidBody::SetImpulse_Linear( const Float3 &worldJ, const Float3 &atWorldPos ) { // by Dan Andersson - Float4 worldOffset = atWorldPos - this->centerPos; + Float3 worldOffset = atWorldPos - this->centerPos; this->impulse_Linear = VectorProjection( worldJ, worldOffset ); this->impulse_Angular = Formula::ImpulseTorque( worldJ, worldOffset ); } \ No newline at end of file diff --git a/Code/OysterPhysics3D/RigidBody.h b/Code/OysterPhysics3D/RigidBody.h index ad619180..c666662a 100644 --- a/Code/OysterPhysics3D/RigidBody.h +++ b/Code/OysterPhysics3D/RigidBody.h @@ -15,7 +15,7 @@ namespace Oyster { namespace Physics3D struct RigidBody { //! A struct of a simple rigid body. public: - ::Oyster::Math::Float4 centerPos, //!< Location of the body's center in the world. + ::Oyster::Math::Float3 centerPos, //!< Location of the body's center in the world. axis, //!< Euler rotationAxis of the body. boundingReach, //!< momentum_Linear, //!< The linear momentum G (kg*m/s). @@ -32,29 +32,29 @@ namespace Oyster { namespace Physics3D RigidBody & operator = ( const RigidBody &body ); void Update_LeapFrog( ::Oyster::Math::Float updateFrameLength ); - void Predict_LeapFrog( ::Oyster::Math::Float4 &outDeltaPos, ::Oyster::Math::Float4 &outDeltaAxis, const ::Oyster::Math::Float4 &actingLinearImpulse, const ::Oyster::Math::Float4 &actingAngularImpulse, ::Oyster::Math::Float deltaTime ); + void Predict_LeapFrog( ::Oyster::Math::Float3 &outDeltaPos, ::Oyster::Math::Float3 &outDeltaAxis, const ::Oyster::Math::Float3 &actingLinearImpulse, const ::Oyster::Math::Float3 &actingAngularImpulse, ::Oyster::Math::Float deltaTime ); - void Move( const ::Oyster::Math::Float4 &deltaPos, const ::Oyster::Math::Float4 &deltaAxis ); - void ApplyImpulse( const ::Oyster::Math::Float4 &worldJ, const ::Oyster::Math::Float4 &atWorldPos ); - void ApplyImpulse_Linear( const ::Oyster::Math::Float4 &worldJ ); - void ApplyImpulse_Angular( const ::Oyster::Math::Float4 &worldJ ); - void ApplyForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength ); - void ApplyForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float4 &atWorldPos ); + void Move( const ::Oyster::Math::Float3 &deltaPos, const ::Oyster::Math::Float3 &deltaAxis ); + void ApplyImpulse( const ::Oyster::Math::Float3 &worldJ, const ::Oyster::Math::Float3 &atWorldPos ); + void ApplyImpulse_Linear( const ::Oyster::Math::Float3 &worldJ ); + void ApplyImpulse_Angular( const ::Oyster::Math::Float3 &worldJ ); + void ApplyForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength ); + void ApplyForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float3 &atWorldPos ); // GET METHODS //////////////////////////////// const ::Oyster::Physics3D::MomentOfInertia & GetMomentOfInertia() const; ::Oyster::Math::Float GetMass() const; - const ::Oyster::Math::Quaternion & GetRotation() const; + const ::Oyster::Math::Quaternion & GetRotationQuaternion() const; ::Oyster::Math::Float4x4 GetRotationMatrix() const; ::Oyster::Math::Float4x4 GetOrientation() const; ::Oyster::Math::Float4x4 GetView() const; ::Oyster::Math::Float4x4 GetToWorldMatrix() const; ::Oyster::Math::Float4x4 GetToLocalMatrix() const; - ::Oyster::Math::Float4 GetSize() const; - ::Oyster::Math::Float4 GetVelocity_Linear() const; - ::Oyster::Math::Float4 GetVelocity_Angular() const; - ::Oyster::Math::Float4 GetLinearMomentum( const ::Oyster::Math::Float4 &atWorldPos ) const; + ::Oyster::Math::Float3 GetSize() const; + ::Oyster::Math::Float3 GetVelocity_Linear() const; + ::Oyster::Math::Float3 GetVelocity_Angular() const; + ::Oyster::Math::Float3 GetLinearMomentum( const ::Oyster::Math::Float3 &atWorldPos ) const; // SET METHODS //////////////////////////////// @@ -63,19 +63,19 @@ namespace Oyster { namespace Physics3D void SetMass_KeepVelocity( const ::Oyster::Math::Float &m ); void SetMass_KeepMomentum( const ::Oyster::Math::Float &m ); - void SetOrientation( const ::Oyster::Math::Float4x4 &o ); - void SetRotation( const ::Oyster::Math::Float4x4 &r ); - void SetSize( const ::Oyster::Math::Float4 &widthHeight ); + //void SetOrientation( const ::Oyster::Math::Float4x4 &o ); + //void SetRotation( const ::Oyster::Math::Float4x4 &r ); + void SetSize( const ::Oyster::Math::Float3 &widthHeight ); - void SetMomentum_Linear( const ::Oyster::Math::Float4 &worldG, const ::Oyster::Math::Float4 &atWorldPos ); + void SetMomentum_Linear( const ::Oyster::Math::Float3 &worldG, const ::Oyster::Math::Float3 &atWorldPos ); - void SetVelocity_Linear( const ::Oyster::Math::Float4 &worldV ); - void SetVelocity_Linear( const ::Oyster::Math::Float4 &worldV, const ::Oyster::Math::Float4 &atWorldPos ); - void SetVelocity_Angular( const ::Oyster::Math::Float4 &worldW ); + void SetVelocity_Linear( const ::Oyster::Math::Float3 &worldV ); + void SetVelocity_Linear( const ::Oyster::Math::Float3 &worldV, const ::Oyster::Math::Float3 &atWorldPos ); + void SetVelocity_Angular( const ::Oyster::Math::Float3 &worldW ); - void SetImpulse_Linear( const ::Oyster::Math::Float4 &worldJ, const ::Oyster::Math::Float4 &atWorldPos ); - void SetForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength ); - void SetForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float4 &atWorldPos ); + void SetImpulse_Linear( const ::Oyster::Math::Float3 &worldJ, const ::Oyster::Math::Float3 &atWorldPos ); + //void SetForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength ); + //void SetForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float3 &atWorldPos ); private: ::Oyster::Math::Float mass; //!< m (kg) diff --git a/Code/OysterPhysics3D/RigidBody_Inline.h b/Code/OysterPhysics3D/RigidBody_Inline.h index 980442dd..795ec5e4 100644 --- a/Code/OysterPhysics3D/RigidBody_Inline.h +++ b/Code/OysterPhysics3D/RigidBody_Inline.h @@ -10,22 +10,22 @@ namespace Oyster { namespace Physics3D { - inline void RigidBody::ApplyImpulse_Linear( const ::Oyster::Math::Float4 &worldJ ) + inline void RigidBody::ApplyImpulse_Linear( const ::Oyster::Math::Float3 &worldJ ) { this->impulse_Linear += worldJ; } - inline void RigidBody::ApplyImpulse_Angular( const ::Oyster::Math::Float4 &worldJ ) + inline void RigidBody::ApplyImpulse_Angular( const ::Oyster::Math::Float3 &worldJ ) { this->impulse_Angular += worldJ; } - inline void RigidBody::ApplyForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength ) + inline void RigidBody::ApplyForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength ) { this->impulse_Linear += worldF * updateFrameLength; } - inline void RigidBody::ApplyForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float4 &atWorldPos ) + inline void RigidBody::ApplyForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float3 &atWorldPos ) { this->ApplyImpulse( worldF * updateFrameLength, atWorldPos ); } @@ -40,26 +40,25 @@ namespace Oyster { namespace Physics3D return this->GetView(); } - inline ::Oyster::Math::Float4 RigidBody::GetSize() const + inline ::Oyster::Math::Float3 RigidBody::GetSize() const { return 2.0f * this->boundingReach; } - inline void RigidBody::SetSize( const ::Oyster::Math::Float4 &widthHeight ) + inline void RigidBody::SetSize( const ::Oyster::Math::Float3 &widthHeight ) { this->boundingReach = ::Utility::Value::Abs( 0.5f * widthHeight ); } - inline void RigidBody::SetForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength ) - { - this->impulse_Linear = worldF * updateFrameLength; - } - - inline void RigidBody::SetForce( const ::Oyster::Math::Float4 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float4 &atWorldPos ) - { - this->SetImpulse_Linear( worldF * updateFrameLength, atWorldPos ); - } + //inline void RigidBody::SetForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength ) + //{ + // this->impulse_Linear = worldF * updateFrameLength; + //} + //inline void RigidBody::SetForce( const ::Oyster::Math::Float3 &worldF, ::Oyster::Math::Float updateFrameLength, const ::Oyster::Math::Float3 &atWorldPos ) + //{ + // this->SetImpulse_Linear( worldF * updateFrameLength, atWorldPos ); + //} } } #endif \ No newline at end of file From 5d1e05cc4e4f5749c846213190d9518cd551f77a Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Tue, 28 Jan 2014 09:24:51 +0100 Subject: [PATCH 47/76] Added new function to set orientation. --- Code/GamePhysics/PhysicsStructs-Impl.h | 9 +++++++++ Code/GamePhysics/PhysicsStructs.h | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Code/GamePhysics/PhysicsStructs-Impl.h b/Code/GamePhysics/PhysicsStructs-Impl.h index 7e1e8c46..3fd430ff 100644 --- a/Code/GamePhysics/PhysicsStructs-Impl.h +++ b/Code/GamePhysics/PhysicsStructs-Impl.h @@ -256,6 +256,13 @@ namespace Oyster this->isSpatiallyAltered = this->isDisturbed = true; } + inline void CustomBodyState::SetOrientation( const ::Oyster::Math::Float3 &angularAxis, const ::Oyster::Math::Float3 &translation ) + { + this->angularAxis.xyz = angularAxis ; + this->centerPos.xyz = translation; + this->isSpatiallyAltered = this->isDisturbed = true; + } + /*inline void CustomBodyState::SetRotation( const ::Oyster::Math::Float4x4 &rotation ) { this->SetRotation( ::Oyster::Math3D::AngularAxis(rotation) ); @@ -267,6 +274,8 @@ namespace Oyster this->SetCenterPosition( orientation.v[3] ); }*/ + + inline void CustomBodyState::SetLinearMomentum( const ::Oyster::Math::Float4 &g ) { this->linearMomentum.xyz = g; diff --git a/Code/GamePhysics/PhysicsStructs.h b/Code/GamePhysics/PhysicsStructs.h index 4eaaf46d..00b382c5 100644 --- a/Code/GamePhysics/PhysicsStructs.h +++ b/Code/GamePhysics/PhysicsStructs.h @@ -94,8 +94,9 @@ namespace Oyster { namespace Physics void SetReach( const ::Oyster::Math::Float4 &halfSize ); void SetCenterPosition( const ::Oyster::Math::Float4 ¢erPos ); void SetRotation( const ::Oyster::Math::Float4 &angularAxis ); - void SetRotation( const ::Oyster::Math::Float4x4 &rotation ); - void SetOrientation( const ::Oyster::Math::Float4x4 &orientation ); + //void SetRotation( const ::Oyster::Math::Float4x4 &rotation ); + //void SetOrientation( const ::Oyster::Math::Float4x4 &orientation ); + void SetOrientation( const ::Oyster::Math::Float3 &angularAxis, const ::Oyster::Math::Float3 &translation ); void SetLinearMomentum( const ::Oyster::Math::Float4 &g ); void SetAngularMomentum( const ::Oyster::Math::Float4 &h ); void SetLinearImpulse( const ::Oyster::Math::Float4 &j ); From f96c5a9f7eb5350968792860470150dbf4bbae7c Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Tue, 28 Jan 2014 09:52:58 +0100 Subject: [PATCH 48/76] Changed float4 to float3 --- .../Implementation/PhysicsAPI_Impl.cpp | 16 +-- .../Implementation/SimpleRigidBody.cpp | 12 +- .../Implementation/SphericalRigidBody.cpp | 12 +- Code/GamePhysics/PhysicsStructs-Impl.h | 106 +++++++++--------- Code/GamePhysics/PhysicsStructs.h | 86 +++++++------- 5 files changed, 116 insertions(+), 116 deletions(-) diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp index bfef1a61..d3650f66 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp @@ -31,8 +31,8 @@ namespace ICustomBody::State protoState; proto->GetState( protoState ); ICustomBody::State deuterState; deuter->GetState( deuterState ); - Float4 protoG = protoState.GetLinearMomentum( worldPointOfContact ), - deuterG = deuterState.GetLinearMomentum( worldPointOfContact ); + Float4 protoG = protoState.GetLinearMomentum( worldPointOfContact.xyz ), + deuterG = deuterState.GetLinearMomentum( worldPointOfContact.xyz ); // calc from perspective of deuter Float4 normal; deuter->GetNormalAt( worldPointOfContact, normal ); @@ -40,7 +40,7 @@ namespace deuterG_Magnitude = deuterG.Dot( normal ); // if they are not relatively moving towards eachother, there is no collision - Float deltaPos = normal.Dot( deuterState.GetCenterPosition() - protoState.GetCenterPosition() ); + Float deltaPos = normal.Dot( Float4(deuterState.GetCenterPosition(), 1) - Float4(protoState.GetCenterPosition(), 1) ); if( deltaPos < 0.0f ) { if( protoG_Magnitude >= deuterG_Magnitude ) @@ -95,13 +95,13 @@ namespace // } - Float kineticEnergyPBefore = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), protoState.GetLinearMomentum().xyz/protoState.GetMass() ); + Float kineticEnergyPBefore = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), protoState.GetLinearMomentum()/protoState.GetMass() ); // protoState.ApplyForwarding( forwardedDeltaPos, forwardedDeltaAxis ); - protoState.ApplyImpulse( bounce, worldPointOfContact, normal ); + protoState.ApplyImpulse( bounce.xyz, worldPointOfContact.xyz, normal.xyz ); proto->SetState( protoState ); - Float kineticEnergyPAFter = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), (protoState.GetLinearMomentum().xyz + protoState.GetLinearImpulse().xyz)/protoState.GetMass() ); + Float kineticEnergyPAFter = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), (protoState.GetLinearMomentum() + protoState.GetLinearImpulse())/protoState.GetMass() ); proto->CallSubscription_CollisionResponse( deuter, kineticEnergyPBefore - kineticEnergyPAFter ); @@ -179,7 +179,7 @@ void API_Impl::Update() { case Gravity::GravityType_Well: { - Float4 d = Float4( this->gravity[i].well.position, 1.0f ) - state.GetCenterPosition(); + Float4 d = Float4( this->gravity[i].well.position, 1.0f ) - Float4( state.GetCenterPosition(), 1.0f ); Float rSquared = d.Dot( d ); if( rSquared != 0.0 ) { @@ -201,7 +201,7 @@ void API_Impl::Update() if( gravityImpulse != Float4::null ) { - state.ApplyLinearImpulse( gravityImpulse ); + state.ApplyLinearImpulse( gravityImpulse.xyz ); (*proto)->SetGravityNormal( gravityImpulse.GetNormalized().xyz ); (*proto)->SetState( state ); } diff --git a/Code/GamePhysics/Implementation/SimpleRigidBody.cpp b/Code/GamePhysics/Implementation/SimpleRigidBody.cpp index 5a9bf53f..2f6a6c0c 100644 --- a/Code/GamePhysics/Implementation/SimpleRigidBody.cpp +++ b/Code/GamePhysics/Implementation/SimpleRigidBody.cpp @@ -56,7 +56,7 @@ SimpleRigidBody::SimpleRigidBody() SimpleRigidBody::SimpleRigidBody( const API::SimpleBodyDescription &desc ) { - this->rigid.SetRotation( desc.rotation ); + //this->rigid.SetRotation( desc.rotation ); this->rigid.centerPos = desc.centerPosition; this->rigid.SetSize( desc.size ); this->rigid.SetMass_KeepMomentum( desc.mass ); @@ -143,8 +143,8 @@ void SimpleRigidBody::SetState( const SimpleRigidBody::State &state ) if( state.IsForwarded() ) { - this->deltaPos += state.GetForward_DeltaPos(); - this->deltaAxis += state.GetForward_DeltaAxis(); + this->deltaPos += Float4(state.GetForward_DeltaPos(), 0); + this->deltaAxis += Float4(state.GetForward_DeltaAxis(), 0); this->isForwarded; } @@ -205,7 +205,7 @@ Sphere & SimpleRigidBody::GetBoundingSphere( Sphere &targetMem ) const Float4 & SimpleRigidBody::GetNormalAt( const Float4 &worldPos, Float4 &targetMem ) const { - Float4 offset = worldPos - this->rigid.centerPos; + Float4 offset = worldPos.xyz - this->rigid.centerPos; Float distance = offset.Dot( offset ); Float3 normal = Float3::null; @@ -295,7 +295,7 @@ UpdateState SimpleRigidBody::Update( Float timeStepLength ) { if( this->isForwarded ) { - this->rigid.Move( this->deltaPos, this->deltaAxis ); + this->rigid.Move( this->deltaPos.xyz, this->deltaAxis.xyz ); this->deltaPos = Float4::null; this->deltaAxis = Float4::null; this->isForwarded = false; @@ -310,7 +310,7 @@ UpdateState SimpleRigidBody::Update( Float timeStepLength ) void SimpleRigidBody::Predict( Float4 &outDeltaPos, Float4 &outDeltaAxis, const Float4 &actingLinearImpulse, const Float4 &actingAngularImpulse, Float deltaTime ) { - this->rigid.Predict_LeapFrog( outDeltaPos, outDeltaAxis, actingLinearImpulse, actingAngularImpulse, deltaTime ); + this->rigid.Predict_LeapFrog( outDeltaPos.xyz, outDeltaAxis.xyz, actingLinearImpulse.xyz, actingAngularImpulse.xyz, deltaTime ); } void SimpleRigidBody::SetScene( void *scene ) diff --git a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp index bb497851..a164cbe1 100644 --- a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp +++ b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp @@ -24,7 +24,7 @@ SphericalRigidBody::SphericalRigidBody() SphericalRigidBody::SphericalRigidBody( const API::SphericalBodyDescription &desc ) { this->rigid = RigidBody(); - this->rigid.SetRotation( desc.rotation ); + //this->rigid.SetRotation( desc.rotation ); this->rigid.centerPos = desc.centerPosition; this->rigid.boundingReach = Float4( desc.radius, desc.radius, desc.radius, 0.0f ); this->rigid.SetMass_KeepMomentum( desc.mass ); @@ -108,8 +108,8 @@ void SphericalRigidBody::SetState( const SphericalRigidBody::State &state ) if( state.IsForwarded() ) { - this->deltaPos += state.GetForward_DeltaPos(); - this->deltaAxis += state.GetForward_DeltaAxis(); + this->deltaPos += Float4(state.GetForward_DeltaPos(), 0); + this->deltaAxis += Float4(state.GetForward_DeltaAxis()); this->isForwarded = false; } @@ -171,7 +171,7 @@ Sphere & SphericalRigidBody::GetBoundingSphere( Sphere &targetMem ) const Float4 & SphericalRigidBody::GetNormalAt( const Float4 &worldPos, Float4 &targetMem ) const { - targetMem = worldPos - this->rigid.centerPos; + targetMem = worldPos.xyz - this->rigid.centerPos; Float magnitude = targetMem.GetMagnitude(); if( magnitude != 0.0f ) { // sanity check @@ -220,7 +220,7 @@ UpdateState SphericalRigidBody::Update( Float timeStepLength ) { if( this->isForwarded ) { - this->rigid.Move( this->deltaPos, this->deltaAxis ); + this->rigid.Move( this->deltaPos.xyz, this->deltaAxis.xyz ); this->deltaPos = Float4::null; this->deltaAxis = Float4::null; this->isForwarded = false; @@ -235,7 +235,7 @@ UpdateState SphericalRigidBody::Update( Float timeStepLength ) void SphericalRigidBody::Predict( ::Oyster::Math::Float4 &outDeltaPos, ::Oyster::Math::Float4 &outDeltaAxis, const ::Oyster::Math::Float4 &actingLinearImpulse, const ::Oyster::Math::Float4 &actingAngularImpulse, ::Oyster::Math::Float deltaTime ) { - this->rigid.Predict_LeapFrog( outDeltaPos, outDeltaAxis, actingLinearImpulse, actingAngularImpulse, deltaTime ); + this->rigid.Predict_LeapFrog( outDeltaPos.xyz, outDeltaAxis.xyz, actingLinearImpulse.xyz, actingAngularImpulse.xyz, deltaTime ); } void SphericalRigidBody::SetSubscription( ICustomBody::EventAction_Collision functionPointer ) diff --git a/Code/GamePhysics/PhysicsStructs-Impl.h b/Code/GamePhysics/PhysicsStructs-Impl.h index 3fd430ff..fa5533fa 100644 --- a/Code/GamePhysics/PhysicsStructs-Impl.h +++ b/Code/GamePhysics/PhysicsStructs-Impl.h @@ -13,8 +13,8 @@ namespace Oyster inline SimpleBodyDescription::SimpleBodyDescription() { this->rotation = ::Oyster::Math::Float4x4::identity; - this->centerPosition = ::Oyster::Math::Float4::standard_unit_w; - this->size = ::Oyster::Math::Float4( 1.0f ); + this->centerPosition = ::Oyster::Math::Float3::null; + this->size = ::Oyster::Math::Float3( 1.0f ); this->mass = 12.0f; this->restitutionCoeff = 1.0f; this->frictionCoeff_Dynamic = 0.5f; @@ -29,7 +29,7 @@ namespace Oyster inline SphericalBodyDescription::SphericalBodyDescription() { this->rotation = ::Oyster::Math::Float4x4::identity; - this->centerPosition = ::Oyster::Math::Float4::standard_unit_w; + this->centerPosition = ::Oyster::Math::Float3::null; this->radius = 0.5f; this->mass = 10.0f; this->restitutionCoeff = 1.0f; @@ -41,7 +41,7 @@ namespace Oyster this->ignoreGravity = false; } - inline CustomBodyState::CustomBodyState( ::Oyster::Math::Float mass, ::Oyster::Math::Float restitutionCoeff, ::Oyster::Math::Float staticFrictionCoeff, ::Oyster::Math::Float kineticFrictionCoeff, const ::Oyster::Physics3D::MomentOfInertia &inertiaTensor, const ::Oyster::Math::Float4 &reach, const ::Oyster::Math::Float4 ¢erPos, const ::Oyster::Math::Float4 &rotation, const ::Oyster::Math::Float4 &linearMomentum, const ::Oyster::Math::Float4 &angularMomentum, const ::Oyster::Math::Float4 &gravityNormal ) + inline CustomBodyState::CustomBodyState( ::Oyster::Math::Float mass, ::Oyster::Math::Float restitutionCoeff, ::Oyster::Math::Float staticFrictionCoeff, ::Oyster::Math::Float kineticFrictionCoeff, const ::Oyster::Physics3D::MomentOfInertia &inertiaTensor, const ::Oyster::Math::Float3 &reach, const ::Oyster::Math::Float3 ¢erPos, const ::Oyster::Math::Float3 &rotation, const ::Oyster::Math::Float3 &linearMomentum, const ::Oyster::Math::Float3 &angularMomentum, const ::Oyster::Math::Float3 &gravityNormal ) { this->mass = mass; this->restitutionCoeff = restitutionCoeff; @@ -53,8 +53,8 @@ namespace Oyster this->angularAxis = rotation; this->linearMomentum = linearMomentum; this->angularMomentum = angularMomentum; - this->linearImpulse = this->angularImpulse = ::Oyster::Math::Float4::null; - this->deltaPos = this->deltaAxis = ::Oyster::Math::Float4::null; + this->linearImpulse = this->angularImpulse = ::Oyster::Math::Float3::null; + this->deltaPos = this->deltaAxis = ::Oyster::Math::Float3::null; this->isSpatiallyAltered = this->isDisturbed = this->isForwarded = false; this->gravityNormal = gravityNormal; } @@ -107,87 +107,87 @@ namespace Oyster return this->inertiaTensor; } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetReach() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetReach() const { return this->reach; } - inline ::Oyster::Math::Float4 CustomBodyState::GetSize() const + inline ::Oyster::Math::Float3 CustomBodyState::GetSize() const { return 2.0f * this->GetReach(); } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetCenterPosition() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetCenterPosition() const { return this->centerPos; } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetAngularAxis() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetAngularAxis() const { return this->angularAxis; } inline ::Oyster::Math::Float4x4 CustomBodyState::GetRotation() const { - return ::Oyster::Math3D::RotationMatrix( this->GetAngularAxis().xyz ); + return ::Oyster::Math3D::RotationMatrix( this->GetAngularAxis() ); } inline ::Oyster::Math::Float4x4 CustomBodyState::GetOrientation() const { - return ::Oyster::Math3D::OrientationMatrix( this->angularAxis.xyz, this->centerPos.xyz ); + return ::Oyster::Math3D::OrientationMatrix( this->angularAxis, this->centerPos ); } - inline ::Oyster::Math::Float4x4 CustomBodyState::GetOrientation( const ::Oyster::Math::Float4 &offset ) const + inline ::Oyster::Math::Float4x4 CustomBodyState::GetOrientation( const ::Oyster::Math::Float3 &offset ) const { - return ::Oyster::Math3D::OrientationMatrix( this->angularAxis.xyz, (this->centerPos + offset).xyz ); + return ::Oyster::Math3D::OrientationMatrix( this->angularAxis, (this->centerPos + offset) ); } inline ::Oyster::Math::Float4x4 CustomBodyState::GetView() const { - return ::Oyster::Math3D::ViewMatrix( this->angularAxis.xyz, this->centerPos.xyz ); + return ::Oyster::Math3D::ViewMatrix( this->angularAxis, this->centerPos ); } - inline ::Oyster::Math::Float4x4 CustomBodyState::GetView( const ::Oyster::Math::Float4 &offset ) const + inline ::Oyster::Math::Float4x4 CustomBodyState::GetView( const ::Oyster::Math::Float3 &offset ) const { - return ::Oyster::Math3D::ViewMatrix( this->angularAxis.xyz, (this->centerPos + offset).xyz ); + return ::Oyster::Math3D::ViewMatrix( this->angularAxis, (this->centerPos + offset) ); } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetLinearMomentum() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetLinearMomentum() const { return this->linearMomentum; } - inline ::Oyster::Math::Float4 CustomBodyState::GetLinearMomentum( const ::Oyster::Math::Float4 &at ) const + inline ::Oyster::Math::Float3 CustomBodyState::GetLinearMomentum( const ::Oyster::Math::Float3 &at ) const { return this->linearMomentum + ::Oyster::Physics3D::Formula::TangentialLinearMomentum( this->angularMomentum, at - this->centerPos ); } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetAngularMomentum() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetAngularMomentum() const { return this->angularMomentum; } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetLinearImpulse() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetLinearImpulse() const { return this->linearImpulse; } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetAngularImpulse() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetAngularImpulse() const { return this->angularImpulse; } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetForward_DeltaPos() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetForward_DeltaPos() const { return this->deltaPos; } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetForward_DeltaAxis() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetForward_DeltaAxis() const { return this->deltaAxis; } - inline const ::Oyster::Math::Float4 & CustomBodyState::GetGravityNormal() const + inline const ::Oyster::Math::Float3 & CustomBodyState::GetGravityNormal() const { return this->gravityNormal; } @@ -227,39 +227,39 @@ namespace Oyster inline void CustomBodyState::SetMomentOfInertia_KeepVelocity( const ::Oyster::Physics3D::MomentOfInertia &tensor ) { ::Oyster::Math::Quaternion rotation = ::Oyster::Math3D::Rotation(this->angularAxis); - ::Oyster::Math::Float4 w = this->inertiaTensor.CalculateAngularVelocity( rotation, this->angularMomentum ); + ::Oyster::Math::Float3 w = this->inertiaTensor.CalculateAngularVelocity( rotation, this->angularMomentum ); this->inertiaTensor = tensor; this->angularMomentum = this->inertiaTensor.CalculateAngularMomentum( rotation, w ); } - inline void CustomBodyState::SetSize( const ::Oyster::Math::Float4 &size ) + inline void CustomBodyState::SetSize( const ::Oyster::Math::Float3 &size ) { this->SetReach( 0.5f * size ); } - inline void CustomBodyState::SetReach( const ::Oyster::Math::Float4 &halfSize ) + inline void CustomBodyState::SetReach( const ::Oyster::Math::Float3 &halfSize ) { - this->reach.xyz = halfSize; - this->reach = ::Utility::Value::Max( this->reach, ::Oyster::Math::Float4::null ); + this->reach = halfSize; + this->reach = ::Utility::Value::Max( this->reach, ::Oyster::Math::Float3::null ); this->isSpatiallyAltered = this->isDisturbed = true; } - inline void CustomBodyState::SetCenterPosition( const ::Oyster::Math::Float4 ¢erPos ) + inline void CustomBodyState::SetCenterPosition( const ::Oyster::Math::Float3 ¢erPos ) { - this->centerPos.xyz = centerPos; + this->centerPos = centerPos; this->isSpatiallyAltered = this->isDisturbed = true; } - inline void CustomBodyState::SetRotation( const ::Oyster::Math::Float4 &angularAxis ) + inline void CustomBodyState::SetRotation( const ::Oyster::Math::Float3 &angularAxis ) { - this->angularAxis.xyz = angularAxis; + this->angularAxis = angularAxis; this->isSpatiallyAltered = this->isDisturbed = true; } inline void CustomBodyState::SetOrientation( const ::Oyster::Math::Float3 &angularAxis, const ::Oyster::Math::Float3 &translation ) { - this->angularAxis.xyz = angularAxis ; - this->centerPos.xyz = translation; + this->angularAxis = angularAxis ; + this->centerPos = translation; this->isSpatiallyAltered = this->isDisturbed = true; } @@ -276,70 +276,70 @@ namespace Oyster - inline void CustomBodyState::SetLinearMomentum( const ::Oyster::Math::Float4 &g ) + inline void CustomBodyState::SetLinearMomentum( const ::Oyster::Math::Float3 &g ) { - this->linearMomentum.xyz = g; + this->linearMomentum = g; this->isDisturbed = true; } - inline void CustomBodyState::SetAngularMomentum( const ::Oyster::Math::Float4 &h ) + inline void CustomBodyState::SetAngularMomentum( const ::Oyster::Math::Float3 &h ) { - this->angularMomentum.xyz = h; + this->angularMomentum = h; this->isDisturbed = true; } - inline void CustomBodyState::SetLinearImpulse( const ::Oyster::Math::Float4 &j ) + inline void CustomBodyState::SetLinearImpulse( const ::Oyster::Math::Float3 &j ) { - this->linearImpulse.xyz = j; + this->linearImpulse = j; this->isDisturbed = true; } - inline void CustomBodyState::SetAngularImpulse( const ::Oyster::Math::Float4 &j ) + inline void CustomBodyState::SetAngularImpulse( const ::Oyster::Math::Float3 &j ) { - this->angularImpulse.xyz = j; + this->angularImpulse = j; this->isDisturbed = true; } - inline void CustomBodyState::SetGravityNormal( const ::Oyster::Math::Float4 &gravityNormal ) + inline void CustomBodyState::SetGravityNormal( const ::Oyster::Math::Float3 &gravityNormal ) { this->gravityNormal = gravityNormal; } - inline void CustomBodyState::AddRotation( const ::Oyster::Math::Float4 &angularAxis ) + inline void CustomBodyState::AddRotation( const ::Oyster::Math::Float3 &angularAxis ) { this->angularAxis += angularAxis; this->isSpatiallyAltered = this->isDisturbed = true; } - inline void CustomBodyState::AddTranslation( const ::Oyster::Math::Float4 &deltaPos ) + inline void CustomBodyState::AddTranslation( const ::Oyster::Math::Float3 &deltaPos ) { this->centerPos += deltaPos; this->isSpatiallyAltered = this->isDisturbed = true; } - inline void CustomBodyState::ApplyLinearImpulse( const ::Oyster::Math::Float4 &j ) + inline void CustomBodyState::ApplyLinearImpulse( const ::Oyster::Math::Float3 &j ) { this->linearImpulse += j; this->isDisturbed = true; } - inline void CustomBodyState::ApplyAngularImpulse( const ::Oyster::Math::Float4 &j ) + inline void CustomBodyState::ApplyAngularImpulse( const ::Oyster::Math::Float3 &j ) { this->angularImpulse += j; this->isDisturbed = true; } - inline void CustomBodyState::ApplyImpulse( const ::Oyster::Math::Float4 &j, const ::Oyster::Math::Float4 &at, const ::Oyster::Math::Float4 &normal ) + inline void CustomBodyState::ApplyImpulse( const ::Oyster::Math::Float3 &j, const ::Oyster::Math::Float3 &at, const ::Oyster::Math::Float3 &normal ) { - ::Oyster::Math::Float4 offset = at - this->centerPos; - ::Oyster::Math::Float4 deltaAngularImpulse = ::Oyster::Physics3D::Formula::AngularMomentum( j, offset ); + ::Oyster::Math::Float3 offset = at - this->centerPos; + ::Oyster::Math::Float3 deltaAngularImpulse = ::Oyster::Physics3D::Formula::AngularMomentum( j, offset ); this->linearImpulse += j - ::Oyster::Physics3D::Formula::TangentialLinearMomentum( deltaAngularImpulse, offset ); this->angularImpulse += deltaAngularImpulse; this->isDisturbed = true; } - inline void CustomBodyState::ApplyForwarding( const ::Oyster::Math::Float4 &deltaPos, const ::Oyster::Math::Float4 &deltaAxis ) + inline void CustomBodyState::ApplyForwarding( const ::Oyster::Math::Float3 &deltaPos, const ::Oyster::Math::Float3 &deltaAxis ) { this->deltaPos += deltaPos; this->deltaAxis += deltaAxis; diff --git a/Code/GamePhysics/PhysicsStructs.h b/Code/GamePhysics/PhysicsStructs.h index 00b382c5..49cf6993 100644 --- a/Code/GamePhysics/PhysicsStructs.h +++ b/Code/GamePhysics/PhysicsStructs.h @@ -12,8 +12,8 @@ namespace Oyster { namespace Physics struct SimpleBodyDescription { ::Oyster::Math::Float4x4 rotation; - ::Oyster::Math::Float4 centerPosition; - ::Oyster::Math::Float4 size; + ::Oyster::Math::Float3 centerPosition; + ::Oyster::Math::Float3 size; ::Oyster::Math::Float mass; ::Oyster::Math::Float restitutionCoeff; ::Oyster::Math::Float frictionCoeff_Static; @@ -30,7 +30,7 @@ namespace Oyster { namespace Physics struct SphericalBodyDescription { ::Oyster::Math::Float4x4 rotation; - ::Oyster::Math::Float4 centerPosition; + ::Oyster::Math::Float3 centerPosition; ::Oyster::Math::Float radius; ::Oyster::Math::Float mass; ::Oyster::Math::Float restitutionCoeff; @@ -52,12 +52,12 @@ namespace Oyster { namespace Physics ::Oyster::Math::Float staticFrictionCoeff = 1.0f, ::Oyster::Math::Float kineticFrictionCoeff = 1.0f, const ::Oyster::Physics3D::MomentOfInertia &inertiaTensor = ::Oyster::Physics3D::MomentOfInertia(), - const ::Oyster::Math::Float4 &reach = ::Oyster::Math::Float4::null, - const ::Oyster::Math::Float4 ¢erPos = ::Oyster::Math::Float4::standard_unit_w, - const ::Oyster::Math::Float4 &rotation = ::Oyster::Math::Float4::null, - const ::Oyster::Math::Float4 &linearMomentum = ::Oyster::Math::Float4::null, - const ::Oyster::Math::Float4 &angularMomentum = ::Oyster::Math::Float4::null, - const ::Oyster::Math::Float4 &gravityNormal = ::Oyster::Math::Float4::null); + const ::Oyster::Math::Float3 &reach = ::Oyster::Math::Float3::null, + const ::Oyster::Math::Float3 ¢erPos = ::Oyster::Math::Float3::null, + const ::Oyster::Math::Float3 &rotation = ::Oyster::Math::Float3::null, + const ::Oyster::Math::Float3 &linearMomentum = ::Oyster::Math::Float3::null, + const ::Oyster::Math::Float3 &angularMomentum = ::Oyster::Math::Float3::null, + const ::Oyster::Math::Float3 &gravityNormal = ::Oyster::Math::Float3::null); CustomBodyState & operator = ( const CustomBodyState &state ); @@ -66,23 +66,23 @@ namespace Oyster { namespace Physics const ::Oyster::Math::Float GetFrictionCoeff_Static() const; const ::Oyster::Math::Float GetFrictionCoeff_Kinetic() const; const ::Oyster::Physics3D::MomentOfInertia & GetMomentOfInertia() const; - const ::Oyster::Math::Float4 & GetReach() const; - ::Oyster::Math::Float4 GetSize() const; - const ::Oyster::Math::Float4 & GetCenterPosition() const; - const ::Oyster::Math::Float4 & GetAngularAxis() const; + const ::Oyster::Math::Float3 & GetReach() const; + ::Oyster::Math::Float3 GetSize() const; + const ::Oyster::Math::Float3 & GetCenterPosition() const; + const ::Oyster::Math::Float3 & GetAngularAxis() const; ::Oyster::Math::Float4x4 GetRotation() const; ::Oyster::Math::Float4x4 GetOrientation() const; - ::Oyster::Math::Float4x4 GetOrientation( const ::Oyster::Math::Float4 &offset ) const; + ::Oyster::Math::Float4x4 GetOrientation( const ::Oyster::Math::Float3 &offset ) const; ::Oyster::Math::Float4x4 GetView() const; - ::Oyster::Math::Float4x4 GetView( const ::Oyster::Math::Float4 &offset ) const; - const ::Oyster::Math::Float4 & GetLinearMomentum() const; - ::Oyster::Math::Float4 GetLinearMomentum( const ::Oyster::Math::Float4 &at ) const; - const ::Oyster::Math::Float4 & GetAngularMomentum() const; - const ::Oyster::Math::Float4 & GetLinearImpulse() const; - const ::Oyster::Math::Float4 & GetAngularImpulse() const; - const ::Oyster::Math::Float4 & GetForward_DeltaPos() const; - const ::Oyster::Math::Float4 & GetForward_DeltaAxis() const; - const ::Oyster::Math::Float4 & GetGravityNormal() const; + ::Oyster::Math::Float4x4 GetView( const ::Oyster::Math::Float3 &offset ) const; + const ::Oyster::Math::Float3 & GetLinearMomentum() const; + ::Oyster::Math::Float3 GetLinearMomentum( const ::Oyster::Math::Float3 &at ) const; + const ::Oyster::Math::Float3 & GetAngularMomentum() const; + const ::Oyster::Math::Float3 & GetLinearImpulse() const; + const ::Oyster::Math::Float3 & GetAngularImpulse() const; + const ::Oyster::Math::Float3 & GetForward_DeltaPos() const; + const ::Oyster::Math::Float3 & GetForward_DeltaAxis() const; + const ::Oyster::Math::Float3 & GetGravityNormal() const; void SetMass_KeepMomentum( ::Oyster::Math::Float m ); void SetMass_KeepVelocity( ::Oyster::Math::Float m ); @@ -90,26 +90,26 @@ namespace Oyster { namespace Physics void SetFrictionCoeff( ::Oyster::Math::Float staticU, ::Oyster::Math::Float kineticU ); void SetMomentOfInertia_KeepMomentum( const ::Oyster::Physics3D::MomentOfInertia &tensor ); void SetMomentOfInertia_KeepVelocity( const ::Oyster::Physics3D::MomentOfInertia &tensor ); - void SetSize( const ::Oyster::Math::Float4 &size ); - void SetReach( const ::Oyster::Math::Float4 &halfSize ); - void SetCenterPosition( const ::Oyster::Math::Float4 ¢erPos ); - void SetRotation( const ::Oyster::Math::Float4 &angularAxis ); + void SetSize( const ::Oyster::Math::Float3 &size ); + void SetReach( const ::Oyster::Math::Float3 &halfSize ); + void SetCenterPosition( const ::Oyster::Math::Float3 ¢erPos ); + void SetRotation( const ::Oyster::Math::Float3 &angularAxis ); //void SetRotation( const ::Oyster::Math::Float4x4 &rotation ); //void SetOrientation( const ::Oyster::Math::Float4x4 &orientation ); void SetOrientation( const ::Oyster::Math::Float3 &angularAxis, const ::Oyster::Math::Float3 &translation ); - void SetLinearMomentum( const ::Oyster::Math::Float4 &g ); - void SetAngularMomentum( const ::Oyster::Math::Float4 &h ); - void SetLinearImpulse( const ::Oyster::Math::Float4 &j ); - void SetAngularImpulse( const ::Oyster::Math::Float4 &j ); - void SetGravityNormal( const ::Oyster::Math::Float4 &gravityNormal ); + void SetLinearMomentum( const ::Oyster::Math::Float3 &g ); + void SetAngularMomentum( const ::Oyster::Math::Float3 &h ); + void SetLinearImpulse( const ::Oyster::Math::Float3 &j ); + void SetAngularImpulse( const ::Oyster::Math::Float3 &j ); + void SetGravityNormal( const ::Oyster::Math::Float3 &gravityNormal ); - void AddRotation( const ::Oyster::Math::Float4 &angularAxis ); - void AddTranslation( const ::Oyster::Math::Float4 &deltaPos ); + void AddRotation( const ::Oyster::Math::Float3 &angularAxis ); + void AddTranslation( const ::Oyster::Math::Float3 &deltaPos ); - void ApplyLinearImpulse( const ::Oyster::Math::Float4 &j ); - void ApplyAngularImpulse( const ::Oyster::Math::Float4 &j ); - void ApplyImpulse( const ::Oyster::Math::Float4 &j, const ::Oyster::Math::Float4 &at, const ::Oyster::Math::Float4 &normal ); - void ApplyForwarding( const ::Oyster::Math::Float4 &deltaPos, const ::Oyster::Math::Float4 &deltaAxis ); + void ApplyLinearImpulse( const ::Oyster::Math::Float3 &j ); + void ApplyAngularImpulse( const ::Oyster::Math::Float3 &j ); + void ApplyImpulse( const ::Oyster::Math::Float3 &j, const ::Oyster::Math::Float3 &at, const ::Oyster::Math::Float3 &normal ); + void ApplyForwarding( const ::Oyster::Math::Float3 &deltaPos, const ::Oyster::Math::Float3 &deltaAxis ); bool IsSpatiallyAltered() const; bool IsDisturbed() const; @@ -118,11 +118,11 @@ namespace Oyster { namespace Physics private: ::Oyster::Math::Float mass, restitutionCoeff, staticFrictionCoeff, kineticFrictionCoeff; ::Oyster::Physics3D::MomentOfInertia inertiaTensor; - ::Oyster::Math::Float4 reach, centerPos, angularAxis; - ::Oyster::Math::Float4 linearMomentum, angularMomentum; - ::Oyster::Math::Float4 linearImpulse, angularImpulse; - ::Oyster::Math::Float4 deltaPos, deltaAxis; // Forwarding data sum - ::Oyster::Math::Float4 gravityNormal; + ::Oyster::Math::Float3 reach, centerPos, angularAxis; + ::Oyster::Math::Float3 linearMomentum, angularMomentum; + ::Oyster::Math::Float3 linearImpulse, angularImpulse; + ::Oyster::Math::Float3 deltaPos, deltaAxis; // Forwarding data sum + ::Oyster::Math::Float3 gravityNormal; bool isSpatiallyAltered, isDisturbed, isForwarded; }; From 6148783fe4db6b7f7e3f15c249cfff2c4033983b Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Tue, 28 Jan 2014 10:18:26 +0100 Subject: [PATCH 49/76] Added args to ApplyEffect --- Code/GamePhysics/Implementation/Octree.cpp | 4 ++-- Code/GamePhysics/Implementation/Octree.h | 4 ++-- Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp | 4 ++-- Code/GamePhysics/Implementation/PhysicsAPI_Impl.h | 2 +- Code/GamePhysics/PhysicsAPI.h | 6 ++++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Code/GamePhysics/Implementation/Octree.cpp b/Code/GamePhysics/Implementation/Octree.cpp index 3faff29e..b4395516 100644 --- a/Code/GamePhysics/Implementation/Octree.cpp +++ b/Code/GamePhysics/Implementation/Octree.cpp @@ -128,13 +128,13 @@ void Octree::Visit(ICustomBody* customBodyRef, VisitorAction hitAction ) } } -void Octree::Visit(const Oyster::Collision3D::ICollideable& collideable, VisitorActionCollideable hitAction) +void Octree::Visit(const Oyster::Collision3D::ICollideable& collideable, void* args, VisitorActionCollideable hitAction) { for(unsigned int i = 0; ileafData.size(); i++) { if(collideable.Intersects(this->leafData[i].container)) { - hitAction( this->GetCustomBody(i) ); + hitAction( this->GetCustomBody(i), args ); } } } diff --git a/Code/GamePhysics/Implementation/Octree.h b/Code/GamePhysics/Implementation/Octree.h index 7b350795..50b9569a 100644 --- a/Code/GamePhysics/Implementation/Octree.h +++ b/Code/GamePhysics/Implementation/Octree.h @@ -18,7 +18,7 @@ namespace Oyster static const unsigned int invalid_ref; typedef void(*VisitorAction)(Octree&, unsigned int, unsigned int); - typedef void(*VisitorActionCollideable)(ICustomBody*); + typedef void(*VisitorActionCollideable)(ICustomBody*, void*); struct Data { @@ -53,7 +53,7 @@ namespace Oyster std::vector& Sample(ICustomBody* customBodyRef, std::vector& updateList); std::vector& Sample(const Oyster::Collision3D::ICollideable& collideable, std::vector& updateList); void Visit(ICustomBody* customBodyRef, VisitorAction hitAction ); - void Visit(const Oyster::Collision3D::ICollideable& collideable, VisitorActionCollideable hitAction ); + void Visit(const Oyster::Collision3D::ICollideable& collideable, void* args, VisitorActionCollideable hitAction ); ICustomBody* GetCustomBody(const unsigned int tempRef); diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp index d3650f66..14c4d9e4 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp @@ -276,9 +276,9 @@ void API_Impl::RemoveGravity( const API::Gravity &g ) } } -void API_Impl::ApplyEffect( const Oyster::Collision3D::ICollideable& collideable, void(hitAction)(ICustomBody*) ) +void API_Impl::ApplyEffect( const Oyster::Collision3D::ICollideable& collideable, void* args, void(hitAction)(ICustomBody*, void*) ) { - this->worldScene.Visit(collideable, hitAction); + this->worldScene.Visit(collideable, args, hitAction); } //void API_Impl::ApplyForceAt( const ICustomBody* objRef, const Float3 &worldPos, const Float3 &worldF ) diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h index b9343ae6..63d8ff08 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h @@ -35,7 +35,7 @@ namespace Oyster void AddGravity( const API::Gravity &g ); void RemoveGravity( const API::Gravity &g ); - void ApplyEffect( const Oyster::Collision3D::ICollideable& collideable, void(hitAction)(ICustomBody*) ); + void ApplyEffect( const Oyster::Collision3D::ICollideable& collideable, void* args, void(hitAction)(ICustomBody*, void*) ); //void ApplyForceAt( const ICustomBody* objRef, const ::Oyster::Math::Float3 &worldPos, const ::Oyster::Math::Float3 &worldF ); diff --git a/Code/GamePhysics/PhysicsAPI.h b/Code/GamePhysics/PhysicsAPI.h index 7f20fcf7..3ced2a55 100644 --- a/Code/GamePhysics/PhysicsAPI.h +++ b/Code/GamePhysics/PhysicsAPI.h @@ -139,9 +139,11 @@ namespace Oyster /******************************************************** * Applies an effect to objects that collide with the set volume. * @param collideable: An ICollideable that defines the volume of the effect. - * @param hitAction: A function that contains the effect. + * @param args: The arguments needed for the hitAction function. + * @param hitAction: A function that contains the effect. Parameterlist contains the custom body + the collideable hits, and the arguments sent to the function. ********************************************************/ - virtual void ApplyEffect( const Oyster::Collision3D::ICollideable& collideable, void(hitAction)(ICustomBody*) ) = 0; + virtual void ApplyEffect( const Oyster::Collision3D::ICollideable& collideable, void* args, void(hitAction)(ICustomBody*, void*) ) = 0; ///******************************************************** // * Apply force on an object. From eb27a4edfbbc5905b5937b0f583044b2c79188ae Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Tue, 28 Jan 2014 10:38:58 +0100 Subject: [PATCH 50/76] Added ExtractForward to frustum. --- Code/OysterPhysics3D/Frustrum.cpp | 5 +++++ Code/OysterPhysics3D/Frustrum.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/Code/OysterPhysics3D/Frustrum.cpp b/Code/OysterPhysics3D/Frustrum.cpp index 54e6a67f..ce2c5256 100644 --- a/Code/OysterPhysics3D/Frustrum.cpp +++ b/Code/OysterPhysics3D/Frustrum.cpp @@ -241,4 +241,9 @@ bool Frustrum::Contains( const ICollideable &target ) const //case Type_line: return false; // TODO: default: return false; } +} + +::Oyster::Math::Float3 Frustrum::ExtractForwad() +{ + return this->bottomPlane.normal.xyz; } \ No newline at end of file diff --git a/Code/OysterPhysics3D/Frustrum.h b/Code/OysterPhysics3D/Frustrum.h index ba5656c5..ae0f086c 100644 --- a/Code/OysterPhysics3D/Frustrum.h +++ b/Code/OysterPhysics3D/Frustrum.h @@ -41,6 +41,8 @@ namespace Oyster { namespace Collision3D bool Intersects( const ICollideable &target ) const; bool Intersects( const ICollideable &target, Oyster::Math::Float4 &worldPointOfContact ) const; bool Contains( const ICollideable &target ) const; + + ::Oyster::Math::Float3 ExtractForwad(); }; // INLINE IMPLEMENTATIONS /////////////////////////////////////// From 4de1c1aafd438efcd3080de77a7ebd9e5d28ee71 Mon Sep 17 00:00:00 2001 From: Pontus Fransson Date: Tue, 28 Jan 2014 11:27:29 +0100 Subject: [PATCH 51/76] GL - More Object structs, LevelLoader api comments. Changed the LevelParser to use the new functions from ParserFunctions. Might need to cast something to (void*). --- Code/Game/GameLogic/LevelLoader.cpp | 2 +- Code/Game/GameLogic/LevelLoader.h | 16 ++++- Code/Game/GameLogic/LevelParser.cpp | 97 +++++++++++++++++------------ Code/Game/GameLogic/LevelParser.h | 25 +------- Code/Game/GameLogic/ObjectDefines.h | 78 +++++++++++++++++++++-- 5 files changed, 148 insertions(+), 70 deletions(-) diff --git a/Code/Game/GameLogic/LevelLoader.cpp b/Code/Game/GameLogic/LevelLoader.cpp index 3827251a..a6f8bb98 100644 --- a/Code/Game/GameLogic/LevelLoader.cpp +++ b/Code/Game/GameLogic/LevelLoader.cpp @@ -12,7 +12,7 @@ std::vector LevelLoader::LoadLevel(std::string fileName) return parser->Parse(fileName); } -ObjectTypeHeader LevelLoader::LoadLevelHeader(std::string fileName) +LevelMetaData LevelLoader::LoadLevelHeader(std::string fileName) { return parser->ParseHeader(fileName); } \ No newline at end of file diff --git a/Code/Game/GameLogic/LevelLoader.h b/Code/Game/GameLogic/LevelLoader.h index ed7aab4d..8f87d141 100644 --- a/Code/Game/GameLogic/LevelLoader.h +++ b/Code/Game/GameLogic/LevelLoader.h @@ -19,8 +19,20 @@ namespace GameLogic public: LevelLoader(){this->parser = new GameLogic::LevelFileLoader::LevelParser(); } ~LevelLoader(){} - std::vector LoadLevel(std::string fileName); //loads the level and objects from file - ObjectTypeHeader LoadLevelHeader(std::string fileName); //just for fast access for the meta information about the level. + + /******************************************************** + * Loads the level and objects from file. + * @param fileName: Path to the level-file that you want to load. + * @return: Returns all structs with objects and information about the level. + ********************************************************/ + std::vector LoadLevel(std::string fileName); + + /******************************************************** + * Just for fast access for the meta information about the level. + * @param fileName: Path to the level-file that you want to load. + * @return: Returns the meta information about the level. + ********************************************************/ + LevelMetaData LoadLevelHeader(std::string fileName); //. private: GameLogic::LevelFileLoader::LevelParser *parser; diff --git a/Code/Game/GameLogic/LevelParser.cpp b/Code/Game/GameLogic/LevelParser.cpp index a957b88a..acc52c69 100644 --- a/Code/Game/GameLogic/LevelParser.cpp +++ b/Code/Game/GameLogic/LevelParser.cpp @@ -6,9 +6,10 @@ using namespace GameLogic; using namespace ::LevelFileLoader; - LevelParser::LevelParser() { + formatVersion.formatVersionMajor = 1; + formatVersion.formatVersionMinor = 0; } LevelParser::~LevelParser() @@ -18,35 +19,43 @@ LevelParser::~LevelParser() // std::vector LevelParser::Parse(std::string filename) { - int stringSize = 0; - //Read entire level file. - Loader loader; - unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), stringSize); + int bufferSize = 0; + unsigned int counter = 0; std::vector objects; - unsigned int counter = 0; - - while(counter < stringSize) + //Read entire level file. + Loader loader; + unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), bufferSize); + + //Read format version + FormatVersion levelFormatVersion; + ParseObject(&buffer[counter], formatVersion, sizeof(formatVersion)); + if(this->formatVersion != levelFormatVersion) + { + //Do something if it's not the same version + } + + while(counter < bufferSize) { //Get typeID ObjectTypeHeader typeID; - typeID = ParseObjectTypeHeader(&buffer[counter]); - //counter += 4; - //Unpack ID + ParseObject(&buffer[counter], typeID, sizeof(typeID)); switch((int)typeID.typeID) { - case TypeID_LevelHeader: - //Call function - counter += LevelHeaderSize; + case ObjectType_LevelMetaData: + LevelMetaData header; + //ParseObject(&buffer[counter], header, sizeof(header)); + objects.push_back(header); + counter += sizeof(header); break; - case TypeID_Object: - //Call function - objects.push_back(ParseObjectHeader(&buffer[counter])); - counter += sizeof(ObjectHeader); - break; + case ObjectType_Dynamic: + ObjectHeader header; + ParseObject(&buffer[counter], header, sizeof(header)); + objects.push_back(header); + counter += sizeof(header); default: //Couldn't find typeID. FAIL!!!!!! break; @@ -56,36 +65,44 @@ std::vector LevelParser::Parse(std::string filename) return objects; } -//för meta information om leveln. Måste göra så den returnerar rätt strukt så fort vi -//vi definierat en! -ObjectTypeHeader LevelParser::ParseHeader(std::string filename) +//för meta information om leveln. +LevelMetaData LevelParser::ParseHeader(std::string filename) { - int stringSize = 0; + int bufferSize = 0; + unsigned int counter = 0; + + LevelMetaData levelHeader; + levelHeader.typeID = ObjectType::ObjectType_Unknown; + //Read entire level file. Loader loader; - unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), stringSize); + unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), bufferSize); + + //Read format version + FormatVersion levelFormatVersion; + ParseObject(&buffer[counter], formatVersion, sizeof(formatVersion)); + if(this->formatVersion != levelFormatVersion) + { + //Do something if it's not the same version + } //Find the header in the returned string. - unsigned int counter = 0; - - - ObjectTypeHeader header; - header.typeID = ObjectType_Level; - - while(counter < stringSize) + while(counter < bufferSize) { ObjectTypeHeader typeID; - typeID = ParseObjectTypeHeader(buffer); + ParseObject(&buffer[counter], typeID, sizeof(typeID)); + switch(typeID.typeID) { - case TypeID_LevelHeader: - //Call function - - counter += LevelHeaderSize; + case ObjectType_LevelMetaData: + //ParseObject(&buffer[counter], levelHeader, sizeof(levelHeader)); + return levelHeader; + counter += sizeof(LevelMetaData); break; - case TypeID_Object: - //Call function - counter += ObjectHeaderSize; + case ObjectType_Dynamic: + //Do not call parse this object, since we are only interested in the LevelMetaData + //Only increase the counter size + counter += sizeof(ObjectHeader); break; default: @@ -94,5 +111,5 @@ ObjectTypeHeader LevelParser::ParseHeader(std::string filename) } } - return header; + return levelHeader; } \ No newline at end of file diff --git a/Code/Game/GameLogic/LevelParser.h b/Code/Game/GameLogic/LevelParser.h index bcb9cb31..6dce25f8 100644 --- a/Code/Game/GameLogic/LevelParser.h +++ b/Code/Game/GameLogic/LevelParser.h @@ -19,31 +19,10 @@ namespace GameLogic std::vector Parse(std::string filename); // - ObjectTypeHeader ParseHeader(std::string filename); + LevelMetaData ParseHeader(std::string filename); private: - static const int TypeHeaderSize = 4; //Including the TypeID - static const int ObjectHeaderSize = 10; //Including modelID, textureID, position, rotation, scale. - static const int LevelHeaderSize = 10; //Including Format version, Name, Map version etc. - - static const int FormatVersionMajor = 1; - static const int FormatVersionMinor = 0; - - /************************************* - Question - *************************************/ - //Could we use the IDs in "ObjectDefines.h" or do we have to use our own??? - enum TypeID - { - TypeID_LevelHeader, - TypeID_Player, - TypeID_Object, - - - TypeID_NUM_OF_TYPES, - - ObjectType_Unknown = -1, - }; + FormatVersion formatVersion; }; } diff --git a/Code/Game/GameLogic/ObjectDefines.h b/Code/Game/GameLogic/ObjectDefines.h index 7f2307d1..2d3e09f0 100644 --- a/Code/Game/GameLogic/ObjectDefines.h +++ b/Code/Game/GameLogic/ObjectDefines.h @@ -1,27 +1,97 @@ #ifndef OBJECT_DEFINES_H #define OBJECT_DEFINES_H +#include +#include + namespace GameLogic { + /************************************ + Enums + *************************************/ + enum ObjectType { - ObjectType_Level, + ObjectType_LevelMetaData, ObjectType_Static, ObjectType_Dynamic, - + //Etc ObjectType_NUM_OF_TYPES, ObjectType_Unknown = -1, }; + enum UsePhysics + { + UsePhysics_UseFullPhysics, + UsePhysics_IgnoreGravity, + UsePhysics_IgnorePhysics, + + UsePhysics_Count, + UsePhysics_Unknown = -1, + }; + + //Should this be moved somewhere else? + enum GameMode + { + GameMode_FreeForAll, + GameMode_TeamDeathMatch, + //Etc + + GameMode_Count, + GameMode_Unknown = -1, + }; + + + /************************************ + Structs + *************************************/ + + struct FormatVersion + { + int formatVersionMajor; + int formatVersionMinor; + + bool operator ==(const FormatVersion& obj) + { + return (this->formatVersionMajor != obj.formatVersionMajor && this->formatVersionMinor != obj.formatVersionMinor); + } + + bool operator !=(const FormatVersion& obj) + { + return !(*this == obj); + } + }; + struct ObjectTypeHeader { ObjectType typeID; - }; - struct ObjectHeader : public ObjectTypeHeader + struct PhysicsObject + { + float mass; + float elasticity; + float frictionCoeffStatic; + float frictionCoeffDynamic; + float inertiaTensor[16]; + UsePhysics usePhysics; + }; + + struct LevelMetaData : ObjectTypeHeader + { + std::string levelName; + FormatVersion levelVersion; + std::string levelDescription; + std::string levelAuthor; + int maxNumberOfPlayer; + float worldSize; + int overviewPictureID; + std::vector gameModesSupported; + }; + + struct ObjectHeader : public PhysicsObject, public ObjectTypeHeader { //Model, int ModelID; From d2ebad74452c02865ada3104056bc6959c3dde00 Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Tue, 28 Jan 2014 11:29:35 +0100 Subject: [PATCH 52/76] GL - Fixed ParseFunction so now we only have 1 + specialcases functions instead of 1 function for every case --- Code/Dokumentation/LevelLoader.uxf | 350 +++++++++--------- Code/Game/GameLogic/GameLogic.vcxproj | 18 +- .../{ => LevelLoader}/LevelLoader.cpp | 0 .../GameLogic/{ => LevelLoader}/LevelLoader.h | 0 .../{ => LevelLoader}/LevelParser.cpp | 1 - .../GameLogic/{ => LevelLoader}/LevelParser.h | 0 .../GameLogic/{ => LevelLoader}/Loader.cpp | 2 + .../Game/GameLogic/{ => LevelLoader}/Loader.h | 2 +- .../{ => LevelLoader}/ObjectDefines.h | 0 .../GameLogic/LevelLoader/ParseFunctions.cpp | 55 +++ .../GameLogic/LevelLoader/ParseFunctions.h | 19 + Code/Game/GameLogic/ParseFunctions.cpp | 33 -- Code/Game/GameLogic/ParseFunctions.h | 19 - 13 files changed, 261 insertions(+), 238 deletions(-) rename Code/Game/GameLogic/{ => LevelLoader}/LevelLoader.cpp (100%) rename Code/Game/GameLogic/{ => LevelLoader}/LevelLoader.h (100%) rename Code/Game/GameLogic/{ => LevelLoader}/LevelParser.cpp (99%) rename Code/Game/GameLogic/{ => LevelLoader}/LevelParser.h (100%) rename Code/Game/GameLogic/{ => LevelLoader}/Loader.cpp (94%) rename Code/Game/GameLogic/{ => LevelLoader}/Loader.h (88%) rename Code/Game/GameLogic/{ => LevelLoader}/ObjectDefines.h (100%) create mode 100644 Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp create mode 100644 Code/Game/GameLogic/LevelLoader/ParseFunctions.h delete mode 100644 Code/Game/GameLogic/ParseFunctions.cpp delete mode 100644 Code/Game/GameLogic/ParseFunctions.h diff --git a/Code/Dokumentation/LevelLoader.uxf b/Code/Dokumentation/LevelLoader.uxf index 90dcb0f7..b5cef8f0 100644 --- a/Code/Dokumentation/LevelLoader.uxf +++ b/Code/Dokumentation/LevelLoader.uxf @@ -4,8 +4,20 @@ com.umlet.element.Relation - 640 - 256 + 632 + 232 + 88 + 176 + + lt=. +<Uses + 24;24;24;64;72;64;72;160 + + + com.umlet.element.Relation + + 136 + 232 40 168 @@ -15,20 +27,111 @@ com.umlet.element.Relation - 840 - 256 - 88 + 448 + 496 + 40 + 88 + + lt=<<<<- + 24;72;24;24 + + + com.umlet.element.Relation + + 432 + 232 + 40 168 - lt=. -<Uses - 24;24;24;64;72;64;72;152 + lt=<<. + 24;24;24;152 com.umlet.element.Class + + 608 + 216 + 128 + 40 + + Resource Loader +<<Dennis>><<Singleton> + + + + com.umlet.element.Class + + 232 + 112 + 128 + 40 + + GameLogic +<<Erik>> + + + + com.umlet.element.Class + + 120 + 232 + 80 + 24 + + Defines + + + + com.umlet.element.Class + + 352 + 568 + 232 + 136 + + <<Interface>> +LevelParser +-- +Functions: +vector<struct> Parse(); +- +Privates: +enum headerType; +const int FileHeaderSize; +const int FileVersion; + + + + + com.umlet.element.Relation + + 336 + 88 + 136 + 160 + + lt=lt=->>>> +m1=1..1 +m2=1..1 +Uses> + 24;40;80;40;120;40;120;144 + + + com.umlet.element.Relation 560 - 408 + 496 + 136 + 104 + + lt=<<<<- + 120;24;120;88;24;88 + + + com.umlet.element.Class + + 352 + 384 232 136 @@ -45,54 +148,79 @@ Privates: - com.umlet.element.Class + com.umlet.element.Relation - 440 - 136 - 128 - 40 + 560 + 600 + 176 + 56 - GameLogic -<<Erik>> - + lt=- +m1=1..1 +m2=1..1 +Uses> + 24;40;160;40 com.umlet.element.Package - 552 - 368 + 344 + 344 584 368 LevelLoader + + com.umlet.element.Relation + + 472 + 200 + 152 + 56 + + lt=- +m1=1..1 +m2=1..1 +Uses> + 24;40;136;40 + com.umlet.element.Class - 560 - 592 - 232 - 136 + 416 + 232 + 80 + 24 - <<Interface>> -Parser --- -Functions: -vector<struct> Parse(); -- -Privates: -enum headerType; -const int FileHeaderSize; -const int FileVersion; + LevelLoader com.umlet.element.Class - 928 - 608 + 48 + 384 + 232 + 104 + + ObjectDefines.h +<<Header file>> +-- +Enum ObjectType(static, dynamic, specials); +. +Struct static; +Struct dynamic; +Struct specials + + + + com.umlet.element.Class + + 720 + 584 200 120 @@ -103,23 +231,23 @@ functions for creating the right structs - com.umlet.element.Relation + com.umlet.element.Package - 768 - 520 - 136 - 104 + 40 + 344 + 248 + 160 - lt=<<<<- - 120;24;120;88;24;88 + Defines + com.umlet.element.Class - 800 - 408 + 592 + 392 208 - 136 + 128 <<Interface>> Loader @@ -135,67 +263,8 @@ Privates: com.umlet.element.Relation - 656 - 520 - 40 - 88 - - lt=<<<<- - 24;72;24;24 - - - com.umlet.element.Class - - 328 - 256 - 80 - 24 - - Defines - - - - com.umlet.element.Relation - - 680 - 224 - 152 - 56 - - lt=- -m1=1..1 -m2=1..1 -Uses> - 24;40;136;40 - - - com.umlet.element.Class - - 624 - 256 - 80 - 24 - - LevelLoader - - - - - com.umlet.element.Relation - - 344 - 256 - 40 - 168 - - lt=<<. - 24;24;24;152 - - - com.umlet.element.Relation - - 384 - 224 + 176 + 200 256 56 @@ -205,73 +274,4 @@ m2=1..1 <Knows about 240;40;24;40 - - com.umlet.element.Relation - - 544 - 112 - 136 - 160 - - lt=lt=->>>> -m1=1..1 -m2=1..1 -Uses> - 24;40;80;40;120;40;120;144 - - - com.umlet.element.Relation - - 768 - 624 - 176 - 56 - - lt=- -m1=1..1 -m2=1..1 -Uses> - 24;40;160;40 - - - com.umlet.element.Package - - 248 - 368 - 248 - 160 - - Defines - - - - com.umlet.element.Class - - 256 - 408 - 232 - 104 - - Defines.h -<<Header file>> --- -Enum ObjectType(static, dynamic, specials); -. -Struct static; -Struct dynamic; -Struct specials - - - - com.umlet.element.Class - - 816 - 240 - 128 - 40 - - Resource Loader -<<Dennis>><<Singleton> - - diff --git a/Code/Game/GameLogic/GameLogic.vcxproj b/Code/Game/GameLogic/GameLogic.vcxproj index df1d09aa..809c82b5 100644 --- a/Code/Game/GameLogic/GameLogic.vcxproj +++ b/Code/Game/GameLogic/GameLogic.vcxproj @@ -184,12 +184,12 @@ - - + + - - - + + + @@ -207,11 +207,11 @@ - - - + + + - + diff --git a/Code/Game/GameLogic/LevelLoader.cpp b/Code/Game/GameLogic/LevelLoader/LevelLoader.cpp similarity index 100% rename from Code/Game/GameLogic/LevelLoader.cpp rename to Code/Game/GameLogic/LevelLoader/LevelLoader.cpp diff --git a/Code/Game/GameLogic/LevelLoader.h b/Code/Game/GameLogic/LevelLoader/LevelLoader.h similarity index 100% rename from Code/Game/GameLogic/LevelLoader.h rename to Code/Game/GameLogic/LevelLoader/LevelLoader.h diff --git a/Code/Game/GameLogic/LevelParser.cpp b/Code/Game/GameLogic/LevelLoader/LevelParser.cpp similarity index 99% rename from Code/Game/GameLogic/LevelParser.cpp rename to Code/Game/GameLogic/LevelLoader/LevelParser.cpp index a957b88a..08c4da7e 100644 --- a/Code/Game/GameLogic/LevelParser.cpp +++ b/Code/Game/GameLogic/LevelLoader/LevelParser.cpp @@ -15,7 +15,6 @@ LevelParser::~LevelParser() { } -// std::vector LevelParser::Parse(std::string filename) { int stringSize = 0; diff --git a/Code/Game/GameLogic/LevelParser.h b/Code/Game/GameLogic/LevelLoader/LevelParser.h similarity index 100% rename from Code/Game/GameLogic/LevelParser.h rename to Code/Game/GameLogic/LevelLoader/LevelParser.h diff --git a/Code/Game/GameLogic/Loader.cpp b/Code/Game/GameLogic/LevelLoader/Loader.cpp similarity index 94% rename from Code/Game/GameLogic/Loader.cpp rename to Code/Game/GameLogic/LevelLoader/Loader.cpp index 0afb48de..26818202 100644 --- a/Code/Game/GameLogic/Loader.cpp +++ b/Code/Game/GameLogic/LevelLoader/Loader.cpp @@ -15,6 +15,8 @@ unsigned char* Loader::LoadFile(std::string fileName, int &size) //convert from wstring to wchar then loads the file unsigned char* buffer = (unsigned char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); + + //gets the size of the char buffer. size = OysterResource::GetResourceSize(buffer); return buffer; } \ No newline at end of file diff --git a/Code/Game/GameLogic/Loader.h b/Code/Game/GameLogic/LevelLoader/Loader.h similarity index 88% rename from Code/Game/GameLogic/Loader.h rename to Code/Game/GameLogic/LevelLoader/Loader.h index d77921c5..2d605169 100644 --- a/Code/Game/GameLogic/Loader.h +++ b/Code/Game/GameLogic/LevelLoader/Loader.h @@ -5,7 +5,7 @@ #ifndef LOADER_H #define LOADER_H -#include "Resource\OysterResource.h" +#include "..\Misc\Resource\OysterResource.h" #include namespace GameLogic diff --git a/Code/Game/GameLogic/ObjectDefines.h b/Code/Game/GameLogic/LevelLoader/ObjectDefines.h similarity index 100% rename from Code/Game/GameLogic/ObjectDefines.h rename to Code/Game/GameLogic/LevelLoader/ObjectDefines.h diff --git a/Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp b/Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp new file mode 100644 index 00000000..abca143c --- /dev/null +++ b/Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp @@ -0,0 +1,55 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#include "ParseFunctions.h" +#include "../../../Misc/Packing/Packing.h" +#include + +using namespace Oyster::Packing; +using namespace GameLogic::LevelFileLoader; +using namespace GameLogic; +using namespace std; + +namespace GameLogic +{ + namespace LevelFileLoader + { + void ParseObject(unsigned char* buffer, void* header, int size) + { + memcpy(header, buffer, size); + } + + void ParseLevelMetaData(unsigned char* buffer, struct LevelMetaData &header) + { + int start = 0; + int tempSize; + memcpy(&header.typeID, &buffer[start], 4); + start += 4; + memcpy(&tempSize , &buffer[start], 4); + start += 4; + memcpy(&header.levelName, &buffer[start], tempSize); + start += tempSize; + memcpy(&header.levelVersion, &buffer[start], 8) + start += 8; + memcpy(&tempSize, &buffer[start], 4); + start +=4; + memcpy(&header.description, &buffer[start], tempSize); + start += tempSize; + memcpy(&tempSize, &buffer[start], 4); + start += 4; + memcpy(&header.author, &buffer[start], tempSize); + start += tempSize; + memcpy(&header.nrOfPlayers, &buffer[start], 4); + start += 4; + memcpy(&header.worldSize, &buffer[start], 4); + start += 4; + memcpy(&header.map, &buffer[start], 4); + start += 4; + memcpy(&tempSize, &buffer[start], 4); + start += 4; + memcpy(&header.gameModes, &buffer[start], 4 * tempSize); + start += tempSize; + } + } +} \ No newline at end of file diff --git a/Code/Game/GameLogic/LevelLoader/ParseFunctions.h b/Code/Game/GameLogic/LevelLoader/ParseFunctions.h new file mode 100644 index 00000000..7a6ab93a --- /dev/null +++ b/Code/Game/GameLogic/LevelLoader/ParseFunctions.h @@ -0,0 +1,19 @@ +////////////////////////////////// +// Created by Sam Svensson 2013 // +////////////////////////////////// + +#ifndef PARSERFUNCTIONS_H +#define PARSERFUNCTIONS_H +#include "ObjectDefines.h" + +namespace GameLogic +{ + namespace LevelFileLoader + { + void ParseObject(unsigned char* buffer, void* header, int size); + void ParseLevelMetaData(unsigned char* buffer, struct ObjectTypeHeader &header); + } +} + + +#endif \ No newline at end of file diff --git a/Code/Game/GameLogic/ParseFunctions.cpp b/Code/Game/GameLogic/ParseFunctions.cpp deleted file mode 100644 index 64405ff5..00000000 --- a/Code/Game/GameLogic/ParseFunctions.cpp +++ /dev/null @@ -1,33 +0,0 @@ -////////////////////////////////// -// Created by Sam Svensson 2013 // -////////////////////////////////// - -#include "ParseFunctions.h" -#include "../../Misc/Packing/Packing.h" -#include - -using namespace Oyster::Packing; -using namespace GameLogic::LevelFileLoader; -using namespace GameLogic; -using namespace std; - -namespace GameLogic -{ - namespace LevelFileLoader - { - ObjectTypeHeader ParseObjectTypeHeader(unsigned char* buffer) - { - struct ObjectTypeHeader header; - memcpy(&header, buffer, sizeof(ObjectTypeHeader)); - return header; - } - - ObjectHeader ParseObjectHeader (unsigned char* buffer) - { - struct ObjectHeader header; - memcpy(&header, buffer, sizeof(ObjectHeader)); - - return header; - } - } -} \ No newline at end of file diff --git a/Code/Game/GameLogic/ParseFunctions.h b/Code/Game/GameLogic/ParseFunctions.h deleted file mode 100644 index 5638f419..00000000 --- a/Code/Game/GameLogic/ParseFunctions.h +++ /dev/null @@ -1,19 +0,0 @@ -////////////////////////////////// -// Created by Sam Svensson 2013 // -////////////////////////////////// - -#ifndef PARSERFUNCTIONS_H -#define PARSERFUNCTIONS_H -#include "ObjectDefines.h" - -namespace GameLogic -{ - namespace LevelFileLoader - { - ObjectTypeHeader ParseObjectTypeHeader(unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. - ObjectHeader ParseObjectHeader (unsigned char* buffer); //send in a char buffer, this function will seperate it and then return the right struct with the values. - } -} - - -#endif \ No newline at end of file From 8435a2c9708335799cf3bf9d9532cf5f7fbd3261 Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Tue, 28 Jan 2014 11:31:11 +0100 Subject: [PATCH 53/76] Formula fix TangentialLinearMomentum(..) --- Code/OysterPhysics3D/OysterPhysics3D.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Code/OysterPhysics3D/OysterPhysics3D.h b/Code/OysterPhysics3D/OysterPhysics3D.h index f814ff46..c6eca8e8 100644 --- a/Code/OysterPhysics3D/OysterPhysics3D.h +++ b/Code/OysterPhysics3D/OysterPhysics3D.h @@ -59,11 +59,20 @@ namespace Oyster { namespace Physics3D /****************************************************************** * Returns the world tangential momentum at worldPos, of a mass in rotation. + * G = ( H x r ) / ( |H| * |r|^2 ) <-> H = r x G * @todo TODO: improve doc ******************************************************************/ inline ::Oyster::Math::Float4 TangentialLinearMomentum( const ::Oyster::Math::Float4 &angularMomentum, const ::Oyster::Math::Float4 &worldOffset ) { - return ::Oyster::Math::Float4( angularMomentum.xyz.Cross(worldOffset.xyz), 0.0f ) /= worldOffset.Dot( worldOffset ); + ::Oyster::Math::Float magnitudeH_squared = angularMomentum.Dot( angularMomentum ); + if( magnitudeH_squared > 0 ) + { + return ::Oyster::Math::Float4( angularMomentum.xyz.Cross(worldOffset.xyz), 0.0f ) /= ( (::Oyster::Math::Float)::std::sqrt(magnitudeH_squared) * worldOffset.Dot( worldOffset ) ); + } + else + { + return ::Oyster::Math::Float4::null; + } } /****************************************************************** From 78706f992980ef1c783b75828cc44673cecbe8b2 Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Tue, 28 Jan 2014 11:43:24 +0100 Subject: [PATCH 54/76] Revert "Formula fix" This reverts commit 8435a2c9708335799cf3bf9d9532cf5f7fbd3261. --- Code/OysterPhysics3D/OysterPhysics3D.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Code/OysterPhysics3D/OysterPhysics3D.h b/Code/OysterPhysics3D/OysterPhysics3D.h index c6eca8e8..f814ff46 100644 --- a/Code/OysterPhysics3D/OysterPhysics3D.h +++ b/Code/OysterPhysics3D/OysterPhysics3D.h @@ -59,20 +59,11 @@ namespace Oyster { namespace Physics3D /****************************************************************** * Returns the world tangential momentum at worldPos, of a mass in rotation. - * G = ( H x r ) / ( |H| * |r|^2 ) <-> H = r x G * @todo TODO: improve doc ******************************************************************/ inline ::Oyster::Math::Float4 TangentialLinearMomentum( const ::Oyster::Math::Float4 &angularMomentum, const ::Oyster::Math::Float4 &worldOffset ) { - ::Oyster::Math::Float magnitudeH_squared = angularMomentum.Dot( angularMomentum ); - if( magnitudeH_squared > 0 ) - { - return ::Oyster::Math::Float4( angularMomentum.xyz.Cross(worldOffset.xyz), 0.0f ) /= ( (::Oyster::Math::Float)::std::sqrt(magnitudeH_squared) * worldOffset.Dot( worldOffset ) ); - } - else - { - return ::Oyster::Math::Float4::null; - } + return ::Oyster::Math::Float4( angularMomentum.xyz.Cross(worldOffset.xyz), 0.0f ) /= worldOffset.Dot( worldOffset ); } /****************************************************************** From 15ae3675b4f936f1c312751de67b44621a0d2a41 Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Tue, 28 Jan 2014 13:39:15 +0100 Subject: [PATCH 55/76] Updated UML with functions --- Code/Dokumentation/Physics_Sprint3.uxf | 756 +++++++++++++------------ 1 file changed, 395 insertions(+), 361 deletions(-) diff --git a/Code/Dokumentation/Physics_Sprint3.uxf b/Code/Dokumentation/Physics_Sprint3.uxf index 0462e35c..6b74de3e 100644 --- a/Code/Dokumentation/Physics_Sprint3.uxf +++ b/Code/Dokumentation/Physics_Sprint3.uxf @@ -6,7 +6,7 @@ com.umlet.element.Class 357 - 609 + 518 252 406 @@ -34,7 +34,7 @@ Forcefield( .. ) : Float com.umlet.element.Class 364 - 819 + 728 238 189 @@ -60,7 +60,7 @@ Cylinder( .. ) : Matrix com.umlet.element.Class 616 - 609 + 518 210 35 @@ -73,7 +73,7 @@ Update_LeapFrog( deltatime : Float ) : void com.umlet.element.Class 616 - 672 + 581 210 203 @@ -101,7 +101,7 @@ CalculateAngularMomentum( .. ) : Vector com.umlet.element.Class 728 - 938 + 847 98 21 @@ -112,7 +112,7 @@ CalculateAngularMomentum( .. ) : Vector com.umlet.element.Class 728 - 966 + 875 98 21 @@ -123,7 +123,7 @@ CalculateAngularMomentum( .. ) : Vector com.umlet.element.Class 728 - 994 + 903 98 21 @@ -134,7 +134,7 @@ CalculateAngularMomentum( .. ) : Vector com.umlet.element.Relation 644 - 623 + 532 132 62 @@ -146,7 +146,7 @@ CalculateAngularMomentum( .. ) : Vector com.umlet.element.Class 350 - 574 + 483 483 448 @@ -157,31 +157,30 @@ bg=green - - - com.umlet.element.Class - - 560 - 511 - 161 - 49 - - Constant : <<namespace>> + + com.umlet.element.Class + + 560 + 420 + 161 + 49 + + Constant : <<namespace>> <<extern>> -- gravity_constant : const Float - - - - com.umlet.element.Class - - 224 - 476 - 329 - 84 - - Default : <<namespace>> + + + + com.umlet.element.Class + + 224 + 385 + 329 + 84 + + Default : <<namespace>> <<intern>> -- EventAction_Destruction : <<PhysicsAPI::EventAction_Destruction>> @@ -189,45 +188,61 @@ EventAction_Collision : <<ICustomBody::EventAction_Collision>> EventAction_CollisionResponse : <<ICustomBody::EventAction_Collision>> EventAction_Move : <<ICustomBody::EventAction_Collision>> - - - - com.umlet.element.Class - - 224 - 161 - 224 - 35 - - API : <<interface>> + + + + com.umlet.element.Class + + 245 + 63 + 224 + 196 + + API : <<interface>> -- {innerclass EventAction_Destruction : <<FunctionPointer>> -innerclass} - - - - com.umlet.element.Class - - 259 - 217 - 133 - 28 - - API_Impl : <<class>> +innerclass} + +Init( .. ) : void +SetFrameTimeLength( .. ) : void +SetGravityConstant( .. ) : void +SetSubscription( .. ) : void +Update() : void +IsInLimbo( .. ) : bool +MoveToLimbo( .. ) : void +ReleaseFromLimbo( .. ) : void +AddObject( .. ) : void +ExtractObject( .. ) : ICustomBody* +DestroyObject( .. ) : void +AddGravity( .. ) : void +RemoveGravity( .. ) : void +ApplyEffect( .. ) : void +CreateRigidBody( .. ) : ICustomBody* + + + + com.umlet.element.Class + + 273 + 280 + 133 + 28 + + API_Impl : <<class>> -- <<uses>> OctTree : class - - - - com.umlet.element.Class - - 455 - 161 - 280 - 84 - - ICustomBody : <<interface>> + + + + com.umlet.element.Class + + 476 + 63 + 280 + 287 + + ICustomBody : <<interface>> {innerclass SubscriptMessage : Enum @@ -243,58 +258,78 @@ innerclass} {innerclass EventAction_Move : <<Subscription : void>> -innerclass} - - - - com.umlet.element.Class - - 259 - 252 - 175 - 35 - - SimpleRigidBody : <<class>> +innerclass} + +Clone() : ICustomBody* +CallSubscription_Collision( .. ) : SubscriptMessage +CallSubscription_CollisionResponse( .. ) : void +CallSubscription_Move() : void +GetState( .. ) : State +SetState( .. ) : void +IsAffectedByGravity() : bool +Intersects( .. ) : bool +GetBoundingSphere : Sphere +GetNormalAt( .. ) : Vector +GetGravityNormal( .. ) : Vector +GetCustomTag() : void* +Update( .. ) : UpdateState +Predict( .. ) : void +SetScene( .. ) : void +SetSubscription( .. ) : void +SetGravity( .. ) : void +SetGravityNormal( .. ) : void +SetCustomTag( .. ) : void + + + + com.umlet.element.Class + + 273 + 315 + 175 + 28 + + SimpleRigidBody : <<class>> -- <<uses>> Physics3D::RigidBody : struct - - - - com.umlet.element.Class - - 259 - 294 - 175 - 35 - - SphericalRigidBody : <<class>> + + + + com.umlet.element.Class + + 273 + 350 + 175 + 28 + + SphericalRigidBody : <<class>> -- <<uses>> Physics3D::RigidBody : struct - - - - com.umlet.element.Class - - 21 - 364 - 196 - 196 - - Formula : <<namespace>> + + + + com.umlet.element.Class + + 21 + 273 + 196 + 196 + + Formula : <<namespace>> <<intern>> -- - - - - com.umlet.element.Class - - 28 - 399 - 175 - 84 - - MomentOfInertia : <<namespace>> + + + + com.umlet.element.Class + + 28 + 308 + 175 + 84 + + MomentOfInertia : <<namespace>> <<intern>> -- CreateSphereMatrix( .. ) : Matrix @@ -302,138 +337,138 @@ CreateHollowSphereMatrix( .. ) : Matrix CreateCuboidMatrix( .. ) : Matrix CreateCylinderMatrix( .. ) : Matrix CreateRodMatrix( .. ) : Matrix - - - - com.umlet.element.Class - - 28 - 497 - 175 - 56 - - CollisionResponse : <<namespace>> + + + + com.umlet.element.Class + + 28 + 406 + 175 + 56 + + CollisionResponse : <<namespace>> <<intern>> -- Bounce( .. ) : Float Friction( .. ) : Vector - - - - com.umlet.element.Relation - - 294 - 175 - 34 - 55 - - lt=<<. - 21;21;21;42 - - - com.umlet.element.Relation - - 413 - 224 - 90 - 55 - - lt=<<. - 77;21;77;42;21;42 - - - com.umlet.element.Class - - 525 - 350 - 203 - 42 - - OctTree : <<class>> + + + + com.umlet.element.Relation + + 308 + 238 + 34 + 55 + + lt=<<. + 21;21;21;42 + + + com.umlet.element.Relation + + 427 + 308 + 97 + 76 + + lt=<<. + 84;42;84;63;35;63;35;21;21;21 + + + com.umlet.element.Class + + 560 + 371 + 203 + 42 + + OctTree : <<class>> -- <<uses>> Collision3D::Sphere : struct <<uses>> Collision3D::BoxAxisAligned : struct - - - - com.umlet.element.Relation - - 546 - 224 - 118 - 139 - - lt=<- -m2= <<uses>> - 105;21;105;112;28;112;28;126 - - - com.umlet.element.Relation - - 413 - 245 - 90 - 69 - - lt=. - 77;21;77;56;21;56 - - - com.umlet.element.Class - - 21 - 161 - 182 - 196 - - Struct : <<namespace>> + + + + com.umlet.element.Relation + + 518 + 329 + 100 + 55 + + lt=<- + <<uses>> + 56;21;56;42 + + + com.umlet.element.Relation + + 427 + 329 + 97 + 55 + + lt=. + 84;21;84;42;21;42 + + + com.umlet.element.Class + + 21 + 63 + 182 + 196 + + Struct : <<namespace>> <<extern>> -- - - - - com.umlet.element.Class - - 28 - 301 - 168 - 14 - - SimpleBodyDescription : struct - - - - com.umlet.element.Class - - 28 - 329 - 168 - 14 - - SimpleSphericalDescription : struct - - - - com.umlet.element.Class - - 28 - 273 - 168 - 14 - - CustomBodyState : struct - - - - com.umlet.element.Class - - 28 - 189 - 168 - 63 - - Gravity : struct + + + + com.umlet.element.Class + + 28 + 203 + 168 + 14 + + SimpleBodyDescription : struct + + + + com.umlet.element.Class + + 28 + 231 + 168 + 14 + + SimpleSphericalDescription : struct + + + + com.umlet.element.Class + + 28 + 175 + 168 + 14 + + CustomBodyState : struct + + + + com.umlet.element.Class + + 28 + 91 + 168 + 63 + + Gravity : struct {innerclass GravityWell innerclass} @@ -443,98 +478,97 @@ innerclass} {innerclass GravityDirectedField innerclass} - - - - com.umlet.element.Relation - - 182 - 189 - 90 - 49 - - lt=<- + + + + com.umlet.element.Relation + + 182 + 105 + 104 + 195 + + lt=<- + <<uses>> + 21;21;56;21;56;168;91;168;91;182 + + + com.umlet.element.Relation + + 182 + 161 + 104 + 139 + + lt=<- + 21;21;56;21;56;112;91;112;91;126 + + + com.umlet.element.Relation + + 385 + 252 + 104 + 49 + + lt=<- <<uses>> - 21;35;77;35 - - - com.umlet.element.Relation - - 182 - 203 - 90 - 83 - - lt=<- - 21;70;42;70;42;21;77;21 - - - com.umlet.element.Relation - - 371 - 189 - 97 - 49 - - lt=<- -<<uses>> - 84;35;21;35 - - - com.umlet.element.Relation - - 182 - 266 - 90 - 49 - - lt=<- -<<uses>> - 21;35;77;35 - - - com.umlet.element.Relation - - 182 - 280 - 90 - 62 - - lt=<- - 21;49;42;49;42;21;77;21 - - - com.umlet.element.Relation - - 231 - 238 - 41 - 76 - - lt=- - 28;21;21;21;21;63 - - - com.umlet.element.Class - - 14 - 126 - 728 - 441 - - Physics : <<namespace>> + 91;35;21;35 + + + com.umlet.element.Relation + + 182 + 189 + 104 + 181 + + lt=<- + 21;21;42;21;42;168;91;168 + + + com.umlet.element.Relation + + 175 + 217 + 111 + 153 + + lt=<- + <<uses>> + 28;21;49;21;49;140;98;140 + + + com.umlet.element.Relation + + 231 + 301 + 55 + 69 + + lt=- + 42;21;42;21;21;21;21;56 + + + com.umlet.element.Class + + 14 + 35 + 756 + 441 + + Physics : <<namespace>> <<extern>> bg=green -- - - - + + com.umlet.element.Class 0 - 91 + 0 847 945 @@ -551,7 +585,7 @@ bg=orange com.umlet.element.Class 175 - 637 + 546 126 21 @@ -562,7 +596,7 @@ bg=orange com.umlet.element.Class 175 - 609 + 518 126 21 @@ -573,7 +607,7 @@ bg=orange com.umlet.element.Class 175 - 665 + 574 126 21 @@ -584,7 +618,7 @@ bg=orange com.umlet.element.Class 175 - 693 + 602 126 21 @@ -595,7 +629,7 @@ bg=orange com.umlet.element.Class 175 - 721 + 630 126 21 @@ -606,7 +640,7 @@ bg=orange com.umlet.element.Class 175 - 861 + 770 126 21 @@ -617,7 +651,7 @@ bg=orange com.umlet.element.Class 175 - 749 + 658 126 21 @@ -628,7 +662,7 @@ bg=orange com.umlet.element.Class 175 - 791 + 700 126 21 @@ -639,7 +673,7 @@ bg=orange com.umlet.element.Class 175 - 819 + 728 126 21 @@ -650,7 +684,7 @@ bg=orange com.umlet.element.Class 175 - 889 + 798 126 21 @@ -661,7 +695,7 @@ bg=orange com.umlet.element.Class 21 - 609 + 518 126 70 @@ -677,7 +711,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 595 + 504 55 321 @@ -688,7 +722,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 812 + 721 55 34 @@ -699,7 +733,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 784 + 693 55 34 @@ -710,7 +744,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 742 + 651 55 34 @@ -721,7 +755,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 854 + 763 55 34 @@ -732,7 +766,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 714 + 623 55 34 @@ -743,7 +777,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 686 + 595 55 34 @@ -754,7 +788,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 658 + 567 55 34 @@ -765,7 +799,7 @@ Contain( .. ) : bool com.umlet.element.Relation 280 - 630 + 539 55 34 @@ -776,7 +810,7 @@ Contain( .. ) : bool com.umlet.element.Relation 196 - 749 + 658 83 55 @@ -788,7 +822,7 @@ Contain( .. ) : bool com.umlet.element.Relation 35 - 658 + 567 153 84 @@ -800,7 +834,7 @@ Contain( .. ) : bool com.umlet.element.Relation 133 - 595 + 504 55 286 @@ -811,7 +845,7 @@ Contain( .. ) : bool com.umlet.element.Relation 196 - 819 + 728 83 55 @@ -823,7 +857,7 @@ Contain( .. ) : bool com.umlet.element.Relation 133 - 805 + 714 55 34 @@ -834,7 +868,7 @@ Contain( .. ) : bool com.umlet.element.Relation 133 - 735 + 644 55 34 @@ -845,7 +879,7 @@ Contain( .. ) : bool com.umlet.element.Relation 133 - 679 + 588 55 34 @@ -856,7 +890,7 @@ Contain( .. ) : bool com.umlet.element.Relation 133 - 651 + 560 55 34 @@ -867,7 +901,7 @@ Contain( .. ) : bool com.umlet.element.Relation 133 - 623 + 532 55 34 @@ -878,7 +912,7 @@ Contain( .. ) : bool com.umlet.element.Class 14 - 574 + 483 329 343 @@ -894,7 +928,7 @@ bg=green com.umlet.element.Class 938 - 168 + 77 98 28 @@ -907,7 +941,7 @@ bg=green com.umlet.element.Class 896 - 266 + 175 42 21 @@ -919,7 +953,7 @@ bg=green com.umlet.element.Class 952 - 266 + 175 70 21 @@ -931,7 +965,7 @@ bg=green com.umlet.element.Relation 896 - 189 + 98 34 90 @@ -942,9 +976,9 @@ bg=green com.umlet.element.Relation 966 - 189 - 34 - 90 + 98 + 28 + 84 lt=()) 21;77;21;21 @@ -953,7 +987,7 @@ bg=green com.umlet.element.Class 896 - 119 + 28 161 28 @@ -966,7 +1000,7 @@ bg=green com.umlet.element.Relation 896 - 126 + 35 48 97 @@ -977,7 +1011,7 @@ bg=green com.umlet.element.Class 889 - 91 + 0 175 133 @@ -990,7 +1024,7 @@ bg=blue com.umlet.element.Relation 910 - 175 + 84 90 48 @@ -1001,9 +1035,9 @@ bg=blue com.umlet.element.Relation 1036 - 112 - 111 - 34 + 21 + 105 + 28 lt=<() r1=provide @@ -1013,9 +1047,9 @@ r1=provide com.umlet.element.Relation 1015 - 161 - 132 - 34 + 70 + 126 + 28 lt=<() r1=provide From a56fcbcb9b4ab54699929493665e645f88ed4d37 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Tue, 28 Jan 2014 15:04:25 +0100 Subject: [PATCH 56/76] GL - can send object position from level objects, gravity and frustrum problems. --- Code/DanBias.sln | 14 ++++++++ .../DanBiasGame/GameClientState/GameState.cpp | 16 +++++---- .../DanBiasServer/GameSession/GameSession.h | 2 +- .../GameSession/GameSession_Events.cpp | 20 +++++++---- .../GameSession/GameSession_General.cpp | 2 ++ .../GameSession/GameSession_Logic.cpp | 2 +- Code/Game/GameLogic/AttatchmentMassDriver.cpp | 5 ++- Code/Game/GameLogic/CollisionManager.cpp | 2 +- Code/Game/GameLogic/Game.cpp | 1 + Code/Game/GameLogic/Game.h | 10 +++--- Code/Game/GameLogic/GameAPI.h | 5 +-- Code/Game/GameLogic/Game_LevelData.cpp | 15 +++++--- Code/Game/GameLogic/Game_PlayerData.cpp | 8 ++--- Code/Game/GameLogic/Level.cpp | 36 +++++++++++++------ Code/Game/GameLogic/Level.h | 4 +++ Code/Game/GameLogic/Object.cpp | 14 +++++++- Code/Game/GameLogic/Object.h | 8 ++++- Code/Game/GameLogic/StaticObject.cpp | 4 +-- 18 files changed, 120 insertions(+), 48 deletions(-) diff --git a/Code/DanBias.sln b/Code/DanBias.sln index 5fc5d0db..bc91fdcf 100644 --- a/Code/DanBias.sln +++ b/Code/DanBias.sln @@ -46,6 +46,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasServerLauncher", "Ga EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Physics lab", "Physics lab\Physics lab.vcxproj", "{5128BD77-6472-4C4A-BE6F-724AD0E589C2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aDanBiasGameLauncher", "Game\aDanBiasGameLauncher\aDanBiasGameLauncher.vcxproj", "{666FEA52-975F-41CD-B224-B19AF3C0ABBA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Mixed Platforms = Debug|Mixed Platforms @@ -284,6 +286,18 @@ Global {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|Win32.Build.0 = Release|Win32 {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|x64.ActiveCfg = Release|x64 {5128BD77-6472-4C4A-BE6F-724AD0E589C2}.Release|x64.Build.0 = Release|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Win32.ActiveCfg = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|Win32.Build.0 = Debug|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|x64.ActiveCfg = Debug|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Debug|x64.Build.0 = Debug|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Mixed Platforms.Build.0 = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.ActiveCfg = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.Build.0 = Release|Win32 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.ActiveCfg = Release|x64 + {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index 73f3ade6..e0021bce 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -93,14 +93,14 @@ bool GameState::LoadModels(std::wstring mapFile) modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; modelData.id ++; - obj = new C_DynamicObj(); + obj = new C_Player(); privData->object.push_back(obj); privData->object[privData->object.size() -1 ]->Init(modelData); - translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,-2,-2)); + /*translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,-2,-2)); modelData.world = modelData.world * translate; modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; - modelData.id ++; + modelData.id ++;*/ translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(0,0,0)); Oyster::Math3D::Float4x4 scale = Oyster::Math3D::Float4x4::identity; @@ -111,7 +111,7 @@ bool GameState::LoadModels(std::wstring mapFile) modelData.modelPath = L"ball.dan"; modelData.id ++; - obj = new C_DynamicObj(); + obj = new C_Player(); privData->object.push_back(obj); privData->object[privData->object.size() -1 ]->Init(modelData); @@ -328,9 +328,11 @@ void GameState::Protocol( ObjPos* pos ) //camera->setRight((Oyster::Math::Float3(world[0], world[1], world[2]))); //camera->setUp((Oyster::Math::Float3(world[4], world[5], world[6]))); //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); - camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); - camera->UpdateViewMatrix(); - + if(i == 0) + { + camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); + camera->UpdateViewMatrix(); + } } } } diff --git a/Code/Game/DanBiasServer/GameSession/GameSession.h b/Code/Game/DanBiasServer/GameSession/GameSession.h index 0e64a821..b2145c19 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession.h +++ b/Code/Game/DanBiasServer/GameSession/GameSession.h @@ -30,7 +30,7 @@ namespace DanBias NetworkSession* owner; Utility::DynamicMemory::DynamicArray> clients; }; - + static GameSession* gameSession; public: GameSession(); virtual~GameSession(); diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index d7cb2f39..2beaf75b 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -11,7 +11,6 @@ #include - using namespace Utility::DynamicMemory; using namespace Oyster; using namespace Oyster::Network; @@ -113,11 +112,20 @@ namespace DanBias void GameSession::ObjectMove(GameLogic::IObjectData* movedObject) { - /*int id= movedObject->GetID(); - Oyster::Math::Float4x4 world = movedObject->GetOrientation(); - Protocol_ObjectPosition p(world, 2); - Send(p.GetProtocol());*/ - + GameLogic::IObjectData* obj = NULL; + if(dynamic_cast(movedObject)) + obj =((GameLogic::ILevelData*)movedObject)->GetObjectAt(0); + if(obj) + { + if(obj->GetType() == OBJECT_TYPE_BOX) + { + obj->GetID(); + Oyster::Math::Float4x4 world =obj->GetOrientation(); + Protocol_ObjectPosition p(world, 1); + GameSession::gameSession->Send(p.GetProtocol()); + } + } + } }//End namespace DanBias diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_General.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_General.cpp index 0f9f3c57..3efa6c86 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_General.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_General.cpp @@ -19,6 +19,7 @@ using namespace GameLogic; namespace DanBias { + GameSession* GameSession::gameSession = nullptr; GameSession::GameSession() :gameInstance(GameAPI::Instance()) { @@ -26,6 +27,7 @@ namespace DanBias this->box = 0; this->isCreated = false; this->isRunning = false; + this->gameSession = this; } GameSession::~GameSession() diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp index 08bb39c9..5bd22a52 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp @@ -80,7 +80,7 @@ namespace DanBias if(clients.Size() >= 1 && clients[0]) { Oyster::Math::Float4x4 world = this->clients[0]->GetPlayer()->GetOrientation(); - Protocol_ObjectPosition p(world, 1); + Protocol_ObjectPosition p(world, 0); Send(p.GetProtocol()); } } diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index d110eb37..52eebd3c 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -46,7 +46,10 @@ void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage, void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float dt) { //Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt); - Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), owner->GetPosition()); + Oyster::Math::Float3 weaponPos; + weaponPos = owner->GetPosition() + 5 * owner->GetLookDir(); + Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), Oyster::Math::Float3(0,0,1), weaponPos); + //Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), weaponPos); Oyster::Math::Float4x4 hitSpace = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/4,1,1,20); Oyster::Collision3D::Frustrum hitFrustum = Oyster::Collision3D::Frustrum(Oyster::Math3D::ViewProjectionMatrix(aim,hitSpace)); diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 887d1f13..5c649b8e 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -72,7 +72,7 @@ using namespace GameLogic; void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) { - Oyster::Math::Float4 pushForce = Oyster::Math::Float4(1,0,0,0) * (500); + Oyster::Math::Float4 pushForce = Oyster::Math::Float4(1,0,0,0) * (20); Oyster::Physics::ICustomBody::State state; state = obj->GetState(); state.ApplyLinearImpulse(pushForce); diff --git a/Code/Game/GameLogic/Game.cpp b/Code/Game/GameLogic/Game.cpp index b22b3522..9f9c930c 100644 --- a/Code/Game/GameLogic/Game.cpp +++ b/Code/Game/GameLogic/Game.cpp @@ -128,6 +128,7 @@ bool Game::NewFrame() { if(this->players[i]->player) this->players[i]->player->EndFrame(); } + //gameInstance.onMoveFnc(this->level); return true; } diff --git a/Code/Game/GameLogic/Game.h b/Code/Game/GameLogic/Game.h index 7d40a3c5..cba5bb52 100644 --- a/Code/Game/GameLogic/Game.h +++ b/Code/Game/GameLogic/Game.h @@ -32,15 +32,14 @@ namespace GameLogic ~PlayerData(); void Move(const PLAYER_MOVEMENT &movement) override; + void Rotate(const Oyster::Math3D::Float3 lookDir) override; void UseWeapon(const WEAPON_FIRE &usage) override; int GetTeamID() const override; PLAYER_STATE GetState() const override; Oyster::Math::Float3 GetPosition() override; Oyster::Math::Float4x4 GetOrientation() override; int GetID() const override; - OBJECT_TYPE GetObjectType() const override; - void Rotate(const Oyster::Math3D::Float3 lookDir) override; - + OBJECT_TYPE GetType() const override; Player *player; }; @@ -52,8 +51,9 @@ namespace GameLogic Oyster::Math::Float3 GetPosition() override; Oyster::Math::Float4x4 GetOrientation() override; int GetID() const override; - OBJECT_TYPE GetObjectType() const override; - + OBJECT_TYPE GetType() const override; + IObjectData* GetObjectAt( int ID ) const override; + Level *level; }; diff --git a/Code/Game/GameLogic/GameAPI.h b/Code/Game/GameLogic/GameAPI.h index 62094675..fec525d9 100644 --- a/Code/Game/GameLogic/GameAPI.h +++ b/Code/Game/GameLogic/GameAPI.h @@ -13,7 +13,6 @@ #include "GameLogicStates.h" #include - namespace GameLogic { class IObjectData; @@ -62,7 +61,7 @@ namespace GameLogic /** Get the type of the object * @return The OBJECT_TYPE of the object is returned */ - virtual OBJECT_TYPE GetObjectType() const = 0; + virtual OBJECT_TYPE GetType() const = 0; }; class IPlayerData :public IObjectData @@ -100,6 +99,8 @@ namespace GameLogic class ILevelData :public IObjectData { public: + virtual IObjectData* GetObjectAt( int ID) const = 0; + }; class DANBIAS_GAMELOGIC_DLL GameAPI diff --git a/Code/Game/GameLogic/Game_LevelData.cpp b/Code/Game/GameLogic/Game_LevelData.cpp index a9434f69..6f377f40 100644 --- a/Code/Game/GameLogic/Game_LevelData.cpp +++ b/Code/Game/GameLogic/Game_LevelData.cpp @@ -25,13 +25,18 @@ Oyster::Math::Float4x4 Game::LevelData::GetOrientation() //return this->level->GetOrientation(); return Oyster::Math::Float4x4(); } + int Game::LevelData::GetID() const { - //this->level->GetID(); - return -1; + return ((IObjectData*)this->level)->GetID(); } -OBJECT_TYPE Game::LevelData::GetObjectType() const +OBJECT_TYPE Game::LevelData::GetType() const { - //return this->level->GetType(); - return OBJECT_TYPE_UNKNOWN; + return ((IObjectData*)this->level)->GetType(); + //return OBJECT_TYPE_UNKNOWN; } + +IObjectData* Game::LevelData::GetObjectAt(int ID) const +{ + return this->level->GetObj(ID); +} \ No newline at end of file diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index fd0d9b89..dcaccc48 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -27,8 +27,7 @@ Game::PlayerData::~PlayerData() delete this->player; } -void Game::PlayerData::Move(const PLAYER_MOVEMENT &movement) -{ +void Game::PlayerData::Move(const PLAYER_MOVEMENT &movement){ this->player->Move(movement); } void Game::PlayerData::UseWeapon(const WEAPON_FIRE &usage) @@ -55,11 +54,12 @@ int Game::PlayerData::GetTeamID() const { return this->player->GetTeamID(); } -OBJECT_TYPE Game::PlayerData::GetObjectType() const + +OBJECT_TYPE Game::PlayerData::GetType() const { return this->player->GetType(); } void Game::PlayerData::Rotate(const Oyster::Math3D::Float3 lookDir) { - //this->player->Rotate(lookDir); + this->player->Rotate(lookDir); } \ No newline at end of file diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 406b81e9..ba7b98bd 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -8,6 +8,7 @@ using namespace Oyster::Physics; Level::Level(void) { + } Level::~Level(void) { @@ -29,7 +30,7 @@ void Level::InitiateLevel(float radius) ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); - //rigidBody->SetCustomTag(levelObj); + ICustomBody::State state; rigidBody->GetState(state); @@ -37,29 +38,34 @@ void Level::InitiateLevel(float radius) rigidBody->SetState(state); levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); - - + rigidBody->SetCustomTag(levelObj); + API::Instance().AddObject(rigidBody); + API::SimpleBodyDescription sbDesc_TestBox; - sbDesc_TestBox.centerPosition = Oyster::Math::Float4(3,15,0,0); + sbDesc_TestBox.centerPosition = Oyster::Math::Float4(5,15,0,0); sbDesc_TestBox.ignoreGravity = false; sbDesc_TestBox.mass = 10; sbDesc_TestBox.size = Oyster::Math::Float4(2,2,2,0); //sbDesc.mass = 0; //10^16 - - + ICustomBody* rigidBody_TestBox = API::Instance().CreateRigidBody(sbDesc_TestBox).Release(); - + + rigidBody_TestBox->SetSubscription(Level::PhysicsOnMoveLevel); testBox = new DynamicObject(rigidBody_TestBox,LevelCollision,OBJECT_TYPE::OBJECT_TYPE_BOX); - - /*API::Gravity gravityWell; + rigidBody_TestBox->SetCustomTag(testBox); + rigidBody_TestBox->GetState(state); + state.ApplyLinearImpulse(Oyster::Math::Float4(0,0,4,0)); + rigidBody_TestBox->SetState(state); + API::Instance().AddObject(rigidBody_TestBox); + + API::Gravity gravityWell; gravityWell.gravityType = API::Gravity::GravityType_Well; gravityWell.well.mass = 10e12f; gravityWell.well.position = Oyster::Math::Float4(0,0,0,1); API::Instance().AddGravity(gravityWell); - */ } void Level::AddPlayerToTeam(Player *player, int teamID) @@ -77,4 +83,12 @@ void Level::RespawnPlayer(Player *player) this->teamManager.RespawnPlayerRandom(player); } - +Object* Level::GetObj( int ID) const +{ + return (Object*)testBox; +} +void Level::PhysicsOnMoveLevel(const ICustomBody *object) +{ + // function call from physics update when object was moved + Object* temp = (Object*)object->GetCustomTag(); +} diff --git a/Code/Game/GameLogic/Level.h b/Code/Game/GameLogic/Level.h index 7d280761..9180a89e 100644 --- a/Code/Game/GameLogic/Level.h +++ b/Code/Game/GameLogic/Level.h @@ -58,6 +58,10 @@ namespace GameLogic * @param obj: physics object for the object that collided with the level ********************************************************/ static void LevelCollision(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); + + Object* GetObj( int ID ) const; + static void PhysicsOnMoveLevel(const Oyster::Physics::ICustomBody *object); + private: TeamManager teamManager; diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index e156f262..8ed2e01c 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -64,7 +64,7 @@ Object::Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oy { Oyster::Physics::API::Instance().AddObject(rigidBody); rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); - + this->rigidBody = rigidBody; this->type = type; this->objectID = GID(); @@ -120,4 +120,16 @@ void Object::EndFrame() this->setState = this->getState; +} +Oyster::Math::Float3 Object::GetPosition() +{ + Oyster::Physics::ICustomBody::State state; + state = this->rigidBody->GetState(); + return state.GetCenterPosition(); +} +Oyster::Math::Float4x4 Object::GetOrientation() +{ + Oyster::Physics::ICustomBody::State state; + state = this->rigidBody->GetState(); + return state.GetOrientation(); } \ No newline at end of file diff --git a/Code/Game/GameLogic/Object.h b/Code/Game/GameLogic/Object.h index f39e2a41..a5e186f2 100644 --- a/Code/Game/GameLogic/Object.h +++ b/Code/Game/GameLogic/Object.h @@ -7,13 +7,14 @@ #define OBJECT_H #include "GameLogicStates.h" +#include "GameAPI.h" #include namespace GameLogic { class Game; - class Object + class Object :public IObjectData { public: Object(); @@ -22,8 +23,13 @@ namespace GameLogic Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); ~Object(void); + // API overrides OBJECT_TYPE GetType() const; int GetID() const; + Oyster::Math::Float3 GetPosition(); + Oyster::Math::Float4x4 GetOrientation(); + + Oyster::Physics::ICustomBody* GetRigidBody(); void ApplyLinearImpulse(Oyster::Math::Float4 force); diff --git a/Code/Game/GameLogic/StaticObject.cpp b/Code/Game/GameLogic/StaticObject.cpp index 293e05ee..b441dabe 100644 --- a/Code/Game/GameLogic/StaticObject.cpp +++ b/Code/Game/GameLogic/StaticObject.cpp @@ -20,11 +20,11 @@ StaticObject::StaticObject(OBJECT_TYPE type) { } -StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type) +/*StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type) :Object(rigidBody,collisionFunc,type) { -} +}*/ StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) :Object(rigidBody, collisionFunc, type) { From 3b552d536162154429eb0d2cf1ed8bb3451b79d1 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Tue, 28 Jan 2014 15:44:32 +0100 Subject: [PATCH 57/76] GL - change gamelogic to use the new physics --- Code/Game/GameLogic/AttatchmentMassDriver.cpp | 7 +++---- Code/Game/GameLogic/AttatchmentMassDriver.h | 2 +- Code/Game/GameLogic/CollisionManager.cpp | 4 ++-- Code/Game/GameLogic/Level.cpp | 8 ++++---- Code/Game/GameLogic/Object.cpp | 6 +++--- Code/Game/GameLogic/Object.h | 2 +- Code/Game/GameLogic/Player.cpp | 14 +++++++------- Code/Game/GameLogic/Player.h | 2 +- 8 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index 52eebd3c..9343ba5b 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -48,12 +48,11 @@ void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float //Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt); Oyster::Math::Float3 weaponPos; weaponPos = owner->GetPosition() + 5 * owner->GetLookDir(); - Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), Oyster::Math::Float3(0,0,1), weaponPos); - //Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), weaponPos); + Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), weaponPos); Oyster::Math::Float4x4 hitSpace = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/4,1,1,20); Oyster::Collision3D::Frustrum hitFrustum = Oyster::Collision3D::Frustrum(Oyster::Math3D::ViewProjectionMatrix(aim,hitSpace)); - Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,ForcePushAction); + Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,NULL, ForcePushAction); } @@ -65,7 +64,7 @@ void AttatchmentMassDriver::ForcePull(const WEAPON_FIRE &usage, float dt) Oyster::Physics::Struct::CustomBodyState state = this->owner->GetRigidBody()->GetState(); //do something with state - state.ApplyLinearImpulse(Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt)); + state.ApplyLinearImpulse(Oyster::Math::Float3(this->owner->GetLookDir()) * (500 * dt)); this->owner->GetRigidBody()->SetState(state); } diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.h b/Code/Game/GameLogic/AttatchmentMassDriver.h index 0ced186a..594ea4fd 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.h +++ b/Code/Game/GameLogic/AttatchmentMassDriver.h @@ -36,7 +36,7 @@ namespace GameLogic ********************************************************/ void ForceSuck(const WEAPON_FIRE &usage, float dt); - static void ForcePushAction(Oyster::Physics::ICustomBody *obj); + static void ForcePushAction(Oyster::Physics::ICustomBody *obj, void* args); private: diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 5c649b8e..495c2ac5 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -70,9 +70,9 @@ using namespace GameLogic; //return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; } - void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj) + void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj, void* args) { - Oyster::Math::Float4 pushForce = Oyster::Math::Float4(1,0,0,0) * (20); + Oyster::Math::Float3 pushForce = Oyster::Math::Float4(1,0,0) * (20); Oyster::Physics::ICustomBody::State state; state = obj->GetState(); state.ApplyLinearImpulse(pushForce); diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index ba7b98bd..5d56f83d 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -27,8 +27,6 @@ void Level::InitiateLevel(float radius) sbDesc.mass = 10e12f; //sbDesc.mass = 0; //10^16 - - ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); @@ -39,8 +37,9 @@ void Level::InitiateLevel(float radius) levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); rigidBody->SetCustomTag(levelObj); - API::Instance().AddObject(rigidBody); + + /* API::SimpleBodyDescription sbDesc_TestBox; sbDesc_TestBox.centerPosition = Oyster::Math::Float4(5,15,0,0); sbDesc_TestBox.ignoreGravity = false; @@ -55,9 +54,10 @@ void Level::InitiateLevel(float radius) testBox = new DynamicObject(rigidBody_TestBox,LevelCollision,OBJECT_TYPE::OBJECT_TYPE_BOX); rigidBody_TestBox->SetCustomTag(testBox); rigidBody_TestBox->GetState(state); - state.ApplyLinearImpulse(Oyster::Math::Float4(0,0,4,0)); + state.ApplyLinearImpulse(Oyster::Math::Float3(0,0,4)); rigidBody_TestBox->SetState(state); API::Instance().AddObject(rigidBody_TestBox); + */ API::Gravity gravityWell; diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 8ed2e01c..7b01e195 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -71,7 +71,7 @@ Object::Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oy } -void Object::ApplyLinearImpulse(Oyster::Math::Float4 force) +void Object::ApplyLinearImpulse(Oyster::Math::Float3 force) { setState.ApplyLinearImpulse(force); } @@ -110,8 +110,8 @@ void Object::EndFrame() //Oyster::Math::Float rot = (setState.GetGravityNormal().xyz).Dot(getState.GetGravityNormal().xyz); //Oyster::Math::Float3 axis = (setState.GetGravityNormal().xyz).Cross(getState.GetGravityNormal().xyz); Oyster::Math::Float4x4 rotMatrix = setState.GetOrientation(); //Oyster::Math3D::RotationMatrix(rot, axis); - Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); - setState.SetOrientation(rotMatrix); + //Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); + //setState.SetOrientation(rotMatrix); this->getState = this->rigidBody->GetState(); diff --git a/Code/Game/GameLogic/Object.h b/Code/Game/GameLogic/Object.h index a5e186f2..d3adbcba 100644 --- a/Code/Game/GameLogic/Object.h +++ b/Code/Game/GameLogic/Object.h @@ -31,7 +31,7 @@ namespace GameLogic Oyster::Physics::ICustomBody* GetRigidBody(); - void ApplyLinearImpulse(Oyster::Math::Float4 force); + void ApplyLinearImpulse(Oyster::Math::Float3 force); void BeginFrame(); void EndFrame(); diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index c57340f7..54e24703 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -16,8 +16,8 @@ Player::Player() teamID = -1; playerState = PLAYER_STATE::PLAYER_STATE_IDLE; lookDir = Oyster::Math::Float4(0,0,-1,0); - setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); - setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); + setState.SetCenterPosition(Oyster::Math::Float3(0,15,0)); + setState.SetReach(Oyster::Math::Float3(2,3.5,2)); } Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) @@ -30,8 +30,8 @@ Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oy this->playerState = PLAYER_STATE_IDLE; lookDir = Oyster::Math::Float4(0,0,-1,0); - setState.SetCenterPosition(Oyster::Math::Float4(0,15,0,1)); - setState.SetReach(Oyster::Math::Float4(2,3.5,2,0)); + setState.SetCenterPosition(Oyster::Math::Float3(0,15,0)); + setState.SetReach(Oyster::Math::Float3(2,3.5,2)); } Player::~Player(void) @@ -82,7 +82,7 @@ void Player::MoveBackwards() void Player::MoveRight() { //Do cross product with forward vector and negative gravity vector - Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); + Oyster::Math::Float3 r = Oyster::Math::Float4(1, 0, 0); //Oyster::Math::Float4 r = (-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); setState.ApplyLinearImpulse(r * 20 * this->gameInstance->GetFrameTime()); @@ -90,7 +90,7 @@ void Player::MoveRight() void Player::MoveLeft() { //Do cross product with forward vector and negative gravity vector - Oyster::Math::Float4 r = Oyster::Math::Float4(1, 0, 0, 0 ); + Oyster::Math::Float3 r = Oyster::Math::Float4(1, 0, 0 ); //Oyster::Math::Float4 r1 = -(-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); //Still get zero setState.ApplyLinearImpulse(-r * 20 * this->gameInstance->GetFrameTime()); } @@ -152,7 +152,7 @@ Oyster::Math::Float4x4 Player::GetOrientation() const } Oyster::Math::Float3 Player::GetLookDir() const { - return this->lookDir.xyz; + return this->lookDir; } int Player::GetTeamID() const { diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index ac799670..d67a18fe 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -74,7 +74,7 @@ namespace GameLogic int teamID; Weapon *weapon; PLAYER_STATE playerState; - Oyster::Math::Float4 lookDir; + Oyster::Math::Float3 lookDir; bool hasTakenDamage; float invincibleCooldown; From f968adebebdede3eb1f49c082c5fd2ece2608f5b Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Tue, 28 Jan 2014 16:15:10 +0100 Subject: [PATCH 58/76] GL - LevelLoader = done and done --- Bin/Level.txt | Bin 0 -> 156 bytes Bin/map | Bin 0 -> 156 bytes Bin/map.txt | Bin 48 -> 60 bytes .../GameLogic/LevelLoader/LevelLoader.cpp | 4 +- Code/Game/GameLogic/LevelLoader/LevelLoader.h | 4 +- .../GameLogic/LevelLoader/LevelParser.cpp | 39 ++++++++------- Code/Game/GameLogic/LevelLoader/Loader.cpp | 16 ++++++- Code/Game/GameLogic/LevelLoader/Loader.h | 2 +- .../GameLogic/LevelLoader/ObjectDefines.h | 2 +- .../GameLogic/LevelLoader/ParseFunctions.cpp | 45 ++++++++++++++---- .../GameLogic/LevelLoader/ParseFunctions.h | 4 +- 11 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 Bin/Level.txt create mode 100644 Bin/map diff --git a/Bin/Level.txt b/Bin/Level.txt new file mode 100644 index 0000000000000000000000000000000000000000..d86dd3db4f89e0c84adfa01d7b0ec5a42dd61adb GIT binary patch literal 156 zcmZQzU|?VYV!y=PR7M~T0wBIiYH>0$&A`y$pam3X0x19jkXjHMgqeXjYRNaJ8F^XG hjYrow<6?ux*}DpzTmbMiDYF0o literal 0 HcmV?d00001 diff --git a/Bin/map b/Bin/map new file mode 100644 index 0000000000000000000000000000000000000000..a578cc3216cf4c730d47a3cae791ddccf2b29610 GIT binary patch literal 156 zcmZQ#U|?VZVrC$YTJp_lMqZY47AJ$m6N^*QIDs?+LxY1BkYob085kIWGyuTm1-1YH literal 48 gcmZQzU|?imU|<4bW~XmUqMWnxW;m}o+K7t{08$7NVE_OC diff --git a/Code/Game/GameLogic/LevelLoader/LevelLoader.cpp b/Code/Game/GameLogic/LevelLoader/LevelLoader.cpp index a6f8bb98..d60aac66 100644 --- a/Code/Game/GameLogic/LevelLoader/LevelLoader.cpp +++ b/Code/Game/GameLogic/LevelLoader/LevelLoader.cpp @@ -9,10 +9,10 @@ using namespace GameLogic::LevelFileLoader; std::vector LevelLoader::LoadLevel(std::string fileName) { - return parser->Parse(fileName); + return parser.Parse(fileName); } LevelMetaData LevelLoader::LoadLevelHeader(std::string fileName) { - return parser->ParseHeader(fileName); + return parser.ParseHeader(fileName); } \ No newline at end of file diff --git a/Code/Game/GameLogic/LevelLoader/LevelLoader.h b/Code/Game/GameLogic/LevelLoader/LevelLoader.h index 8f87d141..4ac7a950 100644 --- a/Code/Game/GameLogic/LevelLoader/LevelLoader.h +++ b/Code/Game/GameLogic/LevelLoader/LevelLoader.h @@ -17,7 +17,7 @@ namespace GameLogic { public: - LevelLoader(){this->parser = new GameLogic::LevelFileLoader::LevelParser(); } + LevelLoader(){this->parser = GameLogic::LevelFileLoader::LevelParser(); } ~LevelLoader(){} /******************************************************** @@ -35,7 +35,7 @@ namespace GameLogic LevelMetaData LoadLevelHeader(std::string fileName); //. private: - GameLogic::LevelFileLoader::LevelParser *parser; + GameLogic::LevelFileLoader::LevelParser parser; }; } diff --git a/Code/Game/GameLogic/LevelLoader/LevelParser.cpp b/Code/Game/GameLogic/LevelLoader/LevelParser.cpp index 72d68d97..3ab3c203 100644 --- a/Code/Game/GameLogic/LevelLoader/LevelParser.cpp +++ b/Code/Game/GameLogic/LevelLoader/LevelParser.cpp @@ -19,17 +19,17 @@ LevelParser::~LevelParser() std::vector LevelParser::Parse(std::string filename) { int bufferSize = 0; - unsigned int counter = 0; + int counter = 0; std::vector objects; //Read entire level file. Loader loader; - unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), bufferSize); + char* buffer = (char*)loader.LoadFile(filename.c_str(), bufferSize); //Read format version FormatVersion levelFormatVersion; - ParseObject(&buffer[counter], formatVersion, sizeof(formatVersion)); + //ParseObject(&buffer[counter], &levelFormatVersion, sizeof(formatVersion)); if(this->formatVersion != levelFormatVersion) { //Do something if it's not the same version @@ -39,22 +39,26 @@ std::vector LevelParser::Parse(std::string filename) { //Get typeID ObjectTypeHeader typeID; - ParseObject(&buffer[counter], typeID, sizeof(typeID)); - + ParseObject(&buffer[counter], &typeID, sizeof(typeID)); switch((int)typeID.typeID) { case ObjectType_LevelMetaData: + { LevelMetaData header; - //ParseObject(&buffer[counter], header, sizeof(header)); + ParseLevelMetaData(&buffer[counter], header, counter); + objects.push_back(header); + break; + } + + case ObjectType_Dynamic: + { + ObjectHeader header; + ParseObject(&buffer[counter], &header, sizeof(header)); objects.push_back(header); counter += sizeof(header); break; - - case ObjectType_Dynamic: - ObjectHeader header; - ParseObject(&buffer[counter], header, sizeof(header)); - objects.push_back(header); - counter += sizeof(header); + } + default: //Couldn't find typeID. FAIL!!!!!! break; @@ -68,18 +72,18 @@ std::vector LevelParser::Parse(std::string filename) LevelMetaData LevelParser::ParseHeader(std::string filename) { int bufferSize = 0; - unsigned int counter = 0; + int counter = 0; LevelMetaData levelHeader; levelHeader.typeID = ObjectType::ObjectType_Unknown; //Read entire level file. Loader loader; - unsigned char* buffer = (unsigned char*)loader.LoadFile(filename.c_str(), bufferSize); + char* buffer = (char*)loader.LoadFile(filename.c_str(), bufferSize); //Read format version FormatVersion levelFormatVersion; - ParseObject(&buffer[counter], formatVersion, sizeof(formatVersion)); + //ParseObject(&buffer[counter], &levelFormatVersion, sizeof(formatVersion)); if(this->formatVersion != levelFormatVersion) { //Do something if it's not the same version @@ -89,14 +93,13 @@ LevelMetaData LevelParser::ParseHeader(std::string filename) while(counter < bufferSize) { ObjectTypeHeader typeID; - ParseObject(&buffer[counter], typeID, sizeof(typeID)); + ParseObject(&buffer[counter], &typeID, sizeof(typeID)); switch(typeID.typeID) { case ObjectType_LevelMetaData: - //ParseObject(&buffer[counter], levelHeader, sizeof(levelHeader)); + ParseLevelMetaData(&buffer[counter], levelHeader, counter); return levelHeader; - counter += sizeof(LevelMetaData); break; case ObjectType_Dynamic: //Do not call parse this object, since we are only interested in the LevelMetaData diff --git a/Code/Game/GameLogic/LevelLoader/Loader.cpp b/Code/Game/GameLogic/LevelLoader/Loader.cpp index 26818202..a0206d89 100644 --- a/Code/Game/GameLogic/LevelLoader/Loader.cpp +++ b/Code/Game/GameLogic/LevelLoader/Loader.cpp @@ -3,19 +3,31 @@ ////////////////////////////////// #include "Loader.h" +#include using namespace GameLogic::LevelFileLoader; using namespace Oyster::Resource; using namespace std; -unsigned char* Loader::LoadFile(std::string fileName, int &size) +char* Loader::LoadFile(std::string fileName, int &size) { //convert from string to wstring std::wstring temp(fileName.begin(), fileName.end()); //convert from wstring to wchar then loads the file - unsigned char* buffer = (unsigned char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); + char* buffer = (char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); + //std::ifstream f; + //f.open(fileName, std::ios::binary); + //if (!f.is_open()) + // return 0; + //f.seekg(0, std::ifstream::end); + //size = f.tellg(); + //f.seekg(0); + //char* buffer = new char[size]; + //f.read(buffer, size); + // + //f.close(); //gets the size of the char buffer. size = OysterResource::GetResourceSize(buffer); return buffer; diff --git a/Code/Game/GameLogic/LevelLoader/Loader.h b/Code/Game/GameLogic/LevelLoader/Loader.h index 2d605169..198c2a87 100644 --- a/Code/Game/GameLogic/LevelLoader/Loader.h +++ b/Code/Game/GameLogic/LevelLoader/Loader.h @@ -17,7 +17,7 @@ namespace GameLogic public: Loader (){}; ~Loader(){}; - unsigned char* LoadFile(std::string fileName, int &size); + char* LoadFile(std::string fileName, int &size); //TODO: //Add functionality to load physicsObjects (hitboxes) diff --git a/Code/Game/GameLogic/LevelLoader/ObjectDefines.h b/Code/Game/GameLogic/LevelLoader/ObjectDefines.h index 2d3e09f0..dcf960a5 100644 --- a/Code/Game/GameLogic/LevelLoader/ObjectDefines.h +++ b/Code/Game/GameLogic/LevelLoader/ObjectDefines.h @@ -91,7 +91,7 @@ namespace GameLogic std::vector gameModesSupported; }; - struct ObjectHeader : public PhysicsObject, public ObjectTypeHeader + struct ObjectHeader : public ObjectTypeHeader { //Model, int ModelID; diff --git a/Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp b/Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp index abca143c..f456b2d7 100644 --- a/Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp +++ b/Code/Game/GameLogic/LevelLoader/ParseFunctions.cpp @@ -15,41 +15,66 @@ namespace GameLogic { namespace LevelFileLoader { - void ParseObject(unsigned char* buffer, void* header, int size) + void ParseObject(char* buffer, void *header, int size) { memcpy(header, buffer, size); } - void ParseLevelMetaData(unsigned char* buffer, struct LevelMetaData &header) + void ParseLevelMetaData(char* buffer, LevelMetaData &header, int &size) { int start = 0; int tempSize; + char tempName[100]; + memcpy(&header.typeID, &buffer[start], 4); start += 4; + memcpy(&tempSize , &buffer[start], 4); start += 4; - memcpy(&header.levelName, &buffer[start], tempSize); + + memcpy(&tempName, &buffer[start], tempSize); + header.levelName.assign(&tempName[0], &tempName[tempSize]); start += tempSize; - memcpy(&header.levelVersion, &buffer[start], 8) + + memcpy(&header.levelVersion, &buffer[start], 8); start += 8; + memcpy(&tempSize, &buffer[start], 4); start +=4; - memcpy(&header.description, &buffer[start], tempSize); + + memcpy(&tempName, &buffer[start], tempSize); + header.levelDescription.assign(&tempName[0], &tempName[tempSize]); start += tempSize; + memcpy(&tempSize, &buffer[start], 4); start += 4; - memcpy(&header.author, &buffer[start], tempSize); + + memcpy(&tempName, &buffer[start], tempSize); + header.levelAuthor.assign(&tempName[0], &tempName[tempSize]); start += tempSize; - memcpy(&header.nrOfPlayers, &buffer[start], 4); + + memcpy(&header.maxNumberOfPlayer, &buffer[start], 4); start += 4; + memcpy(&header.worldSize, &buffer[start], 4); start += 4; - memcpy(&header.map, &buffer[start], 4); + + memcpy(&header.overviewPictureID, &buffer[start], 4); start += 4; + memcpy(&tempSize, &buffer[start], 4); start += 4; - memcpy(&header.gameModes, &buffer[start], 4 * tempSize); - start += tempSize; + + int temp; + + for(int i = 0; i < tempSize; i++) + { + memcpy(&temp, &buffer[start], 4); + start += 4; + header.gameModesSupported.push_back((GameMode)temp); + } + + size += start; } } } \ No newline at end of file diff --git a/Code/Game/GameLogic/LevelLoader/ParseFunctions.h b/Code/Game/GameLogic/LevelLoader/ParseFunctions.h index 7a6ab93a..08962b4a 100644 --- a/Code/Game/GameLogic/LevelLoader/ParseFunctions.h +++ b/Code/Game/GameLogic/LevelLoader/ParseFunctions.h @@ -10,8 +10,8 @@ namespace GameLogic { namespace LevelFileLoader { - void ParseObject(unsigned char* buffer, void* header, int size); - void ParseLevelMetaData(unsigned char* buffer, struct ObjectTypeHeader &header); + void ParseObject(char* buffer, void *header, int size); + void ParseLevelMetaData(char* buffer, LevelMetaData &header, int &size); } } From 60062feb070e66cf84684902947179e179cfcb57 Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Tue, 28 Jan 2014 17:51:02 +0100 Subject: [PATCH 59/76] Documentation update Math treatises --- .../Other/Timestep_Impulse_Fix.odt | Bin 69508 -> 70176 bytes .../Other/Timestep_Impulse_Fix.pdf | Bin 123976 -> 123757 bytes .../angular momentum to angular velocity.odt | Bin 201605 -> 173183 bytes .../angular momentum to angular velocity.pdf | Bin 0 -> 123925 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Code/Dokumentation/Other/angular momentum to angular velocity.pdf diff --git a/Code/Dokumentation/Other/Timestep_Impulse_Fix.odt b/Code/Dokumentation/Other/Timestep_Impulse_Fix.odt index 25ac526e38e7cf2ec810df86c0d02cc34b0075c2..1ad0992a9c21df85d915572204e1462134c6708c 100644 GIT binary patch delta 39644 zcmZU(b9^Sv)-D`7lZkEHb|!Wvwrx8d8xz|$C$^1=?TKwna^~6l-TOP|{qBEOt$X3B zuKum+T32^)E<|1t1frr0I0PC92n+~_Z7^RvqBQtFLW}2LB30#I;*#~>f;cfR0p*`z zq82FUzvLt+apF!C+W(B%|KmoK_;0s=%z26bHY?6zB}RgY{ma$A5i9W&jP_q+!#u1+ zCUCO$^#*ZYE@dR@2o95LjxGIu()&mE#=geJ+U!2SCXObP{bcIqr+`^&UbCP%z&Nz&W zl!AWGQI~QxwIA0MnsgSD48NsXH4_1@k??MoL@^eKKM3#wae0#B&lP z#)PCn^5qa|U*gvUa?S22s783BWL`@57ezC9?R`YM&?mZZ)k|*p=5OiT^i-;*`efhI zuNoxv$?_gtaSrZ*cE!F|#Yv7C?nvt9SgW6k;@ zGJ9C1KjI^+u#P*rwZMjIRoi&Sm)k|;H?MhjMQVbpt;;`=)nSiAmev(;*mBTF%HJ#B zr4m#MoS?W@Lt0nZF3R84%36Tz18s5Veze=VL_UOqx?dg*cFH)j`P7QX2Pqvs>=CUb z4WtMjG?lb_HhxVfJzd^!0y^=ob(AH426)|Y^ogHQmk^yKTc6hDs_ zF48^%jN!_zjFCmH=#EW6`BmF(D`1i|nSj z(QqYW#Zs!9_NJh+WvW5;Q7E(~f72zhb9)dcsr9hB&4zd6PA|3ijz$M|U5Af0hE-D9 zmmpg*qM2IN*KQ)*USJ4lWMYOBLX7pA7#=k0BzSqoK&w#10lC)PxbrizQ zhDAl;7HYLXl-J_CxtJ8>3F_RDN&#!Gu3D>rsmg823jGKt8;j6tud8^cI zmN}uVE3)Q6=dja85Dta8f~IRA{)Pe!2~t>O#zZzKy;;wY#T7e@-4uIF<@d%qCPv!L zE4B$Q{Up;r%vd6)CIVKOGB&)+4uAz&BvZSPq<%~>jS?UM-WVCBAq@!vh?K?@o%v;l z1|Jtr`iqeRag;L1ixo zV&QI+X@~*+Zlf`?wuJdcS$ZLYIg9c!K~@~mWUnXo*Mz~kTX;rF1T{;?7?NU77|;U_ zkgZHfOSZzE+d;I_w(n|>(@UL&-w9V~((dpvsgp@wq0okc->aSM;fqr*&?BhFgx7h< zH0oCv)Le_d(2QDu48ItIY(1B{vzbVx&^cXP^|b)O)PHDEgEW;@$H`*5KnoL7ZF@$HZXO=!er4pHYNzDq$%x#0_Mk~1C9OD(i#vgd zaDZ%(R}Z+SUX+92B}FEW!uXW3Ti+;+$xBmzgevFuff_aQ; z>_9us8TJwHkBI(7i;8}gn9fts5}Yk-95 z@NH?8Eg0dQjNK$x>DyM~!VhN-M8g5U1RSTC65=zdQ*$?gKYU7AkJBZ@ZxMSS}D@)`?W+l0gzw!bUQ;P=ebU4=B(h5qDMMFisayuk|^L4NunY8B2 zTnxfbBxa@WJ~uY}s4YRjP0lqy++?ExF#Kw-&LAvgrxw0C`6_2}cT>_Hr6&F?Z|k-X z`|?P?>1-=ic*k=1kaAb$qI6B`&TcSm`9N+?Br$~=33s*)ISHZQ$S zoxpnm3LdN765#*(`O(3|M|n~*oLK3@RL|a)u^fKr4ab;F58oJ?iSQnEC0JAhOm#k& zc{=uZBI2%xEx97hKcbx(4P;O+B|nw(EWkxRA2mDQKORIPswIO@{j8Kmh#jQ$!?dbl zlxm>hUT5F@Lb^aFp{ahoh1eI+Do{6;PeG?i;m6vl623OW@5kjZ zWA|`I*#2IaD*M@SQ;k{IOllJIgI#24KDcVH*&SC)Q(|tsrciYbYI}JGj!J}yX@UvA z6pWP0F~+wb=B3nHJ-m4UF!hDjTIIJmOnC^P@VmJ-8hU0P@)KLg%~zF6ibN(PG<<-h zf&GP=DUw^U9fa0laN1J-C&Cu)f%Ox4EeUa1udV9vo4s3XiapW(*0gd7vlw=VrX2Dwdp1(BbQdaTg`sy5YVu72R@*h) zbFXEl>m^Bt!D?$9j@L}d%@;cw0~*>{Ul8wj7WUjM(zbU)x0BoQ7Q?iPP3WLSp5#d&U*2i8b%?p(hB<1`;>%TRd>>f2(Z%^dKOB5@;Q| zyJott$H{?a>1obRtu0#M-$1;Y@H2dg!esQITK0?vUZg#N7p(=4_lX4dA@5wLs!$*@ z_IiTa&vk0(__rOn1l>6TqnZA;ZC-eYXP85aKk=iUzH@#%_Il_1QS@WoVgBS6l6%2C z+1`^TkOcAR5WrQMS*1a#RynHI|JwafcSyUGlkW`Uo?&pLf0TmyByi+j!u%~JB-(O? zwZ)C!uvze6{pOBu=1Ox!;}50BHsThwt&W+8s7%gH-Ia$17Sh}BK=-yUp?#h?mV zYsxN3OQ_u?0)MDnC$gD%8n)qY=TK|_c({uN>_2(T@GWfKl(NKQg;>F+VAygIXW1^b zNx(B{X1PKSmjj^MNN1z}Z9;;;u`glBHiODoL~1>(e}-ZEACqF9Wt()6?n6nkd0LG+ zIF894vW0jacEIl_u#9~P-KUL0;m`|ixY<^y$5ZT>qleFiHTV691(f zzW2v)m1S#ld(b|vyzQsiMi;iQ8ALiGyvcO8w=wppt2NR7>S@6^OWn!;OhzAVR;9<) zp;;B3LkDxY+1|%o?OR6rNt`4r$y7L-KH4unwqoHaSJd0Z3tSVAlxmO|Z9K)8e_sgc zET6%J%A0kTe95k-l}OMCeg?9sc#l!X9T@bm(WNXW|4 zpsXql&mGwf*RFJc*>dOY?#0L6uZ~jnyoEPAHus7lt^|%@_@b7G#yu#-XEtr>fqj69q2J-t?_u82X_&28#bq%zg@ zD#;}dZBjqUqUHTOdRYU)oxJ$5Yl{U)v7G1C<7#}iWC;SKQj}wcoXnJ$N&9zlcHgPj zJ89aESXyMwzwoNUkfwW0$lay=qCg!!V@KktHU{vIt%#x48 zs;67r2lPpz%#+vVP1iy6>(j)F{qUk7D{pOlWP}vvlLTYgE1a1Wzk898OMKOvp^CEe zJ7G-};sFD0r=HCe`S9>M^f~+0BOrrlhUW*EtW-@b6YzG<=cAeh|Wui2ps$;C2*Wkf+W5shQl6ti4xI*;7%TtgVKLnS#G9 zY3G5g!G}Lf?xsMM)gR}LJ-lBN5GL&JxNr|U!o!rV(M`IDR|IIc1L)gN0@KePSQ^Cw zCprNZGdwSIhVfZozW=fci#fLAVLtpq^s_4PJFzPRWla-G0dBA)oSvQVdfh2zU^CoE zU^RQ05gU)gz!d5j1**!z{7HxkJ1f`p%`zL#Al$FjS&FfmNb4LafZoAu+-}CzJqycO zLq&aoyRn7rVGxv0y2Fs`*pb`**ELPs)Fj}8X*yJ0BPc}26l!7P`I_lJTpQ*~;V)i< zqxh|LWPR`CRU*r=+}%+-pt*{pDl<(ey|Oi&40Ry?ifJw)$k z+qk`t=&N-u^t&8kme(ifKP6Z^lrwDs4g%7E^1n(@6y)!(5dk9x=D!@y1|dqKV$&Ds z{}kyz+z&1hO5%0nznG-NfB2t&^xOdKf5ik43jJTP|K@`J;cqtn`uvAp`iJk)A^Lx9 z|2NmOK}7Md9DjM9Mtovq(0`OhW|9iX%aG>=LLndK#QszUs=y4F8iE=jsoe&JOqi6Xv&xr`GMK&*_)xwkNYI9`c^nBAe)d zDl&i(oILSqV)nf>EW7MXr~r|QKZ1soren77)ZXri=Y6TxF%OI9zIwfDc!&2ZKY|Om zL@+A9$J7Ob<5&I!`Iv9%<-+*mWeBD!R&A6|EL0;m%w@h{Lk_BS67&1dww{y))XSPi z;A<(qM{aLa-i}mrmjZkcb2KWhzrN^HV{8C+oY+f+1`?l44SQXQ#I;^2&NUM{r&W3u zIXVZUmE|te>2^#@9lAvNM<`dq!4@L%E`T8omy+6G^5W`W>&yKY31c*FX&6mE2!9BC?meUbJ*i`22im=loI> zIj<6WZr@S#@R@mcB%-~#>d1YCYZqepc6yT!>Dj3+X!Am&?dmkY= z0w!8i4oE^M*53Uc%~oDpm^pi(7smm^ZLiFZ#w-tl8+c`(LVcWD0B_&C0shJen0G+20bVNxUyzCu$UD9V!3GEgsT5Zq z$@P~yjB`*s@ELvA4x}LafTP}{UJcwit68{dD9S)F1NJn?8tDDLiyc+qAmh)D=Jm{u z<2Bkd#WUfxDid9%1uqu-2iQ5J9hh<-`;Jf@!6XDBSeS{-7h|(DvQorvL zeUzZs85DW!dPfP6$NoZj8s`Oo=#8wXcE44!D>mBtrHVX=6}}5;Q+&6mdtdPTtc>r} zFP#eE1=zLvIRKVI?6Te3Z-PP1;O%exuIqIjN{EBQaQ>FfWqzy)Sk~Qih}*%9iT2mZy+tNaC2e-UWJ% zCo(sTyqz5lBjpyLlMYczlM(z76Pv_NS16_umnmrA_-!GH!y;U@eA1A)XPQ79mm7*2#%I z)cS%lSghyQ0TVJ<#@+r#T=BJS?Q!J<{)Ja)nxK=Vuh9rViTXlyMjwWu?Qa67rQc~9 zC6nXi^nkDN0nShQS?sEi!sumNIMZiS3eQK#F#SY9ylXP=^2It2k&~yi+v)EyE_gL~ z5pS*CVtE0S#(biw~80IZAG22j7}rG4$x6qkiq7(yc}=Xt-`E zQy6AuZhPYa+XrmD+orGFv4kAglvutxTAS@MlvIBrJ9WnUIWtW_kcI4 z(j`ClR@bVcu$zWBa}P?S26(}}eO-}nHh+*4LlYgclwnR_s)2NHeX-g zpNgNUTVJkZfGp&Kp~jz@n5-62_5x$oX`gk@-I=E=G4pUPJPitKY z&lGkroz1-3E3X3OJ@zJrK=SsVb;>EqxyLbmA9ITlfjNq|*K_acqQXj;Ne!LA>_N4N zONk^PfqdaqyR}qjs~y>#ic~>;YaVULb{cubd)TLk0#TN!*_W~Z?$(DgkF-Kx+iumw zM9wpEys*`IcLwkI3k9wZAswh7l z2U;hLz-X>{fVL2h{5>4j7XonJfTKmHBB?b1I=$%AA`wq;fNY{UKP^vW~wxW_2r0V~1O7kR8VZr(I4{6d!V0Ugt2AJ1DylA zXOiOXaMi>wO78w%dFI;<$K3i5#VaC*=FDeRL$`fZ!&Yr|gC9d9e{1%|z@Jq;AHH=U zZp7I8_Sr^1C+=eEuJMm1(p%6&J)pt(7WL5h>A`=lphx(Yx;*i*^v;-zO;7Q0uUBcT z&9W*HqStL?r)ImkI1!3Y<0+TfwKPnp@)Q3dhjr4z=6#0qJ+6h|!yA2w@oxbIOi?o$ z{D>_(50}0Hw2!_F#B9ET@;Kj^T$KR@F9=Jo>9pio^bH9ZN4+BvqEWr(y*v(xtstGb zxxYau0~&>RJs4M@J9#B?_OQTnWQf|q-$5oJo`S}9+?Y@{VDv&O#C9wlD4q$p2sWU3 zdd;~wdR+}rxCqY?bDQ%xKmzihZ+nlN^Z5N$(*V9)X{cH9=}~Z38sz z%i5;+@Kp^pCu*AJIjT$9mgu}>T0;zmYW4G}81?Jh^hP63bVkdZ^hQTr81-*=7&ikP zQO}1`QO_sLQO~y7V(+bMBz}OdC9)u=#^9At*PZt)f&kd#Ou~Mse^=jnUt^j%!n3vm zjRw**q)cz}2GRyx&VU!vwTBIo1E~kSOwNiFu6Pps7NmHP>yD*PlOQ|+#5Od5h?8#( zn+7fqP1}dOGjAYxZOjCA{)Jcf6tpK00AIenghmRm_ZNRQN8&>9Apmqh1;D!v6HCLm z&~m{fOk_MQo13m?Qa;oh4xcx6S@fnHus;uXS+@8t;GD?r%R!rK`^g|oWyZ-7}k^_INgml$~EE6MfO72em9YjF39~dGGf^8-n)4yp6 zMn{Q1s@E!9*f=g7(4Z(Kw@+USL$rS*y&D!t>BkNsjdWiFV3oBw7uJ*w6AQm0t%OY; zYCJzm8j7;rnkVqpj2(i{5m^0EfIco~+QbRGQAyKNYOx|RH}X|@N`k>2A?z+kzWqin zGW_$yQO6=j+BE(BXLg1jLAn;v9csDlcxQdUByWtn2D;lydCmtEiy*@ITkH$5zDAi2 zx(wToo|y$XV7yK5pQphOS8Et?qBC^#inv=1%(EBumdDnv*03@Yv)yBTS4_Qco^&-5 ze^apZsp7v;qy3F( zqefk&Y=a=+g`?c--QBxJ(~DJtNU-{p_8sp~9?MOnA}ROuD1S*%(Qdh#7|U*%mPWjJj^LiJ=yp%@;k|#xVgmmT5dEQX@gL#hxOJumGqyGOZJaF>&%1X2Ut{aKH zS~4za_-i0$hvbVqqtzd9SJ->oHo-V>MSUv_6y{(rTWiGSO+ z#%5OZe>HReC;-cpmRvZV7@8k{;;IR1VImQPduox828u4vabFM)8C{K|>t-F)O^Sb_7jZ-%blN@|f3qBM1S$VmuBv{A$u zcJnh*-dYB0r{P-$(>kv~&?q8pxP4wYzvxW+lK>F0$A*2(2Uj8kftewTR5V2xJgB(o z(mzK@kj=`!h+JIpniv|BhhJ;&6yl^;#Fv9{Tvs7zL;fb=3rqXu>d)<~{~;ep#@JkT zdW$rTG{K9d<8D|LbcTBVX;z7&L&#;pOQ7S9pH{z8J)ElVveSyduvhC!)7bYj`s9kd zbpUYS82t%ntT_X_8H7Q<#oAu$h!DaBEt$g#f|yh3{W1;0_N~kG+IHFly;Tm1kH|sL z3%&Iv6~Famj5j$Db6Q^4_5-Q(vcO1xAaLFLg~lx=F5f7TNUM}Z`8<`WOF57iqI80& zn{l5`#cK!f>kS;zeu6mnO*+LQ=M{xrW&#Y5uT?U9jo0YTBXJL(^Fws|dRGR6^9*n5 zjr-Btl>#%vhUj8d^BbBX!^HBP%Lg7H<0rUgD;SxrKJLSP!K>m_!fz^>HhxesISOryf}-gvWQ6`T~)a97Jc&*2*%V?UXNsM`p2mRQrLuXO+ zjRwff@Ec{ZTZ{b+Pw%r_;wK$Ro&bI5mxRovnkXdY#p#Y%!R#?cc#BnOH&U#TT~_Fe z=YxD(c54efbvk`+pHD+lC(MhKtMUssqrqZItTOb~i!rs3xb&o71Sd1(t(qE%!si9O z@{F9PmPL!70~hf%yWc?J_8!Y>gGAEfU`*-j@yHt2#F1?)aF)D_h{3zaP=KBYqK4+h zyLIw*VE#@QPamyRqXRT6u#rJhV5qltQ9jP%E*p-qsmi)dE+kHF(T^YYCtJ43-m-p) z$J@he_joPB{!{YFc-tgJwyW$XUEkDh(LB4O*V<2EpR_Tm@A(PkkK4R9jx*_+rMxKN z^B9X3FbdA`Y&~1ljY_6~E@jzKHt=CC;`(I&4<}ZGE^z1%0Rloy{J%D?#&~Y9|8821 zeLOG!RRH!yZ5azxVXPe6@9RxVgI0GI)iJgUMTpWB7(TR-Ds^fV8rO>D->t0m2ft+zLA_hpiQByOhDtr=uSpDc$=$-9}G_k4U# zre0-cUv4#38!ltSgrs#_Rz;_CUd;TlWjCG%8wLapncPC;=K@w&Gz-z<2=fyL7KEZt zQx*SAxNKQvy}fnAxrItwmzfy4-fQ;aysde;iMKyJxG^^e=e+CgSKoCw$EfXLynHS6 zShJ8bF147E+sXHq&mh1rfh%@7FX|d7_>@yk9`K@dj2S;dIa-0pzp<61Z*pDGDFmhe z?F%sG4|VQ;TTk9aAM_N#TO#7n_sBi59lg&9^{Y9kOu-y;_SGsk4(V?fbjDgYmjPsfO=%9Pi{UOQ8K z!KY0+f3vhQd1_~9f_nNPa##VF~^ z{n#iXNC58Su4>R3d8+}=LZwF*L*vPP3XXK#8_i9Ky^KS78Rxo`uNqMTj?`l- z%vUZ3$yqNQ8N-GVNLhY{JQ_Rfoqb1KdNjvMe!9LzdEbpSbBcX6<5#ijbT5mRs5(HZ z$P|MqBKSN|v_Q%c8xkx`l7!UF21WBQjVL0ENoz0DZR4I?8`;*p{p?6&2XfK(WY!#p zN&FHk-jRzqyd@{qaRZKtYy5;uB>H`BJh#o+z@S!m^gcw?e62y*@%_Cq!~n@{{J9vX zXBA4F8G1!-Ba?OmBCmOW=j-O+E(%~n%(M+l2jRyh_8s&OEp!+a1VejWh)@LXyqu2n zKzl?Qbfzdn5?JEs2a5q`UmtP8Xex^h%1H+2<^}{N*oGWhGHF{xv7G^@;f*9Gazxsu zD4FkSIhUXJc);dhADjW_&DdW&B6??saJ#)zFdJe;^}ldFs=apoY9Z7RKxpM(wAru| z3W8UI&0t$(jtB&LFp7BEr~rM3FAT$E@!thGA~x<7I63=5K-^K!z$EPo7NO56)aa7z zNN_f7oKWYI8d#&wvNFKOT;)OMc}i8jkwsMw9i<&@$)elbG}1!&D>41{`E)ybuD&ehjFMPg%xJ4Lol zs(h=u0IT*9i1ScHuP&(~#aZ|rgqw5{gG=XC$R|*(3;jm_wyP9lpxtrXx?|c`Zb)(? z^$g^9xBHhd-!l6V0(5X^LVaea318amz&DWXSVxDt4fkdC%~rHdw#GhFnxSBM^*Oxi>sePl&e+$yDu3F{;z#W6eKZ` zn;PJl($bYgQ$iDHWUZ{hHPfhWPd=kdt~C2F)zl0og49$>nEkeEukkWalZR|H$7rhb zeeRn;$**q@IHHP3#9d-YA_B`Y*bgd5^y_WjM_cK1m9FO5tW3PitGwm{J*-DZ9gSgU zPaGWVREnTJ%DqTSa_Jok3t%=b;2ydl?l%A$K`;1nv=W`$7l~C2@O`cxgrn`gy9~$6 zr!%-6n%L%j$q=0P$>2Y}`21n=?k$fwU>CGj|%etvcQs$GZ-kobD!md~bJbKyTmHWePxG z=OKb@z$KvW^z4?`W)H{O$jVaU;KI;aE&jWYlh73`BW9sXm75K!@XHsfz;+&zg~MX0 z(^qf0g$G77eU(5doLr>1{Sy#+29BS;SP-5U({B$Np(o2Et3^q&JDFx_%mnq(7OehdM{#7FrLnt3nx(&=J8UPiOy4o>wGY8Db*AQL)x zT8gj*yj5uBOf)TV*tw`%q$rR~|Z@wIqH@49& z$oTB4!7|L@B?MYvO-j?@WF04Eus39XZxruPW+@|GrJgbi;ON1mpM_EGde~3vUaTAuz zG=l2kCR;bvX@;Iy5XV5*aD?p_X87sTLyxMTz%K5hm*_{pF0#|gSu8l=Ia^i_O30Me zJf(LcqgJ%g&4&N=(D8p_3jt1-`6|~6&|wg^Px;cl%V8J!{rs(xYZPtoq8fx`%C_zj zV?r4#C-Fp-V3yEkFGahd2T3JlV7{QQspHzDAW*v6JXwkl#1LJq?ZB=n<9^>!)sR`L zjCXBXOd;5lIZ|=n*f+Wn2~Jr%rD4|R{;I5fQoKPnB-*c2kqUQeQ4Um}BbLZ~7B@5C z46l^v)VkEUi zs8~@~i`GO)I*bpudQ&IZaAMietacUe7;OgKL`<}IM^7A(#Y*&ahX;ZV!bGvj z1j)vUUWk>T#^QXx#-3JmSDlpjj(4Li!*W*e6wr+!f`Vox51uo^HZpAxSyrQS6H4b8`MT88)$?8n*mY>;M7P>O#X$l+o8S_Hzjgjr%=fhGV z=&V(pvd>jL^ROfJ)rShkKR3JO_jQe+*RnevuE2&J|J*L21p)O#zSt4A=h^H>GO>{J z`ve7FrOUNOn7t}b^;DY5+tnDl^}r1MNS`87hSt}Lj8KT4vw?+`Frdp)((gRTVaa|4 z@pj|r6-O;+yW&e&`#SdVZ?uhR+1h49_Vjz*1Xsudn-0XppM=L%_iJ}dJ%nH{e?AuU zKMfi_95N5?{nq+tjzDI>FWO|JGZor5k=pgDe17HhbET1cKg zpk*aX)-S>S4WF$rXx44@EBWa7 zqD;eVp0b}HpWnW~u`u$GaXwNVm?p+8w%EM8;2b!Mu*&)|?P{J-4=PY3QVD*(N7#Ns z?2(XBsy(CLBBK+R;u$xtMh`*JNVpIXy*R4Qa{gTHblkq+0!i$UB?7(`AS~}d|CEN1 zu@(B%kbXSxcB@W!hgNN1Wo5NfI!i&fnaZmEJ&ViT?zueZ`WlBL^z~J-@|XSd^YTF6 zQTaWR|9W}rZ!-ik{NK+lTl%#X)w^o})VsrsR4kRohlw&&-gcT1)^)-!dXe4hiBXVq$`2JwiVQUIN_Mmd58A+iSR^^C*Q_z&`Z!C9!(XM7;9!zej4 z{KNsbDk4sheCGSX$ZAw%*^C%h`%NpIE}H1(DEP1 zT>Cx7Xk{B<)pdrewT72}dXP`Fo^gmO^@cke@$ZwxdCV2v$Yij4okb?=#2+60r02QV za-!2?UgTTzFpI&5pQ1fIokrpY+JHo1B?15=yM24=WFm2%wEeoEGjeXU6k0CBIhO>w zIA>p|64O+Jj)}i6qsmXQKE8-N zr^ek9+(P(idfr>5@5s_Pu^IKjw@hJTQ)S`y7*^CY)0qANrck3I)8k*5u|9FAo>lw!qd1j=NS;{N~_CVKBltwY? z_m{Xt3Qfn~#*XqYXw?YIzaVgy4(}%-f9hOLyV9QnrKuhtNWe$GNfh>HJU%5i3v-($ znG6A^gTBaYHzuEj9}{fs1W$Z$lmW>R5&3xjJD2?ssTK)LsRc?z05Io|1?%ODgb&4| z19H&7R5K(G+s5i(Q!9bb`u)PQ)noR3blgJL?qZn4oNhedL!(-?Pa$}TK9DKo2R+Jt zg{Y94s3qg|vWn>6ID&SOU7qt+{?WOxjrhO7*Y%o7A#JPrKd92tvYEqi)wFt}yN3lJ zt1xc!VN8{3bao`ML~Cgoy49+h5Gsq-?;LLGUL28Hiwt~%5_@DZfKH9~=l7$VI~jeR zB3Z0Zrf;P6?jv2;7ELl(Un+Cdx~FZC=&iVLAeRs;=k$pDn4Iv!#Uxy(+AIrRtfkSP zd-$CMc{Xj(+WV`2^{pg34$A11Hc$-K<9Gj9z+IqCq|wa`qx~4Pxp0$N7PjUvRH>~~ z)?WEd5oFE#0`MTOCDUDZ(CMN1;Yr0x8-K5?ezUfy8r7x{yi@d522ZY?Cb?30 z>LlAMMw;^fYOX?g#Rf+)Myg$q!I2%qh-=ynd9JeTpU#WdESa&tpxXo8^9-%MKU zI!U)#Tb>XI3U)t>J-6j{Ocw_;m79;cp?Vy=6CH3t`j~#8;6Db3gQ8Nw40RWWiXgl8 zfnjXaXyEKK-|dS!5#4JcvV}|(&97S2gZOgUc*_?y_HrRw>`DXy&Y=vIq6G&9RG1=$ z0?X;)JVnU!0CfkG5|TzVO!jb=ffLnl;$M>{BBs#>tlf~6SA~Zo*rTAtLiRiy{faRr z-*0(zk(Q|FUg4vM|cUV&MEcRly)fCckUrnd$T-(v~NPPBnZR z9=D=c{!CB}4jo1$=+>fdO0!7TUdqVk1 zspCX)XAZbI4rmvQGohs4H}ZviPN%cs(7RAKPKgIlx2e%5n6H+edSi|4C%8K!OphV4 zw7!o~_QV=u$;Z&tfd^wR2T|!b%+v$^9q}<{SWB2bjwe_3 z+#%_#PlUOzTe=>%&hyJ

OUoN`l0E9I*E}kO3k?`ZM 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

-k{Aiq#>nLwEmK)9MDP2FLH! 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 delta 75039 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

^5_RaeZ*+8nVc8HES3L3OU10n$iEi@S3LU?*3sy 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 diff --git a/Code/Dokumentation/Other/angular momentum to angular velocity.odt b/Code/Dokumentation/Other/angular momentum to angular velocity.odt index 9cb227205c1dd9f3cb8867ccba5d73170f01e7ef..d0faa22d1250bd830d164b5715b0bcca38035f0b 100644 GIT binary patch delta 71731 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 z%0A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⩓$Dv26YoIZ1r&fA%Vm(63&MxWpE$ctRqno|Y0B1x13Q%lc1Eb^XP*vAMN z0H?Wy47g^QQj{$yAAjhTx=SlZ?F%VBK$>roMr^1f!frRK#Cz=j*>jds{?J%5aqwk( zV0oXyASPXHFXgTg4!3_FZaRr-t^7jffu3rHH#^bjs%!w4hP3Y6q`{(}Hd*hOLw7tC zoc%M}q-@gPzTrvC1B#BnZVhI7w_YKP=O~rdU;B=N$SCWK#L1ScI2fqP?LLJjmmESI^p8EiU;l|67SeZT>p+DP@Z8`|1-%6F5; z)9w-Miw-qO(Kj00H<`BH+e^V%lI0(Zq#4fak;htQ$7dMxmO64%evGv;=J}8~_6xw8 zU9zzXb7}7_nyn8Wo24}Z4Fr=5h_;M!K2pLAP6!Ygb4+fVyVSSg&2YXk(=_tr4o7CO z!u<#xYo+-G*%nxtY~UN={EnG}60sUuXv0F&$S+Law=bB>1a5s0xeWSKQUTXA97(^K z)H9mEN;KEX?+GQczv!=<@WxXSN{w@ zsTDm+Iw|QaXiBOON@ye#M5H6E73OXuzrg5>M1hgTd1w1~nDZqKw^JYY3=Z}drhnf2 z1h)Ka*r;%vTDV}z=PQ@X6-8x9h7$#%@`>_|7!{8U&y@0apC3kLT?Sc99V`-)@2}}i z`QX@o?{Qe|Iap7Z`62e>;DXdvXz_ax7j@Bc*-^2R!Aa89x2ZLoboG``BjPXfN(g`I z1V$XEU-H#lk60M#U2=B^1p!|=x3<`?AebFppxn%o<=o|l4VO)RLM6F8_c`DOy24a_ z5Vdr<;R#{2w6e2*Gi=}+OjCC9of#5WpOJgq4p0e2qc)l@pB`PN%R8wptdKJ2RM=@G z==}nI09!C_7GJFc4S$)106StW8&3J!+xJWL3C|r3Vw9+h26ctd_mYM=4QX!((iZ)1$}( zn|;Jitxx0S<4e@)RCcZ^fDVU`j=*R14WZBp#l5BgGN5)d5D$uo`o!i3Z$ zVqb@nC_KsVUf8|KceT%R92q}QSmAZS>eeSO%;VEMoCIOASH`h<_Pw&n;hv<0>@^?RD)F8^*Y!g3jDLF;ID_huok$#a?U=>GbhayI+ucO)1EXg4Ii0e! z*SBKcZMO!39}J6nLlwZJ-WdZ6C~5uv3lG-B@Wg{uyS5<@1mM-TAl8iVMEDJ5KHI9| zH=>$^5uC|c<|2Blw?o!r8FjOYRy=Q3OI+vt$&m9DS~Cxmh`4bPL5DT5{TfJnW~-9R zQjPGe{rnV}qr84CtZ|n~QO_KQDDI?vJ^Ha%hg`@=;yFU3S&@1jXk&uT=V5JuJE>5+ z4j1(PZHwJLy1-@-Ch_M9Jwv8t(ndCNCBth@6=u{nH^c~Y=m6<=PSHH?b(zbhUr`XL zQC|t`obYjkzV6GFkZd%wi@t(4m;xwU>U_VNkl8u zSZ=2v()^!tS^{Px=n))?vo-FJF1U)6i1$S=*{p<6PUqQv;jK zZXA5Up2tRY9L8V_Q^BCx^IE&^FkaUdSd^M9&9dKgaKZ~=l%{2D| zQk3OAHho@#ciih|=0LDiAM2Bf@* zX{DsYN?L#s24P<_PT>SB+m|HVA=+mKxAgaxZ7G&*TS zKu`(Ya894KiGIXws*PGvNZ1F8Nm#%WI%tq>tJIQPCN_N zmH=9H(j^Ue78Egf0eyyF3JN$prZ3CsEl zMNi>ztxr*i?Nd#h73+Lyr8HdyhNr(iO90)73iANAlRKqG;!~} zwj3|Q)>m64iBCw!jpI8{ zV8jXY8!R%h}?;xFUGjME&fvsJk${XOPC*qt_?EuxL5& zFIWT%b$=e@J=qMJFZdf5aZeg}f^iY)-Pz|7gRN@&U6HBJzAhYu3$Hn$jbWuJoQ7?K z>G*>aImO{?Gd&YC2v>O?UABsmhJ@4)b|TOa$1hQb9^t9raMD=HD`Q6xny6zw(jM8` z5~(42I=?VJUuL(zzqhck*K^_N)p8X+)KF}j7b~5mw|t0R9skJLFJSjFZ1)nzwngOsk@l8Rc{JO;FYfNH!7YRU0fM^+ zcXxM}Cb&azm*DR1F2UX1-CYiO*V=pSbM6`U!@VE6pHV$#)o7hn^Ql?CfAjfOM@hby zr3LD8_2lHLs1xvTY^eLJ2)n(6?(qBN0|02~mZ%J9SlAEsb<;pQ_J%BEq>8LK=z@w$3iu!U$2Z&2UWQ9 z^HMg_csCzCW`I&@13~0eIT5PdI|m}sawfm?-F*f30JzSFc`vyod0dZB@OeEKM9zP2 zi_iS|c5nLgqT^Ki)4NAD3G6$IoEIS4*8DWrS7@{Psr9^6-Sj(#F$YAg&pQV!1Ioa& zl<}#tb2Gg23SM$zRvGG1LV#ge146Dydn)1lhoU%nLFf86#vj0QcmCqE>f@Lim#GTe zslnZHCDOrD5LWIs+ETm-ma*ueSUt{Xb<;;bv=w_F>N5RfR*nx9ZL1lM zZ;g~kb|>J7)eW?d^I}TW?~CCxE(>GArTOnuk!0`Qf#zOV884ZMjjCs1;`B9qt`-iI zU8#qc$5Yi;d`*VdASB-82BDh21yODNG$aqvQC!%6R;=E7~b1y}&uHnEh;251Eqp7EZ!a9z+!^o6ZB{$yD(m*O`WRxfhNKhoz^h&^nkJKH~v{0TtKL3&ld)x*2j zsv}y^T(BlaWwm70SooBnhw-wsWMO;#=EOxJQKHJ6F{NEDEwN+?CMJRhDY5Kx+^{B+ zh9*jeGzN(pS@P>_8rMloUTwX~8uWrTA=_};~h z1^sr3JiO>IsA0cQ6{%N(^!F6(c0WVX`y3c>vC2RP$@b@w>34H zaaL&*D0LBF^9DO{l$J9sOw?Gz?n><3O`uGvU4yfjN@t7svfcK*Z~i9|m#*42SttR_ z_c{s!7~?;f9V=HqBKeynesFQz&5O5ii6EaP5*6+fe{xDT}Pc)3J$)kdI2yeag1tk1j$1{2kEP!0K%(4k zJcFq9OSn_yK%xC}+(Lz<>^S!tW5fq2pOz~gE6MJ1ur+$`u#8iJtr#)L=5+i&>8FXR zqH<|L(Cf>}NDBbHBgWRU%%2@zieE#DsWd1js&@u02bSx0cinDRa860s$01u>euNd-9%cB zKwPDuv;Az1gFm+7T3LY29gRrlb0tTb$@L@RMnORrOYb$wDwG1Xd66-hfapH|yc&ai zXf+$)%P|Ady=>=keTgP!CAUQxaM-(gj9E6Hi2HEjOXsxp7S`8euD0Xz0EUN?qrK8u zDd`C{a2EQloijJZY17wv$=s~9}c!#6fZPzGUqwn-boQM#DI zRniNIPXM)LIhtmM$5Xn9XHmCjnRNC3`W2CQ&TPw1U3;tN7O#V4c^qCwX&CXyk*rPX?@_SrRA?&Bn2O=D1yM71?ysvnQ z;Sdy*5c>LxzJSjsG}2IA?t+H>mB65zyNtEhxxKr!wbhKbJFoRe@Ad5)#`HGZ$rv}v z7?R;0A(2>MB(ddb9j6n-2mYdF9wO7O*K})7%`5vvDH&C)5OO3Rlqp05JK-%Q2rul7H6ifpINM4Jxt}N>5!WUhtc~&%0$})L2)9b?fbu2v zjAB%s@V6oQ_PRj%?hr2whfD&b%zo*qy}j+FD8#q28aCv+hJIGCQycXveZa~3)3Zkw z&ca)}v%%6j={Zbo2exPEEBB^ob%%==-pM$?cei?~vdEs$)m}wVdS0q&YDueEO*>&u zW&>}^%3jIim#1tXpH137NDpzlw2;?Eih6wO+dF9IzKT#XhQon;*KhlCX&DNBzM%ag zUHsIjM9~!drb0Z`y9jYIEHe@QG!?;A&R&tPMa@|6D-{9L4>XalG7A>_jW9*ymG$lb zN>i048C}yZR;*YYb(A>I@02wWS?w&2MGIv!<`5at@99TP**;p$x-HS$U?2&FxgJbP zR~cYzx(+()f@b)+He$qH`hzw_u^zjRu*~9n;Db!@3K%r)i$YJ${`L+DnnayOgkU&j zb2W_RbM)2BIbQ3S?35;+`(1nCg*Lf>T|bj`01NGlSX$K*_z12-n=i}LGYdyfCJ*z@ zeJ*hl3uBz^*ntWMxyjHjC;`1mTm;x&crys4i6)Y+gNUeYu3aEqmTiA43Yy6YGKfR} zlYJL1IJm=?Zw5YpTabZCVxgt`W`axPGLsZEJadd`ZbX|PmkbQ&9Wgd# zu$Rlra5Kpw{zN8>hE$(J*_il&54KOxi3qAZEocejvxIK}ljhLL1|h?thYv=8r!S~m zqNUbuKo()%xfo1fTit!vGlNtxb!rDK(fP@*3BXIp(X97=Z3eMba+&*%eM5HxI^ zf?iuW+G!+hBvRrO6IOy*DF$@GEtCJb?^_8yUp^PU@}gVmY-id)ZAY%-JH7*b=BZbg zy!7&cR99XKy!En40C{eYtp2t4tc{oVz$3{KbI!#Ew{YJkN9jpyo^7YZQF;DMKIlcP zn|&$tLjaZ>8Tb4uI_1(A6AyqJDnkEkDtHASp%F?7&x^#lqRZ@@EeCWZ3TIF9#aCxC z`xMu9{VrCtVUV?ltX+emtDDB1K%wgPU_(UjGHup}tUlLr#dHaTpIO_(8cxl-H7=S^ zRlF&JD#A@)L*Q&Wu>Q4%Q#$oVrj++{@Kg4b4}H^NS{}3}0kJA)PiK|clL?&WwL54z z2Xvk`g?#*y(ufv5=^HSJ3ObruB3}`oIyTf<90=F$PutLYAsZM#v@2R&=sA4UGi(Sl zB0cX;aam+&?Al)vfs0VvP~lnW>0SttDtpe@w=)e$R6W)<-^x45}n=>;Xs-QPX}p+LZes#t@bI zGa#<%Qpt@I6GS!{yw)XT3))!n$iU= zl{j_AOj-Or)e0>7W>0NHCL!&Jk(Po#j=)-2Sx(|tgC{HaAM#-}=_(C_4aaJ0Ld*Xc z5iTGv9PwLGjYXXKS-G4qb1R^((JecO##9bYbL}vx$AMxYen%UirZ#pGz3TGt84_6W zsEOZwr|O%w&sz`xExYTH3HK*<(bv4B>42L|UzSupOa~|`X^q-eV>{c+%Q0fr7G+Hh znM-!wz*>5wRKaV^m5Y!(rScL;2nWMdXx@c3x=Z5{BljLaQd>kt=}KZI63{*Q>?poB z8oi8S5)g0Le+>5h>l$GV8vFDZs2*C-mbW4!Cw1+hnsw&*xHq~YgxHiWhkWYNHo|Q0 z9Fb$4s(=6q`r{N0#26>$3=qUg1q{$MCsrk0V=XRPJtg;iJg#H+4m_6QrxFqEIf^FW zyl`|45m|Jut}Ow3zp}b~jhn^k8P5h z`}YR+(p&L6*Sj}F%8@j+68~h6Z4y0WK@2uZ4uH51i3M4Nxo1R@Sv5Yg1CeW_6=wi} zB~eDPXJj{5WzdSF^R-1^%G%7+d1|$}xUH^cU$O-3R|ZL$A*NYDMS8Bu1BF{XfBcTn zRZ{ehP&C}WhQhb@41Q1~K5K@E;uZBT^p(PV%gL8TT8BRgw<_NMNmJyewhrC;7*JfJ z`23$Eioc_RM1^+H-T!3C8F&0UO#d6SH)wT%cK>Su`9G%OouGUFG1ch@g#=6qlrRR) zN6t>h#wZs@+0dlpt?QXy1r|tr=ahw>K$nnfm5GI{^sH1?a?2-Fb(YLa^wkf7Zu>-w zX0fkl=6rNf;b*a#A;4(GBHD{&v2^H;n9zVi1g*)xU$MfbZ5I%^|^A$!_6cB*$*4A`oa1M3U} znZNF1NAKUf*1p`9pKyQjfOYEdjF_qSil3xHo^g(9D;?+9-#l7?Pf;=ZbV6)));`^u zQm~f55bU;-)}a38sD*?RbIwxDy?r?TRpUilCvS7;t?Gf>34Dp1c?nQ|Oi|z}t{X1t zD~rF}LN}<>%88Wi_?ihEd-`3y23zX%d#3)$__cCA`r-?kC^G?I3t)tQErLm!V!7&Oy+i;f|GXMAE{1Z`mGN|bYX z?9XxR+pWqKG{ER-GQ`(EFr+~H)`BT9mKSh_EuNW2fVb!gng>ug)Jo@a>lnzjwl;_|usjybvW8@6rtpg0w(@vm zS;0)GNB306v(=1k8$R?Vk%``88HQur62+FVkJaPnX3+dKcfGX>Ct@7Tzif&A!u*6we2)J+>B z15IdcZPuOw-;DQDVf2(?FAJR5U_wjsxCr}LEU2j#iL?7AwCStE#Lp<^gEEHD4{ADQ4kOQn5hZfUBB zDoce^BP#*1OoXOepA^F`chlvXu;B00(<6PhySVTw?XWA8d+`@nk+m-qG-W4pPeY9K zH2eeCREv>+`m<5K&Sf4^q|0@79rRYyLU;ywJbZz|xYUnyQSBEvhi4|3H=~MW8;fJ%q8@U5N+Cu)sHP`g}YnL=X%c?3Dvnk~ig- zk|IlW*~bsfm}gux{!~$8+J@^V1*hQ*HglMM_0q@^DjCKNf@|gMqm{Pe{F2`7s=E8y ziMD12{_u9TA4_Wuj?W#9X*UQD6=8YE(0ni=-zV*8#o0YHXM@Y2H2j2yy!`nHqm79i zs`?4Il29(X?$zh3QBRGutqw5$T!u5TuaD8=!YzYf7?v=p!fNW5hgve-5{{E5E*OwXq`~zYE}~#AiXf>sqFOYh zzLyAJFaJw=cY5fq689sW`AY8p>VT#DKzIJzs{$+>vzi)~7)BU8(`^kJ^_auf4VEqR zWvgcFNo>8nd**oTVzt22Rdt{Lvf8QyZcc5g2Jjp-`QMjGXf;a1R=#578c! zcQ3g|GH+|$_75T%JiaXVYcnoH*ckBV3}?a88?pZeb=PE(X18s~FR9kayiR)f;)T_S z5UjI)mEt{`&!p}>ns8GR`_Sup&fI>S;wa56k$)-A2EA&3wxXdj%il08`qcuV*$4=M z$Er07Nx9x+33Q(FVZ(m!@@2yw*zns>@EnxU*r+DiwyMy%IzDt z(mw6ZIAWvXzZOb9dTF*dPgWq5Do#39F4{8SCMnvAuwGDPPVbe$ge0~sPMY|sAPgFm zM2mE=P_z|pO|InlCGPSq2RRWaifLXO-o}qow{s@&N4V4*VlkC`c?&+2$vLgRo&z3HMjEFdwNv!H+DN7M`lkQ!pGG z{qLl{Cxyv%x`I1BHlPa|6U4oHf}dar2)q%&prQZPj*qD9(!2Kss|7w0)|4ica@R%) z2<`j0;UOjG2fDf-}XAtdW7GC}fXCVTeDynXZegVN>F9fdt&Smuw*eDgavd!A@%3rltKQ4vSO zu^b6JY>c<3^LGt}FAcz%8Pyp&hODk9ygx2yImv+oiIrZkTq#_z;fI<2XZf6c(#fG#=dEYzQw=~(0eKHHTy{9<CqT#i*P?bqmoOK_=!PH9=5p zgCTuRwJITfu93U3Kn$4~?Ll>O@f$%dywO@fY+2PHi$f)z+M4|sOVC$u1o9C^xrNqU z?H+A4*e^l*%k=+nA2Dzm^)WyXEL%pkUGCM=uFpu`gcTqtAt?lWD=ks~h(X1Wbt{gK z`aVZPQjrd%?qa7AWZZiez5lU)dwq4^)A<)?+}-M!hU3HQ?#upPz3#xD|G5-`m1Q=~ zO$@{CV=2T*c8kSs@#2yDi5|ZkhPtD!JZcEPGp_in9JQ*IVUV!A9)+R^R-7WlB;@J@ zzYqo?1|;DGFRB={oDc>oD*rsO-g!euIyjf@s?*+b=gCMUOHHO%$NGKSy+?ny^ywp0ICbN$Jl}v=gCq~uX98HS^f|BiZNPVV zTr@_7J!EpslNr?z=0=FRYjHzIt4Z#JepVn7gYoTB3t427Y&T_>jL(*p&F(1L4sRv8 zqceG7n`mJMi-I=iP(3F>Eo#q6KWwu^kX5UP8L=ERw}aajER4UCG?bKG7+bJ~AK`Q^ zr%Y^i5^73j)EvA_^w}2(t6zo;bMBPB=?^?!rClvPqzz>yRrL85ol2y8PPu{7rs0lP z>SA#gKzQ`wKMI*n5$Qj^dg?%{HYetfq~}2X)jExRqC}9)%2cNG!tdq{TLXBSm&6-v z181MGjAAz<)UdR~>`l2VkSE=E?!R_4^(;T6xKd|+Ur3mt%vb=PH2M+BW5pjplD zM_0uovvSdoO*L|3gDZr~Cid=}IGI7$_$GbfrNH;P+%Lsy1a@Qdj9V@SdSjD(8Pa9> zjx|X3B&Ky?^BH*5)Ce5e*EW2m%Vg1L@-61X8=yR&IQfRlM*!KX6-_hE$)zMrW>y8t z)}eR%Cfz~FxjU{l1Z%|YqnwDy1z)uUtsy{=;HSkpGzMCp()#CP2lK7Bc<4ZL0Zo7X z`-%IL8LusO8ws&0Le3R)-$V>DT_7>!!mmwXP+P+V6##}l5I!JDzY?&fbE%F+;te%A#Qvkoj{RTG?ssHo8B+i)EzNkczK;xl{ktRSkBy2X-w~k9`W5w(Y zU8{J7Xd4)l;#XHW(Hb)vm4GRK8MpzTEAo+4EdaPl0*k#rp2^9}Dd#mI^vJsQmlbv0 zytAt!3CJ{`ntd=jrA{ZcB4g~kf#=!Hk$3q88$Ab^tw>kqDAP#zyh-fLe?lMzlo-oAyB?Ej zMCq-Vz0Z3)9lkvcj)2vT<9)BLM$6ROL;Gr71rGz6-4%j_L)^Ufsn#qP=%GR9?#Yq| zj*ARan=BB~QEtn*%QFLh6AP2eF{_Kf{aeX+VGPt%MOzW=CpfhahXo43GDVs`z<1@vWT9LNyV7EUed7+1lyt^Hj6SiOZuj>lll{cH zF#GBbkj#LlRGyLytu#z@9ymK5e;<8>CDK@y34A!pV6dbcJQ0c?QlcvL7-Os}1|TiD zvdO>%bQo`hs^_o~2s})^RXnloA)W+c-S?5LRb7LUCF)S^3O$3Stg5mVt)D6-=_*WN zVXN^p`-$gaaI>Cd8wYMN5>CJ7i#9gLFtT5`m?)Y9`gP-|p3lSn+}G(>10*5_F}1iB zq{a5SGX|1UQuxAXL!)s#2{%|<)06h@%Zvcw9=_tEM}-pnS$l~nNhdpsBZd}rKwFW-#ow`5~%v_1s6>&!}XF1b)=w;qJl&xm&}{vlTlznKJ^he6Tg%!z#P zdb+>UV%-}7R}Ma09JxY5U;T7DG4NR=`s7ADTY7R>W_^&X19krpE%d${5Rk>%Fq;FN zX+KW=0^4EQDJCIih~VS7yZN#sX~F`xGw3jjatKvT$zhjpgr%T3a5Mw=tS|6WDni&i zn@{A-@`#&Y&y%H+OywxAFpz;9jDYWk+%5M@LCtEn*?1B+u z4vOXGVp>61dZ<}BoL&8%sukU3RMDAa1>LWsgP-dbWeNCIhLHuc5E^Jx`>CaY4PpKj z{w4)28hey)vWO3uR92K;C`d~$$wH8X<+W95^Lf51DdmpIDW}mZ+h}IgcqwKRMg^o) zYC)sH+5)Tb+~r!XbWStaMA0H)@Qj@xYM&vnvM?l}w?t2HdEnDN+tuKSso&U%7)#Sk z=B`dyo6@NcvTm!=7uEi#m$Lf-l>dPYh(qS(+dTCJi-1JpC>4gdHbm_Ypi&kTIzm8T zh1v<@T7WC#Ekyw=SkrJQR!bQ038N^)`#z1Dz^jfFz%{=_R}bvalhV>pwo)l8Y~HRX zZ&`+wBk8RB;wC20z)3d7cdgypgqv$Fb%0irOmi(5B24aLPW@!0n3bfe0m=*cm6qytlV#&m zSpOK7MAW`~5c6QIBs5X2M>KD>c_* zz8o>j2W;=@=uwSBLq(wq(J`duL?n&4?xaK^GXsq2%hN;#QQ+Q8qE;Y+;3opQjiW?|8n~OMVb3R$l-vpCT55K@90l3R+iW*LZQEZ zJH%GO{*$2UqUn$Y)kC>i;tMYoErevO|WJwj6@G$F1~I9Y)77JmPDFtl|s?=JyUHvq$x0 zo4%iT5M+Cl%l{hev|#0R4VFx(4LE^VYpB>imu+Q{zcwgtfyw%U*b0=-wT>qFsMqrs7`<0+EYo+M~sm8Vv2qdMs1*Z)Hz)|uBV-L z*HgRh7fF&FmCee{d`j3v#ApjEQMZ-i1VF42M#xuwd3%IL3HgajtPljoGysicHDDLH z8vfgzBUF+vio;mI7!{Dq1U-Sc^9RFzmeU2`e^G&GGbk8*$u6Bv|Bc}N$*jw#cE`Ps zUEXxQ`ZAjU&fI4o6y}UCcB(D zC}BpM&M4g=U>hoRIOyu(bDrkBoiA4s4q}z;8j6ukroH18w{g)1XVUY8pslFIY&&H^uyxwXLE+u@_BQW)xg3 zYvd@7@crc_zXuDLptSVL1Ib;b zUi#dAoAudL3V%<^j@qYB6}$Yxq^0-`*QP@vsz(ZjnzD;>JtKN?sow=Y6k17e3&TYm zI2lBgY_<6(r5yw)qy2u;zn;D{Hstc5`{_3d#?V+=vVDE4rqgdg&Chh&!*N zJ}{j4>0u2#Z|MGDPtM1#KH-F-A=+k^F*Os!<8|!xp6C&1&}$RJxj~6e#;wSp|BPk_ zcBJb^B$Y*+fFxv&V5~d;D|m+B__OJ(Kb-r?9J2km zIlJG0UAF?wYD5)MSGHgiWZK~fVyr>yejwXeEn{Czhq!A$c4yUJ__O`ZTIaBO7m(_D zM|sx@>N#1@Q_;m`egie)0=}QaJkGlCqFDWmh|XjF?OYc0>qFzFYRluk$?1D{v4wO; z`&oyE78 z*73Ug1C462yb);kJ7h%QG zpFi!MGMEIwpQ!vU!q_~w0#&D=cxrC$HBzHuH#AxeL0-qXU4de+dc?KhCD-Kh%F7&d znF9BMZQn8^B`3qaaR0B~-{DeTZ_Al@?rM;6!`<$Rcr&rPuE9gVgYx=Ofq<@8*UmTV zRwQrZcfc3eK!1Vz4tHtLfYG;@DB{GK=_3}B_nP!NR9!jsJqhUSM{YlFU6);+Y1O>| z*%~m@y%I&nvT)*1c=CF~FAc_{pb?_=wWU3E*2x^@QQBn5dxxi&{rs#T9FTVf{|Q)O zN_t_|H*f&^7>U}-&ET{EXOuHBtOkFamBmP@ol75D#Pd*~DCbhVbHD>Iy7Qw9AC0Hp zsD&QEDUdP6-Wm;Zub^u!xiDETMs3J2=uFTDb5Zcw%DL{C;pAz0SNU+D@g$HxDh{Gv z+b5T1C7ttK1@1((h={+TP>0~Xa`eN{e1ab{s6lQb=@vryP;>=&WVYEewnAk;&W*hh z79!tML_8o|m$DaCkI*cn5jr}Qgjf*_oYF1&#crJsJs58Qyt(i^M;b6#{tSs{Na)~6I#-w#^;D+<8&t!QdbWg6YIoynG6G}WGQ@(jGUhg-y1z< zgreW{?0OHdSy1t3EQY{LHoWyVjJtzU;m}{0hKMU5J-RI;Px4k#gNY$<|KGOqEz#z+ z&IWe>*t%{MMvLke`kGye!~Tk<$rWvm%kvKv)yhTM7L+hNG$q!6hG9-QIb%g(90E&iw4u|m1COEVOT z5XfWhY;$ucq(reDV3DrCE{|6!>StkIgJ@3;z@%vj+5S{_>DQJ7d6Tx_3#vSN@s~`wU!)-pyUDKFm7!t$LPx z8(bpGRq|1^OPV{ywWA9b=G>nNUFVK_=v>vb^62}}Y?oqZX^|kYSglQhN;q(HI}GW^ zJ2?|;%)KVvA1>`}(q2N~D7;Y&Nj^5$&6f@6)E}p_`I{tDR@mdESJS{&ah71YDC($4)v|s*fX1n=v)4N)3gR zt)@yPVYLSeh-2YVwm(Qoeyz$qothSgjfk|Um=mb_4hZh2Z+=vqk1d$4oHo61S@$=> zvibrSUI$_H>V?=^Oz~y)o1!ZKTfgeQ6yz$?YU>?$?~4_x-;rB<7Ty+j$V<_tcrY|; zmB%W5AFvg0?Pp8zKfmhoO=z94eq?BZ2JR$w7z7F)gT($StcSftCtg)ri)HYF(J9vF zv96^WhvnzwB~Nt0v*Yq_EN%BBRJHtbwW+s5yDukDM3A3a{yKm z>N=Z&2;4evt_&W}LbkE}I!(Qm%?dyHPAXw#!-F+}o|n&vp02VwMVBH@>vtTrLUU`$ z5Vyz1XR!HIHIe+NFJ($ITGrYW->o#(EZ5kuYiFPmW)^YSpG69XOkvnavM7~rWPo|(191G%OyY!R^S zoDb(Q;4s2|2<>`d3e4SqNF_0L^aOOku+uI+->o$cbPhBdbOpNJ8y^#uVs zywM;#oNA(aRsAzoiYb+lz61a9Nt0wutnV{IX7K<-BetND6N-#-lDYb)yQ)*eOyYWYQ+QcOxGKPE zIzQUx=%<8Sz$8XVWvQ6ic#KoS%!42zwS5SnmW=q7Jbu#eTJRfLardcIqxASL7I*ah zkm=G74}BxWsdi6?)UzEf;WyjQp^_4sYB{NoU-F>*9)C4xLR>+ zpX=i-6@<<5Al$UbfAgwMdaiT1IwQwndV2I2nz zzFGnmP~?b7!lr9=yVvhAJcpIAmWg%_6)zS-zZ%2e|FQV7|gHtmjTE9{QGfwS0# zU}FCU6s81X=-JkjtWMI?%OvfjCm!K=iQ)*!xLL0LKO4~IZme3(C6`KbQpf$Tge$5B z75;bBV(UshXC3Yh#!Xh6{HBo9R_g}K4!hfezOAMt}oDpjFO+a-oCx^vxWQX9kjuDhh>mtTE))}@Lnx?EH*?Hc7xJRXU$D!+8)QiJ zgbV{0LDn&nD&l=jmN|Q03BL%wtc^NUfL8bt2RQA(AW7hNja-sjxkdmk`OgD%o1%n` zKf0OxdzA1gReh{H5Ie`Yqef}5II+-ugQqUk>sh}j?fNb|oe@{W5+9htwVm=&s#*&0 zk%CL56L-%hCh;wd@e?Ocemm1XMc!e&fKEBkESN~0c25n@&kw&-HJHP;XtNf8P z;r7woL)?cNXdf*8b@~99V!bg$j0DGEP~>V1FnwFG6K%Bc{yLfT^V}F;6oo5-f5@?G zRiPhSjWtHBp*91(bYJz2`UqwlHn%#sk8Vq&`qcC*h6g_AK^9s{Lks?6#dN6dcMH zV-{Pem5{+bBUOpB6yjaN8~xz(^Hl1wI&+-c2oMgq{a_E8=ar(GsZPHiHlvhB8XM)0 zm&%(Dbq@Ck{~yCO;Qtxt4Uqd}27J!Z*0c}R;WGP`pGuM3a@T;-8zsv3JH^XKS1!$B zVT`hk8p0(fOc1%=$_%e?m2qSV(L0!u%dE?$0Oz76^HD`s;}W0gB1L$`_B>Bc*4Ezl z!Oj6<%$2|y2i=~@Fq}}A@vDlNbQyrtyoCcnG=ysMeX)cHukxlqAq?`u0f%0bsok2S z{>IFL#mDrs?utZC#@0@+!&Ocfq0E_ci-~0qd1umm)DhdW!^&cN&+Dlx!f*0O{@_7x zXVEUr6Yq-j9_rQRA@uK;iy@+$qVD0B+0)_SxQIyK5-D^Z;XLnKRM@Z8Y)yaxk_^ii zzG_Fk%;q`_JFd{b1ySoB8`4`oX!!MlYFh{fO2-@(z8hK6Z3`i<&?@u_Kknz zA^l~kDo2xZu-@S&QR3m+eHic{@V=P$xt2UsLu9?u%Z{M>af-@Z^EpBDOW(_Kl-?z{ z>wAxagWcBFgWO$nxpdHh8h|3x@~W?ELCZ`Y4!7I?r2AX+eG?`@eO0|gAe@2n zG_`mk@)48Wlu~ls(q*mTq*5a^z8$#MA7lAaVlr0)P~Rk@+^x>QIwggNt&RcIk{Q#g3~j$zB&4W4p%^s~PNzs==7L zDkSE!O^2mIXKrUwN6aebNm0mHQNB93 z620Eh;FQgS!Do|yya6gUcwbE(`Ws|y!he~8k6s09t#s8Q&SZln1742m{l>}nS17*- z9j+{(8cxb0nxCT@sC+YWJ96D+-L+RfPuX5#GgyZ`vPgXyFJ9+dM<;f^W-1^a!x#33 zdON6B4<~U@z>^QPz;g=O@~35-(n(3ad!BKX_ulbGd3X~&_XZ9N-WTtIoBMd%=a!;g zbL?{-DwNHT%Yzr{4iAxF`#2I&j_-(%2j|!0&@jCR_NFIIb~Q{WfuB0n`fB`bvRmxM zZi8P?Dt@#+r@?U$Aq)+HK0`d2qsr%ujV>dzXORpSgsuLbKaehP-f>FvS^rs9R!V)= zzWSy_96)Sj+zANp_#8dhd2yHoR95WXWs1k0t_cSgfxG4x9LNQ`ixZd@7IYqz-SapZ zc+Vt(s&rJQkzvPQzqh~Ay}t#$?{~b;oWE9dJd^DX`?fa!wyD#}`(C5-yg8n!GGFy!C!0MOrZ&-p$V-$;@FU&SS?BF4r0MOh z;cdfaywpMmYw`UFMcDuSY>Uo)YL?C%UPtMW5uT1w-BBrcynGi9LP3g7!mc)=xJo-Q=4-6{xxx>-;V_nhly*@#Xi1$;|n^J#v|9v|Z zliTYhyZ80{`-P)@nomSy1ug#P`ajITVy8}9B+%~E^!)Ca9`Jl@Mfr!K{OEwqazz?L zBMGD-!bsx}S#7%iB^JiSbOjlxHAncyulfY7KT%5?c3olEOlUffDs&b%0|E;=cf+LU zsOHlVxoPjuyd4ge+~EWrfTaD-5?tyRu&SxVr5VX~*NB;igo>Jttw>!3=X7hYebuv! z2H=jh=Y_dGVb%Vn(NfX%;=YZz`uWIXS2Kb;!cTa2J18q4cW3?mzy-*^apGOnBOLIy zJuG-k7(z?jFn)3UvK`M?Zd8d39WYF0mLR2l^`0cVu#U|8a?!j0GzD$5fw{r<;woXk zgB}Vqt|o6W#a|*KZO0HB{`z{{@y6#50Gsf{>1!G2wsfy~?dvD7f3FXjvL^@T%73Tu zkm>o`D$HG83`)=2(j7EaLJt&ji1#yBdOtK^_CAz34sakgsslsy35dFR#?Qn3u9D(k z?b1Y7)t!N-KV8wU$W#dmL~@!9ddn?A$1yQ}oAYm4$5DC@e}p^ZpCqg55ZD014oH0+ z+&zJGHusq_BlZnZPJhj28IRcLB`G_1jwa3SohVOl4&}&b4x+CaU!07zsF`fr-sD%+ zq4Kqw`5(Pi;JOPy}s;l$+biN294a(PTd9uA|l7 z#TXZfax`st)m$-9#VPx0b-+8ci7GtJaD%K`y;pK$-U}&b)k3909m=4|jXf;#Hr;^2 zSQugHZJ8kQWI5KAIfq>T>F%_B!yH=zkq2&r7y2XRzKLho?Cr9`&2j@qRLlEK)7nHp zftj_x?i>+|k(vq%=nlp!)=`P|;b@WRwOG7HqNuRcA8|sy;1(@nOn_;wqvu-A5MEae zML0huC@t$_Vsq7%O*5P&#fw$Jni)R46W;`hBruBiW`&AZhO6~m>LbKXp?y9a2T{B` z#dxi6K5|xUVz6HGMEh&dUnBEXs;`Y)7Afem`w^4^8 zd`@~G`i3+@oInjU^@LzPJe$=(HkF|mXKv)qmTUxdPLHx4WR}|pH&}@L(S_gsY)F60 zvhEO%=-UYs7hK0KExBNUyd~Z;iTXyOc?Qz01;d@F&7aDji?_`_K$)2O3L z`#SUiR95I$C+qo!_n8RhV~&g% z)&&_q!}(-D`KTEw@kYRV?`Y3`^mq&UJEU;rW5M>?u7n53JsXU#9M4i_VhqhUY39{j z9Yk*!I!vGG%C}SQ)HY7fpRxX}FFSiM1u$37RJBU$uWkff^*)yXC zwoMhCiblI%JMdjN-K{uoRi8Y%CAWea?KYd&Ej9?QQ8F1yJ+H+cUOF;U-ZCy?9za8V zccO#+kwspIetR-rfr#<6LC>&nDC!~<+yT`dBr;?S#&XN2%Gyjr1@}HND~Z;5-Gbn& zuL}lEMB%iU8lfbQH$*=ceH5CJ`Csnt8}eylukH@kxE^u^X^kdkb3sT9mHNj>wRwlHt{Jlq%`omWCbxUoGvzW-sVeQ^J{w_u6v$fDxHiZ3YN z^;`Z|YhNBtRTuq#@3{?xcrzu022%q?B~sBGg_Ja+q7oS@l0quV)NsoVDH@a}G*B-R zDM^!fOGQ#DNrOZw^xJ2hbI(2J*6)w+_xaxE@znk7wb$8e+-vW1?%Dn2igzp4ZRl67 zT%zV^@v9~@eI#l*=I>?}+OZ-he&+k0yN~qmHF?N){|iltIF+uw)}HV4N!9RjhK+ix z+PzbHUfLy5H7=$GNoBo&$M-c3roo;?E3!2&=tU0@T8^x>OPf90 zeBYv}<;#7tLKo*5Yuw#1=B;I7@w4^4igM1F89Yl4L>C8#hV^QAci>lv-!i!#a*B6r8K4A&w$H?a?4+C?Y!3O;>MWR3dOve8hIn?d(Kq* zVm|^O!1$D3*)cQ92j85P|9$#m`EPBtwERWc0VespsgJL0iX1y})8!rSLQk9zxHE0h zl+vNE%~ss>xY`z_+&Hy8{9BB5o9AA&Z!J}})%x!Zu8c`p8aLRmwT%BBY*X>- zbCF!L+4*zRZrJ#g=EZ2GKbUpRF+4QTdc@@W(FG0DPWtX$Rw=(fKxgj$?0}t53Vz&c zePaCOi@#@0>g=mClpk0`nvB<2^y=Q$4F~g67G_0y)>)2v@Nd~|%lNiv!PSn9-mP(Q zA?`Ae2 z{X_4imbR)qzr8zqtbB&vM&HC*JMB-C?z`-|w0-7=VC&LtmUk78$5$hRc&bzx<4qF$ab0JMN)F;bfjI7(XK6Yjby}#_}HZp3n z@#O_cISS?nCpzYOj#D#n^DvL?cXXnT!V3AM*&U8DHU3xJXXrkwI=11JsnYO8F+I2K z{qi9^`BuiJ#J4B1HqF8(B`X^3)RhZv0>QF!Y{aoDaK-#lM`TTooAb@z~dYv`FH6OI45xa;}q z$OaoNcbWW0tNRBM`_Qe%H{Px_5n79N1NK{QJhQ$zZ1pthBU*VjLGiI=A?i!kYwc6H z`Eb+%52wAuwkJhzI5gxfy0Ec4!}id7rGqo1#(jPGaeilb)ziMtW;cx=RSg@sQ+3FP zpwZLg8_fr-tHkfop1&lapryDeR4`k{a`dp5aYkv!igy9;JonMc_53;RpQ|&D&3$4J z^7+V7?+4L(Lc5kLZUEOz<&mdwc#(XxbSapLbKfTm2Wip&`$r-?^)M zVu+8%(HWj7S@$r zO)iV#Tho0L@6S=#b$nC-f%N@aabqsQ8ckq@rPpY}Q{_>>oX zX`;%%XP4_$XXz-`=q*QjUDXqGYln}LExw^Sp}fWTeEqc1Gv3WU>iF8$O~+9Becam*mSgF0R&`AZ6E!DK7p8ch5urH=X_u`}}49=WnAgk(pwgWYF34b7O?J!ixFX zNAR>Sv*YOL9}fZ^e=1y7v!v}-rg2Q9|HrLAnyvT#7$2v4uWM86wn*~}ueycZk`E6X zaYWT6F~&euw*SNq?b}xPkNkIH#{$uR{%8N<7v$dxs!~$lm_k0TJY&l2$>*(I!>O|_ z*iXBmWNTt5{oI-wiknQHo_eeg0DD!`kSz(&2dv^p-zW;^Fg!St*=906gH?23Uh^bk z0@)ei{|ph*=f8?NQitO~3+w1*wH$>hM0fhdD|&=l&WNeRZt7>$H)as}p*B9xrz-9E z-mXlj)+qI{%sbZbPuYUw)61tgXitC4pB1}8WwU<0aaPxs8G3PtRu%1UTl}^AY; z2?sB|Q*M27GASiJYJ=L7)%n9;Rab_tNjh7$%^=(TQGHtG2cO+8wfDZYJ{9mCd|SQz z6Mc|}$FsvOns(FPc_iI(3?6rImV#FA1&PJkmKGh(UMmCpc*w6jykOm$P}3*x-z*Wj zglUo=Wce+PQ&-AM|5{#VgXbEOc^vG-rY4MfKQzd!&VGZyeSP4Vrb_%KG$-Mf>K$FRYM#rzqrE@YEj8DFc3|F% z70udvW>IXo zEd)ay&W+AMi=Qv|=)W$Z(@@#;)VoG!<2g+!hM}(Z2Wn3kh3hsh*0jkV7;toe>(L&y zm0fjGiKo_W7}}~Ea8&A`{#Tp*5fsHJ3x|IA0nqPhg^mVRt z-i16Fze@NhHSWZVCfyS~MjNyWZSQ=zwDZid;P9d2<71O`{rs2bSqFde8DM1*_v`f2 zn)z~}P1hO=Kdt%g`|iA9ABWc)uO-&B{CE9uMq5++`IKGe^GoJ@R<>{$;(F96#K&{3 zSJN-W9pQ1hSTyZ9P37B(INi#ZtB!`6HOLmiW!1uo`wIJOWv#4MO7+IW_jsV7_OBBT~Xly1f}Q$^M~Q_$19xoBXFH-MSyrIaR1;{oD89(t=4?4^Q($ zx;M2BDOg$%-u6?o(Yf*7r`92^87`KkA2PCYYeSo6Zw~T)-2aVmVR(Q(9?Zc30g>Th zn!Uc(+iUxOo4n~~?Z?}n5ABWV+xB1o@7}MU?9f@2sL|-`G$Y`!R9~!3rAz0=#GNTw z+sF?M-W2lPJUp#5HO{@+_1&(ptpgrRwi1l$ZP#SRuc%km^-7#twCjl8ij;AI!?XpX z<~$&NfYLyT-cZyc;z48NcHxQzErcQ zb@+Q|&9Uj@n>(o88*PWqqSoLK(E9yFAK%Zz2wl;6f>CDI4e3XLuIue12L zVt?+E#Dnick^;P|Yub&*pE!5LuJzY!=TNU)ExX!|wp4?U(G!0E`0%+c<;h2@BQ6b! z-*0JLFdI=b_;Fq5{56MVDsmOl0u5|3t|_F=m#$EkuCOWk=IOTSUD3B*&y6l+c3Rs! zH*&i0&Cu;vQlIRC^weL^UrKNHo<6fG;yCrsA} zn9_I-O$lvbbLcks_DJ+i+L4$DU2Glc5dhyajx=8w&u>TecH%g6c!a`y|Pth^8qzMU2 z6=7U|4WxhwczPU>42i=4##a$aAVxE`C-H(Je3?L~qOf>^ddV3Kbk`z6Mu__Bpo!i@ zFGg-+wJLBncBZ+21&1vLS|#M_NhphHKLBlf5Q~%;(ii&M2N5bjNcJbZDe%i2LPeWY zx=~r@y>Ixb&WnS1JY}-*U_KdrQIjh%5KL2f=R|+xkV~i#Z#9sS8!^a&?E5M?+R09a z$IG$e@p>`+$p7ru#=o^%ALQi@zqFE!0Z@_pP?Uv_;X>Er=`n*g7ttuAPEcJQjdFDd zYy`tdHpUDU3;LDYNJq3bWLn;_Jm%n)+pU2yZe=@+ediJDv zA7UU_x~plP#u}8m1Y25Tig2whW$6K=6I`~Q(SEKkkGHFy$HQ5H^(mXqlPvYcehRm>5<#F+tl*HIwp7ND#*{S0@MViDD`!nn#BxB_MR)7nbPUMjN&;zG{I zT^u>1P~mn$4aQ4N4Xe=J_1Ny*lrVTTg)*uDrA1IElTsj2+**z5))SIy=;cW=pn?d5 znCVH9Q8flIivbD=`R`JogaUeFImj_Gfzc}f$AJABq!&QXe6vWv)=QWf&=+YvB%}t3 zeuZ1R#HRabtgA`*kNg#``18{Toev=Ti8bNrizWn8nqUP7(%}YL znBzxv=H|fAA2@LR!I=Uj^pQxTj6QOMu=v&fs49roM;8g)Tqkr0 z>5Jr(2`QA4!1jK>K;*KCwhqXX6^Jr6i5BBz#n5LXNda!bQU-{+fNtW7noOfyQEOryFz1TuLV*&ZrqU?m{D~F8`+H%>KmLdFzYaonTj-);@~_Zis5y*Kkuwe!`@R_( zAuFJKAEd4h5&G?7O?+Sn%TNqT!a(DG?*SnsLRdXDi&@mOo`ymO}LF%7%u!A(-%!>pshca z%#%)0nXBdl;X+=>e-3qT6lKG#=ASoH+gXq?Pi4!`A9!`PWz`aF*j5mWsQkigZMoi#26F{y$ zq9{Vf!epHzcQ=zs@*_rxlRT6>x3h%Rd6KdQTo(EjN>1Hy&3vkbyYB6zK*kL~xTV>c zhoQu-z)=#EoY!8FAj_*k;z2n;$(7gxK(54T6d_}5gN>U*r{P7wJ-n#r*0v2f$TER8 zcGl}YA5ilFyeHuQFA7T_w3&U8{>j+=wXPwHWOiw{v;gr>{!z_0#CT%Ek3QLa*^ zUZ>bFCI=2tG|B`9Xy6WwGM)gmvV|lEZKU8tC_runT3bL_8v=mF0ZLWOkkcL(3Tx}A z$qM>HUP~zwX1ro~K>kTWz1yr?c+t~rPYeON&!;dHMMA0e_}ULe8$k9Xex1b2PZK7v zj$#!AJPr02o@xoE^9!b4TYyNGghPqY^__-zsk0>=^NS?u+?LauXaE(EP_riu!Dzmd zP^u@LqJV1HZ4A|IzaP{B34y6tx#C7fgL>6MlX?Y>H;Z|O$>GA;_~ z+miyh=^G+Ci)@jM=vIoH>$;;VFeNLZ!E*yiP-3-~1DIG3;I{1)$mB3^U5iR|H%cnrj$PR{0ra;E7(ETL^G7^HVw4OnC z#?}D3QXrGFpnEC_4i?wqK>3mcwUDb5(Ff{F7t>j>eQ-rRaImDMb)E|geySk*OLTo{ z)-xh*EoI~lwP^DtS|wmQ-PdRjpa0jq?M*i^N`U3dIT~fO0Cw(Oj~?>*tSK4HH>aLt zF1atU4`^94HCV6`Xj$n9IH?%E}}9aU)u=frgooCewl7l8Bh=;iUZqKdCkOFrhU2JJj5>|bPP$d z!DrV+h+b*IEp%FWe37Tt~6#4z!G;fGwjbP^=p8hB6vu zR0G~1--EgsqZ;7g04Qsry%+y7vzT=ci>Lq!$_HReKPz{oTos=FcKlJmEB9nXH8BOp zfLlJ(o{TJndtL%#DL(2N#8*WRZxT{Wkw=Dyphkv|{6_Fug#bA|!FX8;X5=OVL=Zxw ze~A#mO;H;sLoK6wI0_NF55wFq}1lug47&fn0CkO-&&XCl+^c z4Pw_DIp3v2Ap`>gDFr_IWP-(Kb|Jv)|5Ze(CHUqmGaW$Ow9_c#1`sz3lqlLi^CfWv znl!{4*ueZ1-GR{tM1+nq$z}wM6@X;`sEa3m=zNSqxgqk3CM81zeE-hIvX*5yQ#zJK5lwfttvn90v!Z709AFG|FfN5@ug~OOd4I zY6aN1KojNQ5FJPQqttTtUA@-;NES$gwSXlfI>eAW1&Xx<=t&yo)=2NQkVOT%M!Kqv zLMp^Hk~-lr2-V(Y)xv_agZL79{Z3fILn$G0qI8si)}~Ng-4Rjib0Ql7hun(I_J+ za5zn&j7&hR%c*9G0m!eita+hMQCF0l7ci;*Z+v!oZmUExT zxV(v(cv6P|RGiF5z321$F#X!~c8z=mn)ehr}|aR*A< z27P(^N3wRMQARjO)`c|smt-wvOR?d)!E_pBlmS+Zp;1O?aD)4Rvg(=Mv(UtM?0Uw2 z7I_V-o`DL_{=nXdJ=pq<|43SY-z+|Qv6hZ^C}p%$lPZvW1Kz1}w?}#nbhv{lmFDu1 z@k(~`2E(rx!CY~{6#vp~jH`h0+eJMXd4YJ`JeohF9I$e*2xjzvVGTlra%@j@&O;Ag z6GqImFBs4&=W69*-}76@~s3p}iV0dSx}B z!$wMuM638z19^-7KRg=lNN#zOT?~QqVUowwH5BKnMnVo;#$@Z*8OWsxyUclSiv1`_ z_IHL%8T1Xa-;TYI%l^N$6gRIA^yoK#z}Hapr5OF8vF@*z^fba_-13K(E&ZTNd=t$_ zp$YTJ_NTbd6rl@AwC8$M*hFX!GnO2t&RK;;c;V06@W(KRZy>u+TKGirSQ-F4kp4&d zdY#`eIw_FiY$D0oW!lvnxK!lX;$0W60`_gB80BF`;7I>}U40Frxc{Nvw&tmeJFyR$ z8%c6E4yL%mf}hgB3J`x5h5-Y434svs ziQPr<>MPgT0%oX((`CWLXwdo$j?6XRD#ETxZEFOIsXOAbUle0O9LJY zIBRq>Mypf60siP>MK)eMMs*2prn_i<#i+&!3Z4Iz7K%AEElg55Q%|DiX2JjxhUP9s z;hp#voG<(`Kva~18Nf(DSKBiwwv8gTDnB9xY1n9)BPETtGK(VJ(n2%%Nsep&Wneq( zkCN*Y*cQy_elEqxOv0v<*{5W18NJK{Mhaskx*%=R>z5Z%0x!Uq z0TZ&{JI`eleS_4Prh;UEpsQ_|(VpAD$av%*9?pKLG54{U>QX-5hIgW$`XlFG*^3XD z;$;;1wqpZMEhqPr)og;S>{(CB$u4@x>WW{3Og&ml)@pJ|tR@qr<4qWVamh(pu9nm0 z=0d>xj}bYI%W0L1S(>0M7eWvEL7N+=jUP$3kvwIOvD(NNCaZ97!pOdsXxE3m3-)_# zx!W{@AF;>5JdOyjDun`HuDp^>K`vIon&MZM@_6`>HP$Dr3Qg~#bp+cMuiN>EKN&9x zxU~Q&kHGLJ-NvkUb$;P_T!but`c+B#^QeL1gH?M=y$w+F24|ASdo^uFtaKz8+1axu;@J=x(js09Z c*A6;-fOcf;{S7)$8+w1XB*!C15IDv4*&oF delta 84718 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 z zlUm5dcEsYuy=PHh=~nY{n{FYacWA&p+e+x18?*S#*Bwb#L^Ya`U2-V1hYGGIoO z>|}9DB98px((-PLVUbI7$y)CIPwxH$K= zF;El9TB7k2^;0x?9Ts$KmrS@6^{1Or$-Qt_Dp9=V_XWh4#A_Fd7>~0{yRi}Idc}zBSysc|-|1pD4AveR3*ypf2u@% zP4J-mS0yUpS|y63Ld*up1eJ;U9<#<}CM-z+R*4$YWy!+Ib<>J|MF4SvBdpbxvtO4m z9QqZZ0`Rx&Nt;zNziO&Uah7~t8K6t2Zplp#akQ-RctAE66D(=N>_m~0KXL--mKbW8(t55x1B1|hF5$57bq86!`$}hii3oXm_H%j;Y$Ax zGAe$M+6mxlSlN)ne|`Xe0>|x;L4e>p>og(b05?#8uM3Z9ng_4maXXwjZpgN)KMR{Up=QfkX!XZJU4e4$onT~ z<8$!C@@P15>RLnw(ivRq`?wZGOQ4@_EJRXT`2D5vjhX1mA;z8s^ac^jx zqv(WknP+fM+)BtamaPJsn2ZSgHtyRmyp!WsV8~mG!r;=^G3hZ!mKkabYXjN4tj}B&vY))zmPsw z=mY99Lc(pma%Jsuh+%egbzQN8a(~Xvf0!95&MAV!yy8-@g=&I%HV@v~oy`?*rF#>m z7K|w^#r5yhyZin59>j>OXk*>d*IO5p8$Zt!JT79bJpJzd>ZA2NKG=Tv$Mi3Ad9!^V zH#s#Q)O*={`>hqqu1)YsWGqFmU2XovLa9Hm zcE4Q{ez@sg8!9h|J1hlphcj^Lvbl}3Zrou$usduIc8BY)-QhWz4vjZVpf8*r{;wDa0a!%!qXoueoh_WG6hr7|JIemq4;@(Fjc3Shm^8=X;z z*}2sjh1w*6zcvYC8*hb1!Ch}fNFj#sR&^9&fc~nELi^oS9fh3zM|G6rAJx(SW^?|( zo>4#pPhLD5>Ho=P&0w1Y{^D+O#&l@M4GyAs^+Pm%$mr2V4( zR!JMg@d(__rX8A1xVh%aNBXu*oUq#t^^tB)fNyr4SGYQPOPSsr-==qo6An9SuN^=l zkc=uU79_D7c$Ev+>lK8ade%q%@Aw9LHJ~SG`}nRqTnT=E0(ZQjYd`hz++KV=9_V=< z>>FL_gpLiOgoyqt;p?h1qJ)utXks1ojT=Cez&-%|g*OL(X$%k*5BO}wz|~~f3XfE9rvDYm9_pj z_(a~7$PlYwOuY5A382XD)ZR0~De?~J<7Q*MHa*Mmb`uykC%vzRI?q#V@@# zE?pPrGVxeTGBT>oCp<6-J~;{L&oKjVM|3DAk^ZYJOg=$rXd&DlbvB7CbJT)bs3(DI z;__R!jNQ;a8rLn_&J2O`$27BQkr$t}7%$(;^6pL9mrtr3GqRtkrFh(POAQJ(5`ClI zV`bFKL+;#HsfodvMu=0~P$GD`s^`Uut8B+p$Tt&L@_Ox?MG5!$_(b0l)>je&>1? zPsP%_*r^mMwtN3pEI-NY73NZ*2&rE>``MwPus2^ju|llWIb0!0kFqn)p;ULABwhpS zQ(6JTED2d99uSRl(4O)Wd@}j=kHD`@#<_W+H2gabSu<%J_n6NzLO?J>V%9`z7&VC8 zxtPd5Vw;TUiL>cGp9z09&yCop23cwacL_u8Qn^<_{^}ZE{|Y3?CdO*O#-A=PjmGnVXjX!rYMGFgL)a&0Febh#?|)rn#}vp}~GETjZC(Q*XSaDBnnl zZsC4K+4QN->A@c0{CU5;Ktf`_93}9B=y;>&Ouy?*e3sn_?zE=l zp?@*I;7oDwrwxX)r(bTNNGWeYwIco5?@{6UUB^<>WF|E8H>3qww`>nlr19 zZT+h6OMI*qBvkG;Fix7KPBee7D(lhA!Yg9FsC+PI?pXMKVT1=h^e|~8a%De+2n{vI z5vf#A6}R&_W-;4CV_+S-*vLyvGkMo4E)$X?qI=IUEyQM$qnBZ(jF-*B(2?CeojYkY z=%Vn9zcloOHQDnP)|M_OnP?93f}XtIf@iO6;ZI+O+XiM8REjX_^76+@+IJDdo#won z)Kz*X++{tTl4`Cg4O>~-@kOzP=3am%zIlQUemqQ>^`yO6sRVFdHDdfB4yfCk5N5dR z0S<1RacHz!-QjaudJ*Fh3mdAiOZ1IOy>Xm^csTHtf@>H#t1zbA-^bBLhe3_lD%#!7v`?XkFE^@jhR7Hx1p-@#}9Ir)2 zBiJwEumNKZNoBL7h=y2m^OqU!P~4olauQ$d>7vQSp zL?4zS0sF;OALzqU4Zt+i^)?SzeZVag399~AeIRHIWjFxPO zXnH0#0Y9OAHgTZ#*chk9UPWWtYo3{onu<}{!J}BSv<1=nAbtJP zHw%OPPE{AoXOzdAA_`V`n{A_?S32zpo-e#wxO^fqzJ_*K*6`Zpfn)CCn~V`d(2oTS z_pMbu(qZ9>*sn|czh?Si90CpNyC!=a1ux!gYEGMhc9- zWYzt!hZ;6wqDi~}Cf32JZpjXx8Yjd-`o zJ+!He(HP71f$-_7qm%pLc+-Z@#-g{f&#rj_EiNtaD7Te`*|*v}@x4n%zIyg*bLseo z_qd=;l>_OP+ss^>QpidFs*5Aze=<_TcNi%J2qWd{24jnd zDVI{*fx2xkqzPx2zxj7L zg?Ba@Fca`ec9J_KkwkX8)HH5Uuf!32V=Mm{Ag#uLE-lIYP{qB$NNfLoF-Drs?BoJK^to=wWA(eI$}9%ODouyWHhcH5$gSV^)}FCkiyZ)?JvVIZj(2#bSIq7gMW9#m@*;L&r-Z*fH{S zw{V6qecpC9Hi<7ExJG++jG)JVqVW3^R}+o!9=QHQ%%7cJ>ICr3*XBDQxefbWr`?ua zu}07-F|m`{r(w9>hB|PrcHLemu|WZnt91hBYT=AU#vsYs=g*U=G1fjGmZbmUg7=Z@ z{;pN>t1|yUO;g;ZBBky<^)+i1{QoCwg?PnUB{UU?@bKH^<767?EQXGKdh>2B_dU4o zTh!I_s_q-_ypRu4C-&d+w8ROD->-}*)I2MG|1aU3!uS6S=OEp#GqqQCAJTA>s{OWn zo2vb`3{BNub$YaN6ZhTC)ewlZ?qmxRF5 z)y+rtuR<;elk2 zi?D4NHEXsDkk-^Zah27DP>%hvUx}HAb@$5co*vKtKFW+Pn;*7C`^4@X{+K6!qE!o~ zpn!|kC68gD&DYbtbaqp_B>E2#vf76Ahh*UI0U2cB;Z4t-7EIEP`MEh3?|U>CyZgGI zH3?_JoJ^XVt)0zubVO3fLQITe=%r=;AoO!{tfY2M;h_o7WRRH9HWx~8d}w&Ni`d10 z)R=B;7X!5Xz*TLKU3YOTqbQ^-`04%V zoJEYi7Oz77BpxBJwIlW3JLXGLHeg4XMC)wa$uad{%lJ%@P)4_E_qJbXdx}xj(=x*g z#`an=LZqEKb-0i=Q%?1By8ipdNRTelAZ&BgrtdDys zpXg$bZ4DtFV9HWTp7cFMKE3zu`@{G*IzS=VkJyUcZ7}jhq~Y$gWYR@(-Ct0i>twxy zjf(0GzzdL}uH|Q#*>j<(Oxcylhtmx!FJdn!TC`mX!TjJkehIBB|Dyqe`yCb>N}`kf zPjn_u;N8W7gp-J})Bmr+GaBE;_7^whHZ-NN^#_i!%SVDjVrP!h`0#YRbvNN)5;PpN z1&4!~`+r^l%mT6#-l?UjzRxxgX!Yz9u6Jl22U34ZLZ}&bDR^z=Mmp$rt{`xY&xQuP zB84-`FD<_=?rTO;N`h1VaF^BIkd*%bq=f2TaJWpB&MY>Hd%vbN^S!abrTS3tDx;vZ za!<9=B0R+V;FN#isXqJ4Nj!LCD0XnlU#K}`D%KAkH&QRiz zouTB3sMa3Afj5SlCg!L^LPur`;mWf8g!l^)91RxSM1zTR=vUET<5e^m4UGnIq0!*! z!{iNIa5PAF6%DF@qd^96G`Iwf1{0dQ$$^llZ(i*eIOxNN1buAh4{%JOL0{A1bE-ZO zV~4@XXsQdeqAqJqsbs{_vO{Ex&^B%3Sp(`{C(Y8WMwTJ6=2&9p{ZqXG3-9+Cvc!@T zXwLl{MC=688^5!XfSr3N1-<{|5yclpP5Et=-Y1~|1eWdQ*-{XNv^Cf5AbHa z#RYNI`5(Mi_}}n48j9E3;4G8-KUpS_`4a$hLPi0>{{qYlU|_xk12gajn02tdg*{Lb8ybD&;$ww_UBg+cYSb+CQz)|I={jiD3GvUT+yo62mgU8_UHeB zD(n~kqDl?t<=|EN9L&r#kN^Nzs0XTq2S6bgvR@(y3jqKP3!(T68i2pETOtIiuUf91 z0%*8+)pAqe1;J+MMK>WA^rB?NPx>5a0Xe~fmZ`o@M1MM zBLf_6cL0P2ZVR~A+xkHQL<&5>)hZ}(FbVR-16(;kkXZx`INv7xuL4EzmA5kuo=?Ub znivA&B2cWqdBW|i?%bRLM}!Z6|1I_Q|7g1Z-)z6h|MTg72!jXuNeqA%TVcTJOn_R> z0i{5TD{Dv*RPfE^6+i7Sp>lGn3;tYGSlqPf?JCFeEz7(kdE>g+)CAt09RU?jZ*7>wRnwK<+lmZM zIpe@P;jM9g#f8*38I+eGkBOy@Q+)e5dBhl%w)yy(p}QIzQ7ZMTDY!Udr~0bJBQ_t@ zhuA;zWJRfFk8B^oj1dNCPnOTIBtw&|hxF{WhZVteBe& zR_zui6d4`I>cr-<32c}oL|&5J$Gq@u{7r*9R!JS7>V6BX%IotsxhEK7k|w+eMgf)> zukF{%aS*d(hTEf7M&l7Jp?25bTPECm#VIZMhI&(^`r%@eQ)-gme6(H-*#ida9SV^L zhQYvj6Q ziJpi6T(a~VT(YzbMC03}q{oXpeuUI;lb6|t}GVx{Egv-AOQ3x zcg@yy%iU1ps?Xh6U}2jckO&?0)g)w)Ut3tngwK*+VFPq# zMJTQ@fbD4TQ|F7tfs;hUE32Eq~LT;ENtXtvnbX#GwCyPU$=FRe#5L({cs6SC6@R z9j+q1N4(ds3p8E#w{Q(Ii*9^?sJ~bRovuu#>pwqaA{AKv$D6Cj0)Fz%zQoWeg|^)M z!J$(MZMlii_m}~gufqH~*FM2SE?+zM$Bw0HMEeZ-Y7ejSB#M1C69^hX9>1 zeo1h{jVcCYQd=djAolA0B$vATeuA&Q8Ub|L79sb#`D5R-{GCW5(ABf<&Zd#{-Pz2R zzAgiV%x0<#WH#@9`U66DCJ;KmAA}%dy-i|6mm!1pgaB9n0ngvn;$DdNfh~MzaNxPV zU4DFh_UfIx?fQ4s8!<>G1e%rYu$yKlYWt1%+TFjnCx0xjUVe0Dy32FgjfJxp;YD~f zS%Qs(!(?r-x_#>|j922*BfLj)8QpO~h=}*g)@$K9fM@5Ims1yKAFEt12Asvk@`vFY zj7wj_16Cwmh33&~bt$xbva)9zj17xBh$W1Pc)PTfsDWNZuAZI&72LGG z2Ccy{D^IYTd%|{fGV!u~8D|}>_Ste}-1nZX{e;1lXsXVB^|+)}7&y5X3O8polpnm$ zWnCq;NoAFdNALHRRx|H%Dui$Hz%_AdqF6c%nP5eS)~txg9=zh1lDI7U50L@iB3YRS z?Z>YExP|1GIBVN!-ztIDh~I5oUY^9QGa?^vDu|S)Ak=!2kT{1PywIgckmc{Uv{axT zKt;C7kT6?EZE77Zb|EA#+`pi16+xQ{!H=^=IbdU^xqN?N+n@I{q9!&+2j)cJeDL0& z75wjIDnToF9ZoXplup+BBk?V-KE7R6KHW1J*0?k(=KA^W>7?);kfxmM_s`RF^|@ya zzAw&{LJBEX519;b)aGRs_bJ`=nnOVakIY~}jS8i^3mUZ7`fR-+~l;NqW6kkp98~_qH>8x$uRntqh=8= zzr=23u^gb`?$Yq0Pb);>ap4HC~b;EpNcP z+`K5zCQeAEHM3Xgd$%Jgo6Jkr`?CWsv2SDh>Z*cAlO|014L)WUF$zL6M1z7kgVo@4 zEdab*`hPB4B5H^b1H}IQ(m>Q20BFDh^49>|Ml7HOjq5KR>RVH2-n9bYbGJAE{qD0g zufOY?qrVt>?TJIK_y?iDSNt2efIxAu7@)UWfVCT#0!_t%SNykYLstCX>( z*o~`e?$`~+Xo%g=`v|V)z?=JxpR1d~*aNBN=p3q$%QH`MBjx7DB^tou&Ro<&=1nRo zYgtFye{w?aEw^g?9jSK`GnU_kEBRCF(X7`G5o!L`k2^6Kbjc^(&KQ$h=@NcVKYCGZ zkNAP9JYOIt+o0C4xD9+`(I%-w_vD|13MBCn6cLw4g1|2=goLXnEQIkNw+Jl{y?;`H za6a;RVLpZ?qVOZbzZWZZ?NRZ9G9R*vq%Uk@-Rc!vMflrl%7 zHR_$6WEj2B=hhT`6Q>?gcB1?FOV{VF#sgY=1^xpb{9za3L3E0|#1AlY@!aY~6k~gI z&YYwN`f?vukP-w-Vl7gS>5c-+n+#9ckNO-kN8YQN1Hobhgv&3RzN@?%)ggIoq4l7) zwjI&_t=lKJ=J-Z)ZT#uiB>KMDKLozU%*vxsI4{Nt#F3y(J5kjdYvpgBg`2xQDO7c_ zl3n#Eb6315cyM{h$@ymE;qQi2{{#D0Hu;h7b(=6N)wop{=mtBX{yv9?)+p~UUVZs8 zp!s&U=E-|t%4f==r(f+=S4#e9y4IZ6B?g#Ux>!t^L=Y&i)x{`J{Lm3Ez`+fs*r5v! z6DTrz?`EfgmJ35yvO#$Cp@br-RdD zWs$3aN;~P|`7%4t<2OCgIhMeC*t~Z~SKH$x^!}MH0Za&eqPEx8;LDr4hG!E)cTM>MB7pxE@~k4<>6UHr&h(_ zwB`XVt{#vxEa{;+HT@Rq*C$$e7*+;L4qP})`Ldt6!yPid$wk!G?3H;g452V=MZ=8$ ziZLNK<12oHL8r(>9eb}iu$v23`sc%Y7Z1bF$`op9NECirg+z{s7Hi4^JP1kG*BzUF zktm;~1+z{k9z&ytUV{iuGjR0qnflt)A;aAG5Mb6H6rl>h0WLgQV#n@V*m-MoQnAuk zY$4--H!Nh{-U9fts!GtGY%V0n=m0!_keGvH+f&~}AHq8|?8}!nh*(jf4Jd65h{Jn1 zW0n3jgT#TdFmZTh==-0Gz?;=CP1w@kyWXr`46<20 zd;%ppB9OQ8&p?FQ#i$#Bd~oI_E9m3c^G&>-SzCi=&zjR322?4JqlRLodZ3GokT`Ko zEO;x;TQA1J0v2`U2|E&FE`pFX;~Awa;3zyB8igPJjl!EEAyN1OI0`?DhT-fTk()1a zErOJ&D4%~2u61}fF7gSO2;n442*8|VbWN`mn3MDe3m+-JnHU}#y~-J$-Q*01nHMm? zyVd`47&dMaGhR|-CTNiC6#knt6oKRnwa#;j`oNqd9LWb;uJz4iW%P;Lde$?ThRO+71Yd*Q+`21qauo3HQO<%p3pIf^0MITUSD(C=A|a z-fzrjp_JXO0{f-U)7)q5+&;8p)Nu?>_4SKK_P4=xtS!3#I%x-!&BS($yj0_xcxqzz z1QpJwIwW9tLU-p_J#DGvpes24>gUwR@!D+$Gj4z2m}jLNL>q4Ja46i2vgjwq93%r z7N+^HIkBl*(Md-)>kJEeCp7rIIwXDy(7tn+`3%5tdkD)Z01|fr;GwEGad2^FzBxv2i zVj_=e9-}^{i~B_S$H?qE$?U)m4B`B8Xe&|B04#LHK*T}&Bs;jKAO_(-yejBifDR17 zre-d(zCnMYmGa#i6Um^p?shzsVPk?R2QjhPX@Tg7s4yZ}f8g#O5)BEiKQeq24Ux1< z2sTFm6$f<@bvzXvBT>go-&WyO-u}+#n|AM6*4-xN=Y38y7Tkx9PCbX3R37chKK|~4 zTWY!i3dgVPXB*qX+M(^$BadoL=$>@DNCc*crhMWJi9I=awj`5+L#X=anC0xu_U!o2 zuJa}EO=N3t?4M%hnCNc{xiCo+rOmKyH* zTSfvI3pjidCfb_%CTH?_z3{4vfRBz7Z+;j zz?p{~2(|qPV(m%#e)k(>0?=5K76Ynwq|*mTv|DHzr}0n4nLbC9ODfaDF~y6%+?|gZ z6R;u1V7-s_sn3vIKX4Zqer{xa4!EaO86$p%TB!Hj6m$t|4Ya3F>M#DjEk8%k7yH3 zvz@v=g(+_h>R{Yy7v&@4U#~cnyU7ZKc`3C!2UUf>2cSqPLg?YV4xQsRu^0`#kO1MPmKcra(O@)zcsI(n2XzIQl0>@$QD zT6yt~8p!+I?ZaHKNl}Ixecu!@jFQ6Q1uHCTDGFJR#|8;bhP(-KZ{&_a<<@y8}<yH`7d1Z2(>Q~=)>u}~_kaM6$MnN-sg5v>rGOga!C63j_2 z#%>)Znnuv4PN_I=sB*=aA9|H#QR0nVPRB(}6mi!qm07TWJxF<4gupTh)S&XN)X2gC z#G?i&EbGVq3Jv;->%~}BZ9VeIQE%KRrmLSXwtM2N7=8l%l|6T8XSXO z?rW)i`{z}mhvtlKZR3Tot7$?ks9*+n^w(akSHf%QQ-xLjHp*8a3GWjJ41FRS_NQHX z4Ga8J5~s<|!0Hu990;56u2g=CC+z)f3vxdFw-dQpMt_Gs1qzsq@%xE7RG+JDTX z8(@%321R3$S01`VM@$$44DH9g1fk{K<0V1?(uoIf7`#bng zk3EU3-0I{CZg=~(N23O_kvwK?lsiNOc%j1PZoHZ{g^+Gvn`1Nr`Gv2(;!(|vQ8tFA z3D133-C8fEp!G^6VDFk9Geyf6#%OdiD@OVX$IDPT645$&xKEE-DSWQVQ6Q|;D7Ll! z^`1>jToTMou(m=F@M&0n2;Z^MmW1ALK)QWWe*5w3oxV>EUA~jvxl))}hc1<5Uo(eF z+YgsdKKKhajo~L}rmLfXhg1)`xyVa!l&QCGX-brztx%XJqYp>6#BZR zgX?fBe{XerEPBs~0=O1>Re}cjrmG&zg$QMi#;{yqi;R%#TM7_Pd&mpb|LKYrOeWVq z1pZ3MKv4^p7OJ1EMX`FRE{!3kcSu(hxv*dIEVsClS#1%HTW^&Pg%M_(M=(6l*=%ty zkqkNB(X$sJ_vx!10-T1HMq0$a=%4&Je3kmom1<4gX;#bIGmukwtm$DJS1J4-|CF?J z$68iiHYyoXhVQk0K)uT<>cJRY^*H6B787tw{lI~sXk5Nx)W0llyu>{hHTV0`%T(+h zzag?M?Wzaxn7bl;@XGEfrY<%2*z0te^mkB)+U-(`BADTqw%R2Yy;}PD#RSQ!Oi=;T zlQ6o-2A_J%=u(}HO2dbps)TTwm6&YkCG^SKwQ%7vt8k}wy0;}QBLlx_D#c-ESuX(% zDAalORCpbD#9Ib~N>N%jQKMrPzIZQMFMWqqQ-VpYg2${+&1%KME)7q(Um4u)Nmz-U zC1lYFxt$vF*;+3FY;7}XRVq6}<#;z9n;X4T7usyN_fzyUz-pVxCuK7&6gPT_&B9P^ z9&ri3l5h(j+3{`>EC-${gZS~Fd7#yzbl*+@R%2?G5OK*kd{(T)BBC(D3*;XHKARIi z1d^maIm0IZYREH4Bj{tFj-fz$aevUqQcF%OL$pfEADG5(eitOh) zCLg0nSx4p%xrjz;%tIF;ZhXqGMhkHzJzGP8=k;OkwpfPzx@{-WfGdWzmT(p<7J^s& zA6^h--7qMr52`?A2->v*EZ!bnSOZr69yQ?D019s%M$ivi!13)#40{0f zos$XMxFES;LFl?@}2K znsEeBF^F^{?NGW`aVKSi_a|99>ZScn#BBY4PbImQrd?b^M@j}unF~xFE z$HeUW%)#Yn;v8p=$Y35cW1SgJtw+CR2YIM)$@Op%y-KR)z<+orFx&VK(|F|*fA9bh zz!q|Xd|d$~h>8GIG`KwwU;sb`YI6lVK=9kgr`iUsxdI-EHR0X=umIP1s*)wHI9}g^S;E|pu1S3=8rF3+ zT}wQ+=|H@EgwmXGigAfJg0Zh2vTj}vd$b}cJxb^!k;*;eJ%5*EcAMe^F+Rg_8R#Pk9@oq=?*bXRRRP5qy!|G=Erlny>!^G zezzK)HCGJUbt`_cLH*3VcSfll;}LEJ#k*C{u3V-@s@N?~rQT(CPkYvZryH=R4Rahi z_qW2Bwy=G-gD$e2)=OUIXz-}SzGjPoKUoG+AN256u!U|Vj%p9085-d8KHLVWS2HX< z%MGKBe*LvKamD%)yCGP4@P+@Amc_(seQjAQ0@i2q239hTZkn=ItjUA>%MTGO zL{Zy{E92sD=(9NAc55c*b*5uhvVApUtfiVKIB?h_n6^_|yWfIA^ovhN%uynoslv1Y zXk||?*samkqm@7K;BD5^_9;h$Q}QIR82ztz!hWoFk-vv#0NRE-&V7zgUwm?#@%XMe`46NiP5h9t7tPypo=YCi8TPYYJKb8T5FpBO; zwiGuR-F!}#G3vW5sV;5R1IElTL|l@K!Nwr9`NIdqW9ikmWPt8?raBJ&^ z#uE6m>$_pTU~aN!&~FdO!8@Kh_ySj)sCPlh?RN$p2Z|Nb=m^Cl_<-m|DKFLHS|iQc z>6&0wW;%wwxSyUyqbL4#4PO)Z;!%Cm;c>)wH$F!io+IULd`@sZ9U2-kD+YwmxGZ4) z9Qq0Vm*Ac`cxIlUK(C^rn5k|=o(gQgK@%P*lnx(?ajLB1=>!JMy2;cNucd$jQ(iMk_aF{n{nmE zG1rRpg?G7A)AVQ`5O-qr6zC)H$wGH(bYe|As1dQ2up!d1WgE8{MtU(lk$0)JHc#41 zcH#?W$o`up-uzeOI2KD&t_lXu%F-3<=UJ@qdcI8jXIi_@rY*zj7upa}PPZI%{wUcW zD;@c&8U_!XD8-H*>eI+i+H-My<1^Kh9@_O)B@V^oZVnV=0v>$+j*Y{|N2cE0E9$PL zN6sX{2;$}bBZ;{_lC`R4ii11)uz8D6>uk5C;Gj?4AfsQq-|LZ3ezRmA>z{JAk(33& zm^3sWW;^t6ij*((Qb9}dwo7gwx5FPi|Lj<)A3K1)Tem5cGe^M((pRS+)$~vr zESBMBvS>-rxR`cv+L^)|^mQZ9z=TNKi?`xg9S;t|7NX%!MLyJZF!_k|VNoLj2eHuG zz%L=P&X@4ajYF9D#bj;`A z`)ZDnrMFpx`;8TH+d7y4?xQKwbHb)amW0B@SI|5 zFW$2Ge1S!+v-FbyvJ_%z~{eU3CqC`Z3PRm`?vx~ z5kDdQ{|a3MVCcGc7rK^NeF^_}=z`_A;>!Nt1!!haCGe`;0_4yMU;=WCrz=?Es^NtU ze{%UXDpDuSXhKS;2T!dDPl=!R2evdv{b?9mQJqPxwB-{%#y=GSc$*S9+9GbOQmWo! zIJ0;**;EzY%-wyxU!%WzD4YM7&(^anGxtS$rAU`BFmZWyl9;fyT6947DnIh&}r~#v?`RCNOX7%e69QUI#FP^}m-L9d%U3cYEU%^3o zjRBS4EA_-f3*0*AqfPQ(7thg}Ou12Qr7&$7L_wpkziRxB-#8c~2acy<=DSgT@gDlI zn*to_v;Hi1^a`CQDalLFaVdw`*4AGQwWSmnf%fQ{cX zM!S|6E0eybK=mJcb(^et zlCbh+8Rn(Kz`~l_ht^j%y>bg_5)9K4Lm%vPl@I*GcPdMCeS0h^b4&~1YndxdQ=Wmc zUiWam5`N|noH-cK`_o27xu*B0=gF>lw;7AKA%a;+Qp+=+cHd1kZ$GX|PSTJyKH}rj zER(@@ifE%Fdv#C2(dgV-rYt+oyh908Ua`cg%~N?cF=o2Sn1*XN?vp2e3?t##p`gUpeU= z5aAYkucYnmSC=2PfphNGrh|lUvo#urh2iMQ@JxQ+cH@$}HPLLzTx;K0K+R%BXH3Bq zFu0l^dO3&vv0KS&7G>{eH+lZj&&3^*XQt)#eU4r&tr1w24f>sSo6#zj4W~%HLfWt2 zEd2Uv8^HB6f|*EurCM^113|mc<@8+iWLM-I1!BbGM^#Cq;9+1Kz)|M^h%=Z$z2SGv z7--K5fKOAtPfYZzik-b1=H-~5grbm30<-==qlmd`rMZ667 z@^10NY~C4}WFw&5Untt2v(P^s0fi3{Q9lGmT4!YH6{(9x%mkxW@~IQXx0sLQ@OzAC zd2ChUuyH9JHANES8;_`vij08Rl3Uttl3ya2h&vvu7=&{dt^@Gj=T?kSIKw>1R(_(T z*8$_4${DVlD_+FoNF%Y;+{w~7R>6cGgZ>T8-SD$|`9#0iTJtQOa1`_N9+FKG~1fp1NO%wA9BxQ73I9X-z6TYy~> z*WWtDze68259ytT7Ls65*mKo-SPIw-f+0>1eZY&wZPK7}7}af^Anvv@(XG&Nq5>PrYCaR^ z%}J`3;En{l;D$4AA}Xoi;1y+ME}^uFk3R7(6}I$>5Ecw@*c%$yRqM} z?&ET&X5G6~HHqQ~A;QH6F^lJ_;pW~0S%T@2^ws^t3%kHT4=eZ6@`0~2Ei*IrtH&A6 zzDM;hj!VWf+^z7a&$HDKz}uYHegyJ1+#~;euZ}+~X14-;em&=WuRJA%{$(lqH~4e! zGbZmd$x- ziv_Wr!*=1;E`Qe<#O8+7Kl`CEY~QE%j-_l+7aW?*{YVR5z`M&)%b_&J5=j~skdcsU zyo!7VNRO7s_a~OO&8?HSvt19ZAM>}h-$Gp&34-N=H?e;{m3-*NEhJuya>$$HYl-#4 zXl4fNf!g<3A=n}LwTABkeZh%RmcWo4!=BH3ya5AS!@(GitG}z1wiSZ0*t?bRj6xbc z`E0^wmN~M=KmV4uc3ls3iz41NHqv&$BgxUnQDb}xSVX0HciLL{nNQs;HC^;eiR3C) zzU*7MMch_5R;pjUyHa!mFoY>mb}D_tv{<$(ooFqzC)n%Hm$alG0XzE;eI$qXxI%zo zWRG>7*8J8f;b1P*dWP-sYLcs$xr9P;CVIZ@p`jBtO<6R{`bcP0sEF1Qo>k7SA931j zbLZ~@oC+sCax^`0MqqZIR!8}mAyU!#L;0m<`>d5I4>P!G4m(X~jxgDNc0Jy2C?khQ z`@1r4$)7{N`8D_`MqsXZPCD~!QMKP*dF;1M{-SnQ7)gBNbWx7R?;O3vwh{jqZ*Kuq zN7Mc5;!bdPhv2Tk-95OwTX5L8ySuvtcM0z98roC2!D3a4# z)P4Locm)(@fK^)lCgVmiH#=HWK@55(DfgQka!(sLcz2 zThnk9edbtSv>GuBy_1t8jiqOI89Zk%FaI7$%yoxj#*-7!yXw2UV)*L2hB~CS&hs|> z=FazOq{dEf`yt>n-S>{kjo(lk_dX75XJs&iL~;HaQ0(#AFVY=?i(f#L$*S3~7j;Co$rSnlkQ{yu z1_U*i?4r|UMYrzH<{=B<8ijf*3fGfj=$X)2&THNfr3crJ{bC_tU$Itt1j=7`pvTeX z)w!`7vmF0@YJ>m0#E5f!X|n$dz1sx3WvELssd{>a4Hf0gSwOVu7uHHI-P+-O$6viE zn|Rm{ui^Ak*q7F^jJFQq>0S!p1BjA1a7G~(rN1r;rYJ%U$=k!0yYe6`+KT8UU-OHR zw5hq4-zbQY;Jj9I-YZjCVRSTuv{7AYbJ{6wXTppX^+hID%TyOxoEDX9>4UF2C5{Fl zo}Ik~R^K-=h2U+VL;CKdqxaM|jT4-0Kc@rIDSO|o;8^QzF6fk_dk?#gA zVnSPK`rH=t%bf#sRA+D`_*m3k9|Iv5kG0Z>T5XuCor-mBFj$oe^}zJte;J}xOM++9 zwc;xkemhFm)I_h4MBuLOP`)H`KT=vQl8iSO5TYD~POfhl z_m$ieja5~)aADUTATb53xz-7$Mquisjq%%j$&sK9$&dmkFrZ5m?3O<=1KGio;j8xo zC$F#BbT_s(?5-C&p^?@j^jUEJZMX^Ao|7{ng-Y3Hxz3}I!1X0=`|853CiSqJBcuv! z70rk!O%(n$_Sm;P=?eghgRklv z&({2MMsNBt!V=^zWx}#X4vPv)!uRTkoUzsjy-DAsENVuxnk;-qvsTfk=SQ-Zf8oqL zp8Wc2RR(px!kP8yr_OiypKM*zX+AL5-|pzYtXmUmE^z6exb#1pO<>kPJ8a?uW<~}m z&>ET;W_U_4BPKg0Cn5EZ4|WiB5OIhxP#9R?e|U`fY=FtZ{=;ER`ae00|Ce32-s4Qd z|L3lorb(p`_~qX%OfIeZ_sfaD-&y?C@Z{(Q;QfEP256dcYJj`{PM-Kv3DFL``>#p> z$9#si6{-sA;KkVa>7W%~qm)FA#S$I8Jk&5-02pEQT1$YD5-IuTUvxQ_GonOWc#bJ`ac<$-}7M^YhHcuIflM|{IJVkQ{4nxWQ=B%n4$8z=i^(Z&~?%i*Pk*R0Ao4~aqS1ZAh#Zc@6AzkAnfFy<~4 z!UIz0GkAdp}7$o&* z*A1H1RNo~buW=;vpJYf9Gkc@tez9bK9C~_Pzol4hhEB8i(?Es?1bne|fv}qte1CbU9nDJFC3f=p;ei*+@qeFbL?tJdku=akLW+QF> zp#dB6qm^%%3fwN$wN3GHG*6ofP>*+JC%J))>3td?a$|^l)%ev|`up=jPons?wQOr> z`;=huxkQ_*<`hFGtr_1+XAJjm2*GXFbU4l(_T(J0~iVqI9a7$6w7Eq>umS})Yi9?-#}0Jc;tDJ~XK zsthSMqCoT&c#*0aoMlgaua#WJ1zSfN0{u5<=5^sR+gzpZ0TZQ0H-X)7F6}ZweOJz` zy;!!%Myv6g_|A0^tp|`O#%0|z2ZIzd#9rZ-Y0=s>1GOyHBJM+o z4S%pJh@BsIW{i(z8t=8T6fZ39Km_^t)m*!EsJ2tNH=p}Q&7CB`Vg-?%1>mhC?_(6( z$<-!X<_dg0F2(-%_G_Ff+#FB=0~@uRkgCOeXG<31oacsI{1oNk?|BaIfT18tXp=yt@LBqQ$`H7QCTM7U9Zjp`V9Epie_DF#oa{s?I^gs?R)O`UUydJc(?S$ZX4N`q z2X6ryLChJ4%U~<0Vjce-Ksavu@h8}@$#e=5|1+2_TJgWkN7LubO7^c8nKS^-d2N7| zk_u*r_F}_&YlG%u!^uJewe$ibqh>@{HB>i{sJ95ZL_}Wt7g~ECW~Nx05I;ll zzDE)tUG@3n!@Sw^x=RKlvMgrKs0rw;OQN#{ZHYLv+GLYry?aQ@k}!0^1W1w z9f;(;JCEHv|LeEyIggJIEwzedQ>DFB73ny>^*rX^t2eGjvT}gkpM@RTQge_G8uhCy zaeU#zJ!Jr;T3Ull9xDm?&;VDuotKwgaO5~OlIS;)=G!QlV{`zt+gCQ2J64Ep z?djNEIJ%GxWZ|i>Rhn=DiQM=DVqUn;VF^$6;p{?#^V%%3oA0;I^j<91>`$y*EU`E& zU5d^qc|v1#W<@8>sicdDxhN{QAB_KAL1R;8zM}Y}W(d}SrsLY%0Rk)h(dpv%w z7n)BtG}!x|<2JH6FfjZ;zs!u&CZhvXs2U`w{JA^I6zXET zEtS}NM{CJ<5b88u^>h8QvCXGd-K{9eLG6R+D-6nVbUTF)ztQD=2Uf%g|| zo97t~ZW$-d7L;>k`aJq~I{1LCmiJ=e}m4{$57V+`QDE()b(8 zZmD5YcY?^e0QrZyH&Uank2`NjDkWe#ivtw76!X5)nP}UWhi6);)rg%_Njtc1#4}u5N5~P#h8h?;kT^CLHM^2oRp6Vfq8NF@Bu*vltaVQ|S5h3)9#mkiQiR(Vs1aW5IA!XvYE|;w`y{wb z9kkf4gJpGsfHv45vzdh|1NIpWu<%J#UCmf|HKyGFRoR+OvUe!q6H?v=(?_s&VH3e1 z^?5gu8XD72UNN6_(8%Evct6(iP#(=h4g!Lv$+;Hisi#}W99XA0adY0Tw^yy*$moHz zsb&Wf4O`KIalVg{Uv$P41w)T=LSQH0<6H0H%86f+6$;{hfkmPrqdZ6<1|D}-NOH{K zV%!95-`QjbDZ3{r2A;=&x%G>b2?E>bW9f1ZzQ7L*=`GoCx@#-h4U`eesgv_*2OdC^d!?NocM zNq*myPdo6OzFg)x;YuXCS}VWDq8<{-m#RX?_NQOy|V*&ttge=8lA+uF#c6TlM9yS{0kLrXE@KNyh>2%2Qn(LfA#* zwX>OVq+5{bp$oJ;;2AElR)3k2vd6VU^z!3vP7e$zpzF+~@#YZvZwP%V(F}$ID`{vK)8kryGa@=s0KgqTzI{7V>q($%P7cu$L}(X zy3$w_nO^a>@8Q%cg-@`mH-nPk)PD4e2jwxi@V+%%nvE6%P*txMmqm*nVat=vcIbJ` zec0axuhu4cMPd`Tf2_OregmlV3!5G6DQNYc&}X2JJ87HWJEQ%!0jST&NRk8ZYs}vS zs->eoCLFcS%B*O+w&}}x&XD8P+t;B!VycG~3tP&hNjXECpS(Q<2Hlxqv($2CphLRY zf_7$jtU|FI$$c@DvACB8YovNAPO~jSUSr(PP30Kh z?9!v;>fb+b*xR;g<)%j270L(?6yI2iGm{Q^%s)OdkuD9f>V7o>!{07zac4qv=ouYX zI!!IpNMwj_DNIW#21;g@6Z3()P{#S}{%3f$mk9Y^G6`%RsVSv;r3PQOz;M}+ij2Q@ z8ILR1p}f$8*V~!YlLGDmm*sxR4#R0^yh(k$VXRu-Gkv_rMDiiMsY}053sjtcEm}_a z$u4(bDq{Q0>wc2uKL2%%gJ}cjW26u1P1s5dgxHK9^!yo_QYufQBs^{1>*SqTe!}5w z_as`wF}Kuu2=@IPTkSd>ktfe>VFJ`^yBNRsyo8_NwCsc{A+c^h8t5c&bOJS(_hI;B z5iDo5mMeWM{*A`^)s=8 zsfXq$e}z+^gk3~MM(KyuC@-4Qfstm4v_aLUzqjZ>1@qMtKvdS75<(J20=tu^zb6z% z4Dm9Z%wP_;-aMa(%5EE*SYBo}1-wske%y>V8EU?5kyOINaQO0}4h3%p*Lr)jpybd3 z;Ppnp>>v@gzQk>oX=rGs_r=kc_+qGia4cl^@1Wl;{W?JFMWdNF7>4t>WW|b#x`%J` zJkN@qea(1*vCK@|o>N^IyOx^UHf~@AH?a$cz1-xS70~?_gm&7T-E-Cu`XNJro9#+% z36oR>9@WiN?+;v$?b#rBlGA3pHobNLu&WI8uciB-L04Ohqi5wTN7@YA0u|pi-@UJ%nKo&pv0XE2DKp{z*0_>lKh_tL}6* zx9{pB!hA93;7T$*a(6sO`IutUDC{!{Q-178zm-9>Nmoi+6U>BmJ5XZIl+&sUum<4= zUGPc*vjcfxAs@sl&|pK!zzuBP2+Ulw(T8ts|4lcqb_wr#B_Z_d(MX>zQufF*671H{ zu1P7N#Z}DsK(s;&Zqpmm#BR?U`nyDd=0#DMO(pIO0iEPUdc6(ng-6OnN7d)c><2Fy zic>E~)gv!n-Rg&@O9>%9ISBm=K%Eiv#f|xIj2Z_9*(8%c~FB~Y9to!h}SR*dA#Vav7Ekh>wN z*5bvMX6fH6>S6t825V@|;Qq;ICgj{t!CD_dGLt^@7x^NI@qwq0uc7)p+nq}BaHL* z$Y4T@FWB2VxQMhL*WJ5xGe1%2L%r-slqR?{WR?G`wvs;Qd!Vv=QI9vWx_BWD+wwM8 zHokuRgq_=!d(RQ>EG@qs>SRs&_;82Xh2rpGjY^q?<8cC~ z(*Z5ULrT$1K+bU-- zy1vrIBCN~7>FJu|ulI?EteZB1YU`7zq6LUhw^VoWv4J=Q^KBE6#K3@4gmy0Z zS2grD#3~VDiqfQX7Jk}0g8cX7(se}Jmm4l_Zn*}ovZK^&-n!)pl1%M3A>X(4;CaiS z!o$PqG)7)Y;YnNMs;uy6@|rp)Y(w|MG-ipW^V~10UtbPi3zR;J%m*vDB!%@Fo_LVf~Eb$w(p6?<9PS~0lky^9TwX~w| zZNf486ZS`Yte;lo%sql)_Ko~`EM(8}@0meYty@x4Y}(SPiktv~@7;wmW*3d+>Vj#q zd|ib{x0p)qVJEOr_Vxm2#r3qBedCF@39a?XZ0V%q#4nF4Cey#Ic~Kfy!=zz18%2ir zMP+VA`QNdcbhV^cp7ZR>$MsJwE6OjxFb<@Unr5$J^MdD+Z;eYcTzyJyk)lg;eV;oC z_dH7GUZ-6}J^BCz_%62VqR&P`^_KarHaH*1O>Qsr@b=}U6-C;48;BjI^3Yt{)c9Wd zFg-eq@gzFdUFf^f)B!BjMmL4(UR1(fgh6AWQ`QgHRO>&z)=V7w6a2{1HbUV()@ss;Ms#Ou9JSyxwp@f!YrGSEi8WRV zn$URdC98tpfy1KLU-cxk>To{WZg1E|+v|VSsEMzJ>{5!H9tq{8{~3My`S2t@Z>9=W z4cYW(&%Yb07AUhF);=29C&Q(J>;l+;72L*mtCrAegG}jDQ6?AHQiI0f2!&+^*RR67 z>*=hFdeM_u%e1gU#;6z|wa>@-EJa5C)g7Om`|$MHEJ(i76_cv&zrJC{y7Xg_{p!T_ zFIT@eqN3~gXP&hq&wouoe}(=3^Cvm88kpd7*7+*{1Mtg&nL)XZiQyIKx(GNh#m6rL zZe4!i<0Fzk2`CO&*XOw0=eQW8Up>bl<#`vY)rSU$Fg&#zPTYa<5u;!q{QExn-U0tD zjVV~_7lRr>nWss(Ca{rHpR|FYsV{vMdJjN%^+f90V9JEx1)4rf*;w1fCooNfMX#n{ zwH{ptaLGEuz*v8U@dG11Fvec++7o7iyXgKS&U^_^Y|yUUL@*$oRtRX#ypfd48dTmM zh*~9c0TdO=)R#lXe~X4kk`;n?jvtFZ27139+JD&G+X@bjOWL2EM7*}!@245nn!N}| zPh`%{dylrD{iZMOLlS%(2_{y=@m3Tw_Md~cJIekta2557 zaI-l{qRk5EMZHvpe{{~c%l#fw#LTbJmm}Imm9xUfpS%G&1B%~I!HEc8)SY6h?R&2I#W~xQe$75>P7;91 zAqzkkmjW6!1Xhj7L~}=jnMwm0(w_p|29DNX(-CIY)*6P=i4CQOZ=PMa1}1~Fx853q zgyljIa&2~li&M14;REfJt=@q{D$F8kiOP2vIz#aZ%#i&|S>OWA#%hFtUnx>5zTC9o z+Ji>eE{bo3syGrBHpww8z(>JalcAuW?glKsaM5#_dFb+Nf8MAYA@AoMX)&r@lq*�{?y~1H141 z3dw7MR{&vs5iVh3_t1G<0BL`5;tyBxAR^ow`kRP?hMmJhR}03!@yJO|-;O==3xFDK zyGW^Wd-K$b@hkj!ky820=dY!d2rIw8=Z)mh3QDU$x*!o(a)pG)P?z~TKDVn`+hGuG z%m-;)z|KJd-F3FkK}uXuE4qDt-EV66?ca;~#-B}+tO^7FY!b+QXBh;qMRDop^^z<1 zH_yGa7cl!e=)cpdU4;A9aA7l6aPt%fQcAMKF5nScz@A#_{v&UKuw8Wxqg_`I0xQfJFi8 zAblMXhlzi-r2wS`{$SE%= zLoV4IF%uv)-RIQF1_7KuGl1K64l!-!r5Rip8J3v9x-w0;FJ(0+n40r%31Mjt!;*UN zzO!6zu_dA*6)wi+-9C45pV6*vMk&%eJF874o_%Ip!{OU3jq;?>{7KiK(qtw6@|iL1 z@jqrvgD|oDxl1D5YG#HtpxS!h40C=Wa>DW9DR|?zj}0gzXhN(@+>&>$Qm}71 z@9@bK!@x8k)P%_@!=E1FFUcOmz_bEHK=>Ao9D%rfmyNv#*n9MPtGJ$+Hr~I?y|Cb;#>ZYL~U~J%jl4jy-pfN#M8UC#|`H7hMEdNs$ zF1c$G7z>!;UqW!xfIqeWRLT7NkN>4ddG+U7|5DBTECRE}D`5Ujp!z)bvxJ2ChfEXy zr!Kh$<=@f-e*qc+tPKAtO|Yk0W3&1xEq{jwiAIUs#dv}W9;a&1NDPtIZ&OU@jFp-c zD4rlS6T3^O)9Q3S^D&y7kU**KT+l7BhI-~u;2Q1n5G8h;QO5$amE?fbH`d^>&dw&k z-yiSu^z&`+W-uS+1x`~|coz;e0eUe@f)fCUmX9M{-?e7n=RMBM%F2NSAMWp`z~S~2 z4H)i1Sqdd>`BIACf{sGwnn8(0{fnO`n(1gIj=p3C1DwM?CQcGw(BW%``u>U%GEAZO z)hzxxdQ!a{%LkY`n0ro@N_`!UhLwKd}H(U zvi^u;CD|COZv5CxI`wRImd{egN3KtkBwUtMzYy_-Y^${+z@<#0KP+U@>EIY3U{JY+ zb~^|Pb)?+myFcbX*XrMfyC;Bf-S;q9*54X@qhOlRvlw+(1{`AyPG|{AlMJqBz7C4; zni?Ra*1dOP8?NTfkX-f%1hze5zyb{_+8v>4xH)ouSdvWtb#-;rLc@Hqb!BFD4M+3G zO=#nf9U&I3i6(9{FVtgC3z=`kGl7ACKkVi(+Q7HJlD+ug{%Z`yf8tjD8ZeuLz&MaV z)=fxgWrFd1PXyrS0N!k`_|1H*_(l&g0LB3&x=sGPMa$|R=t%}0Z&;WZLEaxlWrI&e zWs?r?qk)agm5E{hDV9uGLhe%e(EF?Y{h=+BnY;xQKE;_#o9${6%G(HuG83f@N*W3G+*tN{ehkc{R70j z(VE18R>7h37d)6vWPQo3)Np5%M+g(%Z1Qhe>9ef&(p{M zt+5>NM`M`?rs&z{Q)3yrLQe7@8p|VHoh2O3|Dmyr1o{t+<&-}f%c>%M|CCq;mF&bO z%kxVCT!3sAHjx@su$?{{D4##iB!BWpn~f`|Lq0%`n>AFBiaJ@!zx6HGT&Q5O;|c%n z`1u!q6gX}fY6y;VRqgU6nHL-t%2Ba^0P*~VO|;8N?2pEBz9Q1OsgZbh=RwGo^_hQy z;q&U3y~^^TcJBnO1trDoNhj)0`luF`34kBey|&|P@hhBwOQ@@RVi}K>`9nz=A=l#A zic4t7jWv*`{mj%~Oj7BMr;ORZ2q}2~Z$irLIPFA8yUE{#l<54@TX1-0#D5b~gtpT` ztVqBinn7wR!JhhC3nGX<)puHhX<%G`W`s+E@?VI?e|yE!qW^C;X~93)q+WQiAxmnz zJVCkhmA1G|4*%3&_{2QN|E<5k^>>a{7#~&c|E$08_pB{ILl`eV{C5QBUlt+R9u0&f zZW%235)Amyr+?v=KWBGen!l;VpJw8-q#yq;?VZ2P4C8-Via#g(r)glM`w!C)Z$I*P z-QPyUp6>6`r;Ye@0>mfg{Y_8%9E+cqVVuXUYsmlPDgT3rlzcP{OaRbyT;W3W=KDpw z^N=95IvXHy2n0<_OZ6GkE4vZUp~qUgKr#&c?)9B*w?Gf7VS1KR4z96*`fTsTd9m}k zIihbFg>qa$$D@H=O3mg19g^9;kHI?OAdKIGWI^s6MRq3({oyyK~qmPcdJ96EWqH|Vu=Ox@IG3}1Cwtdq7A@~wF!}?cg|s3Fb1?m7hjwvq zll-XifXF+F6rfROr{k>yVc}gcyLgY7$#18(CrQw=9kIHQ{j@drAk|uf6oo3I>RjoU zR?67I@)j*u)P4dm9(Fss%DqZD0lHeJS<(aWEdyM~#`5|4@Nl)CdfBm}Y4 zCXlUJAo8`C7t8P5yKmopF=fQ(oSOqnMz$2PD;XH^P$fv4>YkNnRzspGq)xQkoKiob zV;3E&w9PMB`2Ii}{8K7Uxm^p6be#doUQLT3i61~UJ68bDG;MKI=DrDlU`3`d)yf_` zv(*U8(gq{>$VO-<-04>_ap6)oAPW$wn;bQs82_V5qVvL7WTa^rKO!Zo9!iWs#+$P* za^-uwfSi5?5SXph|wq~8AE0@b!)Av5jK+jb zo12sZ{E^+HtEe`WPY-Qu$s6oVGuXJUHx-?Ue!Q$k?s8!c&rPzXyStJvaf4uBcy-4Ly>0fg2tNzs^n|U5M6* zNY>dXjzFBrGY|-(_7#7T6=1kcc_Jw`T2)ptru6O4X@$wxvf@a@vEthf;c|qDNW#J? zYQzacIh1_T2TRyeH6Fz*gl?w{X`_57qZauBf>8?mYqP$$0HH^qm@l~`XEc#5>Be38 zQgRj$Pp4Fe66c{s3@;)?4c~govP*hW%{eVq;!!)<0Bc(q^*jbTV`kqCj!6hB+69te zu*d(J@~xY~SBczafmtG)cVei5j4Z7lX9d9;9JWOjHykYyeI$wM%gB$Gf|%P?-;0XeC--ZS}g{Z`X#i;0Dyo4g7%*`?g z`c0`H9bfS&GpAe#j1bFbvW>Bw-5Yg2cJjTgAC0(q zaQjR<0S4~M+eJBO=ZqYkCu`?h^z-pN(qVahimGE{Y6?FpEJfma>zeNOXB6p^_b*U@ zt(*qJWp^;AFYjO|W0Dfz@0#Z4u7kfS1^|?cDOE{e}~+ zPI7{*pKwtqxV`-@Ut37^D=73jjU=Rm&DfHuk}KW0_VPu%b=?;*fZ%ZIXo`Un5NjWubb|9E9(ua4Zzdjm5#JsE z7Z=XM04y`#es4&Fm&#l4b$kPuCmx>fISQm~udu(&hxRL-n7-z*lb8mdNhQ-t>5n<) zIWdGCJ>Ksm#sBgk)5sN#;tvY_-rBM~?J+35RHQR8&*LbL(XPugt}N zU@(P6Tm#$o@qmE#@sd-PfCT_}l@h`N%B~q_+Hxf~OF#I6&LzUgfr3zPa2Eq z;du5lN+Asl7-mOog>pLh4TvLC@0`4^+wkz-c)Gfbzu?o>e)GKG{H*afzp!G%;U~P` zbUAMW34w&)g%pJNKRO19)a24E=uhd`l=4w)bg096=s;J4%sD%eC)Gy2Hv)G*SXAmp z4+6l>z?F%Zll8%7Y3E0zg5zxKEoy(f&F0 zkl~HMBp%3B?j49eO2*%?espOy8jj#2H|o)w=)2LT1r;*AX}}H`#+B18p)E~zdAiMb z9;+wqxnz6R*^!7ECn|Bm|pFwpSpc}6kc=(s0Sz4hwQqORcAM9DW01S0G%Bb z%eZRP9W{Khoe7^$pWcL@1#rz}Pp;&b6h`Mgrx+9c+CbQzZ#zFm z(T-yRJa^?~PXcFAtT<-xvgw{b=rSG<^sQK)v5kF0ezPq3wVHn8MK|i#Y zpo6XCJ^t?d*wisJ1IhOCfPA9yo2&yU!1n$SP`pT-O&9L8@k7KJUF53sr0A&s;HBgD zON=oQmAUUl@T=yeA%+~=ZR3|V)~Xx_!2{r-=wAL^1g->b7%&vJvKV>x(jo8_WqZ&uZ+sw0QcwS9?>!>p{Y^^n8W7eMNIa9`i;X>fDN4 zfS-6h2zv!R`li2m9J?@B;5qSh@YiIKoVG;4p{3Zd?8r2^@5+xr^13r>ahgyB3nr3A zX#nB}i-=$C88&l2e69yG1%?Qef0J$m)FUxs`AH_Cd<{ALxq_+LdC^juGj)i=Ku}~{ z(WnSTBsSI)a+#Y(FK~Uj&@%{mvWZn-41l;Ftwb9ka0$+M)t`zr)JR}!Cam3(2q29U zrER(niMQc?=tN$L?x3lw$>TDlFo9%SuF(V0#X`QMC`pt@_4YRgWEWFEL5 zY_#^c3-SBnn)vlS`&lqNjhA zN~w|2@xAu7_^fyW-(T1d#W|QbtxiSi;m6u8A}KRj?DIp|RXhk?Y2!IU+2E%uh^^H# zmF`ssaY<>u7_Iv7=;_`xGrbY}XFxI_R&J4IwM5o-DIrD*+NixIIESNzEr6kK&+V$_ z=-O)`GQBf_Jch{C$1_}G!shio<)TokL|-M!xvJZfN-)c5rJXiFow@HDVOwA>ylfVU z<9UG6p&Hd{`wf7OWo3qF^2!HL``&8GI)Jdx)t zT+NYq-Y9`-LCP=cyK!plRBW3o(IY7q&6xxv>}zF|1d|FiCqHy>RMU4u#;Ej3S|+*5 z?JCitO#4gf%e=ppr_3~A$L^bTVh9=gK9yEii(ydAn8%QM;Er#i5CbF_NjRj^WN9Ea zVZoD!hZc(TQtpx@F{tiFqf6?|jaRN1Mpo81Q3NK25%O)}=dm*>kK?ktBrE5a+P>>$ z6)_Bx#6cDz$l;e_1Qpm+Q`fcS=*Cm7b=c+ucd*D%y>-%X6Uxwh zm99o1E3-Jx$_}>+MghzsH<<`;KrV1QpLV63tIGLx72Q1Lu;J`<$2m{_B(34jps!G& zw!C59s(1^I%dTW`fUSJA_4Bfc$gG#;>{}{AcgHP<2W=}V7SrSmbfh3MJ*e&lu z*{wBVoF%Ii`>C^+x^PT6IHVE{&gJ2#vQ8ZW6Ro5jZ#^J5-UQHHjGx&lkU2P&M&Ce3 z&<5xmGFWqlp8gO|^YHDVcz5KGQ*jyXz{Ka`LwpDq>xs@sZO1|RPQIk@Rj!i1l+dl^ zAu@P0TAH4B^+(y11AGP=&e4ua z!T4?E`PX`TlJ5X&om#dPRa^0!pz5J}g+x}nq9x|}ggHvz#KqM5*{X-dp5V=`Ym!;1 zAC^_E>5$XhO!Mrssn8)B90igMHq+pJf->ON-zLpB+tmhh={b;(iJ#C}1KD(%qo)RD zj|Dp93q4uqG7L7PRA#%PA^g44j{SRk_rwZy(iRb#*V0gCHc9-OQ{%BPU6 zW7@@4m0ftyQ$x;IbknQ?FQTrMDzbjsHk^3P+pSBpY7e%SK2-{7ZgpPSJt@qbJI}Aj zebtmdD{=!DpoDL;u+RaeH^NW=etW|Zfgz#g)ct0v%bL+noe8Q{cezI1J|3*2Ov^e}PEEgf#b(S~ z;L7!}`;?3|+1ft}VmS=EAIr%jfkSUm6h*D3)VVRpd>-n=KSKVb<-mQ)Wyt~oU&O1e zIY5;qt#ZXYX4BoJ@BuP*#i4DTZdx<77}JEcoCa{e@_9|*u7}a+`sF2@NNLB`m;6Os ziW8Wsz1AZOG@+ z88tO4bZMyQXvV-=Xf`lx4w5Iqcs@6u3aeFr)Fy@EO8&4UJ@|xLfY89}yGcm8A zT?$D=YB=euStK&u1uccmR70RY`g`*fBthHa`y?+nfZ5ciBYg>-%rlk-$k=A4wS8gA zce}ZZh+$&F&~9qTgFrdntp(o}i2Hy$cgXv8%n%$vvJ%URXQUm);EJ=gH-V7PcRsNi zx}I_HYFf2uDME#0@-3A#>Lv$6hJ=sKN0=FJCUUG1Z^)hL*r&y6P^RJg0ry+0wp1`> z8jEIDLk`_OWGJr@x7VY9F+cljVb-jfpNl=E|_B0!Sx(eYdmg{Gb=;{=2Icuy9rraZ6qZX=9$wN zq1gPq+er9Db*r5BT*-^$k`>#7S}<%^N>T?mEXJ_t{&iVtqtEN@s7|Aj+=y(qSd)@N z|F;gQ#WjpAF^1HVsQeIsM?8|r9OfoKEvfzaYx~=MWx1O!WlOvJRCC?Ce_SEFqx*yr z(7-oqDRgaj`#EpvqUe(cr(#nW{_urP~7VD{qp+5yR1iOD z?_54TY}zRVpfnAwU~~UU!qb&{gYp`h;p@puU~>}vRU33)HG?=+%8a0`FYwvuh7jIi zp4ui$VzvfiB5EuxS4cG?D`md*uOYL!4T=*-0{UxTKaTXOnsxohU#!fd_t-?Je9;fyv_`I!E3HCy$*yybH6u&z_Atq1>`*M~T)#t>_m zN`iBn;;4`vt=0!^a6M-^=XIc>_I`+kg<|wb{PYL~pcIJZKo`_`Xs$7sH}$ z=kwVHu3MG1y3xal466EIdCwv=bNRuKBqvES=G_@s`0bZ8fa!8D^X1N$q3#+lx2j*x zu0P|2oI!7%69R~R*NKitOkT1Q@AY!~z3Y}i*yaTngVYgMS>@zRtP`KTOfiPn2v{v` zGUeK;6Np^C=rsI{@VcutvU;0ZUQEo?1+>&B#a{4{w!Cxf$)9Dq?2^%MypC*mU2zA; z?*IiTYcFGS0v0~=f&)OC2EY3WpvLNPO~#RKzHAd0)p0rb4Lp29M%HApHJof^x=JK3 zkH*oz5Ih;N8I4vvu!ppWakUW9eLeGwd@aNCOsgDTt-Rh$5PtZF9S5$!%Ma1O_s zQyUFIJxXUYlq92~Q)Frm(~T$?w3aw%t9MtjOWB3~02m*f^2wf{*}QsP1X<|-XtT>3 z^X+3wDn2&rV-j8~i#HzC&QEM=E%1sxcIKk5yNp7W?AW~5Qm?{DW|@XW3gug5(#YeJ zU#n)3o;6ICPQw$muEo?iFpH#WBj-VSGb zPi~s+0h->3SXp6&F5*#f@3rB*k$Tx<(2Tovt6n=@4&nzy)3zVEDjqurpD}4h&+15a zqHm4uq*pUi2vu@`Qn=*~j_M50=swkx zb05m~oN>Cw^Gmo_MD3x{n|f1dsz}t}d%{)u|+!)for@yj#Kc44Mx#*2QZ-IiE$ z*E5>FnFHVv@_x;4zkFBxa2F)ICgVsB7QY`uIeKRmx<&LHKBby7edgCzTb7tCP%x8# zgkzMj*1lk1!BPtTZkN_%fxd=E`=0Zodbv$|%jsx5|PxD~IXBZgc`U zHu}K5I2_&kDNukSf+_@uX>^(bWVXAi{~QkWV+6x(Pvlg(y$<+@>&c$s{L@wS^}JVC z&m9z>laKI#cypRFuLYLJ+q-L+4n!7W{p)Jhen<4xg|Bt*tx0IyQ-+$)uXHwcz-o_E z-sLv&F>E(NR2z{fT1T=>s5B6TbS?x>X^wY?8onMze| zHIKk903|ul%k-<=4N#(bMrE1_S+LT@U4m4S zE)?wtleznJ&Y?qQfdBY?hxN!-;!zcJbSObiSpqJy%GN*$!PxZ+u}iL*0_M+c)&w)N zlpl7~Jr9Myu(c@o03)z=Fq?y7s(lg#{VWz8I-?ii*GH^`6c5HyW^u+I?9RGBaq=Dd z_lf0sJ0MVZJJAXq$}T(()-VPs~|reQNGtyf)Jr^ z2~wSvdC;sV9^5pe-{v0g`Mq(WoN>=gB=^RT!4#2L8`g9(OP+hKcIpGU#PB%Pf509) zzNFyrqGkXlqI-O|DkS2@6;}=uSLMnY`pM1~^pxbQ9o$j~m^5jotS-PV(z3gt~4Ux)?-_Kg)`#bNU zm&cg6mAuQ30w&ho>phrv3mclvT z@8Ia#4}8>8s*m)N^cHsfuDmOQ?fDJYZ-x>HFS*_B>6??RDa*jEgoa)dW)~D0b zT#*FJzFVQtPRx|T3c?n;#htNhQ&L^_EGd27rgZJjGyY@UIEpTrbW`8*I(}QUr;yrC$X+98_77_&D@9je_H$Ucq+dCe|Hqhz4kTPC1hU;+1Ip?ETt$!N)p*h zbhSyT(8X9vNsBg>N+ODOrG!?c1xZrcl%@KfIWy+Y+&-W0AHVO<1^%8Eko8_}+@5!Wr)31l4PY2aX6?V+o`ueP- zw@$?e{opyoa>TV zVf=f=j0;lN9zmZU7ers;qEa^+2)2@1BMD=TepB!}^SfwjJ&7 zWfV-@-)$?;F|?~>hV9mRZ@y5<23(hOb+KVWaGeBSrflx+61BOZVMAN3Pq&}S7#7el zlGVh=UZW36FcCmvsLKIgt?aMXZ_mezkN+J!zMOgu9$VZ z=53b8w~AvcV>ne3%o?FB9x7ee?NQL zQ5Fwy|mP^C{$_v_lpmYbgNaJ^j}_R*gb7# z+lZ-Rw@k+@&7df8jn7Ae!IANGwR6kG>z_$fw+iaCX$=*A$}CGP(7gTf#Mh6fnkyF% zR5Kz*?r&S&>z3>((o-WfaANDT*1643PkF?$#T|4WdleqKb-O`ae_U~t<{A-cJ&yHS z-#Z7l#kEhcDArtA>Yr{gU3Ko-=||t{d|Y}|bibflSo7^yy)IAQ$)0{%(~&!3?r$D2 zZRI|BGv|KY52tS2i%C49zv%qe^ldYzUFcrrHS74&j8h*1lm}vv$UCB&6~c`7w^*mz5ez11`M5nC-I z-&Eq}rI9XB?ihURSZTF->ANSnxgVFkzL@u9nkGv?GVO0dPx9Y&H6rg~KZqS}6LS~1 z^z>qT^5Q*aCyo|zj`?lRmifq-A~Gy*?v#31}KVzC;O~laICR z^qRH7=JU?;BO~Pzt`8b3C)wT>s);+0JKkxmuy}gc08aO7WBE+u3Kh zKR>ad`PHs%*Ns049q$@y-e&wfvE<2#XA^pr8wT{Jp4V#G8`oMYro@f`A6+|nJM(dE z?9&IS9^zg1hqbqxe~-}lk~81V^ZJ5|{zsOiPhT5pFH$KnXqquyeB6U54kx|x<2u`0 zZ$JFnSm~u=D`x!v{*V2Csr>(;F8lw~`v0u~O4^(5IK@qzG+Zl!7&4vrlt|ppa2@Z{ z^8U(?L(euoE4Tj43|c0~c9q>cvi8u7r2PZ3JI?tx-*;445RIh#7Tk0#&;8;Osrqb5 zOHsQGDxKh8$p7WvFZR+$(hnSqWc{%%_ioPaPGt^(o8~fS@s9zYdIYBs-P5O`{ey68 z>c%o1>|Ppd2NGgTZd?T$j<9{JMt9U9k@yvk{{q{Su%e^8Siiuc(czO|QSghhXH}lE zV^#j&{i?>a{i?LDs24lT7&|Fw=|M@u5;5eIlSFpqoP3KhKRYzcQ^wrAILUT+_*++? z!J%oY7472|%1F-K+4ARU)wu)Jec}EO4^NCfaX>Y#N^3cPRDfabJe}tA{cr9y3|5VK-N3pMAs4PbtqEuFHpiO7*x^tL~vThQI34 zxWsvA+M)-;{I_gO2A7qs7VO$9I99IHpkBcHwnvt{3#aCxM&HA?x84rDI;|_11CL=H zZ|E?;4m2+Vb`jHd5K~V<@X7FTGD^l_n#?j#M4E4M&KNi-)mmQc^O&=@u~f8O>bDkO znZAbB;cd5f6x}~8dnM9>b8pN<_FX?M>BO>Xe|-aV*DR@+i}5ir9g*#tj>(-SmbTK; z8SSpBZ$&~huiuXd{1N`+oB}&RXVK)J4qge*v2Vlmy^|4R`_CP_ar>Om;ir|4*O)f1 zFFEtVxzVlg=g+-i*0t&>JB_eyl8SJfWWcJbTQcq!pKr@A4;l5r#Pn# zbtXPwI-@hz+Lg_7%*wl;c0;>jINNbyio%lGq7_o-1`N#@Y=1PhrzJ&c)#+w!TgN8U z$tv>jOwmOPm3?HKx(ob<_3+K)B=?Mr9};Fr)oi1{e(9;pkWqWd?6OSqTi9@L)d=&>34@Xa2|7E&Ip>Wl?D_2}S#P_7PNY*D!Q7`k?(*34h zvNU@_37@Qkj;vT%s)VQK?cn_foy-=59g?ikQ0czB+SB^)hFvoojW^z$R^+fSLv(y> z+0jWAZbb_EQD?8!eH9W$1Dg1^Pcz#8TTNx?c|-7(;P$yY*UdJHJMhfM?;875V8X_g zac9CBdb%@=7dWOw$E3V^6*+8hW&A4rh~%KAw7dsp`?dEM9uBNG7QPW5eY$+}Cc_I^ zef_!_Z2z00s_V_vBOQwB9BvI4t3-Uv8Q0wWR#iDQcS~|p&5O|CS4QtkLz_C}yu&K} zVqUG1914?~xmbQ{(cTAlO^dJ3dalX-{#d?s;Nahu^yc|D4^8g=W?ZB7qq#!1CQiUd zwk$jT#hdGX`DV73k>x$&?H$Ypmw9$fRP1V~V4(4vFd5Bf#T)e&OqqJ{gyQ$g&0j1d zqzZe!-_`Fl3(4#_{cFkk9aV0T(PJ)X?rR(#nJgSt{y~}T())Y<>`y=U>tF3T&Q6+O z@Z--EO`CxB$%~^y`;R<}S&HvOEqfuU#s~IYcZl-zJlMW#;B}b>6WJRzR&_9p2bV8g zrqVZN&)%&yDVlII=TPsIpxIhaMJ}8wl8ackKY9L%M-89+^&B6sh})+#5;XulER-rTu0PuCo-s&Fkb<6DgyPYSM zBzUG(j?EX82zz`c{@l7xnt%7*ym@C|)A@aiCv5ZHaQYlW!*;T_{qo?Jr~XsR;uLp% zZ`abT@Qf)h8XnFMv;MP?Pkw3Vp@deQ<^-+z?x&wci|S2W9mnStTK7?y)ylTI8`(SZ z&AGauPuVW)+w7x@`X2jL^zBSvCbE0~g-!?Ev}TKbgO-HG_d5%%Uc@Z8?}oZMO+M1gvKB~Z@tp)$%-Nk_ zJH%_<2+vNN6V1uaI)uxfx`x_HbGI1Pj{*OnNs>Y+{ZONveEPrjqXI=i0XFP1~HTA`XX( zF7j3Okp-zCe^^0nPm%)Pe#MFJFP?uM`0nZ8yr@H`3SKBQD@HN3r?tcidKQ%xg!J6k zyOVbCT%vRCV*4`_Z%FnCPv}y4{=+afvF%VEQ_lJNw6RZJ#xtWB!2#oTq{lhSJ0ASW z&g{}FH}BlK=6*OMS?l9$#YZ-M|X$IbKVq;?hVKOUxuSHUJRxz?97m{F)fk!Hm)f&6a$~LpZ?)SCHrn^iH9y2&wrABF^Mx(Ge$7V&m$%wA> zK)7mtNUh8w=lTBmUqw}Z=nUT4Gt#i6hIz;KwOvxq$hjGR+}J^X z*`8m!{|w&E9QplV=gJ93A6YqGY?&i@qPzHJT}}gQn)yiI&g3P`C-Iq^Ma@OZx6U}Q zML6wE{j3{L?Hy?gj#>W`|hYl(6LSGJuIj5NFYNI<9lakTMu_IrVL-}H{4{-v+h zw;U08d2FrBg=0bU!`GTfzgkgf*+2C_S4cRY^P}d;H{Cz(Jl-0<`L_Tz-bo8TrS%I8 zTcw3ZU6%5lWZ;eP;0~K_)zPWQ@!T>6iCD>|foB?m3*wTp`lOy7-9JaDPsOcTqg_%h z`cuN+Z-YthmQQ*!%@*6w*m1e{ynCTLe`N-vE4}QSd5!I**s}FYEY0_ODPQLMZ9b;V z9gcNVH|(8daOq`SaGkZro|zdOu|r3bMAcvSENvft*e;Xn?`P7$UQ%^`-m%*;FCT@+ zRas7VJ{z@Y{`~o$1|lt6U2Tr0KRCGO;b)LS1hEuSL8g%VcM^(MOs_E&IX1F5OmM~q z_tH&G*N>d7j_~ib96c)(V*T@{3I2iP8@Js)m(Dr#8GIml@wpDAMZYdTQoWZ0zK?7= zmwi{a`jqeR+ml!dk-ZC2NO$AJHhI~`iAftC?-4$$GPC>YmD%9?$ik6d`>zIFatiX- zmrY-!_D1Pa!%pY$s(|)^%r&=n^mh$9yOoqmrK*FYLSfa5j5~(72_*G(6HFkBm6zJv zn<@qR2!=aU-cx-kG;@I-n<*As#GgMe;N#A`n{tlzci%9VE1cVS**RYBl00^Vs2v<3 z${EKGj}QfaD!jlwLX?dkArc8o+zgHoX`jr{fk%k$M%@HQh%~52h_d*IBSc%k5h6bg zUuO;M2$68p@yrBvcCgvZ>ywOLjlDXH?RMR1)zhFQ-lvk~Z*TXIdtn$4j}V<%_(=T7 zM$>cF^{<y0*SkUwf4r^+{HD(R8=Ba)Us7Ge1V zSu#=a{ZBl9_{01${eQ_HMb}N}`6JJX$RE*TgBRiXL;c{-%s6GX-^8yD7GW1sY<4cZ zGe1&$l3uuSq>c2WLwglU zVt3>%2eqj(78#x)aXkvRPMOww?VNepyfL|}Pbs-7>YL4n?|U_mXcO3=_`T z6#P9Nd-vFACBu0=PJ}GE(TY4q#5Ojerj48|BX@oyG3VjE!JS2B;ybcc+ImW7NSmjG z-YYx&L++X5UegukV+7x7?%nb%bgVWqui(p!zk}?%OHTbfyKUctNgA!e?f%03@0-1@ z`ljF7R(;~|g2MbAxt4)a=`xBB@1DxO@c2l(srJP+wF~vSQnR8X*q^p~-f&Hq@;B0X z=eG4D* zhgZkfY78^K8yT?&KkN7JSF%!+G0K{x-*srW-ZQHtL)}WvrNd2coAzzCoW3cwqcZG( zSh9S={l8|xpE>h7n@R@}MXa;DYN3Tg3cy#GQ@`u++?#cbjI2w`~?4Knydb%6Rmt|jx z-FJ9iad*{;M<0?NS-PH$Qs|3sds#atb?A0?Y_yJ!(r=}$Y3nod#ipOzYS#Nhc-o&& zt?}-)!gE@BCPmNa{AoGeZY0`8dW`1O3on;;EPV2C#^;>+aAmW}tJ+`16n+1-k`c10 zm~+{)aPF02e~X<ha4J&TJh}0$V3x|v zu&Y;e+s!~vSKfbh$t9;^>qsf#8?3CTZT^Q-)rwcB&V<;6aIf49X=YmS^&ed+3S@#t3{=d752N8!ft zf>rvHb=$@Tf?K`}AKhyEYd0q_+52onR8bmd`4&C)<&w@5o@zR2gZg}nhi0kW48JBa zd~i+QUAx`M^{)%gD%g+6&fWIt_zpwXA-_KH`y$Dw>nktWcK(Rll~~ur%(|zPaxe4T zuCZ6xJzjPj4JSJfG2U#5=wRg4VtrU&=W^BSiLD z5dMbu+@xMZO7puEe8a2c3_O+?drK?teFw+0$ad=e>q)YFrxS$LT*4cHR|faZ^4OhJ zf8g4J`C@;iTZ1m>sL!@u;k!Pi#qrf$!^lsf>yiUbB`)La70?D}sJpuv9c>MQ+OFR( zDmDxsRy+4wtMcTnLrVKUq{j+$&svdZ!+QU4Kxf}qlQGF|*E@WVe?N2hhi9mLY+3$= z&l2L@iSr5-$HP zvgwgl!HgwMhWwre+lpJ-W@_FDxvHg6t9^CqyaO4t#0qC-E;%XOASafbdh%1IGvh%{ zi`LlhL9G*RXt{X~_xEd;YQE_V*DeTlbjlIdTF!21+P8M0taH~Lt%aQ3f@%g<9ot&& z?Z5o8#8&^s$t$tX@ktKRqN_P{Kc~#eIgKVgbnX!0(!l%Cj3I^#l_J5hOOb#8aCp>k zn4vwI<$jusl-!u*llLiE?&~?!E#h+f$Ywj~%%`tvUtd4F zsHXTv`!=if9MiO)g2T&YBYgk9 z*bsN68BOru`}pzeqdOgmZI^Gqd-+o|A!E_!@;OEK3agVViY5&wXB~BGNuEA5H&W)O zQ2Nm+1_d802A?m?P+r}3Fz++x-7@R+We>Gd`xkGh88VqwC^>97RIGDxFm8S2(UYsP z53TSGdD#Y5>H6NVn;hehhIJnQYxA{AZ@5PHc~X{5K$Pyw89V=cbg}-;)@?B|doaD} zZ?(fdvpJL77QXfu@IHS^q`G2;%ebDbzQU&i5fj>q>Q=3HiD)~wdTCpiX+=Q&s%njj zzIjt6_IkN)T56eA^We=6%gMg2mr4(gXrDWX#0srXxSJyV=#<@wQugjS-!psPmEA6i z$-9=Fy0G+CteCuf66i(L`CHlfuFFFj$CYMH9svk9Au%T&j_R~blsBF2nnv+8a?7GuV-RP6-M zPP?2Mn6^^Lr6M)3bfpjjXzq>K7)O6c!;+Ok*Q!hI{}9Bgbq}$k%U<1nH^ho7v&Cvz zJz1R=^Ltrr85=xo`!Cour^~AD*9&FM9xG36`+TM7^U%n%gKaatFSOqI!>8!v)gN+2 z)qOzWSqS?|vHJjIJ9(HDUHGd*p6@ z=?LG?>z8d6o;}}O;_H7n=%PJfheZ;wRiHPUZT|mb5-5`1x3uG$&&F zOT4ZkUf0jr^Jm2DmU71Drz(+8Gy9%IwT6#xvfnG{@3DKqTz6m3R+e4M_rF)$*lE@5 zkV5vv3k685SU2MdM_mSZh*aFPXQquI{fmv?`WioTtx}|s*?`{m+B9Hu{}KDQMfT5f z?OjhD0UBI)^+#vg*sc(1=a-u;=LqXu=Fh2Q_*DMQUN{HXd9n6b|F=h;6clV=tD@mT)&)$d2XjfML+j0~cYB?&KG_S?uE z9M)@-bu0LBYk@0U^px4@oG(S%9RWHW)=zHqdgOeW5PI2H{HN_k4>`LP-SmG1JV z%lmuIgKwApd>)k(uutonPrP1N_7|P%?=gU9z%k3v%MB8)?rizf;1;>FmrIsrl@I4G zXxo)i{%z%jeM>?U^nIV5O7V$bwd&U5lOncTT&*n)>;%`%kg%~d&{Wzdd-u=bIkToF0(GM7Xh7}URX|6uYu#rAK*h4BeKn(I7W8?W7UzH;HUX#KqE zr+kVIWip?F_0FbWC}-CCc9X=o5RP~K{?8JK&%8{8cOs ze#r_V#_(6rbnq)o5V7FWYDZ~bu#zG|$W%y+76re8g%DG2$@4-47f2g3j-bI`zN5c( zjRUuyV$En8N3;O)H5gCuh3(lqp4y(4@kD!INgZKo$qmBPXWkd4K2wEBZQU9swPgJ$ zZIDT{$BISONFj^bf?gK21r8$A7UYT$C1LB{jnbghmZIGDcyJZfE=p~Rz8Fy`R1C2b z0;+M6bul+U?OBKndQA*bLsc}9F|aab9hS8WAD>5Xc<9nt6=U#W)_6n<4O~eSz)Z{9 zLM!k_j-mus7nKHPf)!Ae$;fza%UBYKFt{Wy$cHfz6`M*>Fu_=%ved^Gn;~%Qf&wuo zGHE1L17tE}h-rY?OQE4}d>BgTYXiyynR;?uwrG$qLnc=Yfz!koX9eae9fa_{3$@C6DbIbV1u7nb$ioI>+Cz3I za_nU%qW!)I+;@Ot{)qJAV3aKa$73jF2xN*9EP2%yQGnGkH6d#-PzSwgMiCOJz)K#m zBC2DeAp;nwh&D+e(xm$(QPdzkwq zUUiHpql1!!G|E!o$>tiUl@y`@t4)~VdCde&yY8{$;~URaA^5*{lhG6@M2_1wPZN%^ z-bD0(FVQxrt}GlR)de;46$eSpKpk3`g6f)duR_+Da+c^()CDzDgMk#yK=3bCeIiOK z0rIprgCJd98*vcR3~YS5ERgw{8Coh!DB(T^9GHss$|7*K3b(PlB`45yB1*UMZX68M zBy0?2euRUh%+T;tr*jUNDx!8Oh%8rSiPHg3ip)@wDy*ayI@g-&A|qHy@GeU;yp|{?SU#kyF541K=M$4z60E$_j}3I2bLjm+-Gb#q|(r)){Nk`%R5`yg$f3 zR}s8YlcGU$*5e@EoZ}o|Nm5-<32qMPTt$Re3%jtQhFsTegr)F=1I;Mv2zk+k_rO8A z@L4!W+IF21=$cv_xdNHMc!$rr3}{HB6Z%YvFf%k>;tWomJw-y;fHn-&Mwupcr-4O~fkXIvbh>|poOj?EWr1f~AI7n&)KK&>T(uMEFK~hc7 z@wFo%Pf{ZXCIOACjO5sc$f9~IgbDq;uwt<=&u35YouvrKGfeZ2|e-4Rg zwKg&V$`3X5JQ4je1#|=Sh*w>Yqx1w2M4+Unz>c7P6s2iPAc^2Wa|;&&rRysVqLg0a zb`ol@OBpvIL;@=6rQaWcBS5Zi;2*swoSjTj)Wc0gz8BU54rI20^Cm_P zwbP?Iz?+R6{k6Oyp}Glzq@Yk$n=p_!B<>I;X~MHM<2-4y&%r^u=ovUjca;Y?NOu+S zEs!VXDlnVh`@!^<0B%z8kE?iX;rv~P#|az^g`1p6hJzubbSt{wklI7~Y)6eus7V0E zX$66jK>@`dAW+g%p!KbH5FBU<*hrxCV5uWeQUx&9`O|R@q`0sR&Jd;e#MX3lqX}XR z$Ad40*RQ<;EN}3S?6=an)~AdI6Q^wUL&Y2r8Kh+=Dr1UhSYXk%?~hK)Fc?Q+|G=hI z${#zEk8f3IY>@uiRS~0aSEIMr!PIgf8_hIDY`OgiBZqS!op(rKkArlLrC}gt90bL^ zhnJ-5ir-TME&$7Ql1pJ|ejnN9i5!LINej338|=P*P^-Ps0$U3@f0I*zUvl z;=>3CY=i)>8m}YNqNfdzX7gb*(uNoatIh?0*Aeur4K@Dxj-y}f{)zt}0;Suwm_X^?{C<=JE&g>+ z;CxBj!bV0AC_VnmAW9h`_7lbEVjG^sSz;o>P9Q@0-su~_ov}^O^c1k>B-d~8K_X0+ zHY=10K(fTf3yG(|)@YCk9H^N<>ns}KKnz}CkS{-rUUndY9Cm?s&T?KHrDOnTB(ZV; zf&?p60X9^j8)pz1Afw*Aq6Qeg9`xIE*{@b{jc*3u5a58}QEsj; zw$B>d>HsE2D1TQkb1R^4qN#(URaeooZa_Jt%fp79hbZ1K=qKN=qKuh@pFr#T)Zz6= z;bFae0wom%g?~n%bd@O7;~eNJ2_R6qv+N^K(lK5?xOJ=@DCfX9Ugf;1M`zAP)ZqXk zP)~dLa-b3Ee>B2(gR{E|ENN*YLOc^vCvS6gHSIr5NPVLfpifA7gOp9oW*JTUcS4Gl zO{Jp`^%1yH1jRJUYNgSrQ&I{nxdsYfl$?{obk1Bzp`k{b5V(p4=bKms6w)+Qd>nNu z>ji~|Rg-}uPZ&$0t8F#W4Iq}7N_V->MX`}4r2EONTZoG3Au=qvdt5qr0GN7$dO!bj zdGlCcoy|b^^uhV{2k67u6#rxKKjd;S$JDJ$4memdsWhlfyEVkyE9R7}9aK6>vjnr3 zLPMMM39Vp?W=?75J`!bY1#gh#GzaFc%+)+}xXlMw=vfNMoU2f1hi?=bM_e3qm!nf6 zkgG`x_o*CDamv)6n@ViV=usDXpvO__m~*H8YUL|V!M=`_)XHTF+C!cCV`)F-(k$rO zVeO#O=zfZ20agQ*&b12kQ}Jh1zNDW*9r#daJkze25?DDDIvBgs=2o9jX#Z($RsK2m zx&L8q)sKfvQSg6HqlJ`8_*j}JjWSExC{I-}rMNEQ`HE9_g}3L-!$3`x=BUWiR~*Jw z%4<2MN5tYaD(+61%|?=6Lw@wWvj&#p9X0*Mi_$wu+c&U2*`LC`cOwqcM@`q>pu-;2 zQ4{AaYBQIZa-rl6K4t0;KD&2d%H_>?WC{$?Rr3&qG06}D<4HJMgOvs+C*U9W^d{x+ z99GWAideIm;|Z#c?*@>)Jc5T8LCGW>B-3D)4*;*=AerxAb-oXJw`X7hp3To`Sqq zE*T;vMuP&094qzv=(N!b^XcX;9N}cWl124RiIG2MNd6go49=HMiY73l@n;Yy?LCkG z0fCb0z^u-1$iO*}>VO<#36zWq%y9hY36#_cLe^s6n-2CI{C?G0?(5qA$Zn1^m+l8K`F{Fgn?M*wDODm<(#Dm#3pJ>Ef`ovJhl~ zn;D9~1R%wsLhiq{9&UoDX3x?5aQT42FG(i z2F?USVFNLAC6j?}2qT2WJeOZyjKO*MVFFiH%-8tW5hy7t77YBA5S_wZ0>HeS|Ia9g z(YIU3!CoBaOPUpPSN;?NCCv)aIszpHgB%bEoC9fASk|6EF=^pT%P(&}$qvxBF(>kR z{U$K=N5(m}>F91~aH$`sP zzF}B)0(W)?qLvdd4imVqx==^!2$XbvD9dF6rEB9ifzq{Mqk!|JYa^LJN!zD~11;Ao zFwkS+h$^&uBS4TNis*-M!tBs%y;T|L)iDf8E`zRLh{L?DKQaoF{s4X4N)0km;0g;J zJ!upsrG?&pbrdGWg-j522HG#c;Pz{?FYoXCGAhyX43-;fQ1WHCc?ELk#@v>xO z!(KgxKuPH!-wKFQdi5VfF;ZI08a4q=kP^A)Oz4VV6Op)F_)GkRt}@fvA5bwFC`ulr%HuD~KzB zl4`*0hU_L#GQeSGzfYi~axk+Y5=J;*QWubeH$*9u7uiN=e-w2F^D!)eFKNK}td)H; zX2ydQ_Y`|4rwwG}j7fP-@F@)#?LsXN)g$r6pNA&slW1yg-#iUvBm!3;r*)t!+#yiX z6`*;w&GE9NW}qu9hbX>{2o^g*6eE=ajs5|mlx`lPQ!H?nv^Ym12$UA*$O!_);vCAw zv_(BD(X*R?r9WDblEXzW81>_m5C!3PmK2IM@BuOup#F1gP@@Fk17s+`iJC_QO3Dv? zV5%)%mhJ<|1WJkveV_)Sl;Iup0frsMfii{Z2m#(<+-3qLwE`>OAW*WOgLAin_GrRd z>RZSYd$ejTVPa^HEzam0HZj(M*1ZM+O8*7}UEKvMMj6|{$}0$zln&awlt4)jhsNwB zP}0L8s_TmLC53@}R}m=P#3u=q6!F9+5IKFW= z1WF2O4OEZtd!P&1ge_ArV4)|vk4>>9zmN|DHFRQVaJMDEgziSjG{TL?V zJ`$#lRt$Y1Z%W$6?8m=)6egvG@R?DV6b!Q~|Cdpi)B-GRIG^B63cPzekS}^ZdSW|a z*(MAKUx4;)N0jLuHNUh!1Ff455+NxE%-`{&FfAbYYjAiHS4Wt6_Hz0pKie2Je`y z2`^0BT?UzeQ83Lb=8eK+U_gUrjly)VXdZ=0ML@5R3?q1xYJ*JXjl!f?L}vmg z;Dn*OGZ8h|$L|qLRa^nHIa0D5ZOR0`LGQplB2dE})Ww*L2sC8}bup$Nmf)RYnzjn7 z$GaSpK%k@!V6;{fC@Cw9_um9c$^^?=N8)@*1wfQdprj+*^xt6|0uuN`RbEG^i$qm+ zA?h&IPtAHX1v_9OJArpGV)+^}t(oB46`_Bl$dDgU@cKo>d)DZCB4R#dM`4@uyA%MTWu?26P z-TLQcUx4Kosbjo#q``iUECgQrgevU;51zgi0~FB(dk_|sr^se2_+lU*Uo40j`tzoy z!lHV{ypL19Uiuy^0`NTo&7@ax+J#=$N_68|G}g?_d>=p|0ugte}gR1&fQ$)4 z8LAWzo(%O07OkOu!U6h^!_XOsUq#;(5c|{<3qb29)!|C|$Ady;@>53p5jqd)8+ao! z3yH^F3~Lfp0zG2JJYK0L-^5$y3W~xm=R+ZQX2xB}K#$kkoKpJ5zovq&GA^H&(Zzc> zqvxZ~jF_(U+5(t;Xc}+PZI2*#eY1ahh#r1dcWgyb6{Hk;*Es$;tRrh?#Oo`RfcI;| z85{q@c!SuywT*yjj1@2AO)rQ#|1hS0Qk>qH{MSSsUgvkBexRFPN2!yj^N+JrzoJdA z6W;~vh#UXo*U5W7|=f0oF5G6RF}Y-7hJXv3WP`#pGxr~kI>ScORXq+I9 z5`C*aGmA<#(&t^aen=%pH&70|V76mQe&aqE+^vT%y26QF=dSoo^`Iv-0#QrXwhyHa zcL7J(0kB*6oy!ib^CRZH;MyYf6OHse1p?r*9rU3XA_d3SaMq3c)kZ!(IZBS><9qp= zf$n<>z8`%00A+cEHK>4$PBt4G{-<3Zb`z-uXnPG<#Z(%7H62>#4VBKd4m7N)B9|{W zW?)=|Qb@qbXp7}XDKwtA=%JG6aiOh5c^DZN|9z9yN=kV=E?|}<_gb;mD03N7;s83k zvO1TBUGj)KI}F`rR1ydgHS~-xWqOl&ghHnW1JtLW2A4C%+o3*PDKy~ilt6+0nocKE zf&r2nsbreB!*+{K;yxJMD#;rw9#k?d7^rs&(t{z3QU?YD3?AyOXY`(@buzaJScKsF z+@V@hr*KKwy=auvaL^z(DRk_nG;R7xJ0?q)TOWN5zH$wFC&)UILNca%FsqD4Blm+q z7ye44(OuY7kNY@q(;B5`fx3>TlHY(0s`TwN6?8JbdkN}}di@q%*S{!r;6f_w<*7FW z(RFQN$Za9d(IVzs6D|$hAw=n~VVZO_<&sUf8)%?pJ18{lj-iS4=^j=Cg>FJuHgmig zmoIh&kqUkChv`nCbBcR;{F{|YA)D~*Uuve%uxpFRm1-Cn5>vSk22{%K8tM%`bbWhM z>QFYR!aG*rq&{=oG%iyWQOX(` zrf>eg1WHa+!m@#m7zf@Z!UF_Kn``3lBv5j$3DXR}rW4MAoK=E+mpdV7|1g-+q(wD< zDS!<}7~BOy6jK8K2LRtIA!b{@V2H2_f{GqO)M2)#9u%j0V1*lk+At6qnYpvZg11IK TzIY@aDZIwV*XjY@UHSe8syj3= diff --git a/Code/Dokumentation/Other/angular momentum to angular velocity.pdf b/Code/Dokumentation/Other/angular momentum to angular velocity.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8c6ef85ba4e3206698d7b1a03204f157f0851c9d GIT binary patch literal 123925 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

NB%CEtP#;X*4FAp`EPN z7k^+yz&_aEtjP_~Ufkedpc|b!LA^ZLpc@*T3AY*QLv(gpa{e@Sq~V+g@z!AaXV=bL zj&2V;eF+Km@LWz_#V<{<(q<~bbp&}EAMEnarCt-X`T=#-)b(}UbT|I`U~{h9Tm%vc z16|Agp21ob0Mfzo%va`vcLqXX)*Ee1ih;9MZ_9ys1Y6&JqLQd^aBZSok-N4hMHE{?iqj%F8qlb=Ae~}v4xB-cEP^CN@vTXIXW@7%CN9jfIwDDm1 zej<5nq4gc&c(SkvwjJLb0Kh;NgS2mfQM4g5I`-4uriQ5M#7p|e8j+#v(Ee$!z=k=T+PtL~{)YIVT z`|XWz`KQ!0d$D#i;5=_-KWW*xcAHxKst)%8WH~w|W58aTM`>cXuksM2ndT>P#ev|d zk3)=|a|Fq%uW}2d`CSICz`)I+HhypKA!j4}%G6nz0x}@jo&Y!zJaEU-qmoffi~}tYz4qwP z0zbWP;9ULte%HCnqtfZG;30V3pniTUIRY}^NZ&}#=+AM8VKUJe)?Q^(=l4fIMcoXs zQNfbCo!?St(C@zhMa)i-@K4?pjRG29!=5O``{l2az=iThz$^Vyc|!)d7b+I^48=zel@j7%FBN@&S<+a7`M>a;61OTE|3HBI8QPWHKIyl`%`M)5s)qu z&kc#ailvrVWt#XVy>6elSHq#kO_WQH8B;Evv8W(8HC|OZ$|^4qvaCS(nrL=6Czaky zDQ$Q~SC5hQF%;3!oG)q0ab$ZjxSKLDeCL4Yl;_N6VG6waC?2aWTB|ZpB}Juq-6{|= z4zxQ(_t@f{(vheR-NCYKQiAiQds$*4djUB5T+T?=cdctYCY#^bUX>aR2UTGzBrGKP z!y!M78#f@-Z%6{qt@E!5;SPYYIJ=-92xiMj!6k-6z5M(^hO;NU1&}1bZgX=#AW-T(^^^FW&6on#i9n zj@yoC`YU@Y*Z!mMu6;ADJouUKsF>GWd%vk;e`ronDScbQ_An?Qg@Y=4FuYz?94{YM zsjPgJ{P>qoI>LohuT;X&J2>|RKJ;zF zP`?oRObFR>IQcjabO&%1k@oZn^^YLzI$k<%iCBe0yV!21Cc71gsKT@Xq!IA|G9pnD z1aOEd(eb&-@=-DvNdlnUK_1HI(x^sKq*h|2RZ?VcaUoHKdb%<*2?~jX2!2Jmni564 zG~}dc?yJ-H8_zdKoL{QFs_GpBYc1Bqh3#V6Aw z-_>CRS!|`Iint@Boc~aMeq8@DW_%K3^n2ngbP3+m3IKc@XRhOKEph_KaE3!jeC)t} zWc-+HXo)d$(c)j<&>pvj{yVr&A=wz8!noLkF2AwHf82>nZimJLdVcjqz~0@}6`{l7 zQTBfo<+{jU`U0Mlyz(9r#P9<_^d{64^)im@vSDI5|F*i-G*2RaDKN~fMof>b;tUAiu$J`KC!gI8Zpzi(*}yp|HE&c@nF>pmhE@Z-U(^PPrSNSIyK^bi^xhra!MQvON&5+2u~w(=dbFF?dCeK5Tm z=?29H@i|%zVoNx?LP@!{GNogbHeruCm1;YE+1!sB}bvx}5Jl#<3qSxiS*I z@p#gn153#3&-zr~QAvHeVADc)tZjkjMroXKuoh$CR$#)U{ZzQ|Hp!J3{q|;nmZ+>- zQEJNNIGIf@bn2`gBjK&|7|{mma-)4GMz~n{ABz7au3fQ%z!MOS!Ett z`NvsIcf=zj{D@Sf#9~_`{Px9bGiGyqX;%|wH{)hEyG?A_Q?iFmaK}w>g3oE6cENrZ z=Q-ah$T43wOAb@r&qRjIJw~I8+{~41zwf>CIaqDdu83(1^~4(O$HJGHe1wlPV$aW~ zKiSB01mD8p7lq-$JzA!`ORk86^d})YuoF>2su$lEJrhALr5lqE`BG6Er`1%u@)O1& zH7vz{^Aq)tnb`EFSz(C@QvGtrkhrQ;HH4I%atVC zp(>myPNff}Aju=;jC8N@qRyuO7>q#bfIF*-HAf0=2eKpm3c8oq>W!W8?#U8>&i{dv z9aL`kS84Eng2(@4kr`N+8JPZ?L16#yi1L4h#Qzn8Ao?#E1R4ov18WPT|JD%L{zEJH zcMgG>gYDlr1craY)C}~D%>R=^_)8@~(h2;#f$+b82#ies7ZBl}jP?IaU32{RTl$aG zH51eS3L+%V*#^-=kKS;L#QrG=oCmB5j@R6f#C{et^7@sTPP+)MkPo))+wjL`5jH>X zYVMdwR-0{XG?T;ksmRg*wWncYh^6zAwfEX)v{OYQB~-qko~cBig{>$&6j$L z+!w0F;yFtF>d8GIjNJDQv%5j9;k|qlzK!jqSN|7R6mcbGK>?goF6YY@47}26=R$$? zu?ddmH}FjqP=X@$dL$-`bgj}Zmsd!+bfRSPi4#h|%8#YRC{3EKk1MaZNj&Mqa|2{H zA{xkjD2K>)W9U7`iPK>=J0Brh^*RAxW2otM_!6eaoW~+|`pY#pW*(0#{WnK|0Wdto&V4AKY{-??b|zX&wH}GYuCA?Gsgl70hXawaM#y9byH5ZbNDa!86N5}5 znnNVl%(KQ~2LhHztZV98B&8~v?+I*_a_6I)W1Ujze|VV|ThrVuynKD%eR%ukk(nH4 zXK*y-a5&9obg0d~FTi|_hqvOxR!q^IWA&b0u-OWL_e8_D((JVJx{q-Abt2pn{q6k& zG=!$x?2XSJ5w9B+f13uiYK=L+s{O1LOs$8n>@qg_-HYPaek2Mdc-1C^#iS}Y8lsfN z93T2DZ+s+jLC27e+kv=}LB4B8YQA@W)kQhh)em8*bxzoS(D3^``Lq z^|J@}xJ7q}d;x}Iy;S%^0y2abA`OobajG8dMPPDu()1krZB3!ajLMg-;RH10q z5>_;6lSt#n{UvPK)YaYl=NkY=`1j8@f~`O!eI5v(8`nJn^A{Jm7(q7|i`zSt0gGa( zstn%Vy}w3+^|(*rDJHVOO(EAjz;ckUB>Qr#)B@tL5!*d}pw$95H31r8JuV6%$lrg( z<9}&oK0~C9n8}5Yb|9M(g=Y9aQG@0Hj)kU=F3nJLnZ9r~B6Yh0kL=67$W`UPX2zPz zc0{A(S09t+!33X9Wt)+*5}-*utEKxuXsU%)i^}Hf7ML|7uMu4ZsB})Dn_b@}oN?bl zbR*|-o{KolX*v=Agj|cw@iGSwq%=lqd5n;N)Lc8qOs z2TusyP#4F;V-S6oWiz&osMsyZD?S879Tc9NUZgdBA9A!amM8odI8WF&aE;6E$d-tb z%n}s8ZVP$9MnRd?yvylFe}~!L2Y2f(d@c~p4|rdQ-&oDjc0ekg0Me<_V_rKv+&+~A zQio)D`WELGaO42Ain$RiCs>WZ&Im*GzEi<<&!M3s$@=uw!S*54TU@DCX`T?FB;b|G zDMR||+yd1pi+UaGR)n8S?cr$1MJZj-9qEE(`UuM@K{K9DAV~F)sP&kDc0`qdY1&@U zs~d41xiN$rK%3U<#$)MOmIAnO3D}d+jLNB{GmR%Aj$ogdpCHm26ibqP6NKAd)}8Ao zH^*?!P|nz&fvQiO&x6mVPqaW4ch_??B{Bbt<{ZXlh#g#Je_9Y-v)vEucbemRII#SZ z40qb*W@xzGsXN$Dm`|He`t2~kXt;xyTd~)hVVPc0TQa#Mv~dE*^r@45Z7esu7e>5& zzbU`kv#jYnPWq>XW&=(J9gZHO_w6&bQnEG5SAqR3c`ET$%lWX?e4JCP)2#l7o>Q|^ zmH4i<^%wGY^xwNq)Pc+%`oad)yMWrzO78bs zkj4tY6H`GvX@WHPPEH0XYWWDSv4t_GhPw+T{dZmhj;=+_o|s?2%`XJ6y#d?C2PHc% z`XsRh63ihG)n)!Iw==Ol(5j^<#U*GjLmQ@jX?s<-F|V|*+d+LHH(=l2fIobpf?6(4*0Lv`3~Tz;U3}!ep85tME4H=xOb(E_eRi>)qV$E zo7UDrAeTR{pzh#H8~z5UV>24I2yQ+hspu#viKbY>IVr{vr+VJE+x&g3 z*!*3t{=zS;PQ=bWFW@pS25pzv6a5Q7^-CptkdbfXW1f!x0c4&BzvU-j)Bx@q3oiiY z5n^MmWM1(byxPO@zNCWHfX9vHJDGXNu`~TxSGQ-?Xl%z>P|D6#1Z74r#`Rt+e2DLK;_?;s+4(IfjIUSq*9jpKw3qZ1$0bXx_ z=|R@~>oTM>{ADDTm;;FTQgFS(8UZ)yC5kWk-_|-}Y5lc5V5)a7r|9Z2_`0y&?s#7? z#yudeatxPwrsyNq^#)erY%kE3Bf8Acob~XJu28r76xD~E=0}u|ENgOb0WfbI@90vm z+>b(!oR`>Wmk&qteW)85VY7iMdSSSrSMDshVc96;{`>u9bM+Xj@lW%3D@|_xxjf)T zo=81FnMRp0Rp_e+tm-{Sr*_VCLU2s-Xxl)n9(a`?QtP8V(cj^O`Z`+ralGJ9o&v1X zHcET=T0R!T=RV4hrB{t^Tglacq0#ugF|gu2S= z@KK`4_T=PX_g}nqV^@xxmmaKD#td1}pgpox2q3vc=qPPeI6VUm>Nn_kO8*~Y?--<8 z?6rCKY1_7aw{4rJZQHhOTc>T?wrv}yZO-}6JTvo7)$`%4+8>haRVztVcI{Mht=}@8 zrOUS{MqqlE8$ZK?5mFsJXEEy66+`FA`3*J^;P*$lG5G>YiWUqqGb>B8m=YI#ag?)y1NxFbJJ+9mAd-t zJ<+g9+zx07Xf0hDYbQu2CY|Wt8B=Q*VKo#u>xs(7#wd>z4qiVdhRux@<4Uu+q*oR_ z>IEGY{$TZF3d~6S0+XpE1M<>D2NsIfC`(n=9$^k#2UT0?>t0K(38}N7-9Vd=#=P53 zB4bT$*0X`EK(CEajdWaxJbAu@)@9ltTvQIUI`@idcb+5G zdQG!ima_PR#TO$HHC4Y&9$`F?$wAPf)4L<5q4GPZV)Dc|lw#2R;j20^BYK;s5c6O?EWeW? zt{E4krQz}E9kNBGTX5+)6}#1bN$yO_@y0URTn+I@9Zgy3X|RXp{Kz`^wRrDjOmvA# zr-5#^KApI8+ud1|TYF4LG~lxR(`g4kB;bm9^KG>Cnwu{^yg8nv2E>md5bbH%5NXxT)t|u%NUZ?fLM@~ znZyO!sW*uavuC$EkNZN+RItYk*7B&*~4|H;q0pXyy{F^F$o?huJ=^3`@)D2({36NDGicJ_m`uz(9J- zz*(+K-#QhL4_YkqklxNZNYw7<4UQF}g`7gZfNVuO>$}g2T?ao}3>pstpQ{U>8iQB@ zYrg{*+&9Ft8pj9)fAPE%kKug^q0OCAju8}5Jcus#qRa$5n1Y_(h|in@4!L|L^2UW2 zA!LD;Z3UurbL_-)OJBH7Wq1De;CyW?uPnF7uE@6FRL6zKW#T)2tvxB_={gsvS2nKq z4trEza{bGYNbyz*@@{Nh>|NhB-!$*0v!jGA+q`ZWbyQePN^l6@ukn8a#A~LNaYgB_p!k%II#oS%T=^c(L_xIXv6Nf1h5urtpjG%-i z_ePzU!B>_^!z0^;EnP&*bJ7iMTzsm+$Up;=iHTX8lTn}$gG&AzB4rR-FtXy28Wm0X zm@kA9J(k1VPhJ9jT4AEB{LYp zR`OmTt(X&F+`Xw^*`;n^@p|@Xge^c&Ez}Mj41^HJA#Lcf2Tnz@Vg~A4)?2i2UM=C9 zF`LDJ+`M=UlP}ZhI*YZ2^=_TU;iln+?doCPy3}p=A_z5(ezk?Zi}_)v5K;BtX(;G* zo?=h1R@AD9gGy=N_XXe5dS1t8USZUyZBwD^#bg1%NxbM!jhzZL4ZfCys^5hKN&)&S-*lkTRNfbgeZwFOz>8Q z@!&hLEo~brnJZ~sahnJlP&zmGXiU_!Dz7YU@Ca3y38`tqC>4Pf1&KuVAw_;+5szmk zUajT!%U7be8&?IBN{32IL~N+53=~z<4HL2q5H=0!J6NC#S-}cvMN_2mh*u2R2{J+Y zb^Re6A;kfIb%3QXf-nj*!kxyht&wI-r!`YpJ{JGYD1R4NeSJ8{vySz>y(Eifzs*L8*+HF$P@$s;4?5c@he~1!8K;4=p6lCae_iV$9B_p&YJGJ!=t| zJPDgH3Y;Ks*)Ug)-oTD`Q`Xr98}qz}w8fk$Kmv7@&REzm00rN%eC^CBq(W$_LTL6! zj^*EGUwQx3RFC;*OZw`!;Gwc$igW{6m)R_!ER(wIMI8paU7QV&zRX|y98kWws%$}w zB)imzd!xCE*kL`vpbmm%IM*I3QTcm=GG&lwj+DSLj*!1c>}dm19UZ5^poezu?}e)y zSS|}blsE1isJ>dMtjaI0_H;ctR!$7AS#-`A4H_$V4A-}<2AGIM2@%?{0wu?>nW@K^ z3u>5q2p?a+Fwq7rT4WC9Q>`igj4#t@cSUI6ydpyTYMI|;#BbW@1lh86dtucj^Wg;q zSl)g~Y$+Pb(rF=$kLkOk)XM$Q^X?er=(LBz>4Q4X#)oh)O4h-A#e`@IvZ=|aXK+{0 zxKOvhf#UjkBzy^j+VAG#W*irbOR&$^AgjKMPSs}_*_Wl(i(A?)>z7rvU_||O^)eas zhqj2Hup3}uGAoHI`r8c4LHRn{ z)V~lMCmrH1*o>+if@@$YqV3_qT=AP05Q!cA!}26#Gp7>xlik!2<)*8u@;izq(#o>L z_kf~Sx7yuhzHAvQ9A0KSE=g@V9!7$NdZx{kNd_bZG#jw6Dy&pX`LFDE&(k zCLCXfDTq@v|Dr{e$c||XRTur6aw;rH)Y$~8Cf+eMwBH{9V`M@FtwNHXJ&2sjlb@-z z25yGa?$mHf+58wW1gVa_HZ!$DkX1zERdq_4s*7!)N}&&TB)&}-7ZvWCg)5-j$?OlJ zst44Gi%4WDcL2q3TPJuf2glinz9kytDF}N7e5DT83(iJEL_DXQb$C>iy{2nz#Yrzps`UIk39X&#@x$jy=63RQwKs*qW zhumr+F%Gy)VkQmvcS1OxTpCOWLA(KhcN_bar{YX$c!9_fvA~kIsGfl$2;85>&t=(E znDPt(QA0vKyK}+!(p#pXLlgPyIouW8dHYOQUn$l<#8J|&NNUKwNK|O>) z)UU$jK?XdyBb2lulCQ0IU}lPTO<~m9>V@OEMx&nOQ#%g0^p;C+!j z=5#7NIt@A#>c;d%a$$E$JsIp;onkAY(1Svmf-Lu73p;!p1MIP^RUg0x`3hIVJn%I@ zH3)@xX3y7*TTjcyyp-^dAy;~mkZ&SB;uQyUJ4_Rd`;lf3bR`I{y=5Z>R>xM5Z8`Klv-b^ZulRgV* zh&R(AGl&~fBI3D;Kk`Nhh{3utf+|>9Nt(5Wh_h{?edkS=FVAql;-j1s|K&xf88dH6 zH%4I`-ljv#bW=PpuPo5#DJ#O9a$+nam!9}-MAx+)k{}JN(J2Gx3>H4s^r7xA51t9U z`HK^>LU&5f9uXAg-<|h*!2p*{!vQ-#p_px^L)=j97$f&`BO>| zY7iIq0x}a?JK4~(1@ZF;vC_ht#QHv%=vvu^CC;+CU`4{aR!qfU4z<-#ll#_*9IGjZ zuFKkAWuMK5${QB!hooSoe!u}!wjOJsD@`wRxkcQumMJr}9wy4(BaB4`UZQ!)5EGeof-%Dd zmYl-hdSxsY(cf!{iAto!p@tEL8+owp!lm5NF-4D)7a~+b9uL zS!vVTao7eMiuwd-o!)cR5w@0hM0>bJ{hBb55YB!G=6UhL`QI5i7C#K?W@aWXV3U7F zgE7(aFRVolC!l3hOMq$J1*46gKi#RtdLk}ICm*(iAx&@ua<(oT!Ev1L@Y%iB zZfOw~9+w47so)imF5e55w+PvoOK&T?WS!(v$4YAKc2)Q^ze-IHlI_~KHyjNot_s(t zbm>0HMbtmR$q!EtIWtbz-lsIyE*A892pzZcIMZY{XqSh!(4sE|W&WpbXE8I4BAqAEwBw zIeH&YP0UQtP}|jX)!uuh%qo9(-S5&+P{R$Tv^DJ1z*&4W<23QiuUMU0(J$|mb<2O$ zJyUG#n!A8k%X3#9U$FsB*@llmo77q2tMX*2uSIyrNgXOh?QvZvuT8FrUVN^%uR(05 zH?I-4^x8OEwOFO8*b`WX^r=##l7?l6y&bd^E6Jz_k)C{l6FbPaO}BsH5r7IbY((ce zk~TX?lPKbN;Yc19!e8CaucNu5vB`ea(Far%TmIG|QeY%N=0lgr_yesYRNDuWn?ZPn zrR@t)dR;QZ;$%yFRg4pnC+96xe)02cf?#D;%qZ#fr)IVg%H(lX&aCUO6kJ|6H?E$J zta(gSwe(klzL4CT-@<3;c-RJga%LVaEhw3(JtK>4{il4+FC$XLVGd#rjuzAb3QOUt z;qoW#rSg9=2)BHF2NJrn&*27{*NUswx}&^`RJ_2Pe~>0dnl7I=S9x9P(l+JUIerPFTxENyS+T3E zhj^c?Y~AT(zke}m$LXiyqS!P2Hy$PA`0oRancgs_um}UroJ=a2(Z=C9M^ZQU5``y!G!D#uM`K!{#Dsmwh&7Y=xwc`M@;?Sn*H_jv zIXTnoBU4wjFyd!%(;OCj=6u#CQ}|?V>3p`@+~qF3zMaDSgiWPIWluT!r53LZgDgn} zxd~OjOlw$i0~}^vBQ;beuIwl&0L&E{iC*pYCriuKt2ynr4eo*@J;b$R7~}L+`4l_j zW0o8jy7R21Hx&@KT%JuiTO9>-@uxcBarHrTYupMxNCtd~h;2k{7qgEWx9M<$8<7%$ z%V4~e*u)KEUDzE%Q;u2dxT)jcG3db7uBdy~Vdc%kj zL*4jmlg0r~bbT>1{kfrFf4^*Q zq80240(>bCxdoTy)ScLYosrIZlh_4i3dItSd%S}r5ho45)!P-Y`wGZ+_u%vksi;&z z>t->>@F@kKGhUUzB2Q>{iZ+h^sA4RELxXByPz&_Os|Kx`pa5sCkXaVFJtwS-u!u!d z6>@#7f*a)5DRc_7&e_|yCZpLVs zGqYeZFB0VWt17V(5K3#ot~ZgX(H&p>Dmv#4)pT~$8{GLD8te3;O8cESNuy{41}6W2 zJ%0B=dzWX|q87u+{N?UZ}aoxk=yXATJI%)C{Oa*!2|EDl4~;+P#Tc(D{>`7htztC5C(7MfG)0 ze%EwKAMMNA98+biX|T*}46A`e-b#eiu98x{QTqGnzV5m_*|}YaAt7G2(r840LMclA zyFe4s8W3F<>~+U*a))pWG8RcNq$kV?Hg1cJGWtSqy%XCK+rnw%p=wWd9@CJbL#4*$ z8)FBspmMnDxL+qz+8i=F?5X&kgZ6mZo zrXb9LNZ){wLgqlYJ0RXn=19e4Zjxi}7Eqmgc_g(8YMb0`vL)M8VxnnuwxznB-bnLiY|H-j=X|ozDyJ_oiOq=@f@a1YgX$XDvK^B#vZ<7%%m7q(JDGtEGl{n4A8QT+D#*3ho z3;X`t5H~S)!lVt(H8fu`+6yLb?cOC3_k`8OZG)eZJ7B7?_G|h4rr~Jvjr8Y0Kww6P zc=S+o-N=nHg_cX*x$R5>N$*H9^$W;}G8lcYkg}4-@nsm!9+{KN<8iG%cigSWNP~BN za48r9{%mwBN&KAJXoB44dLX{HXDR0_Umc-grQ>}3c&K{6ysgM)j!k>kP)3Kva|;My zRGsTEahyj!o3Sl^1=??c`_mTU`i!j(r~5F^$;0iY@IeeL3g%^I?CWi^!kDqIR(_*GtXBp8V+43RyYN^O$S7 z(CGqrx_(FJOAmWsI$H3-kHgIlG2<1c6P?aRqm?8w^8qd<<=8i|AKeKZe5?x(Q|`nX zM@Zv~&fVVf-?i<7%Or7*C^Ir_D0!SdiF2Q-^uM1TD_>FJq?oq3n(3{nut6?A37a3h zZ-ko_D^CHHHxow{XKoW+R;i5dsKr(z=mSuOOG)BhZPnV7(PoRkFL5(w{`uKEe3n9i zdcQF@3uZx`K#!KnU6|L>9gpN>}l!e$6<@xsvxEgy=D;+?s6FY^=nx}-0k zhTt5kN>&*#(X}pS7D0EVm@VNh#qMz%@F6=#0R%lZNx#|V@zy1&96u3V6}$Nt)>h|i zs&hC<208qh5CX{#T%pwWJ?Z z-9R^nWA7%j;2m0L{Gj2mq-Bx7oz+u|bD!>1zSjALQ#Q?II zo4YyoS}wm+jd?7)>$cnao%S!K-QjuG={x(Oh`hdHZg9H|3$FBYALE&3_Uk;)fmHKS zZ_>qH64^RoP8Q*52&W|x4sIgc0|6YIw(T*Rz3rm(A3<*S)KT&^Fycn>RhVL^IT;2f zb@Bz4w#&^Rj_Jxmy zT{77O)R8Kx=b$O=JLWUY|sYa%971UrUq zijFuw&!NAT_#LCE!O_b1wNR~B?V}W4-OjZjJw$ z+xE*R68laz`BRSx-CUjS=%UKqGjcORL{uU|SNlQzi4!FQS3F7(wq@2MrnO`tN#!Bs zCgu4{Crj7K=?xY^O-R^DjkKB?(oNLLGlm9siu-qF7$I%(ybsKoN+G#Y?`2th+QCU5 z^AY$!%IUdt;DStL;T3dc1uB2@$!&s!1IDmINT&cVLt%W_KhCIfUlbLgXDEzpXn0LX zTm%=38PUQhutGGjLc*ve@=ah1X_)zmSN)dxIO(uL5h~*l3F}}9;~8XEVzMYrg#v{7 z7SP5ee*CsgW<&}K>EMoHV`tAK2JcYI;7vk6kRd<{P|t^?3lNW%tS`jF5Y{9bi4{8= z$;vM1;revR(a0v{=Ckw2nAo^U@Ns5DGH7htO`xu5MY@VE{vy))wE+u14{g#Vw`WP$ z`bqOQP^>wRLY+uT>w7;7I2~PAml%&y0sOv-;%-?3DqM|fc8~j-s{CdI$MKth{R}mB zjOFzpj+Mr})JCl^kkEK$(h%vMbw##DgX3+of08YaZ`!=&6K5y>%=dW!O(hB6zmO6; zF0Xz(9S32s>yDGmcXy4^Diu~UTD?YY%X&)ZpC&n*!CI!?d)j5+4hoAT#CgX4xq$nE z+1c<^&!i=#e&wpL+mmc&3*p^C_QESN^TG4nB35Ov!&%-#g=Ff_i=_<|z9NR7n;6EM zXtcok)hfymhFq{k@phBCJhVr%rviBq-1&Xm?ZE(yF_?+VSD#O>f_W4`>vs z6RxA@^bwSC-rYjAw)IxYwTSA6Gs@2A;*qS1?Ty@o4WyI6V`VzXku!UqkUeWDowmy1{2e5qhS_R067>wrrZxEw*(SrQIfxJAVoZQpgJC+n(tsXd%Uw7-&n@PCau%H8qv5>k}D>D62vBYjUU(Qjr--{noh z(75U->+OVFNndW1@SW4!>3N)lH2y|oW?ZoZ*y`;^V|pB9HBQe{LUkGqDzzH!<~+?C zBF8{8zsSsFy%!kV{oAa(Sz1l$kHzgKcvZ}_4)P)VLG~`M*fQ~4YTA7)N@`uNXr?3# zsHIFhYr2Lw>Vt_CC9w(*-J#xx5W$Lf%3C6@VZ?3*=L||K$6qZ`n5qdVQBb8CO3-pd zal<9>i<%6?Fk}K3Tg;l`vnbZ@@-?L%&#fDfJD0+jOWZ%IA(fn9#8p~Xa(qV|B{q1E z6isthKOmF0n2w<7y0R@;b;&bTLRtSHpIfX8MOmOg(c6>vM8@@k_4@26WWZ%V7A=rU z4sTI65LEnyG(B&xTLm;Mizr&153H>(iAw1F$=lA+IVAy~75Wf5ICxio)L+D5y<@$3 zwowI(p`+@UW2H5&Q^K9|g35p8-St{-_<%kcqdI`<^{hz(#SAt*YYJ=X&j>UHEN5&- zRJA_ZBeZ-c{;;kUQRP}SVdVAoYFc~ZOJyl-S;97jeSxXh?tY9yqouZT8Epbsoz=l} z5l<>dR@+Ym8~B{n>lwp`zR#c8oVDF@S!Q1h|__`avw5^|f+37v4SKnca z4+ND6Kq%@9fxabPHoTl`7)@ULTQ6pe(arZrRd2;13j_)Q-+(^8CnN!VSXXhk5!qlh z4qwPxm#$-vN@gNzsi-z@KVMV8$dP3kIi-)s(-7uHm&!fmU4^y*2s%0alnM?+lNI=gKdOsJDcf}wmerN6cY#*+@74t|!j2ZIQ?M(~s zP-nnC9t7jqJ%jSUNOl4CkqM-wRD&Gs*xx%D!`l!K4r`Xb z->7U_y%;m~ZS-yI;`9u&Hxn_&?!wV>>T!I|4-n)0*5>!}5vtaYv~=q7I$ZT6xa@vB zt~|A?>ZYigr=Y%LE%5dAInKVv0t1-AJWx>3<8gWqZs+OfuodR{nJeb*iWejy$UWJr zUYsuZyo_1txH!vyAR>$Dv-<2QVlF*&X*s_q3z0;KL1F8pw=Vb?IGZV!D74LSK}K*_ zyKxdsp`>N8_IsnkTLrzH)E&?_r%Sd?oC<4dMeuw5nlA$^?$I&Ws-ez$9}KY~(aKQx zIH(9QQi&PnjM1%6US<1?l=41;t@zZ>FmaofN7#DOfOt6tcKmuQUVb>2u5?NXK59PJ zcnFtxG3xS1kuM29b8f@64IAx`N-!6S6m`fJi^M1hu`?B7a zvcs@wNc6-VrGHMYKx|WB3)_})p);Gdsm4-`$W+yn^hpvuwXOybO@4cRpvdRBT?W`N z*XmLqzflutIF4gYgESV-|1pa1^fsv=+-NYTb~69S>usO1Ix6M&vvQEJ!`hD6th2u< z9)2tl3w**1N-~5Lwa@U>QU~^P6WE~CsnVv`DdjQs{JpScfX$ACs|X2ABPC(Sc66yj zsOmVGUu3kcr%!^P#qHKtIy;@b0NGcpP6X4poF2ojiSkSu^ zDXcm^z?~Am3@aBaT^~#m@;HD%5VCEyFYx^6nX_^*G*f!3BGF%5fxP53VK`V1sMo0< ztS4b)q#h1rWL*K{{?NhAHdsk-4BI3!Dl$xgRBG>cE81ntV7#j2Mx^;tHpy> z@s#$iDm$I+(rd8LKv968i`u?v4#Sz{r&^iFESVyW(np!f1txJ#WF zV!lKLX6@0D+Iyf@B6Y^iRHsV5#;w?-ljHEK2UD*E7UPyX880D>B%3c?e!#$vVV8~H z8uCPIP)Y>KiH|H$K{*m$yj8?aG{hYIMy*)CEPkyDGzcsD8`%fNY|M7~y!q9b;Eomh zF;`t9K(nsWKdT)Sb7N4~?8bt_)f#!`{&AsUAJd#`uP`ci*Qsi+!HG}QAFn+vv|&IR zO|n_P;?vt#(r3tujp5v<7~ejszuMhXeB}yL=77DFSTT(A3V&?E7X8O?bwabz_B*I^ z57l?%@n%TW9>3Jie`?s}!o)3V_jY^!S>ZCs#%nXTb`BMr;3BiF_&KW8)9O$DbM z=>vcR&_>DLx^$<@kV!?G+J}xAPc)`V15IO+Y!gvzSEq}NdG_wy&ehXRM)WZ9@jb-l z&V!6p7+XENa>9P_C=TL@n#>)B)Ud)=u@?{ZPH@F~#9}JHs0wW`F^6nH>7K2}8E4`! zL)5nRX8S;%`DJ%Sb?=s3K-W5*YZ(~C*geGk2Rrde_6g>avmJlcY|N}e68aiCQJ?E$ zz+FY9{j!Mv90K=db+Kb%UQDo?Y&Q-a+>bH>DH%MbEaUwOxL0e%EhXG^EWVupTaU zJ><9}ut9WJyWni2Vcy8riO6KuYRR{z$K(n&l-CGvEx~c2aUq;kTWEZ^C-6Zp z`;i}mQnZ>ElY6_BbR4ab35g3`JUCF_OsLRd{rM~4!dSF@M6kof91*?86ZRr5*d+6m z6TFZBM(j?RHD}TDsjsyp-in5_?w3IDgc#85>44T$)6b=Cx1;LG^5m8(9r1$S~m=z)`5+?NL(4{|0te93P znnu;TaN-OyyXaRXFLvp?@}_EMoMb>^v|do9FsM}1O!=MY^DB`|e);Wc41q?@ zuMh;2>d8ije%NqiCZMNFdAoq%yPlZq;el0SoBqtFJ{8%QGL?T#t1&4r)B7vHPIjfb zx7+ceVn1?kZKqsxG^Wo39t^jvJb$wV)cQ&nhppo&sNRUaGgSkdZ{D-%ihUp9Gr!{S zh<>P)YY1)t`_J<6_X$*GDU@t;p_3e55_Rtx;3BxH(-fybI9Op{*DU(G*Es zK4!V7!`LP1Bl{U-4g`yAd>SHw@I(IRYP@aGr{cGPGfp2gC5bklCa6jE+z9YkBk;5k zB-Gg`gdp;a7-N|q_#R{5`OUN09}Dl`s9W1_{@^de-=G#C5SO-JI^bz{nbPTg0&(+{C9rPiiIOHeI|zGZ%CB4&dbFXZ<;lmc9z z%%mooEZ|4Sh-qi{LGK34;ZNsa4UFJV6Lel};W6SBInoD3 zk5F}I_OX@eKl#F}611jf_qmp+Rgdjau2Ja&QW<9j^-QN>{&iWLsfH=nUc7pMJ5$)U z(r1r!FKVL#f2t^>HER!doYg`f22$G;1iLEKqxkXk&&KdCJ628M=W0*#V8Bnu1TOsF zGxTHoz1|w`FyX8Y3`dSzJ@$luuRMvuKz^Q%IdNZW{Yd{)to@Hwe%QVzm)qj5BgfQs zw#_Ke8=I5Kzs^G1bN0o!*dB&CaYu}HrtFzhSRP3J4bJ3*0*kKR^_wJY(e-=gmS;j( znm7yu0kF=vdG1bodSs6oSDd#GKh~;cJGsvzrP1KSiS>oUnZ3|EYL8Hyrp<|aR&1@u z*HN<2fD3O$ZL&(47c1TY2pe=S>cpaBeh2wyYPL!vw5t4W1s}$mQi6qF`dLr@ZxW-K z#|BU~qn@_#r4#oe%{- zUd_?pUqB!0dK%~AH2#i^$$CSMLFT{uqOg_`kDdgj0shs$I{dEMuW2s8>I;KV+( z(r~m8RRn8I%CPH%%-s+HVGBf*1VSKlzVp3X_kPRQ+PTrvaHy6yi)yy&;Z?SQ@RpyC0&6^MJbdiGTKpVMEi7wYrn0xu{V-}Z zdKf%~emXyABj0GnQ4sVOdKlVseh#Gbrcjp_cl;~Z+89?bH3WaP=KU0R&o1`n`uuov zek%4vb_}tbW1e+*+MWmY1_A>5kH7j8_cAU&R%*_7K~=;dR^h4v@VUHg-d}U(_P)Dk zk>AaiLC4K0XxqX#ff!FV(6;KU1lqmGBPS15yLtf<$F88&q5Q zImK?oEM4(2nLc#J(|1|%2Y*A|a7VcB*~rY6^XtlS;k06Tq<`^l7+Ht?sPNRNSdZ5P z@j>{Mc9AQ+4n&^#3rEZ=0t zBbwv4+{q!ien)>tE6p0`2%jcAqc)#cls(GQ&mb|X=5&mzX)W4&ic0ww$whH?!feCy zia>M6zisn_Q2F|^bo1jJc5)p#@BA;CvFE7K%zTlOA>=)ik0t zAAktiAg~2$fCU;mj9K0V@R2Oe{Kb$7X4R;#f=*kb><1L=qja3Hr4iDo+JIJ&vf*sR&G9>aOiA=S7^1%-cV z*xX@6*K!Bgi$i&*2bC2DMfsTj8 z;`sYmkDP34F;2v6*)ZnbRdn#zls9fp91$|k9d;#V8JnKA`t=p4h>=}%H@3Ka3*#^m z&-0Lc6ySko**Nrt=ORdG4vTvigb0a!z$chRgh;rXLq}DL=g>L&bOH@^#t(e@MbZQY z9Ofz*t)UqraL$iC>$Gt4Lt<0k;AvMn`CG$S+fmJ#b+et_fQR0pR|Gn-eHR24=##O} z+n?T%Uyz(_24WQOdl>!fW>etX#uf$BD#I(h6IGiz!z;NH`3-a3ZsEk5zgH4tgeLQ8 zi)8CK+8B>L@Hfz^edQvW;x~YnT@kbaGpc85<_g!R znr5IYI;lD^W$siuL1t}$EtO~#Ei8x;Ur5P#ziUfX56><0>NVKPUd%*cM7uD1%mHyk zcS0>#C-vx5!9D*jt1avDI-Y5ONy=%_bjm&RmUa5O^+@%)h4?>khbsI&z(ViOK^Uqa z8D>88&Q#QSQP-J;E&kZ7kgcF+2&|D_a;U4tFxVlh3z~_%6AUIjdXWiPR?f@=LlH^! zgrg zk9sYdT7f=T2XTb(wr<4!J7n(PCooxW&#d3UJjVhGzv2r1jtN|bU4zRixCXxu6#Ne% zmAVNNAmEGjI0thF4+*L%BcvIq9x?}G0Q1icywEFVAUNKE)E|O($U@iyz(2=b)AJBm z!rF|{h3R}J`+?@aqvE64k~){Uy0C0Uvc^qqnR_+XXl+_7mn+O2LOLSFidm?G%R1E9 zt2PS`C!8y4q(>z?{>08X?JjTdosjIzh&b)B*UmjU6a}VKRJC?0Os=nGl5(w_i z5zz`t1tu5i&f-~mcit6>T`Qb!<`B3zhV6KMRLAWcz^8QS+$%zry1rjfSESVw09lWA zMJKgE(f>;xU>m+UWpnDDA?+wj2;*IAuvRzRH{Z*&WU@ypJSX6a>54T13jr4fH<0eG zz(b3V#E%|ASFeVP-Wtw5q*Fj?m6%B$Lw!%c-kQknU*1WGA0#k`9`45QFMJAuh;M3v zTfGD0z#vtX=-MzH*a2&pkaBLtF2ELMh?`J>D^rz_uFwJ!+Wci#V29yf6XHG2G67vw z84|H*AW|Ul>mei`E+;aPQ9=-sV_kb}*NoU3SMxBPIylN`G6c!n3hZl z+7aR3fg7i;0xWpmmCUGsI54D{AR){rY_wMw+GHlcn1ZDpC36cZW-b{vKcb`rA+dAO zCi(gLaf-IA(z~-AyNVKTV%wY@G!R|Ln_w^zk)6?+y|6x8B2Lgd*kBOxhD*^7YtT46 zZuNLf?sW$)JwzM2J*@&fFk9|6!&>+Aj;0{j+3^f41D;}ZTKSPrHQ*{v;}btht^6eXd^`MnIQ)DQX%Pfz zaq$&_5*CbT8HeqQ(fa+R?tHxNyuR+drS80|&O&jGg-~0kT^CD0n_6PWRDfq_ZE{C?plgT7espK>HoC-J-Dle;raCkDTFQXy{z+qLLk=?nAN%GYq5~3oKA)4ewTAO0@S!7ZVQ;f^BGeA zd}lK>|K;V?^u;_{w9Ev%)G>#~SBy6N&H} z5{*!;r{oKvTII80TDSNsk(se+zch@v@Ie@uC)-0G1%L%)I}qKYD;Gm{foG=2TogoB zPa!yf2mioPoxdImkd{J38oyiJ2e<<9X`+Cqq+KqDVz2+HI_D@(T)6x5DQqnOQYFe8 zxu5fKBx(NgFapcR4e%>5ZQ=m@R3rBBy#>v{9PzuD_8q3|h5e$8|uOv@YW zboyO1`7yTq*NwZakPU-A@rYxliXjAwc!q@&t0)i+2f25@#ZB>Eg zg^E(DxIQHdyq^qj)h7f70W6X0n>S(vpfU|D691yB~(YFQeKY0W~_ z+GR^oKrNYOk?4?|A63d9`js`&2;hWL###G_I=wd2-OvJhkU>E!+PEz7lbm%X)tvOS zd6_&$)ueRxlD}MT@1oM9pv7Qu>nHghQCeF9JYu$E z`c3YV;PQ*utM6xd2fJ|$bUn|e1R;2}cSmnBqzGc8H}icfe@AR!C-e2^m=^djuEkvB3*H7D8;j?iDEH5QR>6saync3|IE z62C5Mh- z8r7Qy_{Im!=zBMGQK#bB5JlGG8x|p&BmWTizH5EcQ}5qp;H=W+|4a+jF6PB<@S0109FTdX zmVMcrXIxCV7S~pLdH1hmCM>>p+==M1YV{V?S2xvTZ>$C;&{N^(55t)2ku=pJZL9*j z{JON-!2-DJt|szoE(!Ss4DWF;o*A@0z6)&wb0y&j-4ZknFGn8=+ci{$4Pmd(#8oNpE-tHW}A?6*3ToWpE( z^e5A3n7K1K|H`vp{yj(6By4ra0^U!hn=AN~DiqxQn7r6$;$}O8zy5eykzmJl<-j6F zCOl(w`s0=5gF?DAr=CFov%E>dHy$ z*Coxk`o1&hLEn3RvTCJE(zP=+gVb`bjXszN4oJDLEE%kb-#H7GcK;u$<;g`>*gvrJ z0pHRO2S}eF^S_&-{yS9Ce>gz@lhwt+%+B;A|pdVq|A*^8dvh=_F472h-*M zUzo1oUKJq;0024c%iJ140A-OV1`*72?=Qxvb&@OP`@zu4>4w2_HV!ZCer1j^49q1& zHSN^mwv@hF8yweOV7VaO=D6SSS_YwW`(G}42DXm8NMgv{t?jP0l)tf-9RtFMJ?dbt zbL=f2WtwibHzVHtke&vL3aK)JHy1oEC+#>yj~X9h?Ux1`FLeK-x$6tDq6*_kWH>zp z5lOWVgQ&YA-I;UdzlgSb|CnUE+g+{fvc&b?*}GS_JL}Bc)X1zrNFR(SkRS}IhaRGr zAjk(HA=HOndI$+35Y2k1#YACK)_&iaoptWHlH2COE{u2Po9~?OcfRv|=X_@#eqNgT z_QvivR~-8FvsJzKfA#z7^Mk*reV=?@FOD8xHhyg?_vi=VAD0@9pGW>YQF`v-c^8;ZXCWE zojLN*<8SQyVcYVrZcaVHmw!EUr_i)+&)QrJ{tJuO-9qt>99{-jv=^H7cs!~&jwATG zQm!rfCH^hJ*#%Cyl$(pVyHXvJxp)W8IfeMSgxqD~m);Q<81d1~@pzFAF5(5Fi}=E* zJ?<5{@81sM^c;m&~355Kk5>>s{%Ax)rP~MHsZ$>ZMaa_8(NwQHppyfxjg8yo>=9jk&Xj|1dY$K_)JVpQrkt$fvGQ-R)4EGAJ z@RQ1gZ&fblYTFu88(VALIwyIY1^uE_u0r7Z)bBbX{T%3oAHFdu~aOTbloy^ z-NEu;7iKSpXKaCvw@E?BMsY>>s;s@Z^Ccu(&O!2eIgE8Q%^!iRoPjD5YS!eOb0bBQ zGfcUiVi?<8j!{%T)2v4oHA$+(uSuRISV$uo3lRaPOM}SBtXYw&a|`pfW^0c&5QKF< z6d8>Hl>J&X=5(6ES6}j^*T> zdBjv0_$|a#8c!{Xt*)5LECv73Q;P_SR-uK()Z&2>yWNrlB^M*}jVUJwPf;ADF-2?E zJYs61S>2t9DY0J3F(vk^g?MUFLCL4RCE=-suIP*DDO;{*@>Q@>2Mz4e4OFyAiK}Qs zlYT4N%__;>Dt7f&dtyQr|LE9QpKtp6{BSt*$9yVX{RrD&p&3n1R1Xw_nkXGa`AWN- zHq?$Mnk}lCMcJdE3TZX^{MM5@M%HDwK|34s+3I5eRR>*;uJit+0#6N_8vOauB$y{Hu|JK92>u=dPJo|a%wByVNfIO zUAs{k%oa!{6-HDPkqH-9S}dh9YD!%YynF(UbOkih&FB#<+k%OF=vU()OlvTVUZ|rh zVMZW>8V)t}cFj-qO!5eMg1W^h=-H-`(3Qf#JMxx6UV;uRCci8-i(00DFAIHz4P0la zez?m}0CIc1&U$sdmkOA)q}q=tP{8=i74$Our`nc_GWQ=(_7|z8zwUx-|f$Dbp zyDc*~)wEd|+Hwaeo|Vzm8>pQ-Wu8SVc~*wDrrBk~1F?Ijp3WR@AY-MrHf2OsMtXD> z?T~S1(m^RJWALpfVTO#G5@yH@Pr?G3MJHdWKO1LpS>_tZCrQt9B|kHG)IinLsfYQL z#sNLE%LklUZr3=NBl{kH_8iFvI{GsuyW|1AOV8xIThF8;QWi7Xy1L{89bYp{X}r2& zar*4g(H1gCwqaPN-QCvh!eZFEA?sN>3`;g3U9%X%p9%GK@NxovtMJma9 zE|+};J)1U#tX;@FI<9ugJiRNonCI#eKE$cp)s{0C-@H2e;|SA?EDmv&^F#VVHPU>| z$5N-2gCJJLhKkQhTU%oR81GTr(nCS5s2k0^S>OfJHH*AIk9jNF`8*N_3$108c&W5W zxw{ITV6mI#$NkzK{P$U^=GjHpbBiU Date: Wed, 29 Jan 2014 10:18:01 +0100 Subject: [PATCH 60/76] Broekn branch not broekn more --- Code/DanBias.sln | 18 - Code/Dokumentation/GameServer.uxf | 407 ++++++------------ Code/Game/DanBiasGame/DanBiasGame.vcxproj | 2 + Code/Game/DanBiasGame/DanBiasGame_Impl.cpp | 19 +- .../Game/DanBiasGame/GameClientRecieverFunc.h | 5 +- .../GameClientState/C_obj/C_Player.cpp | 3 +- .../DanBiasGame/GameClientState/Camera.cpp | 192 +++++++++ .../Game/DanBiasGame/GameClientState/Camera.h | 63 +++ .../DanBiasGame/GameClientState/GameState.cpp | 220 ++++++---- .../DanBiasGame/GameClientState/GameState.h | 4 + .../DanBiasServer/LobbySessions/GameLobby.cpp | 21 - .../DanBiasServerLauncher.vcxproj | 29 +- .../DanBiasServerLauncher/ServerLauncher.cpp | 16 +- Code/Game/GameProtocols/PlayerProtocols.h | 51 ++- .../GameProtocols/ProtocolIdentificationID.h | 15 +- Code/Game/GameServer/GameClient.h | 14 +- Code/Game/GameServer/GameLobby.h | 30 +- Code/Game/GameServer/GameServer.h | 33 -- Code/Game/GameServer/GameServer.vcxproj | 23 +- Code/Game/GameServer/GameServerAPI.h | 10 +- Code/Game/GameServer/GameSession.h | 47 +- Code/Game/GameServer/GameSessionManager.h | 82 ---- .../GameServer/Implementation/GameClient.cpp | 14 +- .../GameServer/Implementation/GameLobby.cpp | 43 +- .../GameLobby_ProtocolParser.cpp | 100 +++++ .../GameServer/Implementation/GameServer.cpp | 194 ++++----- .../Implementation/GameSessionManager.cpp | 121 ------ ...on_Events.cpp => GameSession_Gameplay.cpp} | 72 ++-- .../Implementation/GameSession_General.cpp | 111 +---- .../Implementation/GameSession_Logic.cpp | 74 ---- .../Implementation/GameSession_Network.cpp | 29 -- .../LobbyGeneralProtocolParser.cpp | 52 --- .../Implementation/LobbyProtocolParser.cpp | 58 --- Code/Game/aDanBiasGameLauncher/Launcher.cpp | 11 +- .../aDanBiasGameLauncher.vcxproj | 19 +- Code/Misc/Thread/OysterThread_Impl.cpp | 20 +- Code/Misc/WinTimer.h | 1 + Code/Network/NetworkAPI/CustomNetProtocol.cpp | 63 +-- Code/Network/NetworkAPI/NetworkAPI.vcxproj | 12 +- Code/Network/NetworkAPI/NetworkClient.cpp | 123 +++--- Code/Network/NetworkAPI/NetworkClient.h | 18 +- Code/Network/NetworkAPI/NetworkServer.cpp | 106 ++++- Code/Network/NetworkAPI/NetworkServer.h | 8 +- Code/Network/NetworkAPI/NetworkSession.cpp | 89 +++- Code/Network/NetworkAPI/NetworkSession.h | 37 +- Code/Network/NetworkAPI/Translator.cpp | 1 + .../NetworkDependencies/Connection.cpp | 2 + Code/Network/NetworkDependencies/IListener.h | 27 -- Code/Network/NetworkDependencies/Listener.cpp | 2 +- Code/Network/NetworkDependencies/Listener.h | 61 ++- .../NetworkDependencies.vcxproj | 1 - .../NetworkDependencies.vcxproj.filters | 1 - Code/OysterGraphics/FileLoader/DanLoader.cpp | 17 +- 53 files changed, 1305 insertions(+), 1486 deletions(-) create mode 100644 Code/Game/DanBiasGame/GameClientState/Camera.cpp create mode 100644 Code/Game/DanBiasGame/GameClientState/Camera.h delete mode 100644 Code/Game/DanBiasServer/LobbySessions/GameLobby.cpp delete mode 100644 Code/Game/GameServer/GameServer.h delete mode 100644 Code/Game/GameServer/GameSessionManager.h create mode 100644 Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp delete mode 100644 Code/Game/GameServer/Implementation/GameSessionManager.cpp rename Code/Game/GameServer/Implementation/{GameSession_Events.cpp => GameSession_Gameplay.cpp} (57%) delete mode 100644 Code/Game/GameServer/Implementation/GameSession_Logic.cpp delete mode 100644 Code/Game/GameServer/Implementation/GameSession_Network.cpp delete mode 100644 Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp delete mode 100644 Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp delete mode 100644 Code/Network/NetworkDependencies/IListener.h diff --git a/Code/DanBias.sln b/Code/DanBias.sln index 540348f9..5e8f837e 100644 --- a/Code/DanBias.sln +++ b/Code/DanBias.sln @@ -32,9 +32,6 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameLogic", "Game\GameLogic\GameLogic.vcxproj", "{B1195BB9-B3A5-47F0-906C-8DEA384D1520}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasLauncher", "Game\DanBiasLauncher\DanBiasLauncher.vcxproj", "{8690FDDF-C5B7-4C42-A337-BD5243F29B85}" - ProjectSection(ProjectDependencies) = postProject - {52380DAA-0F4A-4D97-8E57-98DF39319CAF} = {52380DAA-0F4A-4D97-8E57-98DF39319CAF} - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetworkAPI", "Network\NetworkAPI\NetworkAPI.vcxproj", "{460D625F-2AC9-4559-B809-0BA89CEAEDF4}" EndProject @@ -44,8 +41,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DanBiasServerLauncher", "Ga EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aDanBiasGameLauncher", "Game\aDanBiasGameLauncher\aDanBiasGameLauncher.vcxproj", "{666FEA52-975F-41CD-B224-B19AF3C0ABBA}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerDependencies", "Game\ServerDependencies\ServerDependencies.vcxproj", "{52380DAA-0F4A-4D97-8E57-98DF39319CAF}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GameServer", "Game\GameServer\GameServer.vcxproj", "{143BD516-20A1-4890-A3E4-F8BFD02220E7}" EndProject Global @@ -274,18 +269,6 @@ Global {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|Win32.Build.0 = Release|Win32 {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.ActiveCfg = Release|x64 {666FEA52-975F-41CD-B224-B19AF3C0ABBA}.Release|x64.Build.0 = Release|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Mixed Platforms.Build.0 = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Win32.ActiveCfg = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|Win32.Build.0 = Debug|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|x64.ActiveCfg = Debug|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Debug|x64.Build.0 = Debug|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Mixed Platforms.ActiveCfg = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Mixed Platforms.Build.0 = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Win32.ActiveCfg = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|Win32.Build.0 = Release|Win32 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|x64.ActiveCfg = Release|x64 - {52380DAA-0F4A-4D97-8E57-98DF39319CAF}.Release|x64.Build.0 = Release|x64 {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {143BD516-20A1-4890-A3E4-F8BFD02220E7}.Debug|Win32.ActiveCfg = Debug|Win32 @@ -313,7 +296,6 @@ Global {DA2AA800-ED64-4649-8B3B-E7F1E3968B78} = {20720CA7-795C-45AD-A302-9383A6DD503A} {060B1890-CBF3-4808-BA99-A4776222093B} = {20720CA7-795C-45AD-A302-9383A6DD503A} {666FEA52-975F-41CD-B224-B19AF3C0ABBA} = {20720CA7-795C-45AD-A302-9383A6DD503A} - {52380DAA-0F4A-4D97-8E57-98DF39319CAF} = {20720CA7-795C-45AD-A302-9383A6DD503A} {143BD516-20A1-4890-A3E4-F8BFD02220E7} = {20720CA7-795C-45AD-A302-9383A6DD503A} EndGlobalSection EndGlobal diff --git a/Code/Dokumentation/GameServer.uxf b/Code/Dokumentation/GameServer.uxf index 039b4dda..735a2592 100644 --- a/Code/Dokumentation/GameServer.uxf +++ b/Code/Dokumentation/GameServer.uxf @@ -1,11 +1,11 @@ - + 10 UMLClass - 380 - 360 + 610 + 340 100 30 @@ -16,8 +16,8 @@ UMLClass - 360 - 540 + 580 + 480 160 80 @@ -28,77 +28,33 @@ /players./ - - UMLClass - - 560 - 360 - 100 - 30 - - LobbyClient - - com.umlet.element.Relation - 400 - 460 - 50 - 100 + 620 + 340 + 60 + 160 lt=->>>> - 30;80;30;30 + 30;140;40;30 com.umlet.element.Relation - 450 - 340 - 130 + 710 + 480 + 100 50 - lt=>>>- - 110;30;30;30 - - - com.umlet.element.Relation - - 460 - 270 - 170 - 110 - - lt=>>>- - 150;90;150;30;30;30 - - - com.umlet.element.Relation - - 490 - 450 - 140 - 140 - - lt=>>>- - 120;30;120;120;30;120 - - - com.umlet.element.Relation - - 460 - 180 - 400 - 150 - - lt=<<. - 380;130;170;130;170;30;30;30 + lt=>>>>- + 80;30;30;30 UMLClass - 370 - 200 + 600 + 180 120 120 @@ -108,42 +64,19 @@ com.umlet.element.Relation - 400 - 290 + 630 + 270 50 90 lt=>>>>- 30;70;30;30 - - com.umlet.element.Relation - - 600 - 300 - 260 - 80 - - lt=<<. - 240;30;30;30;30;60 - - - com.umlet.element.Package - - 840 - 290 - 120 - 50 - - NetworkAPI -bg=#a21aff - - UMLClass - 360 - 120 + 590 + 100 130 40 @@ -154,8 +87,8 @@ DanBiasServerAPI com.umlet.element.Relation - 400 - 130 + 630 + 110 50 90 @@ -166,10 +99,10 @@ DanBiasServerAPI UMLClass - 150 - 360 + 1060 + 330 120 - 30 + 50 NetworkSession @@ -177,41 +110,41 @@ DanBiasServerAPI com.umlet.element.Relation - 180 - 270 - 210 - 110 + 690 + 220 + 390 + 50 - lt=->>>>> - 190;30;30;30;30;90 + lt=-<<<< + 30;30;370;30 com.umlet.element.Relation - 240 - 340 - 160 + 680 + 320 + 400 50 lt=->>>>> - 140;30;30;30 + 30;30;380;30 com.umlet.element.Relation - 180 - 360 - 200 - 210 + 690 + 340 + 390 + 160 lt=->>>>> - 180;190;30;190;30;30 + 30;140;40;40;370;30 com.umlet.element.Package - 840 - 410 + 460 + 640 120 50 @@ -222,164 +155,19 @@ bg=blue com.umlet.element.Relation - 600 - 360 - 260 - 90 + 370 + 530 + 350 + 290 lt=<<. - 240;70;30;70;30;30 - - - com.umlet.element.Package - - 840 - 350 - 120 - 40 - - PhysicsAPI -bg=blue --- - - - - - com.umlet.element.Relation - - 630 - 340 - 230 - 50 - - lt=.<< - 210;30;30;30 - - - com.umlet.element.Relation - - 490 - 420 - 370 - 190 - - lt=<<. - 350;30;190;30;190;170;30;170 + 90;150;30;270;330;240;240;30 UMLClass - 170 - 160 - 130 - 90 - - ServerInitReader --- -Helper to load ini files to server - -elementstyle=wordwrap - - - - - com.umlet.element.Relation - - 270 - 180 - 120 - 50 - - lt=>>. - - 100;30;30;30 - - - UMLClass - - 360 - 440 - 160 - 50 - - GameSessionManager --- -/Creates game sessions/ - - - - com.umlet.element.Relation - - 400 - 360 - 50 - 100 - - lt=>>- - 30;30;30;80 - - - com.umlet.element.Package - - 840 - 470 - 120 - 40 - - ProtocolManager -bg=#aaaaa - - - - com.umlet.element.Relation - - 160 - 360 - 770 - 300 - - lt=<<. - 750;150;750;280;30;280;30;30 - - - com.umlet.element.Relation - - 930 - 300 - 70 - 210 - - lt=<<. - 30;190;50;190;50;30;30;30 - - - UMLClass - - 10 - 350 - 120 - 40 - - /<<interface>>/ -INetworkSession - - - - com.umlet.element.Relation - - 100 - 340 - 70 - 50 - - lt=->>>>> - 50;30;30;30 - - - UMLClass - - 560 - 450 + 790 + 500 100 30 @@ -389,28 +177,91 @@ INetworkSession com.umlet.element.Relation - 580 - 360 - 50 - 110 + 670 + 530 + 150 + 180 - lt=>>>- - 30;30;30;90 + lt=<<. + 130;160;30;30 + + + com.umlet.element.Package + + 800 + 670 + 120 + 40 + + GameProtocols +bg=#aaaaa + + + + com.umlet.element.Package + + 1040 + 200 + 160 + 190 + + NetworkAPI +bg=#a21aff + UMLClass - 0 - 460 - 160 - 50 + 1060 + 280 + 120 + 40 - MapManager --- -Manages all map stuff. - -elementstyle=wordwrap - + NetworkClient + + UMLClass + + 1060 + 230 + 120 + 40 + + NetworkServer + + + + com.umlet.element.Relation + + 690 + 240 + 390 + 80 + + lt=-> + 30;30;200;30;200;60;370;60 + + + com.umlet.element.Relation + + 690 + 260 + 390 + 100 + + lt=-<<<< + 30;30;180;30;180;80;370;80 + + + com.umlet.element.Relation + + 860 + 270 + 380 + 260 + + lt=>>>- + 320;30;360;30;360;230;30;240 + diff --git a/Code/Game/DanBiasGame/DanBiasGame.vcxproj b/Code/Game/DanBiasGame/DanBiasGame.vcxproj index 0e856428..25696efd 100644 --- a/Code/Game/DanBiasGame/DanBiasGame.vcxproj +++ b/Code/Game/DanBiasGame/DanBiasGame.vcxproj @@ -192,6 +192,7 @@ + @@ -205,6 +206,7 @@ + diff --git a/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp b/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp index 930d4bad..07d2d0ec 100644 --- a/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp +++ b/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp @@ -61,22 +61,20 @@ namespace DanBias return DanBiasClientReturn_Error; m_data->recieverObj = new GameRecieverObject; - - m_data->recieverObj->nwClient = new Oyster::Network::NetworkClient(m_data->recieverObj, Oyster::Network::NetworkProtocolCallbackType_Object); - m_data->recieverObj->nwClient->Connect(desc.port, desc.IP); + m_data->recieverObj->Connect(desc.port, desc.IP); - if (!m_data->recieverObj->nwClient->IsConnected()) + if (!m_data->recieverObj->IsConnected()) { // failed to connect return DanBiasClientReturn_Error; } // Start in lobby state m_data->recieverObj->gameClientState = new Client::LobbyState(); - if(!m_data->recieverObj->gameClientState->Init(m_data->recieverObj->nwClient)) + if(!m_data->recieverObj->gameClientState->Init(m_data->recieverObj)) return DanBiasClientReturn_Error; - m_data->timer = new Utility::WinTimer(); //why dynamic memory? - m_data->timer->reset(); + m_data->timer = new Utility::WinTimer(); //why dynamic memory? + m_data->timer->reset(); return DanBiasClientReturn_Sucess; } @@ -157,7 +155,7 @@ namespace DanBias return E_FAIL; break; } - m_data->recieverObj->gameClientState->Init(m_data->recieverObj->nwClient); // send game client + m_data->recieverObj->gameClientState->Init(m_data->recieverObj); // send game client } return S_OK; @@ -184,10 +182,9 @@ namespace DanBias { m_data->recieverObj->gameClientState->Release(); delete m_data->recieverObj->gameClientState; - m_data->recieverObj->nwClient->Disconnect(); - delete m_data->recieverObj->nwClient; - delete m_data->timer; + m_data->recieverObj->Disconnect(); delete m_data->recieverObj; + delete m_data->timer; delete m_data->inputObj; delete m_data; diff --git a/Code/Game/DanBiasGame/GameClientRecieverFunc.h b/Code/Game/DanBiasGame/GameClientRecieverFunc.h index 5e129fca..360d1c60 100644 --- a/Code/Game/DanBiasGame/GameClientRecieverFunc.h +++ b/Code/Game/DanBiasGame/GameClientRecieverFunc.h @@ -1,11 +1,12 @@ #ifndef DANBIAS_CLIENTRECIEVEROBJECT_H #define DANBIAS_CLIENTRECIEVEROBJECT_H +//WTF!? No headers included??? + namespace DanBias { -struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject +struct GameRecieverObject :public Oyster::Network::NetworkClient { - Oyster::Network::NetworkClient* nwClient; Client::GameClientState* gameClientState; // receiver function for server messages diff --git a/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp b/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp index 4cd6fbd3..49c450b5 100644 --- a/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp +++ b/Code/Game/DanBiasGame/GameClientState/C_obj/C_Player.cpp @@ -8,6 +8,7 @@ struct C_Player::myData Oyster::Math3D::Float4x4 view; Oyster::Math3D::Float4x4 proj; Oyster::Graphics::Model::Model *model; + Oyster::Math3D::Float4 lookDir; int ID; }privData; @@ -29,7 +30,7 @@ void C_Player::Init(ModelInitData modelInit) privData->model->WorldMatrix = modelInit.world; privData->model->Visible = modelInit.visible; privData->ID = modelInit.id; - + privData->lookDir = Oyster::Math3D::Float4 (0,0,1,0); } void C_Player::setPos(Oyster::Math::Float4x4 world) { diff --git a/Code/Game/DanBiasGame/GameClientState/Camera.cpp b/Code/Game/DanBiasGame/GameClientState/Camera.cpp new file mode 100644 index 00000000..13b5a70f --- /dev/null +++ b/Code/Game/DanBiasGame/GameClientState/Camera.cpp @@ -0,0 +1,192 @@ +#include "Camera.h" + +Camera::Camera() +{ + this->m_position = Oyster::Math::Float3(0, 50, 0); + this->mRight = Oyster::Math::Float3(1, 0, 0); + this->mUp = Oyster::Math::Float3(0, 1, 0); + this->mLook = Oyster::Math::Float3(0, 0, 1); +} + +Camera::~Camera() +{ +} + +void Camera::SetPosition(const Oyster::Math::Float3& v) +{ + this->m_position = v; +} + +Oyster::Math::Float3 Camera::GetPosition()const +{ + return this->m_position; +} + +Oyster::Math::Float3 Camera::GetRight()const +{ + return this->mRight; +} + +Oyster::Math::Float3 Camera::GetUp()const +{ + return this->mUp; +} + +Oyster::Math::Float3 Camera::GetLook()const +{ + return this->mLook; +} + +float Camera::GetNearZ()const +{ + return this->mNearZ; +} + +float Camera::GetFarZ()const +{ + return this->mFarZ; +} + +float Camera::GetAspect()const +{ + return this->mAspect; +} + +Oyster::Math::Float3 Camera::CrossMatrix(const Oyster::Math::Float3& vector, const Oyster::Math::Float4x4& matrix) +{ + Oyster::Math::Float3 vec; + vec.x = matrix.m11*vector.x + matrix.m12*vector.y + matrix.m13*vector.z; + vec.y = matrix.m21*vector.x + matrix.m22*vector.y + matrix.m23*vector.z; + vec.z = matrix.m31*vector.x + matrix.m32*vector.y + matrix.m33*vector.z; + return vec; +} + +void Camera::SetLens(float fovY, float aspect, float zn, float zf) +{ + this->mFovY = fovY; + this->mAspect = aspect; + this->mNearZ = zn; + this->mFarZ = zf; + + /*float yScale = tan((Oyster::Math::pi*0.5f) - (mFovY*0.5f)); + float xScale = yScale/this->mAspect; + + mProj = Oyster::Math::Float4x4(xScale, 0, 0, 0, + 0, yScale, 0, 0, + 0, 0, zf/(zf-zn), 1, + 0, 0, -zn*zf/(zf-zn), 0); + mProj.Transpose();*/ + mProj = Oyster::Math3D::ProjectionMatrix_Perspective(fovY,aspect,zn,zf); +} + +void Camera::LookAt(Oyster::Math::Float3 pos, Oyster::Math::Float3 target, Oyster::Math::Float3 worldUp) +{ + Oyster::Math::Float3 L; + + L = target - pos; + L.Normalize(); + + Oyster::Math::Float3 R; + R = worldUp.Cross(L); + R.Normalize(); + + Oyster::Math::Float3 U; + U = L.Cross(R); + + this->m_position = pos; + this->mLook = L; + this->mRight = R; + this->mUp = U; +} + +Oyster::Math::Float4x4 Camera::View()const +{ + return this->mView; +} + +Oyster::Math::Float4x4 Camera::Proj()const +{ + return this->mProj; +} + +Oyster::Math::Float4x4 Camera::ViewsProj()const +{ + Oyster::Math::Float4x4 M; + M = mView * mProj; + return M; +} + +void Camera::Walk(float dist) +{ + this->m_position += dist*this->mLook; +} + +void Camera::Strafe(float dist) +{ + this->m_position += dist*this->mRight; +} + +void Camera::Pitch(float angle) +{ + float radians = angle * 0.0174532925f; + + Oyster::Math::Float4x4 R; + + Oyster::Math3D::RotationMatrix(radians,-mRight,R); + this->mUp = CrossMatrix(this->mUp, R); + this->mLook = CrossMatrix(this->mLook, R); +} + +void Camera::Yaw(float angle) +{ + float radians = angle * 0.0174532925f; + + Oyster::Math::Float4x4 R; + + Oyster::Math::Float3 up(0,1,0); + Oyster::Math3D::RotationMatrix(radians,-up,R); + + this->mRight = CrossMatrix(this->mRight, R); + this->mUp = CrossMatrix(mUp, R); + this->mLook = CrossMatrix(this->mLook, R); +} + +void Camera::UpdateViewMatrix() +{ + mLook.Normalize(); + mUp = mLook.Cross(mRight); + mUp.Normalize(); + mRight = mUp.Cross(mLook); + mView = Oyster::Math3D::ViewMatrix_LookAtDirection(mLook, mUp, m_position); + /* + mLook.Normalize(); + mUp = mLook.Cross(mRight); + mUp.Normalize(); + mRight = mUp.Cross(mLook); + + float x = -m_position.Dot(mRight); + float y = -m_position.Dot(mUp); + float z = -m_position.Dot(mLook); + + mView.m11 = mRight.x; + mView.m21 = mRight.y; + mView.m31 = mRight.z; + mView.m41 = x; + + mView.m12 = mUp.x; + mView.m22 = mUp.y; + mView.m32 = mUp.z; + mView.m42 = y; + + mView.m13 = mLook.x; + mView.m23 = mLook.y; + mView.m33 = mLook.z; + mView.m43 = z; + + mView.m14 = 0.0f; + mView.m24 = 0.0f; + mView.m34 = 0.0f; + mView.m44 = 1.0f; + + mView.Transpose();*/ +} \ No newline at end of file diff --git a/Code/Game/DanBiasGame/GameClientState/Camera.h b/Code/Game/DanBiasGame/GameClientState/Camera.h new file mode 100644 index 00000000..56ea5569 --- /dev/null +++ b/Code/Game/DanBiasGame/GameClientState/Camera.h @@ -0,0 +1,63 @@ +#ifndef CAMERA__H +#define CAMERA__H + +#include "OysterMath.h" + +class Camera +{ +private: + + Oyster::Math::Float3 m_position; + Oyster::Math::Float3 mRight; + Oyster::Math::Float3 mUp; + Oyster::Math::Float3 mLook; + + + + float mNearZ; + float mFarZ; + float mAspect; + float mFovY; + + Oyster::Math::Float4x4 mView; + Oyster::Math::Float4x4 mProj; + +public: + Camera(); + virtual ~Camera(); + + void SetPosition(const Oyster::Math::Float3& v); + + Oyster::Math::Float3 GetPosition()const; + + Oyster::Math::Float3 GetRight()const; + Oyster::Math::Float3 GetUp()const; + Oyster::Math::Float3 GetLook()const; + + float GetNearZ()const; + float GetFarZ()const; + float GetAspect()const; + + Oyster::Math::Float3 CrossMatrix(const Oyster::Math::Float3& v, const Oyster::Math::Float4x4& m); + + void SetLens(float fovY, float aspect, float zn, float zf); + + void LookAt(Oyster::Math::Float3 pos, Oyster::Math::Float3 target, Oyster::Math::Float3 worldUp); + + void setLook(Oyster::Math::Float3 look) { mLook = look; } + void setUp(Oyster::Math::Float3 up) { mUp = up; } + void setRight(Oyster::Math::Float3 right) { mRight = right; } + + Oyster::Math::Float4x4 View()const; + Oyster::Math::Float4x4 Proj()const; + Oyster::Math::Float4x4 ViewsProj()const; + + void Walk(float dist); + void Strafe(float dist); + + void Pitch(float angle); + void Yaw(float angle); + + void UpdateViewMatrix(); +}; +#endif \ No newline at end of file diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index 70385e07..3e89a824 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -4,7 +4,7 @@ #include "C_obj/C_DynamicObj.h" #include #include "NetworkClient.h" - +#include "Camera.h" using namespace DanBias::Client; @@ -17,7 +17,6 @@ struct GameState::myData int modelCount; Oyster::Network::NetworkClient* nwClient; gameStateState state; - }privData; @@ -38,10 +37,12 @@ GameState::~GameState(void) bool GameState::Init(Oyster::Network::NetworkClient* nwClient) { // load models + camera = new Camera; privData = new myData(); privData->state = gameStateState_loading; privData->nwClient = nwClient; privData->state = LoadGame(); + return true; } GameState::gameStateState GameState::LoadGame() @@ -92,14 +93,14 @@ bool GameState::LoadModels(std::wstring mapFile) modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; modelData.id ++; - obj = new C_DynamicObj(); + obj = new C_Player(); privData->object.push_back(obj); privData->object[privData->object.size() -1 ]->Init(modelData); - translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,-2,-2)); + /*translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,-2,-2)); modelData.world = modelData.world * translate; modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; - modelData.id ++; + modelData.id ++;*/ translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(0,0,0)); Oyster::Math3D::Float4x4 scale = Oyster::Math3D::Float4x4::identity; @@ -110,7 +111,7 @@ bool GameState::LoadModels(std::wstring mapFile) modelData.modelPath = L"..\\Content\\Models\\ball.dan"; modelData.id ++; - obj = new C_DynamicObj(); + obj = new C_Player(); privData->object.push_back(obj); privData->object[privData->object.size() -1 ]->Init(modelData); @@ -119,10 +120,19 @@ bool GameState::LoadModels(std::wstring mapFile) } bool GameState::InitCamera(Oyster::Math::Float3 startPos) { + Oyster::Math::Float3 dir = Oyster::Math::Float3(0,0,-1); + Oyster::Math::Float3 up =Oyster::Math::Float3(0,1,0); + Oyster::Math::Float3 pos = Oyster::Math::Float3(0, 0, 20); + + camera->LookAt(pos, dir, up); + camera->SetLens(3.14f/2, 1024/768, 1, 1000); + privData->proj = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/2,1024.0f/768.0f,.1f,1000); //privData->proj = Oyster::Math3D::ProjectionMatrix_Orthographic(1024, 768, 1, 1000); Oyster::Graphics::API::SetProjection(privData->proj); - + camera->UpdateViewMatrix(); + privData->view = camera->View(); + privData->view = Oyster::Math3D::ViewMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); privData->view = Oyster::Math3D::OrientationMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); return true; @@ -147,85 +157,9 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI // read server data // update objects { - bool send = false; - GameLogic::Protocol_PlayerMovement movePlayer; - movePlayer.bForward = false; - movePlayer.bBackward = false; - movePlayer.bLeft = false; - movePlayer.bRight = false; + readKeyInput(KeyInput); + camera->UpdateViewMatrix(); - if(KeyInput->IsKeyPressed(DIK_W)) - { - - if(!key_forward) - { - movePlayer.bForward = true; - send = true; - key_forward = true; - - GameLogic::Protocol_General_Text tp; - tp.text = "What!?"; - this->privData->nwClient->Send(tp); - } - } - else - key_forward = false; - - if(KeyInput->IsKeyPressed(DIK_S)) - { - if(!key_backward) - { - movePlayer.bBackward = true; - send = true; - key_backward = true; - } - } - else - key_backward = false; - - if(KeyInput->IsKeyPressed(DIK_A)) - { - if(!key_strafeLeft) - { - movePlayer.bLeft = true; - send = true; - key_strafeLeft = true; - } - } - else - key_strafeLeft = false; - - if(KeyInput->IsKeyPressed(DIK_D)) - { - if(!key_strafeRight) - { - movePlayer.bRight = true; - send = true; - key_strafeRight = true; - } - } - else - key_strafeRight = false; - - - if (privData->nwClient->IsConnected() && send) - { - privData->nwClient->Send(movePlayer); - } - - //send delta mouse movement - if (KeyInput->IsMousePressed()) - { - GameLogic::Protocol_PlayerMouse deltaMouseMove; - deltaMouseMove.dxMouse = KeyInput->GetYaw(); - deltaMouseMove.dyMouse = KeyInput->GetPitch(); - //privData->nwClient->Send(deltaMouseMove); - } - - // send event data - // - if(KeyInput->IsKeyPressed(DIK_L)) - privData->state = GameState::gameStateState_end; } break; case gameStateState_end: @@ -240,7 +174,9 @@ GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyI } bool GameState::Render() { - Oyster::Graphics::API::SetView(privData->view); + Oyster::Graphics::API::SetView(camera->View()); + //Oyster::Graphics::API::SetProjection(camera->Proj()); + //Oyster::Graphics::API::SetView(privData->view); Oyster::Graphics::API::SetProjection(privData->proj); Oyster::Graphics::API::NewFrame(); for (unsigned int i = 0; i < privData->object.size(); i++) @@ -259,10 +195,108 @@ bool GameState::Release() privData->object[i] = NULL; } + delete this->camera; + delete privData; privData = NULL; return true; } +void GameState::readKeyInput(InputClass* KeyInput) +{ + + bool send = false; + GameLogic::Protocol_PlayerMovement movePlayer; + movePlayer.bForward = false; + movePlayer.bBackward = false; + movePlayer.bLeft = false; + movePlayer.bRight = false; + + if(KeyInput->IsKeyPressed(DIK_W)) + { + + if(!key_forward) + { + movePlayer.bForward = true; + send = true; + key_forward = true; + } + } + else + key_forward = false; + + if(KeyInput->IsKeyPressed(DIK_S)) + { + if(!key_backward) + { + movePlayer.bBackward = true; + send = true; + key_backward = true; + } + } + else + key_backward = false; + + if(KeyInput->IsKeyPressed(DIK_A)) + { + if(!key_strafeLeft) + { + movePlayer.bLeft = true; + send = true; + key_strafeLeft = true; + } + } + else + key_strafeLeft = false; + + if(KeyInput->IsKeyPressed(DIK_D)) + { + if(!key_strafeRight) + { + movePlayer.bRight = true; + send = true; + key_strafeRight = true; + } + } + else + key_strafeRight = false; + + + if (privData->nwClient->IsConnected() && send) + { + privData->nwClient->Send(movePlayer); + } + + //send delta mouse movement + if (KeyInput->IsMousePressed()) + { + camera->Yaw(KeyInput->GetYaw()); + camera->Pitch(KeyInput->GetPitch()); + camera->UpdateViewMatrix(); + GameLogic::Protocol_PlayerLook playerLookDir; + Oyster::Math::Float3 look = camera->GetLook(); + playerLookDir.lookDirX = look.x; + playerLookDir.lookDirY = look.y; + playerLookDir.lookDirZ = look.z; + privData->nwClient->Send(playerLookDir); + } + if(KeyInput->IsKeyPressed(DIK_Z)) + { + if(!key_Shoot) + { + GameLogic::Protocol_PlayerShot playerShot; + playerShot.hasShot = true; + privData->nwClient->Send(playerShot); + key_Shoot = true; + } + } + else + key_Shoot = false; + + // send event data + // + if(KeyInput->IsKeyPressed(DIK_L)) + privData->state = GameState::gameStateState_end; +} void GameState::Protocol(ProtocolStruct* pos) { @@ -290,13 +324,17 @@ void GameState::Protocol( ObjPos* pos ) for (unsigned int i = 0; i < privData->object.size(); i++) { - if(privData->object[i] && privData->object[i]->GetId() == pos->object_ID) + if(privData->object[i]->GetId() == pos->object_ID) { privData->object[i]->setPos(world); - - //privData->view = world; - //privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); - + //camera->setRight((Oyster::Math::Float3(world[0], world[1], world[2]))); + //camera->setUp((Oyster::Math::Float3(world[4], world[5], world[6]))); + //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); + if(i == 0) + { + camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); + camera->UpdateViewMatrix(); + } } } } diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.h b/Code/Game/DanBiasGame/GameClientState/GameState.h index 3942afba..30884043 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.h +++ b/Code/Game/DanBiasGame/GameClientState/GameState.h @@ -3,6 +3,7 @@ #include "GameClientState.h" #include "OysterMath.h" #include +#include "Camera.h" namespace DanBias { namespace Client @@ -21,6 +22,8 @@ private: bool key_backward; bool key_strafeRight; bool key_strafeLeft; + bool key_Shoot; + Camera* camera; struct myData; myData* privData; @@ -33,6 +36,7 @@ public: bool InitCamera(Oyster::Math::Float3 startPos) ; gameStateState LoadGame(); + void readKeyInput(InputClass* KeyInput); bool Render()override; bool Release()override; diff --git a/Code/Game/DanBiasServer/LobbySessions/GameLobby.cpp b/Code/Game/DanBiasServer/LobbySessions/GameLobby.cpp deleted file mode 100644 index fc770db8..00000000 --- a/Code/Game/DanBiasServer/LobbySessions/GameLobby.cpp +++ /dev/null @@ -1,21 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#include "GameLobby.h" - - -namespace DanBias -{ - GameLobby::GameLobby(Utility::DynamicMemory::SmartPointer owner) - { - - } - GameLobby::~GameLobby() - { - - } - void GameLobby::Release() - { - - } -}//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj b/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj index 62344a57..dde29257 100644 --- a/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj +++ b/Code/Game/DanBiasServerLauncher/DanBiasServerLauncher.vcxproj @@ -71,7 +71,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)WindowManager\;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -79,7 +79,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)WindowManager\;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -87,7 +87,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)WindowManager\;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -95,7 +95,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + $(SolutionDir)Game\GameServer;C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)WindowManager\;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -111,8 +111,8 @@ Windows true - DanBiasGame_$(PlatformShortName)D.dll;DanBiasServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) - DanBiasGame_$(PlatformShortName)D.lib;DanBiasServer_$(PlatformShortName)D.lib;%(AdditionalDependencies) + DanBiasGame_$(PlatformShortName)D.dll;GameServer_$(PlatformShortName)D.dll + GameServer_$(PlatformShortName)D.lib;DanBiasGame_$(PlatformShortName)D.lib;%(AdditionalDependencies) @@ -128,8 +128,8 @@ Windows true - DanBiasServer_$(PlatformShortName)D.dll;DanBiasGame_$(PlatformShortName)D.dll;%(DelayLoadDLLs) - DanBiasGame_$(PlatformShortName)D.lib;DanBiasServer_$(PlatformShortName)D.lib;%(AdditionalDependencies) + DanBiasGame_$(PlatformShortName)D.dll;GameServer_$(PlatformShortName)D.dll + GameServer_$(PlatformShortName)D.lib;DanBiasGame_$(PlatformShortName)D.lib;%(AdditionalDependencies) @@ -149,8 +149,8 @@ true true true - DanBiasServer_$(PlatformShortName).dll;DanBiasGame_$(PlatformShortName).dll;%(DelayLoadDLLs) - DanBiasServer_$(PlatformShortName).lib;DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) + DanBiasGame_$(PlatformShortName).dll;GameServer_$(PlatformShortName).dll;%(DelayLoadDLLs) + GameServer_$(PlatformShortName).lib;DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) @@ -170,13 +170,18 @@ true true true - DanBiasServer_$(PlatformShortName).dll;DanBiasGame_$(PlatformShortName).dll;%(DelayLoadDLLs) - DanBiasServer_$(PlatformShortName).lib;DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) + DanBiasGame_$(PlatformShortName).dll;GameServer_$(PlatformShortName).dll;%(DelayLoadDLLs) + GameServer_$(PlatformShortName).lib;DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) + + + {35aea3c0-e0a7-4e1e-88cd-514aa5a442b1} + + diff --git a/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp b/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp index 7781717d..fbf19b57 100644 --- a/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp +++ b/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp @@ -5,7 +5,8 @@ #define NOMINMAX //Blame it on windows #include #include - +#include +#include #include @@ -16,13 +17,14 @@ int WINAPI WinMain( HINSTANCE hinst, HINSTANCE prevInst, PSTR cmdLine, int cmdSh return cmdShow; } - DanBias::GameServerAPI::GameInitDesc desc; - desc.connectionPort = 15151; - desc.maxNumberOfClients = 0; - desc.threaded = false; - if( !DanBias::GameServerAPI::Create(desc) == DanBias::DanBiasServerReturn_Sucess) - { + WindowShell::CreateConsoleWindow(); + DanBias::GameServerAPI::GameInitDesc desc; + desc.listenPort = 15151; + if(DanBias::GameServerAPI::Create(desc) == DanBias::DanBiasServerReturn_Sucess) + { + DanBias::GameServerAPI::Start(); + DanBias::GameServerAPI::Terminate(); } return cmdShow; } \ No newline at end of file diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index b91cb46d..ffb009cf 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -56,33 +56,36 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_PlayerMouse :public Oyster::Network::CustomProtocolObject + struct Protocol_PlayerLook :public Oyster::Network::CustomProtocolObject { - float dxMouse; - float dyMouse; - + float lookDirX; + float lookDirY; + float lookDirZ; - Protocol_PlayerMouse() + Protocol_PlayerLook() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerMouseMovement; + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerLookDir; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Float; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; + this->protocol[3].type = Oyster::Network::NetAttributeType_Float; } - const Protocol_PlayerMouse& operator=(Oyster::Network::CustomNetProtocol& val) + const Protocol_PlayerLook& operator=(Oyster::Network::CustomNetProtocol& val) { - dxMouse = val[1].value.netFloat; - dyMouse = val[2].value.netFloat; + lookDirX = val[1].value.netFloat; + lookDirY = val[2].value.netFloat; + lookDirZ = val[3].value.netFloat; return *this; } Oyster::Network::CustomNetProtocol* GetProtocol() override { - this->protocol[1].value = dxMouse; - this->protocol[2].value = dyMouse; + this->protocol[1].value = lookDirX; + this->protocol[2].value = lookDirY; + this->protocol[3].value = lookDirZ; return &protocol; } @@ -119,6 +122,32 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; + struct Protocol_PlayerShot :public Oyster::Network::CustomProtocolObject + { + bool hasShot; + + Protocol_PlayerShot() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerShot; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; + } + const Protocol_PlayerShot& operator=(Oyster::Network::CustomNetProtocol& val) + { + hasShot = val[1].value.netBool; + return *this; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = hasShot; + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + } #endif // !GAMELOGIC_PLAYER_PROTOCOLS_H diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index 7b742eb9..0bb902c6 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -47,14 +47,15 @@ /***********[ 300 - 399 ]***********/ #define protocol_GameplayMIN 300 #define protocol_Gameplay_PlayerMovement 300 -#define protocol_Gameplay_PlayerMouseMovement 301 +#define protocol_Gameplay_PlayerLookDir 301 #define protocol_Gameplay_PlayerChangeWeapon 302 -#define protocol_Gameplay_ObjectPickup 303 -#define protocol_Gameplay_ObjectDamage 304 -#define protocol_Gameplay_ObjectPosition 305 -#define protocol_Gameplay_ObjectEnabled 306 -#define protocol_Gameplay_ObjectDisabled 307 -#define protocol_Gameplay_ObjectCreate 308 +#define protocol_Gameplay_PlayerShot 303 +#define protocol_Gameplay_ObjectPickup 304 +#define protocol_Gameplay_ObjectDamage 305 +#define protocol_Gameplay_ObjectPosition 306 +#define protocol_Gameplay_ObjectEnabled 307 +#define protocol_Gameplay_ObjectDisabled 308 +#define protocol_Gameplay_ObjectCreate 309 #define protocol_GameplayMAX 399 diff --git a/Code/Game/GameServer/GameClient.h b/Code/Game/GameServer/GameClient.h index 3fa0a6f6..241f13ca 100644 --- a/Code/Game/GameServer/GameClient.h +++ b/Code/Game/GameServer/GameClient.h @@ -7,26 +7,28 @@ #include #include #include +#include namespace DanBias { + /** + * Container to keep logic player and network client together as a unit. + */ class GameClient { public: - GameClient(Oyster::Network::NetworkClient client, GameLogic::IPlayerData* player); + GameClient(Utility::DynamicMemory::SmartPointer client, GameLogic::IPlayerData* player); virtual~GameClient(); - //void SetCallback(Oyster::Callback::OysterCallback value); - GameLogic::IPlayerData* GetPlayer(); GameLogic::IPlayerData* ReleasePlayer(); - Oyster::Network::NetworkClient* GetClient(); - Oyster::Network::NetworkClient ReleaseClient(); + Utility::DynamicMemory::SmartPointer GetClient(); + Utility::DynamicMemory::SmartPointer ReleaseClient(); int GetID() const; private: GameLogic::IPlayerData* player; - Oyster::Network::NetworkClient client; + Utility::DynamicMemory::SmartPointer client; int id; }; diff --git a/Code/Game/GameServer/GameLobby.h b/Code/Game/GameServer/GameLobby.h index 7a47638d..1391641e 100644 --- a/Code/Game/GameServer/GameLobby.h +++ b/Code/Game/GameServer/GameLobby.h @@ -8,6 +8,7 @@ #include #include #include +#include "GameSession.h" namespace DanBias { @@ -16,33 +17,32 @@ namespace DanBias public: GameLobby(); virtual~GameLobby(); - void Release(); + void Update(); - void Frame(); - - //void ClientEventCallback(NetEvent e) override; + operator bool(); private: - void ParseEvents(); - void ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, Oyster::Network::NetworkClient* c); - void ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, Oyster::Network::NetworkClient* c); + void ParseProtocol(Oyster::Network::CustomNetProtocol& p, Oyster::Network::NetworkClient* c); - //Lobby events - void LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Network::NetworkClient* c); - void LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); - - //General events - void GeneralStatus(GameLogic::Protocol_General_Status& p, Oyster::Network::NetworkClient* c); - void GeneralText(GameLogic::Protocol_General_Text& p, Oyster::Network::NetworkClient* c); + void GeneralStatus(GameLogic::Protocol_General_Status& p, Oyster::Network::NetworkClient* c); //id = protocol_General_Status: + void GeneralText(GameLogic::Protocol_General_Text& p, Oyster::Network::NetworkClient* c); //id = protocol_General_Text: + void LobbyCreateGame(GameLogic::Protocol_LobbyCreateGame& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Create: + void LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Start: + void LobbyJoin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Join: + void LobbyLogin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Login: + void LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Refresh: + void LobbyMainData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_MainData: + void LobbyGameData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_GameData: private: void ClientEventCallback(Oyster::Network::NetEvent e) override; - void ClientConnectedEvent(Oyster::Network::NetEvent e) override; + void ClientConnectedEvent(Utility::DynamicMemory::SmartPointer client) override; private: Utility::WinTimer timer; float refreshFrequency; + GameSession* gameSession; }; }//End namespace DanBias #endif // !DANBIASGAME_GAMELOBBY_H diff --git a/Code/Game/GameServer/GameServer.h b/Code/Game/GameServer/GameServer.h deleted file mode 100644 index 73857b2d..00000000 --- a/Code/Game/GameServer/GameServer.h +++ /dev/null @@ -1,33 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_GAME_SERVER_H -#define DANBIASSERVER_GAME_SERVER_H - -#include "GameServerAPI.h" -#include "GameLobby.h" -#include -#include - -namespace DanBias -{ - class GameServer - { - public: - GameServer(); - virtual~GameServer(); - - DanBiasServerReturn Create(const GameServerAPI::GameInitDesc& desc); - - private: - static void Run(GameServer* owner); - void Run(); - void Release(); - - private: - int maxClients; - Utility::DynamicMemory::SmartPointer lobby; - Utility::DynamicMemory::SmartPointer server; - }; -}// End namspace DanBias -#endif // !DANBIASSERVER_DBSERVER_H diff --git a/Code/Game/GameServer/GameServer.vcxproj b/Code/Game/GameServer/GameServer.vcxproj index e736a111..6c0f3ea9 100644 --- a/Code/Game/GameServer/GameServer.vcxproj +++ b/Code/Game/GameServer/GameServer.vcxproj @@ -178,31 +178,20 @@ - - - - - - true - true - true - true - - - true - true - true - true + + + false + false + false + false - - diff --git a/Code/Game/GameServer/GameServerAPI.h b/Code/Game/GameServer/GameServerAPI.h index 08e25114..cc03ec01 100644 --- a/Code/Game/GameServer/GameServerAPI.h +++ b/Code/Game/GameServer/GameServerAPI.h @@ -20,6 +20,7 @@ namespace DanBias { DanBiasServerReturn_Error, DanBiasServerReturn_Sucess, + DanBiasServerReturn_GameNotCreated, }; extern "C" @@ -29,13 +30,16 @@ namespace DanBias public: struct GameInitDesc { - //stuff - int connectionPort; - int maxNumberOfClients; + int listenPort; bool threaded; }; + public: static DanBiasServerReturn Create(const GameInitDesc& desc); + static void Start(); + static void Stop(); + static void Terminate(); + };//End class DanBiasServer }//End Extern "C" } //End namspace DanBias diff --git a/Code/Game/GameServer/GameSession.h b/Code/Game/GameServer/GameSession.h index eed2b4b4..ed480c35 100644 --- a/Code/Game/GameServer/GameSession.h +++ b/Code/Game/GameServer/GameSession.h @@ -15,20 +15,24 @@ #include #include #include +#include + namespace DanBias { - class GameSession : public Oyster::Thread::IThreadObject + class GameSession : public Oyster::Network::NetworkSession + , public Oyster::Thread::IThreadObject { public: + /** * A container to use when initiating a new session */ struct GameDescription { std::wstring mapName; - NetworkSession* owner; - Utility::DynamicMemory::DynamicArray clients; + Oyster::Network::NetworkSession* owner; + Utility::DynamicMemory::DynamicArray clients; }; public: @@ -44,45 +48,28 @@ namespace DanBias /** Join an existing/running game session * @param client The client to attach to the session */ - bool Join(Utility::DynamicMemory::SmartPointer client); + bool Attach(Oyster::Network::NetClient client) override; - /** - * Closes the game session - * @param disconnectClients If set to true clients is dissconnected from the server, if false the clients is sent to the given owner of the session. - */ - void CloseSession(bool disconnectClients = false); - inline bool IsCreated() const { return this->isCreated; } inline bool IsRunning() const { return this->isRunning; } //Private member functions private: //Handles all events recieved - void ParseEvents(); - //Handles all gameplay events - void ParseGameplayEvent(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c); - //Handles all general events - void ParseGeneralEvent(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c); - //Adds a client to the client list - void InsertClient(Utility::DynamicMemory::SmartPointer obj); - //Removes a client from the client list - void RemoveClient(DanBias::GameClient* obj); - //Sends a protocol ta all clients in session - void Send(Oyster::Network::CustomNetProtocol* p); - //Frame function, derived from IThreadObject - bool DoWork ( ) override; + //void ParseEvents(); + + void ClientEventCallback(Oyster::Network::NetEvent e) override; + void ParseProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c); + //Sends a client to the owner, if obj is NULL then all clients is sent void SendToOwner(DanBias::GameClient* obj); - //Do a cleanup on all the private data - void Clean(); - //Update game objects if needed - void UpdateGameObjects(); - + + //Frame function, derived from IThreadObject + bool DoWork ( ) override; + //Private member variables private: - Utility::DynamicMemory::DynamicArray> clients; - //Oyster::PostBox *box; Oyster::Thread::OysterThread worker; GameLogic::GameAPI& gameInstance; GameLogic::ILevelData *levelData; diff --git a/Code/Game/GameServer/GameSessionManager.h b/Code/Game/GameServer/GameSessionManager.h deleted file mode 100644 index a2c8a3ee..00000000 --- a/Code/Game/GameServer/GameSessionManager.h +++ /dev/null @@ -1,82 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#ifndef DANBIASSERVER_GAME_SEESION_MANAGER_H -#define DANBIASSERVER_GAME_SEESION_MANAGER_H - -#include -#include -#include -#include - -using namespace Oyster::Network; - -namespace DanBias -{ - struct GameSessionDescription - { - std::wstring mapName; - Utility::DynamicMemory::DynamicArray clients; - NetworkSession* exitDestionation; //The new owner when session dies - }; - struct GameSessionInfo - { - std::wstring mapName; - unsigned int numberOfPlayers; - float gametime; - }; - - class GameSessionManager - { - public: - /** - * Add a new game session. - * On success, the function returns the game instance id number greater than 0. - */ - static int AddSession(GameSessionDescription& instance, bool run); - - /** - * Starts an existing game session - * @param session The session id recieved when created. - * @param run Indicates if the game session should start imidiatly when created. - * @return Returns false if session is not found. - */ - static bool StartSession(int session); - - /** - * Join an exiting session - * @param session The session id recieved when created. - * @param client The client that is to join a game session - * @return Returns false on failure. - */ - static bool JoinSession(int session, Utility::DynamicMemory::SmartPointer client); - - /** - * Gets information about a given session - * @param session The session id recieved when created. - * @param sessionInformation The output parameter that will be filled. - */ - static void GetSessionInfo(int session, GameSessionInfo& sessionInformation); - - /** - * Close a session. - * @param session The session id recieved when created a session. - */ - static void CloseSession(int session); - - /** - * Close all sessions. - */ - static void CloseSessions(); - - /** - * Get total sessions running - * @return Returns the total sessions curently running. - */ - static int GetSessionSize(); - - private: - friend class AdminInterface; - }; -} -#endif // !DANBIASSERVER_GAME_SEESION_MANAGER_H diff --git a/Code/Game/GameServer/Implementation/GameClient.cpp b/Code/Game/GameServer/Implementation/GameClient.cpp index 94ac807a..07999205 100644 --- a/Code/Game/GameServer/Implementation/GameClient.cpp +++ b/Code/Game/GameServer/Implementation/GameClient.cpp @@ -7,12 +7,13 @@ #include using namespace Utility::DynamicMemory; +using namespace Oyster::Network; using namespace DanBias; using namespace GameLogic; static int gameClientIDCount = 1; -GameClient::GameClient(Oyster::Network::NetworkClient client, GameLogic::IPlayerData* player) +GameClient::GameClient(SmartPointer client, GameLogic::IPlayerData* player) { this->client = client; this->id = gameClientIDCount++; @@ -20,7 +21,7 @@ GameClient::GameClient(Oyster::Network::NetworkClient client, GameLogic::IPlayer } GameClient::~GameClient() { - this->client.Disconnect(); + this->client->Disconnect(); this->player = 0; this->id = -1; } @@ -35,13 +36,14 @@ GameLogic::IPlayerData* GameClient::ReleasePlayer() this->player = 0; return temp; } -Oyster::Network::NetworkClient* GameClient::GetClient() +SmartPointer GameClient::GetClient() { - return &this->client; + return this->client; } -Oyster::Network::NetworkClient GameClient::ReleaseClient() +SmartPointer GameClient::ReleaseClient() { - Oyster::Network::NetworkClient temp = this->client; + SmartPointer temp = this->client; + this->client = 0; return temp; } int GameClient::GetID() const diff --git a/Code/Game/GameServer/Implementation/GameLobby.cpp b/Code/Game/GameServer/Implementation/GameLobby.cpp index ad3de1c7..cd90793a 100644 --- a/Code/Game/GameServer/Implementation/GameLobby.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby.cpp @@ -18,34 +18,37 @@ namespace DanBias { } void GameLobby::Release() - { } + { + NetworkSession::CloseSession(true); + } - void GameLobby::Frame() + void GameLobby::Update() { - ParseEvents(); + this->ProcessClients(); + } + GameLobby::operator bool() + { + return true; } void GameLobby::ClientEventCallback(NetEvent e) { - + switch (e.args.type) + { + case NetworkClient::ClientEventArgs::EventType_Disconnect: + break; + case NetworkClient::ClientEventArgs::EventType_ProtocolFailedToRecieve: + break; + case NetworkClient::ClientEventArgs::EventType_ProtocolFailedToSend: + break; + case NetworkClient::ClientEventArgs::EventType_ProtocolRecieved: + this->ParseProtocol(e.args.data.protocol, e.sender); + break; + } } - void GameLobby::ClientConnectedEvent(NetEvent e) + void GameLobby::ClientConnectedEvent(Utility::DynamicMemory::SmartPointer client) { - - } - -//////// Private - void GameLobby::ParseEvents() - { - //if(this->box && !this->box->IsEmpty()) - //{ - // NetEvent &e = this->box->Fetch(); - // - // short type = e.protocol[0].value.netShort; - // - // if(ProtocolIsLobby(type)) ParseLobbyProtocol(e.protocol, e.sender); - // else if(ProtocolIsGeneral(type)) ParseGeneralProtocol(e.protocol, e.sender); - //} + //Attach(client); } }//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp new file mode 100644 index 00000000..45ec4dfa --- /dev/null +++ b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp @@ -0,0 +1,100 @@ +#include "..\GameLobby.h" + +using namespace DanBias; +using namespace GameLogic; +using namespace Oyster::Network; + + +void GameLobby::ParseProtocol(Oyster::Network::CustomNetProtocol& p, NetworkClient* c) +{ + switch (p[0].value.netShort) + { + //LobbyStartGame(GameLogic::Protocol_LobbyStartGame(p), c); + //LobbyRefresh(GameLogic::Protocol_LobbyRefresh(p), c); + //LobbyLogin(GameLogic::Protocol_LobbyLogin(p), c); + //LobbyJoin(GameLogic::Protocol_LobbyJoin(p), c); + //GeneralStatus(GameLogic::Protocol_General_Status(p), c); + //GeneralText(GameLogic::Protocol_General_Text(p), c); + + case protocol_General_Status: + break; + case protocol_General_Text: + break; + case protocol_Lobby_Create: + break; + case protocol_Lobby_Start: + break; + case protocol_Lobby_Join: + break; + case protocol_Lobby_Login: + break; + case protocol_Lobby_Refresh: + break; + case protocol_Lobby_MainData: + break; + case protocol_Lobby_GameData: + break; + } +} + + +void GameLobby::GeneralStatus(GameLogic::Protocol_General_Status& p, Oyster::Network::NetworkClient* c) +{ + switch (p.status) + { + case Protocol_General_Status::States_ready: + { + + } + case Protocol_General_Status::States_idle: + { + + } + case Protocol_General_Status::States_leave: + case Protocol_General_Status::States_disconected: + { + Detach(c)->Disconnect(); + } + } +} +void GameLobby::GeneralText(GameLogic::Protocol_General_Text& p, Oyster::Network::NetworkClient* c) +{ + printf(p.text.c_str()); +} +void GameLobby::LobbyCreateGame(GameLogic::Protocol_LobbyCreateGame& p, Oyster::Network::NetworkClient* c) +{ + +} +void GameLobby::LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Network::NetworkClient* c) +{ + +} +void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +{ + //for (unsigned int i = 0; i < this->gameLobby.Size(); i++) + //{ + // if (this->gameLobby[i]->GetID() == p.value) + // { + // this->gameLobby[i]->Attach(Detach(c)); + // return; + // } + //} +} +void GameLobby::LobbyLogin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +{ + +} +void GameLobby::LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +{ + //Dont need to handle this on the server... +} +void GameLobby::LobbyMainData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +{ + +} +void GameLobby::LobbyGameData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +{ + +} + + diff --git a/Code/Game/GameServer/Implementation/GameServer.cpp b/Code/Game/GameServer/Implementation/GameServer.cpp index 90620c18..ac2abc8a 100644 --- a/Code/Game/GameServer/Implementation/GameServer.cpp +++ b/Code/Game/GameServer/Implementation/GameServer.cpp @@ -3,137 +3,93 @@ ///////////////////////////////////////////////////////////////////// #define NOMINMAX #include -#include #include +#include - -#include "..\GameServer.h" -#include "..\GameSessionManager.h" +#include "..\GameServerAPI.h" +#include "..\GameLobby.h" #include "..\GameSession.h" -#include -#include +#include #include -#include -#include -namespace DanBias +#include +#include +#include +#include + +using namespace DanBias; +using namespace Oyster::Network; +using namespace Oyster::Thread; +using namespace Utility; + +namespace { - using namespace Oyster::Network; + GameLobby lobby; + NetworkServer server; + WinTimer timer; + GameServerAPI instance; + //typedef void(*WorkerThreadFnc)(GameServerAPI*); +} - GameServer* instance = 0; - std::thread workerThread; - typedef void(*WorkerThreadFnc)(GameServer*); - DanBiasServerReturn GameServerAPI::Create(const GameInitDesc& desc) +DanBiasServerReturn GameServerAPI::Create(const GameInitDesc& desc) +{ + + if(server.Init(desc.listenPort, &lobby) == NetworkServer::ServerReturnCode_Error) { - if(!instance) - instance = new GameServer(); - - return instance->Create(desc); + return DanBiasServerReturn_Error; } + std::printf("Server created!\t-\t%s: [%i]\n", server.GetLanAddress().c_str(), desc.listenPort); + + return DanBiasServerReturn_Sucess; +} +void GameServerAPI::Start() +{ + server.Start(); + + timer.reset(); + + while (true) + { + int c = server.ProcessConnectedClients(); + if(c > 0) printf(" - [%i] client(s) connected!\n", c); + lobby.Update(); + + if(GetAsyncKeyState(0x51)) //Q for exit + break; + } + + double total = timer.getElapsedSeconds(); + int time = (int)total; + int hour, min, sec; + + hour=time / 3600; + time=time % 3600; + min=time / 60; + time=time % 60; + sec = time; + + printf( "Server has been running for: %i:%i:%i - [hh:mm:ss] \n\n", hour, min, sec ); + printf( "Terminating in : "); + for (int i = 0; i < 4; i++) + { + printf( "%i ", 3-i ); + Sleep(1000); + } +} +void GameServerAPI::Stop() +{ + server.Stop(); + lobby.ProcessClients(); +} +void GameServerAPI::Terminate() +{ + lobby.Release(); + server.Shutdown(); - GameServer::GameServer() - : maxClients(0) - { } - GameServer::~GameServer() - { } - DanBiasServerReturn GameServer::Create(const GameServerAPI::GameInitDesc& desc) - { - this->maxClients = desc.maxNumberOfClients; - this->server = new NetworkServer(); - this->lobby = new GameLobby(); + printf( "Server terminated!" ); - if(desc.threaded) - { - if(!this->server->Init(desc.connectionPort, this->lobby)) - return DanBiasServerReturn_Error; - - if(!this->server->Start()) - return DanBiasServerReturn_Error; - - WorkerThreadFnc temp = GameServer::Run; - workerThread = std::thread(temp, this); - } - else - { - if(!this->server->Init(desc.connectionPort, this->lobby)) - return DanBiasServerReturn_Error; - - if(!this->server->Start()) - return DanBiasServerReturn_Error; - - Run(); - } - - return DanBiasServerReturn_Sucess; - } - void GameServer::Run(GameServer* owner) - { - while (true) - { - owner->server->ProcessConnectedClients(); - owner->lobby->ProcessClients(); - - if(GetAsyncKeyState(0x51)) //Q for exit - break; - } - } - void GameServer::Run() - { - while (true) - { - this->server->ProcessConnectedClients(); - this->lobby->Frame(); - - if(GetAsyncKeyState(0x51)) //Q for exit - break; - } - } - void GameServer::Release() - { - GameSessionManager::CloseSessions(); - } - - - - - //void GameServer::ClientConnected(NetworkClient* client) - //{ - // static bool myTest = false; - // static int sessionId = -1; - // printf("Client with ID [%i] connected.\n", client->GetID()); - // - // if(!myTest) - // { - // Utility::DynamicMemory::SmartPointer c = new Client(client); - // - // GameSessionDescription desc; - // desc.mapName = L"test"; - // desc.clients.Push(c); - // desc.exitDestionation = this->lobby; - // if((sessionId = GameSessionManager::AddSession(desc, true)) == 0) - // printf("Failed to create a game session\n"); - // myTest = true; - // //myTest = new GameSession(); - // // - // //DanBias::GameSession::GameSessionDescription desc; - // //desc.owner = 0; - // //desc.clients.Push(c); - // // - // //if(!myTest->Create(desc)) return; - // //myTest->Run(); - // } - // else - // { - // Utility::DynamicMemory::SmartPointer c = new NetworkSession(client); - // GameSessionManager::JoinSession(sessionId, c); - // } - // - // - // //Utility::DynamicMemory::SmartPointer c = new NetworkSession(client); - // //this->mainLobby->Attach(c, this->mainLobby->GetPostbox()); - //} -}//End namespace DanBias +} diff --git a/Code/Game/GameServer/Implementation/GameSessionManager.cpp b/Code/Game/GameServer/Implementation/GameSessionManager.cpp deleted file mode 100644 index 5eb8d664..00000000 --- a/Code/Game/GameServer/Implementation/GameSessionManager.cpp +++ /dev/null @@ -1,121 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#include "..\GameSessionManager.h" -#include "..\GameSession.h" -#include - -using namespace DanBias; -using namespace Utility::DynamicMemory; - -struct GameSessionData -{ - DynamicArray< SmartPointer< GameSession > > sessions; - - int freeSpot; - - int Existst(int session) - { - for (unsigned int i = 0; i < sessions.Size(); i++) - { - if(!sessions[i] && freeSpot == -1) freeSpot = i; - if(sessions[i]->GetID() == session) return i; - } - return -1; - } - int GetFree() - { - for (unsigned int i = 0; i < sessions.Size(); i++) - { - if(!sessions[i]) - { - this->freeSpot = i; - return this->freeSpot; - } - } - - this->freeSpot = -1; - return this->freeSpot; - } - -} gameSessionData; - - -int GameSessionManager::AddSession(GameSessionDescription& instance, bool run) -{ - int k = gameSessionData.GetFree(); - - SmartPointer gs = new GameSession(); - - DanBias::GameSession::GameDescription desc; - desc.owner = instance.exitDestionation; - desc.clients = instance.clients; - desc.mapName = instance.mapName; - - - if(!gs->Create(desc)) return 0; - - if(k == -1) gameSessionData.sessions.Push(gs); - else gameSessionData.sessions[k] = gs; - - if(run) gs->Run(); - - return gs->GetID(); -} - -bool GameSessionManager::StartSession(int session) -{ - int i = -1; - if((i = gameSessionData.Existst(session)) != -1) return false; - - gameSessionData.sessions[i]->Run(); - - return true; -} - -bool GameSessionManager::JoinSession(int session, Utility::DynamicMemory::SmartPointer client) -{ - int i = -1; - if((i = gameSessionData.Existst(session)) == -1) return false; - - gameSessionData.sessions[i]->Join(client); - - return true; -} - -void GameSessionManager::GetSessionInfo(int session, GameSessionInfo& data) -{ - memset(&data, 0, sizeof(GameSessionInfo)); - - int i = -1; - if((i = gameSessionData.Existst(session)) != -1) return; - - //data.gametime = gameSessionData.sessions[i]-> - //data.mapName = gameSessionData.sessions[i]-> - //data.numberOfPlayers = gameSessionData.sessions[i]-> -} - -void GameSessionManager::CloseSessions() -{ - for (unsigned int i = 0; i < gameSessionData.sessions.Size(); i++) - { - gameSessionData.sessions[i]->CloseSession(); - } -} -void GameSessionManager::CloseSession(int session) -{ - int i = -1; - if((i = gameSessionData.Existst(session)) != -1) return; - - - gameSessionData.sessions[i]->CloseSession(); -} - -int GameSessionManager::GetSessionSize() -{ - return gameSessionData.sessions.Size(); -} - - - - diff --git a/Code/Game/GameServer/Implementation/GameSession_Events.cpp b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp similarity index 57% rename from Code/Game/GameServer/Implementation/GameSession_Events.cpp rename to Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp index b3b831f6..ef1898be 100644 --- a/Code/Game/GameServer/Implementation/GameSession_Events.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp @@ -4,14 +4,18 @@ #include "..\GameSession.h" #include "..\GameClient.h" -#include #include #include #include #include - +#define NOMINMAX #include +#define DELTA_TIME_20 0.05f +#define DELTA_TIME_24 0.04166666666666666666666666666667f +#define DELTA_TIME_30 0.03333333333333333333333333333333f +#define DELTA_TIME_60 0.01666666666666666666666666666667f +#define DELTA_TIME_120 0.00833333333333333333333333333333f using namespace Utility::DynamicMemory; using namespace Oyster; @@ -21,25 +25,45 @@ using namespace GameLogic; namespace DanBias { - void GameSession::ParseEvents() + bool GameSession::DoWork( ) { - if( !this->box->IsEmpty() ) + if(this->isRunning) { - NetworkSession::NetEvent &e = this->box->Fetch(); - static int ii = 0; - printf("%i - Message recieved! [%i]\n", ii++, e.protocol[0].value); + double dt = this->timer.getElapsedSeconds(); + gameInstance.SetFrameTimeLength((float)dt); - if(e.protocol[0].type != Oyster::Network::NetAttributeType_Short) return; + if(dt >= DELTA_TIME_20) + { + this->ProcessClients(); - if( ProtocolIsGameplay(e.protocol[protocol_INDEX_ID].value.netShort) ) - ParseGameplayEvent(e.protocol, e.gameClient); + this->gameInstance.NewFrame(); - if( ProtocolIsGeneral(e.protocol[protocol_INDEX_ID].value.netShort) ) - ParseGeneralEvent(e.protocol, e.gameClient); + this->timer.reset(); + } } + + return this->isRunning; } - void GameSession::ParseGameplayEvent(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c) + //void GameSession::ParseEvents() + //{ + // if( !this->box->IsEmpty() ) + // { + // NetworkSession::NetEvent &e = this->box->Fetch(); + // static int ii = 0; + // printf("%i - Message recieved! [%i]\n", ii++, e.protocol[0].value); + // + // if(e.protocol[0].type != Oyster::Network::NetAttributeType_Short) return; + // + // if( ProtocolIsGameplay(e.protocol[protocol_INDEX_ID].value.netShort) ) + // ParseGameplayEvent(e.protocol, e.gameClient); + // + // if( ProtocolIsGeneral(e.protocol[protocol_INDEX_ID].value.netShort) ) + // ParseGeneralEvent(e.protocol, e.gameClient); + // } + //} + + void GameSession::ParseProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c) { switch (p[protocol_INDEX_ID].value.netShort) { @@ -55,10 +79,10 @@ namespace DanBias c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_RIGHT); } break; - case protocol_Gameplay_PlayerMouseMovement: + case protocol_Gameplay_PlayerLookDir: { - Protocol_PlayerMouse m; m = p; - c->GetPlayer()->Rotate(m.dxMouse, m.dyMouse); + Protocol_PlayerLook m; m = p; + //c->GetPlayer()->Rotate(m.dxMouse, m.dyMouse); } break; case protocol_Gameplay_PlayerChangeWeapon: @@ -67,19 +91,12 @@ namespace DanBias case protocol_Gameplay_ObjectDamage: break; - } - } - - void GameSession::ParseGeneralEvent(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c) - { - switch (p[protocol_INDEX_ID].value.netShort) - { case protocol_General_Status: switch (p[1].value.netInt) { case GameLogic::Protocol_General_Status::States_disconected: printf("Client with ID [%i] dissconnected\n", c->GetClient()->GetID()); - this->RemoveClient(c); + this->Detach(c->GetClient()->GetID()); break; case GameLogic::Protocol_General_Status::States_idle: @@ -101,17 +118,18 @@ namespace DanBias printf("Message recieved from (%i):\t %s\n", c->GetID(), temp.text.c_str()); } break; - } } - void GameSession::ObjectMove(GameLogic::IObjectData* movedObject) { movedObject->GetID(); movedObject->GetOrientation(); - } + void GameSession::ClientEventCallback(NetEvent e) + { + + } }//End namespace DanBias diff --git a/Code/Game/GameServer/Implementation/GameSession_General.cpp b/Code/Game/GameServer/Implementation/GameSession_General.cpp index e68ac8dc..306837d9 100644 --- a/Code/Game/GameServer/Implementation/GameSession_General.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_General.cpp @@ -3,11 +3,11 @@ ///////////////////////////////////////////////////////////////////// #include "..\GameSession.h" #include "..\GameClient.h" -#include "..\GameServer.h" #include #include #include +#define NOMINMAX #include @@ -23,16 +23,18 @@ namespace DanBias :gameInstance(GameAPI::Instance()) { this->owner = 0; - this->box = 0; this->isCreated = false; this->isRunning = false; } GameSession::~GameSession() { - delete this->box; - this->box = 0; + this->worker.Terminate(); + this->clients.Clear(); + this->gameInstance; this->owner = 0; + this->isCreated = false; + this->isRunning = false; } bool GameSession::Create(GameDescription& desc) @@ -45,7 +47,6 @@ namespace DanBias /* standard initialization of some data */ this->clients.Resize(desc.clients.Size()); - this->box = new PostBox(); this->owner = desc.owner; /* Initiate the game instance */ @@ -61,18 +62,13 @@ namespace DanBias return false; } - /* Create a callback object */ - Oyster::Callback::OysterCallback c; - c.value.callbackPostBox = this->box; - c.callbackType = Oyster::Callback::CallbackType_PostBox; - /* Create the players in the game instance */ GameLogic::IPlayerData* p = 0; for (unsigned int i = 0; i < desc.clients.Size(); i++) { if( (p = this->gameInstance.CreatePlayer()) ) { - this->clients[i] = new GameClient(desc.clients[i], p, c); + this->clients[i] = new GameClient(desc.clients[i], p); } else { @@ -81,12 +77,12 @@ namespace DanBias } /* Create the worker thread */ - if(this->worker.Create(this, true, true) != OYSTER_THREAD_ERROR_SUCCESS) + if(this->worker.Create(this, false) != OYSTER_THREAD_ERROR_SUCCESS) return false; this->worker.SetPriority(Oyster::Thread::OYSTER_THREAD_PRIORITY_3); - /* Set some gameinstance data options */ + /* Set some game instance data options */ this->gameInstance.SetSubscription(GameLogic::GameEvent::ObjectEventFunctionType_OnMove, GameSession::ObjectMove); this->isCreated = true; @@ -95,106 +91,39 @@ namespace DanBias void GameSession::Run() { - if(this->isRunning) return; if(this->clients.Size() > 0) { + this->worker.Start(); this->worker.SetPriority(OYSTER_THREAD_PRIORITY_1); this->isRunning = true; } } - bool GameSession::Join(Utility::DynamicMemory::SmartPointer client) + + + bool GameSession::Attach(Utility::DynamicMemory::SmartPointer client) { if(!this->isCreated) return false; - Oyster::Callback::OysterCallback c; - c.value.callbackPostBox = this->box; - c.callbackType = Oyster::Callback::CallbackType_PostBox; - - SmartPointer obj = new GameClient(client, this->gameInstance.CreatePlayer(), c); - InsertClient(obj); - - return true; - } - - void GameSession::CloseSession(bool dissconnectClients) - { - if(dissconnectClients) - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - this->clients[i]->GetClient()->Disconnect(); - } - } - else - { - this->SendToOwner(0); //Send all clients to the current owner - } - this->Clean(); - } - - void GameSession::InsertClient(SmartPointer obj) - { + client->SetOwner(this); + SmartPointer obj = new GameClient(client, this->gameInstance.CreatePlayer()); + for (unsigned int i = 0; i < clients.Size(); i++) { if(!clients[i]) { clients[i] = obj; - return; + return true; } } + clients.Push(obj); + + return true; } - void GameSession::RemoveClient(DanBias::GameClient* obj) - { - for (unsigned int i = 0; i < clients.Size(); i++) - { - if(clients[i] && clients[i]->GetID() == obj->GetID()) - { - clients[i] = 0; - return; - } - } - } - - void GameSession::SendToOwner(DanBias::GameClient* obj) - { - DanBias::NetworkSession *s = GameServer::MainLobbyInstance(); - - if(this->owner) s = this->owner; - - if(obj) - { - s->Attach(obj->ReleaseClient()); - RemoveClient(obj); - } - else - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[i]) - { - s->Attach(this->clients[i]->ReleaseClient()); - RemoveClient(this->clients[i]); - } - } - } - } - - void GameSession::Clean() - { - this->worker.Terminate(); - this->clients.Clear(); - delete this->box; - this->box = 0; - this->gameInstance; - this->owner = 0; - this->isCreated = false; - this->isRunning = false; - } }//End namespace DanBias diff --git a/Code/Game/GameServer/Implementation/GameSession_Logic.cpp b/Code/Game/GameServer/Implementation/GameSession_Logic.cpp deleted file mode 100644 index 1388f6fe..00000000 --- a/Code/Game/GameServer/Implementation/GameSession_Logic.cpp +++ /dev/null @@ -1,74 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#include "..\GameSession.h" -#include "..\GameClient.h" - -#include -#include -#include -#include -#include - -#define DELTA_TIME_20 0.05f -#define DELTA_TIME_24 0.04166666666666666666666666666667f -#define DELTA_TIME_30 0.03333333333333333333333333333333f -#define DELTA_TIME_60 0.01666666666666666666666666666667f -#define DELTA_TIME_120 0.00833333333333333333333333333333f - -using namespace Utility::DynamicMemory; -using namespace Oyster; -using namespace Oyster::Network; -using namespace Oyster::Thread; -using namespace GameLogic; - -namespace DanBias -{ - bool GameSession::DoWork( ) - { - if(this->isRunning) - { - if(GetAsyncKeyState(VK_UP)) - { - Protocol_General_Status p(Protocol_General_Status::States_ready); - Send(p.GetProtocol()); - Sleep(100); - } - if(GetAsyncKeyState(VK_DOWN)) - { - Oyster::Math::Float4x4 world = Oyster::Math::Matrix::identity; - Protocol_ObjectCreate p(world, 2, "../Content/crate"); - Send(p.GetProtocol()); - Sleep(100); - } - - double dt = this->timer.getElapsedSeconds(); - gameInstance.SetFrameTimeLength((float)dt); - - if(dt >= DELTA_TIME_20) - { - this->ParseEvents(); - - this->gameInstance.NewFrame(); - - this->UpdateGameObjects(); - - this->timer.reset(); - } - } - - return this->isRunning; - } - - void GameSession::UpdateGameObjects() - { - if(clients.Size() >= 1 && clients[0]) - { - Oyster::Math::Float4x4 world = this->clients[0]->GetPlayer()->GetOrientation(); - Protocol_ObjectPosition p(world, 1); - Send(p.GetProtocol()); - } - } - -}//End namespace DanBias - diff --git a/Code/Game/GameServer/Implementation/GameSession_Network.cpp b/Code/Game/GameServer/Implementation/GameSession_Network.cpp deleted file mode 100644 index df546b35..00000000 --- a/Code/Game/GameServer/Implementation/GameSession_Network.cpp +++ /dev/null @@ -1,29 +0,0 @@ -///////////////////////////////////////////////////////////////////// -// Created by [Dennis Andersen] [2013] -///////////////////////////////////////////////////////////////////// -#include "..\GameSession.h" -#include "..\GameClient.h" - -#include -#include -#include -#include - -using namespace Utility::DynamicMemory; -using namespace Oyster; -using namespace Oyster::Network; -using namespace Oyster::Thread; -using namespace GameLogic; - -namespace DanBias -{ - void GameSession::Send(Oyster::Network::CustomNetProtocol* p) - { - for (unsigned int i = 0; i < this->clients.Size(); i++) - { - if(this->clients[i] && this->clients[i]->GetClient()) - this->clients[i]->GetClient()->Send(p); - } - } -}//End namespace DanBias - diff --git a/Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp b/Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp deleted file mode 100644 index 23195087..00000000 --- a/Code/Game/GameServer/Implementation/LobbyGeneralProtocolParser.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "GameLobby.h" -#include "NetworkSession.h" - -using namespace DanBias; -using namespace GameLogic; - -void GameLobby::ParseGeneralProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::NetworkSession* c) -{ - switch (p[0].value.netShort) - { - case protocol_General_Status: - { - GeneralStatus(GameLogic::Protocol_General_Status(p), c); - } break; - case protocol_General_Text: - { - GeneralText(GameLogic::Protocol_General_Text(p), c); - } break; - } -} - -////////////////////////////////////////////////////// - -void GameLobby::GeneralStatus(GameLogic::Protocol_General_Status& p, DanBias::NetworkSession* c) -{ - switch (p.status) - { - case Protocol_General_Status::States_ready: - { - - } - case Protocol_General_Status::States_idle: - { - - } - case Protocol_General_Status::States_leave: - { - - } - case Protocol_General_Status::States_disconected: - { - Detach(c)->Disconnect(); - } - } -} - -void GameLobby::GeneralText(GameLogic::Protocol_General_Text& p, DanBias::NetworkSession* c) -{ - -} - - diff --git a/Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp b/Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp deleted file mode 100644 index 4063db18..00000000 --- a/Code/Game/GameServer/Implementation/LobbyProtocolParser.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "..\GameLobby.h" - -using namespace DanBias; - -void GameLobby::ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::NetworkSession* c) -{ - switch (p[0].value.netShort) - { - case protocol_Lobby_Start: - LobbyStartGame(GameLogic::Protocol_LobbyStartGame(p), c); - break; - - case protocol_Lobby_Refresh: - LobbyRefresh(GameLogic::Protocol_LobbyRefresh(p), c); - break; - case protocol_Lobby_Login: - { - LobbyLogin(GameLogic::Protocol_LobbyLogin(p), c); - } break; - case protocol_Lobby_Join: - { - LobbyJoin(GameLogic::Protocol_LobbyJoin(p), c); - } break; - } -} - -void GameLobby::LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, DanBias::NetworkSession* c) -{ - -} - -void GameLobby::LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, DanBias::NetworkSession* c) -{ - double now = this->timer.getElapsedSeconds() + c->lastPoll; - - if(now > this->refreshFrequency) - { - c->lastPoll = (float)now; - } -} - -void GameLobby::LobbyLogin(GameLogic::Protocol_LobbyLogin& p, DanBias::NetworkSession* c) -{ - -} - -void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyJoin& p, DanBias::NetworkSession* c) -{ - for (unsigned int i = 0; i < this->gameLobby.Size(); i++) - { - if (this->gameLobby[i]->GetID() == p.value) - { - this->gameLobby[i]->Attach(Detach(c)); - return; - } - } -} - diff --git a/Code/Game/aDanBiasGameLauncher/Launcher.cpp b/Code/Game/aDanBiasGameLauncher/Launcher.cpp index 7e533171..17e3c8bc 100644 --- a/Code/Game/aDanBiasGameLauncher/Launcher.cpp +++ b/Code/Game/aDanBiasGameLauncher/Launcher.cpp @@ -4,18 +4,19 @@ #include #include "DanBiasGame.h" -#include +#include #include void ServerFnc() { - - if( DanBias::DanBiasServerAPI::Initiate() == DanBias::DanBiasServerReturn_Sucess) + DanBias::GameServerAPI::GameInitDesc desc; + desc.listenPort = 15151; + if( DanBias::GameServerAPI::Create(desc) == DanBias::DanBiasServerReturn_Sucess) { - DanBias::DanBiasServerAPI::Run(); - DanBias::DanBiasServerAPI::Release(); + DanBias::GameServerAPI::Start(); + DanBias::GameServerAPI::Terminate(); } Sleep(100); } diff --git a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj index 5f33ada6..303d075f 100644 --- a/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj +++ b/Code/Game/aDanBiasGameLauncher/aDanBiasGameLauncher.vcxproj @@ -71,7 +71,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\GameServer;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -79,7 +79,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D - $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\GameServer;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -87,7 +87,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\GameServer;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -95,7 +95,7 @@ $(SolutionDir)..\Bin\Executable\ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) - $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\DanBiasServer;$(IncludePath) + $(SolutionDir)..\External\Include\;C:\Program Files %28x86%29\Visual Leak Detector\include;C:\Users\Dennis\Desktop\Skola\DV1477 - Stort spelutvecklingsprojekt\DanBias\Code\Game\GameServer;$(IncludePath) $(OutDir)..\DLL\;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) @@ -110,7 +110,7 @@ Windows true - DanBiasGame_$(PlatformShortName)D.dll;DanBiasServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName)D.dll;GameServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) DanBiasGame_$(PlatformShortName)D.lib;DanBiasServer_$(PlatformShortName)D.lib;%(AdditionalDependencies) @@ -126,7 +126,7 @@ Windows true - DanBiasGame_$(PlatformShortName)D.dll;DanBiasServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName)D.dll;GameServer_$(PlatformShortName)D.dll;%(DelayLoadDLLs) DanBiasGame_$(PlatformShortName)D.lib;%(AdditionalDependencies) @@ -146,7 +146,7 @@ true true true - DanBiasGame_$(PlatformShortName).dll;DanBiasServer_$(PlatformShortName).dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName).dll;GameServer_$(PlatformShortName).dll;%(DelayLoadDLLs) DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) @@ -166,7 +166,7 @@ true true true - DanBiasGame_$(PlatformShortName).dll;DanBiasServer_$(PlatformShortName).dll;%(DelayLoadDLLs) + DanBiasGame_$(PlatformShortName).dll;GameServer_$(PlatformShortName).dll;%(DelayLoadDLLs) DanBiasGame_$(PlatformShortName).lib;%(AdditionalDependencies) @@ -177,6 +177,9 @@ {2a1bc987-af42-4500-802d-89cd32fc1309} + + {143bd516-20a1-4890-a3e4-f8bfd02220e7} + diff --git a/Code/Misc/Thread/OysterThread_Impl.cpp b/Code/Misc/Thread/OysterThread_Impl.cpp index a09574df..45807e84 100644 --- a/Code/Misc/Thread/OysterThread_Impl.cpp +++ b/Code/Misc/Thread/OysterThread_Impl.cpp @@ -118,15 +118,17 @@ using namespace Utility::DynamicMemory; { SmartPointer data; - PrivateData(){} + PrivateData() + { + data = new RefData(); + } ~PrivateData() { - data.Release(); + data = 0; } OYSTER_THREAD_ERROR Create(ThreadFunction fnc, OwnerContainer worker, bool start, bool detach) { - if(data) return OYSTER_THREAD_ERROR_ThreadAlreadyCreated; - data = new RefData(); + if(!data) data = new RefData(); return data->Create(fnc, worker, start, detach); } OYSTER_THREAD_ERROR Terminate() @@ -205,9 +207,8 @@ using namespace Utility::DynamicMemory; OysterThread::OysterThread() -{ - this->privateData = new PrivateData(); -} + :privateData(0) +{ } OysterThread::OysterThread(const OysterThread& original) { this->privateData = new PrivateData(*original.privateData); @@ -227,9 +228,8 @@ OysterThread::~OysterThread() OYSTER_THREAD_ERROR OysterThread::Create(IThreadObject* worker, bool start, bool detach) { - if(!this->privateData) this->privateData = new PrivateData(); + if(!this->privateData) this->privateData = new PrivateData(); - if(this->privateData->data->isCreated) return OYSTER_THREAD_ERROR_ThreadAlreadyCreated; OwnerContainer c; c.type = Oyster::Callback::CallbackType_Object; c.value = worker; @@ -237,7 +237,7 @@ OYSTER_THREAD_ERROR OysterThread::Create(IThreadObject* worker, bool start, bool } OYSTER_THREAD_ERROR OysterThread::Create(ThreadFnc worker, bool start, bool detach) { - if(!this->privateData) this->privateData = new PrivateData(); + if(!this->privateData) this->privateData = new PrivateData(); OwnerContainer c; c.type = Oyster::Callback::CallbackType_Function; diff --git a/Code/Misc/WinTimer.h b/Code/Misc/WinTimer.h index 73e9091f..60d7a28d 100644 --- a/Code/Misc/WinTimer.h +++ b/Code/Misc/WinTimer.h @@ -7,6 +7,7 @@ #ifndef WINTIMER_H #define WINTIMER_H +#define NOMINMAX #include namespace Utility diff --git a/Code/Network/NetworkAPI/CustomNetProtocol.cpp b/Code/Network/NetworkAPI/CustomNetProtocol.cpp index 6d4ea1bf..2ac6fcf5 100644 --- a/Code/Network/NetworkAPI/CustomNetProtocol.cpp +++ b/Code/Network/NetworkAPI/CustomNetProtocol.cpp @@ -4,36 +4,27 @@ #include "CustomNetProtocol.h" #include #include "Translator.h" +#include "Utilities.h" using namespace Oyster::Network; +using namespace Utility::DynamicMemory; struct CustomNetProtocol::PrivateData { - std::map attributes; + std::map attributes; //...Im an idiot + Utility::DynamicMemory::ReferenceCount *c; PrivateData() - { } - PrivateData( const CustomNetProtocol::PrivateData& o) - { - for (auto i = o.attributes.begin(); i != o.attributes.end(); i++) - { - if(i->second.type == NetAttributeType_CharArray) - { - size_t size = strlen(i->second.value.netCharPtr); - if(size == 0) continue; - - attributes[i->first].value.netCharPtr = new char[size + 1]; - memcpy(&attributes[i->first].value.netCharPtr[0], &i->second.value.netCharPtr[0], size + 1); - attributes[i->first].type = NetAttributeType_CharArray; - } - else - { - attributes[i->first] = i->second; - } - } + { + //this->attributes = new std::map(); + this->c = new ReferenceCount(); + c->Incref(); } + ~PrivateData() { + delete c; + c = 0; for (auto i = attributes.begin(); i != attributes.end(); i++) { RemoveAttribute(i->first); @@ -49,7 +40,6 @@ struct CustomNetProtocol::PrivateData { case NetAttributeType_CharArray: delete [] i->second.value.netCharPtr; - //i->second.value.netCharPtr = 0; break; } } @@ -64,17 +54,40 @@ CustomNetProtocol::CustomNetProtocol() } CustomNetProtocol::CustomNetProtocol(const CustomNetProtocol& o) { - this->privateData = new PrivateData(*o.privateData); + this->privateData = o.privateData; + if(this->privateData) + { + this->privateData->c = o.privateData->c; + this->privateData->c->Incref(); + } } const CustomNetProtocol& CustomNetProtocol::operator=(const CustomNetProtocol& o) { - delete this->privateData; - this->privateData = new PrivateData(*o.privateData); + if(this->privateData && this->privateData->c) + { + if(this->privateData->c->Decref() == 0) + { + delete this->privateData; + } + } + + this->privateData = o.privateData; + if(this->privateData) + { + this->privateData->c = o.privateData->c; + this->privateData->c->Incref(); + } return *this; } CustomNetProtocol::~CustomNetProtocol() { - delete this->privateData; + if(this->privateData && this->privateData->c) + { + if(this->privateData->c->Decref() == 0) + { + delete this->privateData; + } + } } NetAttributeContainer& CustomNetProtocol::operator[](int ID) { diff --git a/Code/Network/NetworkAPI/NetworkAPI.vcxproj b/Code/Network/NetworkAPI/NetworkAPI.vcxproj index b4b8a31f..13ab5af9 100644 --- a/Code/Network/NetworkAPI/NetworkAPI.vcxproj +++ b/Code/Network/NetworkAPI/NetworkAPI.vcxproj @@ -88,16 +88,16 @@ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(SolutionDir)..\Bin\DLL\ $(ProjectName)_$(PlatformShortName) - C:\Program Files %28x86%29\Visual Leak Detector\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath); - C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSDK_LibraryPath_x86); + C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) + C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) false $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(SolutionDir)..\Bin\DLL\ $(ProjectName)_$(PlatformShortName) - C:\Program Files %28x86%29\Visual Leak Detector\lib\Win64;$(VCInstallDir)lib\amd64;$(VCInstallDir)atlmfc\lib\amd64;$(WindowsSDK_LibraryPath_x64); - C:\Program Files %28x86%29\Visual Leak Detector\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath); + C:\Program Files %28x86%29\Visual Leak Detector\lib\Win64;$(LibraryPath) + C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath) @@ -107,6 +107,7 @@ Disabled NETWORKAPI_EXPORT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(SolutionDir)Misc\ Console @@ -121,6 +122,7 @@ Disabled NETWORKAPI_EXPORT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(SolutionDir)Misc\ Console @@ -137,6 +139,7 @@ true NETWORKAPI_EXPORT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(SolutionDir)Misc\ Console @@ -155,6 +158,7 @@ true NETWORKAPI_EXPORT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(SolutionDir)Misc\ Console diff --git a/Code/Network/NetworkAPI/NetworkClient.cpp b/Code/Network/NetworkAPI/NetworkClient.cpp index 71614f77..f5d7c643 100644 --- a/Code/Network/NetworkAPI/NetworkClient.cpp +++ b/Code/Network/NetworkAPI/NetworkClient.cpp @@ -26,8 +26,9 @@ using namespace Utility::Container; PrivateData *************************************/ typedef NetworkClient::ClientEventArgs CEA; -struct NetDataContainer : public IThreadObject -{ //This struct is contained within a smart pointer. To avoide dependencies in link its implemented here.. + +struct NetworkClient::PrivateData : public IThreadObject +{ NetworkSession *owner; NetworkClient *parent; Connection connection; @@ -43,19 +44,17 @@ struct NetDataContainer : public IThreadObject static unsigned int currID; const unsigned int ID; - NetDataContainer() + PrivateData() : ID(currID++) , parent(0) , owner(0) { InitWinSock(); - this->thread.Create(this, true); + this->thread.Create(this, false); this->thread.SetPriority(Oyster::Thread::OYSTER_THREAD_PRIORITY_1); } - NetDataContainer(const NetDataContainer& obj) - :ID(obj.ID) { } - ~NetDataContainer() + ~PrivateData() { ShutdownWinSock(); this->connection.Disconnect(); @@ -73,7 +72,6 @@ struct NetDataContainer : public IThreadObject return true; } - int Send() { int errorCode = 0; @@ -96,7 +94,6 @@ struct NetDataContainer : public IThreadObject return errorCode; } - int Recv() { int errorCode = -1; @@ -119,33 +116,19 @@ struct NetDataContainer : public IThreadObject this->recieveQueue.Push(e); } } - else - { - CEA parg; - parg.type = CEA::EventType_ProtocolFailedToRecieve; - parg.data.nothing = 0; - NetEvent e = { this->parent, parg }; - this->recieveQueue.Push(e); - } + //else + //{ + // CEA parg; + // parg.type = CEA::EventType_ProtocolFailedToRecieve; + // parg.data.nothing = 0; + // NetEvent e = { this->parent, parg }; + // this->recieveQueue.Push(e); + //} return errorCode; } }; - - -struct NetworkClient::PrivateData -{ - SmartPointer dat; - -public: - PrivateData() - { this->dat = new NetDataContainer(); } - PrivateData(const PrivateData& obj) - { this->dat = obj.dat; } - ~PrivateData() - { this->dat = 0; } -}; -unsigned int NetDataContainer::currID = 0; +unsigned int NetworkClient::PrivateData::currID = 0; /************************************* NetworkClient @@ -155,21 +138,6 @@ NetworkClient::NetworkClient() : privateData(0) { } -NetworkClient::NetworkClient(const NetworkClient& obj) -{ - if(obj.privateData) this->privateData = new PrivateData(*obj.privateData); - else this->privateData = 0; -} - -NetworkClient& NetworkClient::operator =(const NetworkClient& obj) -{ - delete privateData; - this->privateData = 0; - if(obj.privateData) this->privateData = new PrivateData(*obj.privateData); - - return *this; -} - NetworkClient::~NetworkClient() { if(this->privateData) @@ -181,22 +149,25 @@ NetworkClient::~NetworkClient() bool NetworkClient::operator ==(const NetworkClient& obj) { - return (this->privateData->dat->ID == obj.privateData->dat->ID); + return (this->privateData->ID == obj.privateData->ID); } bool NetworkClient::operator ==(const int& ID) { - return this->privateData->dat->ID == ID; + return this->privateData->ID == ID; } -void NetworkClient::ProcessMessages() +void NetworkClient::Update() { - while (!this->privateData->dat->recieveQueue.IsEmpty()) + while (!this->privateData->recieveQueue.IsEmpty()) { - if(this->privateData->dat->owner) - { - this->privateData->dat->owner->ClientEventCallback(this->privateData->dat->recieveQueue.Pop()); - } + NetEvent temp = this->privateData->recieveQueue.Pop(); + + this->DataRecieved(temp); + + //--------- Deprecate --------- + this->NetworkCallback(temp.args.data.protocol); + //------------------------------ } } @@ -206,13 +177,16 @@ bool NetworkClient::Connect(int socket) if(this->privateData) return false; if(!this->privateData) this->privateData = new PrivateData(); - int result = this->privateData->dat->connection.Connect(socket, true); + int result = this->privateData->connection.Connect(socket, false); //Connect has succeeded - if(result == 0) return true; + if(result != 0) return false; + + this->privateData->parent = this; + this->privateData->thread.Start(); //Connect has failed - return false; + return true; } bool NetworkClient::Connect(unsigned short port, const char serverIP[]) @@ -221,46 +195,59 @@ bool NetworkClient::Connect(unsigned short port, const char serverIP[]) if(this->privateData) return false; if(!this->privateData) this->privateData = new PrivateData(); - int result = this->privateData->dat->connection.Connect(port, serverIP, false); + int result = this->privateData->connection.Connect(port, serverIP, false); //Connect has succeeded - if(result == 0) return true; + if(result != 0) return false; + + this->privateData->parent = this; + this->privateData->thread.Start(); //Connect has failed - return false; + return true; } void NetworkClient::Disconnect() { - privateData->dat->connection.Disconnect(); - privateData->dat->thread.Terminate(); + privateData->connection.Disconnect(); + privateData->thread.Terminate(); } void NetworkClient::Send(CustomProtocolObject& protocol) { - this->privateData->dat->sendQueue.Push(*protocol.GetProtocol()); + this->privateData->sendQueue.Push(*protocol.GetProtocol()); } void NetworkClient::Send(CustomNetProtocol* protocol) { - this->privateData->dat->sendQueue.Push(*protocol); + this->privateData->sendQueue.Push(*protocol); } void NetworkClient::SetOwner(NetworkSession* owner) { - this->privateData->dat->owner = owner; + this->privateData->owner = owner; } bool NetworkClient::IsConnected() { if(!this->privateData) return false; - return privateData->dat->connection.IsConnected(); + return privateData->connection.IsConnected(); } int NetworkClient::GetID() const { - return this->privateData->dat->ID; + return this->privateData->ID; } +void NetworkClient::DataRecieved(NetEvent e) +{ + if(this->privateData->owner) + { + this->privateData->owner->ClientEventCallback(e); + } +} + +void NetworkClient::NetworkCallback(Oyster::Network::CustomNetProtocol& p) +{} diff --git a/Code/Network/NetworkAPI/NetworkClient.h b/Code/Network/NetworkAPI/NetworkClient.h index 39350537..869a5100 100644 --- a/Code/Network/NetworkAPI/NetworkClient.h +++ b/Code/Network/NetworkAPI/NetworkClient.h @@ -41,8 +41,6 @@ namespace Oyster public: NetworkClient(); - NetworkClient(const NetworkClient& obj); - NetworkClient& operator =(const NetworkClient& obj); virtual ~NetworkClient(); bool operator ==(const NetworkClient& obj); @@ -51,7 +49,7 @@ namespace Oyster /** * */ - void ProcessMessages(); + void Update(); /** * @@ -93,7 +91,21 @@ namespace Oyster */ int GetID() const; + /** + * + */ + virtual void DataRecieved(NetEvent e); + + /** ! Deprecate ! + * Do not use this furthermore, instead use void DataRecieved(NetEvent e); + * @see DataRecieved + */ + virtual void NetworkCallback(Oyster::Network::CustomNetProtocol& p); + private: + NetworkClient(const NetworkClient& obj); + NetworkClient& operator =(const NetworkClient& obj); + struct PrivateData; PrivateData* privateData; }; diff --git a/Code/Network/NetworkAPI/NetworkServer.cpp b/Code/Network/NetworkAPI/NetworkServer.cpp index de341f81..9d6db207 100644 --- a/Code/Network/NetworkAPI/NetworkServer.cpp +++ b/Code/Network/NetworkAPI/NetworkServer.cpp @@ -9,11 +9,14 @@ #include "../NetworkDependencies/PostBox.h" #include "../NetworkDependencies/WinsockFunctions.h" -#include "../../Misc/Utilities.h" -#include "../../Misc/Thread/OysterThread.h" +#include "Utilities.h" +#include "Thread/OysterThread.h" + +#ifndef _DEBUG +#include +#endif using namespace Oyster::Network; -using namespace ::Server; using namespace Utility::DynamicMemory; using namespace Oyster::Thread; @@ -21,6 +24,41 @@ using namespace Oyster::Thread; PrivateData *************************************/ +void Broadcast() +{ + char pkt[4]; + size_t pkt_length = 4; + sockaddr_in dest; + sockaddr_in local; + WSAData data; + WSAStartup( MAKEWORD( 2, 2 ), &data ); + + local.sin_family = AF_INET; + local.sin_addr.s_addr = inet_addr( "127.0.0.1" ); + local.sin_port = 15151; // choose any + + dest.sin_family = AF_INET; + dest.sin_port = htons( 15151 ); + + // create the socket + SOCKET s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + // bind to the local address + bind( s, (sockaddr *)&local, sizeof(local) ); + + std::string addr; + for (int i = 0; i < 256; i++) + { + addr = "192.168.0."; + char buff[5]; + _itoa_s<5>(i, buff, 10); + + addr.append(buff); + dest.sin_addr.s_addr = inet_addr( addr.c_str() ); + // send the pkt + int ret = sendto( s, pkt, pkt_length, 0, (sockaddr *)&dest, sizeof(dest) ); + } +} + struct NetworkServer::PrivateData : public IThreadObject { public: @@ -30,6 +68,7 @@ public: , isInitiated(0) , isReleased(0) , isRunning(0) + , port(-1) { } ~PrivateData() { } @@ -37,11 +76,11 @@ public: bool DoWork(); public: - IListener* listener; + Listener* listener; PostBox postBox; //Postbox for new clients OysterThread thread; //Server thread NetworkSession *mainSession; - Utility::Container::ThreadSafeQueue clientQueue; + Utility::Container::ThreadSafeQueue> clientQueue; bool isInitiated; bool isReleased; @@ -51,6 +90,8 @@ public: bool NetworkServer::PrivateData::DoWork() { + //Broadcast(); + /** Check for new clients **/ if(postBox.IsFull()) { @@ -60,12 +101,11 @@ bool NetworkServer::PrivateData::DoWork() { //Something went wrong somewhere... do we care? } - - Oyster::Network::NetworkClient client; - client.Connect(clientSocketNum); - if(this->mainSession) - this->clientQueue.Push(client); + SmartPointer client(new NetworkClient()); + client->Connect(clientSocketNum); + + this->clientQueue.Push(client); } return true; @@ -101,6 +141,7 @@ NetworkServer::~NetworkServer() NetworkServer::ServerReturnCode NetworkServer::Init(const int& port, NetworkSession const* mainSession) { + this->privateData->mainSession = const_cast(mainSession); //Check if it's a valid port if(port == 0 || port == -1) { @@ -117,7 +158,7 @@ NetworkServer::ServerReturnCode NetworkServer::Init(const int& port, NetworkSess //Initiate listener this->privateData->listener = new Listener(&this->privateData->postBox); - if(!((Listener*)this->privateData->listener)->Init(port, false)) + if(!this->privateData->listener->Init(port, false)) { return NetworkServer::ServerReturnCode_Error; } @@ -129,14 +170,13 @@ NetworkServer::ServerReturnCode NetworkServer::Init(const int& port, NetworkSess this->privateData->isInitiated = true; this->privateData->isReleased = false; - this->privateData->mainSession = 0; return NetworkServer::ServerReturnCode_Sucess; } NetworkServer::ServerReturnCode NetworkServer::Start() { //Start listener - if(!((Listener*)this->privateData->listener)->Start()) + if(!this->privateData->listener->Start()) { return NetworkServer::ServerReturnCode_Error; } @@ -154,7 +194,7 @@ void NetworkServer::Stop() { if(this->privateData->listener) { - ((Listener*)this->privateData->listener)->Stop(); + this->privateData->listener->Stop(); } this->privateData->thread.Stop(); @@ -164,6 +204,10 @@ void NetworkServer::Stop() void NetworkServer::Shutdown() { + if(this->privateData->mainSession) + { + this->privateData->mainSession->CloseSession(true); + } if(this->privateData->listener) { this->privateData->listener->Shutdown(); @@ -180,12 +224,23 @@ void NetworkServer::Shutdown() this->privateData->isReleased = true; } -void NetworkServer::ProcessConnectedClients() +int NetworkServer::ProcessConnectedClients() { + int c = 0; while(!this->privateData->clientQueue.IsEmpty()) { - if(this->privateData->mainSession) this->privateData->mainSession->Attach(this->privateData->clientQueue.Pop()); + if(this->privateData->mainSession) + { + this->privateData->mainSession->ClientConnectedEvent(this->privateData->clientQueue.Pop()); + c++; + } + else + { + //Clients have nowhere to go? + this->privateData->clientQueue.Pop()->Disconnect(); + } } + return c; } void NetworkServer::SetSession(NetworkSession const* mainSession) @@ -198,7 +253,7 @@ NetworkSession const* NetworkServer::GetMainSession() return this->privateData->mainSession; } -NetworkSession const* NetworkServer::ReleaseMainSessionSession() +NetworkSession const* NetworkServer::ReleaseMainSession() { NetworkSession const * temp; temp = this->privateData->mainSession; @@ -211,7 +266,22 @@ bool NetworkServer::IsStarted() const return this->privateData->isRunning; } - +std::string NetworkServer::GetLanAddress() +{ + std::string szLocalIP; + char szHostName[255]; + struct hostent *host_entry; + + gethostname(szHostName, 255); + + host_entry = gethostbyname(szHostName); + char* temp = inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); + + char buff[255]; + strcpy_s(buff, temp); + szLocalIP = buff; + return szLocalIP; +} diff --git a/Code/Network/NetworkAPI/NetworkServer.h b/Code/Network/NetworkAPI/NetworkServer.h index c3385f86..ca33ebbe 100644 --- a/Code/Network/NetworkAPI/NetworkServer.h +++ b/Code/Network/NetworkAPI/NetworkServer.h @@ -57,7 +57,7 @@ namespace Oyster /** Parses asynchronous connected clients. */ - void ProcessConnectedClients(); + int ProcessConnectedClients(); /** Set the main session connected clients will enter when connected to server. * @param mainSession The session to connect as main server session. @@ -72,13 +72,17 @@ namespace Oyster /** Sets the main session to NULL and returns it * @return Returns the main session */ - NetworkSession const* ReleaseMainSessionSession(); + NetworkSession const* ReleaseMainSession(); /** * */ bool IsStarted() const; + /** + * + */ + std::string GetLanAddress(); private: struct PrivateData; diff --git a/Code/Network/NetworkAPI/NetworkSession.cpp b/Code/Network/NetworkAPI/NetworkSession.cpp index e026413e..d1fc7fec 100644 --- a/Code/Network/NetworkAPI/NetworkSession.cpp +++ b/Code/Network/NetworkAPI/NetworkSession.cpp @@ -10,13 +10,13 @@ #include #include - +using namespace Utility::DynamicMemory; using namespace Oyster::Network; struct NetworkSession::PrivateSessionData { - Utility::DynamicMemory::DynamicArray clients; + Utility::DynamicMemory::DynamicArray clients; NetworkClient::ClientEventFunction messageCallback; std::mutex clientListLock; NetworkSession* owner; //Where clients end up when session is closed. @@ -32,7 +32,7 @@ struct NetworkSession::PrivateSessionData NetworkSession::NetworkSession() - : data(0) + : data(new PrivateSessionData()) {} NetworkSession::NetworkSession(const NetworkSession& orig) { @@ -59,24 +59,26 @@ NetworkSession::~NetworkSession() this->data->clients.Clear(); this->data->clientCount = 0; this->data->messageCallback = 0; + delete this->data; + this->data = 0; } void NetworkSession::ProcessClients() { for (unsigned int i = 0; i < this->data->clients.Size(); i++) { - this->data->clients[i].ProcessMessages(); + if(this->data->clients[i]) this->data->clients[i]->Update(); } } -bool NetworkSession::Attach(NetworkClient client) +bool NetworkSession::Attach(NetClient client) { this->data->clientListLock.lock(); int k = -1; for (unsigned int i = 0; (k == -1) && i < this->data->clients.Size(); i++) { - if(!this->data->clients[i].IsConnected()) //TODO: Dont check connection status, check more general status.. + if(!this->data->clients[i]->IsConnected()) //TODO: Dont check connection status, check more general status.. k = i; } @@ -91,23 +93,44 @@ bool NetworkSession::Attach(NetworkClient client) this->data->clientCount++; + client->SetOwner(this); this->data->clientListLock.unlock(); return true; } -NetworkClient NetworkSession::Detach(const NetworkClient& client) +void NetworkSession::Detach() { - NetworkClient val; + if(this->data->owner) + { + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + this->data->owner->Attach(this->data->clients[i]); + this->data->clients[i] = 0; + } + } + else + { + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + this->data->clients[i]->Disconnect(); + this->data->clients[i] = 0; + } + } +} + +NetClient NetworkSession::Detach(const NetworkClient* client) +{ + NetClient val; this->data->clientListLock.lock(); for (unsigned int i = 0; i < this->data->clients.Size(); i++) { - if(this->data->clients[0].GetID() == client.GetID()) + if(this->data->clients[i] && this->data->clients[0]->GetID() == client->GetID()) { val = this->data->clients[i]; - this->data->clients[i] = NetworkClient(); + this->data->clients[i] = 0; this->data->clientCount--; } } @@ -117,18 +140,18 @@ NetworkClient NetworkSession::Detach(const NetworkClient& client) return val; } -NetworkClient NetworkSession::Detach(short ID) +NetClient NetworkSession::Detach(short ID) { - NetworkClient val; + NetClient val; this->data->clientListLock.lock(); for (unsigned int i = 0; i < this->data->clients.Size(); i++) { - if(this->data->clients[0].GetID() == ID) + if(this->data->clients[i] && this->data->clients[0]->GetID() == ID) { val = this->data->clients[i]; - this->data->clients[i] = NetworkClient(); + this->data->clients[i] = 0; this->data->clientCount--; } } @@ -143,8 +166,11 @@ bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol) bool returnValue = false; for (unsigned int i = 0; i < this->data->clients.Size(); i++) { - this->data->clients[i].Send(&protocol); - returnValue = true; + if(this->data->clients[i]) + { + this->data->clients[i]->Send(&protocol); + returnValue = true; + } } return returnValue; @@ -154,9 +180,9 @@ bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol, int ID) { for (unsigned int i = 0; i < this->data->clients.Size(); i++) { - if(this->data->clients[i].GetID() == ID) + if(this->data->clients[i] && this->data->clients[i]->GetID() == ID) { - this->data->clients[i].Send(&protocol); + this->data->clients[i]->Send(&protocol); return true; } } @@ -169,9 +195,12 @@ void NetworkSession::CloseSession(bool dissconnectClients) for (unsigned int i = 0; i < this->data->clients.Size(); i++) { - if(dissconnectClients) this->data->clients[i].Disconnect(); - else if(this->data->owner) this->data->owner->Attach(this->data->clients[i]); - else this->data->clients[i].Disconnect(); + if(this->data->clients[i]) + { + if(dissconnectClients) this->data->clients[i]->Disconnect(); + else if(this->data->owner) this->data->owner->Attach(this->data->clients[i]); + else this->data->clients[i]->Disconnect(); //Idiot check, clients have to go somewhere.. + } } this->data->clients.Clear(); @@ -180,5 +209,23 @@ void NetworkSession::CloseSession(bool dissconnectClients) this->data->clientListLock.unlock(); } +void NetworkSession::SetOwner(NetworkSession* owner) +{ + this->data->owner = owner; +} +int NetworkSession::GetClientCount() const +{ + int c = 0; + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + if(this->data->clients[i]) c++; + } + return c; +} + +void NetworkSession::ClientConnectedEvent(NetClient client) +{ + this->Attach(client); +} diff --git a/Code/Network/NetworkAPI/NetworkSession.h b/Code/Network/NetworkAPI/NetworkSession.h index 7c50afd0..b8287b36 100644 --- a/Code/Network/NetworkAPI/NetworkSession.h +++ b/Code/Network/NetworkAPI/NetworkSession.h @@ -10,11 +10,13 @@ #include "NetworkAPI_Preprocessor.h" #include "NetworkServerEventStruct.h" #include "NetworkClient.h" +#include "Utilities.h" namespace Oyster { namespace Network { + typedef Utility::DynamicMemory::SmartPointer NetClient; class NET_API_EXPORT NetworkSession { public: @@ -30,49 +32,60 @@ namespace Oyster /** * */ - bool Attach(NetworkClient client); + virtual bool Attach(NetClient client); + /** + * Detaches all clients and sends them to owner. + * If no owner is set the clients is disconnected. + */ + virtual void Detach(); + /** * */ - NetworkClient Detach(const NetworkClient& client); + virtual NetClient Detach(const NetworkClient* client); /** * */ - NetworkClient Detach(short ID); + virtual NetClient Detach(short ID); /** Send a message to all clients in this session * @param message The message */ - bool Send(Oyster::Network::CustomNetProtocol& message); + virtual bool Send(Oyster::Network::CustomNetProtocol& message); /** Send a message to a specific client in this session * @param message The message */ - bool Send(Oyster::Network::CustomNetProtocol& protocol, int ID); + virtual bool Send(Oyster::Network::CustomNetProtocol& protocol, int ID); /** * */ - void CloseSession( bool dissconnectClients = false ); + virtual void CloseSession( bool dissconnectClients = false ); /** * Set the owner that clients will be returned to. * @param owner If owner is NULL, clients will be disconnected when session is over. */ - void SetOwner(NetworkSession* owner); + virtual void SetOwner(NetworkSession* owner); + + /** Get the number of clients active in this session + * @return The client count + */ + int GetClientCount() const; + + /** + * + */ + virtual void ClientConnectedEvent(NetClient client); /** * */ virtual void ClientEventCallback(NetEvent e) = 0; - /** - * - */ - virtual void ClientConnectedEvent(NetEvent e) = 0; - private: struct PrivateSessionData; PrivateSessionData* data; diff --git a/Code/Network/NetworkAPI/Translator.cpp b/Code/Network/NetworkAPI/Translator.cpp index b1196e88..9b855d47 100644 --- a/Code/Network/NetworkAPI/Translator.cpp +++ b/Code/Network/NetworkAPI/Translator.cpp @@ -18,6 +18,7 @@ using namespace std; struct MyCastingStruct { std::map attributes; + Utility::DynamicMemory::ReferenceCount *c; }; // TODO: Check if the package has been packed correctly. diff --git a/Code/Network/NetworkDependencies/Connection.cpp b/Code/Network/NetworkDependencies/Connection.cpp index c79d5fe6..27aa296a 100644 --- a/Code/Network/NetworkDependencies/Connection.cpp +++ b/Code/Network/NetworkDependencies/Connection.cpp @@ -53,6 +53,8 @@ int Connection::Connect(int socket, bool blocking) int Connection::Connect(unsigned short port , const char serverName[], bool blocking) { + if(this->socket == -1 || this->socket == 0) InitiateSocket(); + struct hostent *hostEnt; if((hostEnt = gethostbyname(serverName)) == NULL) { diff --git a/Code/Network/NetworkDependencies/IListener.h b/Code/Network/NetworkDependencies/IListener.h deleted file mode 100644 index fc7bb4f5..00000000 --- a/Code/Network/NetworkDependencies/IListener.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef NETWORK_SERVER_ILISTENER_H -#define NETWORK_SERVER_ILISTENER_H - -///////////////////////////////////// -// Created by Pontus Fransson 2013 // -///////////////////////////////////// - -namespace Oyster -{ - namespace Network - { - namespace Server - { - class IListener - { - public: - virtual ~IListener() {} - virtual bool Init(unsigned int port) = 0; - virtual int Accept() = 0; - virtual void Shutdown() = 0; - - }; - } - } -} - -#endif \ No newline at end of file diff --git a/Code/Network/NetworkDependencies/Listener.cpp b/Code/Network/NetworkDependencies/Listener.cpp index 360ccebb..106ac3b9 100644 --- a/Code/Network/NetworkDependencies/Listener.cpp +++ b/Code/Network/NetworkDependencies/Listener.cpp @@ -1,8 +1,8 @@ #include "Listener.h" -using namespace Oyster::Network::Server; using namespace Utility::DynamicMemory; using namespace Oyster::Thread; +using namespace Oyster::Network; Listener::Listener() { diff --git a/Code/Network/NetworkDependencies/Listener.h b/Code/Network/NetworkDependencies/Listener.h index 422592e7..0c55f86e 100644 --- a/Code/Network/NetworkDependencies/Listener.h +++ b/Code/Network/NetworkDependencies/Listener.h @@ -5,7 +5,6 @@ // Created by Pontus Fransson 2013 // ///////////////////////////////////// -#include "IListener.h" #include "Connection.h" #include "IPostBox.h" #include "../../Misc/Thread/OysterThread.h" @@ -17,45 +16,43 @@ namespace Oyster { namespace Network { - namespace Server + class Listener : public ::Oyster::Thread::IThreadObject { - class Listener : public IListener, public ::Oyster::Thread::IThreadObject - { - public: - Listener(); - Listener(Oyster::Network::IPostBox* postBox); - ~Listener(); + public: + Listener(); + Listener(Oyster::Network::IPostBox* postBox); + ~Listener(); - bool Init(unsigned int port) override; - bool Init(unsigned int port, bool start); - bool Start(); - void Stop(); - void Shutdown(); + bool Init(unsigned int port); + bool Init(unsigned int port, bool start); + bool Start(); + void Stop(); + void Shutdown(); - void SetPostBox(IPostBox* postBox); + void SetPostBox(IPostBox* postBox); - private: - //Thread functions - bool DoWork(); - void ThreadEntry(); - void ThreadExit(); + private: + //Thread functions + bool DoWork(); + void ThreadEntry(); + void ThreadExit(); - //Function that runs in the thread. - int Accept(); - void StopListen(); + //Function that runs in the thread. + int Accept(); + void StopListen(); - private: - ::Oyster::Network::Connection* connection; + private: + ::Oyster::Network::Connection* connection; - ::Oyster::Thread::OysterThread thread; - OysterMutex mutex; - std::mutex stdMutex; + ::Oyster::Thread::OysterThread thread; + OysterMutex mutex; + std::mutex stdMutex; + + IPostBox* postBox; + std::atomic isListening; + int port; + }; - IPostBox* postBox; - std::atomic isListening; - int port; - }; - } } } diff --git a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj index 4d2ad291..061d3c4d 100644 --- a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj +++ b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj @@ -164,7 +164,6 @@ - diff --git a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters index eadbbeb3..188a4377 100644 --- a/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters +++ b/Code/Network/NetworkDependencies/NetworkDependencies.vcxproj.filters @@ -14,7 +14,6 @@ - diff --git a/Code/OysterGraphics/FileLoader/DanLoader.cpp b/Code/OysterGraphics/FileLoader/DanLoader.cpp index 73f0c8c8..6ac0cc24 100644 --- a/Code/OysterGraphics/FileLoader/DanLoader.cpp +++ b/Code/OysterGraphics/FileLoader/DanLoader.cpp @@ -132,13 +132,14 @@ void Oyster::Graphics::Loading::UnloadDAN(void* data) delete info; } -static wchar_t* charToWChar(const char* text) +static std::wstring charToWChar(const char* text) { // Convert to a wchar_t* size_t origsize = strlen(text) + 1; size_t convertedChars = 0; - wchar_t* wcstring = new wchar_t[origsize]; - mbstowcs_s(&convertedChars, wcstring, origsize, text, _TRUNCATE); + //wchar_t* wcstring = new wchar_t[origsize]; + std::wstring wcstring; wcstring.resize(origsize); + mbstowcs_s(&convertedChars, &wcstring[0], origsize, text, _TRUNCATE); return wcstring; } @@ -175,7 +176,7 @@ void Oyster::Graphics::Loading::LoadDAN(const wchar_t filename[], Oyster::Resour buffer = new char[4]; danFile.read(buffer, 4); memcpy(&headerType, buffer, 4); - //delete[] buffer; // ( note: may crash here.) + delete[] buffer; // ( note: may crash here.) // handle header type switch ((HeaderType)headerType) @@ -276,11 +277,14 @@ void Oyster::Graphics::Loading::LoadDAN(const wchar_t filename[], Oyster::Resour delete[] buffer; // ( note: may crash here.) // - ID3D11ShaderResourceView* diffuseMap = (ID3D11ShaderResourceView*)Oyster::Resource::OysterResource::LoadResource(charToWChar(materialHeader.diffuseMapPath), Oyster::Graphics::Loading::LoadTexture); - ID3D11ShaderResourceView* normalMap = (ID3D11ShaderResourceView*)Oyster::Resource::OysterResource::LoadResource(charToWChar(materialHeader.normalMapPath), Oyster::Graphics::Loading::LoadTexture); + ID3D11ShaderResourceView* diffuseMap = (ID3D11ShaderResourceView*)Oyster::Resource::OysterResource::LoadResource(charToWChar(materialHeader.diffuseMapPath).c_str(), Oyster::Graphics::Loading::LoadTexture); + ID3D11ShaderResourceView* normalMap = (ID3D11ShaderResourceView*)Oyster::Resource::OysterResource::LoadResource(charToWChar(materialHeader.normalMapPath).c_str(), Oyster::Graphics::Loading::LoadTexture); modelInfo->Material.push_back(diffuseMap); modelInfo->Material.push_back(normalMap); + delete materialHeader.normalMapPath; + delete materialHeader.diffuseMapPath; + break; } // skeleton header @@ -298,6 +302,7 @@ void Oyster::Graphics::Loading::LoadDAN(const wchar_t filename[], Oyster::Resour } } + // close file danFile.close(); From 4ccf11b5d6ba76021a49fbffb35b46f717ec4687 Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Wed, 29 Jan 2014 10:54:59 +0100 Subject: [PATCH 61/76] Fixed vector issue --- Code/GamePhysics/Implementation/SphericalRigidBody.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp index a164cbe1..3fb6b4ce 100644 --- a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp +++ b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp @@ -171,7 +171,7 @@ Sphere & SphericalRigidBody::GetBoundingSphere( Sphere &targetMem ) const Float4 & SphericalRigidBody::GetNormalAt( const Float4 &worldPos, Float4 &targetMem ) const { - targetMem = worldPos.xyz - this->rigid.centerPos; + targetMem = Float4( worldPos.xyz - this->rigid.centerPos, 0); Float magnitude = targetMem.GetMagnitude(); if( magnitude != 0.0f ) { // sanity check From 1d3b073e7f19e89c0b2af1ed377d9c4f314ae180 Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Wed, 29 Jan 2014 11:22:04 +0100 Subject: [PATCH 62/76] Added functionallity to stop collision responses --- .../Implementation/PhysicsAPI_Impl.cpp | 143 ++++++++---------- .../Implementation/PhysicsAPI_Impl.h | 4 +- .../Implementation/SimpleRigidBody.cpp | 20 +-- .../Implementation/SimpleRigidBody.h | 12 +- .../Implementation/SphericalRigidBody.cpp | 20 +-- .../Implementation/SphericalRigidBody.h | 12 +- Code/GamePhysics/PhysicsAPI.h | 12 +- Code/GamePhysics/PhysicsStructs.h | 8 +- 8 files changed, 105 insertions(+), 126 deletions(-) diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp index 14c4d9e4..5ca06fce 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp @@ -22,92 +22,71 @@ namespace Float4 worldPointOfContact; if( proto->Intersects(*deuter, worldPointOfContact) ) { - switch( proto->CallSubscription_Collision(deuter) ) - { - case ICustomBody::SubscriptMessage_ignore_collision_response: - break; - default: - { // Apply CollisionResponse in pure gather pattern - ICustomBody::State protoState; proto->GetState( protoState ); - ICustomBody::State deuterState; deuter->GetState( deuterState ); + // Apply CollisionResponse in pure gather pattern + ICustomBody::State protoState; proto->GetState( protoState ); + ICustomBody::State deuterState; deuter->GetState( deuterState ); - Float4 protoG = protoState.GetLinearMomentum( worldPointOfContact.xyz ), - deuterG = deuterState.GetLinearMomentum( worldPointOfContact.xyz ); + Float4 protoG = protoState.GetLinearMomentum( worldPointOfContact.xyz ), + deuterG = deuterState.GetLinearMomentum( worldPointOfContact.xyz ); - // calc from perspective of deuter - Float4 normal; deuter->GetNormalAt( worldPointOfContact, normal ); - Float protoG_Magnitude = protoG.Dot( normal ), - deuterG_Magnitude = deuterG.Dot( normal ); - - // if they are not relatively moving towards eachother, there is no collision - Float deltaPos = normal.Dot( Float4(deuterState.GetCenterPosition(), 1) - Float4(protoState.GetCenterPosition(), 1) ); - if( deltaPos < 0.0f ) - { - if( protoG_Magnitude >= deuterG_Magnitude ) - { - break; - } - } - else if( deltaPos > 0.0f ) - { - if( protoG_Magnitude <= deuterG_Magnitude ) - { - break; - } - } - else - { - break; - } - - // bounce - Float4 bounceD = normal * -Formula::CollisionResponse::Bounce( deuterState.GetRestitutionCoeff(), - deuterState.GetMass(), deuterG_Magnitude, - protoState.GetMass(), protoG_Magnitude ); - - //sumJ -= Formula::CollisionResponse::Friction( impulse, normal, - // protoState.GetLinearMomentum(), protoState.GetFrictionCoeff_Static(), protoState.GetFrictionCoeff_Kinetic(), protoState.GetMass(), - // deuterState.GetLinearMomentum(), deuterState.GetFrictionCoeff_Static(), deuterState.GetFrictionCoeff_Kinetic(), deuterState.GetMass()); - - // calc from perspective of proto - proto->GetNormalAt( worldPointOfContact, normal ); - protoG_Magnitude = protoG.Dot( normal ), + // calc from perspective of deuter + Float4 normal; deuter->GetNormalAt( worldPointOfContact, normal ); + Float protoG_Magnitude = protoG.Dot( normal ), deuterG_Magnitude = deuterG.Dot( normal ); - - // bounce - Float4 bounceP = normal * Formula::CollisionResponse::Bounce( protoState.GetRestitutionCoeff(), - protoState.GetMass(), protoG_Magnitude, - deuterState.GetMass(), deuterG_Magnitude ); - Float4 bounce = Average( bounceD, bounceP ); - //Float4 bounce = bounceD + bounceP; - - // FRICTION - // Apply - //sumJ += ( 1 / deuterState.GetMass() )*frictionImpulse; - // FRICTION END - -// Float4 forwardedDeltaPos, forwardedDeltaAxis; -// { // @todo TODO: is this right? -// Float4 bounceAngularImpulse = ::Oyster::Math::Float4( (worldPointOfContact - protoState.GetCenterPosition()).xyz.Cross(bounce.xyz), 0.0f ), -// bounceLinearImpulse = bounce - bounceAngularImpulse; -// proto->Predict( forwardedDeltaPos, forwardedDeltaAxis, bounceLinearImpulse, bounceAngularImpulse, API_instance.GetFrameTimeLength() ); -// } - - - Float kineticEnergyPBefore = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), protoState.GetLinearMomentum()/protoState.GetMass() ); - -// protoState.ApplyForwarding( forwardedDeltaPos, forwardedDeltaAxis ); - protoState.ApplyImpulse( bounce.xyz, worldPointOfContact.xyz, normal.xyz ); - proto->SetState( protoState ); - - Float kineticEnergyPAFter = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), (protoState.GetLinearMomentum() + protoState.GetLinearImpulse())/protoState.GetMass() ); - - proto->CallSubscription_CollisionResponse( deuter, kineticEnergyPBefore - kineticEnergyPAFter ); - + // if they are not relatively moving towards eachother, there is no collision + Float deltaPos = normal.Dot( Float4(deuterState.GetCenterPosition(), 1) - Float4(protoState.GetCenterPosition(), 1) ); + if( deltaPos < 0.0f ) + { + if( protoG_Magnitude >= deuterG_Magnitude ) + { + return; } - break; } + else if( deltaPos > 0.0f ) + { + if( protoG_Magnitude <= deuterG_Magnitude ) + { + return; + } + } + else + { + return; + } + + if( proto->CallSubscription_BeforeCollisionResponse(proto) == ICustomBody::SubscriptMessage_ignore_collision_response ) + { + return; + } + + + // bounce + Float4 bounceD = normal * -Formula::CollisionResponse::Bounce( deuterState.GetRestitutionCoeff(), + deuterState.GetMass(), deuterG_Magnitude, + protoState.GetMass(), protoG_Magnitude ); + + + // calc from perspective of proto + proto->GetNormalAt( worldPointOfContact, normal ); + protoG_Magnitude = protoG.Dot( normal ), + deuterG_Magnitude = deuterG.Dot( normal ); + + // bounce + Float4 bounceP = normal * Formula::CollisionResponse::Bounce( protoState.GetRestitutionCoeff(), + protoState.GetMass(), protoG_Magnitude, + deuterState.GetMass(), deuterG_Magnitude ); + + Float4 bounce = Average( bounceD, bounceP ); + + Float kineticEnergyPBefore = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), protoState.GetLinearMomentum()/protoState.GetMass() ); + + protoState.ApplyImpulse( bounce.xyz, worldPointOfContact.xyz, normal.xyz ); + proto->SetState( protoState ); + + Float kineticEnergyPAFter = Oyster::Physics3D::Formula::LinearKineticEnergy( protoState.GetMass(), (protoState.GetLinearMomentum() + protoState.GetLinearImpulse())/protoState.GetMass() ); + + proto->CallSubscription_AfterCollisionResponse( deuter, kineticEnergyPBefore - kineticEnergyPAFter ); } } } @@ -384,12 +363,12 @@ namespace Oyster { namespace Physics void EventAction_Destruction( ::Utility::DynamicMemory::UniquePointer<::Oyster::Physics::ICustomBody> proto ) { /* Do nothing except allowing the proto uniquePointer destroy itself. */ } - ::Oyster::Physics::ICustomBody::SubscriptMessage EventAction_Collision( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter ) + ::Oyster::Physics::ICustomBody::SubscriptMessage EventAction_BeforeCollisionResponse( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter ) { /* Do nothing except returning business as usual. */ return ::Oyster::Physics::ICustomBody::SubscriptMessage_none; } - void EventAction_CollisionResponse( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ) + void EventAction_AfterCollisionResponse( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ) { /* Do nothing except returning business as usual. */ } diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h index 63d8ff08..9ea9a6fc 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h @@ -61,8 +61,8 @@ namespace Oyster namespace Default { void EventAction_Destruction( ::Utility::DynamicMemory::UniquePointer<::Oyster::Physics::ICustomBody> proto ); - ::Oyster::Physics::ICustomBody::SubscriptMessage EventAction_Collision( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter ); - void EventAction_CollisionResponse( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); + ::Oyster::Physics::ICustomBody::SubscriptMessage EventAction_BeforeCollisionResponse( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter ); + void EventAction_AfterCollisionResponse( const ::Oyster::Physics::ICustomBody *proto, const ::Oyster::Physics::ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); void EventAction_Move( const ::Oyster::Physics::ICustomBody *object ); } } diff --git a/Code/GamePhysics/Implementation/SimpleRigidBody.cpp b/Code/GamePhysics/Implementation/SimpleRigidBody.cpp index 2f6a6c0c..ef3027ee 100644 --- a/Code/GamePhysics/Implementation/SimpleRigidBody.cpp +++ b/Code/GamePhysics/Implementation/SimpleRigidBody.cpp @@ -46,8 +46,8 @@ SimpleRigidBody::SimpleRigidBody() this->rigid = RigidBody(); this->rigid.SetMass_KeepMomentum( 16.0f ); this->gravityNormal = Float3::null; - this->onCollision = Default::EventAction_Collision; - this->onCollisionResponse = Default::EventAction_CollisionResponse; + this->onCollision = Default::EventAction_BeforeCollisionResponse; + this->onCollisionResponse = Default::EventAction_AfterCollisionResponse; this->onMovement = Default::EventAction_Move; this->scene = nullptr; this->customTag = nullptr; @@ -72,7 +72,7 @@ SimpleRigidBody::SimpleRigidBody( const API::SimpleBodyDescription &desc ) } else { - this->onCollision = Default::EventAction_Collision; + this->onCollision = Default::EventAction_BeforeCollisionResponse; } if( desc.subscription_onCollisionResponse ) @@ -81,7 +81,7 @@ SimpleRigidBody::SimpleRigidBody( const API::SimpleBodyDescription &desc ) } else { - this->onCollisionResponse = Default::EventAction_CollisionResponse; + this->onCollisionResponse = Default::EventAction_AfterCollisionResponse; } if( desc.subscription_onMovement ) @@ -163,12 +163,12 @@ void SimpleRigidBody::SetState( const SimpleRigidBody::State &state ) } } -ICustomBody::SubscriptMessage SimpleRigidBody::CallSubscription_Collision( const ICustomBody *deuter ) +ICustomBody::SubscriptMessage SimpleRigidBody::CallSubscription_BeforeCollisionResponse( const ICustomBody *deuter ) { return this->onCollision( this, deuter ); } -void SimpleRigidBody::CallSubscription_CollisionResponse( const ICustomBody *deuter, Float kineticEnergyLoss ) +void SimpleRigidBody::CallSubscription_AfterCollisionResponse( const ICustomBody *deuter, Float kineticEnergyLoss ) { return this->onCollisionResponse( this, deuter, kineticEnergyLoss ); } @@ -318,7 +318,7 @@ void SimpleRigidBody::SetScene( void *scene ) this->scene = (Octree*)scene; } -void SimpleRigidBody::SetSubscription( ICustomBody::EventAction_Collision functionPointer ) +void SimpleRigidBody::SetSubscription( ICustomBody::EventAction_BeforeCollisionResponse functionPointer ) { if( functionPointer ) { @@ -326,11 +326,11 @@ void SimpleRigidBody::SetSubscription( ICustomBody::EventAction_Collision functi } else { - this->onCollision = Default::EventAction_Collision; + this->onCollision = Default::EventAction_BeforeCollisionResponse; } } -void SimpleRigidBody::SetSubscription( ICustomBody::EventAction_CollisionResponse functionPointer ) +void SimpleRigidBody::SetSubscription( ICustomBody::EventAction_AfterCollisionResponse functionPointer ) { if( functionPointer ) { @@ -338,7 +338,7 @@ void SimpleRigidBody::SetSubscription( ICustomBody::EventAction_CollisionRespons } else { - this->onCollisionResponse = Default::EventAction_CollisionResponse; + this->onCollisionResponse = Default::EventAction_AfterCollisionResponse; } } diff --git a/Code/GamePhysics/Implementation/SimpleRigidBody.h b/Code/GamePhysics/Implementation/SimpleRigidBody.h index 271c4362..9e61cee3 100644 --- a/Code/GamePhysics/Implementation/SimpleRigidBody.h +++ b/Code/GamePhysics/Implementation/SimpleRigidBody.h @@ -21,8 +21,8 @@ namespace Oyster { namespace Physics void SetState( const State &state ); //::Oyster::Math::Float3 GetRigidLinearVelocity() const; - SubscriptMessage CallSubscription_Collision( const ICustomBody *deuter ); - void CallSubscription_CollisionResponse( const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); + SubscriptMessage CallSubscription_BeforeCollisionResponse( const ICustomBody *deuter ); + void CallSubscription_AfterCollisionResponse( const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); void CallSubscription_Move(); bool IsAffectedByGravity() const; @@ -44,8 +44,8 @@ namespace Oyster { namespace Physics void SetScene( void *scene ); - void SetSubscription( EventAction_Collision functionPointer ); - void SetSubscription( EventAction_CollisionResponse functionPointer ); + void SetSubscription( EventAction_BeforeCollisionResponse functionPointer ); + void SetSubscription( EventAction_AfterCollisionResponse functionPointer ); void SetSubscription( EventAction_Move functionPointer ); void SetGravity( bool ignore); @@ -65,8 +65,8 @@ namespace Oyster { namespace Physics ::Oyster::Physics3D::RigidBody rigid; ::Oyster::Math::Float4 deltaPos, deltaAxis; ::Oyster::Math::Float3 gravityNormal; - EventAction_Collision onCollision; - EventAction_CollisionResponse onCollisionResponse; + EventAction_BeforeCollisionResponse onCollision; + EventAction_AfterCollisionResponse onCollisionResponse; EventAction_Move onMovement; Octree *scene; void *customTag; diff --git a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp index 3fb6b4ce..539093db 100644 --- a/Code/GamePhysics/Implementation/SphericalRigidBody.cpp +++ b/Code/GamePhysics/Implementation/SphericalRigidBody.cpp @@ -13,8 +13,8 @@ SphericalRigidBody::SphericalRigidBody() this->rigid = RigidBody(); this->rigid.SetMass_KeepMomentum( 10.0f ); this->gravityNormal = Float3::null; - this->onCollision = Default::EventAction_Collision; - this->onCollisionResponse = Default::EventAction_CollisionResponse; + this->onCollision = Default::EventAction_BeforeCollisionResponse; + this->onCollisionResponse = Default::EventAction_AfterCollisionResponse; this->onMovement = Default::EventAction_Move; this->scene = nullptr; this->customTag = nullptr; @@ -40,7 +40,7 @@ SphericalRigidBody::SphericalRigidBody( const API::SphericalBodyDescription &des } else { - this->onCollision = Default::EventAction_Collision; + this->onCollision = Default::EventAction_BeforeCollisionResponse; } if( desc.subscription_onCollisionResponse ) @@ -49,7 +49,7 @@ SphericalRigidBody::SphericalRigidBody( const API::SphericalBodyDescription &des } else { - this->onCollisionResponse = Default::EventAction_CollisionResponse; + this->onCollisionResponse = Default::EventAction_AfterCollisionResponse; } if( desc.subscription_onMovement ) @@ -128,12 +128,12 @@ void SphericalRigidBody::SetState( const SphericalRigidBody::State &state ) } } -ICustomBody::SubscriptMessage SphericalRigidBody::CallSubscription_Collision( const ICustomBody *deuter ) +ICustomBody::SubscriptMessage SphericalRigidBody::CallSubscription_BeforeCollisionResponse( const ICustomBody *deuter ) { return this->onCollision( this, deuter ); } -void SphericalRigidBody::CallSubscription_CollisionResponse( const ICustomBody *deuter, Float kineticEnergyLoss ) +void SphericalRigidBody::CallSubscription_AfterCollisionResponse( const ICustomBody *deuter, Float kineticEnergyLoss ) { this->onCollisionResponse( this, deuter, kineticEnergyLoss); } @@ -238,7 +238,7 @@ void SphericalRigidBody::Predict( ::Oyster::Math::Float4 &outDeltaPos, ::Oyster: this->rigid.Predict_LeapFrog( outDeltaPos.xyz, outDeltaAxis.xyz, actingLinearImpulse.xyz, actingAngularImpulse.xyz, deltaTime ); } -void SphericalRigidBody::SetSubscription( ICustomBody::EventAction_Collision functionPointer ) +void SphericalRigidBody::SetSubscription( ICustomBody::EventAction_BeforeCollisionResponse functionPointer ) { if( functionPointer ) { @@ -246,11 +246,11 @@ void SphericalRigidBody::SetSubscription( ICustomBody::EventAction_Collision fun } else { - this->onCollision = Default::EventAction_Collision; + this->onCollision = Default::EventAction_BeforeCollisionResponse; } } -void SphericalRigidBody::SetSubscription( ICustomBody::EventAction_CollisionResponse functionPointer ) +void SphericalRigidBody::SetSubscription( ICustomBody::EventAction_AfterCollisionResponse functionPointer ) { if( functionPointer ) { @@ -258,7 +258,7 @@ void SphericalRigidBody::SetSubscription( ICustomBody::EventAction_CollisionResp } else { - this->onCollisionResponse = Default::EventAction_CollisionResponse; + this->onCollisionResponse = Default::EventAction_AfterCollisionResponse; } } diff --git a/Code/GamePhysics/Implementation/SphericalRigidBody.h b/Code/GamePhysics/Implementation/SphericalRigidBody.h index 213e225b..390b7171 100644 --- a/Code/GamePhysics/Implementation/SphericalRigidBody.h +++ b/Code/GamePhysics/Implementation/SphericalRigidBody.h @@ -22,8 +22,8 @@ namespace Oyster { namespace Physics void SetState( const State &state ); //::Oyster::Math::Float3 GetRigidLinearVelocity() const; - SubscriptMessage CallSubscription_Collision( const ICustomBody *deuter ); - void CallSubscription_CollisionResponse( const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); + SubscriptMessage CallSubscription_BeforeCollisionResponse( const ICustomBody *deuter ); + void CallSubscription_AfterCollisionResponse( const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); void CallSubscription_Move(); bool IsAffectedByGravity() const; @@ -45,8 +45,8 @@ namespace Oyster { namespace Physics void SetScene( void *scene ); - void SetSubscription( EventAction_Collision functionPointer ); - void SetSubscription( EventAction_CollisionResponse functionPointer ); + void SetSubscription( EventAction_BeforeCollisionResponse functionPointer ); + void SetSubscription( EventAction_AfterCollisionResponse functionPointer ); void SetSubscription( EventAction_Move functionPointer ); void SetGravity( bool ignore); @@ -66,8 +66,8 @@ namespace Oyster { namespace Physics ::Oyster::Physics3D::RigidBody rigid; ::Oyster::Math::Float4 deltaPos, deltaAxis; ::Oyster::Math::Float3 gravityNormal; - EventAction_Collision onCollision; - EventAction_CollisionResponse onCollisionResponse; + EventAction_BeforeCollisionResponse onCollision; + EventAction_AfterCollisionResponse onCollisionResponse; EventAction_Move onMovement; Octree *scene; void *customTag; diff --git a/Code/GamePhysics/PhysicsAPI.h b/Code/GamePhysics/PhysicsAPI.h index 3ced2a55..353756eb 100644 --- a/Code/GamePhysics/PhysicsAPI.h +++ b/Code/GamePhysics/PhysicsAPI.h @@ -244,8 +244,8 @@ namespace Oyster SubscriptMessage_ignore_collision_response }; - typedef SubscriptMessage (*EventAction_Collision)( const ICustomBody *proto, const ICustomBody *deuter ); - typedef void (*EventAction_CollisionResponse)( const ICustomBody *proto, const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); + typedef SubscriptMessage (*EventAction_BeforeCollisionResponse)( const ICustomBody *proto, const ICustomBody *deuter ); + typedef void (*EventAction_AfterCollisionResponse)( const ICustomBody *proto, const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ); typedef void (*EventAction_Move)( const ICustomBody *object ); typedef Struct::SimpleBodyDescription SimpleBodyDescription; typedef Struct::SphericalBodyDescription SphericalBodyDescription; @@ -262,12 +262,12 @@ namespace Oyster /******************************************************** * @todo TODO: need doc ********************************************************/ - virtual SubscriptMessage CallSubscription_Collision( const ICustomBody *deuter ) = 0; + virtual SubscriptMessage CallSubscription_BeforeCollisionResponse( const ICustomBody *deuter ) = 0; /******************************************************** * @todo TODO: need doc ********************************************************/ - virtual void CallSubscription_CollisionResponse( const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ) = 0; + virtual void CallSubscription_AfterCollisionResponse( const ICustomBody *deuter, ::Oyster::Math::Float kineticEnergyLoss ) = 0; /******************************************************** * @todo TODO: need doc @@ -397,14 +397,14 @@ namespace Oyster * whenever a collision occurs. * @param functionPointer: If NULL, an empty default function will be set. ********************************************************/ - virtual void SetSubscription( EventAction_Collision functionPointer ) = 0; + virtual void SetSubscription( EventAction_BeforeCollisionResponse functionPointer ) = 0; /******************************************************** * Sets the function that will be called by the engine * whenever a collision has finished. * @param functionPointer: If NULL, an empty default function will be set. ********************************************************/ - virtual void SetSubscription( EventAction_CollisionResponse functionPointer ) = 0; + virtual void SetSubscription( EventAction_AfterCollisionResponse functionPointer ) = 0; /******************************************************** * Sets the function that will be called by the engine diff --git a/Code/GamePhysics/PhysicsStructs.h b/Code/GamePhysics/PhysicsStructs.h index 49cf6993..00c07b2d 100644 --- a/Code/GamePhysics/PhysicsStructs.h +++ b/Code/GamePhysics/PhysicsStructs.h @@ -19,8 +19,8 @@ namespace Oyster { namespace Physics ::Oyster::Math::Float frictionCoeff_Static; ::Oyster::Math::Float frictionCoeff_Dynamic; ::Oyster::Physics3D::MomentOfInertia inertiaTensor; - ::Oyster::Physics::ICustomBody::EventAction_Collision subscription_onCollision; - ::Oyster::Physics::ICustomBody::EventAction_CollisionResponse subscription_onCollisionResponse; + ::Oyster::Physics::ICustomBody::EventAction_BeforeCollisionResponse subscription_onCollision; + ::Oyster::Physics::ICustomBody::EventAction_AfterCollisionResponse subscription_onCollisionResponse; ::Oyster::Physics::ICustomBody::EventAction_Move subscription_onMovement; bool ignoreGravity; @@ -36,8 +36,8 @@ namespace Oyster { namespace Physics ::Oyster::Math::Float restitutionCoeff; ::Oyster::Math::Float frictionCoeff_Static; ::Oyster::Math::Float frictionCoeff_Dynamic; - ::Oyster::Physics::ICustomBody::EventAction_Collision subscription_onCollision; - ::Oyster::Physics::ICustomBody::EventAction_CollisionResponse subscription_onCollisionResponse; + ::Oyster::Physics::ICustomBody::EventAction_BeforeCollisionResponse subscription_onCollision; + ::Oyster::Physics::ICustomBody::EventAction_AfterCollisionResponse subscription_onCollisionResponse; ::Oyster::Physics::ICustomBody::EventAction_Move subscription_onMovement; bool ignoreGravity; From aa99742894f92e2dd2c882a3674525f460575f9b Mon Sep 17 00:00:00 2001 From: Robin Engman Date: Wed, 29 Jan 2014 12:22:18 +0100 Subject: [PATCH 63/76] Added epsilon value for testing --- .../Implementation/PhysicsAPI_Impl.cpp | 27 ++++++++++++++++++- .../Implementation/PhysicsAPI_Impl.h | 3 ++- Code/GamePhysics/PhysicsAPI.h | 1 + 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp index 5ca06fce..6f164810 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.cpp @@ -99,6 +99,7 @@ API & API::Instance() API_Impl::API_Impl() { this->gravityConstant = Constant::gravity_constant; + this->epsilon = Constant::epsilon; this->updateFrameLength = 1.0f / 120.0f; this->destructionAction = Default::EventAction_Destruction; this->gravity = ::std::vector(); @@ -125,6 +126,11 @@ void API_Impl::SetGravityConstant( float g ) this->gravityConstant = g; } +void API_Impl::SetEpsilon( float e ) +{ + this->epsilon = e; +} + void API_Impl::SetSubscription( API::EventAction_Destruction functionPointer ) { if( functionPointer ) @@ -192,12 +198,31 @@ void API_Impl::Update() proto = updateList.begin(); for( ; proto != updateList.end(); ++proto ) { + Float3 lM = state.GetLinearMomentum() + state.GetLinearImpulse(); + + if( lM.x < this->epsilon ) + { + state.SetLinearMomentum( Float3(0, lM.y, lM.z) ); + state.SetLinearImpulse( Float3(0, lM.y, lM.z) ); + } + if( lM.y < this->epsilon ) + { + state.SetLinearMomentum( Float3(lM.x, 0, lM.z) ); + state.SetLinearImpulse( Float3(lM.x, 0, lM.z) ); + } + if( lM.z < this->epsilon ) + { + state.SetLinearMomentum( Float3(lM.x, lM.y, 0) ); + state.SetLinearImpulse( Float3(lM.x, lM.y, 0) ); + } + switch( (*proto)->Update(this->updateFrameLength) ) { case UpdateState_altered: this->worldScene.SetAsAltered( this->worldScene.GetTemporaryReferenceOf(*proto) ); (*proto)->CallSubscription_Move(); - case UpdateState_resting: default: + case UpdateState_resting: + default: break; } } diff --git a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h index 9ea9a6fc..21460944 100644 --- a/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h +++ b/Code/GamePhysics/Implementation/PhysicsAPI_Impl.h @@ -18,6 +18,7 @@ namespace Oyster void SetFrameTimeLength( float deltaTime ); void SetGravityConstant( float g ); + void SetEpsilon( float e ); void SetSubscription( EventAction_Destruction functionPointer ); float GetFrameTimeLength() const; @@ -52,7 +53,7 @@ namespace Oyster ::Utility::DynamicMemory::UniquePointer CreateRigidBody( const SphericalBodyDescription &desc ) const; private: - ::Oyster::Math::Float gravityConstant, updateFrameLength; + ::Oyster::Math::Float gravityConstant, updateFrameLength, epsilon; EventAction_Destruction destructionAction; ::std::vector gravity; Octree worldScene; diff --git a/Code/GamePhysics/PhysicsAPI.h b/Code/GamePhysics/PhysicsAPI.h index 353756eb..78b65f5e 100644 --- a/Code/GamePhysics/PhysicsAPI.h +++ b/Code/GamePhysics/PhysicsAPI.h @@ -34,6 +34,7 @@ namespace Oyster namespace Constant { const float gravity_constant = (const float)6.67284e-11; //!< The _big_G_! ( N(m/kg)^2 ) Used in real gravityforcefields. + const float epsilon = (const float)1.0e-7; } class PHYSICS_DLL_USAGE API From 16334ea2db8190cc3701b51663c69d88bd408493 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Wed, 29 Jan 2014 13:18:17 +0100 Subject: [PATCH 64/76] GL - collision working, going to merge with physics --- .../Game/DanBiasGame/GameClientRecieverFunc.h | 13 ++++ .../DanBiasGame/GameClientState/GameState.cpp | 65 ++++++++++--------- .../GameSession/GameSession_Events.cpp | 33 ++++++++-- .../GameSession/GameSession_Logic.cpp | 2 +- Code/Game/GameLogic/Game_PlayerData.cpp | 7 +- Code/Game/GameLogic/Level.cpp | 27 ++++---- Code/Game/GameLogic/Object.cpp | 6 +- 7 files changed, 92 insertions(+), 61 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientRecieverFunc.h b/Code/Game/DanBiasGame/GameClientRecieverFunc.h index 5e129fca..76c4926c 100644 --- a/Code/Game/DanBiasGame/GameClientRecieverFunc.h +++ b/Code/Game/DanBiasGame/GameClientRecieverFunc.h @@ -3,6 +3,10 @@ namespace DanBias { +inline bool IsLobbyProtocol(short ID) { return (ID >= protocol_LobbyMIN && ID <= protocol_LobbyMAX); } +inline bool IsGeneralProtocol(short ID) { return (ID >= protocol_GeneralMIN && ID <= protocol_GeneralMAX); } +inline bool IsGameplayProtocol(short ID) { return (ID >= protocol_GameplayMIN && ID <= protocol_GameplayMAX); } + struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject { Oyster::Network::NetworkClient* nwClient; @@ -12,6 +16,13 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject // parsing protocols and sending it to the gameState void NetworkCallback(Oyster::Network::CustomNetProtocol& p) override { + + //if( IsGameplayProtocol(p[protocol_INDEX_ID].value.netShort) ) + //ParseGameplayEvent(e.protocol, e.gameClient); + + //if( IsGeneralProtocol(p[protocol_INDEX_ID].value.netShort) ) + //ParseGeneralEvent(e.protocol, e.gameClient); + int pType = p[0].value.netInt; switch (pType) { @@ -106,6 +117,8 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject break; } + + } }; } diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index e0021bce..bda3ad13 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -75,33 +75,10 @@ bool GameState::LoadModels(std::wstring mapFile) // init models privData->modelCount = 2; + // add world model ModelInitData modelData; - - modelData.world = Oyster::Math3D::Float4x4::identity; - Oyster::Math3D::Float4x4 translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,-2,-2)); - modelData.world = modelData.world * translate; - modelData.visible = true; - modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; - modelData.id = 0; - // load models - C_Object* obj = new C_Player(); - privData->object.push_back(obj); - privData->object[privData->object.size() -1 ]->Init(modelData); - - translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,2,2)); - modelData.world = modelData.world * translate; - modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; - modelData.id ++; - - obj = new C_Player(); - privData->object.push_back(obj); - privData->object[privData->object.size() -1 ]->Init(modelData); - - /*translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,-2,-2)); - modelData.world = modelData.world * translate; - modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; - modelData.id ++;*/ - + Oyster::Math3D::Float4x4 translate; + C_Object* obj; translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(0,0,0)); Oyster::Math3D::Float4x4 scale = Oyster::Math3D::Float4x4::identity; scale.v[0].x = 8; @@ -109,12 +86,40 @@ bool GameState::LoadModels(std::wstring mapFile) scale.v[2].z = 8; modelData.world = scale; //modelData.world * translate modelData.modelPath = L"ball.dan"; - modelData.id ++; + modelData.id = 0; obj = new C_Player(); privData->object.push_back(obj); privData->object[privData->object.size() -1 ]->Init(modelData); + // add box model + modelData.world = Oyster::Math3D::Float4x4::identity; + translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-5,15,0)); + modelData.world = modelData.world * translate; + modelData.modelPath = L"box.dan"; + modelData.id = 1; + + obj = new C_Player(); + privData->object.push_back(obj); + privData->object[privData->object.size() -1 ]->Init(modelData); + modelData.world = Oyster::Math3D::Float4x4::identity; + + // add player model + modelData.world = Oyster::Math3D::Float4x4::identity; + translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(0, 15, 0)); + modelData.world = modelData.world * translate; + modelData.visible = true; + modelData.modelPath = L"..\\Content\\Models\\char_white.dan"; + modelData.id = 2; + // load models + obj = new C_Player(); + privData->object.push_back(obj); + privData->object[privData->object.size() -1 ]->Init(modelData); + + + + + return true; } @@ -328,10 +333,10 @@ void GameState::Protocol( ObjPos* pos ) //camera->setRight((Oyster::Math::Float3(world[0], world[1], world[2]))); //camera->setUp((Oyster::Math::Float3(world[4], world[5], world[6]))); //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); - if(i == 0) + if(i == 2) // playerobj { - camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); - camera->UpdateViewMatrix(); + //camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); + //camera->UpdateViewMatrix(); } } } diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index 2beaf75b..1ffd5e98 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -114,15 +114,34 @@ namespace DanBias { GameLogic::IObjectData* obj = NULL; if(dynamic_cast(movedObject)) - obj =((GameLogic::ILevelData*)movedObject)->GetObjectAt(0); - if(obj) { - if(obj->GetType() == OBJECT_TYPE_BOX) + obj =((GameLogic::ILevelData*)movedObject)->GetObjectAt(0); + if(obj) { - obj->GetID(); - Oyster::Math::Float4x4 world =obj->GetOrientation(); - Protocol_ObjectPosition p(world, 1); - GameSession::gameSession->Send(p.GetProtocol()); + if(obj->GetType() == OBJECT_TYPE_WORLD) + { + obj->GetID(); + Oyster::Math::Float4x4 world =obj->GetOrientation(); + Oyster::Math3D::Float4x4 scale = Oyster::Math3D::Float4x4::identity; + scale.v[0].x = 8; + scale.v[1].y = 8; + scale.v[2].z = 8; + world = world * scale; + Protocol_ObjectPosition p(world, 0); + GameSession::gameSession->Send(p.GetProtocol()); + } + } + obj = NULL; + obj =((GameLogic::ILevelData*)movedObject)->GetObjectAt(1); + if(obj) + { + if(obj->GetType() == OBJECT_TYPE_BOX) + { + obj->GetID(); + Oyster::Math::Float4x4 world = obj->GetOrientation(); + Protocol_ObjectPosition p(world, 1); + GameSession::gameSession->Send(p.GetProtocol()); + } } } diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp index 5bd22a52..fed4202c 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp @@ -80,7 +80,7 @@ namespace DanBias if(clients.Size() >= 1 && clients[0]) { Oyster::Math::Float4x4 world = this->clients[0]->GetPlayer()->GetOrientation(); - Protocol_ObjectPosition p(world, 0); + Protocol_ObjectPosition p(world, 2); Send(p.GetProtocol()); } } diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index dcaccc48..81917071 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -8,13 +8,10 @@ Game::PlayerData::PlayerData() Oyster::Physics::API::SimpleBodyDescription sbDesc; //set some stats that are appropriate to a player - //create rigidbody + //create rigid body Oyster::Physics::ICustomBody *rigidBody = Oyster::Physics::API::Instance().CreateRigidBody(sbDesc).Release(); - - //create player with this rigidbody - - + //create player with this rigid body this->player = new Player(rigidBody,Player::PlayerCollision,OBJECT_TYPE::OBJECT_TYPE_PLAYER); this->player->GetRigidBody()->SetCustomTag(this); } diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 5d56f83d..12a09587 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -20,51 +20,45 @@ void Level::InitiateLevel(std::string levelPath) } void Level::InitiateLevel(float radius) { + // add level sphere API::SphericalBodyDescription sbDesc; sbDesc.centerPosition = Oyster::Math::Float4(0,0,0,1); sbDesc.ignoreGravity = true; - sbDesc.radius = 8; //radius; + sbDesc.radius = 8; sbDesc.mass = 10e12f; - //sbDesc.mass = 0; //10^16 ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); - ICustomBody::State state; rigidBody->GetState(state); - state.SetRestitutionCoeff(0.1); + state.SetRestitutionCoeff(0.01); rigidBody->SetState(state); levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); rigidBody->SetCustomTag(levelObj); - /* + // add box API::SimpleBodyDescription sbDesc_TestBox; - sbDesc_TestBox.centerPosition = Oyster::Math::Float4(5,15,0,0); + sbDesc_TestBox.centerPosition = Oyster::Math::Float4(-5,15,0,0); sbDesc_TestBox.ignoreGravity = false; sbDesc_TestBox.mass = 10; - sbDesc_TestBox.size = Oyster::Math::Float4(2,2,2,0); - //sbDesc.mass = 0; //10^16 - + sbDesc_TestBox.size = Oyster::Math::Float4(0.5f,0.5f,0.5f,0); ICustomBody* rigidBody_TestBox = API::Instance().CreateRigidBody(sbDesc_TestBox).Release(); - rigidBody_TestBox->SetSubscription(Level::PhysicsOnMoveLevel); testBox = new DynamicObject(rigidBody_TestBox,LevelCollision,OBJECT_TYPE::OBJECT_TYPE_BOX); rigidBody_TestBox->SetCustomTag(testBox); rigidBody_TestBox->GetState(state); state.ApplyLinearImpulse(Oyster::Math::Float3(0,0,4)); rigidBody_TestBox->SetState(state); - API::Instance().AddObject(rigidBody_TestBox); - */ + + // add gravitation API::Gravity gravityWell; - gravityWell.gravityType = API::Gravity::GravityType_Well; gravityWell.well.mass = 10e12f; gravityWell.well.position = Oyster::Math::Float4(0,0,0,1); - API::Instance().AddGravity(gravityWell); } @@ -85,7 +79,10 @@ void Level::RespawnPlayer(Player *player) Object* Level::GetObj( int ID) const { - return (Object*)testBox; + if( ID == 0 ) + return (Object*)levelObj; + else + return (Object*)testBox; } void Level::PhysicsOnMoveLevel(const ICustomBody *object) { diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 7b01e195..0ad18f85 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -40,7 +40,7 @@ Object::Object(void* collisionFunc, OBJECT_TYPE type) Oyster::Physics::API::Instance().AddObject(rigidBody); - rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); + //rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); //rigidBody->gameObjectRef = this; @@ -53,7 +53,7 @@ Object::Object(ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type) { Oyster::Physics::API::Instance().AddObject(rigidBody); - rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); + //rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); this->objectID = GID(); @@ -63,7 +63,7 @@ Object::Object(ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type) Object::Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) { Oyster::Physics::API::Instance().AddObject(rigidBody); - rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); + //rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); this->rigidBody = rigidBody; this->type = type; From 446bdd412bb104b401d58b87e763f8f61f931422 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Wed, 29 Jan 2014 14:33:21 +0100 Subject: [PATCH 65/76] GL - updated constructors to set before and after collision response functions --- Code/Game/GameLogic/CollisionManager.cpp | 14 +++- Code/Game/GameLogic/DynamicObject.cpp | 29 +++++--- Code/Game/GameLogic/DynamicObject.h | 8 ++- Code/Game/GameLogic/Game.cpp | 24 ------- Code/Game/GameLogic/Game_PlayerData.cpp | 13 ++-- Code/Game/GameLogic/Level.cpp | 8 ++- Code/Game/GameLogic/Level.h | 5 +- Code/Game/GameLogic/Object.cpp | 92 +++++++++++++----------- Code/Game/GameLogic/Object.h | 10 +-- Code/Game/GameLogic/Player.cpp | 47 +++++++----- Code/Game/GameLogic/Player.h | 7 +- Code/Game/GameLogic/StaticObject.cpp | 36 +++++----- Code/Game/GameLogic/StaticObject.h | 8 ++- 13 files changed, 173 insertions(+), 128 deletions(-) diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 495c2ac5..00830301 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -64,10 +64,18 @@ using namespace GameLogic; } } - //Oyster::Physics::ICustomBody::SubscriptMessage - void Level::LevelCollision(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) + Oyster::Physics::ICustomBody::SubscriptMessage Object::DefaultCollisionBefore(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj) { - //return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; + return Physics::ICustomBody::SubscriptMessage_none; + } + //Oyster::Physics::ICustomBody::SubscriptMessage + Oyster::Physics::ICustomBody::SubscriptMessage Level::LevelCollisionBefore(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj) + { + return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; + } + Oyster::Physics::ICustomBody::SubscriptMessage Level::LevelCollisionAfter(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss) + { + return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; } void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj, void* args) diff --git a/Code/Game/GameLogic/DynamicObject.cpp b/Code/Game/GameLogic/DynamicObject.cpp index 1ccc12ef..437dca2c 100644 --- a/Code/Game/GameLogic/DynamicObject.cpp +++ b/Code/Game/GameLogic/DynamicObject.cpp @@ -7,21 +7,34 @@ using namespace GameLogic; DynamicObject::DynamicObject() :Object() { - -} -DynamicObject::DynamicObject(void* collisionFunc, OBJECT_TYPE type) - :Object(collisionFunc, type) +} +DynamicObject::DynamicObject(OBJECT_TYPE type) + :Object(type) +{ + +} +DynamicObject::DynamicObject(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type) + :Object(rigidBody,type) { } -DynamicObject::DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) - :Object(rigidBody, collisionFunc, type) +DynamicObject::DynamicObject(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) + :Object(collisionFuncBefore,collisionFuncAfter,type) { - -} +} +DynamicObject::DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) + :Object(rigidBody, collisionFuncBefore, collisionFuncAfter, type) +{ + +} +DynamicObject::DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) + :Object(rigidBody, collisionFuncBefore, collisionFuncAfter, type) +{ + +} DynamicObject::~DynamicObject(void) { diff --git a/Code/Game/GameLogic/DynamicObject.h b/Code/Game/GameLogic/DynamicObject.h index 8912d455..3fcdae39 100644 --- a/Code/Game/GameLogic/DynamicObject.h +++ b/Code/Game/GameLogic/DynamicObject.h @@ -14,8 +14,12 @@ namespace GameLogic public: DynamicObject(); - DynamicObject(void* collisionFunc, OBJECT_TYPE type); - DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); + DynamicObject(OBJECT_TYPE type); + DynamicObject(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type); + DynamicObject(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + DynamicObject(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); + ~DynamicObject(void); private: diff --git a/Code/Game/GameLogic/Game.cpp b/Code/Game/GameLogic/Game.cpp index 9f9c930c..ca666c2f 100644 --- a/Code/Game/GameLogic/Game.cpp +++ b/Code/Game/GameLogic/Game.cpp @@ -100,30 +100,6 @@ bool Game::NewFrame() API::Instance().Update(); - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->EndFrame(); - } - - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->BeginFrame(); - } - - API::Instance().Update(); - - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->EndFrame(); - } - - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->BeginFrame(); - } - - API::Instance().Update(); - for (unsigned int i = 0; i < this->players.Size(); i++) { if(this->players[i]->player) this->players[i]->player->EndFrame(); diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index 81917071..a8cd665b 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -4,16 +4,19 @@ using namespace GameLogic; Game::PlayerData::PlayerData() -{ - Oyster::Physics::API::SimpleBodyDescription sbDesc; +{ //set some stats that are appropriate to a player - + Oyster::Physics::API::SimpleBodyDescription sbDesc; + sbDesc.centerPosition = Oyster::Math::Float3(0,15,0); + sbDesc.size = Oyster::Math::Float3(4,7,4); + //create rigid body Oyster::Physics::ICustomBody *rigidBody = Oyster::Physics::API::Instance().CreateRigidBody(sbDesc).Release(); - + //create player with this rigid body - this->player = new Player(rigidBody,Player::PlayerCollision,OBJECT_TYPE::OBJECT_TYPE_PLAYER); + this->player = new Player(rigidBody,Object::DefaultCollisionBefore, Player::PlayerCollision, OBJECT_TYPE::OBJECT_TYPE_PLAYER); this->player->GetRigidBody()->SetCustomTag(this); + } Game::PlayerData::PlayerData(int playerID,int teamID) { diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 12a09587..02fc8961 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -20,6 +20,7 @@ void Level::InitiateLevel(std::string levelPath) } void Level::InitiateLevel(float radius) { + // add level sphere API::SphericalBodyDescription sbDesc; sbDesc.centerPosition = Oyster::Math::Float4(0,0,0,1); @@ -33,8 +34,8 @@ void Level::InitiateLevel(float radius) rigidBody->GetState(state); state.SetRestitutionCoeff(0.01); rigidBody->SetState(state); - - levelObj = new StaticObject(rigidBody, LevelCollision, OBJECT_TYPE::OBJECT_TYPE_WORLD); + + levelObj = new StaticObject(rigidBody, LevelCollisionBefore, LevelCollisionAfter, OBJECT_TYPE::OBJECT_TYPE_WORLD); rigidBody->SetCustomTag(levelObj); @@ -47,7 +48,8 @@ void Level::InitiateLevel(float radius) ICustomBody* rigidBody_TestBox = API::Instance().CreateRigidBody(sbDesc_TestBox).Release(); rigidBody_TestBox->SetSubscription(Level::PhysicsOnMoveLevel); - testBox = new DynamicObject(rigidBody_TestBox,LevelCollision,OBJECT_TYPE::OBJECT_TYPE_BOX); + + testBox = new DynamicObject(rigidBody_TestBox, OBJECT_TYPE::OBJECT_TYPE_BOX); rigidBody_TestBox->SetCustomTag(testBox); rigidBody_TestBox->GetState(state); state.ApplyLinearImpulse(Oyster::Math::Float3(0,0,4)); diff --git a/Code/Game/GameLogic/Level.h b/Code/Game/GameLogic/Level.h index 9180a89e..60f7a932 100644 --- a/Code/Game/GameLogic/Level.h +++ b/Code/Game/GameLogic/Level.h @@ -57,8 +57,9 @@ namespace GameLogic * @param rigidBodyLevel: physics object of the level * @param obj: physics object for the object that collided with the level ********************************************************/ - static void LevelCollision(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); - + static Oyster::Physics::ICustomBody::SubscriptMessage LevelCollisionBefore(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj); + static Oyster::Physics::ICustomBody::SubscriptMessage LevelCollisionAfter(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj, Oyster::Math::Float kineticEnergyLoss); + Object* GetObj( int ID ) const; static void PhysicsOnMoveLevel(const Oyster::Physics::ICustomBody *object); diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 0ad18f85..9ec51f1e 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -16,59 +16,78 @@ const Game *Object::gameInstance = (Game*)(&Game::Instance()); Object::Object() { API::SimpleBodyDescription sbDesc; - //sbDesc.centerPosition = - - //poi - ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); - + this->rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); Oyster::Physics::API::Instance().AddObject(rigidBody); - //rigidBody->gameObjectRef = this; - - this->objectID = GID(); - this->type = OBJECT_TYPE::OBJECT_TYPE_UNKNOWN; + this->objectID = GID(); + this->getState = this->rigidBody->GetState(); + this->setState = this->getState; } -Object::Object(void* collisionFunc, OBJECT_TYPE type) +Object::Object(OBJECT_TYPE type) { API::SimpleBodyDescription sbDesc; - //poi this->rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); - Oyster::Physics::API::Instance().AddObject(rigidBody); - - //rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); - - //rigidBody->gameObjectRef = this; - - this->objectID = GID(); - this->type = type; + this->objectID = GID(); + this->getState = this->rigidBody->GetState(); + this->setState = this->getState; } -Object::Object(ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type) +Object::Object(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type) { Oyster::Physics::API::Instance().AddObject(rigidBody); - - //rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); - - this->objectID = GID(); - - this->type = type; -} - -Object::Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) -{ - Oyster::Physics::API::Instance().AddObject(rigidBody); - //rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_Collision)(collisionFunc)); - this->rigidBody = rigidBody; this->type = type; this->objectID = GID(); + this->getState = this->rigidBody->GetState(); + this->setState = this->getState; +} + +Object::Object(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) +{ + API::SimpleBodyDescription sbDesc; + + this->rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); + Oyster::Physics::API::Instance().AddObject(rigidBody); + this->type = type; + this->objectID = GID(); + this->getState = this->rigidBody->GetState(); + this->setState = this->getState; +} + +Object::Object(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) +{ + Oyster::Physics::API::Instance().AddObject(rigidBody); + + this->rigidBody = rigidBody; + this->rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_BeforeCollisionResponse)(collisionFuncBefore)); + this->rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_AfterCollisionResponse)(collisionFuncAfter)); + + this->type = type; + this->objectID = GID(); + this->getState = this->rigidBody->GetState(); + this->setState = this->getState; +} + +Object::Object(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) +{ + Oyster::Physics::API::Instance().AddObject(rigidBody); + + this->rigidBody = rigidBody; + this->rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_BeforeCollisionResponse)(collisionFuncBefore)); + this->rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_AfterCollisionResponse)(collisionFuncAfter)); + + + this->type = type; + this->objectID = GID(); + this->getState = this->rigidBody->GetState(); + this->setState = this->getState; } void Object::ApplyLinearImpulse(Oyster::Math::Float3 force) @@ -107,19 +126,12 @@ void Object::BeginFrame() void Object::EndFrame() { - //Oyster::Math::Float rot = (setState.GetGravityNormal().xyz).Dot(getState.GetGravityNormal().xyz); - //Oyster::Math::Float3 axis = (setState.GetGravityNormal().xyz).Cross(getState.GetGravityNormal().xyz); Oyster::Math::Float4x4 rotMatrix = setState.GetOrientation(); //Oyster::Math3D::RotationMatrix(rot, axis); //Oyster::Math3D::SnapAxisYToNormal_UsingNlerp(rotMatrix, -setState.GetGravityNormal()); //setState.SetOrientation(rotMatrix); - this->getState = this->rigidBody->GetState(); - - - this->setState = this->getState; - } Oyster::Math::Float3 Object::GetPosition() { diff --git a/Code/Game/GameLogic/Object.h b/Code/Game/GameLogic/Object.h index d3adbcba..24149116 100644 --- a/Code/Game/GameLogic/Object.h +++ b/Code/Game/GameLogic/Object.h @@ -18,9 +18,11 @@ namespace GameLogic { public: Object(); - Object(void* collisionFunc, OBJECT_TYPE type); - Object(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFunc, OBJECT_TYPE type); - Object(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); + Object(OBJECT_TYPE type); + Object(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type); + Object(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + Object(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + Object(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); ~Object(void); // API overrides @@ -35,7 +37,7 @@ namespace GameLogic void BeginFrame(); void EndFrame(); - + static Oyster::Physics::ICustomBody::SubscriptMessage DefaultCollisionBefore(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj); private: OBJECT_TYPE type; int objectID; diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index 54e24703..c366fb8f 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -8,20 +8,38 @@ using namespace GameLogic; using namespace Oyster::Physics; Player::Player() - :DynamicObject(Player::PlayerCollision, OBJECT_TYPE::OBJECT_TYPE_PLAYER) + :DynamicObject() { - weapon = new Weapon(); - - life = 100; - teamID = -1; - playerState = PLAYER_STATE::PLAYER_STATE_IDLE; - lookDir = Oyster::Math::Float4(0,0,-1,0); - setState.SetCenterPosition(Oyster::Math::Float3(0,15,0)); - setState.SetReach(Oyster::Math::Float3(2,3.5,2)); + +} +Player::Player(OBJECT_TYPE type) + :DynamicObject(type) +{ + InitPlayer(); +} +Player::Player(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type) + :DynamicObject(rigidBody,type) +{ + InitPlayer(); } -Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) - :DynamicObject(rigidBody, collisionFunc, type) +Player::Player(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) + :DynamicObject(collisionFuncBefore,collisionFuncAfter,type) +{ + InitPlayer(); +} +Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) + :DynamicObject(rigidBody, collisionFuncBefore, collisionFuncAfter, type) +{ + InitPlayer(); +} +Player::Player(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) + :DynamicObject(rigidBody, collisionFuncBefore, collisionFuncAfter, type) +{ + InitPlayer(); +} + +void Player::InitPlayer() { weapon = new Weapon(2,this); @@ -29,9 +47,6 @@ Player::Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oy this->teamID = -1; this->playerState = PLAYER_STATE_IDLE; lookDir = Oyster::Math::Float4(0,0,-1,0); - - setState.SetCenterPosition(Oyster::Math::Float3(0,15,0)); - setState.SetReach(Oyster::Math::Float3(2,3.5,2)); } Player::~Player(void) @@ -40,11 +55,9 @@ Player::~Player(void) { delete weapon; weapon = NULL; - } - + } } - void Player::Move(const PLAYER_MOVEMENT &movement) { switch(movement) diff --git a/Code/Game/GameLogic/Player.h b/Code/Game/GameLogic/Player.h index d67a18fe..0df0d040 100644 --- a/Code/Game/GameLogic/Player.h +++ b/Code/Game/GameLogic/Player.h @@ -16,8 +16,13 @@ namespace GameLogic { public: Player(void); - Player(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); + Player(OBJECT_TYPE type); + Player(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type); + Player(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + Player(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + Player(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); ~Player(void); + void InitPlayer(); /******************************************************** * Moves the player based on input diff --git a/Code/Game/GameLogic/StaticObject.cpp b/Code/Game/GameLogic/StaticObject.cpp index b441dabe..21b339be 100644 --- a/Code/Game/GameLogic/StaticObject.cpp +++ b/Code/Game/GameLogic/StaticObject.cpp @@ -8,29 +8,33 @@ StaticObject::StaticObject() :Object() { -} - -StaticObject::StaticObject(void* collisionFunc, OBJECT_TYPE type) - :Object(collisionFunc,type) -{ - } StaticObject::StaticObject(OBJECT_TYPE type) - :Object(NULL,type) + :Object(type) { - + } -/*StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type) - :Object(rigidBody,collisionFunc,type) +StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type) + :Object(rigidBody,type) { - -}*/ -StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) - :Object(rigidBody, collisionFunc, type) -{ - + } +StaticObject::StaticObject(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) + :Object(collisionFuncBefore,collisionFuncAfter,type) +{ + +} +StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type) + :Object(rigidBody, collisionFuncBefore, collisionFuncAfter, type) +{ + +} +StaticObject::StaticObject(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type) + :Object(rigidBody, collisionFuncBefore, collisionFuncAfter, type) +{ + +} StaticObject::~StaticObject(void) { diff --git a/Code/Game/GameLogic/StaticObject.h b/Code/Game/GameLogic/StaticObject.h index 5306d43b..c51f93a1 100644 --- a/Code/Game/GameLogic/StaticObject.h +++ b/Code/Game/GameLogic/StaticObject.h @@ -16,10 +16,12 @@ namespace GameLogic public: StaticObject(); - StaticObject(void* collisionFunc, OBJECT_TYPE type); - //StaticObject(Oyster::Physics::ICustomBody *rigidBody,void* collisionFunc, OBJECT_TYPE type); - StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void (*collisionFunc)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); StaticObject(OBJECT_TYPE type); + StaticObject(Oyster::Physics::ICustomBody *rigidBody, OBJECT_TYPE type); + StaticObject(void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + StaticObject(Oyster::Physics::ICustomBody *rigidBody ,void* collisionFuncBefore, void* collisionFuncAfter, OBJECT_TYPE type); + StaticObject(Oyster::Physics::ICustomBody *rigidBody ,Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter), Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss), OBJECT_TYPE type); + ~StaticObject(void); private: From 88e67ff8762c440808e891e154fa173809bd810d Mon Sep 17 00:00:00 2001 From: Dander7BD Date: Wed, 29 Jan 2014 14:57:39 +0100 Subject: [PATCH 66/76] Inertia big patch It's ali-..WORKS! It works! --- Code/GamePhysics/PhysicsStructs-Impl.h | 21 +++--------------- Code/OysterPhysics3D/Inertia.cpp | 30 +++++++++++++++++++------- Code/OysterPhysics3D/OysterPhysics3D.h | 2 +- Code/OysterPhysics3D/RigidBody.cpp | 26 +++------------------- 4 files changed, 29 insertions(+), 50 deletions(-) diff --git a/Code/GamePhysics/PhysicsStructs-Impl.h b/Code/GamePhysics/PhysicsStructs-Impl.h index fa5533fa..05cba2da 100644 --- a/Code/GamePhysics/PhysicsStructs-Impl.h +++ b/Code/GamePhysics/PhysicsStructs-Impl.h @@ -15,7 +15,7 @@ namespace Oyster this->rotation = ::Oyster::Math::Float4x4::identity; this->centerPosition = ::Oyster::Math::Float3::null; this->size = ::Oyster::Math::Float3( 1.0f ); - this->mass = 12.0f; + this->mass = 6.0f; this->restitutionCoeff = 1.0f; this->frictionCoeff_Dynamic = 0.5f; this->frictionCoeff_Static = 0.5f; @@ -124,7 +124,7 @@ namespace Oyster inline const ::Oyster::Math::Float3 & CustomBodyState::GetAngularAxis() const { - return this->angularAxis; + return ::Utility::Value::Radian(this->angularAxis); } inline ::Oyster::Math::Float4x4 CustomBodyState::GetRotation() const @@ -201,8 +201,6 @@ namespace Oyster { if( m != 0.0f ) { // sanity block! - // Formula::LinearMomentum( m, Formula::LinearVelocity(this->mass, this->linearMomentum) ) - // is the same as (this->linearMomentum / this->mass) * m = (m / this->mass) * this->linearMomentum this->linearMomentum *= (m / this->mass); this->mass = m; } @@ -263,19 +261,6 @@ namespace Oyster this->isSpatiallyAltered = this->isDisturbed = true; } - /*inline void CustomBodyState::SetRotation( const ::Oyster::Math::Float4x4 &rotation ) - { - this->SetRotation( ::Oyster::Math3D::AngularAxis(rotation) ); - } - - inline void CustomBodyState::SetOrientation( const ::Oyster::Math::Float4x4 &orientation ) - { - this->SetRotation( ::Oyster::Math3D::ExtractAngularAxis(orientation) ); - this->SetCenterPosition( orientation.v[3] ); - }*/ - - - inline void CustomBodyState::SetLinearMomentum( const ::Oyster::Math::Float3 &g ) { this->linearMomentum = g; @@ -334,8 +319,8 @@ namespace Oyster ::Oyster::Math::Float3 offset = at - this->centerPos; ::Oyster::Math::Float3 deltaAngularImpulse = ::Oyster::Physics3D::Formula::AngularMomentum( j, offset ); this->linearImpulse += j - ::Oyster::Physics3D::Formula::TangentialLinearMomentum( deltaAngularImpulse, offset ); - this->angularImpulse += deltaAngularImpulse; + this->angularImpulse += deltaAngularImpulse; this->isDisturbed = true; } diff --git a/Code/OysterPhysics3D/Inertia.cpp b/Code/OysterPhysics3D/Inertia.cpp index 2e0e436b..45551e2c 100644 --- a/Code/OysterPhysics3D/Inertia.cpp +++ b/Code/OysterPhysics3D/Inertia.cpp @@ -32,10 +32,17 @@ Float3 MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, con } Float3 & MomentOfInertia::CalculateAngularVelocity( const Quaternion &externR, const Float3 &h, Float3 &targetMem ) const -{ // w = (R * I_R) * I_M^-1 * (R * I_R)^-1 * h - Float4x4 rotation = RotationMatrix( externR ) * RotationMatrix( this->rotation ); - Float4 w = rotation.GetInverse() * Float4( h, 0.0f ); - return targetMem = rotation * w.PiecewiseMultiplicationAdd( Float4(1.0f / this->magnitude.x, 1.0f / this->magnitude.y, 1.0f / this->magnitude.z, 0.0f) ); +{ // w = h * | (2/3) * I_M^-1 (R I_R)^-1 h | / |h| + Float hMagnitudeSquared = h.Dot( h ); + if( hMagnitudeSquared > 0.0f ) + { + Float4x4 rotationInverse = (RotationMatrix( externR ) * RotationMatrix( this->rotation )).Transpose(); + Float4 v = rotationInverse * Float4( h, 0.0f ); + v.PiecewiseMultiplicationAdd( Float4((2.0f/3.0f) / this->magnitude.x, (2.0f/3.0f) / this->magnitude.y, (2.0f/3.0f) / this->magnitude.z, 0.0f) ); + return targetMem = (Float4( h, 0.0f ) * ( v.GetMagnitude() / ( (Float)::std::sqrt(hMagnitudeSquared)) ) ).xyz; + } + else + return targetMem = Float3::null; } Float3 MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float3 &w ) const @@ -44,8 +51,15 @@ Float3 MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, con } Float3 & MomentOfInertia::CalculateAngularMomentum( const Quaternion &externR, const Float3 &w, Float3 &targetMem ) const -{ // h = (R * I_R) * I_M * (R * I_R)^-1 * w - Float4x4 rotation = RotationMatrix( externR ) * RotationMatrix( this->rotation ); - Float4 h = rotation.GetInverse() * Float4( w, 0.0f ); - return targetMem = rotation * h.PiecewiseMultiplicationAdd( Float4(this->magnitude.x, this->magnitude.y, this->magnitude.z, 0.0f) ); +{ // h = w * | (3/2) * I_M (R I_R)^-1 w | / |w| + Float wMagnitudeSquared = w.Dot( w ); + if( wMagnitudeSquared > 0.0f ) + { + Float4x4 rotationInverse = (RotationMatrix( externR ) * RotationMatrix( this->rotation )).Transpose(); + Float4 v = rotationInverse * Float4( w, 0.0f ); + v.PiecewiseMultiplicationAdd( Float4((3.0f/2.0f) * this->magnitude.x, (3.0f/2.0f) * this->magnitude.y, (3.0f/2.0f) * this->magnitude.z, 0.0f) ); + return targetMem = (Float4( w, 0.0f ) * ( v.GetMagnitude() / (Float)::std::sqrt(wMagnitudeSquared) ) ).xyz; + } + else + return targetMem = Float3::null; } \ No newline at end of file diff --git a/Code/OysterPhysics3D/OysterPhysics3D.h b/Code/OysterPhysics3D/OysterPhysics3D.h index f814ff46..e1eb94e3 100644 --- a/Code/OysterPhysics3D/OysterPhysics3D.h +++ b/Code/OysterPhysics3D/OysterPhysics3D.h @@ -113,6 +113,7 @@ namespace Oyster { namespace Physics3D /****************************************************************** * Returns the world angular momentum of a mass in rotation. + * H = r x G * @todo TODO: improve doc ******************************************************************/ inline ::Oyster::Math::Float3 AngularMomentum( const ::Oyster::Math::Float3 linearMomentum, const ::Oyster::Math::Float3 &worldOffset ) @@ -381,7 +382,6 @@ namespace Oyster { namespace Physics3D return inertia; } - } } } } diff --git a/Code/OysterPhysics3D/RigidBody.cpp b/Code/OysterPhysics3D/RigidBody.cpp index 8e5f5f2b..0c60c597 100644 --- a/Code/OysterPhysics3D/RigidBody.cpp +++ b/Code/OysterPhysics3D/RigidBody.cpp @@ -53,22 +53,15 @@ void RigidBody::Update_LeapFrog( Float updateFrameLength ) this->centerPos += ( updateFrameLength / this->mass ) * AverageWithDelta( this->momentum_Linear, this->impulse_Linear ); // updating the angular - //Float4x4 rotationMatrix; ::Oyster::Math3D::RotationMatrix( this->rotation, rotationMatrix ); - // Important! The member data is all world data except the Inertia tensor. Thus a new InertiaTensor needs to be created to be compatible with the rest of the world data. - //Float4x4 wMomentOfInertiaTensor = TransformMatrix( rotationMatrix, this->momentOfInertiaTensor ); // RI - - // dO = dt * Formula::AngularVelocity( (RI)^-1, avg_H ) = dt * (RI)^-1 * avg_H - - //! HACK: @todo Rotation temporary disabled - //this->axis += Radian( Formula::AngularVelocity(wMomentOfInertiaTensor.GetInverse(), AverageWithDelta(this->momentum_Angular, this->impulse_Angular)) ); - this->axis += this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, AverageWithDelta(this->momentum_Angular, this->impulse_Angular) ); + // dO = dt * Formula::AngularVelocity( (RI)^-1, avg_H ) = dt * (RI)^-1 * avg_H + this->axis += updateFrameLength * this->momentOfInertiaTensor.CalculateAngularVelocity( this->rotation, AverageWithDelta(this->momentum_Angular, this->impulse_Angular) ); this->rotation = Rotation( this->axis ); // update momentums and clear impulse_Linear and impulse_Angular this->momentum_Linear += this->impulse_Linear; this->impulse_Linear = Float4::null; - //this->momentum_Angular += this->impulse_Angular; //! HACK: @todo Rotation temporary disabled + this->momentum_Angular += this->impulse_Angular; //! HACK: @todo Rotation temporary disabled this->impulse_Angular = Float4::null; } @@ -183,19 +176,6 @@ void RigidBody::SetMass_KeepMomentum( const Float &m ) } } -//void RigidBody::SetOrientation( const Float4x4 &o ) -//{ // by Dan Andersson -// this->axis = ExtractAngularAxis( o ); -// this->rotation = Rotation( this->axis ); -// this->centerPos = o.v[3].xyz; -//} -// -//void RigidBody::SetRotation( const Float4x4 &r ) -//{ // by Dan Andersson -// this->axis = ExtractAngularAxis( r ); -// this->rotation = Rotation( this->axis ); -//} - void RigidBody::SetMomentum_Linear( const Float3 &worldG, const Float3 &atWorldPos ) { // by Dan Andersson Float3 worldOffset = atWorldPos - this->centerPos; From 1e89bb4e307727e6803ff9d9ba576ab21914847f Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Wed, 29 Jan 2014 15:01:14 +0100 Subject: [PATCH 67/76] Fixed protocol messaging. Known crash when recieveing protocol on server after a while... --- Code/Game/GameLogic/Game.cpp | 24 --- Code/Game/GameLogic/Game.h | 2 +- Code/Game/GameLogic/GameAPI.h | 2 +- Code/Game/GameLogic/Game_PlayerData.cpp | 4 +- Code/Game/GameProtocols/GameProtocols.vcxproj | 1 - Code/Game/GameProtocols/GameplayProtocols.h | 13 -- Code/Game/GameProtocols/LobbyProtocols.h | 11 ++ Code/Game/GameProtocols/ObjectProtocols.h | 24 +++ Code/Game/GameProtocols/PlayerProtocols.h | 16 ++ Code/Game/GameProtocols/Protocols.h | 1 - Code/Game/GameServer/GameLobby.h | 8 +- Code/Game/GameServer/GameSession.h | 24 ++- .../GameServer/Implementation/GameLobby.cpp | 10 +- .../GameLobby_ProtocolParser.cpp | 33 ++-- .../GameServer/Implementation/GameServer.cpp | 5 +- .../Implementation/GameSession_Gameplay.cpp | 185 +++++++++++------- Code/Network/NetworkAPI/NetworkClient.cpp | 7 +- Code/Network/NetworkAPI/NetworkClient.h | 5 +- Code/Network/NetworkAPI/NetworkServer.cpp | 8 +- .../NetworkDependencies/Connection.cpp | 32 ++- Code/Network/NetworkDependencies/Connection.h | 9 +- .../Network/NetworkDependencies/IConnection.h | 8 +- Code/Network/NetworkDependencies/Listener.cpp | 10 +- Code/Network/NetworkDependencies/Listener.h | 6 +- 24 files changed, 278 insertions(+), 170 deletions(-) delete mode 100644 Code/Game/GameProtocols/GameplayProtocols.h diff --git a/Code/Game/GameLogic/Game.cpp b/Code/Game/GameLogic/Game.cpp index b22b3522..4380777e 100644 --- a/Code/Game/GameLogic/Game.cpp +++ b/Code/Game/GameLogic/Game.cpp @@ -100,30 +100,6 @@ bool Game::NewFrame() API::Instance().Update(); - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->EndFrame(); - } - - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->BeginFrame(); - } - - API::Instance().Update(); - - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->EndFrame(); - } - - for (unsigned int i = 0; i < this->players.Size(); i++) - { - if(this->players[i]->player) this->players[i]->player->BeginFrame(); - } - - API::Instance().Update(); - for (unsigned int i = 0; i < this->players.Size(); i++) { if(this->players[i]->player) this->players[i]->player->EndFrame(); diff --git a/Code/Game/GameLogic/Game.h b/Code/Game/GameLogic/Game.h index 11c8d325..7d40a3c5 100644 --- a/Code/Game/GameLogic/Game.h +++ b/Code/Game/GameLogic/Game.h @@ -39,7 +39,7 @@ namespace GameLogic Oyster::Math::Float4x4 GetOrientation() override; int GetID() const override; OBJECT_TYPE GetObjectType() const override; - void Rotate(const float x, const float y) override; + void Rotate(const Oyster::Math3D::Float3 lookDir) override; Player *player; }; diff --git a/Code/Game/GameLogic/GameAPI.h b/Code/Game/GameLogic/GameAPI.h index 12f5022f..2f052927 100644 --- a/Code/Game/GameLogic/GameAPI.h +++ b/Code/Game/GameLogic/GameAPI.h @@ -79,7 +79,7 @@ namespace GameLogic * @param x: The relative x axis * @param y: The relative y axis **/ - virtual void Rotate(const float x, const float y) = 0; + virtual void Rotate(const Oyster::Math3D::Float3 lookDir) = 0; /******************************************************** * Uses the chosen players weapon based on input diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index e651a02f..ed628c5e 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -49,7 +49,7 @@ OBJECT_TYPE Game::PlayerData::GetObjectType() const { return this->player->GetType(); } -void Game::PlayerData::Rotate(const float x, const float y) +void Game::PlayerData::Rotate(const Oyster::Math3D::Float3 lookDir) { - this->player->Rotate(x, y); + } \ No newline at end of file diff --git a/Code/Game/GameProtocols/GameProtocols.vcxproj b/Code/Game/GameProtocols/GameProtocols.vcxproj index a5d5b19f..826f9df4 100644 --- a/Code/Game/GameProtocols/GameProtocols.vcxproj +++ b/Code/Game/GameProtocols/GameProtocols.vcxproj @@ -155,7 +155,6 @@ - diff --git a/Code/Game/GameProtocols/GameplayProtocols.h b/Code/Game/GameProtocols/GameplayProtocols.h deleted file mode 100644 index 8ffcd827..00000000 --- a/Code/Game/GameProtocols/GameplayProtocols.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef GAMEPROTOCOLS_GAMEPLAYPROTOCOLS_H -#define GAMEPROTOCOLS_GAMEPLAYPROTOCOLS_H - -#include -#include -#include "ProtocolIdentificationID.h" - -namespace GameLogic -{ - -} - -#endif // !GAMEPROTOCOLS_GAMEPLAYPROTOCOLS_H diff --git a/Code/Game/GameProtocols/LobbyProtocols.h b/Code/Game/GameProtocols/LobbyProtocols.h index 4a262404..8968de74 100644 --- a/Code/Game/GameProtocols/LobbyProtocols.h +++ b/Code/Game/GameProtocols/LobbyProtocols.h @@ -156,6 +156,13 @@ namespace GameLogic list.Reserve(10); } + Protocol_LobbyGameData(Oyster::Network::CustomNetProtocol& p) + { + this->protocol[protocol_INDEX_ID].value = protocol_Lobby_GameData; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + list.Reserve(10); + } Oyster::Network::CustomNetProtocol* GetProtocol() override { int a = 1; @@ -186,6 +193,10 @@ namespace GameLogic this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + } + Protocol_LobbyMainData(Oyster::Network::CustomNetProtocol& p) + { + } Oyster::Network::CustomNetProtocol* GetProtocol() override { diff --git a/Code/Game/GameProtocols/ObjectProtocols.h b/Code/Game/GameProtocols/ObjectProtocols.h index d37aee44..9c4e2df1 100644 --- a/Code/Game/GameProtocols/ObjectProtocols.h +++ b/Code/Game/GameProtocols/ObjectProtocols.h @@ -21,6 +21,10 @@ namespace GameLogic object_ID = -1; pickup_ID = -1; + } + Protocol_ObjectPickup(Oyster::Network::CustomNetProtocol& p) + { + } Protocol_ObjectPickup(int objectID, short pickupID) { @@ -60,6 +64,10 @@ namespace GameLogic object_ID = -1; health = 0.0f; + } + Protocol_ObjectDamage(Oyster::Network::CustomNetProtocol& p) + { + } Protocol_ObjectDamage(int id, float hp) { @@ -100,6 +108,10 @@ namespace GameLogic } object_ID = -1; memset(&worldMatrix[0], 0, sizeof(float) * 16); + } + Protocol_ObjectPosition(Oyster::Network::CustomNetProtocol& p) + { + } Protocol_ObjectPosition(float m[16], int id) { @@ -148,6 +160,10 @@ namespace GameLogic } object_ID = -1; memset(&worldMatrix[0], 0, sizeof(float) * 16); + } + Protocol_ObjectEnable(Oyster::Network::CustomNetProtocol& p) + { + } Protocol_ObjectEnable(float m[16], int id) { @@ -188,6 +204,10 @@ namespace GameLogic this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; + } + Protocol_ObjectDisable(Oyster::Network::CustomNetProtocol& p) + { + } Protocol_ObjectDisable(int id, float time) { @@ -229,6 +249,10 @@ namespace GameLogic { this->protocol[i].type = Oyster::Network::NetAttributeType_Float; } + } + Protocol_ObjectCreate(Oyster::Network::CustomNetProtocol& p) + { + } Protocol_ObjectCreate(float m[16], int id, char *path) { diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index ffb009cf..988693ce 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -32,6 +32,10 @@ namespace GameLogic this->protocol[2].type = Oyster::Network::NetAttributeType_Bool; this->protocol[3].type = Oyster::Network::NetAttributeType_Bool; this->protocol[4].type = Oyster::Network::NetAttributeType_Bool; + } + Protocol_PlayerMovement(Oyster::Network::CustomNetProtocol& p) + { + } const Protocol_PlayerMovement& operator=(Oyster::Network::CustomNetProtocol& val) { @@ -72,6 +76,10 @@ namespace GameLogic this->protocol[2].type = Oyster::Network::NetAttributeType_Float; this->protocol[3].type = Oyster::Network::NetAttributeType_Float; + } + Protocol_PlayerLook(Oyster::Network::CustomNetProtocol& p) + { + } const Protocol_PlayerLook& operator=(Oyster::Network::CustomNetProtocol& val) { @@ -108,6 +116,10 @@ namespace GameLogic this->protocol[2].type = Oyster::Network::NetAttributeType_Float; this->protocol[3].type = Oyster::Network::NetAttributeType_Float; + } + Protocol_PlayerChangeWeapon(Oyster::Network::CustomNetProtocol& p) + { + } const Protocol_PlayerChangeWeapon& operator=(Oyster::Network::CustomNetProtocol& val) { @@ -132,6 +144,10 @@ namespace GameLogic this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; + } + Protocol_PlayerShot(Oyster::Network::CustomNetProtocol& p) + { + } const Protocol_PlayerShot& operator=(Oyster::Network::CustomNetProtocol& val) { diff --git a/Code/Game/GameProtocols/Protocols.h b/Code/Game/GameProtocols/Protocols.h index 1e7236c8..06b0df82 100644 --- a/Code/Game/GameProtocols/Protocols.h +++ b/Code/Game/GameProtocols/Protocols.h @@ -5,6 +5,5 @@ #include "PlayerProtocols.h" #include "LobbyProtocols.h" #include "GeneralProtocols.h" -#include "GameplayProtocols.h" #endif // !GAMEPROTOCOLS_GAMEPROTOCOLS_H diff --git a/Code/Game/GameServer/GameLobby.h b/Code/Game/GameServer/GameLobby.h index 1391641e..47e3b329 100644 --- a/Code/Game/GameServer/GameLobby.h +++ b/Code/Game/GameServer/GameLobby.h @@ -29,11 +29,11 @@ namespace DanBias void GeneralText(GameLogic::Protocol_General_Text& p, Oyster::Network::NetworkClient* c); //id = protocol_General_Text: void LobbyCreateGame(GameLogic::Protocol_LobbyCreateGame& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Create: void LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Start: - void LobbyJoin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Join: - void LobbyLogin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Login: + void LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Join: + void LobbyLogin(GameLogic::Protocol_LobbyLogin& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Login: void LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Refresh: - void LobbyMainData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_MainData: - void LobbyGameData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_GameData: + void LobbyMainData(GameLogic::Protocol_LobbyMainData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_MainData: + void LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_GameData: private: void ClientEventCallback(Oyster::Network::NetEvent e) override; diff --git a/Code/Game/GameServer/GameSession.h b/Code/Game/GameServer/GameSession.h index ed480c35..e75be66d 100644 --- a/Code/Game/GameServer/GameSession.h +++ b/Code/Game/GameServer/GameSession.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace DanBias @@ -55,17 +56,31 @@ namespace DanBias //Private member functions private: - //Handles all events recieved - //void ParseEvents(); - + // TODO: find out what this method does.. void ClientEventCallback(Oyster::Network::NetEvent e) override; - void ParseProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c); + //Sends a client to the owner, if obj is NULL then all clients is sent void SendToOwner(DanBias::GameClient* obj); //Frame function, derived from IThreadObject bool DoWork ( ) override; + + private: + void ParseProtocol (Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c); + + void Gameplay_PlayerMovement ( GameLogic::Protocol_PlayerMovement& p, DanBias::GameClient* c ); + void Gameplay_PlayerLookDir ( GameLogic::Protocol_PlayerLook& p, DanBias::GameClient* c ); + void Gameplay_PlayerChangeWeapon ( GameLogic::Protocol_PlayerChangeWeapon& p, DanBias::GameClient* c ); + void Gameplay_PlayerShot ( GameLogic::Protocol_PlayerShot& p, DanBias::GameClient* c ); + void Gameplay_ObjectPickup ( GameLogic::Protocol_ObjectPickup& p, DanBias::GameClient* c ); + void Gameplay_ObjectDamage ( GameLogic::Protocol_ObjectDamage& p, DanBias::GameClient* c ); + void Gameplay_ObjectPosition ( GameLogic::Protocol_ObjectPosition& p, DanBias::GameClient* c ); + void Gameplay_ObjectEnabled ( GameLogic::Protocol_ObjectEnable& p, DanBias::GameClient* c ); + void Gameplay_ObjectDisabled ( GameLogic::Protocol_ObjectDisable& p, DanBias::GameClient* c ); + void Gameplay_ObjectCreate ( GameLogic::Protocol_ObjectCreate& p, DanBias::GameClient* c ); + void General_Status ( GameLogic::Protocol_General_Status& p, DanBias::GameClient* c ); + void General_Text ( GameLogic::Protocol_General_Text& p, DanBias::GameClient* c ); //Private member variables private: @@ -78,6 +93,7 @@ namespace DanBias bool isRunning; Utility::WinTimer timer; + //Callback method recieving from gamelogic static void ObjectMove(GameLogic::IObjectData* movedObject); diff --git a/Code/Game/GameServer/Implementation/GameLobby.cpp b/Code/Game/GameServer/Implementation/GameLobby.cpp index cd90793a..9396d71d 100644 --- a/Code/Game/GameServer/Implementation/GameLobby.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby.cpp @@ -4,6 +4,7 @@ #include "..\GameLobby.h" #include #include +#include using namespace Utility::DynamicMemory; using namespace Oyster::Network; @@ -24,6 +25,9 @@ namespace DanBias void GameLobby::Update() { + if(GetAsyncKeyState(VK_DOWN)) + this->Send(*GameLogic::Protocol_General_Status().GetProtocol()); + this->ProcessClients(); } GameLobby::operator bool() @@ -40,15 +44,19 @@ namespace DanBias case NetworkClient::ClientEventArgs::EventType_ProtocolFailedToRecieve: break; case NetworkClient::ClientEventArgs::EventType_ProtocolFailedToSend: + printf("\t(%i : %s) - EventType_ProtocolFailedToSend\n", e.sender->GetID(), e.sender->GetIpAddress().c_str()); + e.sender->Disconnect(); break; case NetworkClient::ClientEventArgs::EventType_ProtocolRecieved: + printf("\t(%i : %s) - EventType_ProtocolRecieved\n", e.sender->GetID(), e.sender->GetIpAddress().c_str()); this->ParseProtocol(e.args.data.protocol, e.sender); break; } } void GameLobby::ClientConnectedEvent(Utility::DynamicMemory::SmartPointer client) { - //Attach(client); + printf("New client(%i) connected - %s \n", client->GetID(), client->GetIpAddress().c_str()); + Attach(client); } }//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp index 45ec4dfa..335bf55b 100644 --- a/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp @@ -9,30 +9,23 @@ void GameLobby::ParseProtocol(Oyster::Network::CustomNetProtocol& p, NetworkClie { switch (p[0].value.netShort) { - //LobbyStartGame(GameLogic::Protocol_LobbyStartGame(p), c); - //LobbyRefresh(GameLogic::Protocol_LobbyRefresh(p), c); - //LobbyLogin(GameLogic::Protocol_LobbyLogin(p), c); - //LobbyJoin(GameLogic::Protocol_LobbyJoin(p), c); - //GeneralStatus(GameLogic::Protocol_General_Status(p), c); - //GeneralText(GameLogic::Protocol_General_Text(p), c); - - case protocol_General_Status: + case protocol_General_Status: this->GeneralStatus (Protocol_General_Status (p), c); break; - case protocol_General_Text: + case protocol_General_Text: this->GeneralText (Protocol_General_Text (p), c); break; - case protocol_Lobby_Create: + case protocol_Lobby_Create: this->LobbyCreateGame (Protocol_LobbyCreateGame (p), c); break; - case protocol_Lobby_Start: + case protocol_Lobby_Start: this->LobbyStartGame (Protocol_LobbyStartGame (p), c); break; - case protocol_Lobby_Join: + case protocol_Lobby_Join: this->LobbyJoin (Protocol_LobbyJoin (p), c); break; - case protocol_Lobby_Login: + case protocol_Lobby_Login: this->LobbyLogin (Protocol_LobbyLogin (p), c); break; - case protocol_Lobby_Refresh: + case protocol_Lobby_Refresh: this->LobbyRefresh (Protocol_LobbyRefresh (p), c); break; - case protocol_Lobby_MainData: + case protocol_Lobby_MainData: this->LobbyMainData (Protocol_LobbyMainData (p), c); break; - case protocol_Lobby_GameData: + case protocol_Lobby_GameData: this->LobbyGameData (Protocol_LobbyGameData (p), c); break; } } @@ -69,7 +62,7 @@ void GameLobby::LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Ne { } -void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c) { //for (unsigned int i = 0; i < this->gameLobby.Size(); i++) //{ @@ -80,7 +73,7 @@ void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network:: // } //} } -void GameLobby::LobbyLogin(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +void GameLobby::LobbyLogin(GameLogic::Protocol_LobbyLogin& p, Oyster::Network::NetworkClient* c) { } @@ -88,11 +81,11 @@ void GameLobby::LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Networ { //Dont need to handle this on the server... } -void GameLobby::LobbyMainData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +void GameLobby::LobbyMainData(GameLogic::Protocol_LobbyMainData& p, Oyster::Network::NetworkClient* c) { } -void GameLobby::LobbyGameData(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) +void GameLobby::LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c) { } diff --git a/Code/Game/GameServer/Implementation/GameServer.cpp b/Code/Game/GameServer/Implementation/GameServer.cpp index ac2abc8a..d98794a4 100644 --- a/Code/Game/GameServer/Implementation/GameServer.cpp +++ b/Code/Game/GameServer/Implementation/GameServer.cpp @@ -41,7 +41,7 @@ DanBiasServerReturn GameServerAPI::Create(const GameInitDesc& desc) return DanBiasServerReturn_Error; } - std::printf("Server created!\t-\t%s: [%i]\n", server.GetLanAddress().c_str(), desc.listenPort); + std::printf("Server created!\t-\t%s: [%i]\n\n", server.GetLanAddress().c_str(), desc.listenPort); return DanBiasServerReturn_Sucess; } @@ -53,8 +53,7 @@ void GameServerAPI::Start() while (true) { - int c = server.ProcessConnectedClients(); - if(c > 0) printf(" - [%i] client(s) connected!\n", c); + server.ProcessConnectedClients(); lobby.Update(); if(GetAsyncKeyState(0x51)) //Q for exit diff --git a/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp index ef1898be..151cc739 100644 --- a/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp @@ -45,91 +45,142 @@ namespace DanBias return this->isRunning; } - //void GameSession::ParseEvents() - //{ - // if( !this->box->IsEmpty() ) - // { - // NetworkSession::NetEvent &e = this->box->Fetch(); - // static int ii = 0; - // printf("%i - Message recieved! [%i]\n", ii++, e.protocol[0].value); - // - // if(e.protocol[0].type != Oyster::Network::NetAttributeType_Short) return; - // - // if( ProtocolIsGameplay(e.protocol[protocol_INDEX_ID].value.netShort) ) - // ParseGameplayEvent(e.protocol, e.gameClient); - // - // if( ProtocolIsGeneral(e.protocol[protocol_INDEX_ID].value.netShort) ) - // ParseGeneralEvent(e.protocol, e.gameClient); - // } - //} + void GameSession::ClientEventCallback(NetEvent e) + { + + } + + void GameSession::ObjectMove(GameLogic::IObjectData* movedObject) + { + //GameLogic::IObjectData* obj = NULL; + //if(dynamic_cast(movedObject)) + // obj =((GameLogic::ILevelData*)movedObject)->GetObjectAt(0); + //if(obj) + //{ + // if(obj->GetType() == OBJECT_TYPE_BOX) + // { + // obj->GetID(); + // Oyster::Math::Float4x4 world =obj->GetOrientation(); + // Protocol_ObjectPosition p(world, 1); + // GameSession::gameSession->Send(p.GetProtocol()); + // } + //} + + } + +//*****************************************************// +//****************** Protocol methods *****************// +//******************************************************************************************************************// void GameSession::ParseProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c) { switch (p[protocol_INDEX_ID].value.netShort) { - case protocol_Gameplay_PlayerMovement: - { - if(p[1].value.netBool) //bool bForward; - c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_FORWARD); - if(p[2].value.netBool) //bool bBackward; - c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_BACKWARD); - if(p[3].value.netBool) //bool bStrafeLeft; - c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_LEFT); - if(p[4].value.netBool) //bool bStrafeRight; - c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_RIGHT); - } + case protocol_Gameplay_PlayerMovement: this->Gameplay_PlayerMovement ( Protocol_PlayerMovement (p), c ); break; - case protocol_Gameplay_PlayerLookDir: - { - Protocol_PlayerLook m; m = p; - //c->GetPlayer()->Rotate(m.dxMouse, m.dyMouse); - } + case protocol_Gameplay_PlayerLookDir: this->Gameplay_PlayerLookDir ( Protocol_PlayerLook (p), c ); break; - case protocol_Gameplay_PlayerChangeWeapon: - + case protocol_Gameplay_PlayerChangeWeapon: this->Gameplay_PlayerChangeWeapon ( Protocol_PlayerChangeWeapon (p), c ); break; - case protocol_Gameplay_ObjectDamage: - + case protocol_Gameplay_PlayerShot: this->Gameplay_PlayerShot ( Protocol_PlayerShot (p), c ); break; - case protocol_General_Status: - switch (p[1].value.netInt) - { - case GameLogic::Protocol_General_Status::States_disconected: - printf("Client with ID [%i] dissconnected\n", c->GetClient()->GetID()); - this->Detach(c->GetClient()->GetID()); - break; - - case GameLogic::Protocol_General_Status::States_idle: - - break; - - case GameLogic::Protocol_General_Status::States_ready: - - break; - - case GameLogic::Protocol_General_Status::States_leave: - - break; - } + case protocol_Gameplay_ObjectPickup: this->Gameplay_ObjectPickup ( Protocol_ObjectPickup (p), c ); break; - case protocol_General_Text: - { - GameLogic::Protocol_General_Text temp(p); - printf("Message recieved from (%i):\t %s\n", c->GetID(), temp.text.c_str()); - } + case protocol_Gameplay_ObjectDamage: this->Gameplay_ObjectDamage ( Protocol_ObjectDamage (p), c ); + break; + case protocol_Gameplay_ObjectPosition: this->Gameplay_ObjectPosition ( Protocol_ObjectPosition (p), c ); + break; + case protocol_Gameplay_ObjectEnabled: this->Gameplay_ObjectEnabled ( Protocol_ObjectEnable (p), c ); + break; + case protocol_Gameplay_ObjectDisabled: this->Gameplay_ObjectDisabled ( Protocol_ObjectDisable (p), c ); + break; + case protocol_Gameplay_ObjectCreate: this->Gameplay_ObjectCreate ( Protocol_ObjectCreate (p), c ); + break; + case protocol_General_Status: this->General_Status ( Protocol_General_Status (p), c ); + break; + case protocol_General_Text: this->General_Text ( Protocol_General_Text (p), c ); break; } } - void GameSession::ObjectMove(GameLogic::IObjectData* movedObject) + void GameSession::Gameplay_PlayerMovement ( Protocol_PlayerMovement& p, DanBias::GameClient* c ) { - movedObject->GetID(); - movedObject->GetOrientation(); + if(p.bForward) c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_FORWARD); + if(p.bBackward) c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_BACKWARD); + if(p.bLeft) c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_LEFT); + if(p.bRight) c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_RIGHT); } - - void GameSession::ClientEventCallback(NetEvent e) + void GameSession::Gameplay_PlayerLookDir ( Protocol_PlayerLook& p, DanBias::GameClient* c ) + { + Oyster::Math3D::Float3 lookDir; + lookDir.x = p.lookDirX; + lookDir.y = p.lookDirY; + lookDir.z = p.lookDirZ; + c->GetPlayer()->Rotate(lookDir); + } + void GameSession::Gameplay_PlayerChangeWeapon ( Protocol_PlayerChangeWeapon& p, DanBias::GameClient* c ) { } + void GameSession::Gameplay_PlayerShot ( Protocol_PlayerShot& p, DanBias::GameClient* c ) + { + c->GetPlayer()->UseWeapon(GameLogic::WEAPON_USE_PRIMARY_PRESS); + } + void GameSession::Gameplay_ObjectPickup ( Protocol_ObjectPickup& p, DanBias::GameClient* c ) + { + + } + void GameSession::Gameplay_ObjectDamage ( Protocol_ObjectDamage& p, DanBias::GameClient* c ) + { + + } + void GameSession::Gameplay_ObjectPosition ( Protocol_ObjectPosition& p, DanBias::GameClient* c ) + { + + } + void GameSession::Gameplay_ObjectEnabled ( Protocol_ObjectEnable& p, DanBias::GameClient* c ) + { + + } + void GameSession::Gameplay_ObjectDisabled ( Protocol_ObjectDisable& p, DanBias::GameClient* c ) + { + + } + void GameSession::Gameplay_ObjectCreate ( Protocol_ObjectCreate& p, DanBias::GameClient* c ) + { + + } + void GameSession::General_Status ( Protocol_General_Status& p, DanBias::GameClient* c ) + { + switch (p.status) + { + case GameLogic::Protocol_General_Status::States_disconected: + printf("Client with ID [%i] dissconnected\n", c->GetClient()->GetID()); + this->Detach(c->GetClient()->GetID()); + break; + + case GameLogic::Protocol_General_Status::States_idle: + + break; + + case GameLogic::Protocol_General_Status::States_ready: + + break; + + case GameLogic::Protocol_General_Status::States_leave: + + break; + } + } + void GameSession::General_Text ( Protocol_General_Text& p, DanBias::GameClient* c ) + { + printf("Message recieved from (%i):\t %s\n", c->GetID(), p.text.c_str()); + } + }//End namespace DanBias + + + + + diff --git a/Code/Network/NetworkAPI/NetworkClient.cpp b/Code/Network/NetworkAPI/NetworkClient.cpp index f5d7c643..9539cdeb 100644 --- a/Code/Network/NetworkAPI/NetworkClient.cpp +++ b/Code/Network/NetworkAPI/NetworkClient.cpp @@ -171,7 +171,7 @@ void NetworkClient::Update() } } -bool NetworkClient::Connect(int socket) +bool NetworkClient::Connect(ConnectionInfo& socket) { if(this->IsConnected()) return true; if(this->privateData) return false; @@ -250,4 +250,7 @@ void NetworkClient::DataRecieved(NetEvent e) void NetworkClient::NetworkCallback(Oyster::Network::CustomNetProtocol& p) {} - +std::string NetworkClient::GetIpAddress() +{ + return this->privateData->connection.GetIpAddress(); +} diff --git a/Code/Network/NetworkAPI/NetworkClient.h b/Code/Network/NetworkAPI/NetworkClient.h index 869a5100..dfe247de 100644 --- a/Code/Network/NetworkAPI/NetworkClient.h +++ b/Code/Network/NetworkAPI/NetworkClient.h @@ -15,6 +15,7 @@ namespace Oyster namespace Network { class NetworkSession; + struct ConnectionInfo; extern "C" { @@ -54,7 +55,7 @@ namespace Oyster /** * */ - bool Connect(int socket); + bool Connect(ConnectionInfo& data); /** * @@ -102,6 +103,8 @@ namespace Oyster */ virtual void NetworkCallback(Oyster::Network::CustomNetProtocol& p); + virtual std::string GetIpAddress(); + private: NetworkClient(const NetworkClient& obj); NetworkClient& operator =(const NetworkClient& obj); diff --git a/Code/Network/NetworkAPI/NetworkServer.cpp b/Code/Network/NetworkAPI/NetworkServer.cpp index 9d6db207..9a4195a2 100644 --- a/Code/Network/NetworkAPI/NetworkServer.cpp +++ b/Code/Network/NetworkAPI/NetworkServer.cpp @@ -77,7 +77,7 @@ public: public: Listener* listener; - PostBox postBox; //Postbox for new clients + PostBox postBox; //Postbox for new clients OysterThread thread; //Server thread NetworkSession *mainSession; Utility::Container::ThreadSafeQueue> clientQueue; @@ -95,9 +95,9 @@ bool NetworkServer::PrivateData::DoWork() /** Check for new clients **/ if(postBox.IsFull()) { - int clientSocketNum = postBox.FetchMessage(); - - if(clientSocketNum == -1) + ConnectionInfo clientSocketNum = postBox.FetchMessage(); + + if(clientSocketNum.socket == -1) { //Something went wrong somewhere... do we care? } diff --git a/Code/Network/NetworkDependencies/Connection.cpp b/Code/Network/NetworkDependencies/Connection.cpp index 27aa296a..99cb8a71 100644 --- a/Code/Network/NetworkDependencies/Connection.cpp +++ b/Code/Network/NetworkDependencies/Connection.cpp @@ -1,6 +1,7 @@ #include "Connection.h" #include +#include #include #include #include @@ -40,11 +41,13 @@ Connection::~Connection() CloseSocket( this->socket ); } -int Connection::Connect(int socket, bool blocking) +int Connection::Connect(ConnectionInfo info, bool blocking) { - this->socket = socket; + this->addr = info.addr; + this->socket = info.socket; this->stillSending = true; this->closed = false; + SetBlockingMode(blocking); //connection succesfull! @@ -167,17 +170,24 @@ int Connection::Recieve(OysterByte &bytes) } //Listen will only return the correct socket or -1 for failure. -int Connection::Listen() +ConnectionInfo Connection::Listen() { - if(this->closed) return -1; + ConnectionInfo val = { 0 }; + if(this->closed) return val; - int clientSocket; - if((clientSocket = (int)accept(this->socket, NULL, NULL)) == INVALID_SOCKET) + SOCKADDR_IN client_info = { 0 }; + int addrsize = sizeof(client_info); + + if((val.socket = (int)accept(this->socket, (struct sockaddr*)&client_info, &addrsize)) == INVALID_SOCKET) { - return (int)INVALID_SOCKET;//WSAGetLastError(); + val.socket = WSAGetLastError(); + } + else + { + val.addr = inet_ntoa(client_info.sin_addr); } - return clientSocket; + return val; } bool Connection::IsSending() @@ -213,6 +223,12 @@ int Connection::SetBlockingMode(bool blocking) return 0; } + +std::string Connection::GetIpAddress() +{ + return this->addr; +} + /////////////////////////////////////// //Private functions /////////////////////////////////////// diff --git a/Code/Network/NetworkDependencies/Connection.h b/Code/Network/NetworkDependencies/Connection.h index d2e815d3..0f46a599 100644 --- a/Code/Network/NetworkDependencies/Connection.h +++ b/Code/Network/NetworkDependencies/Connection.h @@ -14,7 +14,6 @@ namespace Oyster { class Connection : public IConnection { - public: Connection(); Connection( int socket ); @@ -27,10 +26,10 @@ namespace Oyster virtual int Recieve( OysterByte &bytes ); virtual int Disconnect(); - virtual int Connect(int socket, bool blocking = false); + virtual int Connect(ConnectionInfo info, bool blocking = false); virtual int Connect(unsigned short port , const char serverName[], bool blocking = false); - virtual int Listen(); + virtual ConnectionInfo Listen(); bool IsSending(); bool IsConnected(); @@ -38,6 +37,8 @@ namespace Oyster //Setting the socket to blocking/non-blocking mode. int SetBlockingMode( bool blocking ); + std::string GetIpAddress(); + private: int InitiateSocket(); @@ -45,7 +46,7 @@ namespace Oyster bool stillSending; bool closed; - + std::string addr; }; } } diff --git a/Code/Network/NetworkDependencies/IConnection.h b/Code/Network/NetworkDependencies/IConnection.h index 76736071..bd59de65 100644 --- a/Code/Network/NetworkDependencies/IConnection.h +++ b/Code/Network/NetworkDependencies/IConnection.h @@ -11,6 +11,12 @@ namespace Oyster { namespace Network { + struct ConnectionInfo + { + int socket; + std::string addr; + }; + class OysterByte; class IConnection { @@ -27,7 +33,7 @@ namespace Oyster virtual int InitiateClient() { return false; }; //Listen function to let client connect, only used by the server - virtual int Listen() { return -1; }; + virtual ConnectionInfo Listen() { return ConnectionInfo(); }; //enables the client to connect with a server with use of name and port //(servers uses Listen instead of connect) diff --git a/Code/Network/NetworkDependencies/Listener.cpp b/Code/Network/NetworkDependencies/Listener.cpp index 106ac3b9..d4ee396a 100644 --- a/Code/Network/NetworkDependencies/Listener.cpp +++ b/Code/Network/NetworkDependencies/Listener.cpp @@ -11,7 +11,7 @@ Listener::Listener() connection = NULL; } -Listener::Listener(Oyster::Network::IPostBox* postBox) +Listener::Listener(Oyster::Network::IPostBox* postBox) { this->isListening = false; connection = NULL; @@ -88,7 +88,7 @@ void Listener::Shutdown() StopListen(); } -void Listener::SetPostBox(Oyster::Network::IPostBox* postBox) +void Listener::SetPostBox(Oyster::Network::IPostBox* postBox) { stdMutex.lock(); this->postBox = postBox; @@ -97,21 +97,21 @@ void Listener::SetPostBox(Oyster::Network::IPostBox* postBox) int Listener::Accept() { - int clientSocket = -1; + ConnectionInfo clientSocket = {0}; clientSocket = connection->Listen(); if(!this->isListening.load()) { return -1; } - if(clientSocket != -1) + if(clientSocket.socket != -1) { stdMutex.lock(); postBox->PostMessage(clientSocket); stdMutex.unlock(); } - return clientSocket; + return clientSocket.socket; } void Listener::StopListen() { diff --git a/Code/Network/NetworkDependencies/Listener.h b/Code/Network/NetworkDependencies/Listener.h index 0c55f86e..49449990 100644 --- a/Code/Network/NetworkDependencies/Listener.h +++ b/Code/Network/NetworkDependencies/Listener.h @@ -20,7 +20,7 @@ namespace Oyster { public: Listener(); - Listener(Oyster::Network::IPostBox* postBox); + Listener(Oyster::Network::IPostBox* postBox); ~Listener(); bool Init(unsigned int port); @@ -29,7 +29,7 @@ namespace Oyster void Stop(); void Shutdown(); - void SetPostBox(IPostBox* postBox); + void SetPostBox(IPostBox* postBox); private: //Thread functions @@ -48,7 +48,7 @@ namespace Oyster OysterMutex mutex; std::mutex stdMutex; - IPostBox* postBox; + IPostBox* postBox; std::atomic isListening; int port; }; From 44ba45bcc2d3f27168e685f1ced3e80fbe12bd8a Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Wed, 29 Jan 2014 16:01:56 +0100 Subject: [PATCH 68/76] GL - added setCollisionfunc to object --- .../Game/DanBiasGame/GameClientRecieverFunc.h | 62 +++++++++---------- Code/Game/GameLogic/Game.cpp | 34 ++++++++++ Code/Game/GameLogic/Object.cpp | 10 +++ Code/Game/GameLogic/Object.h | 4 ++ 4 files changed, 76 insertions(+), 34 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientRecieverFunc.h b/Code/Game/DanBiasGame/GameClientRecieverFunc.h index 76c4926c..6d0f9501 100644 --- a/Code/Game/DanBiasGame/GameClientRecieverFunc.h +++ b/Code/Game/DanBiasGame/GameClientRecieverFunc.h @@ -14,29 +14,11 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject // receiver function for server messages // parsing protocols and sending it to the gameState - void NetworkCallback(Oyster::Network::CustomNetProtocol& p) override + void ParseGamePlayEvent(Oyster::Network::CustomNetProtocol& p) { - - //if( IsGameplayProtocol(p[protocol_INDEX_ID].value.netShort) ) - //ParseGameplayEvent(e.protocol, e.gameClient); - - //if( IsGeneralProtocol(p[protocol_INDEX_ID].value.netShort) ) - //ParseGeneralEvent(e.protocol, e.gameClient); - int pType = p[0].value.netInt; switch (pType) { - case protocol_General_Status: - { - GameLogic::Protocol_General_Status::States state; - state = (GameLogic::Protocol_General_Status::States)p[1].value.netShort; - if( state == GameLogic::Protocol_General_Status::States_disconected) - { - // server disconnected - DanBiasGame::Release(); - } - } - break; case protocol_Gameplay_PlayerMovement: { Client::GameClientState::KeyInput* protocolData = new Client::GameClientState::KeyInput; @@ -51,19 +33,6 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject protocolData = NULL; } break; - //case protocol_Gameplay_PlayerPosition: - // { - // Client::GameClientState::PlayerPos* protocolData = new Client::GameClientState::PlayerPos; - // for(int i = 0; i< 3; i++) - // { - // protocolData->playerPos[i] = p[i].value.netFloat; - // } - // if(dynamic_cast(gameClientState)) - // ((Client::GameState*)gameClientState)->Protocol(protocolData); - // delete protocolData; - // protocolData = NULL; - // } - // break; case protocol_Gameplay_ObjectCreate: { @@ -115,10 +84,35 @@ struct GameRecieverObject :public Oyster::Network::ProtocolRecieverObject default: break; - } - + } + } + void ParseGeneralEvent(Oyster::Network::CustomNetProtocol& p) + { + int pType = p[0].value.netInt; + switch (pType) + { + + case protocol_General_Status: + { + GameLogic::Protocol_General_Status::States state; + state = (GameLogic::Protocol_General_Status::States)p[1].value.netShort; + if( state == GameLogic::Protocol_General_Status::States_disconected) + { + // server disconnected + DanBiasGame::Release(); + } + } + break; + } + } + void NetworkCallback(Oyster::Network::CustomNetProtocol& p) override + { + if( IsGameplayProtocol(p[protocol_INDEX_ID].value.netShort) ) + ParseGamePlayEvent(p); + if( IsGeneralProtocol(p[protocol_INDEX_ID].value.netShort) ) + ParseGeneralEvent(p); } }; } diff --git a/Code/Game/GameLogic/Game.cpp b/Code/Game/GameLogic/Game.cpp index ca666c2f..6bb4cdc9 100644 --- a/Code/Game/GameLogic/Game.cpp +++ b/Code/Game/GameLogic/Game.cpp @@ -104,6 +104,40 @@ bool Game::NewFrame() { if(this->players[i]->player) this->players[i]->player->EndFrame(); } + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->BeginFrame(); + } + + API::Instance().Update(); + + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->EndFrame(); + } + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->BeginFrame(); + } + + API::Instance().Update(); + + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->EndFrame(); + } + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->BeginFrame(); + } + + API::Instance().Update(); + + for (unsigned int i = 0; i < this->players.Size(); i++) + { + if(this->players[i]->player) this->players[i]->player->EndFrame(); + } + //gameInstance.onMoveFnc(this->level); return true; } diff --git a/Code/Game/GameLogic/Object.cpp b/Code/Game/GameLogic/Object.cpp index 9ec51f1e..36f5890b 100644 --- a/Code/Game/GameLogic/Object.cpp +++ b/Code/Game/GameLogic/Object.cpp @@ -133,6 +133,16 @@ void Object::EndFrame() this->getState = this->rigidBody->GetState(); this->setState = this->getState; } + +void Object::setBeforeCollisonFunc(Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter)) +{ + this->rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_BeforeCollisionResponse)(collisionFuncBefore)); +} +void Object::setAfterCollisonFunc(Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss)) +{ + this->rigidBody->SetSubscription((Oyster::Physics::ICustomBody::EventAction_AfterCollisionResponse)(collisionFuncAfter)); +} + Oyster::Math::Float3 Object::GetPosition() { Oyster::Physics::ICustomBody::State state; diff --git a/Code/Game/GameLogic/Object.h b/Code/Game/GameLogic/Object.h index 24149116..67cf2086 100644 --- a/Code/Game/GameLogic/Object.h +++ b/Code/Game/GameLogic/Object.h @@ -37,6 +37,10 @@ namespace GameLogic void BeginFrame(); void EndFrame(); + + void setBeforeCollisonFunc(Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncBefore)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter)); + void setAfterCollisonFunc(Oyster::Physics::ICustomBody::SubscriptMessage (*collisionFuncAfter)(Oyster::Physics::ICustomBody *proto,Oyster::Physics::ICustomBody *deuter,Oyster::Math::Float kineticEnergyLoss)); + static Oyster::Physics::ICustomBody::SubscriptMessage DefaultCollisionBefore(Oyster::Physics::ICustomBody *rigidBodyLevel, Oyster::Physics::ICustomBody *obj); private: OBJECT_TYPE type; From 7d9d8e46154eddb2d3d5954eb941d452635f8dea Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Thu, 30 Jan 2014 09:07:56 +0100 Subject: [PATCH 69/76] GL - added jump --- .../DanBiasGame/GameClientState/GameState.cpp | 16 ++++++++++++ .../DanBiasGame/GameClientState/GameState.h | 1 + .../GameSession/GameSession_Events.cpp | 8 +++++- Code/Game/GameProtocols/PlayerProtocols.h | 25 +++++++++++++++++++ .../GameProtocols/ProtocolIdentificationID.h | 13 +++++----- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index bda3ad13..1f971c7c 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -282,6 +282,8 @@ void GameState::readKeyInput(InputClass* KeyInput) playerLookDir.lookDirZ = look.z; privData->nwClient->Send(playerLookDir); } + + // shoot if(KeyInput->IsKeyPressed(DIK_Z)) { if(!key_Shoot) @@ -295,6 +297,20 @@ void GameState::readKeyInput(InputClass* KeyInput) else key_Shoot = false; + // jump + if(KeyInput->IsKeyPressed(DIK_X)) + { + if(!key_Jump) + { + GameLogic::Protocol_PlayerJump playerJump; + playerJump.hasJumped = true; + privData->nwClient->Send(playerJump); + key_Jump = true; + } + } + else + key_Jump = false; + // send event data // if(KeyInput->IsKeyPressed(DIK_L)) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.h b/Code/Game/DanBiasGame/GameClientState/GameState.h index 30884043..f8f1b67b 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.h +++ b/Code/Game/DanBiasGame/GameClientState/GameState.h @@ -23,6 +23,7 @@ private: bool key_strafeRight; bool key_strafeLeft; bool key_Shoot; + bool key_Jump; Camera* camera; struct myData; diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index 1ffd5e98..b9ad4d35 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -67,7 +67,13 @@ namespace DanBias break; case protocol_Gameplay_PlayerShot: - c->GetPlayer()->UseWeapon(GameLogic::WEAPON_USE_PRIMARY_PRESS); + if (p[1].value.netBool) + c->GetPlayer()->UseWeapon(GameLogic::WEAPON_USE_PRIMARY_PRESS); + break; + case protocol_Gameplay_PlayerJump: + if (p[1].value.netBool) + c->GetPlayer()->Move(GameLogic::PLAYER_MOVEMENT_JUMP); + break; case protocol_Gameplay_ObjectDamage: break; diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index ffb009cf..412ff08c 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -148,6 +148,31 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; + struct Protocol_PlayerJump :public Oyster::Network::CustomProtocolObject + { + bool hasJumped; + + Protocol_PlayerJump() + { + this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerJump; + this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; + } + const Protocol_PlayerJump& operator=(Oyster::Network::CustomNetProtocol& val) + { + hasJumped = val[1].value.netBool; + return *this; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = hasJumped; + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; } #endif // !GAMELOGIC_PLAYER_PROTOCOLS_H diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index bb0414f8..180f6541 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -50,12 +50,13 @@ #define protocol_Gameplay_PlayerLookDir 301 #define protocol_Gameplay_PlayerChangeWeapon 302 #define protocol_Gameplay_PlayerShot 303 -#define protocol_Gameplay_ObjectPickup 304 -#define protocol_Gameplay_ObjectDamage 305 -#define protocol_Gameplay_ObjectPosition 306 -#define protocol_Gameplay_ObjectEnabled 307 -#define protocol_Gameplay_ObjectDisabled 308 -#define protocol_Gameplay_ObjectCreate 309 +#define protocol_Gameplay_PlayerJump 304 +#define protocol_Gameplay_ObjectPickup 305 +#define protocol_Gameplay_ObjectDamage 306 +#define protocol_Gameplay_ObjectPosition 307 +#define protocol_Gameplay_ObjectEnabled 308 +#define protocol_Gameplay_ObjectDisabled 309 +#define protocol_Gameplay_ObjectCreate 310 #define protocol_GameplayMAX 399 From 05740665dac9cc7a7630d57a04ef8446f843017f Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Thu, 30 Jan 2014 11:11:04 +0100 Subject: [PATCH 70/76] GameServer - Fixed a small error regarding not reaching some data --- Code/Game/DanBiasGame/DanBiasGame.vcxproj | 11 +- Code/Game/DanBiasGame/DanBiasMaincpp.cpp | 2 +- .../Game/DanBiasGame/GameClientRecieverFunc.h | 7 -- .../DanBiasGame/GameClientState/GameState.cpp | 4 +- .../DanBiasServerLauncher/ServerLauncher.cpp | 4 +- Code/Game/GameProtocols/LobbyProtocols.h | 106 +++++++++--------- Code/Game/GameServer/GameLobby.h | 20 +++- Code/Game/GameServer/GameSession.h | 2 - .../GameServer/Implementation/GameLobby.cpp | 32 +++++- .../GameLobby_ProtocolParser.cpp | 42 +++---- .../GameServer/Implementation/GameServer.cpp | 21 ++-- Code/Network/NetworkAPI/NetworkSession.cpp | 53 +++++---- Code/Network/NetworkAPI/NetworkSession.h | 11 +- 13 files changed, 179 insertions(+), 136 deletions(-) diff --git a/Code/Game/DanBiasGame/DanBiasGame.vcxproj b/Code/Game/DanBiasGame/DanBiasGame.vcxproj index 25696efd..955107ef 100644 --- a/Code/Game/DanBiasGame/DanBiasGame.vcxproj +++ b/Code/Game/DanBiasGame/DanBiasGame.vcxproj @@ -72,7 +72,7 @@ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D $(SolutionDir)..\External\Lib\;$(SolutionDir)..\Bin\DLL;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)Game\GameServer\ true @@ -80,7 +80,7 @@ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName)D $(SolutionDir)..\External\Lib\;$(SolutionDir)..\Bin\DLL;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)Game\GameServer\ false @@ -88,7 +88,7 @@ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) $(SolutionDir)..\External\Lib\;$(SolutionDir)..\Bin\DLL;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)Game\GameServer\ false @@ -96,7 +96,7 @@ $(SolutionDir)..\Obj\$(ProjectName)\$(PlatformShortName)\$(Configuration)\ $(ProjectName)_$(PlatformShortName) $(SolutionDir)..\External\Lib\;$(SolutionDir)..\Bin\DLL;C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath) - $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include + $(SolutionDir)..\External\Include\;$(SolutionDir)Game\GameProtocols\;$(SolutionDir)Network/NetworkAPI;$(IncludePath);C:\Program Files %28x86%29\Visual Leak Detector\include;$(SolutionDir)Game\GameServer\ @@ -190,6 +190,9 @@ {f10cbc03-9809-4cba-95d8-327c287b18ee} + + {143bd516-20a1-4890-a3e4-f8bfd02220e7} + diff --git a/Code/Game/DanBiasGame/DanBiasMaincpp.cpp b/Code/Game/DanBiasGame/DanBiasMaincpp.cpp index 0b7ea666..b62dbe11 100644 --- a/Code/Game/DanBiasGame/DanBiasMaincpp.cpp +++ b/Code/Game/DanBiasGame/DanBiasMaincpp.cpp @@ -7,7 +7,7 @@ //-------------------------------------------------------------------------------------- #define NOMINMAX #include - +#include < #include "DllInterfaces/GFXAPI.h" //#include "IGame.h" diff --git a/Code/Game/DanBiasGame/GameClientRecieverFunc.h b/Code/Game/DanBiasGame/GameClientRecieverFunc.h index f7214b32..e7710b67 100644 --- a/Code/Game/DanBiasGame/GameClientRecieverFunc.h +++ b/Code/Game/DanBiasGame/GameClientRecieverFunc.h @@ -13,13 +13,6 @@ struct GameRecieverObject :public Oyster::Network::NetworkClient // parsing protocols and sending it to the gameState void NetworkCallback(Oyster::Network::CustomNetProtocol& p) override { - - //if( IsGameplayProtocol(p[protocol_INDEX_ID].value.netShort) ) - //ParseGameplayEvent(e.protocol, e.gameClient); - - //if( IsGeneralProtocol(p[protocol_INDEX_ID].value.netShort) ) - //ParseGeneralEvent(e.protocol, e.gameClient); - int pType = p[0].value.netInt; switch (pType) { diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index 871ff558..ba3508a7 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -335,8 +335,8 @@ void GameState::Protocol( ObjPos* pos ) //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); if(i == 2) // playerobj { - //camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); - //camera->UpdateViewMatrix(); + camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); + camera->UpdateViewMatrix(); } } } diff --git a/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp b/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp index 62e2b828..a27ab406 100644 --- a/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp +++ b/Code/Game/DanBiasServerLauncher/ServerLauncher.cpp @@ -2,7 +2,7 @@ // Launcher to launch Danbias server // // Created by [Dennis Andersen] [2013] // ////////////////////////////////////////////////// -#define NOMINMAX //Blame it on windows +#define NOMINMAX //Blame it on microsoft #include #include #include @@ -23,7 +23,9 @@ int WINAPI WinMain( HINSTANCE hinst, HINSTANCE prevInst, PSTR cmdLine, int cmdSh desc.listenPort = 15151; if(DanBias::GameServerAPI::ServerInitiate(desc) == DanBias::DanBiasServerReturn_Sucess) { + DanBias::GameServerAPI::ServerStart(); + //DanBias::GameServerAPI::GameStart(); while (!(GetAsyncKeyState(0x51))) //Q for exit { DanBias::GameServerAPI::ServerUpdate(); diff --git a/Code/Game/GameProtocols/LobbyProtocols.h b/Code/Game/GameProtocols/LobbyProtocols.h index 87aad62e..8b333a54 100644 --- a/Code/Game/GameProtocols/LobbyProtocols.h +++ b/Code/Game/GameProtocols/LobbyProtocols.h @@ -92,32 +92,32 @@ namespace GameLogic Oyster::Network::CustomNetProtocol protocol; }; - struct Protocol_LobbyJoin :public Oyster::Network::CustomProtocolObject - { - short value; - - Protocol_LobbyJoin() - { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_Short; - } - Protocol_LobbyJoin(Oyster::Network::CustomNetProtocol& p) - { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_Short; - value = p[1].value.netShort; - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - protocol[1].value = value; - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; + //struct Protocol_LobbyJoin :public Oyster::Network::CustomProtocolObject + //{ + // short value; + // + // Protocol_LobbyJoin() + // { + // this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; + // this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + // this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + // } + // Protocol_LobbyJoin(Oyster::Network::CustomNetProtocol& p) + // { + // this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; + // this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + // this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + // value = p[1].value.netShort; + // } + // Oyster::Network::CustomNetProtocol* GetProtocol() override + // { + // protocol[1].value = value; + // return &protocol; + // } + // + // private: + // Oyster::Network::CustomNetProtocol protocol; + //}; struct Protocol_LobbyRefresh :public Oyster::Network::CustomProtocolObject { @@ -140,7 +140,8 @@ namespace GameLogic /** * A protocol that contains all data to send to client when update game lobby */ - struct Protocol_LobbyGameData :public Oyster::Network::CustomProtocolObject + + struct Protocol_LobbyClientData :public Oyster::Network::CustomProtocolObject { // Player list struct PlayerData @@ -150,14 +151,14 @@ namespace GameLogic }; Utility::DynamicMemory::DynamicArray list; - Protocol_LobbyGameData() + Protocol_LobbyClientData() { this->protocol[protocol_INDEX_ID].value = protocol_Lobby_GameData; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; list.Reserve(10); } - Protocol_LobbyGameData(Oyster::Network::CustomNetProtocol& p) + Protocol_LobbyClientData(Oyster::Network::CustomNetProtocol& p) { this->protocol[protocol_INDEX_ID].value = protocol_Lobby_GameData; this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; @@ -181,32 +182,33 @@ namespace GameLogic private: Oyster::Network::CustomNetProtocol protocol; }; + /** * A protocol that contains all data to send to client when update main lobby */ - struct Protocol_LobbyMainData :public Oyster::Network::CustomProtocolObject - { - // Game instance list - - Protocol_LobbyMainData() - { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_MainData; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - - this->protocol[1].type = Oyster::Network::NetAttributeType_Short; - } - Protocol_LobbyMainData(Oyster::Network::CustomNetProtocol& p) - { - - } - Oyster::Network::CustomNetProtocol* GetProtocol() override - { - return &protocol; - } - - private: - Oyster::Network::CustomNetProtocol protocol; - }; + //struct Protocol_LobbyMainData :public Oyster::Network::CustomProtocolObject + //{ + // // Game instance list + // + // Protocol_LobbyMainData() + // { + // this->protocol[protocol_INDEX_ID].value = protocol_Lobby_MainData; + // this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + // + // this->protocol[1].type = Oyster::Network::NetAttributeType_Short; + // } + // Protocol_LobbyMainData(Oyster::Network::CustomNetProtocol& p) + // { + // + // } + // Oyster::Network::CustomNetProtocol* GetProtocol() override + // { + // return &protocol; + // } + // + // private: + // Oyster::Network::CustomNetProtocol protocol; + //}; } #endif // !GAMELOGIC_PLAYER_PROTOCOLS_H diff --git a/Code/Game/GameServer/GameLobby.h b/Code/Game/GameServer/GameLobby.h index 2b9f4bc4..8463b9d9 100644 --- a/Code/Game/GameServer/GameLobby.h +++ b/Code/Game/GameServer/GameLobby.h @@ -12,6 +12,14 @@ namespace DanBias { + struct LobbyLevelData + { + int mapNumber; + int maxClients; + int gameMode; + int gameTime; + std::string gameName; + }; class GameLobby :public Oyster::Network::NetworkSession { public: @@ -20,8 +28,8 @@ namespace DanBias void Release(); void Update(); - void SetGameDesc(const GameSession::GameDescription& desc); - void GetGameDesc(GameSession::GameDescription& desc); + void SetGameDesc(const LobbyLevelData& desc); + void GetGameDesc(LobbyLevelData& desc); bool StartGameSession(); private: @@ -31,11 +39,11 @@ namespace DanBias void GeneralText(GameLogic::Protocol_General_Text& p, Oyster::Network::NetworkClient* c); //id = protocol_General_Text: //void LobbyCreateGame(GameLogic::Protocol_LobbyCreateGame& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Create: void LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Start: - void LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Join: + //void LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Join: void LobbyLogin(GameLogic::Protocol_LobbyLogin& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Login: void LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Refresh: - void LobbyMainData(GameLogic::Protocol_LobbyMainData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_MainData: - void LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_GameData: + void LobbyMainData(GameLogic::Protocol_LobbyClientData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_MainData: + //void LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_GameData: private: void ClientEventCallback(Oyster::Network::NetEvent e) override; @@ -45,7 +53,7 @@ namespace DanBias Utility::WinTimer timer; float refreshFrequency; GameSession gameSession; - GameSession::GameDescription description; + LobbyLevelData description; }; }//End namespace DanBias #endif // !DANBIASGAME_GAMELOBBY_H diff --git a/Code/Game/GameServer/GameSession.h b/Code/Game/GameServer/GameSession.h index 4685440a..cdae58e5 100644 --- a/Code/Game/GameServer/GameSession.h +++ b/Code/Game/GameServer/GameSession.h @@ -32,10 +32,8 @@ namespace DanBias struct GameDescription { int mapNumber; - int maxClients; int gameMode; int gameTime; - std::string gameName; Oyster::Network::NetworkSession* owner; Utility::DynamicMemory::DynamicArray clients; }; diff --git a/Code/Game/GameServer/Implementation/GameLobby.cpp b/Code/Game/GameServer/Implementation/GameLobby.cpp index 24ca7887..308e4ae6 100644 --- a/Code/Game/GameServer/Implementation/GameLobby.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby.cpp @@ -9,6 +9,7 @@ using namespace Utility::DynamicMemory; using namespace Oyster::Network; using namespace Oyster; +using namespace GameLogic; namespace DanBias { @@ -30,17 +31,34 @@ namespace DanBias this->ProcessClients(); } - void GameLobby::SetGameDesc(const GameSession::GameDescription& desc) + void GameLobby::SetGameDesc(const LobbyLevelData& desc) { - this->description = desc; + this->description.gameMode = desc.gameMode; + this->description.gameTime = desc.gameTime; + this->description.mapNumber = desc.mapNumber; + this->description.maxClients = desc.maxClients; } - void GameLobby::GetGameDesc(GameSession::GameDescription& desc) + void GameLobby::GetGameDesc(LobbyLevelData& desc) { - desc = this->description; + desc.gameMode = this->description.gameMode; + desc.gameTime = this->description.gameTime; + desc.mapNumber = this->description.mapNumber; + desc.maxClients = this->description.maxClients; } bool GameLobby::StartGameSession() { - if(this->gameSession.Create(this->description)) + GameSession::GameDescription desc; + desc.gameMode = this->description.gameMode; + desc.gameTime = this->description.gameTime; + desc.mapNumber = this->description.mapNumber; + desc.owner = this; + while (this->GetClientCount()) + { + NetClient c; + if((c = this->Detach())) + desc.clients.Push(c); + } + if(this->gameSession.Create(desc)) { this->gameSession.Run(); return true; @@ -70,6 +88,10 @@ namespace DanBias { printf("New client(%i) connected - %s \n", client->GetID(), client->GetIpAddress().c_str()); Attach(client); + + Protocol_LobbyClientData p; + + client->Send(p.GetProtocol()); } }//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp index 0de7b062..1c841519 100644 --- a/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp @@ -17,16 +17,16 @@ void GameLobby::ParseProtocol(Oyster::Network::CustomNetProtocol& p, NetworkClie //break; case protocol_Lobby_Start: this->LobbyStartGame (Protocol_LobbyStartGame (p), c); break; - case protocol_Lobby_Join: this->LobbyJoin (Protocol_LobbyJoin (p), c); - break; + //case protocol_Lobby_Join: this->LobbyJoin (Protocol_LobbyJoin (p), c); + //break; case protocol_Lobby_Login: this->LobbyLogin (Protocol_LobbyLogin (p), c); break; case protocol_Lobby_Refresh: this->LobbyRefresh (Protocol_LobbyRefresh (p), c); break; - case protocol_Lobby_MainData: this->LobbyMainData (Protocol_LobbyMainData (p), c); - break; - case protocol_Lobby_GameData: this->LobbyGameData (Protocol_LobbyGameData (p), c); + case protocol_Lobby_MainData: this->LobbyMainData (Protocol_LobbyClientData (p), c); break; + //case protocol_Lobby_GameData: this->LobbyGameData (Protocol_LobbyGameData (p), c); + //break; } } @@ -62,17 +62,17 @@ void GameLobby::LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Ne { } -void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c) -{ - //for (unsigned int i = 0; i < this->gameLobby.Size(); i++) - //{ - // if (this->gameLobby[i]->GetID() == p.value) - // { - // this->gameLobby[i]->Attach(Detach(c)); - // return; - // } - //} -} +//void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c) +//{ +// //for (unsigned int i = 0; i < this->gameLobby.Size(); i++) +// //{ +// // if (this->gameLobby[i]->GetID() == p.value) +// // { +// // this->gameLobby[i]->Attach(Detach(c)); +// // return; +// // } +// //} +//} void GameLobby::LobbyLogin(GameLogic::Protocol_LobbyLogin& p, Oyster::Network::NetworkClient* c) { @@ -81,13 +81,13 @@ void GameLobby::LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Networ { //Dont need to handle this on the server... } -void GameLobby::LobbyMainData(GameLogic::Protocol_LobbyMainData& p, Oyster::Network::NetworkClient* c) -{ - -} -void GameLobby::LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c) +void GameLobby::LobbyMainData(GameLogic::Protocol_LobbyClientData& p, Oyster::Network::NetworkClient* c) { } +//void GameLobby::LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c) +//{ +// +//} diff --git a/Code/Game/GameServer/Implementation/GameServer.cpp b/Code/Game/GameServer/Implementation/GameServer.cpp index 0489a12d..416503e5 100644 --- a/Code/Game/GameServer/Implementation/GameServer.cpp +++ b/Code/Game/GameServer/Implementation/GameServer.cpp @@ -39,9 +39,6 @@ DanBiasServerReturn GameServerAPI::ServerInitiate(const ServerInitDesc& desc) return DanBiasServerReturn_Error; } GameSession::GameDescription d; - lobby.GetGameDesc(d); - d.gameName.assign(desc.serverName); - lobby.SetGameDesc(d); std::printf("Server created!\t-\t%s: [%i]\n\n", server.GetLanAddress().c_str(), desc.listenPort); @@ -95,59 +92,59 @@ GameServerAPI::GameServerInfo GameServerAPI::ServerGetInfo() } void GameServerAPI::GameSetMapId(const int& val) { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); d.mapNumber = val; lobby.SetGameDesc(d); } void GameServerAPI::GameSetMaxClients(const int& val) { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); d.maxClients = val; lobby.SetGameDesc(d); } void GameServerAPI::GameSetGameMode(const int& val) { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); d.gameMode = val; lobby.SetGameDesc(d); } void GameServerAPI::GameSetGameTime(const int& val) { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); d.gameTime = val; lobby.SetGameDesc(d); } int GameServerAPI::GameGetMapId() { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); return d.mapNumber; } int GameServerAPI::GameGetMaxClients() { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); return d.maxClients; } int GameServerAPI::GameGetGameMode() { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); return d.gameMode; } int GameServerAPI::GameGetGameTime() { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); return d.gameTime; } const char* GameServerAPI::GameGetGameName() { - GameSession::GameDescription d; + LobbyLevelData d; lobby.GetGameDesc(d); return d.gameName.c_str(); } diff --git a/Code/Network/NetworkAPI/NetworkSession.cpp b/Code/Network/NetworkAPI/NetworkSession.cpp index d1fc7fec..35352243 100644 --- a/Code/Network/NetworkAPI/NetworkSession.cpp +++ b/Code/Network/NetworkAPI/NetworkSession.cpp @@ -20,11 +20,9 @@ struct NetworkSession::PrivateSessionData NetworkClient::ClientEventFunction messageCallback; std::mutex clientListLock; NetworkSession* owner; //Where clients end up when session is closed. - int clientCount; int id; NetworkSession::PrivateSessionData() - : clientCount(0) - , owner(0) + : owner(0) , id(GID()) {} }; @@ -38,7 +36,7 @@ NetworkSession::NetworkSession(const NetworkSession& orig) { this->data->clients = orig.data->clients; this->data->owner = orig.data->owner; - this->data->clientCount = orig.data->clientCount; + this->clientCount = orig.clientCount; this->data->id = orig.data->id; this->data->messageCallback = orig.data->messageCallback; } @@ -47,7 +45,7 @@ const NetworkSession& NetworkSession::operator=(const NetworkSession& orig) { this->data->clients = orig.data->clients; this->data->owner = orig.data->owner; - this->data->clientCount = orig.data->clientCount; + this->clientCount = orig.clientCount; this->data->id = orig.data->id; this->data->messageCallback = orig.data->messageCallback; @@ -57,7 +55,7 @@ const NetworkSession& NetworkSession::operator=(const NetworkSession& orig) NetworkSession::~NetworkSession() { this->data->clients.Clear(); - this->data->clientCount = 0; + this->clientCount = 0; this->data->messageCallback = 0; delete this->data; this->data = 0; @@ -91,7 +89,7 @@ bool NetworkSession::Attach(NetClient client) this->data->clients[k] = client; } - this->data->clientCount++; + this->clientCount++; client->SetOwner(this); this->data->clientListLock.unlock(); @@ -99,7 +97,7 @@ bool NetworkSession::Attach(NetClient client) return true; } -void NetworkSession::Detach() +void NetworkSession::DetachAll() { if(this->data->owner) { @@ -117,6 +115,7 @@ void NetworkSession::Detach() this->data->clients[i] = 0; } } + this->clientCount = 0; } NetClient NetworkSession::Detach(const NetworkClient* client) @@ -131,7 +130,7 @@ NetClient NetworkSession::Detach(const NetworkClient* client) { val = this->data->clients[i]; this->data->clients[i] = 0; - this->data->clientCount--; + this->clientCount--; } } @@ -152,7 +151,29 @@ NetClient NetworkSession::Detach(short ID) { val = this->data->clients[i]; this->data->clients[i] = 0; - this->data->clientCount--; + this->clientCount--; + } + } + + this->data->clientListLock.unlock(); + + return val; +} + +NetClient NetworkSession::Detach() +{ + NetClient val; + + this->data->clientListLock.lock(); + + for (unsigned int i = 0; i < this->data->clients.Size(); i++) + { + if(this->data->clients[i]) + { + val = this->data->clients[i]; + this->data->clients[i] = 0; + this->clientCount--; + break; } } @@ -204,7 +225,7 @@ void NetworkSession::CloseSession(bool dissconnectClients) } this->data->clients.Clear(); - this->data->clientCount = 0; + this->clientCount = 0; this->data->clientListLock.unlock(); } @@ -214,16 +235,6 @@ void NetworkSession::SetOwner(NetworkSession* owner) this->data->owner = owner; } -int NetworkSession::GetClientCount() const -{ - int c = 0; - for (unsigned int i = 0; i < this->data->clients.Size(); i++) - { - if(this->data->clients[i]) c++; - } - return c; -} - void NetworkSession::ClientConnectedEvent(NetClient client) { this->Attach(client); diff --git a/Code/Network/NetworkAPI/NetworkSession.h b/Code/Network/NetworkAPI/NetworkSession.h index b8287b36..ffe047a4 100644 --- a/Code/Network/NetworkAPI/NetworkSession.h +++ b/Code/Network/NetworkAPI/NetworkSession.h @@ -11,6 +11,7 @@ #include "NetworkServerEventStruct.h" #include "NetworkClient.h" #include "Utilities.h" +#include "DynamicArray.h" namespace Oyster { @@ -38,7 +39,7 @@ namespace Oyster * Detaches all clients and sends them to owner. * If no owner is set the clients is disconnected. */ - virtual void Detach(); + virtual void DetachAll(); /** * @@ -50,6 +51,11 @@ namespace Oyster */ virtual NetClient Detach(short ID); + /** + * + */ + virtual NetClient Detach(); + /** Send a message to all clients in this session * @param message The message */ @@ -74,7 +80,7 @@ namespace Oyster /** Get the number of clients active in this session * @return The client count */ - int GetClientCount() const; + inline int GetClientCount() const { return this->clientCount; } /** * @@ -87,6 +93,7 @@ namespace Oyster virtual void ClientEventCallback(NetEvent e) = 0; private: + int clientCount; struct PrivateSessionData; PrivateSessionData* data; }; From 323b7322fad7f26bc646c34be291b021c0d0c876 Mon Sep 17 00:00:00 2001 From: Erik Persson Date: Thu, 30 Jan 2014 11:11:45 +0100 Subject: [PATCH 71/76] GL - testing --- Code/DanBias.sln | 1 + .../DanBiasGame/GameClientState/GameState.cpp | 16 +++++------ .../GameSession/GameSession_Events.cpp | 2 +- .../GameSession/GameSession_Logic.cpp | 28 ------------------- Code/Game/GameLogic/AttatchmentMassDriver.cpp | 8 ++---- Code/Game/GameLogic/CollisionManager.cpp | 7 +++-- Code/Game/GameLogic/Game_PlayerData.cpp | 2 +- Code/Game/GameLogic/Level.cpp | 4 +-- Code/Game/GameLogic/Player.cpp | 6 ++-- 9 files changed, 22 insertions(+), 52 deletions(-) diff --git a/Code/DanBias.sln b/Code/DanBias.sln index bc91fdcf..7df4835b 100644 --- a/Code/DanBias.sln +++ b/Code/DanBias.sln @@ -313,5 +313,6 @@ Global {8690FDDF-C5B7-4C42-A337-BD5243F29B85} = {20720CA7-795C-45AD-A302-9383A6DD503A} {DA2AA800-ED64-4649-8B3B-E7F1E3968B78} = {20720CA7-795C-45AD-A302-9383A6DD503A} {060B1890-CBF3-4808-BA99-A4776222093B} = {20720CA7-795C-45AD-A302-9383A6DD503A} + {666FEA52-975F-41CD-B224-B19AF3C0ABBA} = {20720CA7-795C-45AD-A302-9383A6DD503A} EndGlobalSection EndGlobal diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index 1f971c7c..00fc2599 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -48,17 +48,17 @@ bool GameState::Init(Oyster::Network::NetworkClient* nwClient) GameState::gameStateState GameState::LoadGame() { Oyster::Graphics::Definitions::Pointlight plight; - plight.Pos = Oyster::Math::Float3(0,15,5); + plight.Pos = Oyster::Math::Float3(0,175,5); plight.Color = Oyster::Math::Float3(0,1,0); plight.Radius = 50; plight.Bright = 2; Oyster::Graphics::API::AddLight(plight); - plight.Pos = Oyster::Math::Float3(10,15,5); + plight.Pos = Oyster::Math::Float3(10,175,5); plight.Color = Oyster::Math::Float3(1,0,0); plight.Radius = 50; plight.Bright = 2; Oyster::Graphics::API::AddLight(plight); - plight.Pos = Oyster::Math::Float3(10,-15,5); + plight.Pos = Oyster::Math::Float3(10,-175,5); plight.Color = Oyster::Math::Float3(0,0,1); plight.Radius = 50; plight.Bright = 2; @@ -84,8 +84,8 @@ bool GameState::LoadModels(std::wstring mapFile) scale.v[0].x = 8; scale.v[1].y = 8; scale.v[2].z = 8; - modelData.world = scale; //modelData.world * translate - modelData.modelPath = L"ball.dan"; + modelData.world = translate; //modelData.world * translate + modelData.modelPath = L"world_earth.dan"; modelData.id = 0; obj = new C_Player(); @@ -94,7 +94,7 @@ bool GameState::LoadModels(std::wstring mapFile) // add box model modelData.world = Oyster::Math3D::Float4x4::identity; - translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-5,15,0)); + translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-5,175,0)); modelData.world = modelData.world * translate; modelData.modelPath = L"box.dan"; modelData.id = 1; @@ -351,8 +351,8 @@ void GameState::Protocol( ObjPos* pos ) //camera->setLook((Oyster::Math::Float3(world[8], world[9], world[10]))); if(i == 2) // playerobj { - //camera->SetPosition(Oyster::Math::Float3(world[12], world[13], world[14])); - //camera->UpdateViewMatrix(); + camera->SetPosition(Oyster::Math::Float3(world[12], world[13] + 2.2f, world[14]-1)); + camera->UpdateViewMatrix(); } } } diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp index b9ad4d35..8306e138 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Events.cpp @@ -132,7 +132,7 @@ namespace DanBias scale.v[0].x = 8; scale.v[1].y = 8; scale.v[2].z = 8; - world = world * scale; + //world = world * scale; Protocol_ObjectPosition p(world, 0); GameSession::gameSession->Send(p.GetProtocol()); } diff --git a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp index fed4202c..6d72ff57 100644 --- a/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp +++ b/Code/Game/DanBiasServer/GameSession/GameSession_Logic.cpp @@ -24,39 +24,11 @@ using namespace GameLogic; namespace DanBias { - //TEST SHIT - GameAPI *game = &GameAPI::Instance(); - DynamicArray players; - - //TEST SHIT bool GameSession::DoWork( ) { if(this->isRunning) { - //TEST SHIT - //player creation and testing - //players.Resize(10); - - //for(int i = 0; i < 10; i++) - //{ - // players[i] = game->CreatePlayer();WWW - // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_BACKWARD); - // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_FORWARD); - // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_JUMP); - // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_LEFT); - // players[i]->Move(GameLogic::PLAYER_MOVEMENT::PLAYER_MOVEMENT_RIGHT); - - // //using weapon testing - // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_PRIMARY_PRESS); - // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_PRIMARY_RELEASE); - // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_SECONDARY_PRESS); - // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_SECONDARY_RELEASE); - // players[i]->UseWeapon(GameLogic::WEAPON_FIRE::WEAPON_USE_UTILLITY_PRESS); - //} - - - //TEST SHIT double dt = this->timer.getElapsedSeconds(); gameInstance.SetFrameTimeLength((float)dt); diff --git a/Code/Game/GameLogic/AttatchmentMassDriver.cpp b/Code/Game/GameLogic/AttatchmentMassDriver.cpp index 9343ba5b..7554c879 100644 --- a/Code/Game/GameLogic/AttatchmentMassDriver.cpp +++ b/Code/Game/GameLogic/AttatchmentMassDriver.cpp @@ -46,14 +46,12 @@ void AttatchmentMassDriver::UseAttatchment(const GameLogic::WEAPON_FIRE &usage, void AttatchmentMassDriver::ForcePush(const GameLogic::WEAPON_FIRE &usage, float dt) { //Oyster::Math::Float4 pushForce = Oyster::Math::Float4(this->owner->GetLookDir()) * (500 * dt); - Oyster::Math::Float3 weaponPos; - weaponPos = owner->GetPosition() + 5 * owner->GetLookDir(); - Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), weaponPos); + Oyster::Math::Float4x4 aim = Oyster::Math3D::ViewMatrix_LookAtDirection(owner->GetLookDir(), owner->GetRigidBody()->GetGravityNormal(), owner->GetPosition()); Oyster::Math::Float4x4 hitSpace = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/4,1,1,20); Oyster::Collision3D::Frustrum hitFrustum = Oyster::Collision3D::Frustrum(Oyster::Math3D::ViewProjectionMatrix(aim,hitSpace)); - - Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,NULL, ForcePushAction); + int arg = 0; + Oyster::Physics::API::Instance().ApplyEffect(hitFrustum,&arg,ForcePushAction); } /******************************************************** diff --git a/Code/Game/GameLogic/CollisionManager.cpp b/Code/Game/GameLogic/CollisionManager.cpp index 00830301..19f25dd3 100644 --- a/Code/Game/GameLogic/CollisionManager.cpp +++ b/Code/Game/GameLogic/CollisionManager.cpp @@ -20,6 +20,7 @@ using namespace GameLogic; Player *player = ((Game::PlayerData*)(rigidBodyPlayer->GetCustomTag()))->player; Object *realObj = (Object*)obj->GetCustomTag(); //needs to be changed? + return; switch (realObj->GetType()) { case OBJECT_TYPE::OBJECT_TYPE_GENERIC: @@ -78,12 +79,12 @@ using namespace GameLogic; return Physics::ICustomBody::SubscriptMessage_ignore_collision_response; } - void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj, void* args) + void AttatchmentMassDriver::ForcePushAction(Oyster::Physics::ICustomBody *obj, void *args) { - Oyster::Math::Float3 pushForce = Oyster::Math::Float4(1,0,0) * (20); + Oyster::Math::Float3 pushForce = Oyster::Math::Float4(1,0,0) * (1); Oyster::Physics::ICustomBody::State state; state = obj->GetState(); state.ApplyLinearImpulse(pushForce); obj->SetState(state); //((Object*)obj->GetCustomTag())->ApplyLinearImpulse(pushForce); - } + } \ No newline at end of file diff --git a/Code/Game/GameLogic/Game_PlayerData.cpp b/Code/Game/GameLogic/Game_PlayerData.cpp index a8cd665b..b3c6ef26 100644 --- a/Code/Game/GameLogic/Game_PlayerData.cpp +++ b/Code/Game/GameLogic/Game_PlayerData.cpp @@ -7,7 +7,7 @@ Game::PlayerData::PlayerData() { //set some stats that are appropriate to a player Oyster::Physics::API::SimpleBodyDescription sbDesc; - sbDesc.centerPosition = Oyster::Math::Float3(0,15,0); + sbDesc.centerPosition = Oyster::Math::Float3(0,165,0); sbDesc.size = Oyster::Math::Float3(4,7,4); //create rigid body diff --git a/Code/Game/GameLogic/Level.cpp b/Code/Game/GameLogic/Level.cpp index 02fc8961..f631979d 100644 --- a/Code/Game/GameLogic/Level.cpp +++ b/Code/Game/GameLogic/Level.cpp @@ -25,7 +25,7 @@ void Level::InitiateLevel(float radius) API::SphericalBodyDescription sbDesc; sbDesc.centerPosition = Oyster::Math::Float4(0,0,0,1); sbDesc.ignoreGravity = true; - sbDesc.radius = 8; + sbDesc.radius = 150; sbDesc.mass = 10e12f; ICustomBody* rigidBody = API::Instance().CreateRigidBody(sbDesc).Release(); @@ -59,7 +59,7 @@ void Level::InitiateLevel(float radius) // add gravitation API::Gravity gravityWell; gravityWell.gravityType = API::Gravity::GravityType_Well; - gravityWell.well.mass = 10e12f; + gravityWell.well.mass = 10e16f; gravityWell.well.position = Oyster::Math::Float4(0,0,0,1); API::Instance().AddGravity(gravityWell); } diff --git a/Code/Game/GameLogic/Player.cpp b/Code/Game/GameLogic/Player.cpp index c366fb8f..45ae8f65 100644 --- a/Code/Game/GameLogic/Player.cpp +++ b/Code/Game/GameLogic/Player.cpp @@ -95,16 +95,14 @@ void Player::MoveBackwards() void Player::MoveRight() { //Do cross product with forward vector and negative gravity vector - Oyster::Math::Float3 r = Oyster::Math::Float4(1, 0, 0); - //Oyster::Math::Float4 r = (-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); + Oyster::Math::Float3 r = (-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); setState.ApplyLinearImpulse(r * 20 * this->gameInstance->GetFrameTime()); } void Player::MoveLeft() { //Do cross product with forward vector and negative gravity vector - Oyster::Math::Float3 r = Oyster::Math::Float4(1, 0, 0 ); - //Oyster::Math::Float4 r1 = -(-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); //Still get zero + Oyster::Math::Float3 r = -(-rigidBody->GetGravityNormal()).Cross((Oyster::Math::Float3)this->lookDir); //Still get zero setState.ApplyLinearImpulse(-r * 20 * this->gameInstance->GetFrameTime()); } From 1ccf98e3eb4526f400c3c0b8ae0d0d082499e791 Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Thu, 30 Jan 2014 11:58:44 +0100 Subject: [PATCH 72/76] GL - added loginstate --- Code/Game/DanBiasGame/DanBiasGame.vcxproj | 2 + Code/Game/DanBiasGame/DanBiasGame_Impl.cpp | 8 +- .../GameClientState/GameClientState.h | 1 + .../GameClientState/LobbyState.cpp | 2 +- .../DanBiasGame/GameClientState/LobbyState.h | 2 + .../GameClientState/LoginState.cpp | 166 ++++++++++++++++++ .../DanBiasGame/GameClientState/LoginState.h | 33 ++++ 7 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 Code/Game/DanBiasGame/GameClientState/LoginState.cpp create mode 100644 Code/Game/DanBiasGame/GameClientState/LoginState.h diff --git a/Code/Game/DanBiasGame/DanBiasGame.vcxproj b/Code/Game/DanBiasGame/DanBiasGame.vcxproj index 955107ef..f1cbf970 100644 --- a/Code/Game/DanBiasGame/DanBiasGame.vcxproj +++ b/Code/Game/DanBiasGame/DanBiasGame.vcxproj @@ -206,6 +206,7 @@ + @@ -216,6 +217,7 @@ + diff --git a/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp b/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp index 07d2d0ec..64526c0b 100644 --- a/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp +++ b/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp @@ -5,6 +5,7 @@ #include "GameClientState/GameClientState.h" #include "GameClientState\GameState.h" #include "GameClientState\LobbyState.h" +#include "GameClientState\LoginState.h" #include #include "NetworkClient.h" @@ -61,15 +62,15 @@ namespace DanBias return DanBiasClientReturn_Error; m_data->recieverObj = new GameRecieverObject; - m_data->recieverObj->Connect(desc.port, desc.IP); + /*m_data->recieverObj->Connect(desc.port, desc.IP); if (!m_data->recieverObj->IsConnected()) { // failed to connect return DanBiasClientReturn_Error; - } + }*/ // Start in lobby state - m_data->recieverObj->gameClientState = new Client::LobbyState(); + m_data->recieverObj->gameClientState = new Client::LoginState(); if(!m_data->recieverObj->gameClientState->Init(m_data->recieverObj)) return DanBiasClientReturn_Error; @@ -134,6 +135,7 @@ namespace DanBias m_data->inputObj->Update(); + DanBias::Client::GameClientState::ClientState state = DanBias::Client::GameClientState::ClientState_Same; state = m_data->recieverObj->gameClientState->Update(deltaTime, m_data->inputObj); diff --git a/Code/Game/DanBiasGame/GameClientState/GameClientState.h b/Code/Game/DanBiasGame/GameClientState/GameClientState.h index 2ba930b7..28a671ba 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameClientState.h +++ b/Code/Game/DanBiasGame/GameClientState/GameClientState.h @@ -51,6 +51,7 @@ public: }; enum ClientState { + ClientState_Login, ClientState_Lobby, ClientState_Game, ClientState_Same, diff --git a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp index 89d8b1b7..d30fcd59 100644 --- a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp @@ -31,7 +31,7 @@ LobbyState::~LobbyState(void) bool LobbyState::Init(Oyster::Network::NetworkClient* nwClient) { privData = new myData(); - + this->nwClient = nwClient; // load models LoadModels(L"UImodels.txt"); InitCamera(Oyster::Math::Float3(0,0,5.4f)); diff --git a/Code/Game/DanBiasGame/GameClientState/LobbyState.h b/Code/Game/DanBiasGame/GameClientState/LobbyState.h index e7cb7b8c..057fc850 100644 --- a/Code/Game/DanBiasGame/GameClientState/LobbyState.h +++ b/Code/Game/DanBiasGame/GameClientState/LobbyState.h @@ -3,6 +3,7 @@ #include "GameClientState.h" #include "OysterMath.h" +#include "NetworkClient.h" #include namespace DanBias { @@ -12,6 +13,7 @@ namespace DanBias class LobbyState : public GameClientState { private: + Oyster::Network::NetworkClient* nwClient; struct myData; myData* privData; public: diff --git a/Code/Game/DanBiasGame/GameClientState/LoginState.cpp b/Code/Game/DanBiasGame/GameClientState/LoginState.cpp new file mode 100644 index 00000000..8fa4936d --- /dev/null +++ b/Code/Game/DanBiasGame/GameClientState/LoginState.cpp @@ -0,0 +1,166 @@ +#include "LoginState.h" +#include "DllInterfaces/GFXAPI.h" +#include "OysterMath.h" +#include "C_obj/C_Player.h" +#include "C_obj/C_StaticObj.h" +#include "C_obj/C_DynamicObj.h" +#include + +using namespace DanBias::Client; + +struct LoginState::myData +{ + myData(){} + Oyster::Math3D::Float4x4 view; + Oyster::Math3D::Float4x4 proj; + C_Object* object[2]; + int modelCount; + // UI object + // game client* +}privData; + +LoginState::LoginState(void) +{ + +} + +LoginState::~LoginState(void) +{ + +} + +bool LoginState::Init(Oyster::Network::NetworkClient* nwClient) +{ + privData = new myData(); + this->nwClient = nwClient; + // load models + LoadModels(L"UImodels.txt"); + InitCamera(Oyster::Math::Float3(0,0,5.4f)); + return true; +} +bool LoginState::LoadModels(std::wstring file) +{ + Oyster::Graphics::Definitions::Pointlight plight; + plight.Pos = Oyster::Math::Float3(-2,3,0); + plight.Color = Oyster::Math::Float3(0,1,0); + plight.Radius = 10; + plight.Bright = 1; + Oyster::Graphics::API::AddLight(plight); + // open file + // read file + // init models + privData->modelCount = 2; + + ModelInitData modelData; + + modelData.world = Oyster::Math3D::Float4x4::identity; + modelData.visible = true; + modelData.modelPath = L"..\\Content\\Models\\box_2.dan"; + // load models + privData->object[0] = new C_StaticObj(); + privData->object[0]->Init(modelData); + + Oyster::Math3D::Float4x4 translate = Oyster::Math3D::TranslationMatrix(Oyster::Math::Float3(-2,-2,-2)); + modelData.world = modelData.world * translate; + + privData->object[1] = new C_DynamicObj(); + privData->object[1]->Init(modelData); + return true; +} + +bool LoginState::InitCamera(Oyster::Math::Float3 startPos) +{ + privData->proj = Oyster::Math3D::ProjectionMatrix_Perspective(Oyster::Math::pi/2,1024.0f/768.0f,.1f,1000); + //privData->proj = Oyster::Math3D::ProjectionMatrix_Orthographic(1024, 768, 1, 1000); + Oyster::Graphics::API::SetProjection(privData->proj); + + privData->view = Oyster::Math3D::OrientationMatrix_LookAtDirection(Oyster::Math::Float3(0,0,-1),Oyster::Math::Float3(0,1,0),startPos); + privData->view = Oyster::Math3D::InverseOrientationMatrix(privData->view); + return true; +} +GameClientState::ClientState LoginState::Update(float deltaTime, InputClass* KeyInput) +{ + // picking + // mouse events + // different menus + // play sounds + // update animation + // send data to server + // check data from server + + // create game + if( KeyInput->IsKeyPressed(DIK_C)) + { + DanBias::GameServerAPI::ServerInitDesc desc; + + DanBias::GameServerAPI::ServerInitiate(desc); + DanBias::GameServerAPI::ServerStart(); + // my ip + nwClient->Connect(15151, "127.0.0.1"); + + if (!nwClient->IsConnected()) + { + // failed to connect + return ClientState_Same; + } + return ClientState_Lobby; + } + // join game + if( KeyInput->IsKeyPressed(DIK_J)) + { + // game ip + nwClient->Connect(15151, "194.47.150.56"); + + if (!nwClient->IsConnected()) + { + // failed to connect + return ClientState_Same; + } + return ClientState_Lobby; + } + return ClientState_Same; +} +bool LoginState::Render() +{ + + Oyster::Graphics::API::SetView(privData->view); + Oyster::Graphics::API::SetProjection( privData->proj); + + + Oyster::Graphics::API::NewFrame(); + // render objects + for (int i = 0; i < privData->modelCount; i++) + { + privData->object[i]->Render(); + } + + // render effects + + // render lights + + Oyster::Graphics::API::EndFrame(); + return true; +} +bool LoginState::Release() +{ + for (int i = 0; i < privData->modelCount; i++) + { + privData->object[i]->Release(); + delete privData->object[i]; + privData->object[i] = NULL; + } + + delete privData; + privData = NULL; + return true; +} +void LoginState::Protocol(ProtocolStruct* protocol) +{ + if((PlayerName*)protocol) + PlayerJoinProtocol((PlayerName*)protocol); + +} +void LoginState::PlayerJoinProtocol(PlayerName* name) +{ + +} \ No newline at end of file diff --git a/Code/Game/DanBiasGame/GameClientState/LoginState.h b/Code/Game/DanBiasGame/GameClientState/LoginState.h new file mode 100644 index 00000000..d426187d --- /dev/null +++ b/Code/Game/DanBiasGame/GameClientState/LoginState.h @@ -0,0 +1,33 @@ +#ifndef DANBIAS_CLIENT_LOGINSTATE_H +#define DANBIAS_CLIENT_LOGINSTATE_H + +#include "GameClientState.h" +#include "OysterMath.h" +#include "NetworkClient.h" +#include +namespace DanBias +{ + namespace Client + { + + class LoginState : public GameClientState + { + private: + Oyster::Network::NetworkClient* nwClient; + struct myData; + myData* privData; + public: + LoginState(void); + ~LoginState(void); + bool Init(Oyster::Network::NetworkClient* nwClient); + bool LoadModels(std::wstring file); + bool InitCamera(Oyster::Math::Float3 startPos); + ClientState Update(float deltaTime, InputClass* KeyInput); + + bool Render(); + bool Release(); + void Protocol(ProtocolStruct* protocol)override; + void PlayerJoinProtocol(PlayerName* name); + + };};}; +#endif // ! DANBIAS_CLIENT_LOGINSTATE_H \ No newline at end of file From c78bbba3eb17f848c6eb32c4b44a86ca9ccb0e5a Mon Sep 17 00:00:00 2001 From: Sam Mario Svensson Date: Thu, 30 Jan 2014 13:33:59 +0100 Subject: [PATCH 73/76] GL- Removed commentet unnecessary code --- Code/Game/GameLogic/LevelLoader/Loader.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Code/Game/GameLogic/LevelLoader/Loader.cpp b/Code/Game/GameLogic/LevelLoader/Loader.cpp index a0206d89..3e15315c 100644 --- a/Code/Game/GameLogic/LevelLoader/Loader.cpp +++ b/Code/Game/GameLogic/LevelLoader/Loader.cpp @@ -17,18 +17,6 @@ char* Loader::LoadFile(std::string fileName, int &size) //convert from wstring to wchar then loads the file char* buffer = (char*)OysterResource::LoadResource(temp.c_str(), Oyster::Resource::ResourceType::ResourceType_Byte_Raw, -1 , false); - //std::ifstream f; - //f.open(fileName, std::ios::binary); - //if (!f.is_open()) - // return 0; - //f.seekg(0, std::ifstream::end); - //size = f.tellg(); - //f.seekg(0); - //char* buffer = new char[size]; - //f.read(buffer, size); - // - //f.close(); - //gets the size of the char buffer. size = OysterResource::GetResourceSize(buffer); return buffer; } \ No newline at end of file From 09f39ec26d4bd30cad18c4176cb9526370e5ea21 Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Thu, 30 Jan 2014 14:15:25 +0100 Subject: [PATCH 74/76] GameServer - Added acces directly to clients in network session --- Code/Game/GameProtocols/GeneralProtocols.h | 10 +- Code/Game/GameProtocols/LobbyProtocols.h | 107 +++++++++++++----- Code/Game/GameProtocols/ObjectProtocols.h | 48 ++++---- Code/Game/GameProtocols/PlayerProtocols.h | 20 ++-- .../GameProtocols/ProtocolIdentificationID.h | 7 +- Code/Game/GameServer/GameLobby.h | 2 +- .../GameServer/Implementation/GameLobby.cpp | 30 +++-- .../GameLobby_ProtocolParser.cpp | 14 ++- .../Implementation/GameSession_Gameplay.cpp | 2 +- Code/Network/NetworkAPI/NetworkSession.cpp | 79 +++++++------ Code/Network/NetworkAPI/NetworkSession.h | 7 ++ 11 files changed, 199 insertions(+), 127 deletions(-) diff --git a/Code/Game/GameProtocols/GeneralProtocols.h b/Code/Game/GameProtocols/GeneralProtocols.h index 98b7c060..02763ba0 100644 --- a/Code/Game/GameProtocols/GeneralProtocols.h +++ b/Code/Game/GameProtocols/GeneralProtocols.h @@ -19,8 +19,8 @@ namespace GameLogic Protocol_General_Status() { - this->protocol[protocol_INDEX_ID].value = protocol_General_Status; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_General_Status; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Short; } @@ -31,8 +31,8 @@ namespace GameLogic } Protocol_General_Status(States state) { - this->protocol[protocol_INDEX_ID].value = protocol_General_Status; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_General_Status; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->status = state; this->protocol[1].type = Oyster::Network::NetAttributeType_Short; } @@ -61,7 +61,7 @@ namespace GameLogic } Oyster::Network::CustomNetProtocol* GetProtocol() override { - this->protocol.Set(protocol_INDEX_ID, protocol_General_Text, Oyster::Network::NetAttributeType_Short); + this->protocol.Set(0, protocol_General_Text, Oyster::Network::NetAttributeType_Short); this->protocol.Set(1, destination, Oyster::Network::NetAttributeType_Int); this->protocol.Set(2, text); return &protocol; diff --git a/Code/Game/GameProtocols/LobbyProtocols.h b/Code/Game/GameProtocols/LobbyProtocols.h index 8b333a54..c653f556 100644 --- a/Code/Game/GameProtocols/LobbyProtocols.h +++ b/Code/Game/GameProtocols/LobbyProtocols.h @@ -22,8 +22,8 @@ namespace GameLogic Protocol_LobbyCreateGame() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Create; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Lobby_Create; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_CharArray; this->protocol[2].type = Oyster::Network::NetAttributeType_Char; @@ -50,8 +50,8 @@ namespace GameLogic Protocol_LobbyStartGame() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Start; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Lobby_Start; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Short; } @@ -74,8 +74,8 @@ namespace GameLogic // Login stuff Protocol_LobbyLogin() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Lobby_Join; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Short; } @@ -98,14 +98,14 @@ namespace GameLogic // // Protocol_LobbyJoin() // { - // this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; - // this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + // this->protocol[0].value = protocol_Lobby_Join; + // this->protocol[0].type = Oyster::Network::NetAttributeType_Short; // this->protocol[1].type = Oyster::Network::NetAttributeType_Short; // } // Protocol_LobbyJoin(Oyster::Network::CustomNetProtocol& p) // { - // this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Join; - // this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + // this->protocol[0].value = protocol_Lobby_Join; + // this->protocol[0].type = Oyster::Network::NetAttributeType_Short; // this->protocol[1].type = Oyster::Network::NetAttributeType_Short; // value = p[1].value.netShort; // } @@ -123,8 +123,8 @@ namespace GameLogic { Protocol_LobbyRefresh() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_Login; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Lobby_Login; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; } Protocol_LobbyRefresh(Oyster::Network::CustomNetProtocol& o) { @@ -147,35 +147,90 @@ namespace GameLogic struct PlayerData { std::string name; + std::string ip; int id; + int team; }; Utility::DynamicMemory::DynamicArray list; - + Protocol_LobbyClientData() { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_GameData; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Lobby_ClientData; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_UnsignedInt; //DataType list.Reserve(10); } Protocol_LobbyClientData(Oyster::Network::CustomNetProtocol& p) { - this->protocol[protocol_INDEX_ID].value = protocol_Lobby_GameData; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; - - list.Reserve(10); + unsigned int size = this->protocol[1].value.netUInt; + list.Reserve(size); + int a = 2; + for (unsigned int i = 0; i < list.Size(); i++) + { + PlayerData d; + d.id = this->protocol[a++].value.netInt; + d.team = this->protocol[a++].value.netInt; + d.name = this->protocol.Get(a++).value.netCharPtr; + d.ip = this->protocol.Get(a++).value.netCharPtr; + list.Push(d); + } } Oyster::Network::CustomNetProtocol* GetProtocol() override { - int a = 1; + this->protocol[1].value = list.Size(); + + int a = 2; for (unsigned int i = 0; i < list.Size(); i++) { - this->protocol[a].type = Oyster::Network::NetAttributeType_Int; - this->protocol[a].type = Oyster::Network::NetAttributeType_CharArray; + this->protocol[a].type = Oyster::Network::NetAttributeType_Int; // client-id + this->protocol[a++].value = list[i].id; - this->protocol[a].value = list[i].id; - this->protocol.Set(a, list[i].name); + this->protocol[a].type = Oyster::Network::NetAttributeType_Int; // team-id + this->protocol[a++].value = list[i].team; + + this->protocol[a].type = Oyster::Network::NetAttributeType_CharArray; // clientName + this->protocol.Set(a++, list[i].name); + + this->protocol[a].type = Oyster::Network::NetAttributeType_CharArray; // clientIP + this->protocol.Set(a++, list[i].ip); } + + return &protocol; + } + + private: + Oyster::Network::CustomNetProtocol protocol; + }; + + struct Protocol_LobbyGameData :public Oyster::Network::CustomProtocolObject + { + std::string mapName; + int majorVersion; + int minorVersion; + + Protocol_LobbyGameData() + { + this->protocol[0].value = protocol_Lobby_GameData; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; + + this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[2].type = Oyster::Network::NetAttributeType_Int; + this->protocol[3].type = Oyster::Network::NetAttributeType_CharArray; + } + Protocol_LobbyGameData(Oyster::Network::CustomNetProtocol& p) + { + majorVersion = (int)p.Get(1).value.netInt; + minorVersion = (int)p.Get(2).value.netInt; + mapName = p.Get(3).value.netCharPtr; + } + Oyster::Network::CustomNetProtocol* GetProtocol() override + { + this->protocol[1].value = majorVersion; + this->protocol[2].value = minorVersion; + this->protocol[3].value.netCharPtr = const_cast(mapName.c_str()); + return &protocol; } @@ -192,8 +247,8 @@ namespace GameLogic // // Protocol_LobbyMainData() // { - // this->protocol[protocol_INDEX_ID].value = protocol_Lobby_MainData; - // this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + // this->protocol[0].value = protocol_Lobby_MainData; + // this->protocol[0].type = Oyster::Network::NetAttributeType_Short; // // this->protocol[1].type = Oyster::Network::NetAttributeType_Short; // } diff --git a/Code/Game/GameProtocols/ObjectProtocols.h b/Code/Game/GameProtocols/ObjectProtocols.h index 9c4e2df1..5df97855 100644 --- a/Code/Game/GameProtocols/ObjectProtocols.h +++ b/Code/Game/GameProtocols/ObjectProtocols.h @@ -13,8 +13,8 @@ namespace GameLogic Protocol_ObjectPickup() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPickup; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectPickup; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_Short; @@ -28,8 +28,8 @@ namespace GameLogic } Protocol_ObjectPickup(int objectID, short pickupID) { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectPosition; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_Short; @@ -56,8 +56,8 @@ namespace GameLogic Protocol_ObjectDamage() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDamage; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectDamage; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; @@ -71,8 +71,8 @@ namespace GameLogic } Protocol_ObjectDamage(int id, float hp) { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDamage; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectDamage; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; @@ -97,8 +97,8 @@ namespace GameLogic Protocol_ObjectPosition() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectPosition; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; @@ -115,8 +115,8 @@ namespace GameLogic } Protocol_ObjectPosition(float m[16], int id) { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectPosition; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectPosition; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; @@ -149,8 +149,8 @@ namespace GameLogic Protocol_ObjectEnable() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectEnabled; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectEnabled; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; @@ -167,8 +167,8 @@ namespace GameLogic } Protocol_ObjectEnable(float m[16], int id) { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectEnabled; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectEnabled; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; @@ -199,8 +199,8 @@ namespace GameLogic Protocol_ObjectDisable() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDisabled; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectDisabled; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; @@ -211,8 +211,8 @@ namespace GameLogic } Protocol_ObjectDisable(int id, float time) { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectDisabled; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectDisabled; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; @@ -239,8 +239,8 @@ namespace GameLogic Protocol_ObjectCreate() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectCreate; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_ObjectCreate; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_CharArray; @@ -256,8 +256,8 @@ namespace GameLogic } Protocol_ObjectCreate(float m[16], int id, char *path) { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_ObjectCreate; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Int; + this->protocol[0].value = protocol_Gameplay_ObjectCreate; + this->protocol[0].type = Oyster::Network::NetAttributeType_Int; this->protocol[1].type = Oyster::Network::NetAttributeType_Int; this->protocol[2].type = Oyster::Network::NetAttributeType_CharArray; diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index d33ef9c3..7f8b81f0 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -25,8 +25,8 @@ namespace GameLogic Protocol_PlayerMovement() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerMovement; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_PlayerMovement; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; this->protocol[2].type = Oyster::Network::NetAttributeType_Bool; @@ -69,8 +69,8 @@ namespace GameLogic Protocol_PlayerLook() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerLookDir; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_PlayerLookDir; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Float; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; @@ -109,8 +109,8 @@ namespace GameLogic Protocol_PlayerChangeWeapon() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerChangeWeapon; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_PlayerChangeWeapon; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Float; this->protocol[2].type = Oyster::Network::NetAttributeType_Float; @@ -140,8 +140,8 @@ namespace GameLogic Protocol_PlayerShot() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerShot; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_PlayerShot; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; } @@ -170,8 +170,8 @@ namespace GameLogic Protocol_PlayerJump() { - this->protocol[protocol_INDEX_ID].value = protocol_Gameplay_PlayerJump; - this->protocol[protocol_INDEX_ID].type = Oyster::Network::NetAttributeType_Short; + this->protocol[0].value = protocol_Gameplay_PlayerJump; + this->protocol[0].type = Oyster::Network::NetAttributeType_Short; this->protocol[1].type = Oyster::Network::NetAttributeType_Bool; } diff --git a/Code/Game/GameProtocols/ProtocolIdentificationID.h b/Code/Game/GameProtocols/ProtocolIdentificationID.h index 180f6541..cb596a1c 100644 --- a/Code/Game/GameProtocols/ProtocolIdentificationID.h +++ b/Code/Game/GameProtocols/ProtocolIdentificationID.h @@ -8,11 +8,6 @@ /* THERE CAN ABSOLUTLEY NOT BE TWO DEFINITIONS WITH THE SAME ID!! */ -/** Index where the identifier is located(aka protocol identification index) **/ -/* Use this as id accesser since it may change in the future. */ -#define protocol_INDEX_ID 0 - - /***********************************/ /********* RESERVERD PROTOCOLS ***************************************************************************************************/ /********** [ 0 - 99 ] *********/ @@ -37,7 +32,7 @@ #define protocol_Lobby_Join 202 #define protocol_Lobby_Login 203 #define protocol_Lobby_Refresh 204 -#define protocol_Lobby_MainData 205 +#define protocol_Lobby_ClientData 205 #define protocol_Lobby_GameData 206 #define protocol_LobbyMAX 299 diff --git a/Code/Game/GameServer/GameLobby.h b/Code/Game/GameServer/GameLobby.h index 8463b9d9..8a421e1d 100644 --- a/Code/Game/GameServer/GameLobby.h +++ b/Code/Game/GameServer/GameLobby.h @@ -42,8 +42,8 @@ namespace DanBias //void LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Join: void LobbyLogin(GameLogic::Protocol_LobbyLogin& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Login: void LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_Refresh: + void LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_GameData: void LobbyMainData(GameLogic::Protocol_LobbyClientData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_MainData: - //void LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c); //id = protocol_Lobby_GameData: private: void ClientEventCallback(Oyster::Network::NetEvent e) override; diff --git a/Code/Game/GameServer/Implementation/GameLobby.cpp b/Code/Game/GameServer/Implementation/GameLobby.cpp index 308e4ae6..4291f44b 100644 --- a/Code/Game/GameServer/Implementation/GameLobby.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby.cpp @@ -52,12 +52,8 @@ namespace DanBias desc.gameTime = this->description.gameTime; desc.mapNumber = this->description.mapNumber; desc.owner = this; - while (this->GetClientCount()) - { - NetClient c; - if((c = this->Detach())) - desc.clients.Push(c); - } + desc.clients = this->clients; + if(this->gameSession.Create(desc)) { this->gameSession.Run(); @@ -89,9 +85,27 @@ namespace DanBias printf("New client(%i) connected - %s \n", client->GetID(), client->GetIpAddress().c_str()); Attach(client); - Protocol_LobbyClientData p; + Protocol_LobbyClientData p1; + Protocol_LobbyGameData p2; - client->Send(p.GetProtocol()); + for (unsigned int i = 0; i < this->clients.Size(); i++) + { + if(this->clients[i]) + { + Protocol_LobbyClientData::PlayerData t; + t.id = this->clients[i]->GetID(); + t.ip = this->clients[i]->GetIpAddress(); + t.team = 0; + t.name = "DennisÄrKung"; + p1.list.Push(t); + } + } + p2.majorVersion = 1; + p2.minorVersion = 0; + p2.mapName = "BetsMap"; + + client->Send(p1.GetProtocol()); + client->Send(p2.GetProtocol()); } }//End namespace DanBias \ No newline at end of file diff --git a/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp index 1c841519..3a86bc6b 100644 --- a/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby_ProtocolParser.cpp @@ -23,7 +23,9 @@ void GameLobby::ParseProtocol(Oyster::Network::CustomNetProtocol& p, NetworkClie break; case protocol_Lobby_Refresh: this->LobbyRefresh (Protocol_LobbyRefresh (p), c); break; - case protocol_Lobby_MainData: this->LobbyMainData (Protocol_LobbyClientData (p), c); + case protocol_Lobby_GameData: this->LobbyGameData (Protocol_LobbyGameData (p), c); + break; + case protocol_Lobby_ClientData: this->LobbyMainData (Protocol_LobbyClientData (p), c); break; //case protocol_Lobby_GameData: this->LobbyGameData (Protocol_LobbyGameData (p), c); //break; @@ -60,7 +62,7 @@ void GameLobby::GeneralText(GameLogic::Protocol_General_Text& p, Oyster::Network //} void GameLobby::LobbyStartGame(GameLogic::Protocol_LobbyStartGame& p, Oyster::Network::NetworkClient* c) { - + //TODO: Prio 1 } //void GameLobby::LobbyJoin(GameLogic::Protocol_LobbyJoin& p, Oyster::Network::NetworkClient* c) //{ @@ -80,14 +82,14 @@ void GameLobby::LobbyLogin(GameLogic::Protocol_LobbyLogin& p, Oyster::Network::N void GameLobby::LobbyRefresh(GameLogic::Protocol_LobbyRefresh& p, Oyster::Network::NetworkClient* c) { //Dont need to handle this on the server... +} +void GameLobby::LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c) +{ + } void GameLobby::LobbyMainData(GameLogic::Protocol_LobbyClientData& p, Oyster::Network::NetworkClient* c) { } -//void GameLobby::LobbyGameData(GameLogic::Protocol_LobbyGameData& p, Oyster::Network::NetworkClient* c) -//{ -// -//} diff --git a/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp index a17f1ce8..0a528f04 100644 --- a/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp @@ -74,7 +74,7 @@ namespace DanBias void GameSession::ParseProtocol(Oyster::Network::CustomNetProtocol& p, DanBias::GameClient* c) { - switch (p[protocol_INDEX_ID].value.netShort) + switch (p[0].value.netShort) { case protocol_Gameplay_PlayerMovement: this->Gameplay_PlayerMovement ( Protocol_PlayerMovement (p), c ); break; diff --git a/Code/Network/NetworkAPI/NetworkSession.cpp b/Code/Network/NetworkAPI/NetworkSession.cpp index 35352243..c7bf15ba 100644 --- a/Code/Network/NetworkAPI/NetworkSession.cpp +++ b/Code/Network/NetworkAPI/NetworkSession.cpp @@ -16,7 +16,6 @@ using namespace Oyster::Network; struct NetworkSession::PrivateSessionData { - Utility::DynamicMemory::DynamicArray clients; NetworkClient::ClientEventFunction messageCallback; std::mutex clientListLock; NetworkSession* owner; //Where clients end up when session is closed. @@ -34,7 +33,7 @@ NetworkSession::NetworkSession() {} NetworkSession::NetworkSession(const NetworkSession& orig) { - this->data->clients = orig.data->clients; + this->clients = orig.clients; this->data->owner = orig.data->owner; this->clientCount = orig.clientCount; this->data->id = orig.data->id; @@ -43,7 +42,7 @@ NetworkSession::NetworkSession(const NetworkSession& orig) const NetworkSession& NetworkSession::operator=(const NetworkSession& orig) { - this->data->clients = orig.data->clients; + this->clients = orig.clients; this->data->owner = orig.data->owner; this->clientCount = orig.clientCount; this->data->id = orig.data->id; @@ -54,7 +53,7 @@ const NetworkSession& NetworkSession::operator=(const NetworkSession& orig) NetworkSession::~NetworkSession() { - this->data->clients.Clear(); + this->clients.Clear(); this->clientCount = 0; this->data->messageCallback = 0; delete this->data; @@ -63,9 +62,9 @@ NetworkSession::~NetworkSession() void NetworkSession::ProcessClients() { - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - if(this->data->clients[i]) this->data->clients[i]->Update(); + if(this->clients[i]) this->clients[i]->Update(); } } @@ -74,19 +73,19 @@ bool NetworkSession::Attach(NetClient client) this->data->clientListLock.lock(); int k = -1; - for (unsigned int i = 0; (k == -1) && i < this->data->clients.Size(); i++) + for (unsigned int i = 0; (k == -1) && i < this->clients.Size(); i++) { - if(!this->data->clients[i]->IsConnected()) //TODO: Dont check connection status, check more general status.. + if(!this->clients[i]->IsConnected()) //TODO: Dont check connection status, check more general status.. k = i; } if(k == -1) { - this->data->clients.Push(client); + this->clients.Push(client); } else { - this->data->clients[k] = client; + this->clients[k] = client; } this->clientCount++; @@ -101,18 +100,18 @@ void NetworkSession::DetachAll() { if(this->data->owner) { - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - this->data->owner->Attach(this->data->clients[i]); - this->data->clients[i] = 0; + this->data->owner->Attach(this->clients[i]); + this->clients[i] = 0; } } else { - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - this->data->clients[i]->Disconnect(); - this->data->clients[i] = 0; + this->clients[i]->Disconnect(); + this->clients[i] = 0; } } this->clientCount = 0; @@ -124,12 +123,12 @@ NetClient NetworkSession::Detach(const NetworkClient* client) this->data->clientListLock.lock(); - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - if(this->data->clients[i] && this->data->clients[0]->GetID() == client->GetID()) + if(this->clients[i] && this->clients[0]->GetID() == client->GetID()) { - val = this->data->clients[i]; - this->data->clients[i] = 0; + val = this->clients[i]; + this->clients[i] = 0; this->clientCount--; } } @@ -145,12 +144,12 @@ NetClient NetworkSession::Detach(short ID) this->data->clientListLock.lock(); - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - if(this->data->clients[i] && this->data->clients[0]->GetID() == ID) + if(this->clients[i] && this->clients[0]->GetID() == ID) { - val = this->data->clients[i]; - this->data->clients[i] = 0; + val = this->clients[i]; + this->clients[i] = 0; this->clientCount--; } } @@ -166,12 +165,12 @@ NetClient NetworkSession::Detach() this->data->clientListLock.lock(); - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - if(this->data->clients[i]) + if(this->clients[i]) { - val = this->data->clients[i]; - this->data->clients[i] = 0; + val = this->clients[i]; + this->clients[i] = 0; this->clientCount--; break; } @@ -185,11 +184,11 @@ NetClient NetworkSession::Detach() bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol) { bool returnValue = false; - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - if(this->data->clients[i]) + if(this->clients[i]) { - this->data->clients[i]->Send(&protocol); + this->clients[i]->Send(&protocol); returnValue = true; } } @@ -199,11 +198,11 @@ bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol) bool NetworkSession::Send(Oyster::Network::CustomNetProtocol& protocol, int ID) { - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - if(this->data->clients[i] && this->data->clients[i]->GetID() == ID) + if(this->clients[i] && this->clients[i]->GetID() == ID) { - this->data->clients[i]->Send(&protocol); + this->clients[i]->Send(&protocol); return true; } } @@ -214,17 +213,17 @@ void NetworkSession::CloseSession(bool dissconnectClients) { this->data->clientListLock.lock(); - for (unsigned int i = 0; i < this->data->clients.Size(); i++) + for (unsigned int i = 0; i < this->clients.Size(); i++) { - if(this->data->clients[i]) + if(this->clients[i]) { - if(dissconnectClients) this->data->clients[i]->Disconnect(); - else if(this->data->owner) this->data->owner->Attach(this->data->clients[i]); - else this->data->clients[i]->Disconnect(); //Idiot check, clients have to go somewhere.. + if(dissconnectClients) this->clients[i]->Disconnect(); + else if(this->data->owner) this->data->owner->Attach(this->clients[i]); + else this->clients[i]->Disconnect(); //Idiot check, clients have to go somewhere.. } } - this->data->clients.Clear(); + this->clients.Clear(); this->clientCount = 0; this->data->clientListLock.unlock(); diff --git a/Code/Network/NetworkAPI/NetworkSession.h b/Code/Network/NetworkAPI/NetworkSession.h index ffe047a4..b44f5c61 100644 --- a/Code/Network/NetworkAPI/NetworkSession.h +++ b/Code/Network/NetworkAPI/NetworkSession.h @@ -6,6 +6,9 @@ //warning C4150: deletion of pointer to incomplete type, no destructor called #pragma warning(disable : 4150) +//needs to have dll-interface to be used by clients of class 'Oyster::Network::NetworkSession' +#pragma warning(disable : 4251) + #include "NetworkAPI_Preprocessor.h" #include "NetworkServerEventStruct.h" @@ -18,6 +21,7 @@ namespace Oyster namespace Network { typedef Utility::DynamicMemory::SmartPointer NetClient; + typedef Utility::DynamicMemory::DynamicArray NetClientList; class NET_API_EXPORT NetworkSession { public: @@ -92,6 +96,9 @@ namespace Oyster */ virtual void ClientEventCallback(NetEvent e) = 0; + protected: + NetClientList clients; + private: int clientCount; struct PrivateSessionData; From 05e24d051417bb8c2f88b769aff57a155846ae8d Mon Sep 17 00:00:00 2001 From: lindaandersson Date: Thu, 30 Jan 2014 14:17:50 +0100 Subject: [PATCH 75/76] GL - uppdate server --- Code/Game/DanBiasGame/GameClientState/GameState.cpp | 2 ++ Code/Game/DanBiasGame/GameClientState/LobbyState.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Code/Game/DanBiasGame/GameClientState/GameState.cpp b/Code/Game/DanBiasGame/GameClientState/GameState.cpp index 437a40f4..2e04959d 100644 --- a/Code/Game/DanBiasGame/GameClientState/GameState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/GameState.cpp @@ -5,6 +5,7 @@ #include #include "NetworkClient.h" #include "Camera.h" +#include using namespace DanBias::Client; @@ -143,6 +144,7 @@ bool GameState::InitCamera(Oyster::Math::Float3 startPos) GameClientState::ClientState GameState::Update(float deltaTime, InputClass* KeyInput) { + DanBias::GameServerAPI::ServerUpdate(); switch (privData->state) { case gameStateState_loading: diff --git a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp index d30fcd59..260bf1be 100644 --- a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp @@ -4,6 +4,7 @@ #include "C_obj/C_Player.h" #include "C_obj/C_StaticObj.h" #include "C_obj/C_DynamicObj.h" +#include using namespace DanBias::Client; @@ -86,9 +87,14 @@ GameClientState::ClientState LobbyState::Update(float deltaTime, InputClass* Key // update animation // send data to server // check data from server + DanBias::GameServerAPI::ServerUpdate(); if( KeyInput->IsKeyPressed(DIK_G)) - return ClientState_Game; + { + DanBias::GameServerAPI::GameStart(); + return ClientState_Game; + } + return ClientState_Same; } bool LobbyState::Render() From 40f0fe61f088b3facfd1727ab47282f68af0906c Mon Sep 17 00:00:00 2001 From: Dennis Andersen Date: Fri, 31 Jan 2014 08:41:08 +0100 Subject: [PATCH 76/76] GameServer - Fixed Protocols not recieveing properly and fixed the resource loader. --- Code/Game/DanBiasGame/DanBiasGame_Impl.cpp | 3 +- Code/Game/DanBiasGame/DanBiasMaincpp.cpp | 316 --------------- .../Game/DanBiasGame/GameClientRecieverFunc.h | 207 ++++++---- .../GameClientState/LobbyState.cpp | 3 +- Code/Game/GameLogic/Game.cpp | 3 +- Code/Game/GameProtocols/LobbyProtocols.h | 4 +- Code/Game/GameProtocols/ObjectProtocols.h | 9 +- Code/Game/GameProtocols/PlayerProtocols.h | 18 +- Code/Game/GameServer/GameClient.h | 2 + .../GameServer/Implementation/GameClient.cpp | 6 + .../GameServer/Implementation/GameLobby.cpp | 2 + .../Implementation/GameSession_Gameplay.cpp | 82 +++- .../Implementation/GameSession_General.cpp | 2 + Code/Misc/Resource/OResource.h | 4 +- Code/Misc/Resource/ResourceManager.cpp | 383 +++++++++++++----- Code/Misc/Resource/ResourceManager.h | 16 +- Code/Misc/Utilities-Impl.h | 2 +- Code/Network/NetworkAPI/NetworkClient.cpp | 3 +- Code/Network/NetworkAPI/NetworkSession.h | 2 +- 19 files changed, 520 insertions(+), 547 deletions(-) delete mode 100644 Code/Game/DanBiasGame/DanBiasMaincpp.cpp diff --git a/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp b/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp index 64526c0b..43edd84a 100644 --- a/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp +++ b/Code/Game/DanBiasGame/DanBiasGame_Impl.cpp @@ -52,6 +52,7 @@ namespace DanBias DanBiasClientReturn DanBiasGame::Initiate(DanBiasGameDesc& desc) { + WindowShell::CreateConsoleWindow(); if(! m_data->window->CreateWin(WindowShell::WINDOW_INIT_DESC())) return DanBiasClientReturn_Error; @@ -132,7 +133,7 @@ namespace DanBias HRESULT DanBiasGame::Update(float deltaTime) { - + m_data->recieverObj->Update(); m_data->inputObj->Update(); diff --git a/Code/Game/DanBiasGame/DanBiasMaincpp.cpp b/Code/Game/DanBiasGame/DanBiasMaincpp.cpp deleted file mode 100644 index b62dbe11..00000000 --- a/Code/Game/DanBiasGame/DanBiasMaincpp.cpp +++ /dev/null @@ -1,316 +0,0 @@ -//-------------------------------------------------------------------------------------- -// File: TemplateMain.cpp -// -// BTH-D3D-Template -// -// Copyright (c) Stefan Petersson 2011. All rights reserved. -//-------------------------------------------------------------------------------------- -#define NOMINMAX -#include -#include < -#include "DllInterfaces/GFXAPI.h" -//#include "IGame.h" - -#include "L_inputClass.h" - -// debug window include -#include -#include -#include -#include - - - - -//-------------------------------------------------------------------------------------- -// Global Variables -//-------------------------------------------------------------------------------------- -HINSTANCE g_hInst = NULL; -HWND g_hWnd = NULL; - -//GameLogic::IGame* game; -InputClass* inputObj; - - -//-------------------------------------------------------------------------------------- -// Forward declarations -//-------------------------------------------------------------------------------------- -HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ); -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -HRESULT Render(float deltaTime); -HRESULT Update(float deltaTime); -HRESULT InitDirect3D(); -HRESULT InitGame(); -HRESULT CleanUp(); - - - -//-------------------------------------------------------------------------------------- -// Entry point to the program. Initializes everything and goes into a message processing -// loop. Idle time is used to render the scene. -//-------------------------------------------------------------------------------------- - -void SetStdOutToNewConsole() -{ - // allocate a console for this app - AllocConsole(); - - // redirect unbuffered STDOUT to the console - HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); - int fileDescriptor = _open_osfhandle((intptr_t)consoleHandle, _O_TEXT); - FILE *fp = _fdopen( fileDescriptor, "w" ); - *stdout = *fp; - setvbuf( stdout, NULL, _IONBF, 0 ); - - // give the console window a nicer title - - SetConsoleTitle(L"Debug Output"); - - // give the console window a bigger buffer size - CONSOLE_SCREEN_BUFFER_INFO csbi; - if ( GetConsoleScreenBufferInfo(consoleHandle, &csbi) ) - { - COORD bufferSize; - bufferSize.X = csbi.dwSize.X; - bufferSize.Y = 50; - SetConsoleScreenBufferSize(consoleHandle, bufferSize); - } -} - -int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) -{ - // for dynamic .dll loading - // path is relative to the .exe and .dll pos - // also change the VC directories - working dir is set to $(SolutionDir)..\Bin\Executable\Tester - // to fit with where the .obj files is - // linker/ input/ delayed load .dll - specify the .dll that should be loaded - - BOOL success = SetDllDirectory(L"..\\..\\DLL"); - if (success == 0) - { - return 0; - } - - if( FAILED( InitWindow( hInstance, nCmdShow ) ) ) - return 0; - - if( FAILED( InitDirect3D() ) ) - return 0; - - if( FAILED( InitGame() ) ) - return 0; - - __int64 cntsPerSec = 0; - QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec); - float secsPerCnt = 1.0f / (float)cntsPerSec; - - __int64 prevTimeStamp = 0; - QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp); - - //debug window - //SetStdOutToNewConsole(); - - // Main message loop - MSG msg = {0}; - while(WM_QUIT != msg.message) - { - if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE) ) - { - TranslateMessage( &msg ); - DispatchMessage( &msg ); - } - else - { - __int64 currTimeStamp = 0; - QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp); - float dt = (currTimeStamp - prevTimeStamp) * secsPerCnt; - - //render - Update(dt); - Render(dt); - - prevTimeStamp = currTimeStamp; - } - } - CleanUp(); - return (int) msg.wParam; -} - -//-------------------------------------------------------------------------------------- -// Register class and create window -//-------------------------------------------------------------------------------------- -HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow ) -{ - // Register class - WNDCLASSEX wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInstance; - wcex.hIcon = 0; - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wcex.lpszMenuName = NULL; - wcex.lpszClassName = L"BTH_D3D_Template"; - wcex.hIconSm = 0; - if( !RegisterClassEx(&wcex) ) - return E_FAIL; - - // Adjust and create window - g_hInst = hInstance; - RECT rc = { 0, 0, 1024, 768 }; - AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE ); - - if(!(g_hWnd = CreateWindow( - L"BTH_D3D_Template", - L"BTH - Direct3D 11.0 Template", - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, - CW_USEDEFAULT, - rc.right - rc.left, - rc.bottom - rc.top, - NULL, - NULL, - hInstance, - NULL))) - { - return E_FAIL; - } - - ShowWindow( g_hWnd, nCmdShow ); - - return S_OK; -} - -//-------------------------------------------------------------------------------------- -// Create Direct3D with Oyster Graphics -//-------------------------------------------------------------------------------------- -HRESULT InitDirect3D() -{ - if(Oyster::Graphics::API::Init(g_hWnd, false, false, Oyster::Math::Float2( 1024, 768)) != Oyster::Graphics::API::Sucsess) - return E_FAIL; - return S_OK; -} - -//-------------------------------------------------------------------------------------- -// Init the input and the game -//------------------------------------------------------------------------------------- -HRESULT InitGame() -{ - inputObj = new InputClass; - if(!inputObj->Initialize(g_hInst, g_hWnd, 1024, 768)) - { - MessageBox(0, L"Could not initialize the input object.", L"Error", MB_OK); - return false; - } - /*game = new GameLogic::IGame(); - game->Init(); - game->StartGame(); - */ - return S_OK; -} - -HRESULT Update(float deltaTime) -{ - inputObj->Update(); - //GameLogic::keyInput key = GameLogic::keyInput_none; - - //if(inputObj->IsKeyPressed(DIK_W)) - //{ - // key = GameLogic::keyInput_W; - //} - //else if(inputObj->IsKeyPressed(DIK_A)) - //{ - // key = GameLogic::keyInput_A; - //} - //else if(inputObj->IsKeyPressed(DIK_S)) - //{ - // key = GameLogic::keyInput_S; - //} - //else if(inputObj->IsKeyPressed(DIK_D)) - //{ - // key = GameLogic::keyInput_D; - //} - - float pitch = 0; - float yaw = 0; - - //if(inputObj->IsMousePressed()) - //{ - pitch = inputObj->GetPitch(); - yaw = inputObj->GetYaw(); - //} - - //game->Update(key, pitch, yaw); - - - return S_OK; -} - -HRESULT Render(float deltaTime) -{ - int isPressed = 0; - if(inputObj->IsKeyPressed(DIK_A)) - { - isPressed = 1; - //std::cout<<"test"; - } - - //game->Render(); - wchar_t title[255]; - swprintf(title, sizeof(title), L"| Pressing A: %d | \n", (int)(isPressed)); - SetWindowText(g_hWnd, title); - - Oyster::Graphics::API::EndFrame(); - - return S_OK; -} - -HRESULT CleanUp() -{ - - /*if(game) - { - delete game; - game = NULL; - }*/ - return S_OK; -} -//-------------------------------------------------------------------------------------- -// Called every time the application receives a message -//-------------------------------------------------------------------------------------- -LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) -{ - PAINTSTRUCT ps; - HDC hdc; - - switch (message) - { - case WM_PAINT: - hdc = BeginPaint(hWnd, &ps); - EndPaint(hWnd, &ps); - break; - - case WM_DESTROY: - PostQuitMessage(0); - break; - - case WM_KEYDOWN: - - switch(wParam) - { - case VK_ESCAPE: - PostQuitMessage(0); - break; - } - break; - - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - - return 0; -} - diff --git a/Code/Game/DanBiasGame/GameClientRecieverFunc.h b/Code/Game/DanBiasGame/GameClientRecieverFunc.h index c5293738..36b831a0 100644 --- a/Code/Game/DanBiasGame/GameClientRecieverFunc.h +++ b/Code/Game/DanBiasGame/GameClientRecieverFunc.h @@ -5,106 +5,139 @@ namespace DanBias { -struct GameRecieverObject :public Oyster::Network::NetworkClient -{ - Client::GameClientState* gameClientState; - - // receiver function for server messages - // parsing protocols and sending it to the gameState - void NetworkCallback(Oyster::Network::CustomNetProtocol& p) override + + struct GameRecieverObject :public Oyster::Network::NetworkClient { - int pType = p[0].value.netInt; - switch (pType) + Client::GameClientState* gameClientState; + + // receiver function for server messages + // parsing protocols and sending it to the gameState + void NetworkCallback(Oyster::Network::CustomNetProtocol& p) override { - case protocol_General_Status: + int pType = p[0].value.netInt; + switch (pType) { - GameLogic::Protocol_General_Status::States state; - state = (GameLogic::Protocol_General_Status::States)p[1].value.netShort; - if( state == GameLogic::Protocol_General_Status::States_disconected) + case protocol_General_Status: { - // server disconnected - DanBiasGame::Release(); + GameLogic::Protocol_General_Status::States state; + state = (GameLogic::Protocol_General_Status::States)p[1].value.netShort; + if( state == GameLogic::Protocol_General_Status::States_disconected) + { + // server disconnected + DanBiasGame::Release(); + } } - } - break; - case protocol_Gameplay_PlayerMovement: - { - Client::GameClientState::KeyInput* protocolData = new Client::GameClientState::KeyInput; - for(int i = 0; i< 6; i++) + break; + case protocol_Gameplay_PlayerMovement: { - protocolData->key[i] = p[i+1].value.netBool; + Client::GameClientState::KeyInput* protocolData = new Client::GameClientState::KeyInput; + for(int i = 0; i< 6; i++) + { + protocolData->key[i] = p[i+1].value.netBool; + } + + if(dynamic_cast(gameClientState)) + ((Client::GameState*)gameClientState)->Protocol(protocolData); + delete protocolData; + protocolData = NULL; } + break; + //case protocol_Gameplay_PlayerPosition: + // { + // Client::GameClientState::PlayerPos* protocolData = new Client::GameClientState::PlayerPos; + // for(int i = 0; i< 3; i++) + // { + // protocolData->playerPos[i] = p[i].value.netFloat; + // } + // if(dynamic_cast(gameClientState)) + // ((Client::GameState*)gameClientState)->Protocol(protocolData); + // delete protocolData; + // protocolData = NULL; + // } + // break; - if(dynamic_cast(gameClientState)) - ((Client::GameState*)gameClientState)->Protocol(protocolData); - delete protocolData; - protocolData = NULL; - } - break; - //case protocol_Gameplay_PlayerPosition: - // { - // Client::GameClientState::PlayerPos* protocolData = new Client::GameClientState::PlayerPos; - // for(int i = 0; i< 3; i++) - // { - // protocolData->playerPos[i] = p[i].value.netFloat; - // } - // if(dynamic_cast(gameClientState)) - // ((Client::GameState*)gameClientState)->Protocol(protocolData); - // delete protocolData; - // protocolData = NULL; - // } - // break; - - case protocol_Gameplay_ObjectCreate: - { - Client::GameClientState::NewObj* protocolData = new Client::GameClientState::NewObj; - protocolData->object_ID = p[1].value.netInt; - protocolData->path = p[2].value.netCharPtr; - for(int i = 0; i< 16; i++) + case protocol_Gameplay_ObjectCreate: { - protocolData->worldPos[i] = p[i+3].value.netFloat; + Client::GameClientState::NewObj* protocolData = new Client::GameClientState::NewObj; + protocolData->object_ID = p[1].value.netInt; + protocolData->path = p[2].value.netCharPtr; + for(int i = 0; i< 16; i++) + { + protocolData->worldPos[i] = p[i+3].value.netFloat; + } + + if(dynamic_cast(gameClientState)) + ((Client::GameState*)gameClientState)->Protocol(protocolData); + + delete p[2].value.netCharPtr; //delete char array + delete protocolData; + protocolData = NULL; } - - if(dynamic_cast(gameClientState)) - ((Client::GameState*)gameClientState)->Protocol(protocolData); - - delete p[2].value.netCharPtr; //delete char array - delete protocolData; - protocolData = NULL; - } - break; - case protocol_Gameplay_ObjectDisabled: - { - Client::GameClientState::RemoveObj* protocolData = new Client::GameClientState::RemoveObj; - protocolData->object_ID = p[1].value.netInt; - - if(dynamic_cast(gameClientState)) - ((Client::GameState*)gameClientState)->Protocol(protocolData); - - delete protocolData; - protocolData = NULL; - } - break; - case protocol_Gameplay_ObjectPosition: - { - - Client::GameClientState::ObjPos protocolData; - protocolData.object_ID = p[1].value.netInt; - for(int i = 0; i< 16; i++) + break; + case protocol_Gameplay_ObjectDisabled: { - protocolData.worldPos[i] = p[i+2].value.netFloat; + Client::GameClientState::RemoveObj* protocolData = new Client::GameClientState::RemoveObj; + protocolData->object_ID = p[1].value.netInt; + + if(dynamic_cast(gameClientState)) + ((Client::GameState*)gameClientState)->Protocol(protocolData); + + delete protocolData; + protocolData = NULL; } + break; + case protocol_Gameplay_ObjectPosition: + { - if(dynamic_cast(gameClientState)) - ((Client::GameState*)gameClientState)->Protocol(&protocolData); + Client::GameClientState::ObjPos protocolData; + protocolData.object_ID = p[1].value.netInt; + for(int i = 0; i< 16; i++) + { + protocolData.worldPos[i] = p[i+2].value.netFloat; + } + + if(dynamic_cast(gameClientState)) + ((Client::GameState*)gameClientState)->Protocol(&protocolData); + } + break; + + default: + break; + } + + if(ProtocolIsLobby(p[0].value.netInt)) ParseLobbyProtocol(p); + } + + void ParseLobbyProtocol(Oyster::Network::CustomNetProtocol& p) + { + switch (p[0].value.netShort) + { + case protocol_General_Status: //this->GeneralStatus (Protocol_General_Status (p), c); + break; + case protocol_General_Text: //this->GeneralText (Protocol_General_Text (p), c); + break; + //case protocol_Lobby_Create: this->LobbyCreateGame (Protocol_LobbyCreateGame (p), c); + //break; + case protocol_Lobby_Start: //this->LobbyStartGame (Protocol_LobbyStartGame (p), c); + break; + //case protocol_Lobby_Join: this->LobbyJoin (Protocol_LobbyJoin (p), c); + //break; + case protocol_Lobby_Login: //this->LobbyLogin (Protocol_LobbyLogin (p), c); + break; + case protocol_Lobby_Refresh: //this->LobbyRefresh (Protocol_LobbyRefresh (p), c); + break; + case protocol_Lobby_GameData: //this->LobbyGameData (Protocol_LobbyGameData (p), c); + { + GameLogic::Protocol_LobbyGameData temp(p); + printf("%s, %i.%i\n", temp.mapName.c_str(), temp.majorVersion, temp.minorVersion); + } + break; + case protocol_Lobby_ClientData: //this->LobbyMainData (Protocol_LobbyClientData (p), c); + break; + //case protocol_Lobby_GameData: this->LobbyGameData (Protocol_LobbyGameData (p), c); + //break; } - break; - - default: - break; - } - - } -}; + } + }; } #endif \ No newline at end of file diff --git a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp index 260bf1be..5d6bff79 100644 --- a/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp +++ b/Code/Game/DanBiasGame/GameClientState/LobbyState.cpp @@ -91,7 +91,8 @@ GameClientState::ClientState LobbyState::Update(float deltaTime, InputClass* Key if( KeyInput->IsKeyPressed(DIK_G)) { - DanBias::GameServerAPI::GameStart(); + if(!DanBias::GameServerAPI::GameStart()) + return GameClientState::ClientState_Same; return ClientState_Game; } diff --git a/Code/Game/GameLogic/Game.cpp b/Code/Game/GameLogic/Game.cpp index 53cb75ec..12ca7237 100644 --- a/Code/Game/GameLogic/Game.cpp +++ b/Code/Game/GameLogic/Game.cpp @@ -128,8 +128,7 @@ bool Game::NewFrame() if(this->players[i]->player) this->players[i]->player->EndFrame(); } - - //gameInstance.onMoveFnc(this->level); + gameInstance.onMoveFnc(this->level); return true; } diff --git a/Code/Game/GameProtocols/LobbyProtocols.h b/Code/Game/GameProtocols/LobbyProtocols.h index c653f556..192d81f2 100644 --- a/Code/Game/GameProtocols/LobbyProtocols.h +++ b/Code/Game/GameProtocols/LobbyProtocols.h @@ -158,8 +158,6 @@ namespace GameLogic this->protocol[0].value = protocol_Lobby_ClientData; this->protocol[0].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_UnsignedInt; //DataType - list.Reserve(10); } Protocol_LobbyClientData(Oyster::Network::CustomNetProtocol& p) @@ -229,7 +227,7 @@ namespace GameLogic { this->protocol[1].value = majorVersion; this->protocol[2].value = minorVersion; - this->protocol[3].value.netCharPtr = const_cast(mapName.c_str()); + this->protocol.Set(3, mapName.c_str()); return &protocol; } diff --git a/Code/Game/GameProtocols/ObjectProtocols.h b/Code/Game/GameProtocols/ObjectProtocols.h index 5df97855..b71599b1 100644 --- a/Code/Game/GameProtocols/ObjectProtocols.h +++ b/Code/Game/GameProtocols/ObjectProtocols.h @@ -8,7 +8,7 @@ namespace GameLogic { struct Protocol_ObjectPickup :public Oyster::Network::CustomProtocolObject { - int object_ID; + short object_ID; short pickup_ID; Protocol_ObjectPickup() @@ -16,7 +16,7 @@ namespace GameLogic this->protocol[0].value = protocol_Gameplay_ObjectPickup; this->protocol[0].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[1].type = Oyster::Network::NetAttributeType_Short; this->protocol[2].type = Oyster::Network::NetAttributeType_Short; object_ID = -1; @@ -24,14 +24,15 @@ namespace GameLogic } Protocol_ObjectPickup(Oyster::Network::CustomNetProtocol& p) { - + object_ID = p[1].value.netShort; + pickup_ID = p[2].value.netShort; } Protocol_ObjectPickup(int objectID, short pickupID) { this->protocol[0].value = protocol_Gameplay_ObjectPosition; this->protocol[0].type = Oyster::Network::NetAttributeType_Short; - this->protocol[1].type = Oyster::Network::NetAttributeType_Int; + this->protocol[1].type = Oyster::Network::NetAttributeType_Short; this->protocol[2].type = Oyster::Network::NetAttributeType_Short; object_ID = objectID; diff --git a/Code/Game/GameProtocols/PlayerProtocols.h b/Code/Game/GameProtocols/PlayerProtocols.h index 7f8b81f0..2413aa92 100644 --- a/Code/Game/GameProtocols/PlayerProtocols.h +++ b/Code/Game/GameProtocols/PlayerProtocols.h @@ -35,7 +35,10 @@ namespace GameLogic } Protocol_PlayerMovement(Oyster::Network::CustomNetProtocol& p) { - + bForward = p[1].value.netBool; + bBackward = p[2].value.netBool; + bLeft = p[3].value.netBool; + bRight = p[4].value.netBool; } const Protocol_PlayerMovement& operator=(Oyster::Network::CustomNetProtocol& val) { @@ -79,7 +82,9 @@ namespace GameLogic } Protocol_PlayerLook(Oyster::Network::CustomNetProtocol& p) { - + lookDirX = p[1].value.netFloat; + lookDirY = p[2].value.netFloat; + lookDirZ = p[3].value.netFloat; } const Protocol_PlayerLook& operator=(Oyster::Network::CustomNetProtocol& val) { @@ -111,11 +116,6 @@ namespace GameLogic { this->protocol[0].value = protocol_Gameplay_PlayerChangeWeapon; this->protocol[0].type = Oyster::Network::NetAttributeType_Short; - - this->protocol[1].type = Oyster::Network::NetAttributeType_Float; - this->protocol[2].type = Oyster::Network::NetAttributeType_Float; - this->protocol[3].type = Oyster::Network::NetAttributeType_Float; - } Protocol_PlayerChangeWeapon(Oyster::Network::CustomNetProtocol& p) { @@ -147,7 +147,7 @@ namespace GameLogic } Protocol_PlayerShot(Oyster::Network::CustomNetProtocol& p) { - + hasShot = p[1].value.netBool; } const Protocol_PlayerShot& operator=(Oyster::Network::CustomNetProtocol& val) { @@ -177,7 +177,7 @@ namespace GameLogic } Protocol_PlayerJump(Oyster::Network::CustomNetProtocol& p) { - + hasJumped = p[1].value.netBool; } const Protocol_PlayerJump& operator=(Oyster::Network::CustomNetProtocol& val) { diff --git a/Code/Game/GameServer/GameClient.h b/Code/Game/GameServer/GameClient.h index 241f13ca..5db6efdb 100644 --- a/Code/Game/GameServer/GameClient.h +++ b/Code/Game/GameServer/GameClient.h @@ -26,6 +26,8 @@ namespace DanBias Utility::DynamicMemory::SmartPointer ReleaseClient(); int GetID() const; + bool Equals(const Oyster::Network::NetworkClient* c); + private: GameLogic::IPlayerData* player; Utility::DynamicMemory::SmartPointer client; diff --git a/Code/Game/GameServer/Implementation/GameClient.cpp b/Code/Game/GameServer/Implementation/GameClient.cpp index 07999205..3f0c8308 100644 --- a/Code/Game/GameServer/Implementation/GameClient.cpp +++ b/Code/Game/GameServer/Implementation/GameClient.cpp @@ -50,3 +50,9 @@ int GameClient::GetID() const { return this->id; } +bool GameClient::Equals(const NetworkClient* c) +{ + return (c->GetID() == this->client->GetID()); +} + + diff --git a/Code/Game/GameServer/Implementation/GameLobby.cpp b/Code/Game/GameServer/Implementation/GameLobby.cpp index 4291f44b..b68846d4 100644 --- a/Code/Game/GameServer/Implementation/GameLobby.cpp +++ b/Code/Game/GameServer/Implementation/GameLobby.cpp @@ -53,6 +53,8 @@ namespace DanBias desc.mapNumber = this->description.mapNumber; desc.owner = this; desc.clients = this->clients; + + this->clients.Clear(); if(this->gameSession.Create(desc)) { diff --git a/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp index 0a528f04..c0041301 100644 --- a/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_Gameplay.cpp @@ -47,24 +47,80 @@ namespace DanBias void GameSession::ClientEventCallback(NetEvent e) { + int temp = -1; + //Find the idiot + for (unsigned int i = 0; i < this->clients.Size(); i++) + { + if(this->clients[i]->Equals(e.sender)) + { + temp = i; + } + } + if(temp == -1) + { + this->Detach(e.sender)->Disconnect(); + return; + } + SmartPointer cl = this->clients[temp]; + + switch (e.args.type) + { + case NetworkClient::ClientEventArgs::EventType_Disconnect: + break; + case NetworkClient::ClientEventArgs::EventType_ProtocolFailedToRecieve: + break; + case NetworkClient::ClientEventArgs::EventType_ProtocolFailedToSend: + printf("\t(%i : %s) - EventType_ProtocolFailedToSend\n", e.sender->GetID(), e.sender->GetIpAddress().c_str()); + this->Detach(e.sender)->Disconnect(); + break; + case NetworkClient::ClientEventArgs::EventType_ProtocolRecieved: + printf("\t(%i : %s) - EventType_ProtocolRecieved\n", e.sender->GetID(), e.sender->GetIpAddress().c_str()); + this->ParseProtocol(e.args.data.protocol, cl); + break; + } } void GameSession::ObjectMove(GameLogic::IObjectData* movedObject) { - //GameLogic::IObjectData* obj = NULL; - //if(dynamic_cast(movedObject)) - // obj =((GameLogic::ILevelData*)movedObject)->GetObjectAt(0); - //if(obj) - //{ - // if(obj->GetType() == OBJECT_TYPE_BOX) - // { - // obj->GetID(); - // Oyster::Math::Float4x4 world =obj->GetOrientation(); - // Protocol_ObjectPosition p(world, 1); - // GameSession::gameSession->Send(p.GetProtocol()); - // } - //} + if(dynamic_cast (movedObject)) + { + IPlayerData* temp = (IPlayerData*)movedObject; + temp->GetID(); + Oyster::Math::Float4x4 world = temp->GetOrientation(); + + Protocol_ObjectPosition p(world, 2); + GameSession::gameSession->Send(*p.GetProtocol()); + } + GameLogic::IObjectData* obj = NULL; + if(dynamic_cast(movedObject)) + { + obj = ((GameLogic::ILevelData*)movedObject)->GetObjectAt(0); + if(obj) + { + if(obj->GetObjectType() == OBJECT_TYPE_WORLD) + { + obj->GetID(); + Oyster::Math::Float4x4 world =obj->GetOrientation(); + + Protocol_ObjectPosition p(world, 0); + GameSession::gameSession->Send(*p.GetProtocol()); + } + } + + obj = NULL; + obj =((GameLogic::ILevelData*)movedObject)->GetObjectAt(1); + if(obj) + { + if(obj->GetObjectType() == OBJECT_TYPE_BOX) + { + obj->GetID(); + Oyster::Math::Float4x4 world = obj->GetOrientation(); + Protocol_ObjectPosition p(world, 1); + GameSession::gameSession->Send(*p.GetProtocol()); + } + } + } } diff --git a/Code/Game/GameServer/Implementation/GameSession_General.cpp b/Code/Game/GameServer/Implementation/GameSession_General.cpp index 945198af..ae947874 100644 --- a/Code/Game/GameServer/Implementation/GameSession_General.cpp +++ b/Code/Game/GameServer/Implementation/GameSession_General.cpp @@ -51,6 +51,7 @@ namespace DanBias if(this->isCreated) return false; /* standard initialization of some data */ + NetworkSession::clients = desc.clients; this->clients.Resize(desc.clients.Size()); this->owner = desc.owner; @@ -73,6 +74,7 @@ namespace DanBias { if( (p = this->gameInstance.CreatePlayer()) ) { + desc.clients[i]->SetOwner(this); this->clients[i] = new GameClient(desc.clients[i], p); } else diff --git a/Code/Misc/Resource/OResource.h b/Code/Misc/Resource/OResource.h index a0573c92..83ed474f 100644 --- a/Code/Misc/Resource/OResource.h +++ b/Code/Misc/Resource/OResource.h @@ -38,11 +38,11 @@ namespace Oyster { return this->resourceData; } - inline unsigned long long GetResourceSize() const + inline unsigned int GetResourceSize() const { return this->resourceSize; } - inline unsigned long long GetResourceElementSize() const + inline unsigned int GetResourceElementSize() const { return this->resourceElementSize; } diff --git a/Code/Misc/Resource/ResourceManager.cpp b/Code/Misc/Resource/ResourceManager.cpp index 133ce00b..b4195bb4 100644 --- a/Code/Misc/Resource/ResourceManager.cpp +++ b/Code/Misc/Resource/ResourceManager.cpp @@ -4,83 +4,267 @@ #include "ResourceManager.h" +#include "..\Utilities.h" using namespace Oyster::Resource; -struct Oyster::Resource::ResourceData +struct ::ResourceData { + LoadFunction loadFnc; + UnloadFunction unloadFnc; + ResourceType resourcetype; + HRESOURCE resource; + unsigned int resourceSize; + int resourceID; + Utility::DynamicMemory::ReferenceCount referenceCount; +}; +bool ReadFromFile(const wchar_t fileName[], const char openFlag[], std::string& outData, size_t elemSize, bool ANSI = false) +{ + std::string sFilename; + std::wstring wsFile = fileName; + ::Utility::String::WStringToString(wsFile, sFilename); + size_t bytesTotal = 0; + size_t bytesRead = 0; + FILE *stream; + + if( fopen_s( &stream, sFilename.c_str(), openFlag ) == 0 ) + { + //Get size of the file + fseek(stream, 0L, SEEK_END); + bytesTotal = ftell(stream); + fseek(stream, 0L, SEEK_SET); + fflush(stream); + + //Sanity check + if(bytesTotal == 0) return false; + + //Create the new byte buffer + char *buff = new char[bytesTotal + 1]; + + //Read the bytes to the end + bytesRead = fread_s( buff, bytesTotal, elemSize, bytesTotal ,stream ); + fclose( stream ); + + //Did we read enough bytes (Get the bytes if we read with ANSI since the hidden characters is ignored) + if(!ANSI && bytesRead != bytesTotal) return false; + + buff[bytesRead + 1]; + + outData.clear(); + outData.resize(bytesRead); + memcpy(&outData[0], &buff[0], bytesRead); + + delete [] buff; + } + else + { + std::string msg = "Failed to open file: \n"; + msg.append(sFilename.c_str()); + + return false; + } + + return true; } - -ResourceData* FindResource(std::map resources, const HRESOURCE& h) const +const wchar_t* FindResourceKey(std::map& resources, const HRESOURCE h) { for (auto i = resources.begin(); i != resources.end() ; i++) { - if(i->second->GetResourceHandle() == h) + if(i->second->resource == h) + { + return i->first.c_str(); + } + } + return 0; +} +ResourceData* FindResource(std::map& resources, const HRESOURCE h) +{ + for (auto i = resources.begin(); i != resources.end() ; i++) + { + if(i->second->resource == h) { return i->second; } } return 0; } -ResourceData* FindResource(std::map resources, const wchar_t c[]) const +ResourceData* FindResource(std::map& resources, const wchar_t c[]) { std::wstring temp = c; - auto t = this->resources.find(c); - if(t == this->resources.end()) return 0; + auto t = resources.find(c); + if(t == resources.end()) return 0; return t->second; } -void SaveResource( std::map resources, OResource* r, bool addNew ) +void SaveResource( std::map& resources, ResourceData* r, const std::wstring& key, bool addNew ) { - if(!r) return; - if(addNew) { - this->resources[r->GetResourceFilename()] = r; + resources[key] = r; } - r->resourceRef.Incref(); + r->referenceCount.Incref(); } +bool Release(std::map& resources, ResourceData* resource) +{ + if(resource->referenceCount.Decref() == 0) + { + const wchar_t* temp = FindResourceKey(resources, resource->resource); + + switch (resource->resourcetype) + { + case Oyster::Resource::ResourceType_Byte_Raw: + case Oyster::Resource::ResourceType_Byte_ANSI: + case Oyster::Resource::ResourceType_Byte_UTF8: + case Oyster::Resource::ResourceType_Byte_UNICODE: + case Oyster::Resource::ResourceType_Byte_UTF16LE: + delete [] ((char*)resource->resource); + resource->resource = 0; + break; + + case Oyster::Resource::ResourceType_UNKNOWN: + resource->unloadFnc(resource->resource); + resource->resource = 0; + break; + } + + if(temp) delete resources[temp]; + + return true; + } + return false; +} +ResourceData* Load(/*Out*/ResourceData* targetMem, /*in*/const wchar_t source[], /*in*/ResourceType type) +{ + std::string sOut; + bool success = false; + + switch (type) + { + case Oyster::Resource::ResourceType_Byte_Raw: + success = ReadFromFile(source, "rb", sOut, sizeof(char)); + break; + + case Oyster::Resource::ResourceType_Byte_ANSI: + success = ReadFromFile(source, "r", sOut, sizeof(char), true); + break; + + case Oyster::Resource::ResourceType_Byte_UTF8: + success = ReadFromFile(source, "r, ccs=UTF-8", sOut, sizeof(char)); + break; + + case Oyster::Resource::ResourceType_Byte_UNICODE: + success = ReadFromFile(source, "r, ccs=UNICODE", sOut, sizeof(char)); + break; + + case Oyster::Resource::ResourceType_Byte_UTF16LE: + success = ReadFromFile(source, "r, ccs=UTF-16LE", sOut, sizeof(char)); + break; + } + + if(!success) return 0; + + if(sOut.size()) + { + char *data = new char[sOut.size()+1]; + data[sOut.size()] = '\0'; + memcpy(&data[0], &sOut[0], sOut.size()); + + targetMem->resource = (HRESOURCE&)data; + targetMem->loadFnc = 0; + targetMem->unloadFnc = 0; + targetMem->resourceID = 0; + targetMem->resourcetype = type; + } + + return targetMem; +} +ResourceData* Load(/*Out*/ResourceData* targetMem, /*in*/const wchar_t source[], LoadFunction loadFnc, UnloadFunction unloadFnc) +{ + if(loadFnc) + { + targetMem->resource = loadFnc(source); + if(targetMem->resource) + { + targetMem->resourceSize = 0; + targetMem->resourcetype = ResourceType_UNKNOWN; + targetMem->loadFnc = loadFnc; + targetMem->unloadFnc = unloadFnc; + } + } + return targetMem; +} +ResourceData* Reload(std::map resources, ResourceData* resource, const wchar_t* filename) +{ + switch (resource->resourcetype) + { + case Oyster::Resource::ResourceType_Byte_Raw: + case Oyster::Resource::ResourceType_Byte_ANSI: + case Oyster::Resource::ResourceType_Byte_UTF8: + case Oyster::Resource::ResourceType_Byte_UNICODE: + case Oyster::Resource::ResourceType_Byte_UTF16LE: + if(Release(resources, resource)) + return Load(resource, filename, resource->loadFnc, resource->unloadFnc); + break; + + case Oyster::Resource::ResourceType_UNKNOWN: + { + resource->unloadFnc(resource->resource); + + HRESOURCE r = resource->loadFnc(filename); + if(!r) return 0; + resource->resource = r; + } + break; + } + + return resource; +} + ResourceManager::ResourceManager() -{ +{ } +ResourceManager::~ResourceManager() +{ Clean(); } -} -ResourceManager:: -HRESOURCE OysterResource::LoadResource(const wchar_t* filename, ResourceType type, int customID, bool force) +HBYTEARRAY ResourceManager::LoadBytes(const wchar_t filename[], ResourceType type, int customID, bool force) { if(!filename) return 0; - OResource *resourceData = FindResource(filename); + ResourceData *t = FindResource(this->resources, filename); - if(resourceData) + if(t) { if(force) { - return OysterResource::ReloadResource(filename); + return (HBYTEARRAY)Reload(resources, t, filename )->resource; } else { //Add new reference - resourcePrivate.SaveResource(resourceData, false); - return resourceData->GetResourceHandle(); + SaveResource(this->resources, t, filename, false); + return (HBYTEARRAY)t->resource; } } else { - resourceData = OResource::Load(filename, type); - if(resourceData) + t = Load(new ResourceData(), filename, type); + if(t) { - resourceData->SetResourceID(customID); - resourcePrivate.SaveResource(resourceData); + t->resourceID = (customID); + SaveResource(this->resources, t, filename, true); + } + else + { + return 0; } } - return resourceData->GetResourceHandle(); + return (HBYTE*)t->resource; } -HRESOURCE OysterResource::LoadResource(const wchar_t filename[], CustomLoadFunction loadFnc, int customId, bool force) +HRESOURCE ResourceManager::LoadResource(const wchar_t filename[], LoadFunction loadFnc, UnloadFunction unloadFnc, int customId, bool force) { if(!filename) { @@ -91,152 +275,153 @@ HRESOURCE OysterResource::LoadResource(const wchar_t filename[], CustomLoadFunct return 0; } - OResource *resourceData = resourcePrivate.FindResource(filename); - if(resourceData) + ResourceData *t = FindResource(this->resources, filename); + if(t) { if(force) { - return OysterResource::ReloadResource(filename); + return ResourceManager::ReloadResource(filename); } else { //Add new reference - resourcePrivate.SaveResource(resourceData, false); - return resourceData->GetResourceHandle(); + SaveResource(this->resources, t, filename, false); + return t->resource; } } else { - resourceData = OResource::Load(filename, loadFnc); - if(resourceData) + t = Load(new ResourceData(), filename, loadFnc, unloadFnc ); + if(t) { - resourceData->SetResourceID(customId); - resourcePrivate.SaveResource(resourceData); + t->resourceID = (customId); + SaveResource(this->resources, t, filename, true); + } + else + { + delete t; } } - if(!resourceData) + if(!t) { return 0; } - return (OHRESOURCE)resourceData->GetResourceHandle(); + return (HRESOURCE)t->resource; } -OHRESOURCE OysterResource::ReloadResource(const wchar_t filename[]) +HRESOURCE ResourceManager::ReloadResource(const wchar_t filename[]) { - OResource *resourceData = resourcePrivate.FindResource(filename); - if(!resourceData) return 0; //The resource has not been loaded + ResourceData *t = FindResource(this->resources, filename); + if(!t) return 0; //The resource has not been loaded - return OResource::Reload(resourceData)->GetResourceHandle(); + return Reload(this->resources, t, filename)->resource; } -OHRESOURCE OysterResource::ReloadResource(OHRESOURCE resource) +HRESOURCE ResourceManager::ReloadResource(HRESOURCE& resource) { - OResource *resourceData = resourcePrivate.FindResource(resource); - if(!resourceData) return 0; //The resource has not been loaded - - return OResource::Reload(resourceData)->GetResourceHandle(); + ResourceData *t = FindResource(this->resources, resource); + if(!t) return 0; + return Reload(this->resources, t, FindResourceKey(this->resources, resource))->resource; } -void OysterResource::Clean() +void ResourceManager::Clean() { - auto i = resourcePrivate.resources.begin(); - auto last = resourcePrivate.resources.end(); + if(this->resources.empty()) return; + + auto i = this->resources.begin(); + auto last = resources.end(); for (i; i != last; i++) { //Remove all the references - while (!OResource::Release(i->second)); - - std::wstring temp = i->second->GetResourceFilename(); - delete resourcePrivate.resources[temp]; - - + while (!Release(this->resources, i->second)); } - resourcePrivate.resources.clear(); + resources.clear(); } -void OysterResource::ReleaseResource(const OHRESOURCE& resourceData) +void ResourceManager::ReleaseResource(const HRESOURCE& resourceData) { - OResource* t = resourcePrivate.FindResource(resourceData); + ResourceData *t = FindResource(this->resources, resourceData); if(t) { - if(OResource::Release(t)) + if(Release(resources, t)) { - std::wstring temp = t->GetResourceFilename(); - delete resourcePrivate.resources[temp]; - resourcePrivate.resources.erase(temp); + const wchar_t* temp = 0; + if((temp = FindResourceKey(resources, resourceData))) + { + std::wstring ws = std::wstring(temp); + delete resources[ws]; + resources.erase(ws); + } } } } -void OysterResource::ReleaseResource(const wchar_t filename[]) +void ResourceManager::ReleaseResource(const wchar_t filename[]) { - OResource* t = resourcePrivate.FindResource(filename); + ResourceData *t = FindResource(this->resources, filename); if(t) { - if(OResource::Release(t)) + if(Release(resources, t)) { - std::wstring temp = t->GetResourceFilename(); - delete resourcePrivate.resources[temp]; - resourcePrivate.resources.erase(temp); + delete resources[filename]; + resources.erase(filename); } } } -void OysterResource::SetResourceId (const OHRESOURCE& resourceData, unsigned int id) -{ - OResource* t = resourcePrivate.FindResource(resourceData); - if(t) t->SetResourceID(id); + +void ResourceManager::SetResourceId (const HRESOURCE& resourceData, unsigned int id) +{ + ResourceData *t = FindResource(this->resources, resourceData); + + if(t) t->resourceID = (id); } -void OysterResource::SetResourceId(const wchar_t c[], unsigned int id) +void ResourceManager::SetResourceId(const wchar_t c[], unsigned int id) { - OResource* t = resourcePrivate.FindResource(c); + ResourceData *t = FindResource(this->resources, c); - if(t) t->SetResourceID(id); + if(t) t->resourceID = (id); } -ResourceType OysterResource::GetResourceType (const OHRESOURCE& resourceData) +ResourceType ResourceManager::GetResourceType (const HRESOURCE& resourceData) { - OResource* t = resourcePrivate.FindResource(resourceData); + ResourceData *t = FindResource(this->resources, resourceData); - if(t) return t->GetResourceType(); + if(t) return t->resourcetype; return ResourceType_INVALID; } -ResourceType OysterResource::GetResourceType (const wchar_t c[]) +ResourceType ResourceManager::GetResourceType (const wchar_t c[]) { - OResource* t = resourcePrivate.FindResource(c); + ResourceData *t = FindResource(this->resources, c); - if(t) return t->GetResourceType(); + if(t) return t->resourcetype; return ResourceType_INVALID; } -const wchar_t* OysterResource::GetResourceFilename (const OHRESOURCE& resourceData) +const wchar_t* ResourceManager::GetResourceFilename (const HRESOURCE& resourceData) { - OResource* t = resourcePrivate.FindResource(resourceData); + return FindResourceKey(this->resources, resourceData); +} +HRESOURCE ResourceManager::GetResourceHandle(const wchar_t filename[]) +{ + ResourceData *t = FindResource(this->resources, filename); - if(t) return t->GetResourceFilename(); + if(t) return t->resource; return 0; } -OHRESOURCE OysterResource::GetResourceHandle(const wchar_t filename[]) +int ResourceManager::GetResourceId (const HRESOURCE& resourceData) { - OResource* t = resourcePrivate.FindResource(filename); + ResourceData *t = FindResource(this->resources, resourceData); - if(t) return t->GetResourceHandle(); - - return 0; -} -int OysterResource::GetResourceId (const OHRESOURCE& resourceData) -{ - OResource* t = resourcePrivate.FindResource(resourceData); - - if(t) return t->GetResourceID(); + if(t) return t->resourceID; return -1; } -int OysterResource::GetResourceId(const wchar_t c[]) +int ResourceManager::GetResourceId(const wchar_t c[]) { - OResource* t = resourcePrivate.FindResource(c); + ResourceData *t = FindResource(this->resources, c); - if(t) return t->GetResourceID(); + if(t) return t->resourceID; return -1; } diff --git a/Code/Misc/Resource/ResourceManager.h b/Code/Misc/Resource/ResourceManager.h index 0b24b3e4..4ab34b9b 100644 --- a/Code/Misc/Resource/ResourceManager.h +++ b/Code/Misc/Resource/ResourceManager.h @@ -11,6 +11,8 @@ namespace Oyster struct ResourceData; typedef void* HRESOURCE; + typedef char HBYTE; + typedef HBYTE* HBYTEARRAY; /** Typedef on a fuction required for custom unloading */ typedef void(*UnloadFunction)(void* loadedData); @@ -41,8 +43,6 @@ namespace Oyster public: ResourceManager(); ~ResourceManager(); - ResourceManager(const ResourceManager&) = delete; - const ResourceManager& operator=(const ResourceManager&) = delete; /** * Load a resource given a type. @@ -52,7 +52,7 @@ namespace Oyster * @param force If set to true, the resource will be reloaded if it already exists. If it does not, nothing happens. * @return If function suceeds, a handle to the resource will be returned. If failed 0 is returned. */ - char* LoadBytes(const wchar_t filename[], ResourceType type, int customId = -1, bool force = false); + HBYTEARRAY LoadBytes(const wchar_t filename[], ResourceType type, int customId = -1, bool force = false); /** * Load a resource with a custom loading function @@ -68,16 +68,16 @@ namespace Oyster /** * Reload a resource * @param filename The path to the resource. - * @return If function suceeds, the return value is true. + * @return If function suceeds, the return value is the reloaded resource. */ - bool ReloadResource(const wchar_t filename[]); + HRESOURCE ReloadResource(const wchar_t filename[]); /** * Reload a resource * @param filename The path to the resource. - * @return If function suceeds, a handle to the resource will be returned. If failed 0 is returned. + * @return If function suceeds, the return value is the reloaded resource. */ - bool ReloadResource(HRESOURCE resource); + HRESOURCE ReloadResource(HRESOURCE& resource); /** * Releases all resources loaded by the resource handler. @@ -151,6 +151,8 @@ namespace Oyster int GetResourceId(const wchar_t filename[]); private: + ResourceManager(const ResourceManager& obj); + const ResourceManager& operator=(const ResourceManager&); std::map resources; }; diff --git a/Code/Misc/Utilities-Impl.h b/Code/Misc/Utilities-Impl.h index 476861f6..1cfe8b8f 100644 --- a/Code/Misc/Utilities-Impl.h +++ b/Code/Misc/Utilities-Impl.h @@ -243,7 +243,7 @@ namespace Utility this->_ptr = p._ptr; this->_rc = p._rc; - this->_rc->Incref(); + if(this->_rc) this->_rc->Incref(); } return *this; } diff --git a/Code/Network/NetworkAPI/NetworkClient.cpp b/Code/Network/NetworkAPI/NetworkClient.cpp index 9539cdeb..8e8c28fa 100644 --- a/Code/Network/NetworkAPI/NetworkClient.cpp +++ b/Code/Network/NetworkAPI/NetworkClient.cpp @@ -159,6 +159,7 @@ bool NetworkClient::operator ==(const int& ID) void NetworkClient::Update() { + if(!this->privateData) return; while (!this->privateData->recieveQueue.IsEmpty()) { NetEvent temp = this->privateData->recieveQueue.Pop(); @@ -199,7 +200,7 @@ bool NetworkClient::Connect(unsigned short port, const char serverIP[]) //Connect has succeeded if(result != 0) return false; - + this->privateData->owner = 0; this->privateData->parent = this; this->privateData->thread.Start(); diff --git a/Code/Network/NetworkAPI/NetworkSession.h b/Code/Network/NetworkAPI/NetworkSession.h index b44f5c61..f0677c77 100644 --- a/Code/Network/NetworkAPI/NetworkSession.h +++ b/Code/Network/NetworkAPI/NetworkSession.h @@ -32,7 +32,7 @@ namespace Oyster /** Parse session events such as protocols recieved etc. */ - void ProcessClients(); + virtual void ProcessClients(); /** *