feature: /serverlist TCP endpoint

This commit is contained in:
Diavolo 2023-06-16 21:56:18 +02:00
parent b7aba4e978
commit a6864ff725
No known key found for this signature in database
GPG Key ID: FA77F074E98D98A5
7 changed files with 153 additions and 262 deletions

View File

@ -1,17 +0,0 @@
name: Notify Discord
on:
push:
branches:
- "*"
issues:
jobs:
notify:
runs-on: ubuntu-latest
if: github.repository_owner == 'XLabsProject'
steps:
- name: Send notification to Discord
uses: Ilshidur/action-discord@master
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_CI_BOT_WEBHOOK }}

View File

@ -1,48 +0,0 @@
name: "Draft new release"
on:
workflow_dispatch:
inputs:
version:
description: "The version you want to release."
required: true
jobs:
draft-new-release:
name: "Draft a new release"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.5.2
- name: Normalize version
id: normalize_version
run: |
version="${{ github.event.inputs.version }}"
version="r${version#v}"
echo "::set-output name=version::$version"
# Set up committer info and GPG key
- name: Import GPG key
id: import_gpg
uses: XLabsProject/ghaction-import-gpg@25d9d6ab99eb355c169c33c2306a72df85d9f516
with:
git-commit-gpgsign: true
git-committer-email: "${{ secrets.XLABS_CI_EMAIL }}"
git-committer-name: "${{ secrets.XLABS_CI_NAME }}"
# git-push-gpgsign: true
git-tag-gpgsign: true
git-user-signingkey: true
gpg-private-key: ${{ secrets.XLABS_CI_GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.XLABS_CI_GPG_PASSWORD }}
- name: Create Pull Request
uses: repo-sync/pull-request@v2
with:
github_token: ${{ secrets.XLABS_CI_GITHUB_TOKEN }}
source_branch: "develop"
destination_branch: "master"
pr_allow_empty: true
pr_body: |
This Pull Request is for the release of IW4x ${{ steps.normalize_version.outputs.version }} and was [automatically created by a workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) triggered by @${{ github.actor }}.
pr_title: Release ${{ steps.normalize_version.outputs.version }}
pr_label: release

View File

@ -1,83 +0,0 @@
name: Release
on:
pull_request:
branches:
- "master"
types: [closed]
jobs:
merge:
runs-on: ubuntu-latest
name: Merge Release
steps:
- name: Check out files
if: github.event.pull_request.merged
uses: actions/checkout@v3.5.2
with:
submodules: false
lfs: false
# Set up committer info and GPG key
- name: Import GPG key
if: github.event.pull_request.merged
id: import_gpg
uses: XLabsProject/ghaction-import-gpg@25d9d6ab99eb355c169c33c2306a72df85d9f516
with:
git-commit-gpgsign: true
git-committer-email: "${{ secrets.XLABS_CI_EMAIL }}"
git-committer-name: "${{ secrets.XLABS_CI_NAME }}"
git-push-gpgsign: false
git-tag-gpgsign: true
git-user-signingkey: true
gpg-private-key: ${{ secrets.XLABS_CI_GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.XLABS_CI_GPG_PASSWORD }}
- name: Extract version from pull request
if: github.event.pull_request.merged
id: extract_version
run: |
title="${{ github.event.pull_request.title }}"
version="${title#Release }"
echo "::set-output name=version::$version"
- name: Create annotated tag
if: github.event.pull_request.merged
run: |
git tag -a -m "${{ github.event.pull_request.title }}" \
"${{ steps.extract_version.outputs.version }}" \
"${{ github.event.pull_request.merge_commit_sha }}"
git push origin --tags
- name: Create Pull Request
if: github.event.pull_request.merged
uses: repo-sync/pull-request@v2
with:
github_token: ${{ secrets.XLABS_CI_GITHUB_TOKEN }}
source_branch: "master"
destination_branch: "develop"
pr_allow_empty: true
pr_body: |
This Pull Request merges the release of IW4x ${{ steps.extract_version.outputs.version }} and was [automatically created by a workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) triggered by @${{ github.actor }}.
pr_title: Merge release ${{ steps.extract_version.outputs.version }}
pr_label: release
notify:
name: Notify Discord
runs-on: ubuntu-latest
if: |
github.repository_owner == 'XLabsProject' && (
(
github.event.pull_request.merged
) || (
github.event.push.ref == 'refs/heads/master' ||
github.event.push.ref == 'refs/heads/develop'
)
)
steps:
- name: Post CI status notification to Discord
uses: sarisia/actions-status-discord@v1.7.1
if: always()
with:
webhook: ${{ secrets.DISCORD_CI_BOT_WEBHOOK }}
title: "Build"

