Merge pull request #311 from diamante0018/refactor-absn
[Bans] Refactor
This commit is contained in:
commit
828cc74d37
@ -2,14 +2,18 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::recursive_mutex Bans::AccessMutex;
|
// Have only one instance of IW4x read/write the file
|
||||||
|
std::unique_lock<Utils::NamedMutex> Bans::Lock()
|
||||||
bool Bans::IsBanned(Bans::Entry entry)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
static Utils::NamedMutex mutex{"iw4x-ban-list-lock"};
|
||||||
|
std::unique_lock lock{mutex};
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
Bans::BanList list;
|
bool Bans::IsBanned(const banEntry& entry)
|
||||||
Bans::LoadBans(&list);
|
{
|
||||||
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
|
|
||||||
if (entry.first.bits)
|
if (entry.first.bits)
|
||||||
{
|
{
|
||||||
@ -24,7 +28,7 @@ namespace Components
|
|||||||
|
|
||||||
if (entry.second.full)
|
if (entry.second.full)
|
||||||
{
|
{
|
||||||
for (auto& ipEntry : list.ipList)
|
for (const auto& ipEntry : list.ipList)
|
||||||
{
|
{
|
||||||
if (ipEntry.full == entry.second.full)
|
if (ipEntry.full == entry.second.full)
|
||||||
{
|
{
|
||||||
@ -36,17 +40,15 @@ namespace Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::InsertBan(Bans::Entry entry)
|
void Bans::InsertBan(const banEntry& entry)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
Bans::BanList list;
|
|
||||||
Bans::LoadBans(&list);
|
|
||||||
|
|
||||||
if (entry.first.bits)
|
if (entry.first.bits)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& idEntry : list.idList)
|
for (const auto& idEntry : list.idList)
|
||||||
{
|
{
|
||||||
if (idEntry.bits == entry.first.bits)
|
if (idEntry.bits == entry.first.bits)
|
||||||
{
|
{
|
||||||
@ -64,7 +66,7 @@ namespace Components
|
|||||||
if (entry.second.full)
|
if (entry.second.full)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& ipEntry : list.ipList)
|
for (const auto& ipEntry : list.ipList)
|
||||||
{
|
{
|
||||||
if (ipEntry.full == entry.second.full)
|
if (ipEntry.full == entry.second.full)
|
||||||
{
|
{
|
||||||
@ -79,12 +81,14 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::SaveBans(&list);
|
SaveBans(&list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::SaveBans(BanList* list)
|
void Bans::SaveBans(const BanList* list)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
assert(list != nullptr);
|
||||||
|
|
||||||
|
const auto _ = Lock();
|
||||||
|
|
||||||
std::vector<std::string> idVector;
|
std::vector<std::string> idVector;
|
||||||
std::vector<std::string> ipVector;
|
std::vector<std::string> ipVector;
|
||||||
@ -109,32 +113,40 @@ namespace Components
|
|||||||
{ "id", idVector },
|
{ "id", idVector },
|
||||||
};
|
};
|
||||||
|
|
||||||
FileSystem::FileWriter ban("bans.json");
|
FileSystem::FileWriter ("bans.json").write(bans.dump());
|
||||||
ban.write(bans.dump());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::LoadBans(Bans::BanList* list)
|
void Bans::LoadBans(BanList* list)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
assert(list != nullptr);
|
||||||
|
|
||||||
|
const auto _ = Lock();
|
||||||
|
|
||||||
FileSystem::File bans("bans.json");
|
FileSystem::File bans("bans.json");
|
||||||
|
|
||||||
if (bans.exists())
|
if (!bans.exists())
|
||||||
{
|
{
|
||||||
|
Logger::Debug("bans.json does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
json11::Json banData = json11::Json::parse(bans.getBuffer(), error);
|
const auto banData = json11::Json::parse(bans.getBuffer(), error);
|
||||||
|
|
||||||
if (!error.empty())
|
if (!error.empty())
|
||||||
{
|
{
|
||||||
Logger::Error(Game::ERR_FATAL, "Failed to parse bans (bans.json): {}", error);
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Failed to parse bans.json: {}\n", error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) return;
|
if (!banData.is_object())
|
||||||
|
|
||||||
if (banData.is_object())
|
|
||||||
{
|
{
|
||||||
auto idList = banData["id"];
|
Logger::Debug("bans.json contains invalid data");
|
||||||
auto ipList = banData["ip"];
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& idList = banData["id"];
|
||||||
|
const auto& ipList = banData["ip"];
|
||||||
|
|
||||||
if (idList.is_array())
|
if (idList.is_array())
|
||||||
{
|
{
|
||||||
@ -163,41 +175,23 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bans::BanClientNum(int num, const std::string& reason)
|
void Bans::BanClient(Game::client_t* cl, const std::string& reason)
|
||||||
{
|
{
|
||||||
if (!Dvar::Var("sv_running").get<bool>())
|
|
||||||
{
|
|
||||||
Logger::Print("Server is not running.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*Game::svs_clientCount <= num)
|
|
||||||
{
|
|
||||||
Logger::Print("Player {} is not on the server\n", num);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::client_t* client = &Game::svs_clients[num];
|
|
||||||
|
|
||||||
SteamID guid;
|
SteamID guid;
|
||||||
guid.bits = client->steamID;
|
guid.bits = cl->steamID;
|
||||||
|
|
||||||
Bans::InsertBan({guid, client->netchan.remoteAddress.ip});
|
InsertBan({guid, cl->netchan.remoteAddress.ip});
|
||||||
|
|
||||||
Game::SV_GameDropClient(num, reason.data());
|
Game::SV_DropClient(cl, reason.data(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::UnbanClient(SteamID id)
|
void Bans::UnbanClient(SteamID id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
|
|
||||||
Bans::BanList list;
|
const auto entry = std::find_if(list.idList.begin(), list.idList.end(), [&id](const SteamID& entry)
|
||||||
Bans::LoadBans(&list);
|
|
||||||
|
|
||||||
auto entry = std::find_if(list.idList.begin(), list.idList.end(), [&id](SteamID& entry)
|
|
||||||
{
|
{
|
||||||
return id.bits == entry.bits;
|
return id.bits == entry.bits;
|
||||||
});
|
});
|
||||||
@ -207,17 +201,15 @@ namespace Components
|
|||||||
list.idList.erase(entry);
|
list.idList.erase(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::SaveBans(&list);
|
SaveBans(&list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::UnbanClient(Game::netIP_t ip)
|
void Bans::UnbanClient(Game::netIP_t ip)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
|
|
||||||
Bans::BanList list;
|
const auto entry = std::find_if(list.ipList.begin(), list.ipList.end(), [&ip](const Game::netIP_t& entry)
|
||||||
Bans::LoadBans(&list);
|
|
||||||
|
|
||||||
auto entry = std::find_if(list.ipList.begin(), list.ipList.end(), [&ip](Game::netIP_t& entry)
|
|
||||||
{
|
{
|
||||||
return ip.full == entry.full;
|
return ip.full == entry.full;
|
||||||
});
|
});
|
||||||
@ -227,31 +219,75 @@ namespace Components
|
|||||||
list.ipList.erase(entry);
|
list.ipList.erase(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::SaveBans(&list);
|
SaveBans(&list);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::Bans()
|
Bans::Bans()
|
||||||
{
|
{
|
||||||
Command::Add("banclient", [](Command::Params* params)
|
Command::Add("banClient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (!Dvar::Var("sv_running").get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Server is not running.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string reason = "EXE_ERR_BANNED_PERM";
|
if (params->size() < 2)
|
||||||
if (params->size() >= 3) reason = params->join(2);
|
{
|
||||||
|
Logger::Print("{} <client number> : permanently ban a client\n", params->get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Bans::BanClientNum(atoi(params->get(1)), reason);
|
const auto* input = params->get(1);
|
||||||
|
|
||||||
|
for (auto i = 0; input[i] != '\0'; ++i)
|
||||||
|
{
|
||||||
|
if (input[i] < '0' || input[i] > '9')
|
||||||
|
{
|
||||||
|
Logger::Print("Bad slot number: {}\n", input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto num = std::atoi(input);
|
||||||
|
|
||||||
|
if (num < 0 || num >= *Game::svs_clientCount)
|
||||||
|
{
|
||||||
|
Logger::Print("Bad client slot: {}\n", num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* cl = &Game::svs_clients[num];
|
||||||
|
if (cl->state == Game::CS_FREE)
|
||||||
|
{
|
||||||
|
Logger::Print("Client {} is not active\n", num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string reason = params->size() < 3 ? "EXE_ERR_BANNED_PERM" : params->join(2);
|
||||||
|
Bans::BanClient(&Game::svs_clients[num], reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
Command::Add("unbanclient", [](Command::Params* params)
|
Command::Add("unbanClient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (!Dvar::Var("sv_running").get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Server is not running.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string type = params->get(1);
|
if (params->size() < 3)
|
||||||
|
{
|
||||||
|
Logger::Print("{} <type> <ip or guid>\n", params->get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* type = params->get(1);
|
||||||
|
|
||||||
if (type == "ip"s)
|
if (type == "ip"s)
|
||||||
{
|
{
|
||||||
Network::Address address(params->get(2));
|
Network::Address address(params->get(2));
|
||||||
Bans::UnbanClient(address.getIP());
|
UnbanClient(address.getIP());
|
||||||
|
|
||||||
Logger::Print("Unbanned IP {}\n", params->get(2));
|
Logger::Print("Unbanned IP {}\n", params->get(2));
|
||||||
|
|
||||||
@ -261,17 +297,10 @@ namespace Components
|
|||||||
SteamID id;
|
SteamID id;
|
||||||
id.bits = strtoull(params->get(2), nullptr, 16);
|
id.bits = strtoull(params->get(2), nullptr, 16);
|
||||||
|
|
||||||
Bans::UnbanClient(id);
|
UnbanClient(id);
|
||||||
|
|
||||||
Logger::Print("Unbanned GUID {}\n", params->get(2));
|
Logger::Print("Unbanned GUID {}\n", params->get(2));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verify the list on startup
|
|
||||||
Scheduler::OnGameInitialized([]
|
|
||||||
{
|
|
||||||
Bans::BanList list;
|
|
||||||
Bans::LoadBans(&list);
|
|
||||||
}, Scheduler::Pipeline::SERVER);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,27 +5,27 @@ namespace Components
|
|||||||
class Bans : public Component
|
class Bans : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::pair<SteamID, Game::netIP_t> Entry;
|
using banEntry = std::pair<SteamID, Game::netIP_t>;
|
||||||
|
|
||||||
Bans();
|
Bans();
|
||||||
|
|
||||||
static void BanClientNum(int num, const std::string& reason);
|
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(SteamID id);
|
||||||
static void UnbanClient(Game::netIP_t ip);
|
static void UnbanClient(Game::netIP_t ip);
|
||||||
|
|
||||||
static bool IsBanned(Entry entry);
|
static bool IsBanned(const banEntry& entry);
|
||||||
static void InsertBan(Entry entry);
|
static void InsertBan(const banEntry& entry);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class BanList
|
struct BanList
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
std::vector<SteamID> idList;
|
std::vector<SteamID> idList;
|
||||||
std::vector<Game::netIP_t> ipList;
|
std::vector<Game::netIP_t> ipList;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::recursive_mutex AccessMutex;
|
|
||||||
static void LoadBans(BanList* list);
|
static void LoadBans(BanList* list);
|
||||||
static void SaveBans(BanList* list);
|
static void SaveBans(const BanList* list);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Game/Functions.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -129,6 +129,7 @@ using namespace std::literals;
|
|||||||
#include "Utils/Json.hpp"
|
#include "Utils/Json.hpp"
|
||||||
#include "Utils/Library.hpp"
|
#include "Utils/Library.hpp"
|
||||||
#include "Utils/Maths.hpp"
|
#include "Utils/Maths.hpp"
|
||||||
|
#include "Utils/NamedMutex.hpp"
|
||||||
#include "Utils/String.hpp"
|
#include "Utils/String.hpp"
|
||||||
#include "Utils/Thread.hpp"
|
#include "Utils/Thread.hpp"
|
||||||
#include "Utils/Time.hpp"
|
#include "Utils/Time.hpp"
|
||||||
|
43
src/Utils/NamedMutex.cpp
Normal file
43
src/Utils/NamedMutex.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
namespace Utils
|
||||||
|
{
|
||||||
|
NamedMutex::NamedMutex(const std::string& name)
|
||||||
|
{
|
||||||
|
this->handle_ = CreateMutexA(nullptr, FALSE, name.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
NamedMutex::~NamedMutex()
|
||||||
|
{
|
||||||
|
if (this->handle_)
|
||||||
|
{
|
||||||
|
CloseHandle(this->handle_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NamedMutex::lock() const
|
||||||
|
{
|
||||||
|
if (this->handle_)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(this->handle_, INFINITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NamedMutex::try_lock(const std::chrono::milliseconds timeout) const
|
||||||
|
{
|
||||||
|
if (this->handle_)
|
||||||
|
{
|
||||||
|
return WAIT_OBJECT_0 == WaitForSingleObject(this->handle_, static_cast<DWORD>(timeout.count()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NamedMutex::unlock() const noexcept
|
||||||
|
{
|
||||||
|
if (this->handle_)
|
||||||
|
{
|
||||||
|
ReleaseMutex(this->handle_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
src/Utils/NamedMutex.hpp
Normal file
24
src/Utils/NamedMutex.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Utils
|
||||||
|
{
|
||||||
|
class NamedMutex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NamedMutex(const std::string& name);
|
||||||
|
~NamedMutex();
|
||||||
|
|
||||||
|
NamedMutex(NamedMutex&&) = delete;
|
||||||
|
NamedMutex(const NamedMutex&) = delete;
|
||||||
|
NamedMutex& operator=(NamedMutex&&) = delete;
|
||||||
|
NamedMutex& operator=(const NamedMutex&) = delete;
|
||||||
|
|
||||||
|
void lock() const;
|
||||||
|
// Lockable requirements
|
||||||
|
[[nodiscard]] bool try_lock(std::chrono::milliseconds timeout = std::chrono::milliseconds{0}) const;
|
||||||
|
void unlock() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* handle_{};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user