mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 15:57:13 +00:00
fix: route only the chat messages a client sends
A world server passed every routed packet on to the chat server under whatever message ID it carried. It now passes on the messages a client sends, and SHOW_ALL only for the level the showall command needs. The message ID is read as the full 32 bit number, the packet has to be long enough to hold a chat packet and the sender needs a character. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
67
dNet/ChatRouting.h
Normal file
67
dNet/ChatRouting.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef __CHATROUTING__H__
|
||||
#define __CHATROUTING__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "MessageType/Chat.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
|
||||
namespace ChatRouting {
|
||||
// A routed packet is the world header, the size of the chat packet and then the chat packet itself.
|
||||
// The chat packet's message ID sits at this offset and its contents start after its own header.
|
||||
constexpr uint32_t MessageIdOffset = 14;
|
||||
constexpr uint32_t ContentOffset = 23;
|
||||
|
||||
/**
|
||||
* The chat messages a client sends through a world server, with the GM level each one needs.
|
||||
* Every other chat message only ever comes from a server, so it has no level and is not routed.
|
||||
*/
|
||||
constexpr std::optional<eGameMasterLevel> GetRequiredGMLevel(const MessageType::Chat messageId) {
|
||||
switch (messageId) {
|
||||
case MessageType::Chat::GENERAL_CHAT_MESSAGE:
|
||||
case MessageType::Chat::PRIVATE_CHAT_MESSAGE:
|
||||
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
|
||||
case MessageType::Chat::ADD_FRIEND_REQUEST:
|
||||
case MessageType::Chat::ADD_FRIEND_RESPONSE:
|
||||
case MessageType::Chat::REMOVE_FRIEND:
|
||||
case MessageType::Chat::GET_FRIENDS_LIST:
|
||||
case MessageType::Chat::ADD_IGNORE:
|
||||
case MessageType::Chat::REMOVE_IGNORE:
|
||||
case MessageType::Chat::GET_IGNORE_LIST:
|
||||
case MessageType::Chat::TEAM_MISSED_INVITE_CHECK:
|
||||
case MessageType::Chat::TEAM_INVITE:
|
||||
case MessageType::Chat::TEAM_INVITE_RESPONSE:
|
||||
case MessageType::Chat::TEAM_KICK:
|
||||
case MessageType::Chat::TEAM_LEAVE:
|
||||
case MessageType::Chat::TEAM_SET_LOOT:
|
||||
case MessageType::Chat::TEAM_SET_LEADER:
|
||||
case MessageType::Chat::TEAM_GET_STATUS:
|
||||
case MessageType::Chat::GUILD_CREATE:
|
||||
case MessageType::Chat::GUILD_INVITE:
|
||||
case MessageType::Chat::GUILD_INVITE_RESPONSE:
|
||||
case MessageType::Chat::GUILD_LEAVE:
|
||||
case MessageType::Chat::GUILD_KICK:
|
||||
case MessageType::Chat::GUILD_GET_STATUS:
|
||||
case MessageType::Chat::GUILD_GET_ALL:
|
||||
case MessageType::Chat::MAIL:
|
||||
case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE:
|
||||
case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE_PRIVATE:
|
||||
case MessageType::Chat::WHO:
|
||||
return eGameMasterLevel::CIVILIAN;
|
||||
// The same level the showall chat command needs
|
||||
case MessageType::Chat::SHOW_ALL:
|
||||
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
||||
return eGameMasterLevel::JUNIOR_MODERATOR;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool CanRoute(const MessageType::Chat messageId, const eGameMasterLevel senderLevel) {
|
||||
const auto requiredLevel = GetRequiredGMLevel(messageId);
|
||||
return requiredLevel && senderLevel >= *requiredLevel;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //!__CHATROUTING__H__
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "Entity.h"
|
||||
#include "Character.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "ChatRouting.h"
|
||||
#include "ScriptedActivityComponent.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "WorldRoutePacket.h"
|
||||
@@ -1564,14 +1565,24 @@ namespace {
|
||||
return;
|
||||
}
|
||||
|
||||
//We need to insert the player's objectID so the chat server can find who originated this request:
|
||||
LWOOBJID objectID = 0;
|
||||
auto user = UserManager::Instance()->GetUser(sysAddr);
|
||||
if (user) {
|
||||
const auto* const lastChar = user->GetLastUsedChar();
|
||||
if (lastChar) objectID = lastChar->GetObjectID();
|
||||
// The chat server looks the sender up by this ID, so there has to be a character to send as
|
||||
auto* const user = UserManager::Instance()->GetUser(sysAddr);
|
||||
const auto* const lastChar = user ? user->GetLastUsedChar() : nullptr;
|
||||
if (!lastChar) {
|
||||
LOG("Tried to route a chat packet without a character.");
|
||||
return;
|
||||
}
|
||||
|
||||
// The whole 32 bit ID is checked, the chat server gets its first byte
|
||||
const auto chatMessageId = static_cast<MessageType::Chat>(routedMessageID);
|
||||
if (!ChatRouting::CanRoute(chatMessageId, lastChar->GetGMLevel())) {
|
||||
LOG("Character %llu tried to route chat message %u, which they may not send.", lastChar->GetObjectID(), routedMessageID);
|
||||
return;
|
||||
}
|
||||
|
||||
//We need to insert the player's objectID so the chat server can find who originated this request:
|
||||
const LWOOBJID objectID = lastChar->GetObjectID();
|
||||
|
||||
ChatServerLink::Send(ToChat(objectID), SYSTEM_PRIORITY, RELIABLE_ORDERED);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +18,8 @@ SET(DGAMEMESSAGES_TESTS
|
||||
"GameMessageDecoderTests.cpp"
|
||||
"HotPropertyMessageTests.cpp"
|
||||
"ClientMessagesTests.cpp"
|
||||
"LoadSequenceTests.cpp")
|
||||
"LoadSequenceTests.cpp"
|
||||
"ChatRoutingTests.cpp")
|
||||
|
||||
# Get the folder name and prepend it to the files above
|
||||
get_filename_component(thisFolderName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||
|
||||
47
tests/dGameTests/dGameMessagesTests/ChatRoutingTests.cpp
Normal file
47
tests/dGameTests/dGameMessagesTests/ChatRoutingTests.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ChatRouting.h"
|
||||
|
||||
TEST(ChatRoutingTest, PlayersRouteTheMessagesTheClientSends) {
|
||||
const auto civilian = eGameMasterLevel::CIVILIAN;
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::GENERAL_CHAT_MESSAGE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::PRIVATE_CHAT_MESSAGE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::ADD_FRIEND_REQUEST, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::ADD_FRIEND_RESPONSE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::REMOVE_FRIEND, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::GET_FRIENDS_LIST, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::ADD_IGNORE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::REMOVE_IGNORE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::GET_IGNORE_LIST, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::TEAM_INVITE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::TEAM_INVITE_RESPONSE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::TEAM_KICK, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::TEAM_LEAVE, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::TEAM_SET_LOOT, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::TEAM_SET_LEADER, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::TEAM_GET_STATUS, civilian));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::WHO, civilian));
|
||||
}
|
||||
|
||||
TEST(ChatRoutingTest, ServerMessagesAreNeverRouted) {
|
||||
for (const auto level : { eGameMasterLevel::CIVILIAN, eGameMasterLevel::OPERATOR }) {
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::LOGIN_SESSION_NOTIFY, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::GMLEVEL_UPDATE, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::GM_KICK, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::GM_ANNOUNCE, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::GM_MUTE, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::WORLD_ROUTE_PACKET, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::ACHIEVEMENT_NOTIFY, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::UNEXPECTED_DISCONNECT, level));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::CREATE_TEAM, level));
|
||||
// Not a message at all
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(static_cast<MessageType::Chat>(0x100 | 1), level));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ChatRoutingTest, ShowAllNeedsTheChatCommandLevel) {
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::SHOW_ALL, eGameMasterLevel::CIVILIAN));
|
||||
EXPECT_FALSE(ChatRouting::CanRoute(MessageType::Chat::SHOW_ALL, eGameMasterLevel::FORUM_MODERATOR));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::SHOW_ALL, eGameMasterLevel::JUNIOR_MODERATOR));
|
||||
EXPECT_TRUE(ChatRouting::CanRoute(MessageType::Chat::SHOW_ALL, eGameMasterLevel::OPERATOR));
|
||||
}
|
||||
Reference in New Issue
Block a user