diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 25f440fab5..f1b322e240 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -2636,6 +2636,8 @@ void CClientGame::AddBuiltInEvents() m_Events.AddEvent("onClientElementModelChange", "oldModel, newModel", nullptr, false); m_Events.AddEvent("onClientElementDimensionChange", "oldDimension, newDimension", nullptr, false); m_Events.AddEvent("onClientElementInteriorChange", "oldInterior, newInterior", nullptr, false); + m_Events.AddEvent("onClientElementAttach", "attachSource, attachOffsetX, attachOffsetY, attachOffsetZ, attachOffsetRX, attachOffsetRY, attachOffsetRZ", nullptr, false); + m_Events.AddEvent("onClientElementDetach", "detachSource, detachWorldX, detachWorldY, detachWorldZ, detachWorldRX, detachWorldRY, detachWorldRZ", nullptr, false); // Player events m_Events.AddEvent("onClientPlayerJoin", "", NULL, false); diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 857aa91047..3d7e2d8aec 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1371,18 +1371,30 @@ bool CStaticFunctionDefinitions::AttachElements(CClientEntity& Entity, CClientEn RUN_CHILDREN(AttachElements(**iter, AttachedToEntity, vecPosition, vecRotation)) // Can these elements be attached? - if (Entity.IsAttachToable() && AttachedToEntity.IsAttachable() && !AttachedToEntity.IsAttachedToElement(&Entity) && - Entity.GetDimension() == AttachedToEntity.GetDimension()) + if (!Entity.IsAttachToable() || !AttachedToEntity.IsAttachable() || AttachedToEntity.IsAttachedToElement(&Entity) || + Entity.GetDimension() != AttachedToEntity.GetDimension()) { - ConvertDegreesToRadians(vecRotation); + return false; + } - Entity.SetAttachedOffsets(vecPosition, vecRotation); - Entity.AttachTo(&AttachedToEntity); + CLuaArguments Arguments; + Arguments.PushElement(&AttachedToEntity); + Arguments.PushNumber(vecPosition.fX); + Arguments.PushNumber(vecPosition.fY); + Arguments.PushNumber(vecPosition.fZ); + Arguments.PushNumber(vecRotation.fX); + Arguments.PushNumber(vecRotation.fY); + Arguments.PushNumber(vecRotation.fZ); + + if (!Entity.CallEvent("onClientElementAttach", Arguments, true)) + return false; - return true; - } + ConvertDegreesToRadians(vecRotation); - return false; + Entity.SetAttachedOffsets(vecPosition, vecRotation); + Entity.AttachTo(&AttachedToEntity); + + return true; } bool CStaticFunctionDefinitions::DetachElements(CClientEntity& Entity, CClientEntity* pAttachedToEntity) @@ -1390,16 +1402,33 @@ bool CStaticFunctionDefinitions::DetachElements(CClientEntity& Entity, CClientEn RUN_CHILDREN(DetachElements(**iter, pAttachedToEntity)) CClientEntity* pActualAttachedToEntity = Entity.GetAttachedTo(); - if (pActualAttachedToEntity) + if (!pActualAttachedToEntity || (pAttachedToEntity && pActualAttachedToEntity != pAttachedToEntity)) { - if (pAttachedToEntity == NULL || pActualAttachedToEntity == pAttachedToEntity) - { - Entity.AttachTo(NULL); - return true; - } + return false; } - return false; + CVector vecPosition; + CVector vecRotation; + + Entity.GetPosition(vecPosition); + Entity.GetRotationDegrees(vecRotation); + + CLuaArguments Arguments; + Arguments.PushElement(pActualAttachedToEntity); + Arguments.PushNumber(vecPosition.fX); + Arguments.PushNumber(vecPosition.fY); + Arguments.PushNumber(vecPosition.fZ); + Arguments.PushNumber(vecRotation.fX); + Arguments.PushNumber(vecRotation.fY); + Arguments.PushNumber(vecRotation.fZ); + + if (!Entity.CallEvent("onClientElementDetach", Arguments, true)) + { + return false; + } + + Entity.AttachTo(NULL); + return true; } bool CStaticFunctionDefinitions::SetElementAttachedOffsets(CClientEntity& Entity, CVector& vecPosition, CVector& vecRotation) diff --git a/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp index dfd790cdd0..9c9a5b466f 100644 --- a/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp @@ -313,32 +313,100 @@ void CElementRPCs::SetElementDimension(CClientEntity* pSource, NetBitStreamInter void CElementRPCs::AttachElements(CClientEntity* pSource, NetBitStreamInterface& bitStream) { ElementID usAttachedToID; - CVector vecPosition, vecRotation; - if (bitStream.Read(usAttachedToID) && bitStream.Read(vecPosition.fX) && bitStream.Read(vecPosition.fY) && bitStream.Read(vecPosition.fZ) && - bitStream.Read(vecRotation.fX) && bitStream.Read(vecRotation.fY) && bitStream.Read(vecRotation.fZ)) + + CVector vecPosition; + CVector vecRotation; + + if (!(bitStream.Read(usAttachedToID) && bitStream.Read(vecPosition.fX) && bitStream.Read(vecPosition.fY) && bitStream.Read(vecPosition.fZ) && + bitStream.Read(vecRotation.fX) && bitStream.Read(vecRotation.fY) && bitStream.Read(vecRotation.fZ))) { - CClientEntity* pAttachedToEntity = CElementIDs::GetElement(usAttachedToID); - if (pAttachedToEntity) - { - pSource->SetAttachedOffsets(vecPosition, vecRotation); - pSource->AttachTo(pAttachedToEntity); - } + return; } + + CClientEntity* pAttachedToEntity = CElementIDs::GetElement(usAttachedToID); + if (!pAttachedToEntity) + { + return; + } + + ConvertRadiansToDegrees(vecRotation); + + CLuaArguments Arguments; + Arguments.PushElement(pAttachedToEntity); + Arguments.PushNumber(vecPosition.fX); + Arguments.PushNumber(vecPosition.fY); + Arguments.PushNumber(vecPosition.fZ); + Arguments.PushNumber(vecRotation.fX); + Arguments.PushNumber(vecRotation.fY); + Arguments.PushNumber(vecRotation.fZ); + + if (!pSource->CallEvent("onClientElementAttach", Arguments, true)) + { + return; + } + + ConvertDegreesToRadians(vecRotation); + + pSource->SetAttachedOffsets(vecPosition, vecRotation); + pSource->AttachTo(pAttachedToEntity); } void CElementRPCs::DetachElements(CClientEntity* pSource, NetBitStreamInterface& bitStream) { unsigned char ucTimeContext; - if (bitStream.Read(ucTimeContext)) + if (!bitStream.Read(ucTimeContext)) { - pSource->SetSyncTimeContext(ucTimeContext); - pSource->AttachTo(NULL); + return; + } - CVector vecPosition; - if (bitStream.Read(vecPosition.fX) && bitStream.Read(vecPosition.fY) && bitStream.Read(vecPosition.fZ)) - { - pSource->SetPosition(vecPosition); - } + ElementID usAttachedToID; + CClientEntity* pAttachedToEntity = CElementIDs::GetElement(usAttachedToID); + + CVector vecPosition; + CVector vecRotation; + + bitStream.Read(vecPosition.fX); + bitStream.Read(vecPosition.fY); + bitStream.Read(vecPosition.fZ); + + if (g_pNet->CanServerBitStream((eBitStreamVersion::DetachElementsRotation))) + { + bitStream.Read(vecRotation.fX); + bitStream.Read(vecRotation.fY); + bitStream.Read(vecRotation.fZ); + } + else + { + vecRotation.fX = 0; + vecRotation.fY = 0; + vecRotation.fZ = 0; + } + + CLuaArguments Arguments; + Arguments.PushElement(pAttachedToEntity); + Arguments.PushNumber(vecPosition.fX); + Arguments.PushNumber(vecPosition.fY); + Arguments.PushNumber(vecPosition.fZ); + Arguments.PushNumber(vecRotation.fX); + Arguments.PushNumber(vecRotation.fY); + Arguments.PushNumber(vecRotation.fZ); + + if (!pSource->CallEvent("onClientElementDetach", Arguments, true)) + { + return; + } + + pSource->SetSyncTimeContext(ucTimeContext); + pSource->AttachTo(NULL); + + if (vecPosition.fX != 0.0f || vecPosition.fY != 0.0f || vecPosition.fZ != 0.0f) + { + pSource->SetPosition(vecPosition); + } + + if (vecRotation.fX != 0.0f || vecRotation.fY != 0.0f || vecRotation.fZ != 0.0f) + { + pSource->SetRotationDegrees(vecRotation); } } diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 16e7b7312b..3776d15e8e 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -1667,6 +1667,8 @@ void CGame::AddBuiltInEvents() m_Events.AddEvent("onElementModelChange", "oldModel, newModel", NULL, false); m_Events.AddEvent("onElementDimensionChange", "oldDimension, newDimension", nullptr, false); m_Events.AddEvent("onElementInteriorChange", "oldInterior, newInterior", nullptr, false); + m_Events.AddEvent("onElementAttach", "attachSource, attachOffsetX, attachOffsetY, attachOffsetZ, attachOffsetRX, attachOffsetRY, attachOffsetRZ", nullptr, false); + m_Events.AddEvent("onElementDetach", "detachSource, detachWorldX, detachWorldY, detachWorldZ, detachWorldRX, detachWorldRY, detachWorldRZ", nullptr, false); // Radar area events diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index f8bfad986a..91431958a4 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1598,27 +1598,42 @@ bool CStaticFunctionDefinitions::AttachElements(CElement* pElement, CElement* pA assert(pElement); assert(pAttachedToElement); - if (pElement->IsAttachToable() && pAttachedToElement->IsAttachable() && !pAttachedToElement->IsAttachedToElement(pElement) && - pElement->GetDimension() == pAttachedToElement->GetDimension()) + if (!pElement->IsAttachToable() || !pAttachedToElement->IsAttachable() || pAttachedToElement->IsAttachedToElement(pElement) || + pElement->GetDimension() != pAttachedToElement->GetDimension()) { - pElement->SetAttachedOffsets(vecPosition, vecRotation); - ConvertDegreesToRadians(vecRotation); - pElement->AttachTo(pAttachedToElement); + return false; + } - CBitStream BitStream; - BitStream.pBitStream->Write(pAttachedToElement->GetID()); - BitStream.pBitStream->Write(vecPosition.fX); - BitStream.pBitStream->Write(vecPosition.fY); - BitStream.pBitStream->Write(vecPosition.fZ); - BitStream.pBitStream->Write(vecRotation.fX); - BitStream.pBitStream->Write(vecRotation.fY); - BitStream.pBitStream->Write(vecRotation.fZ); - m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pElement, ATTACH_ELEMENTS, *BitStream.pBitStream)); + CLuaArguments Arguments; + Arguments.PushElement(pAttachedToElement); + Arguments.PushNumber(vecPosition.fX); + Arguments.PushNumber(vecPosition.fY); + Arguments.PushNumber(vecPosition.fZ); + Arguments.PushNumber(vecRotation.fX); + Arguments.PushNumber(vecRotation.fY); + Arguments.PushNumber(vecRotation.fZ); - return true; + if (!pElement->CallEvent("onElementAttach", Arguments)) + { + return false; } - return false; + ConvertDegreesToRadians(vecRotation); + + pElement->SetAttachedOffsets(vecPosition, vecRotation); + pElement->AttachTo(pAttachedToElement); + + CBitStream BitStream; + BitStream.pBitStream->Write(pAttachedToElement->GetID()); + BitStream.pBitStream->Write(vecPosition.fX); + BitStream.pBitStream->Write(vecPosition.fY); + BitStream.pBitStream->Write(vecPosition.fZ); + BitStream.pBitStream->Write(vecRotation.fX); + BitStream.pBitStream->Write(vecRotation.fY); + BitStream.pBitStream->Write(vecRotation.fZ); + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pElement, ATTACH_ELEMENTS, *BitStream.pBitStream)); + + return true; } bool CStaticFunctionDefinitions::DetachElements(CElement* pElement, CElement* pAttachedToElement) @@ -1626,28 +1641,47 @@ bool CStaticFunctionDefinitions::DetachElements(CElement* pElement, CElement* pA assert(pElement); CElement* pActualAttachedToElement = pElement->GetAttachedToElement(); - if (pActualAttachedToElement) + if (!pActualAttachedToElement || (pAttachedToElement && pActualAttachedToElement != pAttachedToElement)) { - if (pAttachedToElement == NULL || pActualAttachedToElement == pAttachedToElement) - { - // Detach it. Also generate a new time context to prevent sync screwup from - // old packes arriving. - CVector vecPosition = pElement->GetPosition(); - pElement->AttachTo(NULL); - pElement->GenerateSyncTimeContext(); + return false; + } - CBitStream BitStream; - BitStream.pBitStream->Write(pElement->GetSyncTimeContext()); - BitStream.pBitStream->Write(vecPosition.fX); - BitStream.pBitStream->Write(vecPosition.fY); - BitStream.pBitStream->Write(vecPosition.fZ); - m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pElement, DETACH_ELEMENTS, *BitStream.pBitStream)); + CVector vecPosition = pElement->GetPosition(); + CVector vecRotation; - return true; - } + pElement->GetRotation(vecRotation); + ConvertRadiansToDegrees(vecRotation); + + CLuaArguments Arguments; + Arguments.PushElement(pActualAttachedToElement); + Arguments.PushNumber(vecPosition.fX); + Arguments.PushNumber(vecPosition.fY); + Arguments.PushNumber(vecPosition.fZ); + Arguments.PushNumber(vecRotation.fX); + Arguments.PushNumber(vecRotation.fY); + Arguments.PushNumber(vecRotation.fZ); + + if (!pElement->CallEvent("onElementDetach", Arguments)) + { + return false; } - return false; + // Detach it. Also generate a new time context to prevent sync screwup from + // old packets arriving. + pElement->AttachTo(NULL); + pElement->GenerateSyncTimeContext(); + + CBitStream BitStream; + BitStream.pBitStream->Write(pElement->GetSyncTimeContext()); + BitStream.pBitStream->Write(vecPosition.fX); + BitStream.pBitStream->Write(vecPosition.fY); + BitStream.pBitStream->Write(vecPosition.fZ); + BitStream.pBitStream->Write(vecRotation.fX); + BitStream.pBitStream->Write(vecRotation.fY); + BitStream.pBitStream->Write(vecRotation.fZ); + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pElement, DETACH_ELEMENTS, *BitStream.pBitStream)); + + return true; } bool CStaticFunctionDefinitions::SetElementAlpha(CElement* pElement, unsigned char ucAlpha) diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index 160077ec35..c8eec71717 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -540,6 +540,10 @@ enum class eBitStreamVersion : unsigned short // 2023-10-12 CPlayerJoinCompletePacket_ServerName, + // Send rotation on detachElements + // 2024-04-12 + DetachElementsRotation, + // Add "roadsignstext" to setWorldSpecialPropertyEnabled // 2024-05-17 WorldSpecialProperty_RoadSignsText,