Added EventProcessor for client and server communication

This commit is contained in:
2024-12-17 17:43:13 -05:00
parent 4be3fae368
commit 5387960965
4 changed files with 217 additions and 0 deletions

27
src/Event/MpEvent.h Normal file
View File

@@ -0,0 +1,27 @@
#include <array>
#include <string_view>
#include <string>
#include <unordered_map>
#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<std::string_view, MpEvent> MpEventMap = {{
{"UpgradeAdvancement", MpEvent::UpgradeAdvancement},
{"ResetAdvancement", MpEvent::ResetAdvancement},
{"ResetAllAdvancements", MpEvent::ResetAllAdvancements}
}};
#endif

View File

View File

@@ -0,0 +1,114 @@
#include "MpEventProcessor.h"
#include "MythicPlus.h"
#include "MpLogger.h"
#include "Player.h"
#include <string_view>
#include <string>
#include <vector>
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<std::string> 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<MpEventInterface> handler) {
_eventHandlers[event] = handler;
}
// This fires the execution to the actual event.
bool MpEventProcessor::Dispatch(MpEvent event, Player* player, std::vector<std::string>& 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<std::string> handlerArgs;
char delimiter = '|';
// split the protocol into valid parts
std::vector<std::string> 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<std::string> MpEventProcessor::_splitString(const std::string& s, char delimiter) {
std::vector<std::string> 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;
}

View File

@@ -0,0 +1,76 @@
#ifndef MYTHIC_PLUS_EVENT_PROCESSOR_H
#define MYTHIC_PLUS_EVENT_PROCESSOR_H
#include "MpEvent.h"
#include <string>
#include <string_view>
#include <vector>
/**
* Interface for all Mythic+ Event communication between client and server
*/
class MpEventInterface
{
public:
MpEventInterface() {};
virtual ~MpEventInterface() = default;
[[nodiscard]] virtual bool Execute(std::vector<std::string>&) = 0;
};
using EventParseRslt = std::tuple<MpEvent, uint32_t, std::vector<std::string>>;
/**
* 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<MpEventInterface> handler);
// Dispatch will execute the event handler
bool Dispatch(MpEvent event, Player* player, std::vector<std::string>& args);
private:
MpEventProcessor() {}
~MpEventProcessor() {}
// List of registered handlers for event management
std::unordered_map<MpEvent, std::shared_ptr<MpEventInterface>> _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<std::string> _splitString(const std::string& s, char delimiter = '|');
};
#define sMpEventProcessor MpEventProcessor::instance()
#endif // MYTHIC_PLUS_EVENT_PROCESSOR_H