mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
Converts the skill messages to NetGameMsgs in SkillMessages.{h,cpp}:
AddSkill and RemoveSkill (SendToClient, as before), EchoStartSkill,
EchoSyncSkill and DoClientProjectileImpact, and the received
SelectSkill, StartSkill, SyncSkill and RequestServerProjectileImpact.
The one-off StartSkill, EchoStartSkill, SyncSkill, EchoSyncSkill,
RequestServerProjectileImpact and DoClientProjectileImpact classes are
deleted; SkillComponent, BehaviorContext and InventoryComponent build
the structs, and the dashboard's message decoder reads them.
The received messages are registered in GameMessageHandler's map and
their switch cases are deleted; the handlers call SkillComponent as
before. The echoes still go to every client except the caster, through
the new NetGameMsg::BroadcastExcept. SelectSkill still accepts any
payload, since the old case read nothing. Handler logic is unchanged
(the SyncSkill case's unused hex dump of the payload is dropped).
No wire change. Verified byte for byte against frozen verbatim copies
of the old classes and functions over an input grid (every optional
field set and unset, empty, short and long behavior streams), received
messages compared with the old classes' read sequences and truncated
payloads rejected, the echo's broadcast-except destination compared
with the old send, hand computed golden bytes, round trips, and a
deliberate mutation made the tests fail. Messages that fail to
deserialize are now dropped instead of being handled half read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
4.8 KiB
C++
95 lines
4.8 KiB
C++
#include "GameMessageDecoder.h"
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "GameMessages.h"
|
|
#include "SkillMessages.h"
|
|
#include "master/MessageCapture.h"
|
|
|
|
namespace {
|
|
using json = nlohmann::json;
|
|
using Decoder = std::function<std::optional<json>(RakNet::BitStream&)>;
|
|
|
|
// Object IDs as strings: they don't fit in a JavaScript number
|
|
std::string Id(LWOOBJID id) { return std::to_string(id); }
|
|
json Point(const NiPoint3& point) { return json::array({ point.x, point.y, point.z }); }
|
|
json Rotation(const NiQuaternion& rotation) { return json::array({ rotation.x, rotation.y, rotation.z, rotation.w }); }
|
|
// A skill's behavior data, which is its own bit stream
|
|
json Bytes(const std::string& bytes) { return MessageCapture::ToHex(bytes); }
|
|
|
|
// Reads T with its Deserialize, then lists its fields with `fields`
|
|
template<typename T>
|
|
Decoder Make(std::function<void(const T&, json&)> fields) {
|
|
return [fields](RakNet::BitStream& stream) -> std::optional<json> {
|
|
T message;
|
|
if (!message.Deserialize(stream)) return std::nullopt;
|
|
json out = json::object();
|
|
fields(message, out);
|
|
return out;
|
|
};
|
|
}
|
|
|
|
// (to server, message ID) -> decoder
|
|
const std::map<std::pair<bool, MessageType::Game>, Decoder>& Decoders() {
|
|
using namespace GameMessages;
|
|
using enum MessageType::Game;
|
|
static const std::map<std::pair<bool, MessageType::Game>, Decoder> decoders{
|
|
// Sent by the client
|
|
{ { true, REQUEST_USE }, Make<RequestUse>([](const RequestUse& m, json& j) {
|
|
j = { {"bIsMultiInteractUse", m.bIsMultiInteractUse}, {"multiInteractID", m.multiInteractID},
|
|
{"multiInteractType", m.multiInteractType}, {"object", Id(m.object)}, {"secondary", m.secondary} };
|
|
}) },
|
|
{ { true, REQUEST_SERVER_OBJECT_INFO }, Make<RequestServerObjectInfo>([](const RequestServerObjectInfo& m, json& j) {
|
|
j = { {"bVerbose", m.bVerbose}, {"clientId", Id(m.clientId)}, {"targetForReport", Id(m.targetForReport)} };
|
|
}) },
|
|
{ { true, SHOOTING_GALLERY_FIRE }, Make<ShootingGalleryFire>([](const ShootingGalleryFire& m, json& j) {
|
|
j = { {"target", Point(m.target)}, {"rotation", Rotation(m.rotation)} };
|
|
}) },
|
|
{ { true, PICKUP_ITEM }, Make<PickupItem>([](const PickupItem& m, json& j) {
|
|
j = { {"lootID", Id(m.lootID)}, {"lootOwnerID", Id(m.lootOwnerID)} };
|
|
}) },
|
|
{ { true, START_SKILL }, Make<StartSkill>([](const StartSkill& m, json& j) {
|
|
j = { {"bUsedMouse", m.bUsedMouse}, {"consumableItemID", Id(m.consumableItemID)}, {"fCasterLatency", m.fCasterLatency},
|
|
{"iCastType", m.iCastType}, {"lastClickedPosit", Point(m.lastClickedPosit)}, {"optionalOriginatorID", Id(m.optionalOriginatorID)},
|
|
{"optionalTargetID", Id(m.optionalTargetID)}, {"originatorRot", Rotation(m.originatorRot)}, {"sBitStream", Bytes(m.sBitStream)},
|
|
{"skillID", m.skillID}, {"uiSkillHandle", m.uiSkillHandle} };
|
|
}) },
|
|
{ { true, SYNC_SKILL }, Make<SyncSkill>([](const SyncSkill& m, json& j) {
|
|
j = { {"bDone", m.bDone}, {"sBitStream", Bytes(m.sBitStream)}, {"uiBehaviorHandle", m.uiBehaviorHandle}, {"uiSkillHandle", m.uiSkillHandle} };
|
|
}) },
|
|
{ { true, REQUEST_SERVER_PROJECTILE_IMPACT }, Make<RequestServerProjectileImpact>([](const RequestServerProjectileImpact& m, json& j) {
|
|
j = { {"i64LocalID", Id(m.i64LocalID)}, {"i64TargetID", Id(m.i64TargetID)}, {"sBitStream", Bytes(m.sBitStream)} };
|
|
}) },
|
|
// Sent to the client
|
|
{ { false, ECHO_START_SKILL }, Make<EchoStartSkill>([](const EchoStartSkill& m, json& j) {
|
|
j = { {"bUsedMouse", m.bUsedMouse}, {"fCasterLatency", m.fCasterLatency}, {"iCastType", m.iCastType},
|
|
{"lastClickedPosit", Point(m.lastClickedPosit)}, {"optionalOriginatorID", Id(m.optionalOriginatorID)},
|
|
{"optionalTargetID", Id(m.optionalTargetID)}, {"originatorRot", Rotation(m.originatorRot)}, {"sBitStream", Bytes(m.sBitStream)},
|
|
{"skillID", m.skillID}, {"uiSkillHandle", m.uiSkillHandle} };
|
|
}) },
|
|
{ { false, ECHO_SYNC_SKILL }, Make<EchoSyncSkill>([](const EchoSyncSkill& m, json& j) {
|
|
j = { {"bDone", m.bDone}, {"sBitStream", Bytes(m.sBitStream)}, {"uiBehaviorHandle", m.uiBehaviorHandle}, {"uiSkillHandle", m.uiSkillHandle} };
|
|
}) },
|
|
{ { false, DO_CLIENT_PROJECTILE_IMPACT }, Make<DoClientProjectileImpact>([](const DoClientProjectileImpact& m, json& j) {
|
|
j = { {"i64OrgID", Id(m.i64OrgID)}, {"i64OwnerID", Id(m.i64OwnerID)}, {"i64TargetID", Id(m.i64TargetID)}, {"sBitStream", Bytes(m.sBitStream)} };
|
|
}) },
|
|
};
|
|
return decoders;
|
|
}
|
|
}
|
|
|
|
namespace GameMessageDecoder {
|
|
bool CanDecode(MessageType::Game messageId, bool toServer) {
|
|
return Decoders().contains({ toServer, messageId });
|
|
}
|
|
|
|
std::optional<nlohmann::json> Decode(MessageType::Game messageId, bool toServer, RakNet::BitStream& payload) {
|
|
const auto it = Decoders().find({ toServer, messageId });
|
|
if (it == Decoders().end()) return std::nullopt;
|
|
return it->second(payload);
|
|
}
|
|
}
|