diff --git a/CMakeLists.txt b/CMakeLists.txt index b147b200..33efbdc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,6 +143,7 @@ set(INCLUDED_DIRECTORIES "dGame/dInventory" "dGame/dMission" "dGame/dEntity" + "dGame/dPropertyBehaviors" "dGame/dUtilities" "dPhysics" "dNavigation" diff --git a/dGame/CMakeLists.txt b/dGame/CMakeLists.txt index 6b31802e..eb02eef2 100644 --- a/dGame/CMakeLists.txt +++ b/dGame/CMakeLists.txt @@ -44,6 +44,13 @@ foreach(file ${DGAME_DMISSION_SOURCES}) set(DGAME_SOURCES ${DGAME_SOURCES} "dMission/${file}") endforeach() +add_subdirectory(dPropertyBehaviors) + +foreach(file ${DGAME_DPROPERTYBEHAVIORS_SOURCES}) + set(DGAME_SOURCES ${DGAME_SOURCES} "dPropertyBehaviors/${file}") +endforeach() + + add_subdirectory(dUtilities) foreach(file ${DGAME_DUTILITIES_SOURCES}) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 5ad8e207..18d7e0f6 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -68,6 +68,8 @@ #include "PropertyVendorComponent.h" #include "PropertySelectQueryProperty.h" #include "TradingManager.h" +#include "ControlBehaviors.h" +#include "AMFDeserialize.h" void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) { CBITSTREAM; @@ -2396,8 +2398,24 @@ void GameMessages::SendUnSmash(Entity* entity, LWOOBJID builderID, float duratio } void GameMessages::HandleControlBehaviors(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { - // TODO - Game::logger->Log("GameMessages", "Recieved Control Behavior GameMessage, but property behaviors are unimplemented."); + AMFDeserialize reader; + std::unique_ptr amfArguments(reader.Read(inStream)); + if (amfArguments->GetValueType() != AMFValueType::AMFArray) return; + + uint32_t commandLength{}; + inStream->Read(commandLength); + + std::string command; + for (uint32_t i = 0; i < commandLength; i++) { + unsigned char character; + inStream->Read(character); + command.push_back(character); + } + + auto owner = PropertyManagementComponent::Instance()->GetOwner(); + if (!owner) return; + + ControlBehaviors::ProcessCommand(entity, sysAddr, static_cast(amfArguments.get()), command, owner); } void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { diff --git a/dGame/dPropertyBehaviors/CMakeLists.txt b/dGame/dPropertyBehaviors/CMakeLists.txt new file mode 100644 index 00000000..4f5d60aa --- /dev/null +++ b/dGame/dPropertyBehaviors/CMakeLists.txt @@ -0,0 +1,4 @@ +set(DGAME_DPROPERTYBEHAVIORS_SOURCES + "ControlBehaviors.cpp" + PARENT_SCOPE +) diff --git a/dGame/dPropertyBehaviors/ControlBehaviors.cpp b/dGame/dPropertyBehaviors/ControlBehaviors.cpp new file mode 100644 index 00000000..4e922ee0 --- /dev/null +++ b/dGame/dPropertyBehaviors/ControlBehaviors.cpp @@ -0,0 +1,81 @@ +#include "ControlBehaviors.h" + +#include "AMFFormat.h" +#include "Entity.h" +#include "Game.h" +#include "GameMessages.h" +#include "ModelComponent.h" +#include "dLogger.h" + +void ControlBehaviors::ProcessCommand(Entity* modelEntity, const SystemAddress& sysAddr, AMFArrayValue* arguments, std::string command, Entity* modelOwner) { + if (!modelEntity || !modelOwner || !arguments) return; + + if (command == "sendBehaviorListToClient") + SendBehaviorListToClient(modelEntity, sysAddr, modelOwner); + else if (command == "modelTypeChanged") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "toggleExecutionUpdates") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "addStrip") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "removeStrip") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "mergeStrips") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "splitStrip") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "updateStripUI") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "addAction") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "migrateActions") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "rearrangeStrip") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "add") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "removeActions") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "rename") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "sendBehaviorBlocksToClient") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "moveToInventory") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else if (command == "updateAction") + Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str()); + else + Game::logger->Log("ControlBehaviors", "Unknown behavior command (%s)\n", command.c_str()); +} + +void ControlBehaviors::SendBehaviorListToClient( + Entity* modelEntity, + const SystemAddress& sysAddr, + Entity* modelOwner + ) { + auto* modelComponent = modelEntity->GetComponent(); + + if (!modelComponent) return; + + AMFArrayValue behaviorsToSerialize; + + AMFArrayValue* behaviors = new AMFArrayValue(); // Empty for now + + /** + * The behaviors AMFArray will have up to 5 elements in the dense portion. + * Each element in the dense portion will be made up of another AMFArray + * with the following information mapped in the associative portion + * "id": Behavior ID cast to an AMFString + * "isLocked": AMFTrue or AMFFalse of whether or not the behavior is locked + * "isLoot": AMFTrue or AMFFalse of whether or not the behavior is a custom behavior (true if custom) + * "name": The name of the behavior formatted as an AMFString + */ + + behaviorsToSerialize.InsertValue("behaviors", behaviors); + + AMFStringValue* amfStringValueForObjectID = new AMFStringValue(); + amfStringValueForObjectID->SetStringValue(std::to_string(modelComponent->GetParent()->GetObjectID())); + + behaviorsToSerialize.InsertValue("objectID", amfStringValueForObjectID); + GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorList", &behaviorsToSerialize); +} diff --git a/dGame/dPropertyBehaviors/ControlBehaviors.h b/dGame/dPropertyBehaviors/ControlBehaviors.h new file mode 100644 index 00000000..7c24da68 --- /dev/null +++ b/dGame/dPropertyBehaviors/ControlBehaviors.h @@ -0,0 +1,35 @@ +#pragma once + +#ifndef __CONTROLBEHAVIORS__H__ +#define __CONTROLBEHAVIORS__H__ + +#include + +#include "RakNetTypes.h" + +class Entity; +class AMFArrayValue; + +namespace ControlBehaviors { + /** + * @brief Main driver for processing Property Behavior commands + * + * @param modelEntity The model that sent this command + * @param sysAddr The SystemAddress to respond to + * @param arguments The arguments formatted as an AMFArrayValue + * @param command The command to perform + * @param modelOwner The owner of the model which sent this command + */ + void ProcessCommand(Entity* modelEntity, const SystemAddress& sysAddr, AMFArrayValue* arguments, std::string command, Entity* modelOwner); + + /** + * @brief Helper function to send the behavior list to the client + * + * @param modelEntity The model that sent this command + * @param sysAddr The SystemAddress to respond to + * @param modelOwner The owner of the model which sent this command + */ + void SendBehaviorListToClient(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner); +}; + +#endif //!__CONTROLBEHAVIORS__H__