[Chat]: New save feature (#732)
This commit is contained in:
parent
5805331bfd
commit
bd7d2fe059
@ -5,6 +5,8 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
const char* Bans::BanListFile = "userraw/bans.json";
|
||||
|
||||
// Have only one instance of IW4x read/write the file
|
||||
std::unique_lock<Utils::NamedMutex> Bans::Lock()
|
||||
{
|
||||
@ -89,7 +91,7 @@ namespace Components
|
||||
|
||||
void Bans::SaveBans(const BanList* list)
|
||||
{
|
||||
assert(list != nullptr);
|
||||
assert(list);
|
||||
|
||||
const auto _ = Lock();
|
||||
|
||||
@ -107,7 +109,8 @@ namespace Components
|
||||
ipEntry.bytes[0] & 0xFF,
|
||||
ipEntry.bytes[1] & 0xFF,
|
||||
ipEntry.bytes[2] & 0xFF,
|
||||
ipEntry.bytes[3] & 0xFF));
|
||||
ipEntry.bytes[3] & 0xFF)
|
||||
);
|
||||
}
|
||||
|
||||
const nlohmann::json bans = nlohmann::json
|
||||
@ -116,18 +119,17 @@ namespace Components
|
||||
{ "id", idVector },
|
||||
};
|
||||
|
||||
FileSystem::FileWriter ("bans.json").write(bans.dump());
|
||||
Utils::IO::WriteFile(BanListFile, bans.dump());
|
||||
}
|
||||
|
||||
void Bans::LoadBans(BanList* list)
|
||||
{
|
||||
assert(list != nullptr);
|
||||
assert(list);
|
||||
|
||||
const auto _ = Lock();
|
||||
|
||||
FileSystem::File bans("bans.json");
|
||||
|
||||
if (!bans.exists())
|
||||
const auto bans = Utils::IO::ReadFile(BanListFile);
|
||||
if (bans.empty())
|
||||
{
|
||||
Logger::Debug("bans.json does not exist");
|
||||
return;
|
||||
@ -136,14 +138,20 @@ namespace Components
|
||||
nlohmann::json banData;
|
||||
try
|
||||
{
|
||||
banData = nlohmann::json::parse(bans.getBuffer());
|
||||
banData = nlohmann::json::parse(bans);
|
||||
}
|
||||
catch (const nlohmann::json::parse_error& ex)
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!banData.contains("id") || !banData.contains("ip"))
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "bans.json contains invalid data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& idList = banData["id"];
|
||||
const auto& ipList = banData["ip"];
|
||||
|
||||
|
@ -9,8 +9,6 @@ namespace Components
|
||||
|
||||
Bans();
|
||||
|
||||
static std::unique_lock<Utils::NamedMutex> Lock();
|
||||
|
||||
static void BanClient(Game::client_t* cl, const std::string& reason);
|
||||
static void UnbanClient(SteamID id);
|
||||
static void UnbanClient(Game::netIP_t ip);
|
||||
@ -25,6 +23,10 @@ namespace Components
|
||||
std::vector<Game::netIP_t> ipList;
|
||||
};
|
||||
|
||||
static const char* BanListFile;
|
||||
|
||||
static std::unique_lock<Utils::NamedMutex> Lock();
|
||||
|
||||
static void LoadBans(BanList* list);
|
||||
static void SaveBans(const BanList* list);
|
||||
};
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
#include <json.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Dvar::Var Chat::cg_chatWidth;
|
||||
@ -14,10 +16,19 @@ namespace Components
|
||||
bool Chat::SendChat;
|
||||
|
||||
Utils::Concurrency::Container<Chat::muteList> Chat::MutedList;
|
||||
const char* Chat::MutedListFile = "userraw/muted-users.json";
|
||||
|
||||
bool Chat::CanAddCallback = true;
|
||||
std::vector<Scripting::Function> Chat::SayCallbacks;
|
||||
|
||||
// Have only one instance of IW4x read/write the file
|
||||
std::unique_lock<Utils::NamedMutex> Chat::Lock()
|
||||
{
|
||||
static Utils::NamedMutex mutex{"iw4x-mute-list-lock"};
|
||||
std::unique_lock lock{mutex};
|
||||
return lock;
|
||||
}
|
||||
|
||||
const char* Chat::EvaluateSay(char* text, Game::gentity_t* player, int mode)
|
||||
{
|
||||
SendChat = true;
|
||||
@ -236,7 +247,9 @@ namespace Components
|
||||
|
||||
Game::cgsArray[0].teamChatPos++;
|
||||
if (Game::cgsArray[0].teamChatPos - Game::cgsArray[0].teamLastChatPos > chatHeight)
|
||||
{
|
||||
Game::cgsArray[0].teamLastChatPos = Game::cgsArray[0].teamChatPos + 1 - chatHeight;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Chat::CG_AddToTeamChat_Stub()
|
||||
@ -259,7 +272,20 @@ namespace Components
|
||||
const auto clientNum = ent - Game::g_entities;
|
||||
const auto xuid = Game::svs_clients[clientNum].steamID;
|
||||
|
||||
const auto result = MutedList.access<bool>([&](muteList& clients)
|
||||
const auto result = MutedList.access<bool>([&](const muteList& clients)
|
||||
{
|
||||
return clients.contains(xuid);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Chat::IsMuted(const Game::client_t* cl)
|
||||
{
|
||||
const auto clientNum = cl - Game::svs_clients;
|
||||
const auto xuid = Game::svs_clients[clientNum].steamID;
|
||||
|
||||
const auto result = MutedList.access<bool>([&](const muteList& clients)
|
||||
{
|
||||
return clients.contains(xuid);
|
||||
});
|
||||
@ -273,6 +299,7 @@ namespace Components
|
||||
MutedList.access([&](muteList& clients)
|
||||
{
|
||||
clients.insert(xuid);
|
||||
SaveMutedList(clients);
|
||||
});
|
||||
|
||||
Logger::Print("{} was muted\n", client->name);
|
||||
@ -295,6 +322,67 @@ namespace Components
|
||||
clients.clear();
|
||||
else
|
||||
clients.erase(id);
|
||||
|
||||
SaveMutedList(clients);
|
||||
});
|
||||
}
|
||||
|
||||
void Chat::SaveMutedList(const muteList& list)
|
||||
{
|
||||
const auto _ = Lock();
|
||||
|
||||
const nlohmann::json mutedUsers = nlohmann::json
|
||||
{
|
||||
{ "SteamID", list },
|
||||
};
|
||||
|
||||
Utils::IO::WriteFile(MutedListFile, mutedUsers.dump());
|
||||
}
|
||||
|
||||
void Chat::LoadMutedList()
|
||||
{
|
||||
const auto _ = Lock();
|
||||
|
||||
const auto mutedUsers = Utils::IO::ReadFile(MutedListFile);
|
||||
if (mutedUsers.empty())
|
||||
{
|
||||
Logger::Debug("muted-users.json does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json mutedUsersData;
|
||||
try
|
||||
{
|
||||
mutedUsersData = nlohmann::json::parse(mutedUsers);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mutedUsersData.contains("SteamID"))
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "muted-users.json contains invalid data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& list = mutedUsersData["SteamID"];
|
||||
if (!list.is_array())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MutedList.access([&](muteList& clients)
|
||||
{
|
||||
const nlohmann::json::array_t arr = list;
|
||||
for (auto& entry : arr)
|
||||
{
|
||||
if (entry.is_number_unsigned())
|
||||
{
|
||||
clients.insert(entry.get<std::uint64_t>());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -522,6 +610,8 @@ namespace Components
|
||||
sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");
|
||||
Events::OnSVInit(AddChatCommands);
|
||||
|
||||
LoadMutedList();
|
||||
|
||||
// Intercept chat sending
|
||||
Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4D00D4, PostSayStub, HOOK_CALL).install()->quick();
|
||||
|
@ -8,6 +8,9 @@ namespace Components
|
||||
public:
|
||||
Chat();
|
||||
|
||||
static bool IsMuted(const Game::gentity_s* ent);
|
||||
static bool IsMuted(const Game::client_t* cl);
|
||||
|
||||
private:
|
||||
static Dvar::Var cg_chatWidth;
|
||||
static Dvar::Var sv_disableChat;
|
||||
@ -17,10 +20,13 @@ namespace Components
|
||||
|
||||
using muteList = std::unordered_set<std::uint64_t>;
|
||||
static Utils::Concurrency::Container<muteList> MutedList;
|
||||
static const char* MutedListFile;
|
||||
|
||||
static bool CanAddCallback; // ClientCommand & GSC thread are the same
|
||||
static std::vector<Scripting::Function> SayCallbacks;
|
||||
|
||||
static std::unique_lock<Utils::NamedMutex> Lock();
|
||||
|
||||
static const char* EvaluateSay(char* text, Game::gentity_t* player, int mode);
|
||||
|
||||
static void PreSayStub();
|
||||
@ -30,10 +36,12 @@ namespace Components
|
||||
static void CG_AddToTeamChat(const char* text);
|
||||
static void CG_AddToTeamChat_Stub();
|
||||
|
||||
static bool IsMuted(const Game::gentity_s* ent);
|
||||
static void MuteClient(const Game::client_t* client);
|
||||
static void UnmuteClient(const Game::client_t* client);
|
||||
static void UnmuteInternal(std::uint64_t id, bool everyone = false);
|
||||
static void SaveMutedList(const muteList& list);
|
||||
static void LoadMutedList();
|
||||
|
||||
static void AddChatCommands();
|
||||
|
||||
static int GetCallbackReturn();
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Components
|
||||
{
|
||||
Utils::Signal<Events::ClientCallback> Events::ClientDisconnectSignal;
|
||||
Utils::Signal<Events::ClientConnectCallback> Events::ClientConnectSignal;
|
||||
Utils::Signal<Events::Callback> Events::SteamDisconnectSignal;
|
||||
Utils::Signal<Events::Callback> Events::ShutdownSystemSignal;
|
||||
Utils::Signal<Events::Callback> Events::ClientInitSignal;
|
||||
@ -13,6 +14,11 @@ namespace Components
|
||||
ClientDisconnectSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Events::OnClientConnect(const Utils::Slot<ClientConnectCallback>& callback)
|
||||
{
|
||||
ClientConnectSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Events::OnSteamDisconnect(const Utils::Slot<Callback>& callback)
|
||||
{
|
||||
SteamDisconnectSignal.connect(callback);
|
||||
@ -44,6 +50,13 @@ namespace Components
|
||||
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum); // ClientDisconnect
|
||||
}
|
||||
|
||||
void Events::SV_UserinfoChanged_Hk(Game::client_t* cl)
|
||||
{
|
||||
ClientConnectSignal(cl);
|
||||
|
||||
Utils::Hook::Call<void(Game::client_t*)>(0x401950)(cl); // SV_UserinfoChanged
|
||||
}
|
||||
|
||||
void Events::SteamDisconnect_Hk()
|
||||
{
|
||||
SteamDisconnectSignal();
|
||||
@ -78,6 +91,8 @@ namespace Components
|
||||
{
|
||||
Utils::Hook(0x625235, ClientDisconnect_Hk, HOOK_CALL).install()->quick(); // SV_FreeClient
|
||||
|
||||
Utils::Hook(0x4612BD, SV_UserinfoChanged_Hk, HOOK_CALL).install()->quick(); // SV_DirectConnect
|
||||
|
||||
Utils::Hook(0x403582, SteamDisconnect_Hk, HOOK_CALL).install()->quick(); // CL_Disconnect
|
||||
|
||||
Utils::Hook(0x47548B, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_LoadGame
|
||||
|
@ -6,6 +6,7 @@ namespace Components
|
||||
{
|
||||
public:
|
||||
typedef void(ClientCallback)(int clientNum);
|
||||
typedef void(ClientConnectCallback)(Game::client_t* cl);
|
||||
typedef void(Callback)();
|
||||
|
||||
Events();
|
||||
@ -13,6 +14,9 @@ namespace Components
|
||||
// Server side
|
||||
static void OnClientDisconnect(const Utils::Slot<ClientCallback>& callback);
|
||||
|
||||
// Server side
|
||||
static void OnClientConnect(const Utils::Slot<ClientConnectCallback>& callback);
|
||||
|
||||
// Client side
|
||||
static void OnSteamDisconnect(const Utils::Slot<Callback>& callback);
|
||||
|
||||
@ -25,12 +29,14 @@ namespace Components
|
||||
|
||||
private:
|
||||
static Utils::Signal<ClientCallback> ClientDisconnectSignal;
|
||||
static Utils::Signal<ClientConnectCallback> ClientConnectSignal;
|
||||
static Utils::Signal<Callback> SteamDisconnectSignal;
|
||||
static Utils::Signal<Callback> ShutdownSystemSignal;
|
||||
static Utils::Signal<Callback> ClientInitSignal;
|
||||
static Utils::Signal<Callback> ServerInitSignal;
|
||||
|
||||
static void ClientDisconnect_Hk(int clientNum);
|
||||
static void SV_UserinfoChanged_Hk(Game::client_t* cl);
|
||||
static void SteamDisconnect_Hk();
|
||||
static void Scr_ShutdownSystem_Hk(unsigned char sys);
|
||||
static void CL_InitOnceForAllClients_HK();
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "Chat.hpp"
|
||||
#include "Voice.hpp"
|
||||
|
||||
namespace Components
|
||||
@ -381,6 +382,13 @@ namespace Components
|
||||
|
||||
Events::OnSteamDisconnect(CL_ClearMutedList);
|
||||
Events::OnClientDisconnect(SV_UnmuteClient);
|
||||
Events::OnClientConnect([](Game::client_t* cl) -> void
|
||||
{
|
||||
if (Chat::IsMuted(cl))
|
||||
{
|
||||
SV_MuteClient(cl - Game::svs_clients);
|
||||
}
|
||||
});
|
||||
|
||||
// Write voice packets to the server instead of other clients
|
||||
Utils::Hook(0x487935, CL_WriteVoicePacket_Hk, HOOK_CALL).install()->quick();
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
namespace Utils::IO
|
||||
{
|
||||
bool FileExists(const std::string& file);
|
||||
[[nodiscard]] bool FileExists(const std::string& file);
|
||||
bool WriteFile(const std::string& file, const std::string& data, bool append = false);
|
||||
bool ReadFile(const std::string& file, std::string* data);
|
||||
std::string ReadFile(const std::string& file);
|
||||
[[nodiscard]] std::string ReadFile(const std::string& file);
|
||||
bool RemoveFile(const std::string& file);
|
||||
std::size_t FileSize(const std::string& file);
|
||||
[[nodiscard]] std::size_t FileSize(const std::string& file);
|
||||
bool CreateDir(const std::string& dir);
|
||||
bool DirectoryExists(const std::filesystem::path& directory);
|
||||
bool DirectoryIsEmpty(const std::filesystem::path& directory);
|
||||
std::vector<std::string> ListFiles(const std::filesystem::path& directory, bool recursive = false);
|
||||
[[nodiscard]] bool DirectoryExists(const std::filesystem::path& directory);
|
||||
[[nodiscard]] bool DirectoryIsEmpty(const std::filesystem::path& directory);
|
||||
[[nodiscard]] std::vector<std::string> ListFiles(const std::filesystem::path& directory, bool recursive = false);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user