From 62213cd701be00ca6a58de120a0bfa5c68d627c9 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Mon, 31 Oct 2022 15:32:07 -0700 Subject: [PATCH] Implement basic functionality (#794) Implements the basic functionality and parsing of property behaviors. Unhandled messages and logged and discarded for the time being. The only implemented message is a basic one that sends the needed info the the client side User Interface to pop up. Tested that the User Interface properly shows up with zero behaviors on it. No other functionality is changed. --- CMakeLists.txt | 1 + dGame/CMakeLists.txt | 7 ++ dGame/dGameMessages/GameMessages.cpp | 22 ++++- dGame/dPropertyBehaviors/CMakeLists.txt | 4 + dGame/dPropertyBehaviors/ControlBehaviors.cpp | 81 +++++++++++++++++++ dGame/dPropertyBehaviors/ControlBehaviors.h | 35 ++++++++ 6 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 dGame/dPropertyBehaviors/CMakeLists.txt create mode 100644 dGame/dPropertyBehaviors/ControlBehaviors.cpp create mode 100644 dGame/dPropertyBehaviors/ControlBehaviors.h 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__