From 5387960965f143a09193e8e4b7a1c177b4a0f535 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Tue, 17 Dec 2024 17:43:13 -0500 Subject: [PATCH] Added EventProcessor for client and server communication --- src/Event/MpEvent.h | 27 ++++++++ src/Event/MpEventHandlers.cpp | 0 src/Event/MpEventProcessor.cpp | 114 +++++++++++++++++++++++++++++++++ src/Event/MpEventProcessor.h | 76 ++++++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 src/Event/MpEvent.h create mode 100644 src/Event/MpEventHandlers.cpp create mode 100644 src/Event/MpEventProcessor.cpp create mode 100644 src/Event/MpEventProcessor.h diff --git a/src/Event/MpEvent.h b/src/Event/MpEvent.h new file mode 100644 index 0000000..0df4dbb --- /dev/null +++ b/src/Event/MpEvent.h @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +#ifndef MP_EVENTS_H +#define MP_EVENTS_H + +// This defines +enum class MpEvent +{ + None, // Default catch all for no event + Invalid, // Used to capture identify error events + UpgradeAdvancement, + ResetAdvancement, + ResetAllAdvancements +}; + +// Mapping of Event Strings to EventNames for Event Callbacks from client +std::unordered_map MpEventMap = {{ + {"UpgradeAdvancement", MpEvent::UpgradeAdvancement}, + {"ResetAdvancement", MpEvent::ResetAdvancement}, + {"ResetAllAdvancements", MpEvent::ResetAllAdvancements} +}}; + + +#endif diff --git a/src/Event/MpEventHandlers.cpp b/src/Event/MpEventHandlers.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/Event/MpEventProcessor.cpp b/src/Event/MpEventProcessor.cpp new file mode 100644 index 0000000..6146598 --- /dev/null +++ b/src/Event/MpEventProcessor.cpp @@ -0,0 +1,114 @@ + +#include "MpEventProcessor.h" +#include "MythicPlus.h" +#include "MpLogger.h" +#include "Player.h" + +#include +#include +#include + +bool MpEventProcessor::ProcessMessage(Player* player, const std::string& message) { + + if(!player) { + MpLogger::error("Null player passed to processMessage"); + return false; + } + + EventParseRslt result = _parsePlayerMessage(player, message); + MpEvent event = std::get<0>(result); + uint32 guid = std::get<1>(result); + std::vector args = std::get<2>(result); + + // If th message was not able to be parsed it is a failure + if(event == MpEvent::Invalid) { + MpLogger::warn("Invalid event, could not be parsed for player {} message: {}", player->GetName(), message); + return false; + } + + // if the message is not from the same player who called it ignore it as it is attempt to hack the system + if(player->GetGUID().GetCounter() != guid) { + MpLogger::warn("Player {} sent a message {} for eventId: {} player guid does not match", player->GetName(), message, event); + return false; + } + + // If the event is not registered ignore it + if(!_eventHandlers.contains(event)) { + MpLogger::info("No handler registered for event: {}", event); + return false; + } + + return Dispatch(event, player, args); +} + +void MpEventProcessor::RegisterHandler(MpEvent event, std::shared_ptr handler) { + _eventHandlers[event] = handler; +} + +// This fires the execution to the actual event. +bool MpEventProcessor::Dispatch(MpEvent event, Player* player, std::vector& args) { + if(!_eventHandlers.contains(event)) { + MpLogger::warn("No handler registered for event: {}", event); + return false; + } + + return _eventHandlers[event]->Execute(args); +} + +// Find our eventId using the string name +MpEvent MpEventProcessor::_getEventByName(std::string_view eventName) +{ + auto it = MpEventMap.find(eventName); + return (it != MpEventMap.end()) ? it->second : MpEvent::None; +} + +/** + * Parse the incoming message into id, event, and handler arguments + */ +EventParseRslt MpEventProcessor::_parsePlayerMessage(Player* player, const std::string& msg) +{ + if(msg[0] != 'p') { + MpLogger::warn("Invalid player message format received from player {} message: {}", player->GetName(), msg); + return EventParseRslt{MpEvent::Invalid, 0, {}}; + } + + std::vector handlerArgs; + char delimiter = '|'; + + // split the protocol into valid parts + std::vector parts = _splitString(msg, delimiter); + + if (parts.size() < 3) { + MpLogger::warn("Malformed player message received from player {}: {}", player->GetName(), msg); + return EventParseRslt{MpEvent::Invalid, 0, {}}; + } + + MpEvent event = _getEventByName(parts[2]); + + handlerArgs.assign(parts.begin() + 3, parts.end()); + + MpLogger::info("Player {} sent a client event message {} for event: {} eventId: {} ", player->GetName(), msg, MP_DATA_CHAT_CHANNEL, parts[2], event); + return EventParseRslt{event, player->GetGUID().GetCounter(), std::move(handlerArgs)}; +} + +// Split the string passed in by delimiters +std::vector MpEventProcessor::_splitString(const std::string& s, char delimiter) { + std::vector tokens; + size_t start = 0; + size_t end = s.find(delimiter); + + while (end != std::string::npos) { + if (end != start) { + tokens.emplace_back(s.substr(start, end - start)); + } + start = end + 1; + end = s.find(delimiter, start); + } + + // Add the last token if it's not empty + if (start < s.length()) { + tokens.emplace_back(s.substr(start)); + } + + return tokens; +} diff --git a/src/Event/MpEventProcessor.h b/src/Event/MpEventProcessor.h new file mode 100644 index 0000000..7119189 --- /dev/null +++ b/src/Event/MpEventProcessor.h @@ -0,0 +1,76 @@ +#ifndef MYTHIC_PLUS_EVENT_PROCESSOR_H +#define MYTHIC_PLUS_EVENT_PROCESSOR_H + +#include "MpEvent.h" + +#include +#include +#include + +/** + * Interface for all Mythic+ Event communication between client and server + */ +class MpEventInterface +{ +public: + MpEventInterface() {}; + virtual ~MpEventInterface() = default; + [[nodiscard]] virtual bool Execute(std::vector&) = 0; +}; + +using EventParseRslt = std::tuple>; + +/** + * This class is responsible for processing events that are send from the client. Events + * are processed FIFO and executed immidiately. This is specifically to allow AddOn or AIO + * communication to this module without requiring additional changes to mod-eluna or the core. + * + * Message Body Format + * Client Player Events + * - p|playerGuid|action|input1|input2|input3... + * i.e) p|5793|UpgradeAdvancement|0|10|2 + */ +class MpEventProcessor +{ + +public: + static MpEventProcessor* instance() { + static MpEventProcessor instance; + return &instance; + } + + MpEventProcessor() = default; + ~MpEventProcessor() = default; + + // /* TODO */ Add generic handler later for other events if needed. + // bool ProcessMessage(std::string_view message); + + // Process a message from a specific player + bool ProcessMessage(Player* player, const std::string& message); + + // Registers a handler for a valid MpEvent specified in the MpEvent enum + // In this design Event:Handler is 1:1 + void RegisterHandler(MpEvent event, std::shared_ptr handler); + + // Dispatch will execute the event handler + bool Dispatch(MpEvent event, Player* player, std::vector& args); + +private: + MpEventProcessor() {} + ~MpEventProcessor() {} + + // List of registered handlers for event management + std::unordered_map> _eventHandlers; + + // Get the correct Event associated to handlers by the message strong + MpEvent _getEventByName(std::string_view eventName); + + // Parse a message from the player + EventParseRslt _parsePlayerMessage(Player* player, const std::string& msg); + + std::vector _splitString(const std::string& s, char delimiter = '|'); +}; + +#define sMpEventProcessor MpEventProcessor::instance() + +#endif // MYTHIC_PLUS_EVENT_PROCESSOR_H