diff --git a/Client/mods/deathmatch/logic/CClientBuildingManager.h b/Client/mods/deathmatch/logic/CClientBuildingManager.h index d44cc7d70f..11cc759e8d 100644 --- a/Client/mods/deathmatch/logic/CClientBuildingManager.h +++ b/Client/mods/deathmatch/logic/CClientBuildingManager.h @@ -40,7 +40,11 @@ class CClientBuildingManager private: bool DoPoolResize(size_t newCapacity); - void AddToList(CClientBuilding* pBuilding) { m_List.push_back(pBuilding); } + void AddToList(CClientBuilding* pBuilding) + { + ResizePoolIfNeeds(); + m_List.push_back(pBuilding); + } void RemoveFromList(CClientBuilding* pBuilding); std::list m_List; diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 401cf5dee5..b539aedd59 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -129,8 +129,12 @@ class CClientGame SCRIPTFILE, WATER, WEAPON, - POINTLIGHTS, + _DATABASE_CONNECTION, // server only + TRAIN_TRACK, + ROOT, UNKNOWN, + BUILDING, + POINTLIGHTS, }; enum diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 094812d816..b21e713d4a 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -4218,6 +4218,26 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) break; } + case CClientGame::BUILDING: + { + std::uint16_t modelId; + SRotationRadiansSync rotationRadians(false); + + // Read out the position, rotation, object ID + bitStream.Read(&position); + bitStream.Read(&rotationRadians); + bitStream.ReadCompressed(modelId); + + if (!CClientBuildingManager::IsValidModel(modelId)) + modelId = 1700; + + bitStream.Read(LowLodObjectID); + CClientBuilding* pBuilding = new CClientBuilding(g_pClientGame->m_pManager, EntityID, modelId, position.data.vecPosition, rotationRadians.data.vecRotation, ucInterior); + + pBuilding->SetUsesCollision(bCollisonsEnabled); + break; + } + default: { assert(0); @@ -4287,10 +4307,18 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) if (TempLowLodObjectID != INVALID_ELEMENT_ID) { - CClientObject* pTempObject = DynamicCast(pTempEntity); - CClientObject* pLowLodObject = DynamicCast(CElementIDs::GetElement(TempLowLodObjectID)); - if (pTempObject) - pTempObject->SetLowLodObject(pLowLodObject); + if (CClientObject* pTempObject = DynamicCast(pTempEntity)) + { + CClientObject* pLowLodObject = DynamicCast(CElementIDs::GetElement(TempLowLodObjectID)); + if (pTempObject) + pTempObject->SetLowLodObject(pLowLodObject); + } + else if (CClientBuilding* pTempObject = DynamicCast(pTempEntity)) + { + CClientBuilding* pLowLod = DynamicCast(CElementIDs::GetElement(TempLowLodObjectID)); + if (pTempObject) + pTempObject->SetLowLodBuilding(pLowLod); + } } delete pEntityStuff; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp index a622b3c54d..c374e169e6 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp @@ -53,8 +53,6 @@ CClientBuilding* CLuaBuildingDefs::CreateBuilding(lua_State* const luaVM, std::u else rot.emplace(CVector(0, 0, 0)); - m_pBuildingManager->ResizePoolIfNeeds(); - CClientBuilding* pBuilding = new CClientBuilding(m_pManager, INVALID_ELEMENT_ID, modelId, pos, rot.value() , interior.value_or(0)); CClientEntity* pRoot = pResource->GetResourceDynamicEntity(); diff --git a/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp index 65eca46286..1df3a933d4 100644 --- a/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp @@ -499,6 +499,22 @@ void CElementRPCs::SetElementModel(CClientEntity* pSource, NetBitStreamInterface pObject->CallEvent("onClientElementModelChange", Arguments, true); } + break; + } + case CCLIENTBUILDING: + { + CClientBuilding* building = static_cast(pSource); + const auto currentModel = building->GetModel(); + + if (currentModel != usModel) + { + building->SetModel(usModel); + CLuaArguments Arguments; + Arguments.PushNumber(currentModel); + Arguments.PushNumber(usModel); + building->CallEvent("onClientElementModelChange", Arguments, true); + } + break; } } @@ -543,6 +559,12 @@ void CElementRPCs::SetElementCollisionsEnabled(CClientEntity* pSource, NetBitStr pObject->SetCollisionEnabled(bEnable); break; } + + case CCLIENTBUILDING: + { + static_cast(pSource)->SetUsesCollision(bEnable); + break; + } } } } @@ -594,6 +616,13 @@ void CElementRPCs::SetLowLodElement(CClientEntity* pSource, NetBitStreamInterfac pObject->SetLowLodObject(pLowLodObject); break; } + case CCLIENTBUILDING: + { + CClientBuilding* pLowLodBuilding = DynamicCast(CElementIDs::GetElement(LowLodObjectID)); + CClientBuilding* pBuilding = static_cast(pSource); + pBuilding->SetLowLodBuilding(pLowLodBuilding); + break; + } } } } diff --git a/Server/mods/deathmatch/logic/CBuilding.cpp b/Server/mods/deathmatch/logic/CBuilding.cpp new file mode 100644 index 0000000000..72afed4f39 --- /dev/null +++ b/Server/mods/deathmatch/logic/CBuilding.cpp @@ -0,0 +1,212 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/CBuilding.cpp + * PURPOSE: Object entity class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CBuilding.h" +#include "CBuildingManager.h" +#include "CLogger.h" +#include "Utils.h" + +extern CGame* g_pGame; + +CBuilding::CBuilding(CElement* pParent, CBuildingManager* pBuildingManager) : CElement(pParent) +{ + // Init + m_iType = CElement::BUILDING; + SetTypeName("buidling"); + + m_pBuildingManager = pBuildingManager; + m_model = 0xFFFF; + m_bDoubleSided = false; + m_bCollisionsEnabled = true; + m_pLowLodBuilding = nullptr; + m_pHighLodBuilding = nullptr; + + // Add us to the manager's list + pBuildingManager->AddToList(this); +} + +CBuilding::CBuilding(const CBuilding& Copy) : CElement(Copy.m_pParent), m_pLowLodBuilding(Copy.m_pLowLodBuilding) +{ + // Init + m_iType = CElement::BUILDING; + SetTypeName("buidling"); + + m_pBuildingManager = Copy.m_pBuildingManager; + m_model = Copy.m_model; + m_bDoubleSided = Copy.m_bDoubleSided; + m_vecPosition = Copy.m_vecPosition; + m_vecRotation = Copy.m_vecRotation; + m_bCollisionsEnabled = Copy.m_bCollisionsEnabled; + m_pHighLodBuilding = nullptr; + + // Add us to the manager's list + m_pBuildingManager->AddToList(this); + UpdateSpatialData(); +} + +CBuilding::~CBuilding() +{ + // Unlink us from manager + Unlink(); +} + +CElement* CBuilding::Clone(bool* bAddEntity, CResource* pResource) +{ + return new CBuilding(*this); +} + +void CBuilding::Unlink() +{ + // Remove us from the manager's list + m_pBuildingManager->RemoveFromList(this); + + // Remove LowLod refs in others + SetLowLodBuilding(nullptr); + + if (m_pHighLodBuilding) + m_pHighLodBuilding->SetLowLodBuilding(nullptr); +} + +bool CBuilding::ReadSpecialData(const int iLine) +{ + // Grab the "posX" data + if (!GetCustomDataFloat("posX", m_vecPosition.fX, true)) + { + CLogger::ErrorPrintf("Bad/missing 'posX' attribute in (line %d)\n", iLine); + return false; + } + + // Grab the "posY" data + if (!GetCustomDataFloat("posY", m_vecPosition.fY, true)) + { + CLogger::ErrorPrintf("Bad/missing 'posY' attribute in (line %d)\n", iLine); + return false; + } + + // Grab the "posZ" data + if (!GetCustomDataFloat("posZ", m_vecPosition.fZ, true)) + { + CLogger::ErrorPrintf("Bad/missing 'posZ' attribute in (line %d)\n", iLine); + return false; + } + + // Grab the "rotX", "rotY" and "rotZ" data + GetCustomDataFloat("rotX", m_vecRotation.fX, true); + GetCustomDataFloat("rotY", m_vecRotation.fY, true); + GetCustomDataFloat("rotZ", m_vecRotation.fZ, true); + // We store radians, but load degrees + ConvertDegreesToRadians(m_vecRotation); + + // Grab the "model" data + int iTemp; + if (GetCustomDataInt("model", iTemp, true)) + { + // Valid id? + if (!CBuildingManager::IsValidModel(iTemp)) + { + CLogger::ErrorPrintf("Bad 'model' (%d) id specified in (line %d)\n", iTemp, iLine); + return false; + } + + // Set the building id + m_model = static_cast(iTemp); + } + else + { + CLogger::ErrorPrintf("Bad/missing 'model' attribute in (line %d)\n", iLine); + return false; + } + + if (GetCustomDataInt("interior", iTemp, true)) + m_ucInterior = static_cast(iTemp); + + if (!GetCustomDataBool("collisions", m_bCollisionsEnabled, true)) + m_bCollisionsEnabled = true; + + return true; +} + +void CBuilding::GetMatrix(CMatrix& matrix) +{ + matrix.vPos = GetPosition(); + CVector vecRotation; + GetRotation(vecRotation); + + // Do extra calculation to change rotation order if it will make a difference + if (vecRotation.fX != 0 && vecRotation.fY != 0) + { + ConvertRadiansToDegreesNoWrap(vecRotation); + vecRotation = ConvertEulerRotationOrder(vecRotation, EULER_ZXY, EULER_ZYX); + ConvertDegreesToRadiansNoWrap(vecRotation); + } + matrix.SetRotation(vecRotation); +} + +void CBuilding::SetMatrix(const CMatrix& matrix) +{ + // Set position and rotation from matrix + SetPosition(matrix.vPos); + CVector vecRotation = matrix.GetRotation(); + SetRotation(vecRotation); +} + +const CVector& CBuilding::GetPosition() +{ + return m_vecPosition; +} + +void CBuilding::SetPosition(const CVector& vecPosition) +{ + // Different position? + if (m_vecPosition != vecPosition) + { + // Update our vectors + m_vecPosition = vecPosition; + UpdateSpatialData(); + } +} + +void CBuilding::GetRotation(CVector& vecRotation) +{ + vecRotation = m_vecRotation; +} + +void CBuilding::SetRotation(const CVector& vecRotation) +{ + m_vecRotation = vecRotation; +} + +bool CBuilding::SetLowLodBuilding(CBuilding* pNewLowLodBuilding) noexcept +{ + // Set or clear? + if (!pNewLowLodBuilding) + { + if (m_pLowLodBuilding) + { + m_pLowLodBuilding->SetHighLodObject(nullptr); + m_pLowLodBuilding = nullptr; + } + m_pLowLodBuilding = nullptr; + return true; + } + else + { + // Remove any previous link + SetLowLodBuilding(nullptr); + + // Make new link + m_pLowLodBuilding = pNewLowLodBuilding; + pNewLowLodBuilding->SetHighLodObject(this); + return true; + } +} + diff --git a/Server/mods/deathmatch/logic/CBuilding.h b/Server/mods/deathmatch/logic/CBuilding.h new file mode 100644 index 0000000000..5ffeb1af12 --- /dev/null +++ b/Server/mods/deathmatch/logic/CBuilding.h @@ -0,0 +1,72 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/CBuilding.h + * PURPOSE: Object entity class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ +#pragma once + +#include "CElement.h" +#include "CEvents.h" + +#include "CEasingCurve.h" +#include "TInterpolation.h" +#include "CPositionRotationAnimation.h" + +class CBuildingManager; + +#define SERVERSIDE_BUILDING_MIN_CLIENT_VERSION "1.6.0-9.22824" + +class CBuilding final : public CElement +{ + friend class CPlayer; + +public: + explicit CBuilding(CElement* pParent, class CBuildingManager* pObjectManager); + explicit CBuilding(const CBuilding& Copy); + ~CBuilding(); + CElement* Clone(bool* bAddEntity, CResource* pResource) override; + + bool IsEntity() override { return true; } + + void Unlink() override; + + const CVector& GetPosition() override; + void SetPosition(const CVector& vecPosition) override; + + void GetRotation(CVector& vecRotation) override; + void SetRotation(const CVector& vecRotation); + + void GetMatrix(CMatrix& matrix) override; + void SetMatrix(const CMatrix& matrix) override; + + std::uint16_t GetModel() const noexcept { return m_model; } + void SetModel(std::uint16_t model) noexcept { m_model = model; } + + bool GetCollisionEnabled() const noexcept { return m_bCollisionsEnabled; } + void SetCollisionEnabled(bool bCollisionEnabled) noexcept { m_bCollisionsEnabled = bCollisionEnabled; } + + bool SetLowLodBuilding(CBuilding* pLowLodBuilding) noexcept; + CBuilding* GetLowLodElement() const noexcept { return m_pLowLodBuilding; } + + CBuilding* GetHighLodBuilding() const { return m_pHighLodBuilding; } + void SetHighLodObject(CBuilding* pHighLodObject) { m_pHighLodBuilding = pHighLodObject; } + +protected: + bool ReadSpecialData(const int iLine) override; + +private: + CBuildingManager* m_pBuildingManager; + CVector m_vecRotation; + std::uint16_t m_model; + +protected: + bool m_bCollisionsEnabled; + + CBuilding* m_pLowLodBuilding; + CBuilding* m_pHighLodBuilding; +}; diff --git a/Server/mods/deathmatch/logic/CBuildingManager.cpp b/Server/mods/deathmatch/logic/CBuildingManager.cpp new file mode 100644 index 0000000000..33fbb9596b --- /dev/null +++ b/Server/mods/deathmatch/logic/CBuildingManager.cpp @@ -0,0 +1,74 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/CBuildingManager.cpp + * PURPOSE: Building entity manager class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CBuildingManager.h" +#include "Utils.h" + +constexpr float WORLD_DISTANCE_FROM_CENTER = 3000.0f; + +CBuildingManager::CBuildingManager() +{ +} + +CBuildingManager::~CBuildingManager() +{ + DeleteAll(); +} + +CBuilding* CBuildingManager::Create(CElement* pParent) +{ + CBuilding* pBuilding = new CBuilding(pParent, this); + + if (pBuilding->GetID() == INVALID_ELEMENT_ID) + { + delete pBuilding; + return nullptr; + } + + return pBuilding; +} + +CBuilding* CBuildingManager::CreateFromXML(CElement* pParent, CXMLNode& Node, CEvents* pEvents) +{ + CBuilding* pBuilding = new CBuilding(pParent, this); + + if (pBuilding->GetID() == INVALID_ELEMENT_ID || !pBuilding->LoadFromCustomData(pEvents, Node)) + { + delete pBuilding; + return nullptr; + } + + return pBuilding; +} + +void CBuildingManager::DeleteAll() +{ + // Delete all objects, make sure they dont remove themselves from our list (would make this damn slow) + DeletePointersAndClearList(m_List); +} + +bool CBuildingManager::IsValidModel(unsigned long ulObjectModel) +{ + // We are using the same list + return CObjectManager::IsValidModel(ulObjectModel); +} + +void CBuildingManager::RemoveFromList(CBuilding* pBuilding) +{ + m_List.remove(pBuilding); +} + +bool CBuildingManager::IsValidPosition(const CVector& pos) noexcept +{ + return (pos.fX >= -WORLD_DISTANCE_FROM_CENTER && pos.fX <= WORLD_DISTANCE_FROM_CENTER && pos.fY >= -WORLD_DISTANCE_FROM_CENTER && + pos.fY <= WORLD_DISTANCE_FROM_CENTER); +} diff --git a/Server/mods/deathmatch/logic/CBuildingManager.h b/Server/mods/deathmatch/logic/CBuildingManager.h new file mode 100644 index 0000000000..845a185778 --- /dev/null +++ b/Server/mods/deathmatch/logic/CBuildingManager.h @@ -0,0 +1,43 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/CBuildingManager.h + * PURPOSE: Building entity manager class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "CBuilding.h" +#include + +using std::list; +using CBuildingListType = CFastList; + +class CBuildingManager +{ + friend class CBuilding; + +public: + CBuildingManager(); + ~CBuildingManager(); + + CBuilding* Create(CElement* pParent); + CBuilding* CreateFromXML(CElement* pParent, CXMLNode& Node, CEvents* pEvents); + void DeleteAll(); + + CBuildingListType::const_iterator IterBegin() const noexcept { return m_List.begin(); } + CBuildingListType::const_iterator IterEnd() const noexcept { return m_List.end(); } + + static bool IsValidModel(unsigned long ulObjectModel); + static bool IsValidPosition(const CVector& pos) noexcept; + +private: + void AddToList(CBuilding* pBuilding) { m_List.push_back(pBuilding); }; + void RemoveFromList(CBuilding* pBuilding); + + CBuildingListType m_List; +}; diff --git a/Server/mods/deathmatch/logic/CElement.h b/Server/mods/deathmatch/logic/CElement.h index a036f2f9d1..4a18016f3d 100644 --- a/Server/mods/deathmatch/logic/CElement.h +++ b/Server/mods/deathmatch/logic/CElement.h @@ -82,6 +82,8 @@ class CElement TRAIN_TRACK, ROOT, UNKNOWN, + BUILDING, + _POINTLIGHTS, // client only }; public: diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 632b65d98b..11a72b1668 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -176,6 +176,7 @@ CGame::CGame() : m_FloodProtect(4, 30000, 30000) // Max of 4 connecti m_pVehicleManager = NULL; m_pPickupManager = NULL; m_pObjectManager = NULL; + m_pBuildingManager = nullptr; m_pColManager = NULL; m_pBlipManager = NULL; m_pClock = NULL; @@ -380,6 +381,7 @@ CGame::~CGame() SAFE_DELETE(m_pVehicleManager); SAFE_DELETE(m_pPickupManager); SAFE_DELETE(m_pObjectManager); + SAFE_DELETE(m_pBuildingManager); SAFE_DELETE(m_pColManager); SAFE_DELETE(m_pBlipManager); SAFE_DELETE(m_pClock); @@ -592,6 +594,7 @@ bool CGame::Start(int iArgumentCount, char* szArguments[]) m_pBlipManager = new CBlipManager; m_pColManager = new CColManager; m_pObjectManager = new CObjectManager; + m_pBuildingManager = new CBuildingManager(); m_pPickupManager = new CPickupManager(m_pColManager); m_pPlayerManager = new CPlayerManager; m_pRadarAreaManager = new CRadarAreaManager; diff --git a/Server/mods/deathmatch/logic/CGame.h b/Server/mods/deathmatch/logic/CGame.h index c4344a8180..543e7082c3 100644 --- a/Server/mods/deathmatch/logic/CGame.h +++ b/Server/mods/deathmatch/logic/CGame.h @@ -76,6 +76,7 @@ class CMainConfig; class CMapManager; class CMarkerManager; class CObjectManager; +class CBuildingManager; class CPacket; class CPacketTranslator; class CLatentTransferManager; @@ -220,6 +221,7 @@ class CGame CMapManager* GetMapManager() { return m_pMapManager; } CPlayerManager* GetPlayerManager() { return m_pPlayerManager; } CObjectManager* GetObjectManager() { return m_pObjectManager; } + CBuildingManager* GetBuildingManager() const noexcept { return m_pBuildingManager; } CVehicleManager* GetVehicleManager() { return m_pVehicleManager; } CTeamManager* GetTeamManager() { return m_pTeamManager; } CUnoccupiedVehicleSync* GetUnoccupiedVehicleSync() { return m_pUnoccupiedVehicleSync; } @@ -536,6 +538,7 @@ class CGame CGroups* m_pGroups; CColManager* m_pColManager; CObjectManager* m_pObjectManager; + CBuildingManager* m_pBuildingManager; CPickupManager* m_pPickupManager; CPlayerManager* m_pPlayerManager; CRadarAreaManager* m_pRadarAreaManager; diff --git a/Server/mods/deathmatch/logic/CResourceMapItem.cpp b/Server/mods/deathmatch/logic/CResourceMapItem.cpp index 653d192ac2..92523d2a2a 100644 --- a/Server/mods/deathmatch/logic/CResourceMapItem.cpp +++ b/Server/mods/deathmatch/logic/CResourceMapItem.cpp @@ -20,6 +20,7 @@ #include "CWaterManager.h" #include "CMarkerManager.h" #include "CVehicleManager.h" +#include "CBuildingManager.h" #include "CBlipManager.h" #include "Enums.h" #include "lua/CLuaFunctionParseHelpers.h" @@ -34,6 +35,7 @@ CResourceMapItem::CResourceMapItem(CResource* pResource, const char* szShortName m_pMarkerManager = g_pGame->GetMarkerManager(); m_pBlipManager = g_pGame->GetBlipManager(); m_pObjectManager = g_pGame->GetObjectManager(); + m_pBuildingManager = g_pGame->GetBuildingManager(); m_pPickupManager = g_pGame->GetPickupManager(); m_pPlayerManager = g_pGame->GetPlayerManager(); m_pRadarAreaManager = g_pGame->GetRadarAreaManager(); @@ -188,6 +190,20 @@ void CResourceMapItem::HandleNode(CXMLNode& Node, CElement* pParent) pNode = m_pWaterManager->CreateFromXML(pParent, Node, m_pEvents); break; } + case CElement::BUILDING: + { + const CMtaVersion& minClientVersion = m_resource->GetMinClientFromMetaXml(); + + if (minClientVersion < CMtaVersion(SERVERSIDE_BUILDING_MIN_CLIENT_VERSION)) + { + CLogger::LogPrintf("Resource %s should have client min_mta_version higher or equal than %s\n", m_resource->GetName().c_str(), + SERVERSIDE_BUILDING_MIN_CLIENT_VERSION); + break; + } + + pNode = m_pBuildingManager->CreateFromXML(pParent, Node, m_pEvents); + break; + } default: { pNode = m_pGroups->CreateFromXML(pParent, Node, m_pEvents); @@ -226,45 +242,25 @@ void CResourceMapItem::LinkupElements() } } - for (auto iter = m_pPlayerManager->IterBegin(); iter != m_pPlayerManager->IterEnd(); ++iter) + auto linkupElementsInManager = [pRootElement](auto* pManager) { - CPlayer* const pPlayer = *iter; - const char* szAttachToID = pPlayer->GetAttachToID(); - - if (szAttachToID[0]) + for (auto iter = pManager->IterBegin(); iter != pManager->IterEnd(); ++iter) { - CElement* const pElement = pRootElement->FindChild(szAttachToID, 0, true); - - if (pElement && !pElement->IsAttachedToElement(pPlayer)) - pPlayer->AttachTo(pElement); - } - } + auto* pIterElement = *iter; + const char* szAttachToID = pIterElement->GetAttachToID(); - for (auto iter = m_pObjectManager->IterBegin(); iter != m_pObjectManager->IterEnd(); ++iter) - { - CObject* const pObject = *iter; - const char* szAttachToID = pObject->GetAttachToID(); + if (szAttachToID[0]) + { + CElement* const pElement = pRootElement->FindChild(szAttachToID, 0, true); - if (szAttachToID[0]) - { - CElement* const pElement = pRootElement->FindChild(szAttachToID, 0, true); - - if (pElement && !pElement->IsAttachedToElement(pObject)) - pObject->AttachTo(pElement); + if (pElement && !pElement->IsAttachedToElement(pIterElement)) + pIterElement->AttachTo(pElement); + } } - } - - for (auto iter = m_pBlipManager->IterBegin(); iter != m_pBlipManager->IterEnd(); ++iter) - { - CBlip* const pBlip = *iter; - const char* szAttachToID = pBlip->GetAttachToID(); + }; - if (szAttachToID[0]) - { - CElement* const pElement = pRootElement->FindChild(szAttachToID, 0, true); - - if (pElement && !pElement->IsAttachedToElement(pBlip)) - pBlip->AttachTo(pElement); - } - } + linkupElementsInManager(m_pPlayerManager); + linkupElementsInManager(m_pObjectManager); + linkupElementsInManager(m_pBlipManager); + linkupElementsInManager(m_pBuildingManager); } diff --git a/Server/mods/deathmatch/logic/CResourceMapItem.h b/Server/mods/deathmatch/logic/CResourceMapItem.h index 823ca008d0..86591fbb4d 100644 --- a/Server/mods/deathmatch/logic/CResourceMapItem.h +++ b/Server/mods/deathmatch/logic/CResourceMapItem.h @@ -22,6 +22,7 @@ class CGroups; class CMarkerManager; class CBlipManager; class CObjectManager; +class CBuildingManager; class CPickupManager; class CPlayerManager; class CRadarAreaManager; @@ -58,6 +59,7 @@ class CResourceMapItem : public CResourceFile CMarkerManager* m_pMarkerManager; CBlipManager* m_pBlipManager; CObjectManager* m_pObjectManager; + CBuildingManager* m_pBuildingManager; CPickupManager* m_pPickupManager; CPlayerManager* m_pPlayerManager; CRadarAreaManager* m_pRadarAreaManager; diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 2079adf2d2..4b26251d5e 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -21,6 +21,7 @@ #include "CClock.h" #include "CBlip.h" #include "CWater.h" +#include "CBuilding.h" #include "CPlayerCamera.h" #include "CElementDeleter.h" #include "CMainConfig.h" @@ -775,6 +776,10 @@ bool CStaticFunctionDefinitions::GetElementCollisionsEnabled(CElement* pElement) CPed* pPed = static_cast(pElement); return pPed->GetCollisionEnabled(); } + case CElement::BUILDING: + { + return static_cast(pElement)->GetCollisionEnabled(); + } default: return false; } @@ -830,6 +835,16 @@ bool CStaticFunctionDefinitions::SetLowLodElement(CElement* pElement, CElement* return false; break; } + case CElement::BUILDING: + { + CBuilding* pBuilding = static_cast(pElement); + CBuilding* pLowLodObject = nullptr; + if (pLowLodElement && pLowLodElement->GetType() == CElement::BUILDING) + pLowLodObject = static_cast(pLowLodElement); + if (!pBuilding->SetLowLodBuilding(pLowLodObject)) + return false; + break; + } default: return false; } @@ -855,6 +870,11 @@ bool CStaticFunctionDefinitions::GetLowLodElement(CElement* pElement, CElement*& pOutLowLodElement = pObject->GetLowLodObject(); break; } + case CElement::BUILDING: + { + pOutLowLodElement = static_cast(pElement)->GetLowLodElement(); + break; + } default: return false; } @@ -874,6 +894,11 @@ bool CStaticFunctionDefinitions::IsElementLowLod(CElement* pElement, bool& bOutI bOutIsLowLod = pObject->IsLowLod(); break; } + case CElement::BUILDING: + { + bOutIsLowLod = static_cast(pElement)->GetHighLodBuilding() ? true : false; + break; + } default: return false; } @@ -1939,6 +1964,11 @@ bool CStaticFunctionDefinitions::SetElementCollisionsEnabled(CElement* pElement, pPed->SetCollisionEnabled(bEnable); break; } + case CElement::BUILDING: + { + static_cast(pElement)->SetCollisionEnabled(bEnable); + break; + } default: return false; } diff --git a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 27afc8510e..929a5e1091 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -271,6 +271,7 @@ ADD_ENUM(CElement::WATER, "water") ADD_ENUM(CElement::DATABASE_CONNECTION, "db-connection") ADD_ENUM(CElement::ROOT, "root") ADD_ENUM(CElement::UNKNOWN, "unknown") +ADD_ENUM(CElement::BUILDING, "building") IMPLEMENT_ENUM_END_DEFAULTS("element-type", CElement::UNKNOWN, "unknown") IMPLEMENT_ENUM_BEGIN(CAccountPassword::EAccountPasswordType) diff --git a/Server/mods/deathmatch/logic/lua/CLuaMain.cpp b/Server/mods/deathmatch/logic/lua/CLuaMain.cpp index 189d204c57..2346c8986d 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaMain.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaMain.cpp @@ -20,6 +20,7 @@ #include "luadefs/CLuaACLDefs.h" #include "luadefs/CLuaBanDefs.h" #include "luadefs/CLuaBlipDefs.h" +#include "luadefs/CLuaBuildingDefs.h" #include "luadefs/CLuaColShapeDefs.h" #include "luadefs/CLuaDatabaseDefs.h" #include "luadefs/CLuaMarkerDefs.h" @@ -176,6 +177,7 @@ void CLuaMain::InitClasses(lua_State* luaVM) CLuaACLDefs ::AddClass(luaVM); CLuaBanDefs ::AddClass(luaVM); CLuaBlipDefs ::AddClass(luaVM); + CLuaBuildingDefs ::AddClass(luaVM); CLuaColShapeDefs ::AddClass(luaVM); CLuaDatabaseDefs ::AddClass(luaVM); CLuaMarkerDefs ::AddClass(luaVM); diff --git a/Server/mods/deathmatch/logic/lua/CLuaManager.cpp b/Server/mods/deathmatch/logic/lua/CLuaManager.cpp index 52d25e8b8f..90362c02fd 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaManager.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaManager.cpp @@ -22,6 +22,7 @@ #include "luadefs/CLuaACLDefs.h" #include "luadefs/CLuaBanDefs.h" #include "luadefs/CLuaBlipDefs.h" +#include "luadefs/CLuaBuildingDefs.h" #include "luadefs/CLuaColShapeDefs.h" #include "luadefs/CLuaDatabaseDefs.h" #include "luadefs/CLuaMarkerDefs.h" @@ -191,6 +192,7 @@ void CLuaManager::LoadCFunctions() CLuaAccountDefs::LoadFunctions(); CLuaBanDefs::LoadFunctions(); CLuaBlipDefs::LoadFunctions(); + CLuaBuildingDefs::LoadFunctions(); CLuaCameraDefs::LoadFunctions(); CLuaColShapeDefs::LoadFunctions(); CLuaDatabaseDefs::LoadFunctions(); diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp new file mode 100644 index 0000000000..a9f907e41d --- /dev/null +++ b/Server/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp @@ -0,0 +1,81 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.x + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/luadefs/CLuaObjectDefs.cpp + * PURPOSE: Lua function definitions class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CLuaBuildingDefs.h" +#include "Utils.h" +#include "CDummy.h" +#include + +void CLuaBuildingDefs::LoadFunctions() +{ + constexpr static const std::pair functions[]{ + {"createBuilding", ArgumentParser}, + }; + + // Add functions + for (const auto& [name, func] : functions) + CLuaCFunctions::AddFunction(name, func); +} + +void CLuaBuildingDefs::AddClass(lua_State* luaVM) +{ + lua_newclass(luaVM); + + lua_classfunction(luaVM, "create", "createBuilding"); + + lua_registerclass(luaVM, "Building", "Element"); +} + +CBuilding* CLuaBuildingDefs::CreateBuilding(lua_State* const luaVM, std::uint16_t modelId, CVector pos, std::optional rot, + std::optional interior) +{ + CLuaMain* pLuaMain = m_pLuaManager->GetVirtualMachine(luaVM); + + // Get the resource we belong to + CResource* pResource = pLuaMain->GetResource(); + if (!pResource) + throw std::logic_error("Cannot be done in current environment"); + + const CMtaVersion& minClientVersion = pResource->GetMinClientFromMetaXml(); + + if (minClientVersion < CMtaVersion(SERVERSIDE_BUILDING_MIN_CLIENT_VERSION)) + throw std::logic_error("Expected client min_mta_version in meta.xml hinger or equal than " SERVERSIDE_BUILDING_MIN_CLIENT_VERSION); + + if (!CBuildingManager::IsValidModel(modelId)) + throw std::invalid_argument("Invalid building model id"); + + if (!CBuildingManager::IsValidPosition(pos)) + throw std::invalid_argument("Position is outside of game world"); + + if (rot.has_value()) + ConvertDegreesToRadians(rot.value()); + else + rot.emplace(CVector(0, 0, 0)); + + CBuilding* pBuilding = m_pBuildingManager->Create(pResource->GetDynamicElementRoot()); + + if (!pBuilding) + return nullptr; + + pBuilding->SetPosition(pos); + pBuilding->SetRotation(rot.value()); + pBuilding->SetModel(modelId); + + if (pResource->IsClientSynced()) + { + CEntityAddPacket Packet; + Packet.Add(pBuilding); + m_pPlayerManager->BroadcastOnlyJoined(Packet); + } + + return pBuilding; +} diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.h b/Server/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.h new file mode 100644 index 0000000000..e85353dec2 --- /dev/null +++ b/Server/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.h @@ -0,0 +1,23 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/luadefs/CLuaObjectDefs.h + * PURPOSE: Lua function definitions class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once +#include "CLuaDefs.h" + +class CLuaBuildingDefs : public CLuaDefs +{ +public: + static void LoadFunctions(); + static void AddClass(lua_State* luaVM); + + static CBuilding* CreateBuilding(lua_State* const luaVM, std::uint16_t modelId, CVector pos, std::optional rot, + std::optional interior); +}; diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaClassDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaClassDefs.cpp index b617381c57..de202c4c0b 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaClassDefs.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaClassDefs.cpp @@ -323,6 +323,8 @@ const char* CLuaClassDefs::GetElementClass(CElement* pElement) return "Blip"; case CElement::OBJECT: return "Object"; + case CElement::BUILDING: + return "Building"; case CElement::PICKUP: return "Pickup"; case CElement::RADAR_AREA: diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaDefs.cpp index 6afc751f67..7edf48cabb 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaDefs.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaDefs.cpp @@ -38,25 +38,26 @@ namespace bool ms_bRegisterdPostCallHook = false; } // namespace -CElementDeleter* CLuaDefs::m_pElementDeleter = NULL; -CBlipManager* CLuaDefs::m_pBlipManager = NULL; -CHandlingManager* CLuaDefs::m_pHandlingManager = nullptr; -CLuaManager* CLuaDefs::m_pLuaManager = NULL; -CMarkerManager* CLuaDefs::m_pMarkerManager = NULL; -CObjectManager* CLuaDefs::m_pObjectManager = NULL; -CPickupManager* CLuaDefs::m_pPickupManager = NULL; -CPlayerManager* CLuaDefs::m_pPlayerManager = NULL; -CRadarAreaManager* CLuaDefs::m_pRadarAreaManager = NULL; -CRegisteredCommands* CLuaDefs::m_pRegisteredCommands; -CElement* CLuaDefs::m_pRootElement = NULL; -CScriptDebugging* CLuaDefs::m_pScriptDebugging = NULL; -CVehicleManager* CLuaDefs::m_pVehicleManager = NULL; -CTeamManager* CLuaDefs::m_pTeamManager = NULL; -CAccountManager* CLuaDefs::m_pAccountManager = NULL; -CColManager* CLuaDefs::m_pColManager = NULL; -CResourceManager* CLuaDefs::m_pResourceManager = NULL; -CAccessControlListManager* CLuaDefs::m_pACLManager = NULL; -CMainConfig* CLuaDefs::m_pMainConfig = NULL; +CElementDeleter* CLuaDefs::m_pElementDeleter = NULL; +CBlipManager* CLuaDefs::m_pBlipManager = NULL; +CHandlingManager* CLuaDefs::m_pHandlingManager = NULL; +CLuaManager* CLuaDefs::m_pLuaManager = NULL; +CMarkerManager* CLuaDefs::m_pMarkerManager = NULL; +CObjectManager* CLuaDefs::m_pObjectManager = NULL; +CBuildingManager* CLuaDefs::m_pBuildingManager = nullptr; +CPickupManager* CLuaDefs::m_pPickupManager = NULL; +CPlayerManager* CLuaDefs::m_pPlayerManager = NULL; +CRadarAreaManager* CLuaDefs::m_pRadarAreaManager = NULL; +CRegisteredCommands* CLuaDefs::m_pRegisteredCommands; +CElement* CLuaDefs::m_pRootElement = NULL; +CScriptDebugging* CLuaDefs::m_pScriptDebugging = NULL; +CVehicleManager* CLuaDefs::m_pVehicleManager = NULL; +CTeamManager* CLuaDefs::m_pTeamManager = NULL; +CAccountManager* CLuaDefs::m_pAccountManager = NULL; +CColManager* CLuaDefs::m_pColManager = NULL; +CResourceManager* CLuaDefs::m_pResourceManager = NULL; +CAccessControlListManager* CLuaDefs::m_pACLManager = NULL; +CMainConfig* CLuaDefs::m_pMainConfig = NULL; void CLuaDefs::Initialize(CGame* pGame) { @@ -67,6 +68,7 @@ void CLuaDefs::Initialize(CGame* pGame) m_pLuaManager = pGame->GetLuaManager(); m_pMarkerManager = pGame->GetMarkerManager(); m_pObjectManager = pGame->GetObjectManager(); + m_pBuildingManager = pGame->GetBuildingManager(); m_pPickupManager = pGame->GetPickupManager(); m_pPlayerManager = pGame->GetPlayerManager(); m_pRadarAreaManager = pGame->GetRadarAreaManager(); diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaDefs.h b/Server/mods/deathmatch/logic/luadefs/CLuaDefs.h index 2bde93fb0e..e74b057560 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaDefs.h +++ b/Server/mods/deathmatch/logic/luadefs/CLuaDefs.h @@ -21,6 +21,7 @@ #include "../CMainConfig.h" #include "../CMarkerManager.h" #include "../CObjectManager.h" +#include "../CBuildingManager.h" #include "../CPickupManager.h" #include "../CPlayerManager.h" #include "../CRadarAreaManager.h" @@ -66,26 +67,27 @@ class CLuaDefs // This is just for the Lua funcs. Please don't public this and use it other // places in the server. protected: - static CElementDeleter* m_pElementDeleter; - static CBlipManager* m_pBlipManager; - static CHandlingManager* m_pHandlingManager; - static CLuaManager* m_pLuaManager; - static CMarkerManager* m_pMarkerManager; - static CObjectManager* m_pObjectManager; - static CPickupManager* m_pPickupManager; - static CPlayerManager* m_pPlayerManager; - static CRadarAreaManager* m_pRadarAreaManager; - static CRegisteredCommands* m_pRegisteredCommands; - static CElement* m_pRootElement; - static CScriptDebugging* m_pScriptDebugging; - static CVehicleManager* m_pVehicleManager; - static CTeamManager* m_pTeamManager; - static CAccountManager* m_pAccountManager; - static CColManager* m_pColManager; - static CResourceManager* m_pResourceManager; - static CAccessControlListManager* m_pACLManager; - static CMainConfig* m_pMainConfig; - static inline CLuaModuleManager* m_pLuaModuleManager = nullptr; + static CElementDeleter* m_pElementDeleter; + static CBlipManager* m_pBlipManager; + static CHandlingManager* m_pHandlingManager; + static CLuaManager* m_pLuaManager; + static CMarkerManager* m_pMarkerManager; + static CObjectManager* m_pObjectManager; + static CBuildingManager* m_pBuildingManager; + static CPickupManager* m_pPickupManager; + static CPlayerManager* m_pPlayerManager; + static CRadarAreaManager* m_pRadarAreaManager; + static CRegisteredCommands* m_pRegisteredCommands; + static CElement* m_pRootElement; + static CScriptDebugging* m_pScriptDebugging; + static CVehicleManager* m_pVehicleManager; + static CTeamManager* m_pTeamManager; + static CAccountManager* m_pAccountManager; + static CColManager* m_pColManager; + static CResourceManager* m_pResourceManager; + static CAccessControlListManager* m_pACLManager; + static CMainConfig* m_pMainConfig; + static inline CLuaModuleManager* m_pLuaModuleManager = nullptr; protected: // Old style: Only warn on failure. This should diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index 5521512417..c34a0ebc6f 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -18,6 +18,7 @@ #include "CColRectangle.h" #include "CColTube.h" #include "CDummy.h" +#include "CBuilding.h" #include "CPickup.h" #include "CMarker.h" #include "CBlip.h" @@ -153,6 +154,12 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const bCollisionsEnabled = pObject->GetCollisionEnabled(); break; } + case CElement::BUILDING: + { + CBuilding* pBuilding = static_cast(pElement); + bCollisionsEnabled = pBuilding->GetCollisionEnabled(); + break; + } case CElement::PED: case CElement::PLAYER: { @@ -1145,6 +1152,37 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const break; } + case CElement::BUILDING: + { + if (!BitStream.Can(eBitStreamVersion::ServersideBuildingElement)) + { + CLogger::LogPrintf("not sending this element - id: %i\n", pElement->GetType()); + break; + } + + CBuilding* pBuilding = static_cast(pElement); + + // Position + position.data.vecPosition = pBuilding->GetPosition(); + BitStream.Write(&position); + + // Rotation + SRotationRadiansSync rotationRadians(false); + pBuilding->GetRotation(rotationRadians.data.vecRotation); + BitStream.Write(&rotationRadians); + + // Model id + BitStream.WriteCompressed(pBuilding->GetModel()); + + // Interior + BitStream.WriteCompressed(pBuilding->GetInterior()); + + CBuilding* pLowLodBuilding = pBuilding->GetLowLodElement(); + ElementID lowLodBuildingID = pLowLodBuilding ? pLowLodBuilding->GetID() : INVALID_ELEMENT_ID; + BitStream.Write(lowLodBuildingID); + break; + } + default: { assert(0); diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index f0a61b6666..458cc785a9 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -612,6 +612,10 @@ enum class eBitStreamVersion : unsigned short // 2025-01-29 PedSync_CameraRotation, + // Add serverside building support + // 2025-02-? + ServersideBuildingElement, + // This allows us to automatically increment the BitStreamVersion when things are added to this enum. // Make sure you only add things above this comment. Next,