View File

@ -5,6 +5,7 @@
#include "Download.hpp" #include "Download.hpp"
#include "Events.hpp" #include "Events.hpp"
#include "MapRotation.hpp" #include "MapRotation.hpp"
#include "Node.hpp"
#include "Party.hpp" #include "Party.hpp"
#include "ServerInfo.hpp" #include "ServerInfo.hpp"
@ -40,7 +41,7 @@ namespace Components
void Download::InitiateClientDownload(const std::string& mod, bool needPassword, bool map) void Download::InitiateClientDownload(const std::string& mod, bool needPassword, bool map)
{ {
if (CLDownload.running) return; if (CLDownload.running_) return;
Scheduler::Once([] Scheduler::Once([]
{ {
@ -61,26 +62,26 @@ namespace Components
return; return;
} }
CLDownload.hashedPassword = Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(password), ""); CLDownload.hashedPassword_ = Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(password), "");
} }
CLDownload.running = true; CLDownload.running_ = true;
CLDownload.isMap = map; CLDownload.isMap_ = map;
CLDownload.mod = mod; CLDownload.mod_ = mod;
CLDownload.terminateThread = false; CLDownload.terminateThread_ = false;
CLDownload.totalBytes = 0; CLDownload.totalBytes_ = 0;
CLDownload.lastTimeStamp = 0; CLDownload.lastTimeStamp_ = 0;
CLDownload.downBytes = 0; CLDownload.downBytes_ = 0;
CLDownload.timeStampBytes = 0; CLDownload.timeStampBytes_ = 0;
CLDownload.isPrivate = needPassword; CLDownload.isPrivate_ = needPassword;
CLDownload.target = Party::Target(); CLDownload.target_ = Party::Target();
CLDownload.thread = std::thread(ModDownloader, &CLDownload); CLDownload.thread_ = std::thread(ModDownloader, &CLDownload);
} }
bool Download::ParseModList(ClientDownload* download, const std::string& list) bool Download::ParseModList(ClientDownload* download, const std::string& list)
{ {
if (!download) return false; if (!download) return false;
download->files.clear(); download->files_.clear();
nlohmann::json listData; nlohmann::json listData;
try try
@ -98,7 +99,7 @@ namespace Components
return false; return false;
} }
download->totalBytes = 0; download->totalBytes_ = 0;
const nlohmann::json::array_t listDataArray = listData; const nlohmann::json::array_t listDataArray = listData;
for (auto& file : listDataArray) for (auto& file : listDataArray)
@ -118,8 +119,8 @@ namespace Components
if (!fileEntry.name.empty()) if (!fileEntry.name.empty())
{ {
download->files.push_back(fileEntry); download->files_.push_back(fileEntry);
download->totalBytes += fileEntry.size; download->totalBytes_ += fileEntry.size;
} }
} }
catch (const nlohmann::json::exception& ex) catch (const nlohmann::json::exception& ex)
@ -134,12 +135,12 @@ namespace Components
bool Download::DownloadFile(ClientDownload* download, unsigned int index) bool Download::DownloadFile(ClientDownload* download, unsigned int index)
{ {
if (!download || download->files.size() <= index) return false; if (!download || download->files_.size() <= index) return false;
auto file = download->files[index]; auto file = download->files_[index];
auto path = download->mod + "/" + file.name; auto path = download->mod_ + "/" + file.name;
if (download->isMap) if (download->isMap_)
{ {
path = "usermaps/" + path; path = "usermaps/" + path;
} }
@ -149,16 +150,16 @@ namespace Components
auto data = Utils::IO::ReadFile(path); auto data = Utils::IO::ReadFile(path);
if (data.size() == file.size && Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(data), "") == file.hash) if (data.size() == file.size && Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(data), "") == file.hash)
{ {
download->totalBytes += file.size; download->totalBytes_ += file.size;
return true; return true;
} }
} }
auto host = "http://" + download->target.getString(); auto host = "http://" + download->target_.getString();
auto fastHost = SV_wwwBaseUrl.get<std::string>(); auto fastHost = SV_wwwBaseUrl.get<std::string>();
if (Utils::String::StartsWith(fastHost, "https://")) if (Utils::String::StartsWith(fastHost, "https://"))
{ {
download->thread.detach(); download->thread_.detach();
download->clear(); download->clear();
Scheduler::Once([] Scheduler::Once([]
@ -197,8 +198,8 @@ namespace Components
} }
else else
{ {
url = host + "/file/" + (download->isMap ? "map/" : "") + file.name url = host + "/file/" + (download->isMap_ ? "map/" : "") + file.name
+ (download->isPrivate ? ("?password=" + download->hashedPassword) : ""); + (download->isPrivate_ ? ("?password=" + download->hashedPassword_) : "");
} }
Logger::Print("Downloading from url {}\n", url); Logger::Print("Downloading from url {}\n", url);
@ -212,14 +213,14 @@ namespace Components
Utils::String::Replace(url, " ", "%20"); Utils::String::Replace(url, " ", "%20");
download->valid = true; download->valid_ = true;
fDownload.downloading = true; fDownload.downloading = true;
Utils::WebIO webIO; Utils::WebIO webIO;
webIO.setProgressCallback([&fDownload, &webIO](std::size_t bytes, std::size_t) webIO.setProgressCallback([&fDownload, &webIO](std::size_t bytes, std::size_t)
{ {
if(!fDownload.downloading || fDownload.download->terminateThread) if(!fDownload.downloading || fDownload.download->terminateThread_)
{ {
webIO.cancelDownload(); webIO.cancelDownload();
return; return;
@ -234,14 +235,14 @@ namespace Components
fDownload.downloading = false; fDownload.downloading = false;
download->valid = false; download->valid_ = false;
if (fDownload.buffer.size() != file.size || Utils::Cryptography::SHA256::Compute(fDownload.buffer, true) != file.hash) if (fDownload.buffer.size() != file.size || Utils::Cryptography::SHA256::Compute(fDownload.buffer, true) != file.hash)
{ {
return false; return false;
} }
if (download->isMap) Utils::IO::CreateDir("usermaps/" + download->mod); if (download->isMap_) Utils::IO::CreateDir("usermaps/" + download->mod_);
Utils::IO::WriteFile(path, fDownload.buffer); Utils::IO::WriteFile(path, fDownload.buffer);
return true; return true;
@ -251,16 +252,16 @@ namespace Components
{ {
if (!download) download = &CLDownload; if (!download) download = &CLDownload;
const auto host = "http://" + download->target.getString(); const auto host = "http://" + download->target_.getString();
const auto listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : ""); const auto listUrl = host + (download->isMap_ ? "/map" : "/list") + (download->isPrivate_ ? ("?password=" + download->hashedPassword_) : "");
const auto list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get(); const auto list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get();
if (list.empty()) if (list.empty())
{ {
if (download->terminateThread) return; if (download->terminateThread_) return;
download->thread.detach(); download->thread_.detach();
download->clear(); download->clear();
Scheduler::Once([] Scheduler::Once([]
@ -272,13 +273,13 @@ namespace Components
return; return;
} }
if (download->terminateThread) return; if (download->terminateThread_) return;
if (!ParseModList(download, list)) if (!ParseModList(download, list))
{ {
if (download->terminateThread) return; if (download->terminateThread_) return;
download->thread.detach(); download->thread_.detach();
download->clear(); download->clear();
Scheduler::Once([] Scheduler::Once([]
@ -290,21 +291,21 @@ namespace Components
return; return;
} }
if (download->terminateThread) return; if (download->terminateThread_) return;
static std::string mod; static std::string mod;
mod = download->mod; mod = download->mod_;
for (std::size_t i = 0; i < download->files.size(); ++i) for (std::size_t i = 0; i < download->files_.size(); ++i)
{ {
if (download->terminateThread) return; if (download->terminateThread_) return;
if (!DownloadFile(download, i)) if (!DownloadFile(download, i))
{ {
if (download->terminateThread) return; if (download->terminateThread_) return;
mod = std::format("Failed to download file: {}!", download->files[i].name); mod = std::format("Failed to download file: {}!", download->files_[i].name);
download->thread.detach(); download->thread_.detach();
download->clear(); download->clear();
Scheduler::Once([] Scheduler::Once([]
@ -320,12 +321,12 @@ namespace Components
} }
} }
if (download->terminateThread) return; if (download->terminateThread_) return;
download->thread.detach(); download->thread_.detach();
download->clear(); download->clear();
if (download->isMap) if (download->isMap_)
{ {
Scheduler::Once([] Scheduler::Once([]
{ {
@ -357,22 +358,22 @@ namespace Components
void Download::DownloadProgress(FileDownload* fDownload, std::size_t bytes) void Download::DownloadProgress(FileDownload* fDownload, std::size_t bytes)
{ {
fDownload->receivedBytes += bytes; fDownload->receivedBytes += bytes;
fDownload->download->downBytes += bytes; fDownload->download->downBytes_ += bytes;
fDownload->download->timeStampBytes += bytes; fDownload->download->timeStampBytes_ += bytes;
static volatile bool framePushed = false; static volatile bool framePushed = false;
if (!framePushed) if (!framePushed)
{ {
double progress = 0; double progress = 0;
if (fDownload->download->totalBytes) if (fDownload->download->totalBytes_)
{ {
progress = (100.0 / fDownload->download->totalBytes) * fDownload->download->downBytes; progress = (100.0 / fDownload->download->totalBytes_) * fDownload->download->downBytes_;
} }
static std::uint32_t dlIndex, dlSize, dlProgress; static std::uint32_t dlIndex, dlSize, dlProgress;
dlIndex = fDownload->index + 1; dlIndex = fDownload->index + 1;
dlSize = fDownload->download->files.size(); dlSize = fDownload->download->files_.size();
dlProgress = static_cast<std::uint32_t>(progress); dlProgress = static_cast<std::uint32_t>(progress);
framePushed = true; framePushed = true;
@ -383,18 +384,18 @@ namespace Components
}, Scheduler::Pipeline::MAIN); }, Scheduler::Pipeline::MAIN);
} }
auto delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp; auto delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp_;
if (delta > 300) if (delta > 300)
{ {
const auto doFormat = fDownload->download->lastTimeStamp != 0; const auto doFormat = fDownload->download->lastTimeStamp_ != 0;
fDownload->download->lastTimeStamp = Game::Sys_Milliseconds(); fDownload->download->lastTimeStamp_ = Game::Sys_Milliseconds();
const auto dataLeft = fDownload->download->totalBytes - fDownload->download->downBytes; const auto dataLeft = fDownload->download->totalBytes_ - fDownload->download->downBytes_;
int timeLeft = 0; int timeLeft = 0;
if (fDownload->download->timeStampBytes) if (fDownload->download->timeStampBytes_)
{ {
const double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes) * delta; const double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes_) * delta;
timeLeft = static_cast<int>(timeLeftD); timeLeft = static_cast<int>(timeLeftD);
} }
@ -404,7 +405,7 @@ namespace Components
static int dlDelta, dlTimeLeft; static int dlDelta, dlTimeLeft;
dlTimeLeft = timeLeft; dlTimeLeft = timeLeft;
dlDelta = delta; dlDelta = delta;
dlTsBytes = fDownload->download->timeStampBytes; dlTsBytes = fDownload->download->timeStampBytes_;
Scheduler::Once([] Scheduler::Once([]
{ {
@ -413,7 +414,7 @@ namespace Components
}, Scheduler::Pipeline::MAIN); }, Scheduler::Pipeline::MAIN);
} }
fDownload->download->timeStampBytes = 0; fDownload->download->timeStampBytes_ = 0;
} }
} }
@ -434,7 +435,7 @@ namespace Components
MongooseLogBuffer.push_back(c); MongooseLogBuffer.push_back(c);
} }
static std::string InfoHandler() static std::optional<std::string> InfoHandler([[maybe_unused]] mg_connection* c, [[maybe_unused]] const mg_http_message* hm)
{ {
const auto status = ServerInfo::GetInfo(); const auto status = ServerInfo::GetInfo();
const auto host = ServerInfo::GetHostInfo(); const auto host = ServerInfo::GetHostInfo();
@ -480,10 +481,12 @@ namespace Components
} }
info["players"] = players; info["players"] = players;
return nlohmann::json(info).dump(); std::string out = nlohmann::json(info).dump();
return { out };
} }
static std::string ListHandler() static std::optional<std::string> ListHandler([[maybe_unused]] mg_connection* c, [[maybe_unused]] const mg_http_message* hm)
{ {
static nlohmann::json jsonList; static nlohmann::json jsonList;
static std::filesystem::path fsGamePre; static std::filesystem::path fsGamePre;
@ -526,10 +529,12 @@ namespace Components
jsonList = fileList; jsonList = fileList;
} }
return jsonList.dump(); std::string out = jsonList.dump();
return { out };
} }
static std::string MapHandler() static std::optional<std::string> MapHandler([[maybe_unused]] mg_connection* c, [[maybe_unused]] const mg_http_message* hm)
{ {
static std::string mapNamePre; static std::string mapNamePre;
static nlohmann::json jsonList; static nlohmann::json jsonList;
@ -570,10 +575,12 @@ namespace Components
jsonList = fileList; jsonList = fileList;
} }
return jsonList.dump(); std::string out = jsonList.dump();
return { out };
} }
static void FileHandler(mg_connection* c, const mg_http_message* hm) static std::optional<std::string> FileHandler(mg_connection* c, const mg_http_message* hm)
{ {
std::string url(hm->uri.ptr, hm->uri.len); std::string url(hm->uri.ptr, hm->uri.len);
@ -602,7 +609,7 @@ namespace Components
if ((!Maps::GetUserMap()->isValid() && !Party::IsInUserMapLobby()) || !isValidFile) if ((!Maps::GetUserMap()->isValid() && !Party::IsInUserMapLobby()) || !isValidFile)
{ {
mg_http_reply(c, 403, "Content-Type: text/html\r\n", "%s", "403 - Forbidden"); mg_http_reply(c, 403, "Content-Type: text/html\r\n", "%s", "403 - Forbidden");
return; return {};
} }
url = std::format("usermaps\\{}\\{}", mapName, url); url = std::format("usermaps\\{}\\{}", mapName, url);
@ -612,7 +619,7 @@ namespace Components
if ((!url.ends_with(".iwd") && url != "mod.ff") || url.find("_svr_") != std::string::npos) if ((!url.ends_with(".iwd") && url != "mod.ff") || url.find("_svr_") != std::string::npos)
{ {
mg_http_reply(c, 403, "Content-Type: text/html\r\n", "%s", "403 - Forbidden"); mg_http_reply(c, 403, "Content-Type: text/html\r\n", "%s", "403 - Forbidden");
return; return {};
} }
} }
@ -633,10 +640,44 @@ namespace Components
mg_printf(c, "%s", "\r\n"); mg_printf(c, "%s", "\r\n");
mg_send(c, file.data(), file.size()); mg_send(c, file.data(), file.size());
} }
return {};
}
static std::optional<std::string> ServerListHandler([[maybe_unused]] mg_connection* c, [[maybe_unused]] const mg_http_message* hm)
{
std::vector<std::string> servers;
const auto nodes = Node::GetNodes();
for (const auto& node : nodes)
{
const auto address = node.address.getString();
servers.emplace_back(address);
}
nlohmann::json jsonList = servers;
std::string out = jsonList.dump();
return { out };
} }
static void EventHandler(mg_connection* c, const int ev, void* ev_data, [[maybe_unused]] void* fn_data) static void EventHandler(mg_connection* c, const int ev, void* ev_data, [[maybe_unused]] void* fn_data)
{ {
using callback = std::function<std::optional<std::string>(mg_connection*, const mg_http_message*)>;
static const auto handlers = []() -> std::unordered_map<std::string, callback>
{
std::unordered_map<std::string, callback> f;
f["/file"] = FileHandler;
f["/info"] = InfoHandler;
f["/list"] = ListHandler;
f["/map"] = MapHandler;
f["/serverlist"] = ServerListHandler;
return f;
}();
if (ev != MG_EV_HTTP_MSG) if (ev != MG_EV_HTTP_MSG)
{ {
return; return;
@ -645,26 +686,24 @@ namespace Components
auto* hm = static_cast<mg_http_message*>(ev_data); auto* hm = static_cast<mg_http_message*>(ev_data);
const std::string url(hm->uri.ptr, hm->uri.len); const std::string url(hm->uri.ptr, hm->uri.len);
if (url.starts_with("/info")) auto handled = false;
for (auto i = handlers.begin(); i != handlers.end();)
{ {
const auto reply = InfoHandler(); if (url.starts_with(i->first))
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", reply.data()); {
if (const auto reply = i->second(c, hm))
{
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", reply.value().data());
}
handled = true;
break;
}
++i;
} }
else if (url.starts_with("/list"))
{ if (!handled)
const auto reply = ListHandler();
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", reply.data());
}
else if (url.starts_with("/map"))
{
const auto reply = MapHandler();
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s", reply.data());
}
else if (url.starts_with("/file"))
{
FileHandler(c, hm);
}
else
{ {
mg_http_serve_opts opts = { .root_dir = "iw4x/html" }; // Serve local dir mg_http_serve_opts opts = { .root_dir = "iw4x/html" }; // Serve local dir
mg_http_serve_dir(c, hm, &opts); mg_http_serve_dir(c, hm, &opts);

View File

@ -24,24 +24,24 @@ namespace Components
class ClientDownload class ClientDownload
{ {
public: public:
ClientDownload(bool _isMap = false) : running(false), valid(false), terminateThread(false), isMap(_isMap), totalBytes(0), downBytes(0), lastTimeStamp(0), timeStampBytes(0) {} ClientDownload(bool isMap = false) : running_(false), valid_(false), terminateThread_(false), isMap_(isMap), totalBytes_(0), downBytes_(0), lastTimeStamp_(0), timeStampBytes_(0) {}
~ClientDownload() { this->clear(); } ~ClientDownload() { this->clear(); }
bool running; bool running_;
bool valid; bool valid_;
bool terminateThread; bool terminateThread_;
bool isMap; bool isMap_;
bool isPrivate; bool isPrivate_;
Network::Address target; Network::Address target_;
std::string hashedPassword; std::string hashedPassword_;
std::string mod; std::string mod_;
std::thread thread; std::thread thread_;
std::size_t totalBytes; std::size_t totalBytes_;
std::size_t downBytes; std::size_t downBytes_;
int lastTimeStamp; int lastTimeStamp_;
std::size_t timeStampBytes; std::size_t timeStampBytes_;
class File class File
{ {
@ -51,24 +51,24 @@ namespace Components
std::size_t size; std::size_t size;
}; };
std::vector<File> files; std::vector<File> files_;
void clear() void clear()
{ {
this->terminateThread = true; this->terminateThread_ = true;
if (this->thread.joinable()) if (this->thread_.joinable())
{ {
this->thread.join(); this->thread_.join();
} }
this->running = false; this->running_ = false;
this->mod.clear(); this->mod_.clear();
this->files.clear(); this->files_.clear();
if (this->valid) if (this->valid_)
{ {
this->valid = false; this->valid_ = false;
} }
} }
}; };

View File

@ -353,18 +353,18 @@ namespace Components
Scheduler::OnGameInitialized(loadNodes, Scheduler::Pipeline::MAIN); Scheduler::OnGameInitialized(loadNodes, Scheduler::Pipeline::MAIN);
Command::Add("listnodes", [](const Command::Params*) Command::Add("listNodes", [](const Command::Params*)
{ {
Logger::Print("Nodes: {}\n", Node::Nodes.size()); Logger::Print("Nodes: {}\n", Node::Nodes.size());
std::lock_guard _(Node::Mutex); std::lock_guard _(Node::Mutex);
for (auto& node : Node::Nodes) for (const auto& node : Node::Nodes)
{ {
Logger::Print("{}\t({})\n", node.address.getString(), node.isValid() ? "Valid" : "Invalid"); Logger::Print("{}\t({})\n", node.address.getString(), node.isValid() ? "Valid" : "Invalid");
} }
}); });
Command::Add("addnode", [](const Command::Params* params) Command::Add("addNode", [](const Command::Params* params)
{ {
if (params->size() < 2) return; if (params->size() < 2) return;
auto address = Network::Address{ params->get(1) }; auto address = Network::Address{ params->get(1) };

View File

@ -29,7 +29,7 @@ namespace Components
Dvar::Var ServerList::NETServerQueryLimit; Dvar::Var ServerList::NETServerQueryLimit;
Dvar::Var ServerList::NETServerFrames; Dvar::Var ServerList::NETServerFrames;
bool ServerList::UseMasterServer = true; bool ServerList::UseMasterServer = false;
std::vector<ServerList::ServerInfo>* ServerList::GetList() std::vector<ServerList::ServerInfo>* ServerList::GetList()
{ {