mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 20:57:08 +02:00
The bad-word list now comes from the ReSkate backend's Words page, in the same /api/v1/steam-ids answer the game and servers already read. It has two lists: filtered words, and words not allowed in chat at all. Until the backend has any words the built-in list stays in use; once it does, its lists replace the built-in one. Filtered words work as before: masked for players with the chat filter on, and not allowed in server or lobby names. Words not allowed at all are filtered the same way, and a chat message with one is not passed on. Its player is warned each time, and after three warnings the next one gets them kicked. Dedicated servers: new players setting word_warnings (default 3, 1 to 10, 0 switches the rule off). The count lasts while the server runs, so a kicked player who joins again is kicked at their next one. Party chat and commands are checked too, and each warning and kick is logged under [words]. The lists are read whenever global bans or word warnings are on. Player lobbies: the host's game applies the same rule to its guests, with three warnings.
81 lines
3.2 KiB
C++
81 lines
3.2 KiB
C++
#include "global_bans.h"
|
|
#include "Extension/Multiplayer/developer_identity.h"
|
|
#include "Extension/Multiplayer/word_lists.h"
|
|
#ifdef _WIN32
|
|
#include "Engine/Vfs/https_download.h"
|
|
#else
|
|
#include <cstdio>
|
|
#endif
|
|
#include <algorithm>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
namespace dingosdk::server {
|
|
namespace {
|
|
constexpr std::size_t max_bytes = 256 * 1024;
|
|
|
|
#ifdef _WIN32
|
|
std::string download() {
|
|
const auto url = multiplayer::identity_lists_url;
|
|
https::Download result;
|
|
auto answer = https::get_text(std::wstring(url.begin(), url.end()), max_bytes, 15, L"ReSkateServer/1", &result);
|
|
if (!answer) throw std::runtime_error("HTTP " + std::to_string(result.http_status) + ", error " + std::to_string(result.error));
|
|
return std::move(*answer);
|
|
}
|
|
#else
|
|
// curl does the TLS: the Linux server has no HTTPS client of its own (server_update.cpp).
|
|
// The command is fixed text, with nothing from a player or the config in it.
|
|
std::string download() {
|
|
const auto command = "curl --silent --show-error --fail --max-time 15 --max-filesize " + std::to_string(max_bytes) +
|
|
" --proto =https --user-agent ReSkateServer/1 " + std::string(multiplayer::identity_lists_url) + " 2>&1";
|
|
auto *pipe = popen(command.c_str(), "r");
|
|
if (!pipe) throw std::runtime_error("curl could not be started");
|
|
std::string output;
|
|
char buffer[4096];
|
|
for (std::size_t count; (count = fread(buffer, 1, sizeof(buffer), pipe)) > 0;)
|
|
if (output.size() < max_bytes) output.append(buffer, count);
|
|
if (pclose(pipe) != 0) {
|
|
// curl's own words (or the shell's, when curl is not installed), on one line.
|
|
while (!output.empty() && (output.back() == '\n' || output.back() == '\r')) output.pop_back();
|
|
std::replace(output.begin(), output.end(), '\n', ' ');
|
|
throw std::runtime_error(output.empty() ? "curl failed" : output);
|
|
}
|
|
return output;
|
|
}
|
|
#endif
|
|
} // namespace
|
|
|
|
BanListCheck use_ban_list(std::string_view answer) {
|
|
static std::optional<std::vector<std::uint64_t>> last;
|
|
BanListCheck check;
|
|
try {
|
|
auto lists = multiplayer::parse_identity_lists(answer);
|
|
// (Read before anything is put in use: an answer that is wrong anywhere changes nothing.)
|
|
const auto words = multiplayer::use_word_lists(answer);
|
|
multiplayer::server_tokens_rule = multiplayer::parse_server_tokens_required(answer);
|
|
const auto &bans = lists[static_cast<std::size_t>(multiplayer::IdentityList::banned)];
|
|
check.banned = bans.size();
|
|
check.changed = last != bans;
|
|
last = bans;
|
|
multiplayer::publish_identity_lists(std::move(lists));
|
|
check.words_changed = words.changed;
|
|
check.filtered_words = words.filtered;
|
|
check.forbidden_words = words.forbidden;
|
|
check.ok = true;
|
|
} catch (const std::exception &e) {
|
|
check.problem = std::string("the answer was not the lists: ") + e.what();
|
|
}
|
|
return check;
|
|
}
|
|
BanListCheck read_global_bans() {
|
|
try {
|
|
return use_ban_list(download());
|
|
} catch (const std::exception &e) {
|
|
BanListCheck check;
|
|
check.problem = e.what();
|
|
return check;
|
|
}
|
|
}
|
|
} // namespace dingosdk::server
|