mirror of
https://github.com/araxiaonline/mod-mythic-plus.git
synced 2026-06-13 03:02:24 -04:00
New client event dispatchers to commumicate to WoW Client using formatted event structure
This commit is contained in:
45
src/Event/MpClientDispatch.cpp
Normal file
45
src/Event/MpClientDispatch.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "MpClientDispatcher.h"
|
||||
#include "MpEventProcessor.h"
|
||||
#include "Player.h"
|
||||
#include "MpLogger.h"
|
||||
#include "Chat.h"
|
||||
#include "WorldPacket.h"
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* In order to allow communication from client UIs without modifying mod-eluna directly to support
|
||||
* This sends a packet directly using the AddOn message channel in the background.
|
||||
*/
|
||||
bool MpClientDispatcher::Dispatch(MpClientEvent event, Player* player, std::vector<std::string>& args)
|
||||
{
|
||||
if(!MpClientEventNames.contains(event)) {
|
||||
MpLogger::warn("No event registered for event: {}", event);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the message string in same format to send to the client.
|
||||
std::string_view eventName = MpClientEventNames.at(event);
|
||||
std::string message = std::to_string(player->GetGUID().GetCounter()) + "|" + eventName.data();
|
||||
for(auto& arg : args) {
|
||||
message += "|" + arg;
|
||||
}
|
||||
|
||||
std::string prefix = std::string(MP_DATA_CHAT_CHANNEL);
|
||||
std::string fullmsg = prefix + "\t" + message;
|
||||
|
||||
WorldPacket data(SMSG_MESSAGECHAT, 100);
|
||||
data << uint8(CHAT_MSG_ADDON);
|
||||
data << int32(LANG_ADDON);
|
||||
data << player->GetGUID().GetCounter();
|
||||
data << uint32(0);
|
||||
data << player->GetGUID().GetCounter();
|
||||
data << uint32(fullmsg.length() + 1);
|
||||
data << fullmsg;
|
||||
data << uint8(0);
|
||||
player->GetSession()->SendPacket(&data);
|
||||
return 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
35
src/Event/MpClientDispatcher.h
Normal file
35
src/Event/MpClientDispatcher.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef MYTHIC_PLUS_CLIENT_DISPATCHER_H
|
||||
#define MYTHIC_PLUS_CLIENT_DISPATCHER_H
|
||||
|
||||
#include "MpEvent.h"
|
||||
#include "Player.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class MpClientDispatcher
|
||||
{
|
||||
|
||||
public:
|
||||
static MpClientDispatcher* instance() {
|
||||
static MpClientDispatcher instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
MpClientDispatcher(const MpClientDispatcher&) = delete;
|
||||
MpClientDispatcher& operator=(const MpClientDispatcher&) = delete;
|
||||
|
||||
// encode and send a message to the client for an event in the map
|
||||
bool Dispatch(MpClientEvent event, Player* player, std::vector<std::string>& args);
|
||||
|
||||
private:
|
||||
MpClientDispatcher() {};
|
||||
~MpClientDispatcher() {};
|
||||
|
||||
};
|
||||
|
||||
#define sMpClientDispatcher MpClientDispatcher::instance()
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,21 +6,41 @@
|
||||
#ifndef MP_EVENTS_H
|
||||
#define MP_EVENTS_H
|
||||
|
||||
// This defines
|
||||
// This defines list of incoming events typically from the client
|
||||
enum class MpEvent
|
||||
{
|
||||
None, // Default catch all for no event
|
||||
Invalid, // Used to capture identify error events
|
||||
None, // Default catch all for no event
|
||||
Invalid, // Used to capture identify error events
|
||||
UpgradeAdvancement, // Upgrades a player advancement 1 level
|
||||
ResetAdvancement, // Resets a player Advancement back to 0
|
||||
ResetAllAdvancements, // Resets all player advancements
|
||||
GetAdvancementRank // Get the details about the rank of a specific advancement (cost, bonus, etc)
|
||||
};
|
||||
|
||||
// Client events that are communicated to the over an addon message to the player client
|
||||
enum class MpClientEvent
|
||||
{
|
||||
Error,
|
||||
UpgradeAdvancement,
|
||||
ResetAdvancement,
|
||||
ResetAllAdvancements
|
||||
ResetAllAdvancements,
|
||||
GetAdvancementRank
|
||||
};
|
||||
|
||||
// Mapping of Event Strings to EventNames for Event Callbacks from client
|
||||
std::unordered_map<std::string_view, MpEvent> MpEventMap = {{
|
||||
inline std::unordered_map<std::string_view, MpEvent> MpEventMap = {{
|
||||
{"UpgradeAdvancement", MpEvent::UpgradeAdvancement},
|
||||
{"ResetAdvancement", MpEvent::ResetAdvancement},
|
||||
{"ResetAllAdvancements", MpEvent::ResetAllAdvancements}
|
||||
{"ResetAllAdvancements", MpEvent::ResetAllAdvancements},
|
||||
{"GetAdvancementRank", MpEvent::ResetAllAdvancements}
|
||||
}};
|
||||
|
||||
inline std::unordered_map<MpClientEvent, std::string_view> MpClientEventNames = {{
|
||||
{MpClientEvent::Error, "Error"},
|
||||
{MpClientEvent::UpgradeAdvancement, "UpgradeAdvancement"},
|
||||
{MpClientEvent::ResetAdvancement, "ResetAdvancement"},
|
||||
{MpClientEvent::ResetAllAdvancements, "ResetAllAdvancements"},
|
||||
{MpClientEvent::GetAdvancementRank, "GetAdvancementRank"}
|
||||
}};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
#include "MpEvent.h"
|
||||
#include "MpLogger.h"
|
||||
#include "AdvancementMgr.h"
|
||||
#include "MpEventProcessor.h"
|
||||
#include "MpClientDispatcher.h"
|
||||
#include "Player.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* All handlers for passed between client and server handlers are managed
|
||||
* here.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handles Updates to a players advancement system
|
||||
* Event format
|
||||
* p|playerGuid|UpgradeAdvancement|advancementId|rank|diceSpent|bonus
|
||||
*/
|
||||
enum class MP_EVENT_CODE
|
||||
{
|
||||
SUCCESS = 0,
|
||||
INVALID_EVENT = 100,
|
||||
INVALID_ARGUMENT_SIZE = 201,
|
||||
INVALID_ARGUMENT = 202,
|
||||
INVALID_ARGUMENT_TYPE = 203,
|
||||
|
||||
};
|
||||
|
||||
class UpdateAdvancements : public MpEventInterface
|
||||
{
|
||||
public:
|
||||
const std::string& EventName() const override
|
||||
{
|
||||
return "UpgradeAdvancement";
|
||||
}
|
||||
|
||||
bool Execute(Player* player, std::vector<std::string>& args)
|
||||
{
|
||||
// Store the event data to send back to the client for parsing
|
||||
std::vector<std::string> eventData;
|
||||
|
||||
MpLogger::info("(EventProcessor) Executing {}}", EventName());
|
||||
for(auto& arg : args) {
|
||||
MpLogger::info("{} Arg: {}", EventName(), arg);
|
||||
}
|
||||
|
||||
// Validate the message is int he right format
|
||||
if(args.size() != 5) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT_SIZE, "Invalid number of arguments expected 5, found " + std::to_string(args.size()));
|
||||
}
|
||||
|
||||
uint32 advancementId = std::stoi(args[0]);
|
||||
if(advancementId >= MpAdvancements::MP_ADV_MAX) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid advancement id " + args[0] + " max is " + std::to_string(MpAdvancements::MP_ADV_MAX));
|
||||
}
|
||||
|
||||
uint32 diceLevel = std::stoi(args[1]);
|
||||
if(diceLevel < 1 || diceLevel > 3) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid dice level " + args[1] + " valid values are 1,2,3");
|
||||
}
|
||||
|
||||
uint32 itemEntry1 = std::stoi(args[2]);
|
||||
if(itemEntry1 == 0) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid item entry1 can not be empty " + args[2]);
|
||||
}
|
||||
|
||||
uint32 itemEntry2 = std::stoi(args[3]);
|
||||
uint32 itemEntry3 = std::stoi(args[4]);
|
||||
|
||||
// Upgrade the advancement for the player!
|
||||
if(! sAdvancementMgr->UpgradeAdvancement(player, static_cast<MpAdvancements>(advancementId), diceLevel, itemEntry1, itemEntry2, itemEntry3)) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Failed to upgrade advancement for player " + player->GetName());
|
||||
}
|
||||
std::vector<std::string> eventData = {"0", "success"};
|
||||
|
||||
// Send response back to the client
|
||||
sMpClientDispatcher->Dispatch(MpClientEvent::UpgradeAdvancement, player, eventData);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class GetAdvancementRank : public MpEventInterface
|
||||
{
|
||||
public:
|
||||
const std::string& EventName() const override
|
||||
{
|
||||
return "GetAdvancementRank";
|
||||
}
|
||||
|
||||
bool Execute(Player* player, std::vector<std::string>& args)
|
||||
{
|
||||
// Store the event data to send back to the client for parsing
|
||||
std::vector<std::string> eventData;
|
||||
|
||||
MpLogger::info("(EventProcessor) Executing {}}", EventName());
|
||||
for(auto& arg : args) {
|
||||
MpLogger::info("{} Arg: {}", EventName(), arg);
|
||||
}
|
||||
|
||||
// Validate the message is int he right format
|
||||
if(args.size() != 1) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT_SIZE, "Invalid number of arguments expected 1, found " + std::to_string(args.size()));
|
||||
}
|
||||
|
||||
uint32 advancementId = std::stoi(args[0]);
|
||||
if(advancementId >= MpAdvancements::MP_ADV_MAX) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Invalid advancement id " + args[0] + " max is " + std::to_string(MpAdvancements::MP_ADV_MAX));
|
||||
}
|
||||
|
||||
MpAdvancementRank* rank = sAdvancementMgr->GetAdvancementRank(1, static_cast<MpAdvancements>(advancementId));
|
||||
if(!rank) {
|
||||
return SendEventError(player, EventName(),MP_EVENT_CODE::INVALID_ARGUMENT, "Failed to get advancement rank for player " + player->GetName());
|
||||
}
|
||||
|
||||
std::vector<std::string> eventData = {std::to_string(rank->rank), std::to_string(rank->advancementId)};
|
||||
|
||||
// Send response back to the client
|
||||
sMpClientDispatcher->Dispatch(MpClientEvent::GetAdvancementRank, player, eventData);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Send an error event to the client
|
||||
bool SendEventError(Player* player, const std::string& method, MP_EVENT_CODE code, std::string message)
|
||||
{
|
||||
std::vector<std::string> clientError = { std::to_string(static_cast<int>(code)), message };
|
||||
MpLogger::error("Event Processor) Sending client error: {}", clientError);
|
||||
sMpClientDispatcher->Dispatch(MpClientEvent::Error, player, clientError);
|
||||
return false;
|
||||
}
|
||||
|
||||
void MP_Register_EventHandlers()
|
||||
{
|
||||
auto updateAdvancements = std::make_shared<UpdateAdvancements>();
|
||||
sMpEventProcessor->RegisterHandler(MpEvent::UpgradeAdvancement, updateAdvancements);
|
||||
}
|
||||
|
||||
@@ -3,23 +3,40 @@
|
||||
#include "MythicPlus.h"
|
||||
#include "MpLogger.h"
|
||||
#include "Player.h"
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool MpEventProcessor::ProcessMessage(Player* player, const std::string& message) {
|
||||
bool MpEventProcessor::ProcessMessage(Player* player, const std::string& msg) {
|
||||
|
||||
if(!player) {
|
||||
MpLogger::error("Null player passed to processMessage");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check prefix of message channel is formatted correctly
|
||||
if(! msg.starts_with(MP_DATA_CHAT_CHANNEL)) {
|
||||
MpLogger::error("Invalid message format received from player {} message: {}", player->GetName(), msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string message = msg;
|
||||
|
||||
// shift the message identifier off the front including first '|' character
|
||||
message.erase(0, MP_DATA_CHAT_CHANNEL.size()+1);
|
||||
|
||||
// clean up the message before passing it to the parser
|
||||
boost::replace_all(message, "||", "|");
|
||||
|
||||
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);
|
||||
|
||||
MpLogger::info("MpEvent Processor - event: {} guid: {} args: {}", event, guid, args.size());
|
||||
|
||||
// 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);
|
||||
@@ -52,7 +69,7 @@ bool MpEventProcessor::Dispatch(MpEvent event, Player* player, std::vector<std::
|
||||
return false;
|
||||
}
|
||||
|
||||
return _eventHandlers[event]->Execute(args);
|
||||
return _eventHandlers[event]->Execute(player, args);
|
||||
}
|
||||
|
||||
// Find our eventId using the string name
|
||||
|
||||
@@ -2,27 +2,39 @@
|
||||
#define MYTHIC_PLUS_EVENT_PROCESSOR_H
|
||||
|
||||
#include "MpEvent.h"
|
||||
#include "Player.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
|
||||
/**
|
||||
* In order to allow communication from client UIs without modifying mod-eluna directly to support
|
||||
* this mods functionality the following custom chat channel below is for all MP UI Client interactions.
|
||||
*
|
||||
* p|playerGuid|action|input1|input2|input3...
|
||||
*/
|
||||
inline const std::string_view MP_DATA_CHAT_CHANNEL = "MPUi";
|
||||
|
||||
/**
|
||||
* Interface for all Mythic+ Event communication between client and server
|
||||
*/
|
||||
class MpEventInterface
|
||||
{
|
||||
public:
|
||||
MpEventInterface() {};
|
||||
MpEventInterface() = default;
|
||||
virtual ~MpEventInterface() = default;
|
||||
[[nodiscard]] virtual bool Execute(std::vector<std::string>&) = 0;
|
||||
[[nodiscard]] virtual bool Execute(Player* player, std::vector<std::string>& args) = 0;
|
||||
|
||||
[[nodiscard]] virtual const std::string& EventName() const = 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
|
||||
* are processed FIFO and executed. 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
|
||||
@@ -39,14 +51,11 @@ public:
|
||||
return &instance;
|
||||
}
|
||||
|
||||
MpEventProcessor() = default;
|
||||
~MpEventProcessor() = default;
|
||||
|
||||
// /* TODO */ Add generic handler later for other events if needed.
|
||||
// bool ProcessMessage(std::string_view message);
|
||||
MpEventProcessor(const MpEventProcessor&) = delete;
|
||||
MpEventProcessor& operator=(const MpEventProcessor&) = delete;
|
||||
|
||||
// Process a message from a specific player
|
||||
bool ProcessMessage(Player* player, const std::string& message);
|
||||
bool ProcessMessage(Player* player, const std::string& msg);
|
||||
|
||||
// Registers a handler for a valid MpEvent specified in the MpEvent enum
|
||||
// In this design Event:Handler is 1:1
|
||||
@@ -68,6 +77,7 @@ private:
|
||||
// Parse a message from the player
|
||||
EventParseRslt _parsePlayerMessage(Player* player, const std::string& msg);
|
||||
|
||||
// Helper to break up a string into pieces used in parsing the message
|
||||
std::vector<std::string> _splitString(const std::string& s, char delimiter = '|');
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user