feature: /serverlist TCP endpoint
This commit is contained in:
parent
b7aba4e978
commit
a6864ff725
17
.github/workflows/discord-notify.yml
vendored
17
.github/workflows/discord-notify.yml
vendored
@ -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 }}
|
|
48
.github/workflows/draft-new-release.yml
vendored
48
.github/workflows/draft-new-release.yml
vendored
@ -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
|
|
83
.github/workflows/release.yml
vendored
83
.github/workflows/release.yml
vendored
@ -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"
|
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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) };
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user