commit
4812e4da9f
2
.github/ISSUE_TEMPLATE/get-help.md
vendored
2
.github/ISSUE_TEMPLATE/get-help.md
vendored
@ -24,7 +24,7 @@ If IW4x is crashing, include the minidump file and the crash address in text for
|
|||||||
Describe any steps you've already taken to try to get past this issue. Have you found a workaround?
|
Describe any steps you've already taken to try to get past this issue. Have you found a workaround?
|
||||||
|
|
||||||
**What version of IW4x are you using?**
|
**What version of IW4x are you using?**
|
||||||
`iw4x.exe -version` will show you the version. Please make sure you are up to date with the latest master branch.
|
`iw4x.exe -version` will show you the version. Please make sure you are up to date with the latest build from the master branch.
|
||||||
|
|
||||||
**Anything else we should know?**
|
**Anything else we should know?**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
7
.gitmodules
vendored
7
.gitmodules
vendored
@ -2,10 +2,6 @@
|
|||||||
path = deps/zlib
|
path = deps/zlib
|
||||||
url = https://github.com/madler/zlib.git
|
url = https://github.com/madler/zlib.git
|
||||||
branch = develop
|
branch = develop
|
||||||
[submodule "deps/json11"]
|
|
||||||
path = deps/json11
|
|
||||||
url = https://github.com/dropbox/json11.git
|
|
||||||
branch = master
|
|
||||||
[submodule "deps/libtommath"]
|
[submodule "deps/libtommath"]
|
||||||
path = deps/libtommath
|
path = deps/libtommath
|
||||||
url = https://github.com/libtom/libtommath.git
|
url = https://github.com/libtom/libtommath.git
|
||||||
@ -35,3 +31,6 @@
|
|||||||
[submodule "deps/GSL"]
|
[submodule "deps/GSL"]
|
||||||
path = deps/GSL
|
path = deps/GSL
|
||||||
url = https://github.com/microsoft/GSL.git
|
url = https://github.com/microsoft/GSL.git
|
||||||
|
[submodule "deps/nlohmannjson"]
|
||||||
|
path = deps/nlohmannjson
|
||||||
|
url = https://github.com/nlohmann/json.git
|
||||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -4,6 +4,37 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [0.7.5] - 2022-09-03
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `bg_rocketJumpScale` Dvar (#413)
|
||||||
|
- Add `CastFloat` GSC function (#414)
|
||||||
|
- Add `Strtol` GSC function (#414)
|
||||||
|
- Add `bg_lean` Dvar (#421
|
||||||
|
- Add voice chat (#425)
|
||||||
|
- Add `vote` & `callvote` client commands (#447)
|
||||||
|
- Add `kill` client command (#451)
|
||||||
|
- Add `voteKick`, `voteTempBan`, `voteTypeMap`, `voteMap` and `voteGame` UI script tokens (#456)
|
||||||
|
- Add `Int64IsInt`, `Int64ToInt` and `Int64OP` GSC functions (#419)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Steam status is no longer set to busy (#417)
|
||||||
|
- `HttpGet`& `HttpCancel` are disabled for security reasons (#449)
|
||||||
|
- 'g_allowVote' is a replicated Dvar (#457)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed `startSingleplayer` command (#404)
|
||||||
|
- General stability update
|
||||||
|
|
||||||
|
### Known issues
|
||||||
|
|
||||||
|
- HTTPS is not supported for fast downloads at the moment.
|
||||||
|
- Sound issue fix is experimental as the bug is not fully understood.
|
||||||
|
- `reloadmenus` command does not free resources used by custom menus.
|
||||||
|
|
||||||
## [0.7.4] - 2022-07-28
|
## [0.7.4] - 2022-07-28
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@ -187,7 +218,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
|
|||||||
- Fixed slow motion during final killcams (#111 - #107)
|
- Fixed slow motion during final killcams (#111 - #107)
|
||||||
- Fixed sound issue that causes the game to freeze (#106)
|
- Fixed sound issue that causes the game to freeze (#106)
|
||||||
- Fixed issue where materials strings found in hostnames, player names, chat etc. caused the game to crash (#113)
|
- Fixed issue where materials strings found in hostnames, player names, chat etc. caused the game to crash (#113)
|
||||||
- Fixed issue with servers displaying an invalid player count (#144)
|
- Fixed issue with servers displaying an invalid player count (#113)
|
||||||
|
|
||||||
### Known issues
|
### Known issues
|
||||||
|
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
# IW4x: Client
|
# IW4x: Client
|
||||||
|
|
||||||
## How to compile
|
## Compile from source
|
||||||
|
|
||||||
- Run `premake5 vs2022` or use the delivered `generate.bat`.
|
- Clone the Git repo. Do NOT download it as ZIP, that won't work.
|
||||||
|
- Update the submodules and run `premake5 vs2022` or simply use the delivered `generate.bat`.
|
||||||
- Build via solution file in `build\iw4x.sln`.
|
- Build via solution file in `build\iw4x.sln`.
|
||||||
|
|
||||||
## Premake arguments
|
## Premake arguments
|
||||||
@ -22,7 +23,6 @@
|
|||||||
| `--force-unit-tests` | Always compile unit tests. |
|
| `--force-unit-tests` | Always compile unit tests. |
|
||||||
| `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. |
|
| `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. |
|
||||||
| `--disable-binary-check` | Do not perform integrity checks on the exe. |
|
| `--disable-binary-check` | Do not perform integrity checks on the exe. |
|
||||||
| `--iw4x-zones` | Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches. |
|
|
||||||
|
|
||||||
## Command line arguments
|
## Command line arguments
|
||||||
|
|
||||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 330583f47800c60cf001239550d291d16274756a
|
Subproject commit 10df83d292bf5bbdc487e57dc8c2dc8c7a01f4d1
|
1
deps/json11
vendored
1
deps/json11
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit 2df9473fb3605980db55ecddf34392a2e832ad35
|
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 8fd5dad96b56beb53b5cf199cb63fb76dfba32bb
|
Subproject commit ddfe2e8aa7c4239463a8a1d26724aef123333549
|
1
deps/nlohmannjson
vendored
Submodule
1
deps/nlohmannjson
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 307c053b9b250abc6e6d478a4336fd98592ae173
|
33
deps/premake/json11.lua
vendored
33
deps/premake/json11.lua
vendored
@ -1,33 +0,0 @@
|
|||||||
json11 = {
|
|
||||||
source = path.join(dependencies.basePath, "json11"),
|
|
||||||
}
|
|
||||||
|
|
||||||
function json11.import()
|
|
||||||
links {"json11"}
|
|
||||||
|
|
||||||
json11.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function json11.includes()
|
|
||||||
includedirs {json11.source}
|
|
||||||
end
|
|
||||||
|
|
||||||
function json11.project()
|
|
||||||
project "json11"
|
|
||||||
language "C++"
|
|
||||||
cppdialect "C++11"
|
|
||||||
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(json11.source, "*.cpp"),
|
|
||||||
path.join(json11.source, "*.hpp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
defines {"_LIB"}
|
|
||||||
removedefines {"_USRDLL", "_DLL"}
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(dependencies, json11)
|
|
18
deps/premake/nlohmannjson.lua
vendored
Normal file
18
deps/premake/nlohmannjson.lua
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
nlohmannjson = {
|
||||||
|
source = path.join(dependencies.basePath, "nlohmannjson"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function nlohmannjson.import()
|
||||||
|
nlohmannjson.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function nlohmannjson.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(nlohmannjson.source, "single_include/nlohmann")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function nlohmannjson.project()
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, nlohmannjson)
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
|||||||
Subproject commit fb6f8da08b60b6beb5bb360d79dd3feda0147da7
|
Subproject commit 50bdb17409d4133d51ab6cfa095700f4c816576e
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 2333419cd76cb9ae5f15c9b240b16a2052b27691
|
Subproject commit 5752b171fd4cc96b8d1f9526ec1940199c6e9740
|
@ -86,11 +86,6 @@ newoption {
|
|||||||
description = "Do not perform integrity checks on the exe."
|
description = "Do not perform integrity checks on the exe."
|
||||||
}
|
}
|
||||||
|
|
||||||
newoption {
|
|
||||||
trigger = "iw4x-zones",
|
|
||||||
description = "Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches."
|
|
||||||
}
|
|
||||||
|
|
||||||
newaction {
|
newaction {
|
||||||
trigger = "version",
|
trigger = "version",
|
||||||
description = "Returns the version string for the current commit of the source code.",
|
description = "Returns the version string for the current commit of the source code.",
|
||||||
@ -268,9 +263,6 @@ workspace "iw4x"
|
|||||||
if _OPTIONS["disable-binary-check"] then
|
if _OPTIONS["disable-binary-check"] then
|
||||||
defines {"DISABLE_BINARY_CHECK"}
|
defines {"DISABLE_BINARY_CHECK"}
|
||||||
end
|
end
|
||||||
if _OPTIONS["iw4x-zones"] then
|
|
||||||
defines {"GENERATE_IW4X_SPECIFIC_ZONES"}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pre-compiled header
|
-- Pre-compiled header
|
||||||
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
||||||
|
@ -112,6 +112,8 @@ namespace Components
|
|||||||
Loader::Register(new Ceg());
|
Loader::Register(new Ceg());
|
||||||
Loader::Register(new UserInfo());
|
Loader::Register(new UserInfo());
|
||||||
Loader::Register(new Events());
|
Loader::Register(new Events());
|
||||||
|
Loader::Register(new Voice());
|
||||||
|
Loader::Register(new Vote());
|
||||||
|
|
||||||
Loader::Register(new GSC());
|
Loader::Register(new GSC());
|
||||||
|
|
||||||
|
@ -143,5 +143,7 @@ namespace Components
|
|||||||
#include "Modules/Ceg.hpp"
|
#include "Modules/Ceg.hpp"
|
||||||
#include "Modules/UserInfo.hpp"
|
#include "Modules/UserInfo.hpp"
|
||||||
#include "Modules/Events.hpp"
|
#include "Modules/Events.hpp"
|
||||||
|
#include "Modules/Voice.hpp"
|
||||||
|
#include "Modules/Vote.hpp"
|
||||||
|
|
||||||
#include "Modules/GSC/GSC.hpp"
|
#include "Modules/GSC/GSC.hpp"
|
||||||
|
@ -601,7 +601,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json vertexData = json11::Json::object
|
nlohmann::json vertexData = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "name", vertexdecl->name },
|
{ "name", vertexdecl->name },
|
||||||
{ "streamCount", vertexdecl->streamCount },
|
{ "streamCount", vertexdecl->streamCount },
|
||||||
@ -651,7 +651,7 @@ namespace Components
|
|||||||
literalConsts.push_back(curArg->u.literalConst[3]);
|
literalConsts.push_back(curArg->u.literalConst[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json argData = json11::Json::object
|
nlohmann::json argData = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "type", curArg->type },
|
{ "type", curArg->type },
|
||||||
{ "value", literalConsts },
|
{ "value", literalConsts },
|
||||||
@ -660,7 +660,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else if (curArg->type == 3 || curArg->type == 5)
|
else if (curArg->type == 3 || curArg->type == 5)
|
||||||
{
|
{
|
||||||
json11::Json argData = json11::Json::object
|
nlohmann::json argData = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "type", curArg->type },
|
{ "type", curArg->type },
|
||||||
{ "firstRow", curArg->u.codeConst.firstRow },
|
{ "firstRow", curArg->u.codeConst.firstRow },
|
||||||
@ -671,7 +671,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
json11::Json argData = json11::Json::object
|
nlohmann::json argData = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "type", curArg->type },
|
{ "type", curArg->type },
|
||||||
{ "value", static_cast<int>(curArg->u.codeSampler) },
|
{ "value", static_cast<int>(curArg->u.codeSampler) },
|
||||||
@ -680,7 +680,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json passData = json11::Json::object
|
nlohmann::json passData = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "perObjArgCount", curPass->perObjArgCount },
|
{ "perObjArgCount", curPass->perObjArgCount },
|
||||||
{ "perPrimArgCount", curPass->perPrimArgCount },
|
{ "perPrimArgCount", curPass->perPrimArgCount },
|
||||||
@ -693,7 +693,7 @@ namespace Components
|
|||||||
passDataArray.push_back(passData);
|
passDataArray.push_back(passData);
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json techData = json11::Json::object
|
nlohmann::json techData = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "name", curTech->name },
|
{ "name", curTech->name },
|
||||||
{ "index", technique },
|
{ "index", technique },
|
||||||
@ -708,7 +708,7 @@ namespace Components
|
|||||||
fwrite(&stringData[0], stringData.size(), 1, fp);
|
fwrite(&stringData[0], stringData.size(), 1, fp);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
json11::Json techsetTechnique = json11::Json::object
|
nlohmann::json techsetTechnique = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "name", curTech->name },
|
{ "name", curTech->name },
|
||||||
{ "index", technique },
|
{ "index", technique },
|
||||||
@ -721,7 +721,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json techsetData = json11::Json::object
|
nlohmann::json techsetData = json11::Json::object
|
||||||
{
|
{
|
||||||
{ "name", techset->name },
|
{ "name", techset->name },
|
||||||
{ "techniques", techniques },
|
{ "techniques", techniques },
|
||||||
|
@ -100,26 +100,19 @@ namespace Assets
|
|||||||
|
|
||||||
if (fontDefFile.exists() && fontFile.exists())
|
if (fontDefFile.exists() && fontFile.exists())
|
||||||
{
|
{
|
||||||
std::string errors;
|
auto fontDef = nlohmann::json::parse(fontDefFile.getBuffer());
|
||||||
auto fontDef = json11::Json::parse(fontDefFile.getBuffer(), errors);
|
|
||||||
|
|
||||||
if (!errors.empty())
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Font define {} is broken: {}", name, errors);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fontDef.is_object())
|
if (!fontDef.is_object())
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Font define {} is invalid {}", name, errors);
|
Components::Logger::Error(Game::ERR_FATAL, "Font define {} is invalid", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int w = fontDef["textureWidth"].int_value();
|
int w = fontDef["textureWidth"].get<int>();
|
||||||
int h = fontDef["textureHeight"].int_value();
|
int h = fontDef["textureHeight"].get<int>();
|
||||||
|
|
||||||
int size = fontDef["size"].int_value();
|
int size = fontDef["size"].get<int>();
|
||||||
int yOffset = fontDef["yOffset"].int_value();
|
int yOffset = fontDef["yOffset"].get<int>();
|
||||||
|
|
||||||
auto* pixels = builder->getAllocator()->allocateArray<uint8_t>(w * h);
|
auto* pixels = builder->getAllocator()->allocateArray<uint8_t>(w * h);
|
||||||
|
|
||||||
@ -153,8 +146,9 @@ namespace Assets
|
|||||||
|
|
||||||
if (fontDef["charset"].is_array())
|
if (fontDef["charset"].is_array())
|
||||||
{
|
{
|
||||||
for (auto& ch : fontDef["charset"].array_items())
|
nlohmann::json::array_t charsetArray = fontDef["charset"];
|
||||||
charset.push_back(static_cast<uint16_t>(ch.int_value()));
|
for (auto& ch : charsetArray)
|
||||||
|
charset.push_back(static_cast<uint16_t>(ch.get<int>()));
|
||||||
|
|
||||||
// order matters
|
// order matters
|
||||||
std::sort(charset.begin(), charset.end());
|
std::sort(charset.begin(), charset.end());
|
||||||
|
@ -332,165 +332,13 @@ namespace Assets
|
|||||||
header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material;
|
header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File materialInfo(Utils::String::VA("materials/%s.json", name.data()));
|
Components::FileSystem::File materialInfo(Utils::String::VA("materials/%s.json", name.data()));
|
||||||
|
|
||||||
if (!materialInfo.exists()) return;
|
if (!materialInfo.exists()) return;
|
||||||
|
|
||||||
std::string errors;
|
header->material = nullptr;
|
||||||
json11::Json infoData = json11::Json::parse(materialInfo.getBuffer(), errors);
|
|
||||||
|
|
||||||
if (!infoData.is_object())
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to load material information for {}!", name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto base = infoData["base"];
|
|
||||||
|
|
||||||
if (!base.is_string())
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "No valid material base provided for {}!", name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::Material* baseMaterial = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, base.string_value().data()).material;
|
|
||||||
|
|
||||||
if (!baseMaterial) // TODO: Maybe check if default asset? Maybe not? You could still want to use the default one as base!?
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Basematerial '{}' not found for {}!", base.string_value(), name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::Material* material = builder->getAllocator()->allocate<Game::Material>();
|
|
||||||
|
|
||||||
if (!material)
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to allocate material structure!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy base material to our structure
|
|
||||||
std::memcpy(material, baseMaterial, sizeof(Game::Material));
|
|
||||||
material->info.name = builder->getAllocator()->duplicateString(name);
|
|
||||||
|
|
||||||
material->info.textureAtlasRowCount = 1;
|
|
||||||
material->info.textureAtlasColumnCount = 1;
|
|
||||||
|
|
||||||
// Load animation frames
|
|
||||||
auto anims = infoData["anims"];
|
|
||||||
if (anims.is_array())
|
|
||||||
{
|
|
||||||
auto animCoords = anims.array_items();
|
|
||||||
|
|
||||||
if (animCoords.size() >= 2)
|
|
||||||
{
|
|
||||||
auto animCoordX = animCoords[0];
|
|
||||||
auto animCoordY = animCoords[1];
|
|
||||||
|
|
||||||
if (animCoordX.is_number())
|
|
||||||
{
|
|
||||||
material->info.textureAtlasColumnCount = static_cast<char>(animCoordX.number_value()) & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animCoordY.is_number())
|
|
||||||
{
|
|
||||||
material->info.textureAtlasRowCount = static_cast<char>(animCoordY.number_value()) & 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Model surface textures are special, they need a special order and whatnot
|
|
||||||
bool replaceTexture = Utils::String::StartsWith(name, "mc/");
|
|
||||||
if (replaceTexture)
|
|
||||||
{
|
|
||||||
Game::MaterialTextureDef* textureTable = builder->getAllocator()->allocateArray<Game::MaterialTextureDef>(baseMaterial->textureCount);
|
|
||||||
std::memcpy(textureTable, baseMaterial->textureTable, sizeof(Game::MaterialTextureDef) * baseMaterial->textureCount);
|
|
||||||
material->textureTable = textureTable;
|
|
||||||
material->textureCount = baseMaterial->textureCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load referenced textures
|
|
||||||
auto textures = infoData["textures"];
|
|
||||||
if (textures.is_array())
|
|
||||||
{
|
|
||||||
std::vector<Game::MaterialTextureDef> textureList;
|
|
||||||
|
|
||||||
for (auto& texture : textures.array_items())
|
|
||||||
{
|
|
||||||
if (!texture.is_array()) continue;
|
|
||||||
if (textureList.size() >= 0xFF) break;
|
|
||||||
|
|
||||||
auto textureInfo = texture.array_items();
|
|
||||||
if (textureInfo.size() < 2) continue;
|
|
||||||
|
|
||||||
auto map = textureInfo[0];
|
|
||||||
auto image = textureInfo[1];
|
|
||||||
if (!map.is_string() || !image.is_string()) continue;
|
|
||||||
|
|
||||||
Game::MaterialTextureDef textureDef;
|
|
||||||
|
|
||||||
textureDef.semantic = 0; // No water image
|
|
||||||
textureDef.samplerState = -30;
|
|
||||||
textureDef.nameEnd = map.string_value().back();
|
|
||||||
textureDef.nameStart = map.string_value().front();
|
|
||||||
textureDef.nameHash = Game::R_HashString(map.string_value().data());
|
|
||||||
|
|
||||||
textureDef.u.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, image.string_value(), builder).image;
|
|
||||||
|
|
||||||
if (replaceTexture)
|
|
||||||
{
|
|
||||||
bool applied = false;
|
|
||||||
|
|
||||||
for (char i = 0; i < baseMaterial->textureCount; ++i)
|
|
||||||
{
|
|
||||||
if (material->textureTable[i].nameHash == textureDef.nameHash)
|
|
||||||
{
|
|
||||||
applied = true;
|
|
||||||
material->textureTable[i].u.image = textureDef.u.image;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!applied)
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Unable to find texture for map '{}' in {}!",
|
|
||||||
map.string_value(), baseMaterial->info.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
textureList.push_back(textureDef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!replaceTexture)
|
|
||||||
{
|
|
||||||
if (!textureList.empty())
|
|
||||||
{
|
|
||||||
Game::MaterialTextureDef* textureTable = builder->getAllocator()->allocateArray<Game::MaterialTextureDef>(textureList.size());
|
|
||||||
|
|
||||||
if (!textureTable)
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to allocate texture table!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memcpy(textureTable, textureList.data(), sizeof(Game::MaterialTextureDef) * textureList.size());
|
|
||||||
|
|
||||||
material->textureTable = textureTable;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
material->textureTable = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
material->textureCount = static_cast<char>(textureList.size()) & 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header->material = material;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
|
@ -5,10 +5,10 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.c_str()));
|
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s.json", name.data()));
|
||||||
if (!aliasFile.exists())
|
if (!aliasFile.exists())
|
||||||
{
|
{
|
||||||
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.c_str()).sound;
|
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,11 +19,10 @@ namespace Assets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string errors;
|
nlohmann::json infoData = nlohmann::json::parse(aliasFile.getBuffer());
|
||||||
json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors);
|
nlohmann::json aliasesContainer = infoData["head"];
|
||||||
json11::Json aliasesContainer = infoData["head"];
|
|
||||||
|
|
||||||
auto aliases = aliasesContainer.array_items();
|
nlohmann::json::array_t aliases = aliasesContainer;
|
||||||
|
|
||||||
aliasList->count = aliases.size();
|
aliasList->count = aliases.size();
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ namespace Assets
|
|||||||
|
|
||||||
for (size_t i = 0; i < aliasList->count; i++)
|
for (size_t i = 0; i < aliasList->count; i++)
|
||||||
{
|
{
|
||||||
json11::Json head = aliasesContainer[i];
|
nlohmann::json head = aliasesContainer[i];
|
||||||
|
|
||||||
if (!infoData.is_object())
|
if (!infoData.is_object())
|
||||||
{
|
{
|
||||||
@ -211,37 +210,37 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
|
|
||||||
alias->soundFile->exists = true;
|
alias->soundFile->exists = true;
|
||||||
alias->aliasName = builder->getAllocator()->duplicateString(aliasName.string_value().c_str());
|
alias->aliasName = builder->getAllocator()->duplicateString(aliasName.get<std::string>());
|
||||||
|
|
||||||
if (subtitle.is_string())
|
if (subtitle.is_string())
|
||||||
{
|
{
|
||||||
alias->subtitle = builder->getAllocator()->duplicateString(subtitle.string_value().c_str());
|
alias->subtitle = builder->getAllocator()->duplicateString(subtitle.get<std::string>());
|
||||||
}
|
}
|
||||||
if (secondaryAliasName.is_string())
|
if (secondaryAliasName.is_string())
|
||||||
{
|
{
|
||||||
alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.string_value().c_str());
|
alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.get<std::string>());
|
||||||
}
|
}
|
||||||
if (chainAliasName.is_string())
|
if (chainAliasName.is_string())
|
||||||
{
|
{
|
||||||
alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.string_value().c_str());
|
alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.get<std::string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
alias->sequence = sequence.int_value();
|
alias->sequence = sequence.get<int>();
|
||||||
alias->volMin = float(volMin.number_value());
|
alias->volMin = volMin.get<float>();
|
||||||
alias->volMax = float(volMax.number_value());
|
alias->volMax = volMax.get<float>();
|
||||||
alias->pitchMin = float(pitchMin.number_value());
|
alias->pitchMin = pitchMin.get<float>();
|
||||||
alias->pitchMax = float(pitchMax.number_value());
|
alias->pitchMax = pitchMax.get<float>();
|
||||||
alias->distMin = float(distMin.number_value());
|
alias->distMin = distMin.get<float>();
|
||||||
alias->distMax = float(distMax.number_value());
|
alias->distMax = distMax.get<float>();
|
||||||
alias->flags = flags.int_value();
|
alias->flags = flags.get<int>();
|
||||||
alias->___u15.slavePercentage = float(slavePercentage.number_value());
|
alias->___u15.slavePercentage = slavePercentage.get<float>();
|
||||||
alias->probability = float(probability.number_value());
|
alias->probability = probability.get<float>();
|
||||||
alias->lfePercentage = float(lfePercentage.number_value());
|
alias->lfePercentage = lfePercentage.get<float>();
|
||||||
alias->centerPercentage = float(centerPercentage.number_value());
|
alias->centerPercentage = centerPercentage.get<float>();
|
||||||
alias->startDelay = startDelay.int_value();
|
alias->startDelay = startDelay.get<int>();
|
||||||
alias->envelopMin = float(envelopMin.number_value());
|
alias->envelopMin = envelopMin.get<float>();
|
||||||
alias->envelopMax = float(envelopMax.number_value());
|
alias->envelopMax = envelopMax.get<float>();
|
||||||
alias->envelopPercentage = float(envelopPercentage.number_value());
|
alias->envelopPercentage = envelopPercentage.get<float>();
|
||||||
|
|
||||||
// Speaker map object
|
// Speaker map object
|
||||||
if (!speakerMap.is_null())
|
if (!speakerMap.is_null())
|
||||||
@ -253,12 +252,12 @@ namespace Assets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].string_value().c_str());
|
alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].get<std::string>());
|
||||||
alias->speakerMap->isDefault = speakerMap["isDefault"].bool_value();
|
alias->speakerMap->isDefault = speakerMap["isDefault"].get<bool>();
|
||||||
|
|
||||||
if (speakerMap["channelMaps"].is_array())
|
if (speakerMap["channelMaps"].is_array())
|
||||||
{
|
{
|
||||||
json11::Json::array channelMaps = speakerMap["channelMaps"].array_items();
|
nlohmann::json::array_t channelMaps = speakerMap["channelMaps"];
|
||||||
|
|
||||||
assert(channelMaps.size() <= 4);
|
assert(channelMaps.size() <= 4);
|
||||||
|
|
||||||
@ -268,19 +267,19 @@ namespace Assets
|
|||||||
// subChannelIndex should never exceed 1
|
// subChannelIndex should never exceed 1
|
||||||
for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++)
|
for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++)
|
||||||
{
|
{
|
||||||
json11::Json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3
|
nlohmann::json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3
|
||||||
|
|
||||||
auto speakers = channelMap["speakers"].array_items();
|
nlohmann::json::array_t speakers = channelMap["speakers"];
|
||||||
|
|
||||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size();
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size();
|
||||||
|
|
||||||
for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++)
|
for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++)
|
||||||
{
|
{
|
||||||
auto speaker = speakers[speakerIndex];
|
auto speaker = speakers[speakerIndex];
|
||||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = static_cast<float>(speaker["levels0"].number_value());
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = speaker["levels0"].get<float>();
|
||||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = static_cast<float>(speaker["levels1"].number_value());
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = speaker["levels1"].get<float>();
|
||||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = static_cast<int>(speaker["numLevels"].number_value());
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = speaker["numLevels"].get<int>();
|
||||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = static_cast<int>(speaker["speaker"].number_value());
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = speaker["speaker"].get<int>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,7 +288,7 @@ namespace Assets
|
|||||||
|
|
||||||
if (volumeFalloffCurve.is_string())
|
if (volumeFalloffCurve.is_string())
|
||||||
{
|
{
|
||||||
std::string fallOffCurve = volumeFalloffCurve.string_value();
|
std::string fallOffCurve = volumeFalloffCurve.get<std::string>();
|
||||||
|
|
||||||
if (fallOffCurve.size() == 0)
|
if (fallOffCurve.size() == 0)
|
||||||
{
|
{
|
||||||
@ -305,16 +304,16 @@ namespace Assets
|
|||||||
alias->volumeFalloffCurve = curve;
|
alias->volumeFalloffCurve = curve;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded
|
if (static_cast<Game::snd_alias_type_t>(type.get<int>()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded
|
||||||
{
|
{
|
||||||
alias->soundFile->type = Game::SAT_LOADED;
|
alias->soundFile->type = Game::SAT_LOADED;
|
||||||
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value().c_str(), builder).loadSnd;
|
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.get<std::string>(), builder).loadSnd;
|
||||||
}
|
}
|
||||||
else if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed
|
else if (static_cast<Game::snd_alias_type_t>(type.get<int>()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed
|
||||||
{
|
{
|
||||||
alias->soundFile->type = Game::SAT_STREAMED;
|
alias->soundFile->type = Game::SAT_STREAMED;
|
||||||
|
|
||||||
std::string streamedFile = soundFile.string_value();
|
std::string streamedFile = soundFile.get<std::string>();
|
||||||
std::string directory = ""s;
|
std::string directory = ""s;
|
||||||
int split = streamedFile.find_last_of('/');
|
int split = streamedFile.find_last_of('/');
|
||||||
|
|
||||||
@ -329,7 +328,7 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}! Invalid sound type {}\n", name, type.string_value());
|
Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}! Invalid sound type {}\n", name, type.get<std::string>());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ namespace Components
|
|||||||
|
|
||||||
// Parse proto data
|
// Parse proto data
|
||||||
Proto::Auth::Connect connectData;
|
Proto::Auth::Connect connectData;
|
||||||
if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(&msg->data[12], msg->cursize - 12)))
|
if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(reinterpret_cast<char*>(&msg->data[12]), msg->cursize - 12)))
|
||||||
{
|
{
|
||||||
Network::Send(address, "error\nInvalid connect packet!");
|
Network::Send(address, "error\nInvalid connect packet!");
|
||||||
return;
|
return;
|
||||||
@ -233,7 +233,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Debug("Verified XUID {:#X} ({}) from {}", xuid, userLevel, address.getCString());
|
Logger::Debug("Verified XUID {:#X} ({}) from {}", xuid, userLevel, address.getString());
|
||||||
Game::SV_DirectConnect(*address.get());
|
Game::SV_DirectConnect(*address.get());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -472,7 +472,7 @@ namespace Components
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UIScript::Add("security_increase_cancel", [](UIScript::Token)
|
UIScript::Add("security_increase_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Auth::TokenContainer.cancel = true;
|
Auth::TokenContainer.cancel = true;
|
||||||
Logger::Print("Token incrementation process canceled!\n");
|
Logger::Print("Token incrementation process canceled!\n");
|
||||||
|
@ -107,7 +107,7 @@ namespace Components
|
|||||||
ipEntry.bytes[3] & 0xFF));
|
ipEntry.bytes[3] & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
const json11::Json bans = json11::Json::object
|
const nlohmann::json bans = nlohmann::json
|
||||||
{
|
{
|
||||||
{ "ip", ipVector },
|
{ "ip", ipVector },
|
||||||
{ "id", idVector },
|
{ "id", idVector },
|
||||||
@ -131,13 +131,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
const auto banData = json11::Json::parse(bans.getBuffer(), error);
|
const auto banData = nlohmann::json::parse(bans.getBuffer());
|
||||||
|
|
||||||
if (!error.empty())
|
|
||||||
{
|
|
||||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Failed to parse bans.json: {}\n", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!banData.is_object())
|
if (!banData.is_object())
|
||||||
{
|
{
|
||||||
@ -150,12 +144,15 @@ namespace Components
|
|||||||
|
|
||||||
if (idList.is_array())
|
if (idList.is_array())
|
||||||
{
|
{
|
||||||
for (auto &idEntry : idList.array_items())
|
nlohmann::json::array_t arr = idList;
|
||||||
|
|
||||||
|
for (auto &idEntry : arr)
|
||||||
{
|
{
|
||||||
if (idEntry.is_string())
|
if (idEntry.is_string())
|
||||||
{
|
{
|
||||||
SteamID id;
|
SteamID id;
|
||||||
id.bits = strtoull(idEntry.string_value().data(), nullptr, 16);
|
auto guid = idEntry.get<std::string>();
|
||||||
|
id.bits = std::strtoull(guid.data(), nullptr, 16);
|
||||||
|
|
||||||
list->idList.push_back(id);
|
list->idList.push_back(id);
|
||||||
}
|
}
|
||||||
@ -164,11 +161,13 @@ namespace Components
|
|||||||
|
|
||||||
if (ipList.is_array())
|
if (ipList.is_array())
|
||||||
{
|
{
|
||||||
for (auto &ipEntry : ipList.array_items())
|
nlohmann::json::array_t arr = ipList;
|
||||||
|
|
||||||
|
for (auto &ipEntry : arr)
|
||||||
{
|
{
|
||||||
if (ipEntry.is_string())
|
if (ipEntry.is_string())
|
||||||
{
|
{
|
||||||
Network::Address addr(ipEntry.string_value());
|
Network::Address addr(ipEntry.get<std::string>());
|
||||||
|
|
||||||
list->ipList.push_back(addr.getIP());
|
list->ipList.push_back(addr.getIP());
|
||||||
}
|
}
|
||||||
@ -181,7 +180,7 @@ namespace Components
|
|||||||
SteamID guid;
|
SteamID guid;
|
||||||
guid.bits = cl->steamID;
|
guid.bits = cl->steamID;
|
||||||
|
|
||||||
InsertBan({guid, cl->netchan.remoteAddress.ip});
|
InsertBan({guid, cl->header.netchan.remoteAddress.ip});
|
||||||
|
|
||||||
Game::SV_DropClient(cl, reason.data(), true);
|
Game::SV_DropClient(cl, reason.data(), true);
|
||||||
}
|
}
|
||||||
@ -226,7 +225,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Command::Add("banClient", [](Command::Params* params)
|
Command::Add("banClient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (!Dvar::Var("sv_running").get<bool>())
|
if (!(*Game::com_sv_running)->current.enabled)
|
||||||
{
|
{
|
||||||
Logger::Print("Server is not running.\n");
|
Logger::Print("Server is not running.\n");
|
||||||
return;
|
return;
|
||||||
@ -258,7 +257,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto* cl = &Game::svs_clients[num];
|
const auto* cl = &Game::svs_clients[num];
|
||||||
if (cl->state == Game::CS_FREE)
|
if (cl->header.state == Game::CS_FREE)
|
||||||
{
|
{
|
||||||
Logger::Print("Client {} is not active\n", num);
|
Logger::Print("Client {} is not active\n", num);
|
||||||
return;
|
return;
|
||||||
@ -270,7 +269,7 @@ namespace Components
|
|||||||
|
|
||||||
Command::Add("unbanClient", [](Command::Params* params)
|
Command::Add("unbanClient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (!Dvar::Var("sv_running").get<bool>())
|
if (!(*Game::com_sv_running)->current.enabled)
|
||||||
{
|
{
|
||||||
Logger::Print("Server is not running.\n");
|
Logger::Print("Server is not running.\n");
|
||||||
return;
|
return;
|
||||||
|
@ -294,7 +294,8 @@ namespace Components
|
|||||||
|
|
||||||
Bots::Bots()
|
Bots::Bots()
|
||||||
{
|
{
|
||||||
AssertOffset(Game::client_s, bIsTestClient, 0x41AF0);
|
AssertOffset(Game::client_t, bIsTestClient, 0x41AF0);
|
||||||
|
AssertOffset(Game::client_t, ping, 0x212C8);
|
||||||
|
|
||||||
// Replace connect string
|
// Replace connect string
|
||||||
Utils::Hook::Set<const char*>(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
|
Utils::Hook::Set<const char*>(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
|
||||||
|
@ -5,7 +5,6 @@ namespace Components
|
|||||||
Dvar::Var Branding::CGDrawVersion;
|
Dvar::Var Branding::CGDrawVersion;
|
||||||
Dvar::Var Branding::CGDrawVersionX;
|
Dvar::Var Branding::CGDrawVersionX;
|
||||||
Dvar::Var Branding::CGDrawVersionY;
|
Dvar::Var Branding::CGDrawVersionY;
|
||||||
Game::dvar_t** Branding::Version = reinterpret_cast<Game::dvar_t**>(0x1AD7930);
|
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
constexpr auto* BUILD_TYPE = "IW4x_DEV MP";
|
constexpr auto* BUILD_TYPE = "IW4x_DEV MP";
|
||||||
@ -25,12 +24,12 @@ namespace Components
|
|||||||
auto* const placement = Game::ScrPlace_GetUnsafeFullPlacement();
|
auto* const placement = Game::ScrPlace_GetUnsafeFullPlacement();
|
||||||
auto* const font = Game::UI_GetFontHandle(placement, 0, 0.583f);
|
auto* const font = Game::UI_GetFontHandle(placement, 0, 0.583f);
|
||||||
|
|
||||||
const auto width = Game::UI_TextWidth((*Version)->current.string, 0, font, fontScale);
|
const auto width = Game::UI_TextWidth((*Game::version)->current.string, 0, font, fontScale);
|
||||||
const auto height = Game::UI_TextHeight(font, fontScale);
|
const auto height = Game::UI_TextHeight(font, fontScale);
|
||||||
|
|
||||||
Game::UI_DrawText(placement, (*Version)->current.string, maxChars, font, 1.0f - (CGDrawVersionX.get<float>() + static_cast<float>(width)),
|
Game::UI_DrawText(placement, (*Game::version)->current.string, maxChars, font, 1.0f - (CGDrawVersionX.get<float>() + static_cast<float>(width)),
|
||||||
1.0f - (CGDrawVersionY.get<float>() + static_cast<float>(height)), 3, 3, fontScale, shadowColor, 0);
|
1.0f - (CGDrawVersionY.get<float>() + static_cast<float>(height)), 3, 3, fontScale, shadowColor, 0);
|
||||||
Game::UI_DrawText(placement, (*Version)->current.string, maxChars, font, (0.0f - static_cast<float>(width)) - CGDrawVersionX.get<float>(),
|
Game::UI_DrawText(placement, (*Game::version)->current.string, maxChars, font, (0.0f - static_cast<float>(width)) - CGDrawVersionX.get<float>(),
|
||||||
(0.0f - static_cast<float>(height)) - CGDrawVersionY.get<float>(), 3, 3, fontScale, color, 0);
|
(0.0f - static_cast<float>(height)) - CGDrawVersionY.get<float>(), 3, 3, fontScale, color, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ namespace Components
|
|||||||
static Dvar::Var CGDrawVersion;
|
static Dvar::Var CGDrawVersion;
|
||||||
static Dvar::Var CGDrawVersionX;
|
static Dvar::Var CGDrawVersionX;
|
||||||
static Dvar::Var CGDrawVersionY;
|
static Dvar::Var CGDrawVersionY;
|
||||||
static Game::dvar_t** Version;
|
|
||||||
|
|
||||||
static void CG_DrawVersion();
|
static void CG_DrawVersion();
|
||||||
static void CG_DrawVersion_Hk(int localClientNum);
|
static void CG_DrawVersion_Hk(int localClientNum);
|
||||||
|
@ -7,10 +7,10 @@ namespace Components
|
|||||||
|
|
||||||
float Bullet::BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType)
|
float Bullet::BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType)
|
||||||
{
|
{
|
||||||
assert(weapDef != nullptr);
|
assert(weapDef);
|
||||||
assert(weapDef->penetrateType != Game::PenetrateType::PENETRATE_TYPE_NONE);
|
assert(weapDef->penetrateType != Game::PENETRATE_TYPE_NONE);
|
||||||
assert(weapDef->penetrateType < Game::PenetrateType::PENETRATE_TYPE_COUNT);
|
AssertIn(weapDef->penetrateType, Game::PENETRATE_TYPE_COUNT);
|
||||||
assert(static_cast<size_t>(surfaceType) < Game::materialSurfType_t::SURF_TYPE_COUNT);
|
AssertIn(surfaceType, Game::SURF_TYPE_COUNT);
|
||||||
|
|
||||||
const auto penetrationDepth = BGSurfacePenetration.get<float>();
|
const auto penetrationDepth = BGSurfacePenetration.get<float>();
|
||||||
if (penetrationDepth > 0.0f)
|
if (penetrationDepth > 0.0f)
|
||||||
|
@ -160,7 +160,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
char playerTitle[18];
|
char playerTitle[18];
|
||||||
|
|
||||||
if (Game::svs_clients[i].state >= Game::CS_CONNECTED)
|
if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED)
|
||||||
{
|
{
|
||||||
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE);
|
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE);
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,9 @@ namespace Components
|
|||||||
Utils::Hook::Set<BYTE>(0x432180, 0xC3);
|
Utils::Hook::Set<BYTE>(0x432180, 0xC3);
|
||||||
Utils::Hook::Set<BYTE>(0x461930, 0xC3);
|
Utils::Hook::Set<BYTE>(0x461930, 0xC3);
|
||||||
|
|
||||||
|
// Used next to file system functions
|
||||||
|
Utils::Hook::Set<BYTE>(0x47BC00, 0xC3);
|
||||||
|
|
||||||
// Looking for stuff in the registry
|
// Looking for stuff in the registry
|
||||||
Utils::Hook::Nop(0x4826F8, 5);
|
Utils::Hook::Nop(0x4826F8, 5);
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Dvar::Var Chat::cg_chatWidth;
|
Dvar::Var Chat::cg_chatWidth;
|
||||||
Dvar::Var Chat::sv_disableChat;
|
Dvar::Var Chat::sv_disableChat;
|
||||||
|
Dvar::Var Chat::sv_sayName;
|
||||||
|
|
||||||
Game::dvar_t** Chat::cg_chatHeight = reinterpret_cast<Game::dvar_t**>(0x7ED398);
|
Game::dvar_t** Chat::cg_chatHeight = reinterpret_cast<Game::dvar_t**>(0x7ED398);
|
||||||
Game::dvar_t** Chat::cg_chatTime = reinterpret_cast<Game::dvar_t**>(0x9F5DE8);
|
Game::dvar_t** Chat::cg_chatTime = reinterpret_cast<Game::dvar_t**>(0x9F5DE8);
|
||||||
|
|
||||||
bool Chat::SendChat;
|
bool Chat::SendChat;
|
||||||
|
|
||||||
std::mutex Chat::AccessMutex;
|
Utils::Concurrency::Container<Chat::muteList> Chat::MutedList;
|
||||||
std::unordered_set<std::uint64_t> Chat::MuteList;
|
|
||||||
|
|
||||||
bool Chat::CanAddCallback = true;
|
bool Chat::CanAddCallback = true;
|
||||||
std::vector<Scripting::Function> Chat::SayCallbacks;
|
std::vector<Scripting::Function> Chat::SayCallbacks;
|
||||||
@ -36,21 +36,13 @@ namespace Components
|
|||||||
++text;
|
++text;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock lock(AccessMutex);
|
if (IsMuted(player))
|
||||||
if (MuteList.contains(Game::svs_clients[player->s.number].steamID))
|
|
||||||
{
|
{
|
||||||
lock.unlock();
|
|
||||||
SendChat = false;
|
SendChat = false;
|
||||||
Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(player - Game::g_entities, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"You are muted\"", 0x65));
|
Utils::String::VA("%c \"You are muted\"", 0x65));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test whether the lock is still locked
|
|
||||||
if (lock.owns_lock())
|
|
||||||
{
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& callback : SayCallbacks)
|
for (const auto& callback : SayCallbacks)
|
||||||
{
|
{
|
||||||
if (!ChatCallback(player, callback.getPos(), (text + 1), mode))
|
if (!ChatCallback(player, callback.getPos(), (text + 1), mode))
|
||||||
@ -62,7 +54,7 @@ namespace Components
|
|||||||
if (sv_disableChat.get<bool>())
|
if (sv_disableChat.get<bool>())
|
||||||
{
|
{
|
||||||
SendChat = false;
|
SendChat = false;
|
||||||
Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(player - Game::g_entities, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"Chat is disabled\"", 0x65));
|
Utils::String::VA("%c \"Chat is disabled\"", 0x65));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,25 +235,30 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Chat::IsMuted(const Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
const auto clientNum = ent - Game::g_entities;
|
||||||
|
const auto xuid = Game::svs_clients[clientNum].steamID;
|
||||||
|
|
||||||
|
const auto result = MutedList.access<bool>([&](muteList& clients)
|
||||||
|
{
|
||||||
|
return clients.contains(xuid);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void Chat::MuteClient(const Game::client_t* client)
|
void Chat::MuteClient(const Game::client_t* client)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(AccessMutex);
|
const auto xuid = client->steamID;
|
||||||
|
MutedList.access([&](muteList& clients)
|
||||||
if (!MuteList.contains(client->steamID))
|
|
||||||
{
|
{
|
||||||
MuteList.insert(client->steamID);
|
clients.insert(xuid);
|
||||||
lock.unlock();
|
});
|
||||||
|
|
||||||
Logger::Print("{} was muted\n", client->name);
|
Logger::Print("{} was muted\n", client->name);
|
||||||
Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"You were muted\"", 0x65));
|
Utils::String::VA("%c \"You were muted\"", 0x65));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock.unlock();
|
|
||||||
Logger::Print("{} is already muted\n", client->name);
|
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE,
|
|
||||||
Utils::String::VA("%c \"%s is already muted\"", 0x65, client->name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::UnmuteClient(const Game::client_t* client)
|
void Chat::UnmuteClient(const Game::client_t* client)
|
||||||
@ -269,25 +266,26 @@ namespace Components
|
|||||||
UnmuteInternal(client->steamID);
|
UnmuteInternal(client->steamID);
|
||||||
|
|
||||||
Logger::Print("{} was unmuted\n", client->name);
|
Logger::Print("{} was unmuted\n", client->name);
|
||||||
Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE,
|
||||||
Utils::String::VA("%c \"You were unmuted\"", 0x65));
|
Utils::String::VA("%c \"You were unmuted\"", 0x65));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::UnmuteInternal(const std::uint64_t id, bool everyone)
|
void Chat::UnmuteInternal(const std::uint64_t id, bool everyone)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(AccessMutex);
|
MutedList.access([&](muteList& clients)
|
||||||
|
{
|
||||||
if (everyone)
|
if (everyone)
|
||||||
MuteList.clear();
|
clients.clear();
|
||||||
else
|
else
|
||||||
MuteList.erase(id);
|
clients.erase(id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::AddChatCommands()
|
void Chat::AddChatCommands()
|
||||||
{
|
{
|
||||||
Command::AddSV("muteClient", [](Command::Params* params)
|
Command::AddSV("muteClient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (!Dvar::Var("sv_running").get<bool>())
|
if (!(*Game::com_sv_running)->current.enabled)
|
||||||
{
|
{
|
||||||
Logger::Print("Server is not running.\n");
|
Logger::Print("Server is not running.\n");
|
||||||
return;
|
return;
|
||||||
@ -303,13 +301,14 @@ namespace Components
|
|||||||
const auto* client = Game::SV_GetPlayerByNum();
|
const auto* client = Game::SV_GetPlayerByNum();
|
||||||
if (client != nullptr)
|
if (client != nullptr)
|
||||||
{
|
{
|
||||||
|
Voice::SV_MuteClient(client - Game::svs_clients);
|
||||||
MuteClient(client);
|
MuteClient(client);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Command::AddSV("unmute", [](Command::Params* params)
|
Command::AddSV("unmute", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (!Dvar::Var("sv_running").get<bool>())
|
if (!(*Game::com_sv_running)->current.enabled)
|
||||||
{
|
{
|
||||||
Logger::Print("Server is not running.\n");
|
Logger::Print("Server is not running.\n");
|
||||||
return;
|
return;
|
||||||
@ -327,6 +326,7 @@ namespace Components
|
|||||||
if (client != nullptr)
|
if (client != nullptr)
|
||||||
{
|
{
|
||||||
UnmuteClient(client);
|
UnmuteClient(client);
|
||||||
|
Voice::SV_UnmuteClient(client - Game::svs_clients);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,6 +334,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Logger::Print("All players were unmuted\n");
|
Logger::Print("All players were unmuted\n");
|
||||||
UnmuteInternal(0, true);
|
UnmuteInternal(0, true);
|
||||||
|
Voice::SV_ClearMutedList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -341,6 +342,66 @@ namespace Components
|
|||||||
UnmuteInternal(steamId);
|
UnmuteInternal(steamId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Command::AddSV("say", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
|
auto message = params->join(1);
|
||||||
|
auto name = sv_sayName.get<std::string>();
|
||||||
|
|
||||||
|
if (!name.empty())
|
||||||
|
{
|
||||||
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 0x68, name.data(), message.data()));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{}: {}\n", name, message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 0x68, message.data()));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Console: {}\n", message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::AddSV("tell", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (params->size() < 3) return;
|
||||||
|
|
||||||
|
const auto client = std::atoi(params->get(1));
|
||||||
|
auto message = params->join(2);
|
||||||
|
auto name = sv_sayName.get<std::string>();
|
||||||
|
|
||||||
|
if (!name.empty())
|
||||||
|
{
|
||||||
|
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 0x68, name.data(), message.data()));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} -> {}: {}\n", name, client, message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data()));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Console -> {}: {}\n", client, message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::AddSV("sayraw", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
|
auto message = params->join(1);
|
||||||
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x68, message.data()));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw: {}\n", message);
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::AddSV("tellraw", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (params->size() < 3) return;
|
||||||
|
|
||||||
|
const auto client = atoi(params->get(1));
|
||||||
|
std::string message = params->join(2);
|
||||||
|
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x68, message.data()));
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw -> {}: {}\n", client, message);
|
||||||
|
});
|
||||||
|
|
||||||
|
sv_sayName = Dvar::Register<const char*>("sv_sayName", "^7Console", Game::DVAR_NONE, "The alias of the server when broadcasting a chat message");
|
||||||
}
|
}
|
||||||
|
|
||||||
int Chat::GetCallbackReturn()
|
int Chat::GetCallbackReturn()
|
||||||
@ -357,7 +418,7 @@ namespace Components
|
|||||||
|
|
||||||
const auto* result = &Game::scrVmPub->top[1 - Game::scrVmPub->outparamcount];
|
const auto* result = &Game::scrVmPub->top[1 - Game::scrVmPub->outparamcount];
|
||||||
|
|
||||||
if (result->type != Game::scrParamType_t::VAR_INTEGER)
|
if (result->type != Game::VAR_INTEGER)
|
||||||
{
|
{
|
||||||
// Garbage was returned
|
// Garbage was returned
|
||||||
return 1;
|
return 1;
|
||||||
@ -368,14 +429,14 @@ namespace Components
|
|||||||
|
|
||||||
int Chat::ChatCallback(Game::gentity_s* self, const char* codePos, const char* message, int mode)
|
int Chat::ChatCallback(Game::gentity_s* self, const char* codePos, const char* message, int mode)
|
||||||
{
|
{
|
||||||
const auto entityId = Game::Scr_GetEntityId(self->s.number, 0);
|
const auto entityId = Game::Scr_GetEntityId(self - Game::g_entities, 0);
|
||||||
|
|
||||||
Scripting::StackIsolation _;
|
Scripting::StackIsolation _;
|
||||||
Game::Scr_AddInt(mode);
|
Game::Scr_AddInt(mode);
|
||||||
Game::Scr_AddString(message);
|
Game::Scr_AddString(message);
|
||||||
|
|
||||||
Game::VariableValue value;
|
Game::VariableValue value;
|
||||||
value.type = Game::scrParamType_t::VAR_OBJECT;
|
value.type = Game::VAR_OBJECT;
|
||||||
value.u.uintValue = entityId;
|
value.u.uintValue = entityId;
|
||||||
|
|
||||||
Game::AddRefToValue(value.type, value.u);
|
Game::AddRefToValue(value.type, value.u);
|
||||||
@ -410,6 +471,8 @@ namespace Components
|
|||||||
|
|
||||||
Chat::Chat()
|
Chat::Chat()
|
||||||
{
|
{
|
||||||
|
AssertOffset(Game::client_t, steamID, 0x43F00);
|
||||||
|
|
||||||
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
||||||
sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");
|
sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");
|
||||||
Events::OnSVInit(AddChatCommands);
|
Events::OnSVInit(AddChatCommands);
|
||||||
|
@ -11,6 +11,7 @@ namespace Components
|
|||||||
private:
|
private:
|
||||||
static Dvar::Var cg_chatWidth;
|
static Dvar::Var cg_chatWidth;
|
||||||
static Dvar::Var sv_disableChat;
|
static Dvar::Var sv_disableChat;
|
||||||
|
static Dvar::Var sv_sayName;
|
||||||
|
|
||||||
// Game dvars
|
// Game dvars
|
||||||
static Game::dvar_t** cg_chatHeight;
|
static Game::dvar_t** cg_chatHeight;
|
||||||
@ -18,8 +19,8 @@ namespace Components
|
|||||||
|
|
||||||
static bool SendChat;
|
static bool SendChat;
|
||||||
|
|
||||||
static std::mutex AccessMutex;
|
using muteList = std::unordered_set<std::uint64_t>;
|
||||||
static std::unordered_set<std::uint64_t> MuteList;
|
static Utils::Concurrency::Container<muteList> MutedList;
|
||||||
|
|
||||||
static bool CanAddCallback; // ClientCommand & GSC thread are the same
|
static bool CanAddCallback; // ClientCommand & GSC thread are the same
|
||||||
static std::vector<Scripting::Function> SayCallbacks;
|
static std::vector<Scripting::Function> SayCallbacks;
|
||||||
@ -33,9 +34,10 @@ namespace Components
|
|||||||
static void CG_AddToTeamChat(const char* text);
|
static void CG_AddToTeamChat(const char* text);
|
||||||
static void CG_AddToTeamChat_Stub();
|
static void CG_AddToTeamChat_Stub();
|
||||||
|
|
||||||
|
static bool IsMuted(const Game::gentity_s* ent);
|
||||||
static void MuteClient(const Game::client_t* client);
|
static void MuteClient(const Game::client_t* client);
|
||||||
static void UnmuteClient(const Game::client_t* client);
|
static void UnmuteClient(const Game::client_t* client);
|
||||||
static void UnmuteInternal(const std::uint64_t id, bool everyone = false);
|
static void UnmuteInternal(std::uint64_t id, bool everyone = false);
|
||||||
static void AddChatCommands();
|
static void AddChatCommands();
|
||||||
|
|
||||||
static int GetCallbackReturn();
|
static int GetCallbackReturn();
|
||||||
|
@ -9,7 +9,7 @@ namespace Components
|
|||||||
|
|
||||||
const char* ClanTags::GetClanTagWithName(int clientNum, const char* playerName)
|
const char* ClanTags::GetClanTagWithName(int clientNum, const char* playerName)
|
||||||
{
|
{
|
||||||
assert(static_cast<std::size_t>(clientNum) < Game::MAX_CLIENTS);
|
AssertIn(clientNum, Game::MAX_CLIENTS);
|
||||||
|
|
||||||
if (ClientState[clientNum][0] == '\0')
|
if (ClientState[clientNum][0] == '\0')
|
||||||
{
|
{
|
||||||
@ -96,7 +96,7 @@ namespace Components
|
|||||||
|
|
||||||
char* ClanTags::GamerProfile_GetClanName(int controllerIndex)
|
char* ClanTags::GamerProfile_GetClanName(int controllerIndex)
|
||||||
{
|
{
|
||||||
assert(static_cast<std::size_t>(controllerIndex) < Game::MAX_LOCAL_CLIENTS);
|
AssertIn(controllerIndex, Game::MAX_LOCAL_CLIENTS);
|
||||||
assert(ClanName);
|
assert(ClanName);
|
||||||
|
|
||||||
CL_SanitizeClanName();
|
CL_SanitizeClanName();
|
||||||
@ -115,7 +115,7 @@ namespace Components
|
|||||||
|
|
||||||
void ClanTags::ClientUserinfoChanged(const char* s, int clientNum)
|
void ClanTags::ClientUserinfoChanged(const char* s, int clientNum)
|
||||||
{
|
{
|
||||||
assert(static_cast<std::size_t>(clientNum) < Game::MAX_CLIENTS);
|
AssertIn(clientNum, Game::MAX_CLIENTS);
|
||||||
|
|
||||||
auto* clanAbbrev = Game::Info_ValueForKey(s, "clanAbbrev");
|
auto* clanAbbrev = Game::Info_ValueForKey(s, "clanAbbrev");
|
||||||
|
|
||||||
@ -224,7 +224,7 @@ namespace Components
|
|||||||
Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForController_Stub(const unsigned int controllerIndex)
|
Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForController_Stub(const unsigned int controllerIndex)
|
||||||
{
|
{
|
||||||
auto* result = Utils::Hook::Call<Game::PlayerCardData*(unsigned int)>(0x463B90)(controllerIndex);
|
auto* result = Utils::Hook::Call<Game::PlayerCardData*(unsigned int)>(0x463B90)(controllerIndex);
|
||||||
// controllerIndex should always be 0
|
AssertIn(controllerIndex, Game::MAX_LOCAL_CLIENTS);
|
||||||
Game::I_strncpyz(result->clanAbbrev, GamerProfile_GetClanName(static_cast<int>(controllerIndex)), sizeof(Game::PlayerCardData::clanAbbrev));
|
Game::I_strncpyz(result->clanAbbrev, GamerProfile_GetClanName(static_cast<int>(controllerIndex)), sizeof(Game::PlayerCardData::clanAbbrev));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -28,8 +28,6 @@ namespace Components
|
|||||||
|
|
||||||
static void Dvar_InfoString_Stub(char* s, const char* key, const char* value);
|
static void Dvar_InfoString_Stub(char* s, const char* key, const char* value);
|
||||||
|
|
||||||
static void SetCachedPlayerData(int clientNum);
|
|
||||||
|
|
||||||
static void ClientUserinfoChanged(const char* s, int clientNum);
|
static void ClientUserinfoChanged(const char* s, int clientNum);
|
||||||
static void ClientUserinfoChanged_Stub();
|
static void ClientUserinfoChanged_Stub();
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, std::function<void(Game::gentity_s*, Command::ServerParams*)>> ClientCommand::HandlersSV;
|
std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> ClientCommand::HandlersSV;
|
||||||
|
|
||||||
bool ClientCommand::CheatsOk(const Game::gentity_s* ent)
|
bool ClientCommand::CheatsOk(const Game::gentity_s* ent)
|
||||||
{
|
{
|
||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
|
|
||||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
if (!(*Game::g_cheats)->current.enabled)
|
||||||
{
|
{
|
||||||
Logger::Debug("Cheats are disabled!");
|
Logger::Debug("Cheats are disabled!");
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
|
||||||
@ -26,11 +26,11 @@ namespace Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::Add(const char* name, const std::function<void(Game::gentity_s*, Command::ServerParams*)>& callback)
|
void ClientCommand::Add(const char* name, const std::function<void(Game::gentity_s*, const Command::ServerParams*)>& callback)
|
||||||
{
|
{
|
||||||
const auto command = Utils::String::ToLower(name);
|
const auto command = Utils::String::ToLower(name);
|
||||||
|
|
||||||
ClientCommand::HandlersSV[command] = callback;
|
HandlersSV[command] = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::ClientCommandStub(const int clientNum)
|
void ClientCommand::ClientCommandStub(const int clientNum)
|
||||||
@ -57,9 +57,9 @@ namespace Components
|
|||||||
|
|
||||||
void ClientCommand::AddCheatCommands()
|
void ClientCommand::AddCheatCommands()
|
||||||
{
|
{
|
||||||
ClientCommand::Add("noclip", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("noclip", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (!ClientCommand::CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||||
@ -71,9 +71,9 @@ namespace Components
|
|||||||
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (!ClientCommand::CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ent->client->flags ^= Game::PLAYER_FLAG_UFO;
|
ent->client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||||
@ -85,9 +85,9 @@ namespace Components
|
|||||||
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("god", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("god", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (!ClientCommand::CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ent->flags ^= Game::FL_GODMODE;
|
ent->flags ^= Game::FL_GODMODE;
|
||||||
@ -99,9 +99,9 @@ namespace Components
|
|||||||
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (!ClientCommand::CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ent->flags ^= Game::FL_DEMI_GODMODE;
|
ent->flags ^= Game::FL_DEMI_GODMODE;
|
||||||
@ -113,9 +113,9 @@ namespace Components
|
|||||||
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (!ClientCommand::CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ent->flags ^= Game::FL_NOTARGET;
|
ent->flags ^= Game::FL_NOTARGET;
|
||||||
@ -127,11 +127,11 @@ namespace Components
|
|||||||
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
assert(ent != nullptr);
|
assert(ent != nullptr);
|
||||||
|
|
||||||
if (!ClientCommand::CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Game::vec3_t origin, angles{0.f, 0.f, 0.f};
|
Game::vec3_t origin, angles{0.f, 0.f, 0.f};
|
||||||
@ -163,9 +163,9 @@ namespace Components
|
|||||||
Game::TeleportPlayer(ent, origin, angles);
|
Game::TeleportPlayer(ent, origin, angles);
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("give", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("give", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (!ClientCommand::CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (params->size() < 2)
|
if (params->size() < 2)
|
||||||
@ -236,38 +236,55 @@ namespace Components
|
|||||||
for (std::size_t i = 0; i < std::extent_v<decltype(Game::playerState_s::weaponsEquipped)>; ++i)
|
for (std::size_t i = 0; i < std::extent_v<decltype(Game::playerState_s::weaponsEquipped)>; ++i)
|
||||||
{
|
{
|
||||||
const auto index = ent->client->ps.weaponsEquipped[i];
|
const auto index = ent->client->ps.weaponsEquipped[i];
|
||||||
if (index != 0)
|
if (index)
|
||||||
{
|
{
|
||||||
Game::Add_Ammo(ent, index, 0, 998, 1);
|
Game::Add_Ammo(ent, index, 0, 998, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
|
{
|
||||||
|
assert(ent->client != nullptr);
|
||||||
|
assert(ent->client->sess.connected != Game::CON_DISCONNECTED);
|
||||||
|
|
||||||
|
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Scheduler::Once([ent]
|
||||||
|
{
|
||||||
|
ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE);
|
||||||
|
ent->health = 0;
|
||||||
|
ent->client->ps.stats[0] = 0;
|
||||||
|
Game::player_die(ent, ent, ent, 100000, Game::MOD_SUICIDE, 0, nullptr, Game::HITLOC_NONE, 0);
|
||||||
|
}, Scheduler::Pipeline::SERVER);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::AddDevelopmentCommands()
|
void ClientCommand::AddDevelopmentCommands()
|
||||||
{
|
{
|
||||||
ClientCommand::Add("dropallbots", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("dropallbots", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
Game::SV_DropAllBots();
|
Game::SV_DropAllBots();
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("entitylist", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("entitylist", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
Game::Svcmd_EntityList_f();
|
Game::Svcmd_EntityList_f();
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("printentities", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("printentities", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
Game::G_PrintEntities();
|
Game::G_PrintEntities();
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("entitycount", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("entitycount", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
Logger::Print("Entity count = {}\n", Game::level->num_entities);
|
Logger::Print("Entity count = {}\n", Game::level->num_entities);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also known as: "vis"
|
// Also known as: "vis"
|
||||||
ClientCommand::Add("visionsetnaked", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("visionsetnaked", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2)
|
if (params->size() < 2)
|
||||||
{
|
{
|
||||||
@ -295,7 +312,7 @@ namespace Components
|
|||||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2)
|
if (params->size() < 2)
|
||||||
{
|
{
|
||||||
@ -323,7 +340,7 @@ namespace Components
|
|||||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
assert(ent != nullptr);
|
assert(ent != nullptr);
|
||||||
|
|
||||||
@ -331,21 +348,14 @@ namespace Components
|
|||||||
Logger::Debug("playerState_s.stunTime is {}", ent->client->ps.stunTime);
|
Logger::Debug("playerState_s.stunTime is {}", ent->client->ps.stunTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
Add("dumpEntInfo", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
assert(ent->client != nullptr);
|
G_DumpEntityDebugInfoToConsole(false);
|
||||||
assert(ent->client->sess.connected != Game::CON_DISCONNECTED);
|
});
|
||||||
|
|
||||||
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent))
|
Add("dumpEntInfoCSV", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
return;
|
{
|
||||||
|
G_DumpEntityDebugInfoToCSV("");
|
||||||
Scheduler::Once([ent]
|
|
||||||
{
|
|
||||||
ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE);
|
|
||||||
ent->health = 0;
|
|
||||||
ent->client->ps.stats[0] = 0;
|
|
||||||
Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::HITLOC_NONE, 0);
|
|
||||||
}, Scheduler::Pipeline::SERVER);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,15 +367,123 @@ namespace Components
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* ClientCommand::EntInfoLine(const int entNum)
|
||||||
|
{
|
||||||
|
const auto* ent = &Game::g_entities[entNum];
|
||||||
|
|
||||||
|
Game::XModel* model = nullptr;
|
||||||
|
if (ent->model)
|
||||||
|
{
|
||||||
|
model = Game::G_GetModel(ent->model);
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::vec3_t point, angles;
|
||||||
|
|
||||||
|
point[0] = ent->r.currentOrigin[0] - (*Game::viewposNow)->current.vector[0];
|
||||||
|
point[1] = ent->r.currentOrigin[1] - (*Game::viewposNow)->current.vector[1];
|
||||||
|
point[2] = ent->r.currentOrigin[2] - (*Game::viewposNow)->current.vector[2];
|
||||||
|
|
||||||
|
angles[0] = ent->r.currentAngles[0];
|
||||||
|
angles[1] = ent->r.currentAngles[1];
|
||||||
|
angles[2] = ent->r.currentAngles[2];
|
||||||
|
|
||||||
|
const auto distance = std::sqrtf(point[0] * point[0] + point[1] * point[1]
|
||||||
|
+ point[2] * point[2]);
|
||||||
|
|
||||||
|
const auto* team = (ent->client) ? Game::CG_GetTeamName(ent->client->sess.cs.team) : "";
|
||||||
|
|
||||||
|
const auto* scriptLinkName = (ent->script_linkName) ? Game::SL_ConvertToString(ent->script_linkName) : "";
|
||||||
|
|
||||||
|
const auto* target = (ent->target) ? Game::SL_ConvertToString(ent->target) : "";
|
||||||
|
|
||||||
|
const auto* targetName = (ent->targetname) ? Game::SL_ConvertToString(ent->targetname) : "";
|
||||||
|
|
||||||
|
const auto* codeClassname = (ent->script_classname) ? Game::SL_ConvertToString(ent->script_classname) : "";
|
||||||
|
|
||||||
|
const auto* classname = (ent->classname) ? Game::SL_ConvertToString(ent->classname) : "";
|
||||||
|
|
||||||
|
const auto* eventType = (ent->s.eType < Game::ET_EVENTS) ? Game::G_GetEntityTypeName(ent) : "";
|
||||||
|
|
||||||
|
// See description of the format string in the function G_DumpEntityDebugInfoToCSV
|
||||||
|
// If empty it means the item does not exist in the current version of the game or it was not possible to reverse it
|
||||||
|
return Utils::String::VA("%i,%s,%.0f,%s,%s,%s,%s,%s,%s,%s,%s,%s,%.0f %.0f %.0f,%.0f %.0f %.0f,%i\n",
|
||||||
|
entNum, eventType, distance, classname, codeClassname, (model) ? model->name : "",
|
||||||
|
targetName, target, "", scriptLinkName, team, "",
|
||||||
|
point[0], point[1], point[2], angles[0], angles[1], angles[2], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientCommand::G_DumpEntityDebugInfoToConsole(bool logfileOnly)
|
||||||
|
{
|
||||||
|
const auto channel = logfileOnly ? Game::CON_CHANNEL_LOGFILEONLY : Game::CON_CHANNEL_SERVER;
|
||||||
|
|
||||||
|
Logger::Print(channel, "=====================================================================================\n");
|
||||||
|
Logger::Print(channel, "============(entity dump begin)\n");
|
||||||
|
Logger::Print(channel,
|
||||||
|
"Number,Type,Distance,Classname,Code Classname,Model,"
|
||||||
|
"Targetname,Target,Script Noteworthy,Script Linkname,Team,ParentNum,Origin,Angles,SentToClients\n");
|
||||||
|
|
||||||
|
for (auto i = 0; i < Game::MAX_GENTITIES; ++i)
|
||||||
|
{
|
||||||
|
if (&Game::g_entities[i] == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* line = EntInfoLine(i);
|
||||||
|
assert(line);
|
||||||
|
Logger::Print(channel, "%s", line);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::Print(channel, "(end entity dump)============\n");
|
||||||
|
Logger::Print(channel, "=====================================================================================\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientCommand::G_DumpEntityDebugInfoToCSV(const char* filenameSuffix)
|
||||||
|
{
|
||||||
|
assert(filenameSuffix);
|
||||||
|
|
||||||
|
const auto* fileName = Utils::String::VA("%s%s%s%s", "EntInfo", (*filenameSuffix) ? "_" : "", filenameSuffix, ".csv");
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Opening file \"{}\" for writing.\n", fileName);
|
||||||
|
|
||||||
|
auto h = Game::FS_FOpenTextFileWrite(fileName);
|
||||||
|
if (!h)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_SERVER, "Couldn't open file \"{}\" for writing.\n", fileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::FS_Write("Number,Type,Distance,Classname,Code Classname,Model,Targetname,Target,Script Noteworthy,Script Linkname,Team,Paren"
|
||||||
|
"tNum,Origin,Angles,SentToClients\n", 147, h);
|
||||||
|
|
||||||
|
for (auto i = 0; i < Game::MAX_GENTITIES; ++i)
|
||||||
|
{
|
||||||
|
if (&Game::g_entities[i] == nullptr)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* line = EntInfoLine(i);
|
||||||
|
const auto lineLen = std::strlen(line);
|
||||||
|
assert(line);
|
||||||
|
assert(lineLen);
|
||||||
|
Game::FS_Write(line, lineLen, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::FS_FCloseFile(h);
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Done writing file.\n");
|
||||||
|
}
|
||||||
|
|
||||||
ClientCommand::ClientCommand()
|
ClientCommand::ClientCommand()
|
||||||
{
|
{
|
||||||
// Hook call to ClientCommand in SV_ExecuteClientCommand so we may add custom commands
|
AssertOffset(Game::playerState_s, stats, 0x150);
|
||||||
Utils::Hook(0x6259FA, ClientCommand::ClientCommandStub, HOOK_CALL).install()->quick();
|
|
||||||
|
|
||||||
ClientCommand::AddCheatCommands();
|
// Hook call to ClientCommand in SV_ExecuteClientCommand so we may add custom commands
|
||||||
ClientCommand::AddScriptFunctions();
|
Utils::Hook(0x6259FA, ClientCommandStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
AddCheatCommands();
|
||||||
|
AddScriptFunctions();
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
ClientCommand::AddDevelopmentCommands();
|
AddDevelopmentCommands();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,19 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
ClientCommand();
|
ClientCommand();
|
||||||
|
|
||||||
static void Add(const char* name, const std::function<void(Game::gentity_s*, Command::ServerParams*)>& callback);
|
static void Add(const char* name, const std::function<void(Game::gentity_s*, const Command::ServerParams*)>& callback);
|
||||||
static bool CheatsOk(const Game::gentity_s* ent);
|
static bool CheatsOk(const Game::gentity_s* ent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::unordered_map<std::string, std::function<void(Game::gentity_s*, Command::ServerParams*)>> HandlersSV;
|
static std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> HandlersSV;
|
||||||
|
|
||||||
static void ClientCommandStub(int clientNum);
|
static void ClientCommandStub(int clientNum);
|
||||||
static void AddCheatCommands();
|
static void AddCheatCommands();
|
||||||
static void AddDevelopmentCommands();
|
static void AddDevelopmentCommands();
|
||||||
static void AddScriptFunctions();
|
static void AddScriptFunctions();
|
||||||
|
|
||||||
|
static const char* EntInfoLine(int entNum);
|
||||||
|
static void G_DumpEntityDebugInfoToConsole(bool logfileOnly);
|
||||||
|
static void G_DumpEntityDebugInfoToCSV(const char* filenameSuffix);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ namespace Components
|
|||||||
std::unordered_map<std::string, std::function<void(Command::Params*)>> Command::FunctionMap;
|
std::unordered_map<std::string, std::function<void(Command::Params*)>> Command::FunctionMap;
|
||||||
std::unordered_map<std::string, std::function<void(Command::Params*)>> Command::FunctionMapSV;
|
std::unordered_map<std::string, std::function<void(Command::Params*)>> Command::FunctionMapSV;
|
||||||
|
|
||||||
std::string Command::Params::join(const int index)
|
std::string Command::Params::join(const int index) const
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
@ -24,12 +24,12 @@ namespace Components
|
|||||||
assert(Game::cmd_args->nesting < Game::CMD_MAX_NESTING);
|
assert(Game::cmd_args->nesting < Game::CMD_MAX_NESTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Command::ClientParams::size()
|
int Command::ClientParams::size() const
|
||||||
{
|
{
|
||||||
return Game::cmd_args->argc[this->nesting_];
|
return Game::cmd_args->argc[this->nesting_];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Command::ClientParams::get(const int index)
|
const char* Command::ClientParams::get(const int index) const
|
||||||
{
|
{
|
||||||
if (index >= this->size())
|
if (index >= this->size())
|
||||||
{
|
{
|
||||||
@ -45,12 +45,12 @@ namespace Components
|
|||||||
assert(Game::sv_cmd_args->nesting < Game::CMD_MAX_NESTING);
|
assert(Game::sv_cmd_args->nesting < Game::CMD_MAX_NESTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Command::ServerParams::size()
|
int Command::ServerParams::size() const
|
||||||
{
|
{
|
||||||
return Game::sv_cmd_args->argc[this->nesting_];
|
return Game::sv_cmd_args->argc[this->nesting_];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Command::ServerParams::get(const int index)
|
const char* Command::ServerParams::get(const int index) const
|
||||||
{
|
{
|
||||||
if (index >= this->size())
|
if (index >= this->size())
|
||||||
{
|
{
|
||||||
@ -113,7 +113,7 @@ namespace Components
|
|||||||
Game::Cmd_AddServerCommand(name, callback, Command::Allocate());
|
Game::Cmd_AddServerCommand(name, callback, Command::Allocate());
|
||||||
|
|
||||||
// If the main command is registered as Cbuf_AddServerText, the command will be redirected to the SV handler
|
// If the main command is registered as Cbuf_AddServerText, the command will be redirected to the SV handler
|
||||||
Command::AddRaw(name, Game::Cbuf_AddServerText, false);
|
Command::AddRaw(name, Game::Cbuf_AddServerText_f, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::Execute(std::string command, bool sync)
|
void Command::Execute(std::string command, bool sync)
|
||||||
|
@ -13,9 +13,9 @@ namespace Components
|
|||||||
Params() = default;
|
Params() = default;
|
||||||
virtual ~Params() = default;
|
virtual ~Params() = default;
|
||||||
|
|
||||||
virtual int size() = 0;
|
[[nodiscard]] virtual int size() const = 0;
|
||||||
virtual const char* get(int index) = 0;
|
[[nodiscard]] virtual const char* get(int index) const = 0;
|
||||||
virtual std::string join(int index);
|
[[nodiscard]] virtual std::string join(int index) const;
|
||||||
|
|
||||||
virtual const char* operator[](const int index)
|
virtual const char* operator[](const int index)
|
||||||
{
|
{
|
||||||
@ -28,8 +28,8 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
ClientParams();
|
ClientParams();
|
||||||
|
|
||||||
int size() override;
|
[[nodiscard]] int size() const override;
|
||||||
const char* get(int index) override;
|
[[nodiscard]] const char* get(int index) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int nesting_;
|
int nesting_;
|
||||||
@ -40,8 +40,8 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
ServerParams();
|
ServerParams();
|
||||||
|
|
||||||
int size() override;
|
[[nodiscard]] int size() const override;
|
||||||
const char* get(int index) override;
|
[[nodiscard]] const char* get(int index) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int nesting_;
|
int nesting_;
|
||||||
|
@ -24,28 +24,16 @@ namespace Components
|
|||||||
|
|
||||||
Game::SafeArea Console::OriginalSafeArea;
|
Game::SafeArea Console::OriginalSafeArea;
|
||||||
|
|
||||||
char** Console::GetAutoCompleteFileList(const char* path, const char* extension, Game::FsListBehavior_e behavior, int* numfiles, int allocTrackType)
|
const char** Console::GetAutoCompleteFileList(const char* path, const char* extension, Game::FsListBehavior_e behavior, int* numfiles, int allocTrackType)
|
||||||
{
|
{
|
||||||
if (path == reinterpret_cast<char*>(0xBAADF00D) || path == reinterpret_cast<char*>(0xCDCDCDCD) || ::Utils::Memory::IsBadReadPtr(path)) return nullptr;
|
if (path == reinterpret_cast<char*>(0xBAADF00D) || path == reinterpret_cast<char*>(0xCDCDCDCD) || ::Utils::Memory::IsBadReadPtr(path)) return nullptr;
|
||||||
return Game::FS_GetFileList(path, extension, behavior, numfiles, allocTrackType);
|
return Game::FS_ListFiles(path, extension, behavior, numfiles, allocTrackType);
|
||||||
}
|
|
||||||
|
|
||||||
void Console::ToggleConsole()
|
|
||||||
{
|
|
||||||
// possibly cls.keyCatchers?
|
|
||||||
Utils::Hook::Xor<DWORD>(0xB2C538, 1);
|
|
||||||
|
|
||||||
// g_consoleField
|
|
||||||
Game::Field_Clear(reinterpret_cast<void*>(0xA1B6B0));
|
|
||||||
|
|
||||||
// show console output?
|
|
||||||
Utils::Hook::Set<BYTE>(0xA15F38, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::RefreshStatus()
|
void Console::RefreshStatus()
|
||||||
{
|
{
|
||||||
const auto mapname = Dvar::Var("mapname").get<std::string>();
|
const std::string mapname = (*Game::sv_mapname)->current.string;
|
||||||
const auto hostname = TextRenderer::StripColors(Dvar::Var("sv_hostname").get<std::string>());
|
const auto hostname = TextRenderer::StripColors((*Game::sv_hostname)->current.string);
|
||||||
|
|
||||||
if (Console::HasConsole)
|
if (Console::HasConsole)
|
||||||
{
|
{
|
||||||
@ -58,7 +46,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < maxclientCount; ++i)
|
for (int i = 0; i < maxclientCount; ++i)
|
||||||
{
|
{
|
||||||
if (Game::svs_clients[i].state >= 3)
|
if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED)
|
||||||
{
|
{
|
||||||
++clientCount;
|
++clientCount;
|
||||||
}
|
}
|
||||||
@ -568,6 +556,9 @@ namespace Components
|
|||||||
|
|
||||||
Console::Console()
|
Console::Console()
|
||||||
{
|
{
|
||||||
|
AssertOffset(Game::clientUIActive_t, connectionState, 0x9B8);
|
||||||
|
AssertOffset(Game::clientUIActive_t, keyCatchers, 0x9B0);
|
||||||
|
|
||||||
// Console '%s: %s> ' string
|
// Console '%s: %s> ' string
|
||||||
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x MP: " VERSION "> ");
|
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x MP: " VERSION "> ");
|
||||||
|
|
||||||
@ -580,8 +571,8 @@ namespace Components
|
|||||||
Utils::Hook::Set<BYTE>(0x431565, 0xEB);
|
Utils::Hook::Set<BYTE>(0x431565, 0xEB);
|
||||||
|
|
||||||
// Internal console
|
// Internal console
|
||||||
Utils::Hook(0x4F690C, Console::ToggleConsole, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4F690C, Console::Con_ToggleConsole, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x4F65A5, Console::ToggleConsole, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4F65A5, Console::Con_ToggleConsole, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Patch safearea for ingame-console
|
// Patch safearea for ingame-console
|
||||||
Utils::Hook(0x5A50EF, Console::DrawSolidConsoleStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5A50EF, Console::DrawSolidConsoleStub, HOOK_CALL).install()->quick();
|
||||||
|
@ -63,8 +63,7 @@ namespace Components
|
|||||||
static void StoreSafeArea();
|
static void StoreSafeArea();
|
||||||
static void RestoreSafeArea();
|
static void RestoreSafeArea();
|
||||||
|
|
||||||
static void ToggleConsole();
|
static const char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType);
|
||||||
static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType);
|
|
||||||
|
|
||||||
static void Con_ToggleConsole();
|
static void Con_ToggleConsole();
|
||||||
static void AddConsoleCommand();
|
static void AddConsoleCommand();
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
Dvar::Var Debug::DebugOverlay;
|
const Game::dvar_t* Debug::DebugOverlay;
|
||||||
|
const Game::dvar_t* Debug::BugName;
|
||||||
|
|
||||||
Game::dvar_t** Debug::PlayerDebugHealth = reinterpret_cast<Game::dvar_t**>(0x7A9F7C);
|
Game::dvar_t** Debug::PlayerDebugHealth = reinterpret_cast<Game::dvar_t**>(0x7A9F7C);
|
||||||
|
|
||||||
@ -90,6 +91,16 @@ namespace Components
|
|||||||
"EF_SOFT",
|
"EF_SOFT",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char Debug::strButtons[] =
|
||||||
|
{
|
||||||
|
'\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x0E', '\x0F', '\x10',
|
||||||
|
'\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\0'
|
||||||
|
};
|
||||||
|
|
||||||
|
const char Debug::strTemplate[] = "%s: %s All those moments will be lost in time, like tears in rain.";
|
||||||
|
|
||||||
|
const float Debug::colorWhite[] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
|
||||||
std::string Debug::BuildPMFlagsString(const Game::playerState_s* ps)
|
std::string Debug::BuildPMFlagsString(const Game::playerState_s* ps)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
@ -144,7 +155,6 @@ namespace Components
|
|||||||
auto* const scrPlace = Game::ScrPlace_GetActivePlacement(localClientNum);
|
auto* const scrPlace = Game::ScrPlace_GetActivePlacement(localClientNum);
|
||||||
|
|
||||||
constexpr auto maxChars = 4096;
|
constexpr auto maxChars = 4096;
|
||||||
constexpr float colorWhite[] = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
||||||
|
|
||||||
auto* const font1 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE_2);
|
auto* const font1 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE_2);
|
||||||
auto* const font2 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE2);
|
auto* const font2 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE2);
|
||||||
@ -153,16 +163,16 @@ namespace Components
|
|||||||
MY_SCALE2, colorWhite, 1);
|
MY_SCALE2, colorWhite, 1);
|
||||||
|
|
||||||
const auto pmf = BuildPMFlagsString(&cgameGlob->predictedPlayerState);
|
const auto pmf = BuildPMFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, pmf.data(), maxChars, font1, 30.0f, 20.0f, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, pmf.data(), maxChars, font1, 30.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3);
|
||||||
|
|
||||||
const auto pof = BuildPOFlagsString(&cgameGlob->predictedPlayerState);
|
const auto pof = BuildPOFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, pof.data(), maxChars, font1, 350.0f, 20.0f, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, pof.data(), maxChars, font1, 350.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3);
|
||||||
|
|
||||||
const auto plf = BuildPLFlagsString(&cgameGlob->predictedPlayerState);
|
const auto plf = BuildPLFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, plf.data(), maxChars, font1, 350.0f, 250.0f, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, plf.data(), maxChars, font1, 350.0f, 250.0f, 1, 1, MY_SCALE_2, colorWhite, 3);
|
||||||
|
|
||||||
const auto pef = BuildPEFlagsString(&cgameGlob->predictedPlayerState);
|
const auto pef = BuildPEFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, pef.data(), maxChars, font1, 525.0f, 20.0f, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, pef.data(), maxChars, font1, 525.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::CG_DrawDebugPlayerHealth(const int localClientNum)
|
void Debug::CG_DrawDebugPlayerHealth(const int localClientNum)
|
||||||
@ -196,13 +206,49 @@ namespace Components
|
|||||||
Game::CL_DrawStretchPic(scrPlace, 10.0f, 10.0f, 100.0f * healtha, 10.0f, 1, 1, 0.0f, 0.0f, healtha, 1.0f, color2, *Game::whiteMaterial);
|
Game::CL_DrawStretchPic(scrPlace, 10.0f, 10.0f, 100.0f * healtha, 10.0f, 1, 1, 0.0f, 0.0f, healtha, 1.0f, color2, *Game::whiteMaterial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debug::CG_Debug_DrawFontTest(const int localClientNum)
|
||||||
|
{
|
||||||
|
char strFinal[0x200]{};
|
||||||
|
|
||||||
|
auto* const scrPlace = Game::ScrPlace_GetActivePlacement(localClientNum);
|
||||||
|
|
||||||
|
auto* const font1 = Game::UI_GetFontHandle(scrPlace, 1, 0.4f);
|
||||||
|
auto* const font2 = Game::UI_GetFontHandle(scrPlace, 2, 0.4f);
|
||||||
|
auto* const font3 = Game::UI_GetFontHandle(scrPlace, 3, 0.4f);
|
||||||
|
auto* const font5 = Game::UI_GetFontHandle(scrPlace, 5, 0.4f);
|
||||||
|
auto* const font6 = Game::UI_GetFontHandle(scrPlace, 6, 0.4f);
|
||||||
|
|
||||||
|
sprintf_s(strFinal, strTemplate, font1->fontName, strButtons);
|
||||||
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font1, MY_X, 10.0f, 1, 1, 0.4f, colorWhite, 3);
|
||||||
|
|
||||||
|
sprintf_s(strFinal, strTemplate, font2->fontName, strButtons);
|
||||||
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font2, MY_X, 35.0f, 1, 1, 0.4f, colorWhite, 3);
|
||||||
|
|
||||||
|
sprintf_s(strFinal, strTemplate, font3->fontName, strButtons);
|
||||||
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font3, MY_X, 60.0f, 1, 1, 0.4f, colorWhite, 3);
|
||||||
|
|
||||||
|
sprintf_s(strFinal, strTemplate, font5->fontName, strButtons);
|
||||||
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font5, MY_X, 85.0f, 1, 1, 0.4f, colorWhite, 3);
|
||||||
|
|
||||||
|
sprintf_s(strFinal, strTemplate, font6->fontName, strButtons);
|
||||||
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font6, MY_X, 110.0f, 1, 1, 0.4f, colorWhite, 3);
|
||||||
|
}
|
||||||
|
|
||||||
void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum)
|
void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum)
|
||||||
{
|
{
|
||||||
switch (DebugOverlay.get<int>())
|
switch (DebugOverlay->current.integer)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
CG_Debug_DrawPSFlags(localClientNum);
|
CG_Debug_DrawPSFlags(localClientNum);
|
||||||
break;
|
break;
|
||||||
|
case 5:
|
||||||
|
CG_Debug_DrawFontTest(localClientNum);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -215,7 +261,57 @@ namespace Components
|
|||||||
|
|
||||||
void Debug::Com_Assert_f()
|
void Debug::Com_Assert_f()
|
||||||
{
|
{
|
||||||
assert(("a", false));
|
assert(0 && "a");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::Cbuf_AddServerText_f_Hk()
|
||||||
|
{
|
||||||
|
assert(0 && "Cbuf_AddServerText_f was called.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug::Com_Bug_f(Command::Params* params)
|
||||||
|
{
|
||||||
|
char newFileName[0x105]{};
|
||||||
|
char to_ospath[MAX_PATH]{};
|
||||||
|
char from_ospath[MAX_PATH]{};
|
||||||
|
const char* bug;
|
||||||
|
|
||||||
|
if (!*Game::logfile)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "CopyFile failed: logfile wasn't opened\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params->size() == 2)
|
||||||
|
{
|
||||||
|
bug = params->get(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(BugName);
|
||||||
|
bug = BugName->current.string;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf_s(newFileName, "%s_%s.log", bug, Game::Live_GetLocalClientName(0));
|
||||||
|
|
||||||
|
Game::Sys_EnterCriticalSection(Game::CRITSECT_CONSOLE);
|
||||||
|
|
||||||
|
if (*Game::logfile)
|
||||||
|
{
|
||||||
|
Game::FS_FCloseFile(*Game::logfile);
|
||||||
|
*Game::logfile = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::FS_BuildOSPath(Game::Sys_DefaultInstallPath(), "", "logs/console_mp.log", from_ospath);
|
||||||
|
Game::FS_BuildOSPath(Game::Sys_DefaultInstallPath(), "", newFileName, to_ospath);
|
||||||
|
const auto result = CopyFileA(from_ospath, to_ospath, 0);
|
||||||
|
Game::Com_OpenLogFile();
|
||||||
|
|
||||||
|
Game::Sys_LeaveCriticalSection(Game::CRITSECT_CONSOLE);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
Logger::PrintError(1, "CopyFile failed({}) {} {}\n", GetLastError(), "console_mp.log", newFileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::CL_InitDebugDvars()
|
void Debug::CL_InitDebugDvars()
|
||||||
@ -233,6 +329,8 @@ namespace Components
|
|||||||
|
|
||||||
DebugOverlay = Game::Dvar_RegisterEnum("debugOverlay", debugOverlayNames_0, 0,
|
DebugOverlay = Game::Dvar_RegisterEnum("debugOverlay", debugOverlayNames_0, 0,
|
||||||
Game::DVAR_NONE, "Toggles the display of various debug info.");
|
Game::DVAR_NONE, "Toggles the display of various debug info.");
|
||||||
|
BugName = Game::Dvar_RegisterString("bug_name", "bug0",
|
||||||
|
Game::DVAR_CHEAT | Game::DVAR_CODINFO, "Name appended to the copied console log");
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug::Debug()
|
Debug::Debug()
|
||||||
@ -243,5 +341,10 @@ namespace Components
|
|||||||
Utils::Hook(0x49CB0A, CG_DrawDebugOverlays_Hk, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x49CB0A, CG_DrawDebugOverlays_Hk, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Utils::Hook::Set<void(*)()>(0x60BCEA, Com_Assert_f);
|
Utils::Hook::Set<void(*)()>(0x60BCEA, Com_Assert_f);
|
||||||
|
Utils::Hook(Game::Cbuf_AddServerText_f, Cbuf_AddServerText_f_Hk, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
Command::Add("bug", Com_Bug_f);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ namespace Components
|
|||||||
Debug();
|
Debug();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Dvar::Var DebugOverlay;
|
static const Game::dvar_t* DebugOverlay;
|
||||||
|
static const Game::dvar_t* BugName;
|
||||||
|
|
||||||
// Game dvars
|
// Game dvars
|
||||||
static Game::dvar_t** PlayerDebugHealth;
|
static Game::dvar_t** PlayerDebugHealth;
|
||||||
@ -18,9 +19,17 @@ namespace Components
|
|||||||
static const char* PLFlagsValues[];
|
static const char* PLFlagsValues[];
|
||||||
static const char* PEFlagsValues[];
|
static const char* PEFlagsValues[];
|
||||||
|
|
||||||
|
static const char strButtons[];
|
||||||
|
static const char strTemplate[];
|
||||||
|
|
||||||
static constexpr auto MY_SCALE2 = 0.5f;
|
static constexpr auto MY_SCALE2 = 0.5f;
|
||||||
static constexpr auto MY_SCALE_2 = 0.201f;
|
static constexpr auto MY_SCALE_2 = 0.201f;
|
||||||
|
|
||||||
|
static constexpr auto MY_X = -25.0f;
|
||||||
|
static constexpr auto MY_Y = 20.0f;
|
||||||
|
|
||||||
|
static const float colorWhite[];
|
||||||
|
|
||||||
static std::string BuildPMFlagsString(const Game::playerState_s* ps);
|
static std::string BuildPMFlagsString(const Game::playerState_s* ps);
|
||||||
static std::string BuildPOFlagsString(const Game::playerState_s* ps);
|
static std::string BuildPOFlagsString(const Game::playerState_s* ps);
|
||||||
static std::string BuildPLFlagsString(const Game::playerState_s* ps);
|
static std::string BuildPLFlagsString(const Game::playerState_s* ps);
|
||||||
@ -28,10 +37,13 @@ namespace Components
|
|||||||
|
|
||||||
static void CG_Debug_DrawPSFlags(int localClientNum);
|
static void CG_Debug_DrawPSFlags(int localClientNum);
|
||||||
static void CG_DrawDebugPlayerHealth(int localClientNum);
|
static void CG_DrawDebugPlayerHealth(int localClientNum);
|
||||||
|
static void CG_Debug_DrawFontTest(int localClientNum);
|
||||||
|
|
||||||
static void CG_DrawDebugOverlays_Hk(int localClientNum);
|
static void CG_DrawDebugOverlays_Hk(int localClientNum);
|
||||||
|
|
||||||
static void Com_Assert_f();
|
static void Com_Assert_f();
|
||||||
|
static void Cbuf_AddServerText_f_Hk();
|
||||||
|
static void Com_Bug_f(Command::Params* params);
|
||||||
|
|
||||||
static void CL_InitDebugDvars();
|
static void CL_InitDebugDvars();
|
||||||
};
|
};
|
||||||
|
@ -81,9 +81,9 @@ namespace Components
|
|||||||
{
|
{
|
||||||
std::string list = Utils::String::VA("%c", 20);
|
std::string list = Utils::String::VA("%c", 20);
|
||||||
|
|
||||||
for (int i = 0; i < 18; ++i)
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
||||||
{
|
{
|
||||||
if (Game::svs_clients[i].state >= 3)
|
if (Game::svs_clients[i].header.state >= 3)
|
||||||
{
|
{
|
||||||
list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID));
|
list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID));
|
||||||
|
|
||||||
@ -104,12 +104,12 @@ namespace Components
|
|||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
const auto partyEnable = Dvar::Var("party_enable").get<bool>();
|
const auto partyEnable = Dvar::Var("party_enable").get<bool>();
|
||||||
auto mapname = Dvar::Var("mapname").get<std::string>();
|
std::string mapname = (*Game::sv_mapname)->current.string;
|
||||||
|
|
||||||
if (!partyEnable) // Time wrapping should not occur in party servers, but yeah...
|
if (!partyEnable) // Time wrapping should not occur in party servers, but yeah...
|
||||||
{
|
{
|
||||||
if (mapname.empty()) mapname = "mp_rust";
|
if (mapname.empty()) mapname = "mp_rust";
|
||||||
Command::Execute(Utils::String::VA("map %s", mapname.data()), false);
|
Command::Execute(Utils::String::VA("map %s", mapname.data()), true);
|
||||||
}
|
}
|
||||||
}, Scheduler::Pipeline::SERVER);
|
}, Scheduler::Pipeline::SERVER);
|
||||||
|
|
||||||
@ -138,71 +138,6 @@ namespace Components
|
|||||||
return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::DVAR_NONE, description);
|
return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::DVAR_NONE, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dedicated::AddDedicatedCommands()
|
|
||||||
{
|
|
||||||
// Say command
|
|
||||||
Command::AddSV("say", [](Command::Params* params)
|
|
||||||
{
|
|
||||||
if (params->size() < 2) return;
|
|
||||||
|
|
||||||
auto message = params->join(1);
|
|
||||||
auto name = Dvar::Var("sv_sayName").get<std::string>();
|
|
||||||
|
|
||||||
if (!name.empty())
|
|
||||||
{
|
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data()));
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{}: {}\n", name, message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data()));
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Console: {}\n", message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tell command
|
|
||||||
Command::AddSV("tell", [](Command::Params* params)
|
|
||||||
{
|
|
||||||
if (params->size() < 3) return;
|
|
||||||
|
|
||||||
const auto client = atoi(params->get(1));
|
|
||||||
auto message = params->join(2);
|
|
||||||
auto name = Dvar::Var("sv_sayName").get<std::string>();
|
|
||||||
|
|
||||||
if (!name.empty())
|
|
||||||
{
|
|
||||||
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data()));
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{} -> {}: {}\n", name, client, message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data()));
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Console -> {}: {}\n", client, message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sayraw command
|
|
||||||
Command::AddSV("sayraw", [](Command::Params* params)
|
|
||||||
{
|
|
||||||
if (params->size() < 2) return;
|
|
||||||
|
|
||||||
auto message = params->join(1);
|
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data()));
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw: {}\n", message);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tellraw command
|
|
||||||
Command::AddSV("tellraw", [](Command::Params* params)
|
|
||||||
{
|
|
||||||
if (params->size() < 3) return;
|
|
||||||
|
|
||||||
const auto client = atoi(params->get(1));
|
|
||||||
std::string message = params->join(2);
|
|
||||||
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data()));
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw -> {}: {}\n", client, message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Dedicated::Dedicated()
|
Dedicated::Dedicated()
|
||||||
{
|
{
|
||||||
Dedicated::COMLogFilter = Dvar::Register<bool>("com_logFilter", true,
|
Dedicated::COMLogFilter = Dvar::Register<bool>("com_logFilter", true,
|
||||||
@ -281,12 +216,9 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
Dvar::Register<const char*>("sv_sayName", "^7Console", Game::DVAR_NONE, "The name to pose as for 'say' commands");
|
|
||||||
Dvar::Register<const char*>("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers");
|
Dvar::Register<const char*>("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Events::OnSVInit(Dedicated::AddDedicatedCommands);
|
|
||||||
|
|
||||||
// Post initialization point
|
// Post initialization point
|
||||||
Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
@ -330,7 +262,7 @@ namespace Components
|
|||||||
|
|
||||||
Scheduler::Loop([]
|
Scheduler::Loop([]
|
||||||
{
|
{
|
||||||
if (Dvar::Var("sv_running").get<bool>())
|
if ((*Game::com_sv_running)->current.enabled)
|
||||||
{
|
{
|
||||||
Dedicated::TransmitGuids();
|
Dedicated::TransmitGuids();
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,5 @@ namespace Components
|
|||||||
static void TimeWrapStub(Game::errorParm_t code, const char* message);
|
static void TimeWrapStub(Game::errorParm_t code, const char* message);
|
||||||
|
|
||||||
static Game::dvar_t* Dvar_RegisterSVNetworkFps(const char* dvarName, int value, int min, int max, int flags, const char* description);
|
static Game::dvar_t* Dvar_RegisterSVNetworkFps(const char* dvarName, int value, int min, int max, int flags, const char* description);
|
||||||
|
|
||||||
static void AddDedicatedCommands();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -46,37 +46,37 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnPacket("discovery", [](Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnClientPacket("discovery", [](Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (address.isSelf()) return;
|
if (address.isSelf()) return;
|
||||||
|
|
||||||
if (!address.isLocal())
|
if (!address.isLocal())
|
||||||
{
|
{
|
||||||
Logger::Print("Received discovery request from non-local address: {}\n", address.getCString());
|
Logger::Print("Received discovery request from non-local address: {}\n", address.getString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print("Received discovery request from {}\n", address.getCString());
|
Logger::Print("Received discovery request from {}\n", address.getString());
|
||||||
Network::SendCommand(address, "discoveryResponse", data);
|
Network::SendCommand(address, "discoveryResponse", data);
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnPacket("discoveryResponse", [](Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnClientPacket("discoveryResponse", [](Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (address.isSelf()) return;
|
if (address.isSelf()) return;
|
||||||
|
|
||||||
if (!address.isLocal())
|
if (!address.isLocal())
|
||||||
{
|
{
|
||||||
Logger::Print("Received discovery response from non-local address: {}\n", address.getCString());
|
Logger::Print("Received discovery response from non-local address: {}\n", address.getString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils::ParseChallenge(data) != Discovery::Challenge)
|
if (Utils::ParseChallenge(data) != Discovery::Challenge)
|
||||||
{
|
{
|
||||||
Logger::Print("Received discovery with invalid challenge from: {}\n", address.getCString());
|
Logger::Print("Received discovery with invalid challenge from: {}\n", address.getString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print("Received discovery response from: {}\n", address.getCString());
|
Logger::Print("Received discovery response from: {}\n", address.getString());
|
||||||
|
|
||||||
if (ServerList::IsOfflineList())
|
if (ServerList::IsOfflineList())
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,6 @@ namespace Components
|
|||||||
{
|
{
|
||||||
mg_mgr Download::Mgr;
|
mg_mgr Download::Mgr;
|
||||||
Download::ClientDownload Download::CLDownload;
|
Download::ClientDownload Download::CLDownload;
|
||||||
std::vector<std::shared_ptr<Download::ScriptDownload>> Download::ScriptDownloads;
|
|
||||||
|
|
||||||
std::thread Download::ServerThread;
|
std::thread Download::ServerThread;
|
||||||
bool Download::Terminate;
|
bool Download::Terminate;
|
||||||
@ -63,7 +62,7 @@ namespace Components
|
|||||||
download->files.clear();
|
download->files.clear();
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
json11::Json listData = json11::Json::parse(list, error);
|
nlohmann::json listData = nlohmann::json::parse(list);
|
||||||
|
|
||||||
if (!error.empty() || !listData.is_array())
|
if (!error.empty() || !listData.is_array())
|
||||||
{
|
{
|
||||||
@ -72,8 +71,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
download->totalBytes = 0;
|
download->totalBytes = 0;
|
||||||
|
nlohmann::json::array_t listDataArray = listData;
|
||||||
|
|
||||||
for (auto& file : listData.array_items())
|
for (auto& file : listDataArray)
|
||||||
{
|
{
|
||||||
if (!file.is_object()) return false;
|
if (!file.is_object()) return false;
|
||||||
|
|
||||||
@ -84,9 +84,9 @@ namespace Components
|
|||||||
if (!hash.is_string() || !name.is_string() || !size.is_number()) return false;
|
if (!hash.is_string() || !name.is_string() || !size.is_number()) return false;
|
||||||
|
|
||||||
Download::ClientDownload::File fileEntry;
|
Download::ClientDownload::File fileEntry;
|
||||||
fileEntry.name = name.string_value();
|
fileEntry.name = name.get<std::string>();
|
||||||
fileEntry.hash = hash.string_value();
|
fileEntry.hash = hash.get<std::string>();
|
||||||
fileEntry.size = static_cast<size_t>(size.number_value());
|
fileEntry.size = size.get<size_t>();
|
||||||
|
|
||||||
if (!fileEntry.name.empty())
|
if (!fileEntry.name.empty())
|
||||||
{
|
{
|
||||||
@ -353,9 +353,9 @@ namespace Components
|
|||||||
// Run this on the main thread
|
// Run this on the main thread
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
auto fsGame = Dvar::Var("fs_game");
|
Game::Dvar_SetString(*Game::fs_gameDirVar, mod.data());
|
||||||
fsGame.set(mod);
|
const_cast<Game::dvar_t*>(*Game::fs_gameDirVar)->modified = true;
|
||||||
fsGame.get<Game::dvar_t*>()->modified = true;
|
|
||||||
mod.clear();
|
mod.clear();
|
||||||
|
|
||||||
Command::Execute("closemenu mod_download_popmenu", false);
|
Command::Execute("closemenu mod_download_popmenu", false);
|
||||||
@ -387,9 +387,9 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Game::client_t* client = &Game::svs_clients[i];
|
Game::client_t* client = &Game::svs_clients[i];
|
||||||
|
|
||||||
if (client->state >= 3)
|
if (client->header.state >= Game::CS_CONNECTED)
|
||||||
{
|
{
|
||||||
if (address.getIP().full == Network::Address(client->netchan.remoteAddress).getIP().full)
|
if (address.getIP().full == Network::Address(client->header.netchan.remoteAddress).getIP().full)
|
||||||
{
|
{
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
@ -502,14 +502,14 @@ namespace Components
|
|||||||
// Only handle http requests
|
// Only handle http requests
|
||||||
if (ev != MG_EV_HTTP_REQUEST) return;
|
if (ev != MG_EV_HTTP_REQUEST) return;
|
||||||
|
|
||||||
std::vector<json11::Json> servers;
|
std::vector<nlohmann::json> servers;
|
||||||
|
|
||||||
// Build server list
|
// Build server list
|
||||||
for (auto& node : Node::GetNodes())
|
for (auto& node : Node::GetNodes())
|
||||||
{
|
{
|
||||||
if (node.isValid())
|
if (node.isValid())
|
||||||
{
|
{
|
||||||
servers.push_back(json11::Json{ node });
|
servers.push_back(nlohmann::json{ node.to_json()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,7 +519,7 @@ namespace Components
|
|||||||
"Connection: close\r\n"
|
"Connection: close\r\n"
|
||||||
"Access-Control-Allow-Origin: *\r\n"
|
"Access-Control-Allow-Origin: *\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"%s", json11::Json(servers).dump().data());
|
"%s", nlohmann::json(servers).dump().data());
|
||||||
|
|
||||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
}
|
}
|
||||||
@ -532,17 +532,17 @@ namespace Components
|
|||||||
if (!Download::VerifyPassword(nc, reinterpret_cast<http_message*>(ev_data))) return;
|
if (!Download::VerifyPassword(nc, reinterpret_cast<http_message*>(ev_data))) return;
|
||||||
|
|
||||||
static std::string mapnamePre;
|
static std::string mapnamePre;
|
||||||
static json11::Json jsonList;
|
static nlohmann::json jsonList;
|
||||||
|
|
||||||
std::string mapname = (Party::IsInUserMapLobby() ? Dvar::Var("ui_mapname").get<std::string>() : Maps::GetUserMap()->getName());
|
std::string mapname = (Party::IsInUserMapLobby() ? Dvar::Var("ui_mapname").get<std::string>() : Maps::GetUserMap()->getName());
|
||||||
if (!Maps::GetUserMap()->isValid() && !Party::IsInUserMapLobby())
|
if (!Maps::GetUserMap()->isValid() && !Party::IsInUserMapLobby())
|
||||||
{
|
{
|
||||||
mapnamePre.clear();
|
mapnamePre.clear();
|
||||||
jsonList = std::vector<json11::Json>();
|
jsonList = std::vector<nlohmann::json>();
|
||||||
}
|
}
|
||||||
else if (!mapname.empty() && mapname != mapnamePre)
|
else if (!mapname.empty() && mapname != mapnamePre)
|
||||||
{
|
{
|
||||||
std::vector<json11::Json> fileList;
|
std::vector<nlohmann::json> fileList;
|
||||||
|
|
||||||
mapnamePre = mapname;
|
mapnamePre = mapname;
|
||||||
|
|
||||||
@ -553,7 +553,7 @@ namespace Components
|
|||||||
std::string filename = path + "\\" + mapname + Maps::UserMapFiles[i];
|
std::string filename = path + "\\" + mapname + Maps::UserMapFiles[i];
|
||||||
if (Utils::IO::FileExists(filename))
|
if (Utils::IO::FileExists(filename))
|
||||||
{
|
{
|
||||||
std::map<std::string, json11::Json> file;
|
std::map<std::string, nlohmann::json> file;
|
||||||
std::string fileBuffer = Utils::IO::ReadFile(filename);
|
std::string fileBuffer = Utils::IO::ReadFile(filename);
|
||||||
|
|
||||||
file["name"] = mapname + Maps::UserMapFiles[i];
|
file["name"] = mapname + Maps::UserMapFiles[i];
|
||||||
@ -591,13 +591,13 @@ namespace Components
|
|||||||
// else
|
// else
|
||||||
{
|
{
|
||||||
static std::string fsGamePre;
|
static std::string fsGamePre;
|
||||||
static json11::Json jsonList;
|
static nlohmann::json jsonList;
|
||||||
|
|
||||||
std::string fsGame = Dvar::Var("fs_game").get<std::string>();
|
const std::string fsGame = (*Game::fs_gameDirVar)->current.string;
|
||||||
|
|
||||||
if (!fsGame.empty() && fsGame != fsGamePre)
|
if (!fsGame.empty() && fsGame != fsGamePre)
|
||||||
{
|
{
|
||||||
std::vector<json11::Json> fileList;
|
std::vector<nlohmann::json> fileList;
|
||||||
|
|
||||||
fsGamePre = fsGame;
|
fsGamePre = fsGame;
|
||||||
|
|
||||||
@ -611,7 +611,7 @@ namespace Components
|
|||||||
std::string filename = path + "\\" + *i;
|
std::string filename = path + "\\" + *i;
|
||||||
if (strstr(i->data(), "_svr_") == nullptr && Utils::IO::FileExists(filename))
|
if (strstr(i->data(), "_svr_") == nullptr && Utils::IO::FileExists(filename))
|
||||||
{
|
{
|
||||||
std::map<std::string, json11::Json> file;
|
std::map<std::string, nlohmann::json> file;
|
||||||
std::string fileBuffer = Utils::IO::ReadFile(filename);
|
std::string fileBuffer = Utils::IO::ReadFile(filename);
|
||||||
|
|
||||||
file["name"] = *i;
|
file["name"] = *i;
|
||||||
@ -697,7 +697,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string file;
|
std::string file;
|
||||||
std::string fsGame = Dvar::Var("fs_game").get<std::string>();
|
const std::string fsGame = (*Game::fs_gameDirVar)->current.string;
|
||||||
std::string path = Dvar::Var("fs_basepath").get<std::string>() + "\\" + (isMap ? "" : fsGame + "\\") + url;
|
std::string path = Dvar::Var("fs_basepath").get<std::string>() + "\\" + (isMap ? "" : fsGame + "\\") + url;
|
||||||
|
|
||||||
if ((!isMap && fsGame.empty()) || !Utils::IO::ReadFile(path, &file))
|
if ((!isMap && fsGame.empty()) || !Utils::IO::ReadFile(path, &file))
|
||||||
@ -735,23 +735,23 @@ namespace Components
|
|||||||
Utils::InfoString status = ServerInfo::GetInfo();
|
Utils::InfoString status = ServerInfo::GetInfo();
|
||||||
Utils::InfoString host = ServerInfo::GetHostInfo();
|
Utils::InfoString host = ServerInfo::GetHostInfo();
|
||||||
|
|
||||||
std::map<std::string, json11::Json> info;
|
std::map<std::string, nlohmann::json> info;
|
||||||
info["status"] = status.to_json();
|
info["status"] = status.to_json();
|
||||||
info["host"] = host.to_json();
|
info["host"] = host.to_json();
|
||||||
|
|
||||||
std::vector<json11::Json> players;
|
std::vector<nlohmann::json> players;
|
||||||
|
|
||||||
// Build player list
|
// Build player list
|
||||||
for (int i = 0; i < atoi(status.get("sv_maxclients").data()); ++i) // Maybe choose 18 here?
|
for (int i = 0; i < atoi(status.get("sv_maxclients").data()); ++i) // Maybe choose 18 here?
|
||||||
{
|
{
|
||||||
std::map<std::string, json11::Json> playerInfo;
|
std::map<std::string, nlohmann::json> playerInfo;
|
||||||
playerInfo["score"] = 0;
|
playerInfo["score"] = 0;
|
||||||
playerInfo["ping"] = 0;
|
playerInfo["ping"] = 0;
|
||||||
playerInfo["name"] = "";
|
playerInfo["name"] = "";
|
||||||
|
|
||||||
if (Dvar::Var("sv_running").get<bool>())
|
if ((*Game::com_sv_running)->current.enabled)
|
||||||
{
|
{
|
||||||
if (Game::svs_clients[i].state < 3) continue;
|
if (Game::svs_clients[i].header.state < Game::CS_CONNECTED) continue;
|
||||||
|
|
||||||
playerInfo["score"] = Game::SV_GameClientNum_Score(i);
|
playerInfo["score"] = Game::SV_GameClientNum_Score(i);
|
||||||
playerInfo["ping"] = Game::svs_clients[i].ping;
|
playerInfo["ping"] = Game::svs_clients[i].ping;
|
||||||
@ -760,13 +760,13 @@ namespace Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Score and ping are irrelevant
|
// Score and ping are irrelevant
|
||||||
const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData*>(0x1081C00), i);
|
const char* namePtr = Game::PartyHost_GetMemberName(Game::g_lobbyData, i);
|
||||||
if (!namePtr || !namePtr[0]) continue;
|
if (!namePtr || !namePtr[0]) continue;
|
||||||
|
|
||||||
playerInfo["name"] = namePtr;
|
playerInfo["name"] = namePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
players.push_back(playerInfo);
|
players.emplace_back(playerInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
info["players"] = players;
|
info["players"] = players;
|
||||||
@ -777,7 +777,7 @@ namespace Components
|
|||||||
"Connection: close\r\n"
|
"Connection: close\r\n"
|
||||||
"Access-Control-Allow-Origin: *\r\n"
|
"Access-Control-Allow-Origin: *\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"%s", json11::Json(info).dump().data());
|
"%s", nlohmann::json(info).dump().data());
|
||||||
|
|
||||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||||
}
|
}
|
||||||
@ -909,7 +909,7 @@ namespace Components
|
|||||||
Dvar::Register<const char*>("ui_dl_transRate", "", Game::DVAR_NONE, "");
|
Dvar::Register<const char*>("ui_dl_transRate", "", Game::DVAR_NONE, "");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
UIScript::Add("mod_download_cancel", [](UIScript::Token)
|
UIScript::Add("mod_download_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Download::CLDownload.clear();
|
Download::CLDownload.clear();
|
||||||
});
|
});
|
||||||
@ -926,77 +926,8 @@ namespace Components
|
|||||||
Dvar::Register<bool>("mod_force_download_server", false, Game::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches).");
|
Dvar::Register<bool>("mod_force_download_server", false, Game::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches).");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Scheduler::Loop([]
|
Script::AddFunction("HttpGet", Script::ShowDeprecationWarning);
|
||||||
{
|
Script::AddFunction("HttpCancel", Script::ShowDeprecationWarning);
|
||||||
int workingCount = 0;
|
|
||||||
|
|
||||||
for (auto i = Download::ScriptDownloads.begin(); i != Download::ScriptDownloads.end();)
|
|
||||||
{
|
|
||||||
auto download = *i;
|
|
||||||
|
|
||||||
if (download->isDone())
|
|
||||||
{
|
|
||||||
download->notifyDone();
|
|
||||||
i = Download::ScriptDownloads.erase(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (download->isWorking())
|
|
||||||
{
|
|
||||||
download->notifyProgress();
|
|
||||||
++workingCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& download : Download::ScriptDownloads)
|
|
||||||
{
|
|
||||||
if (workingCount > 5) break;
|
|
||||||
if (!download->isWorking())
|
|
||||||
{
|
|
||||||
download->startWorking();
|
|
||||||
++workingCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, Scheduler::Pipeline::MAIN);
|
|
||||||
|
|
||||||
Events::OnVMShutdown([]
|
|
||||||
{
|
|
||||||
Download::ScriptDownloads.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("HttpGet", []
|
|
||||||
{
|
|
||||||
const auto* url = Game::Scr_GetString(0);
|
|
||||||
|
|
||||||
if (url == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1HttpGet: Illegal parameter!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto object = Game::AllocObject();
|
|
||||||
|
|
||||||
Game::Scr_AddObject(object);
|
|
||||||
|
|
||||||
Download::ScriptDownloads.push_back(std::make_shared<ScriptDownload>(url, object));
|
|
||||||
Game::RemoveRefToObject(object);
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("HttpCancel", []
|
|
||||||
{
|
|
||||||
const auto object = Game::Scr_GetObject(0);
|
|
||||||
for (const auto& download : Download::ScriptDownloads)
|
|
||||||
{
|
|
||||||
if (object == download->getObject())
|
|
||||||
{
|
|
||||||
download->cancel();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Download::~Download()
|
Download::~Download()
|
||||||
@ -1019,7 +950,5 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Download::CLDownload.clear();
|
Download::CLDownload.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Download::ScriptDownloads.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <mongoose.h>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -81,132 +82,8 @@ namespace Components
|
|||||||
size_t receivedBytes;
|
size_t receivedBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScriptDownload
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ScriptDownload(const std::string& _url, unsigned int _object) : url(_url), object(_object), webIO(nullptr), done(false), notifyRequired(false), totalSize(0), currentSize(0)
|
|
||||||
{
|
|
||||||
Game::AddRefToObject(this->getObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptDownload(ScriptDownload&& other) noexcept = delete;
|
|
||||||
ScriptDownload& operator=(ScriptDownload&& other) noexcept = delete;
|
|
||||||
|
|
||||||
~ScriptDownload()
|
|
||||||
{
|
|
||||||
if (this->getObject())
|
|
||||||
{
|
|
||||||
Game::RemoveRefToObject(this->getObject());
|
|
||||||
this->object = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->workerThread.joinable())
|
|
||||||
{
|
|
||||||
this->workerThread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->destroyWebIO();
|
|
||||||
}
|
|
||||||
|
|
||||||
void startWorking()
|
|
||||||
{
|
|
||||||
if (!this->isWorking())
|
|
||||||
{
|
|
||||||
this->workerThread = std::thread(std::bind(&ScriptDownload::handler, this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isWorking()
|
|
||||||
{
|
|
||||||
return this->workerThread.joinable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void notifyProgress()
|
|
||||||
{
|
|
||||||
if (this->notifyRequired)
|
|
||||||
{
|
|
||||||
this->notifyRequired = false;
|
|
||||||
|
|
||||||
if (Game::Scr_IsSystemActive())
|
|
||||||
{
|
|
||||||
Game::Scr_AddInt(static_cast<int>(this->totalSize));
|
|
||||||
Game::Scr_AddInt(static_cast<int>(this->currentSize));
|
|
||||||
Game::Scr_NotifyId(this->getObject(), Game::SL_GetString("progress", 0), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateProgress(size_t _currentSize, size_t _toalSize)
|
|
||||||
{
|
|
||||||
this->currentSize = _currentSize;
|
|
||||||
this->totalSize = _toalSize;
|
|
||||||
this->notifyRequired = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void notifyDone()
|
|
||||||
{
|
|
||||||
if (!this->isDone()) return;
|
|
||||||
|
|
||||||
if (Game::Scr_IsSystemActive())
|
|
||||||
{
|
|
||||||
Game::Scr_AddString(this->result.data()); // No binary data supported yet
|
|
||||||
Game::Scr_AddInt(this->success);
|
|
||||||
Game::Scr_NotifyId(this->getObject(), Game::SL_GetString("done", 0), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDone() { return this->done; };
|
|
||||||
|
|
||||||
std::string getUrl() { return this->url; }
|
|
||||||
unsigned int getObject() { return this->object; }
|
|
||||||
|
|
||||||
void cancel()
|
|
||||||
{
|
|
||||||
if (this->webIO)
|
|
||||||
{
|
|
||||||
this->webIO->cancelDownload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string url;
|
|
||||||
std::string result;
|
|
||||||
unsigned int object;
|
|
||||||
std::thread workerThread;
|
|
||||||
Utils::WebIO* webIO;
|
|
||||||
|
|
||||||
bool done;
|
|
||||||
bool success;
|
|
||||||
bool notifyRequired;
|
|
||||||
size_t totalSize;
|
|
||||||
size_t currentSize;
|
|
||||||
|
|
||||||
void handler()
|
|
||||||
{
|
|
||||||
this->destroyWebIO();
|
|
||||||
|
|
||||||
this->webIO = new Utils::WebIO("IW4x");
|
|
||||||
this->webIO->setProgressCallback(std::bind(&ScriptDownload::updateProgress, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
|
|
||||||
this->result = this->webIO->get(this->url, &this->success);
|
|
||||||
|
|
||||||
this->destroyWebIO();
|
|
||||||
this->done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroyWebIO()
|
|
||||||
{
|
|
||||||
if (this->webIO)
|
|
||||||
{
|
|
||||||
delete this->webIO;
|
|
||||||
this->webIO = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static mg_mgr Mgr;
|
static mg_mgr Mgr;
|
||||||
static ClientDownload CLDownload;
|
static ClientDownload CLDownload;
|
||||||
static std::vector<std::shared_ptr<ScriptDownload>> ScriptDownloads;
|
|
||||||
static std::thread ServerThread;
|
static std::thread ServerThread;
|
||||||
static bool Terminate;
|
static bool Terminate;
|
||||||
static bool ServerRunning;
|
static bool ServerRunning;
|
||||||
|
@ -4,33 +4,33 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const char* Dvar::ArchiveDvarPath = "userraw/archivedvars.cfg";
|
const char* Dvar::ArchiveDvarPath = "userraw/archivedvars.cfg";
|
||||||
|
|
||||||
Dvar::Var::Var(const std::string& dvarName) : Var()
|
Dvar::Var::Var(const std::string& dvarName)
|
||||||
{
|
{
|
||||||
this->dvar = Game::Dvar_FindVar(dvarName.data());
|
this->dvar_ = Game::Dvar_FindVar(dvarName.data());
|
||||||
|
|
||||||
// If the dvar can't be found it will be registered as an empty string dvar
|
// If the dvar can't be found it will be registered as an empty string dvar
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar_ == nullptr)
|
||||||
{
|
{
|
||||||
this->dvar = const_cast<Game::dvar_t*>(Game::Dvar_SetFromStringByNameFromSource(dvarName.data(), "",
|
this->dvar_ = const_cast<Game::dvar_t*>(Game::Dvar_SetFromStringByNameFromSource(dvarName.data(), "",
|
||||||
Game::DvarSetSource::DVAR_SOURCE_INTERNAL));
|
Game::DvarSetSource::DVAR_SOURCE_INTERNAL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> Game::dvar_t* Dvar::Var::get()
|
template <> Game::dvar_t* Dvar::Var::get()
|
||||||
{
|
{
|
||||||
return this->dvar;
|
return this->dvar_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> const char* Dvar::Var::get()
|
template <> const char* Dvar::Var::get()
|
||||||
{
|
{
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar_ == nullptr)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_STRING
|
if (this->dvar_->type == Game::DVAR_TYPE_STRING
|
||||||
|| this->dvar->type == Game::dvar_type::DVAR_TYPE_ENUM)
|
|| this->dvar_->type == Game::DVAR_TYPE_ENUM)
|
||||||
{
|
{
|
||||||
if (this->dvar->current.string != nullptr)
|
if (this->dvar_->current.string != nullptr)
|
||||||
return this->dvar->current.string;
|
return this->dvar_->current.string;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
@ -38,12 +38,12 @@ namespace Components
|
|||||||
|
|
||||||
template <> int Dvar::Var::get()
|
template <> int Dvar::Var::get()
|
||||||
{
|
{
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar_ == nullptr)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_INT || this->dvar->type == Game::dvar_type::DVAR_TYPE_ENUM)
|
if (this->dvar_->type == Game::DVAR_TYPE_INT || this->dvar_->type == Game::DVAR_TYPE_ENUM)
|
||||||
{
|
{
|
||||||
return this->dvar->current.integer;
|
return this->dvar_->current.integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -51,12 +51,12 @@ namespace Components
|
|||||||
|
|
||||||
template <> unsigned int Dvar::Var::get()
|
template <> unsigned int Dvar::Var::get()
|
||||||
{
|
{
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar_ == nullptr)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_INT)
|
if (this->dvar_->type == Game::DVAR_TYPE_INT)
|
||||||
{
|
{
|
||||||
return this->dvar->current.unsignedInt;
|
return this->dvar_->current.unsignedInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -64,12 +64,12 @@ namespace Components
|
|||||||
|
|
||||||
template <> float Dvar::Var::get()
|
template <> float Dvar::Var::get()
|
||||||
{
|
{
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar_ == nullptr)
|
||||||
return 0.f;
|
return 0.f;
|
||||||
|
|
||||||
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT)
|
if (this->dvar_->type == Game::DVAR_TYPE_FLOAT)
|
||||||
{
|
{
|
||||||
return this->dvar->current.value;
|
return this->dvar_->current.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0.f;
|
return 0.f;
|
||||||
@ -79,13 +79,13 @@ namespace Components
|
|||||||
{
|
{
|
||||||
static Game::vec4_t vector{0.f, 0.f, 0.f, 0.f};
|
static Game::vec4_t vector{0.f, 0.f, 0.f, 0.f};
|
||||||
|
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar_ == nullptr)
|
||||||
return vector;
|
return vector;
|
||||||
|
|
||||||
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_2 || this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_3
|
if (this->dvar_->type == Game::DVAR_TYPE_FLOAT_2 || this->dvar_->type == Game::DVAR_TYPE_FLOAT_3
|
||||||
|| this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_4)
|
|| this->dvar_->type == Game::DVAR_TYPE_FLOAT_4)
|
||||||
{
|
{
|
||||||
return this->dvar->current.vector;
|
return this->dvar_->current.vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vector;
|
return vector;
|
||||||
@ -93,12 +93,12 @@ namespace Components
|
|||||||
|
|
||||||
template <> bool Dvar::Var::get()
|
template <> bool Dvar::Var::get()
|
||||||
{
|
{
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar_ == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_BOOL)
|
if (this->dvar_->type == Game::DVAR_TYPE_BOOL)
|
||||||
{
|
{
|
||||||
return this->dvar->current.enabled;
|
return this->dvar_->current.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -111,10 +111,10 @@ namespace Components
|
|||||||
|
|
||||||
void Dvar::Var::set(const char* string)
|
void Dvar::Var::set(const char* string)
|
||||||
{
|
{
|
||||||
assert(this->dvar->type == Game::DVAR_TYPE_STRING);
|
assert(this->dvar_->type == Game::DVAR_TYPE_STRING);
|
||||||
if (this->dvar)
|
if (this->dvar_)
|
||||||
{
|
{
|
||||||
Game::Dvar_SetString(this->dvar, string);
|
Game::Dvar_SetString(this->dvar_, string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,58 +125,64 @@ namespace Components
|
|||||||
|
|
||||||
void Dvar::Var::set(int integer)
|
void Dvar::Var::set(int integer)
|
||||||
{
|
{
|
||||||
assert(this->dvar->type == Game::DVAR_TYPE_INT);
|
assert(this->dvar_->type == Game::DVAR_TYPE_INT);
|
||||||
if (this->dvar)
|
|
||||||
|
if (this->dvar_)
|
||||||
{
|
{
|
||||||
Game::Dvar_SetInt(this->dvar, integer);
|
Game::Dvar_SetInt(this->dvar_, integer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::Var::set(float value)
|
void Dvar::Var::set(float value)
|
||||||
{
|
{
|
||||||
assert(this->dvar->type == Game::DVAR_TYPE_FLOAT);
|
assert(this->dvar_->type == Game::DVAR_TYPE_FLOAT);
|
||||||
if (this->dvar)
|
|
||||||
|
if (this->dvar_)
|
||||||
{
|
{
|
||||||
Game::Dvar_SetFloat(this->dvar, value);
|
Game::Dvar_SetFloat(this->dvar_, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::Var::set(bool enabled)
|
void Dvar::Var::set(bool enabled)
|
||||||
{
|
{
|
||||||
assert(this->dvar->type == Game::DVAR_TYPE_BOOL);
|
assert(this->dvar_->type == Game::DVAR_TYPE_BOOL);
|
||||||
if (this->dvar)
|
|
||||||
|
if (this->dvar_)
|
||||||
{
|
{
|
||||||
Game::Dvar_SetBool(this->dvar, enabled);
|
Game::Dvar_SetBool(this->dvar_, enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::Var::setRaw(int integer)
|
void Dvar::Var::setRaw(int integer)
|
||||||
{
|
{
|
||||||
assert(this->dvar->type == Game::DVAR_TYPE_INT);
|
assert(this->dvar_->type == Game::DVAR_TYPE_INT);
|
||||||
if (this->dvar)
|
|
||||||
|
if (this->dvar_)
|
||||||
{
|
{
|
||||||
this->dvar->current.integer = integer;
|
this->dvar_->current.integer = integer;
|
||||||
this->dvar->latched.integer = integer;
|
this->dvar_->latched.integer = integer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::Var::setRaw(float value)
|
void Dvar::Var::setRaw(float value)
|
||||||
{
|
{
|
||||||
assert(this->dvar->type == Game::DVAR_TYPE_FLOAT);
|
assert(this->dvar_->type == Game::DVAR_TYPE_FLOAT);
|
||||||
if (this->dvar)
|
|
||||||
|
if (this->dvar_)
|
||||||
{
|
{
|
||||||
this->dvar->current.value = value;
|
this->dvar_->current.value = value;
|
||||||
this->dvar->latched.value = value;
|
this->dvar_->latched.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::Var::setRaw(bool enabled)
|
void Dvar::Var::setRaw(bool enabled)
|
||||||
{
|
{
|
||||||
assert(this->dvar->type == Game::DVAR_TYPE_BOOL);
|
assert(this->dvar_->type == Game::DVAR_TYPE_BOOL);
|
||||||
if (this->dvar)
|
|
||||||
|
if (this->dvar_)
|
||||||
{
|
{
|
||||||
this->dvar->current.enabled = enabled;
|
this->dvar_->current.enabled = enabled;
|
||||||
this->dvar->latched.enabled = enabled;
|
this->dvar_->latched.enabled = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,9 +17,9 @@ namespace Components
|
|||||||
class Var
|
class Var
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Var() : dvar(nullptr) {}
|
Var() : dvar_(nullptr) {}
|
||||||
Var(const Var& obj) { this->dvar = obj.dvar; }
|
Var(const Var& obj) { this->dvar_ = obj.dvar_; }
|
||||||
Var(Game::dvar_t* _dvar) : dvar(_dvar) {}
|
Var(Game::dvar_t* dvar) : dvar_(dvar) {}
|
||||||
Var(DWORD ppdvar) : Var(*reinterpret_cast<Game::dvar_t**>(ppdvar)) {}
|
Var(DWORD ppdvar) : Var(*reinterpret_cast<Game::dvar_t**>(ppdvar)) {}
|
||||||
Var(const std::string& dvarName);
|
Var(const std::string& dvarName);
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ namespace Components
|
|||||||
void setRaw(bool enabled);
|
void setRaw(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Game::dvar_t* dvar;
|
Game::dvar_t* dvar_;
|
||||||
};
|
};
|
||||||
|
|
||||||
Dvar();
|
Dvar();
|
||||||
|
@ -15,21 +15,23 @@ namespace Components
|
|||||||
int handle;
|
int handle;
|
||||||
const auto len = Game::FS_FOpenFileReadForThread(filePath.data(), &handle, thread);
|
const auto len = Game::FS_FOpenFileReadForThread(filePath.data(), &handle, thread);
|
||||||
|
|
||||||
if (handle)
|
if (!handle)
|
||||||
{
|
{
|
||||||
auto* buf = AllocateFile(len + 1);
|
return;
|
||||||
|
|
||||||
[[maybe_unused]] auto bytesRead = Game::FS_Read(buf, len, handle);
|
|
||||||
|
|
||||||
assert(bytesRead == len);
|
|
||||||
|
|
||||||
buf[len] = '\0';
|
|
||||||
|
|
||||||
Game::FS_FCloseFile(handle);
|
|
||||||
|
|
||||||
this->buffer.append(buf, len);
|
|
||||||
FreeFile(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* buf = AllocateFile(len + 1);
|
||||||
|
|
||||||
|
[[maybe_unused]] auto bytesRead = Game::FS_Read(buf, len, handle);
|
||||||
|
|
||||||
|
assert(bytesRead == len);
|
||||||
|
|
||||||
|
buf[len] = '\0';
|
||||||
|
|
||||||
|
Game::FS_FCloseFile(handle);
|
||||||
|
|
||||||
|
this->buffer.append(buf, len);
|
||||||
|
FreeFile(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystem::RawFile::read()
|
void FileSystem::RawFile::read()
|
||||||
@ -138,12 +140,28 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::filesystem::path FileSystem::GetAppdataPath()
|
||||||
|
{
|
||||||
|
PWSTR path;
|
||||||
|
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path)))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to read APPDATA path!");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto _0 = gsl::finally([&path]
|
||||||
|
{
|
||||||
|
CoTaskMemFree(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
return std::filesystem::path(path) / "xlabs";
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> FileSystem::GetFileList(const std::string& path, const std::string& extension)
|
std::vector<std::string> FileSystem::GetFileList(const std::string& path, const std::string& extension)
|
||||||
{
|
{
|
||||||
std::vector<std::string> fileList;
|
std::vector<std::string> fileList;
|
||||||
|
|
||||||
int numFiles = 0;
|
auto numFiles = 0;
|
||||||
char** files = Game::FS_GetFileList(path.data(), extension.data(), Game::FS_LIST_PURE_ONLY, &numFiles, 0);
|
const auto** files = Game::FS_ListFiles(path.data(), extension.data(), Game::FS_LIST_PURE_ONLY, &numFiles, 10);
|
||||||
|
|
||||||
if (files)
|
if (files)
|
||||||
{
|
{
|
||||||
@ -151,11 +169,11 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (files[i])
|
if (files[i])
|
||||||
{
|
{
|
||||||
fileList.push_back(files[i]);
|
fileList.emplace_back(files[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::FS_FreeFileList(files);
|
Game::FS_FreeFileList(files, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileList;
|
return fileList;
|
||||||
@ -165,8 +183,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
std::vector<std::string> fileList;
|
std::vector<std::string> fileList;
|
||||||
|
|
||||||
int numFiles = 0;
|
auto numFiles = 0;
|
||||||
char** files = Game::Sys_ListFiles(path.data(), extension.data(), nullptr, &numFiles, folders);
|
const auto** files = Game::Sys_ListFiles(path.data(), extension.data(), nullptr, &numFiles, folders);
|
||||||
|
|
||||||
if (files)
|
if (files)
|
||||||
{
|
{
|
||||||
@ -174,7 +192,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (files[i])
|
if (files[i])
|
||||||
{
|
{
|
||||||
fileList.push_back(files[i]);
|
fileList.emplace_back(files[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,9 +202,9 @@ namespace Components
|
|||||||
return fileList;
|
return fileList;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystem::DeleteFile(const std::string& folder, const std::string& file)
|
bool FileSystem::_DeleteFile(const std::string& folder, const std::string& file)
|
||||||
{
|
{
|
||||||
char path[MAX_PATH] = { 0 };
|
char path[MAX_PATH] = {0};
|
||||||
Game::FS_BuildPathToFile(Dvar::Var("fs_basepath").get<const char*>(), reinterpret_cast<char*>(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast<char**>(&path));
|
Game::FS_BuildPathToFile(Dvar::Var("fs_basepath").get<const char*>(), reinterpret_cast<char*>(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast<char**>(&path));
|
||||||
return Game::FS_Remove(path);
|
return Game::FS_Remove(path);
|
||||||
}
|
}
|
||||||
@ -225,9 +243,9 @@ namespace Components
|
|||||||
|
|
||||||
void FileSystem::RegisterFolder(const char* folder)
|
void FileSystem::RegisterFolder(const char* folder)
|
||||||
{
|
{
|
||||||
std::string fs_cdpath = Dvar::Var("fs_cdpath").get<std::string>();
|
const auto fs_cdpath = Dvar::Var("fs_cdpath").get<std::string>();
|
||||||
std::string fs_basepath = Dvar::Var("fs_basepath").get<std::string>();
|
const auto fs_basepath = Dvar::Var("fs_basepath").get<std::string>();
|
||||||
std::string fs_homepath = Dvar::Var("fs_homepath").get<std::string>();
|
const auto fs_homepath = Dvar::Var("fs_homepath").get<std::string>();
|
||||||
|
|
||||||
if (!fs_cdpath.empty()) Game::FS_AddLocalizedGameDirectory(fs_cdpath.data(), folder);
|
if (!fs_cdpath.empty()) Game::FS_AddLocalizedGameDirectory(fs_cdpath.data(), folder);
|
||||||
if (!fs_basepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_basepath.data(), folder);
|
if (!fs_basepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_basepath.data(), folder);
|
||||||
@ -305,6 +323,12 @@ namespace Components
|
|||||||
Utils::Hook::Call<void(void*)>(0x4291A0)(iwd);
|
Utils::Hook::Call<void(void*)>(0x4291A0)(iwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* FileSystem::Sys_DefaultInstallPath_Hk()
|
||||||
|
{
|
||||||
|
static auto current_path = std::filesystem::current_path().string();
|
||||||
|
return current_path.data();
|
||||||
|
}
|
||||||
|
|
||||||
FileSystem::FileSystem()
|
FileSystem::FileSystem()
|
||||||
{
|
{
|
||||||
FileSystem::MemAllocator.clear();
|
FileSystem::MemAllocator.clear();
|
||||||
@ -352,6 +376,9 @@ namespace Components
|
|||||||
|
|
||||||
// Handle IWD freeing
|
// Handle IWD freeing
|
||||||
Utils::Hook(0x642F60, FileSystem::IwdFreeStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x642F60, FileSystem::IwdFreeStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
// Set the working dir based on info from the Xlabs launcher
|
||||||
|
Utils::Hook(0x4326E0, FileSystem::Sys_DefaultInstallPath_Hk, HOOK_JUMP).install()->quick();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystem::~FileSystem()
|
FileSystem::~FileSystem()
|
||||||
|
@ -8,7 +8,7 @@ namespace Components
|
|||||||
class AbstractFile
|
class AbstractFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~AbstractFile() {};
|
virtual ~AbstractFile() = default;
|
||||||
|
|
||||||
virtual bool exists() = 0;
|
virtual bool exists() = 0;
|
||||||
virtual std::string getName() = 0;
|
virtual std::string getName() = 0;
|
||||||
@ -19,12 +19,12 @@ namespace Components
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
File() = default;
|
File() = default;
|
||||||
File(std::string file) : filePath{std::move(file)} { this->read(); };
|
File(std::string file) : filePath{std::move(file)} { this->read(); }
|
||||||
File(std::string file, Game::FsThread thread) : filePath{std::move(file)} { this->read(thread); };
|
File(std::string file, Game::FsThread thread) : filePath{std::move(file)} { this->read(thread); }
|
||||||
|
|
||||||
bool exists() override { return !this->buffer.empty(); };
|
bool exists() override { return !this->buffer.empty(); }
|
||||||
std::string getName() override { return this->filePath; };
|
std::string getName() override { return this->filePath; }
|
||||||
std::string& getBuffer() override { return this->buffer; };
|
std::string& getBuffer() override { return this->buffer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string filePath;
|
std::string filePath;
|
||||||
@ -36,12 +36,12 @@ namespace Components
|
|||||||
class RawFile : public AbstractFile
|
class RawFile : public AbstractFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RawFile() {};
|
RawFile() = default;
|
||||||
RawFile(const std::string& file) : filePath(file) { this->read(); };
|
RawFile(std::string file) : filePath(std::move(file)) { this->read(); }
|
||||||
|
|
||||||
bool exists() override { return !this->buffer.empty(); };
|
bool exists() override { return !this->buffer.empty(); }
|
||||||
std::string getName() override { return this->filePath; };
|
std::string getName() override { return this->filePath; }
|
||||||
std::string& getBuffer() override { return this->buffer; };
|
std::string& getBuffer() override { return this->buffer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string filePath;
|
std::string filePath;
|
||||||
@ -53,7 +53,7 @@ namespace Components
|
|||||||
class FileReader
|
class FileReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileReader() : handle(0), size(-1), name() {};
|
FileReader() : handle(0), size(-1), name() {}
|
||||||
FileReader(const std::string& file);
|
FileReader(const std::string& file);
|
||||||
~FileReader();
|
~FileReader();
|
||||||
|
|
||||||
@ -73,8 +73,8 @@ namespace Components
|
|||||||
class FileWriter
|
class FileWriter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileWriter(const std::string& file, bool append = false) : handle(0), filePath(file) { this->open(append); };
|
FileWriter(std::string file, bool append = false) : handle(0), filePath(std::move(file)) { this->open(append); }
|
||||||
~FileWriter() { this->close(); };
|
~FileWriter() { this->close(); }
|
||||||
|
|
||||||
void write(const std::string& data);
|
void write(const std::string& data);
|
||||||
|
|
||||||
@ -89,9 +89,10 @@ namespace Components
|
|||||||
FileSystem();
|
FileSystem();
|
||||||
~FileSystem();
|
~FileSystem();
|
||||||
|
|
||||||
|
static std::filesystem::path GetAppdataPath();
|
||||||
static std::vector<std::string> GetFileList(const std::string& path, const std::string& extension);
|
static std::vector<std::string> GetFileList(const std::string& path, const std::string& extension);
|
||||||
static std::vector<std::string> GetSysFileList(const std::string& path, const std::string& extension, bool folders = false);
|
static std::vector<std::string> GetSysFileList(const std::string& path, const std::string& extension, bool folders = false);
|
||||||
static bool DeleteFile(const std::string& folder, const std::string& file);
|
static bool _DeleteFile(const std::string& folder, const std::string& file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::mutex Mutex;
|
static std::mutex Mutex;
|
||||||
@ -115,5 +116,7 @@ namespace Components
|
|||||||
static int LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
|
static int LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
|
||||||
|
|
||||||
static void IwdFreeStub(Game::iwd_t* iwd);
|
static void IwdFreeStub(Game::iwd_t* iwd);
|
||||||
|
|
||||||
|
static const char* Sys_DefaultInstallPath_Hk();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -126,22 +126,14 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Friends::UpdateState(bool force)
|
void Friends::UpdateState()
|
||||||
{
|
{
|
||||||
if (Friends::CLAnonymous.get<bool>() || Friends::IsInvisible() || !Steam::Enabled()) return;
|
if (Friends::CLAnonymous.get<bool>() || Friends::IsInvisible() || !Steam::Enabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (force)
|
Friends::TriggerUpdate = true;
|
||||||
{
|
|
||||||
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamFriends)
|
|
||||||
{
|
|
||||||
int state = Steam::Proxy::SteamFriends->GetPersonaState();
|
|
||||||
Steam::Proxy::ClientFriends.invoke<void>("SetPersonaState", (state == 1 ? 2 : 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Friends::TriggerUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Friends::UpdateServer(Network::Address server, const std::string& hostname, const std::string& mapname)
|
void Friends::UpdateServer(Network::Address server, const std::string& hostname, const std::string& mapname)
|
||||||
@ -424,14 +416,13 @@ namespace Components
|
|||||||
{
|
{
|
||||||
return Utils::String::VA("%s", user.name.data());
|
return Utils::String::VA("%s", user.name.data());
|
||||||
}
|
}
|
||||||
else if (user.name == user.playerName)
|
|
||||||
|
if (user.name == user.playerName)
|
||||||
{
|
{
|
||||||
return Utils::String::VA("%s", user.name.data());
|
return Utils::String::VA("%s", user.name.data());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return Utils::String::VA("%s ^7(%s^7)", user.name.data(), user.playerName.data());
|
||||||
return Utils::String::VA("%s ^7(%s^7)", user.name.data(), user.playerName.data());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
@ -613,12 +604,12 @@ namespace Components
|
|||||||
// Show blue icons on the minimap
|
// Show blue icons on the minimap
|
||||||
Utils::Hook(0x493130, Friends::IsClientInParty, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x493130, Friends::IsClientInParty, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
UIScript::Add("LoadFriends", [](UIScript::Token)
|
UIScript::Add("LoadFriends", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Friends::UpdateFriends();
|
Friends::UpdateFriends();
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("JoinFriend", [](UIScript::Token)
|
UIScript::Add("JoinFriend", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
|
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
|
||||||
if (Friends::CurrentFriend >= Friends::FriendsList.size()) return;
|
if (Friends::CurrentFriend >= Friends::FriendsList.size()) return;
|
||||||
@ -660,7 +651,7 @@ namespace Components
|
|||||||
if (Friends::TriggerUpdate)
|
if (Friends::TriggerUpdate)
|
||||||
{
|
{
|
||||||
Friends::TriggerUpdate = false;
|
Friends::TriggerUpdate = false;
|
||||||
Friends::UpdateState(true);
|
Friends::UpdateState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ namespace Components
|
|||||||
static bool IsClientInParty(int controller, int clientNum);
|
static bool IsClientInParty(int controller, int clientNum);
|
||||||
|
|
||||||
static void UpdateUserInfo(SteamID user);
|
static void UpdateUserInfo(SteamID user);
|
||||||
static void UpdateState(bool force = false);
|
static void UpdateState();
|
||||||
|
|
||||||
static void SortList(bool force = false);
|
static void SortList(bool force = false);
|
||||||
static void SortIndividualList(std::vector<Friend>* list);
|
static void SortIndividualList(std::vector<Friend>* list);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
#include "Int64.hpp"
|
||||||
#include "IO.hpp"
|
#include "IO.hpp"
|
||||||
#include "Script.hpp"
|
#include "Script.hpp"
|
||||||
#include "ScriptExtension.hpp"
|
#include "ScriptExtension.hpp"
|
||||||
@ -9,6 +10,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
GSC::GSC()
|
GSC::GSC()
|
||||||
{
|
{
|
||||||
|
Loader::Register(new Int64());
|
||||||
Loader::Register(new IO());
|
Loader::Register(new IO());
|
||||||
Loader::Register(new Script());
|
Loader::Register(new Script());
|
||||||
Loader::Register(new ScriptExtension());
|
Loader::Register(new ScriptExtension());
|
||||||
|
95
src/Components/Modules/GSC/Int64.cpp
Normal file
95
src/Components/Modules/GSC/Int64.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
#include "Int64.hpp"
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
|
#define INT64_OPERATION(expr) [](const std::int64_t a, [[maybe_unused]] const std::int64_t b) { return expr; }
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, Int64::int64_OP> Int64::Operations =
|
||||||
|
{
|
||||||
|
{"+", INT64_OPERATION(a + b)},
|
||||||
|
{"-", INT64_OPERATION(a - b)},
|
||||||
|
{"*", INT64_OPERATION(a * b)},
|
||||||
|
{"/", INT64_OPERATION(a / b)},
|
||||||
|
{"&", INT64_OPERATION(a & b)},
|
||||||
|
{"^", INT64_OPERATION(a ^ b)},
|
||||||
|
{"|", INT64_OPERATION(a | b)},
|
||||||
|
{"~", INT64_OPERATION(~a)},
|
||||||
|
{"%", INT64_OPERATION(a % b)},
|
||||||
|
{">>", INT64_OPERATION(a >> b)},
|
||||||
|
{"<<", INT64_OPERATION(a << b)},
|
||||||
|
{"++", INT64_OPERATION(a + 1)},
|
||||||
|
{"--", INT64_OPERATION(a - 1)},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Int64::int64_Comp> Int64::Comparisons
|
||||||
|
{
|
||||||
|
{">", INT64_OPERATION(a > b)},
|
||||||
|
{">=", INT64_OPERATION(a >= b)},
|
||||||
|
{"==", INT64_OPERATION(a == b)},
|
||||||
|
{"<=", INT64_OPERATION(a <= b)},
|
||||||
|
{"<", INT64_OPERATION(a < b)},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::int64_t Int64::GetInt64Arg(unsigned int index, bool optional)
|
||||||
|
{
|
||||||
|
if ((optional) && (index >= Game::Scr_GetNumParam()))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::Scr_GetType(index) == Game::VAR_INTEGER)
|
||||||
|
{
|
||||||
|
return Game::Scr_GetInt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::Scr_GetType(index) == Game::VAR_STRING)
|
||||||
|
{
|
||||||
|
return std::strtoll(Game::Scr_GetString(index), nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_ParamError(index, Utils::String::VA("cannot cast %s to int64", Game::Scr_GetTypeName(index)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Int64::AddFunctions()
|
||||||
|
{
|
||||||
|
Script::AddFunction("Int64IsInt", []
|
||||||
|
{
|
||||||
|
const auto value = GetInt64Arg(0, false);
|
||||||
|
Game::Scr_AddBool(value <= std::numeric_limits<std::int32_t>::max() && value >= std::numeric_limits<std::int32_t>::min());
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("Int64ToInt", []
|
||||||
|
{
|
||||||
|
Game::Scr_AddInt(static_cast<std::int32_t>(GetInt64Arg(0, false)));
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("Int64OP", []
|
||||||
|
{
|
||||||
|
const auto a = GetInt64Arg(0, false);
|
||||||
|
const auto* op = Game::Scr_GetString(1);
|
||||||
|
const auto b = GetInt64Arg(2, true);
|
||||||
|
|
||||||
|
if (const auto got = Operations.find(op); got != Operations.end())
|
||||||
|
{
|
||||||
|
Game::Scr_AddString(Utils::String::VA("%lld", got->second(a, b)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto got = Comparisons.find(op); got != Comparisons.end())
|
||||||
|
{
|
||||||
|
Game::Scr_AddBool(got->second(a, b));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_ParamError(1, "Invalid int64 operation");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Int64::Int64()
|
||||||
|
{
|
||||||
|
AddFunctions();
|
||||||
|
}
|
||||||
|
}
|
20
src/Components/Modules/GSC/Int64.hpp
Normal file
20
src/Components/Modules/GSC/Int64.hpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class Int64 : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Int64();
|
||||||
|
|
||||||
|
private:
|
||||||
|
using int64_OP = std::function<std::int64_t(std::int64_t, std::int64_t)>;
|
||||||
|
using int64_Comp = std::function<bool(std::int64_t, std::int64_t)>;
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, int64_OP> Operations;
|
||||||
|
static std::unordered_map<std::string, int64_Comp> Comparisons;
|
||||||
|
|
||||||
|
static std::int64_t GetInt64Arg(unsigned int index, bool optional);
|
||||||
|
static void AddFunctions();
|
||||||
|
};
|
||||||
|
}
|
@ -50,12 +50,10 @@ namespace Components
|
|||||||
|
|
||||||
void Script::RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage)
|
void Script::RuntimeError(const char* codePos, unsigned int index, const char* msg, const char* dialogMessage)
|
||||||
{
|
{
|
||||||
const auto developer = Dvar::Var("developer").get<int>();
|
|
||||||
|
|
||||||
// Allow error messages to be printed if developer mode is on
|
// Allow error messages to be printed if developer mode is on
|
||||||
// Should check scrVarPub.developer but it's absent
|
// Should check scrVarPub.developer but it's absent
|
||||||
// in this version of the game so let's check the dvar
|
// in this version of the game so let's check the dvar
|
||||||
if (!Game::scrVmPub->terminal_error && !developer)
|
if (!Game::scrVmPub->terminal_error && !(*Game::com_developer)->current.integer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If were are developing let's call RuntimeErrorInternal
|
// If were are developing let's call RuntimeErrorInternal
|
||||||
@ -227,42 +225,44 @@ namespace Components
|
|||||||
Game::Scr_StartupGameType();
|
Game::Scr_StartupGameType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not use C++ objects because Scr_LoadScript may longjmp
|
||||||
void Script::GScr_LoadGameTypeScript_Stub()
|
void Script::GScr_LoadGameTypeScript_Stub()
|
||||||
{
|
{
|
||||||
// Clear handles (from previous GSC loading session)
|
// Clear handles (from previous GSC loading session)
|
||||||
Script::ScriptMainHandles.clear();
|
Script::ScriptMainHandles.clear();
|
||||||
Script::ScriptInitHandles.clear();
|
Script::ScriptInitHandles.clear();
|
||||||
|
|
||||||
const auto list = FileSystem::GetFileList("scripts/", "gsc");
|
char path[MAX_PATH]{};
|
||||||
|
|
||||||
for (const auto& file : list)
|
auto numFiles = 0;
|
||||||
|
const auto** files = Game::FS_ListFiles("scripts/", "gsc", Game::FS_LIST_ALL, &numFiles, 10);
|
||||||
|
|
||||||
|
for (auto i = 0; i < numFiles; ++i)
|
||||||
{
|
{
|
||||||
std::string script = "scripts/" + file;
|
const auto* scriptFile = files[i];
|
||||||
|
Logger::Print("Loading script {}...\n", scriptFile);
|
||||||
|
|
||||||
if (Utils::String::EndsWith(script, ".gsc"))
|
sprintf_s(path, "%s/%s", "scripts", scriptFile);
|
||||||
|
|
||||||
|
// Scr_LoadScriptInternal will add the '.gsc' suffix so we remove it
|
||||||
|
path[std::strlen(path) - 4] = '\0';
|
||||||
|
|
||||||
|
if (!Game::Scr_LoadScript(path))
|
||||||
{
|
{
|
||||||
script = script.substr(0, script.size() - 4);
|
Logger::Print("Script {} encountered an error while loading. (doesn't exist?)", path);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print("Loading script {}.gsc...\n", script);
|
Logger::Print("Script {}.gsc loaded successfully.\n", path);
|
||||||
|
|
||||||
if (!Game::Scr_LoadScript(script.data()))
|
|
||||||
{
|
|
||||||
Logger::Print("Script {} encountered an error while loading. (doesn't exist?)", script);
|
|
||||||
Logger::Error(Game::ERR_DROP, "Could not find script '{}'", script);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::Print("Script {}.gsc loaded successfully.\n", script);
|
|
||||||
Logger::Debug("Finding script handle main or init...");
|
Logger::Debug("Finding script handle main or init...");
|
||||||
|
|
||||||
const auto initHandle = Game::Scr_GetFunctionHandle(script.data(), "init");
|
const auto initHandle = Game::Scr_GetFunctionHandle(path, "init");
|
||||||
if (initHandle != 0)
|
if (initHandle != 0)
|
||||||
{
|
{
|
||||||
Script::ScriptInitHandles.push_back(initHandle);
|
Script::ScriptInitHandles.push_back(initHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto mainHandle = Game::Scr_GetFunctionHandle(script.data(), "main");
|
const auto mainHandle = Game::Scr_GetFunctionHandle(path, "main");
|
||||||
if (mainHandle != 0)
|
if (mainHandle != 0)
|
||||||
{
|
{
|
||||||
Script::ScriptMainHandles.push_back(mainHandle);
|
Script::ScriptMainHandles.push_back(mainHandle);
|
||||||
@ -271,6 +271,7 @@ namespace Components
|
|||||||
// Allow scripts with no handles
|
// Allow scripts with no handles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Game::FS_FreeFileList(files, 10);
|
||||||
Game::GScr_LoadGameTypeScript();
|
Game::GScr_LoadGameTypeScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,18 +398,16 @@ namespace Components
|
|||||||
{
|
{
|
||||||
// execute our hook
|
// execute our hook
|
||||||
pushad
|
pushad
|
||||||
|
|
||||||
call Script::StoreScriptBaseProgramNum
|
call Script::StoreScriptBaseProgramNum
|
||||||
|
|
||||||
popad
|
popad
|
||||||
|
|
||||||
// execute overwritten code caused by the jump hook
|
// execute overwritten code caused by the jump hook
|
||||||
sub eax, ds:201A460h // gScrVarPub_programBuffer
|
sub eax, ds:201A460h // gScrVarPub_programBuffer
|
||||||
add esp, 0Ch
|
add esp, 0Ch
|
||||||
mov ds : 1CFEEF8h, eax // gScrCompilePub_programLen
|
mov ds:1CFEEF8h, eax // gScrCompilePub_programLen
|
||||||
|
|
||||||
// jump back to the original code
|
// jump back to the original code
|
||||||
push 426C3Bh
|
push 426C3Bh
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,6 +534,14 @@ namespace Components
|
|||||||
return &Game::svs_clients[ent->s.number];
|
return &Game::svs_clients[ent->s.number];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Script::ShowDeprecationWarning()
|
||||||
|
{
|
||||||
|
Toast::Show("cardicon_gumby", "WARNING!", "You are using deprecated HttpGet/HttpCancel GSC function.", 2048);
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SCRIPT, "*** DEPRECATION WARNING ***\n");
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Attempted to execute deprecated built-in HttpGet/HttpCancel! These functions have been deemed unsafe and are scheduled for removal. Please update your mod!\n");
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SCRIPT, "***************************\n");
|
||||||
|
}
|
||||||
|
|
||||||
void Script::AddFunctions()
|
void Script::AddFunctions()
|
||||||
{
|
{
|
||||||
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
||||||
@ -632,7 +639,7 @@ namespace Components
|
|||||||
Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook::Nop(0x61E933, 1);
|
Utils::Hook::Nop(0x61E933, 1);
|
||||||
|
|
||||||
Scheduler::Loop([]()
|
Scheduler::Loop([]
|
||||||
{
|
{
|
||||||
if (!Game::SV_Loaded())
|
if (!Game::SV_Loaded())
|
||||||
return;
|
return;
|
||||||
@ -641,11 +648,12 @@ namespace Components
|
|||||||
|
|
||||||
if (Script::LastFrameTime != -1)
|
if (Script::LastFrameTime != -1)
|
||||||
{
|
{
|
||||||
const auto timeScale = Dvar::Var("timescale").get<float>();
|
const auto timeTaken = (nowMs - Script::LastFrameTime) * static_cast<int>((*Game::com_timescale)->current.value);
|
||||||
const auto timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * timeScale);
|
|
||||||
|
|
||||||
if (timeTaken >= 500)
|
if (timeTaken >= 500)
|
||||||
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Hitch warning: {} msec frame time\n", timeTaken);
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Hitch warning: {} msec frame time\n", timeTaken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Script::LastFrameTime = nowMs;
|
Script::LastFrameTime = nowMs;
|
||||||
|
@ -14,6 +14,8 @@ namespace Components
|
|||||||
|
|
||||||
static const char* GetCodePosForParam(int index);
|
static const char* GetCodePosForParam(int index);
|
||||||
|
|
||||||
|
static void ShowDeprecationWarning();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ScriptFunction
|
struct ScriptFunction
|
||||||
{
|
{
|
||||||
@ -59,7 +61,6 @@ namespace Components
|
|||||||
static void Scr_StartupGameType_Stub();
|
static void Scr_StartupGameType_Stub();
|
||||||
static void GScr_LoadGameTypeScript_Stub();
|
static void GScr_LoadGameTypeScript_Stub();
|
||||||
|
|
||||||
static bool IsDeprecated(const std::string& name);
|
|
||||||
static Game::BuiltinFunction BuiltIn_GetFunctionStub(const char** pName, int* type);
|
static Game::BuiltinFunction BuiltIn_GetFunctionStub(const char** pName, int* type);
|
||||||
static Game::BuiltinMethod BuiltIn_GetMethodStub(const char** pName, int* type);
|
static Game::BuiltinMethod BuiltIn_GetMethodStub(const char** pName, int* type);
|
||||||
|
|
||||||
|
@ -175,16 +175,16 @@ namespace Components
|
|||||||
// Func present on IW5
|
// Func present on IW5
|
||||||
Script::AddFunction("IsEndStr", [] // gsc: IsEndStr(<string>, <string>)
|
Script::AddFunction("IsEndStr", [] // gsc: IsEndStr(<string>, <string>)
|
||||||
{
|
{
|
||||||
const auto* s1 = Game::Scr_GetString(0);
|
const auto* str = Game::Scr_GetString(0);
|
||||||
const auto* s2 = Game::Scr_GetString(1);
|
const auto* suffix = Game::Scr_GetString(1);
|
||||||
|
|
||||||
if (s1 == nullptr || s2 == nullptr)
|
if (str == nullptr || suffix == nullptr)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1IsEndStr: Illegal parameters!\n");
|
Game::Scr_Error("^1IsEndStr: Illegal parameters!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::Scr_AddBool(Utils::String::EndsWith(s1, s2));
|
Game::Scr_AddBool(Utils::String::EndsWith(str, suffix));
|
||||||
});
|
});
|
||||||
|
|
||||||
Script::AddFunction("IsArray", [] // gsc: IsArray(<object>)
|
Script::AddFunction("IsArray", [] // gsc: IsArray(<object>)
|
||||||
@ -206,6 +206,41 @@ namespace Components
|
|||||||
|
|
||||||
Game::Scr_AddBool(result);
|
Game::Scr_AddBool(result);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Func present on IW5
|
||||||
|
Script::AddFunction("CastFloat", [] // gsc: CastFloat()
|
||||||
|
{
|
||||||
|
switch (Game::Scr_GetType(0))
|
||||||
|
{
|
||||||
|
case Game::VAR_STRING:
|
||||||
|
Game::Scr_AddFloat(static_cast<float>(std::atof(Game::Scr_GetString(0))));
|
||||||
|
break;
|
||||||
|
case Game::VAR_FLOAT:
|
||||||
|
Game::Scr_AddFloat(Game::Scr_GetFloat(0));
|
||||||
|
break;
|
||||||
|
case Game::VAR_INTEGER:
|
||||||
|
Game::Scr_AddFloat(static_cast<float>(Game::Scr_GetInt(0)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Game::Scr_ParamError(0, Utils::String::VA("cannot cast %s to float", Game::Scr_GetTypeName(0)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("Strtol", [] // gsc: Strtol(<string>, <int>)
|
||||||
|
{
|
||||||
|
const auto* input = Game::Scr_GetString(0);
|
||||||
|
const auto base = Game::Scr_GetInt(1);
|
||||||
|
|
||||||
|
char* end;
|
||||||
|
const auto result = std::strtol(input, &end, base);
|
||||||
|
if (input == end)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "cannot cast string to int");
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_AddInt(result);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptExtension::AddMethods()
|
void ScriptExtension::AddMethods()
|
||||||
@ -216,7 +251,7 @@ namespace Components
|
|||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
const auto* client = Script::GetClient(ent);
|
const auto* client = Script::GetClient(ent);
|
||||||
|
|
||||||
std::string ip = Game::NET_AdrToString(client->netchan.remoteAddress);
|
std::string ip = Game::NET_AdrToString(client->header.netchan.remoteAddress);
|
||||||
|
|
||||||
if (const auto pos = ip.find_first_of(":"); pos != std::string::npos)
|
if (const auto pos = ip.find_first_of(":"); pos != std::string::npos)
|
||||||
ip.erase(ip.begin() + pos, ip.end()); // Erase port
|
ip.erase(ip.begin() + pos, ip.end()); // Erase port
|
||||||
@ -241,7 +276,7 @@ namespace Components
|
|||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
auto* client = Script::GetClient(ent);
|
auto* client = Script::GetClient(ent);
|
||||||
|
|
||||||
client->ping = static_cast<int16_t>(ping);
|
client->ping = ping;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const json11::Json json = Data;
|
const nlohmann::json json = Data;
|
||||||
|
|
||||||
FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump());
|
FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump());
|
||||||
});
|
});
|
||||||
|
@ -169,8 +169,8 @@ namespace Components
|
|||||||
{Game::K_APAD_RIGHT, Game::K_RIGHTARROW},
|
{Game::K_APAD_RIGHT, Game::K_RIGHTARROW},
|
||||||
};
|
};
|
||||||
|
|
||||||
Gamepad::GamePad Gamepad::gamePads[Game::MAX_GAMEPADS]{};
|
Gamepad::GamePad Gamepad::gamePads[Game::MAX_GPAD_COUNT]{};
|
||||||
Gamepad::GamePadGlobals Gamepad::gamePadGlobals[Game::MAX_GAMEPADS]{{}};
|
Gamepad::GamePadGlobals Gamepad::gamePadGlobals[Game::MAX_GPAD_COUNT]{{}};
|
||||||
int Gamepad::gamePadBindingsModifiedFlags = 0;
|
int Gamepad::gamePadBindingsModifiedFlags = 0;
|
||||||
|
|
||||||
Dvar::Var Gamepad::gpad_enabled;
|
Dvar::Var Gamepad::gpad_enabled;
|
||||||
@ -241,12 +241,12 @@ namespace Components
|
|||||||
mov dl, byte ptr[edi + 1Ah] // to_forwardMove
|
mov dl, byte ptr[edi + 1Ah] // to_forwardMove
|
||||||
mov dh, byte ptr[edi + 1Bh] // to_rightMove
|
mov dh, byte ptr[edi + 1Bh] // to_rightMove
|
||||||
|
|
||||||
mov[esp + 30h], dx // to_buttons
|
mov [esp + 30h], dx // to_buttons
|
||||||
|
|
||||||
mov dl, byte ptr[ebp + 1Ah] // from_forwardMove
|
mov dl, byte ptr [ebp + 1Ah] // from_forwardMove
|
||||||
mov dh, byte ptr[ebp + 1Bh] // from_rightMove
|
mov dh, byte ptr [ebp + 1Bh] // from_rightMove
|
||||||
|
|
||||||
mov[esp + 2Ch], dx // from_buttons
|
mov [esp + 2Ch], dx // from_buttons
|
||||||
|
|
||||||
// return back
|
// return back
|
||||||
push 0x60E40E
|
push 0x60E40E
|
||||||
@ -261,7 +261,7 @@ namespace Components
|
|||||||
|
|
||||||
if (Game::MSG_ReadBit(msg))
|
if (Game::MSG_ReadBit(msg))
|
||||||
{
|
{
|
||||||
short movementBits = static_cast<short>(key ^ Game::MSG_ReadBits(msg, 16));
|
const auto movementBits = static_cast<short>(key ^ Game::MSG_ReadBits(msg, 16));
|
||||||
|
|
||||||
forward = static_cast<char>(movementBits);
|
forward = static_cast<char>(movementBits);
|
||||||
right = static_cast<char>(movementBits >> 8);
|
right = static_cast<char>(movementBits >> 8);
|
||||||
@ -314,7 +314,8 @@ namespace Components
|
|||||||
|
|
||||||
bool Gamepad::GPad_Check(const int gamePadIndex, const int portIndex)
|
bool Gamepad::GPad_Check(const int gamePadIndex, const int portIndex)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
if (XInputGetCapabilities(portIndex, XINPUT_FLAG_GAMEPAD, &gamePad.caps) == ERROR_SUCCESS)
|
if (XInputGetCapabilities(portIndex, XINPUT_FLAG_GAMEPAD, &gamePad.caps) == ERROR_SUCCESS)
|
||||||
@ -332,7 +333,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
auto currentGamePadNum = 0;
|
auto currentGamePadNum = 0;
|
||||||
|
|
||||||
for (auto currentPort = 0; currentPort < XUSER_MAX_COUNT && currentGamePadNum < Game::MAX_GAMEPADS; currentPort++)
|
for (auto currentPort = 0; currentPort < XUSER_MAX_COUNT && currentGamePadNum < Game::MAX_GPAD_COUNT; currentPort++)
|
||||||
{
|
{
|
||||||
if (GPad_Check(currentGamePadNum, currentPort))
|
if (GPad_Check(currentGamePadNum, currentPort))
|
||||||
currentGamePadNum++;
|
currentGamePadNum++;
|
||||||
@ -366,7 +367,7 @@ namespace Components
|
|||||||
bool Gamepad::AimAssist_IsPlayerUsingOffhand(Game::AimAssistPlayerState* ps)
|
bool Gamepad::AimAssist_IsPlayerUsingOffhand(Game::AimAssistPlayerState* ps)
|
||||||
{
|
{
|
||||||
// Check offhand flag
|
// Check offhand flag
|
||||||
if ((ps->weapFlags & 2) == 0)
|
if ((ps->weapFlags & Game::PWF_USING_OFFHAND) == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If offhand weapon has no id we are not using one
|
// If offhand weapon has no id we are not using one
|
||||||
@ -421,7 +422,8 @@ namespace Components
|
|||||||
|
|
||||||
bool Gamepad::AimAssist_IsLockonActive(const int gamePadIndex)
|
bool Gamepad::AimAssist_IsLockonActive(const int gamePadIndex)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& aaGlob = Game::aaGlobArray[gamePadIndex];
|
auto& aaGlob = Game::aaGlobArray[gamePadIndex];
|
||||||
|
|
||||||
if (!aim_lockon_enabled.get<bool>() || !gpad_lockon_enabled.get<bool>())
|
if (!aim_lockon_enabled.get<bool>() || !gpad_lockon_enabled.get<bool>())
|
||||||
@ -439,7 +441,9 @@ namespace Components
|
|||||||
void Gamepad::AimAssist_ApplyLockOn(const Game::AimInput* input, Game::AimOutput* output)
|
void Gamepad::AimAssist_ApplyLockOn(const Game::AimInput* input, Game::AimOutput* output)
|
||||||
{
|
{
|
||||||
assert(input);
|
assert(input);
|
||||||
assert(input->localClientNum < Game::MAX_GAMEPADS);
|
assert(output);
|
||||||
|
AssertIn(input->localClientNum, Game::STATIC_MAX_LOCAL_CLIENTS);
|
||||||
|
|
||||||
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
||||||
|
|
||||||
const auto prevTargetEnt = aaGlob.lockOnTargetEnt;
|
const auto prevTargetEnt = aaGlob.lockOnTargetEnt;
|
||||||
@ -554,10 +558,11 @@ namespace Components
|
|||||||
void Gamepad::AimAssist_CalcSlowdown(const Game::AimInput* input, float* pitchScale, float* yawScale)
|
void Gamepad::AimAssist_CalcSlowdown(const Game::AimInput* input, float* pitchScale, float* yawScale)
|
||||||
{
|
{
|
||||||
assert(input);
|
assert(input);
|
||||||
assert(input->localClientNum < Game::MAX_GAMEPADS);
|
|
||||||
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
|
||||||
assert(pitchScale);
|
assert(pitchScale);
|
||||||
assert(yawScale);
|
assert(yawScale);
|
||||||
|
AssertIn(input->localClientNum, Game::STATIC_MAX_LOCAL_CLIENTS);
|
||||||
|
|
||||||
|
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
||||||
|
|
||||||
*pitchScale = 1.0f;
|
*pitchScale = 1.0f;
|
||||||
*yawScale = 1.0f;
|
*yawScale = 1.0f;
|
||||||
@ -586,7 +591,10 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::AimAssist_ApplyTurnRates(const Game::AimInput* input, Game::AimOutput* output)
|
void Gamepad::AimAssist_ApplyTurnRates(const Game::AimInput* input, Game::AimOutput* output)
|
||||||
{
|
{
|
||||||
assert(input->localClientNum < Game::MAX_GAMEPADS);
|
assert(input);
|
||||||
|
assert(output);
|
||||||
|
AssertIn(input->localClientNum, Game::STATIC_MAX_LOCAL_CLIENTS);
|
||||||
|
|
||||||
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
||||||
|
|
||||||
auto slowdownPitchScale = 0.0f;
|
auto slowdownPitchScale = 0.0f;
|
||||||
@ -649,7 +657,8 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::AimAssist_UpdateGamePadInput(const Game::AimInput* input, Game::AimOutput* output)
|
void Gamepad::AimAssist_UpdateGamePadInput(const Game::AimInput* input, Game::AimOutput* output)
|
||||||
{
|
{
|
||||||
assert(input->localClientNum < Game::MAX_GAMEPADS);
|
AssertIn(input->localClientNum, Game::STATIC_MAX_LOCAL_CLIENTS);
|
||||||
|
|
||||||
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
auto& aaGlob = Game::aaGlobArray[input->localClientNum];
|
||||||
|
|
||||||
output->pitch = input->pitch;
|
output->pitch = input->pitch;
|
||||||
@ -784,8 +793,8 @@ namespace Components
|
|||||||
|
|
||||||
float Gamepad::CL_GamepadAxisValue(const int gamePadIndex, const Game::GamepadVirtualAxis virtualAxis)
|
float Gamepad::CL_GamepadAxisValue(const int gamePadIndex, const Game::GamepadVirtualAxis virtualAxis)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
|
||||||
assert(virtualAxis > Game::GPAD_VIRTAXIS_NONE && virtualAxis < Game::GPAD_VIRTAXIS_COUNT);
|
assert(virtualAxis > Game::GPAD_VIRTAXIS_NONE && virtualAxis < Game::GPAD_VIRTAXIS_COUNT);
|
||||||
|
|
||||||
const auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
const auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
||||||
|
|
||||||
const auto& [physicalAxis, mapType] = gamePadGlobal.axes.virtualAxes[virtualAxis];
|
const auto& [physicalAxis, mapType] = gamePadGlobal.axes.virtualAxes[virtualAxis];
|
||||||
@ -818,7 +827,8 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::CL_GamepadMove(const int gamePadIndex, Game::usercmd_s* cmd, const float frameTimeBase)
|
void Gamepad::CL_GamepadMove(const int gamePadIndex, Game::usercmd_s* cmd, const float frameTimeBase)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
auto& clientActive = Game::clients[gamePadIndex];
|
auto& clientActive = Game::clients[gamePadIndex];
|
||||||
|
|
||||||
@ -960,7 +970,8 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::CL_GamepadResetMenuScrollTime(const int gamePadIndex, const int key, const bool down, const unsigned time)
|
void Gamepad::CL_GamepadResetMenuScrollTime(const int gamePadIndex, const int key, const bool down, const unsigned time)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
||||||
|
|
||||||
if (!down)
|
if (!down)
|
||||||
@ -979,8 +990,8 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::CL_GamepadGenerateAPad(const int gamePadIndex, const Game::GamepadPhysicalAxis physicalAxis, unsigned time)
|
void Gamepad::CL_GamepadGenerateAPad(const int gamePadIndex, const Game::GamepadPhysicalAxis physicalAxis, unsigned time)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
assert(physicalAxis < Game::GPAD_PHYSAXIS_COUNT && physicalAxis >= 0);
|
assert(physicalAxis >= 0 && physicalAxis < Game::GPAD_PHYSAXIS_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
@ -1014,8 +1025,8 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::CL_GamepadEvent(const int gamePadIndex, const Game::GamepadPhysicalAxis physicalAxis, const float value, const unsigned time)
|
void Gamepad::CL_GamepadEvent(const int gamePadIndex, const Game::GamepadPhysicalAxis physicalAxis, const float value, const unsigned time)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
assert(physicalAxis < Game::GPAD_PHYSAXIS_COUNT && physicalAxis >= 0);
|
assert(physicalAxis >= 0 && physicalAxis < Game::GPAD_PHYSAXIS_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
||||||
@ -1054,7 +1065,8 @@ namespace Components
|
|||||||
|
|
||||||
bool Gamepad::Scoreboard_HandleInput(int gamePadIndex, int key)
|
bool Gamepad::Scoreboard_HandleInput(int gamePadIndex, int key)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::STATIC_MAX_LOCAL_CLIENTS);
|
||||||
|
|
||||||
auto& keyState = Game::playerKeys[gamePadIndex];
|
auto& keyState = Game::playerKeys[gamePadIndex];
|
||||||
|
|
||||||
if (keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "togglescores") == 0)
|
if (keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "togglescores") == 0)
|
||||||
@ -1080,7 +1092,8 @@ namespace Components
|
|||||||
|
|
||||||
bool Gamepad::CL_CheckForIgnoreDueToRepeat(const int gamePadIndex, const int key, const int repeatCount, const unsigned time)
|
bool Gamepad::CL_CheckForIgnoreDueToRepeat(const int gamePadIndex, const int key, const int repeatCount, const unsigned time)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::STATIC_MAX_LOCAL_CLIENTS);
|
||||||
|
|
||||||
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
||||||
|
|
||||||
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
||||||
@ -1113,7 +1126,7 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::CL_GamepadButtonEvent(const int gamePadIndex, const int key, const Game::GamePadButtonEvent buttonEvent, const unsigned time)
|
void Gamepad::CL_GamepadButtonEvent(const int gamePadIndex, const int key, const Game::GamePadButtonEvent buttonEvent, const unsigned time)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
const auto pressed = buttonEvent == Game::GPAD_BUTTON_PRESSED;
|
const auto pressed = buttonEvent == Game::GPAD_BUTTON_PRESSED;
|
||||||
const auto pressedOrUpdated = pressed || buttonEvent == Game::GPAD_BUTTON_UPDATE;
|
const auto pressedOrUpdated = pressed || buttonEvent == Game::GPAD_BUTTON_UPDATE;
|
||||||
@ -1199,9 +1212,9 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::CL_GamepadButtonEventForPort(const int gamePadIndex, const int key, const Game::GamePadButtonEvent buttonEvent, const unsigned time)
|
void Gamepad::CL_GamepadButtonEventForPort(const int gamePadIndex, const int key, const Game::GamePadButtonEvent buttonEvent, const unsigned time)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
|
||||||
|
|
||||||
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
gamePad.inUse = true;
|
gamePad.inUse = true;
|
||||||
gpad_in_use.setRaw(true);
|
gpad_in_use.setRaw(true);
|
||||||
|
|
||||||
@ -1244,7 +1257,9 @@ namespace Components
|
|||||||
|
|
||||||
float Gamepad::GPad_GetStick(const int gamePadIndex, const Game::GamePadStick stick)
|
float Gamepad::GPad_GetStick(const int gamePadIndex, const Game::GamePadStick stick)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
assert(stick & Game::GPAD_STICK_MASK);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
return gamePad.sticks[stick];
|
return gamePad.sticks[stick];
|
||||||
@ -1252,7 +1267,7 @@ namespace Components
|
|||||||
|
|
||||||
float Gamepad::GPad_GetButton(const int gamePadIndex, Game::GamePadButton button)
|
float Gamepad::GPad_GetButton(const int gamePadIndex, Game::GamePadButton button)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
float value = 0.0f;
|
float value = 0.0f;
|
||||||
@ -1276,7 +1291,9 @@ namespace Components
|
|||||||
|
|
||||||
bool Gamepad::GPad_IsButtonPressed(const int gamePadIndex, Game::GamePadButton button)
|
bool Gamepad::GPad_IsButtonPressed(const int gamePadIndex, Game::GamePadButton button)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
assert(button & (Game::GPAD_DIGITAL_MASK | Game::GPAD_ANALOG_MASK));
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
bool down = false;
|
bool down = false;
|
||||||
@ -1310,7 +1327,8 @@ namespace Components
|
|||||||
|
|
||||||
bool Gamepad::GPad_IsButtonReleased(int gamePadIndex, Game::GamePadButton button)
|
bool Gamepad::GPad_IsButtonReleased(int gamePadIndex, Game::GamePadButton button)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
bool down = false;
|
bool down = false;
|
||||||
@ -1340,7 +1358,8 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::GPad_UpdateSticksDown(const int gamePadIndex)
|
void Gamepad::GPad_UpdateSticksDown(const int gamePadIndex)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
for (auto stickIndex = 0u; stickIndex < std::extent_v<decltype(GamePad::sticks)>; stickIndex++)
|
for (auto stickIndex = 0u; stickIndex < std::extent_v<decltype(GamePad::sticks)>; stickIndex++)
|
||||||
@ -1371,7 +1390,7 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::GPad_UpdateSticks(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
void Gamepad::GPad_UpdateSticks(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
@ -1403,7 +1422,7 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::GPad_UpdateDigitals(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
void Gamepad::GPad_UpdateDigitals(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
@ -1425,7 +1444,7 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::GPad_UpdateAnalogs(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
void Gamepad::GPad_UpdateAnalogs(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePad = gamePads[gamePadIndex];
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
@ -1452,7 +1471,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
GPad_RefreshAll();
|
GPad_RefreshAll();
|
||||||
|
|
||||||
for (auto currentGamePadIndex = 0; currentGamePadIndex < Game::MAX_GAMEPADS; currentGamePadIndex++)
|
for (auto currentGamePadIndex = 0; currentGamePadIndex < Game::MAX_GPAD_COUNT; currentGamePadIndex++)
|
||||||
{
|
{
|
||||||
const auto& gamePad = gamePads[currentGamePadIndex];
|
const auto& gamePad = gamePads[currentGamePadIndex];
|
||||||
if (!gamePad.enabled)
|
if (!gamePad.enabled)
|
||||||
@ -1477,7 +1496,7 @@ namespace Components
|
|||||||
const auto time = Game::Sys_Milliseconds();
|
const auto time = Game::Sys_Milliseconds();
|
||||||
|
|
||||||
bool gpadPresent = false;
|
bool gpadPresent = false;
|
||||||
for (auto gamePadIndex = 0; gamePadIndex < Game::MAX_GAMEPADS; gamePadIndex++)
|
for (auto gamePadIndex = 0; gamePadIndex < Game::MAX_GPAD_COUNT; gamePadIndex++)
|
||||||
{
|
{
|
||||||
const auto& gamePad = gamePads[gamePadIndex];
|
const auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
@ -1540,7 +1559,8 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::Gamepad_WriteBindings(const int gamePadIndex, const int handle)
|
void Gamepad::Gamepad_WriteBindings(const int gamePadIndex, const int handle)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
|
|
||||||
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
||||||
|
|
||||||
Game::FS_Printf(handle, "unbindallaxis\n");
|
Game::FS_Printf(handle, "unbindallaxis\n");
|
||||||
@ -1584,7 +1604,7 @@ namespace Components
|
|||||||
push 0x60B26E
|
push 0x60B26E
|
||||||
retn
|
retn
|
||||||
|
|
||||||
endMethod:
|
endMethod:
|
||||||
push 0x60B298
|
push 0x60B298
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
@ -1592,7 +1612,7 @@ namespace Components
|
|||||||
|
|
||||||
void Gamepad::Gamepad_BindAxis(const int gamePadIndex, const Game::GamepadPhysicalAxis realIndex, const Game::GamepadVirtualAxis axisIndex, const Game::GamepadMapping mapType)
|
void Gamepad::Gamepad_BindAxis(const int gamePadIndex, const Game::GamepadPhysicalAxis realIndex, const Game::GamepadVirtualAxis axisIndex, const Game::GamepadMapping mapType)
|
||||||
{
|
{
|
||||||
assert(gamePadIndex < Game::MAX_GAMEPADS);
|
AssertIn(gamePadIndex, Game::MAX_GPAD_COUNT);
|
||||||
assert(realIndex > Game::GPAD_PHYSAXIS_NONE && realIndex < Game::GPAD_PHYSAXIS_COUNT);
|
assert(realIndex > Game::GPAD_PHYSAXIS_NONE && realIndex < Game::GPAD_PHYSAXIS_COUNT);
|
||||||
assert(axisIndex > Game::GPAD_VIRTAXIS_NONE && axisIndex < Game::GPAD_VIRTAXIS_COUNT);
|
assert(axisIndex > Game::GPAD_VIRTAXIS_NONE && axisIndex < Game::GPAD_VIRTAXIS_COUNT);
|
||||||
assert(mapType > Game::GPAD_MAP_NONE && mapType < Game::GPAD_MAP_COUNT);
|
assert(mapType > Game::GPAD_MAP_NONE && mapType < Game::GPAD_MAP_COUNT);
|
||||||
|
@ -43,6 +43,8 @@ namespace Components
|
|||||||
|
|
||||||
static void OnMouseMove(int x, int y, int dx, int dy);
|
static void OnMouseMove(int x, int y, int dx, int dy);
|
||||||
|
|
||||||
|
static Dvar::Var sv_allowAimAssist;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Game::ButtonToCodeMap_t buttonList[];
|
static Game::ButtonToCodeMap_t buttonList[];
|
||||||
static Game::StickToCodeMap_t analogStickList[4];
|
static Game::StickToCodeMap_t analogStickList[4];
|
||||||
@ -60,8 +62,8 @@ namespace Components
|
|||||||
static Game::keyname_t combinedLocalizedKeyNamesPs3[];
|
static Game::keyname_t combinedLocalizedKeyNamesPs3[];
|
||||||
static ControllerMenuKeyMapping controllerMenuKeyMappings[];
|
static ControllerMenuKeyMapping controllerMenuKeyMappings[];
|
||||||
|
|
||||||
static GamePad gamePads[Game::MAX_GAMEPADS];
|
static GamePad gamePads[Game::MAX_GPAD_COUNT];
|
||||||
static GamePadGlobals gamePadGlobals[Game::MAX_GAMEPADS];
|
static GamePadGlobals gamePadGlobals[Game::MAX_GPAD_COUNT];
|
||||||
|
|
||||||
static int gamePadBindingsModifiedFlags;
|
static int gamePadBindingsModifiedFlags;
|
||||||
|
|
||||||
@ -87,7 +89,6 @@ namespace Components
|
|||||||
static Dvar::Var gpad_slowdown_enabled;
|
static Dvar::Var gpad_slowdown_enabled;
|
||||||
static Dvar::Var input_viewSensitivity;
|
static Dvar::Var input_viewSensitivity;
|
||||||
static Dvar::Var input_invertPitch;
|
static Dvar::Var input_invertPitch;
|
||||||
static Dvar::Var sv_allowAimAssist;
|
|
||||||
static Dvar::Var aim_turnrate_pitch;
|
static Dvar::Var aim_turnrate_pitch;
|
||||||
static Dvar::Var aim_turnrate_pitch_ads;
|
static Dvar::Var aim_turnrate_pitch_ads;
|
||||||
static Dvar::Var aim_turnrate_yaw;
|
static Dvar::Var aim_turnrate_yaw;
|
||||||
|
@ -2,46 +2,48 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
Dvar::Var Lean::BGLean;
|
||||||
|
|
||||||
Game::kbutton_t Lean::in_leanleft;
|
Game::kbutton_t Lean::in_leanleft;
|
||||||
Game::kbutton_t Lean::in_leanright;
|
Game::kbutton_t Lean::in_leanright;
|
||||||
|
|
||||||
void Lean::IN_LeanLeft_Up()
|
void Lean::IN_LeanLeft_Up()
|
||||||
{
|
{
|
||||||
Game::IN_KeyUp(&Lean::in_leanleft);
|
Game::IN_KeyUp(&in_leanleft);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lean::IN_LeanLeft_Down()
|
void Lean::IN_LeanLeft_Down()
|
||||||
{
|
{
|
||||||
Game::IN_KeyDown(&Lean::in_leanleft);
|
Game::IN_KeyDown(&in_leanleft);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lean::IN_LeanRight_Up()
|
void Lean::IN_LeanRight_Up()
|
||||||
{
|
{
|
||||||
Game::IN_KeyUp(&Lean::in_leanright);
|
Game::IN_KeyUp(&in_leanright);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lean::IN_LeanRight_Down()
|
void Lean::IN_LeanRight_Down()
|
||||||
{
|
{
|
||||||
Game::IN_KeyDown(&Lean::in_leanright);
|
Game::IN_KeyDown(&in_leanright);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lean::SetLeanFlags(Game::usercmd_s* cmds)
|
void Lean::SetLeanFlags(Game::usercmd_s* cmd)
|
||||||
{
|
{
|
||||||
if (Lean::in_leanleft.active || Lean::in_leanleft.wasPressed)
|
if ((in_leanleft.active || in_leanleft.wasPressed) && BGLean.get<bool>())
|
||||||
{
|
{
|
||||||
cmds->buttons |= BUTTON_FLAG_LEANLEFT;
|
cmd->buttons |= Game::CMD_BUTTON_LEAN_LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Lean::in_leanright.active || Lean::in_leanright.wasPressed)
|
if ((in_leanright.active || in_leanright.wasPressed) && BGLean.get<bool>())
|
||||||
{
|
{
|
||||||
cmds->buttons |= BUTTON_FLAG_LEANRIGHT;
|
cmd->buttons |= Game::CMD_BUTTON_LEAN_RIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lean::in_leanleft.wasPressed = false;
|
in_leanleft.wasPressed = false;
|
||||||
Lean::in_leanright.wasPressed = false;
|
in_leanright.wasPressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __declspec(naked) Lean::CL_CmdButtonsStub()
|
void __declspec(naked) Lean::CL_CmdButtons_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -51,21 +53,35 @@ namespace Components
|
|||||||
|
|
||||||
pushad
|
pushad
|
||||||
push esi
|
push esi
|
||||||
call Lean::SetLeanFlags
|
call SetLeanFlags
|
||||||
pop esi
|
pop esi
|
||||||
popad
|
popad
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lean::PM_UpdateLean_Stub(Game::playerState_s* ps, float msec, Game::usercmd_s* cmd, void(*capsuleTrace)(Game::trace_t*, const float*, const float*, const Game::Bounds*, int, int))
|
||||||
|
{
|
||||||
|
if (BGLean.get<bool>())
|
||||||
|
{
|
||||||
|
Game::PM_UpdateLean(ps, msec, cmd, capsuleTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Lean::Lean()
|
Lean::Lean()
|
||||||
{
|
{
|
||||||
Command::AddRaw("+leanleft", Lean::IN_LeanLeft_Down, true);
|
Command::AddRaw("+leanleft", IN_LeanLeft_Down, true);
|
||||||
Command::AddRaw("-leanleft", Lean::IN_LeanLeft_Up, true);
|
Command::AddRaw("-leanleft", IN_LeanLeft_Up, true);
|
||||||
|
|
||||||
Command::AddRaw("+leanright", Lean::IN_LeanRight_Down, true);
|
Command::AddRaw("+leanright", IN_LeanRight_Down, true);
|
||||||
Command::AddRaw("-leanright", Lean::IN_LeanRight_Up, true);
|
Command::AddRaw("-leanright", IN_LeanRight_Up, true);
|
||||||
|
|
||||||
Utils::Hook(0x5A6D84, Lean::CL_CmdButtonsStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5A6D84, CL_CmdButtons_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x4A0C72, PM_UpdateLean_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x4A0D72, PM_UpdateLean_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
BGLean = Dvar::Register<bool>("bg_lean", true,
|
||||||
|
Game::DVAR_CODINFO, "Enable CoD4 leaning");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define BUTTON_FLAG_LEANLEFT 0x40
|
|
||||||
#define BUTTON_FLAG_LEANRIGHT 0x80
|
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
class Lean : public Component
|
class Lean : public Component
|
||||||
@ -10,6 +7,8 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
Lean();
|
Lean();
|
||||||
|
|
||||||
|
static Dvar::Var BGLean;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Game::kbutton_t in_leanleft;
|
static Game::kbutton_t in_leanleft;
|
||||||
static Game::kbutton_t in_leanright;
|
static Game::kbutton_t in_leanright;
|
||||||
@ -20,7 +19,9 @@ namespace Components
|
|||||||
static void IN_LeanRight_Up();
|
static void IN_LeanRight_Up();
|
||||||
static void IN_LeanRight_Down();
|
static void IN_LeanRight_Down();
|
||||||
|
|
||||||
static void CL_CmdButtonsStub();
|
static void CL_CmdButtons_Stub();
|
||||||
static void SetLeanFlags(Game::usercmd_s* cmds);
|
static void SetLeanFlags(Game::usercmd_s* cmd);
|
||||||
|
|
||||||
|
static void PM_UpdateLean_Stub(Game::playerState_s* ps, float msec, Game::usercmd_s* cmd, void(*capsuleTrace)(Game::trace_t*, const float*, const float*, const Game::Bounds*, int, int));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -249,6 +249,144 @@ namespace Components
|
|||||||
Localization::Set("IW4X_CREDITS", credits);
|
Localization::Set("IW4X_CREDITS", credits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* Localization::SEH_LocalizeTextMessageStub(const char* pszInputBuffer, const char* pszMessageType, Game::msgLocErrType_t errType)
|
||||||
|
{
|
||||||
|
constexpr auto szStringCount = 10;
|
||||||
|
constexpr auto szStringSize = 1024;
|
||||||
|
|
||||||
|
char szInsertBuf[szStringSize];
|
||||||
|
char szTokenBuf[szStringSize];
|
||||||
|
|
||||||
|
static thread_local int iCurrString;
|
||||||
|
static thread_local char szStrings[szStringCount][szStringSize];
|
||||||
|
|
||||||
|
iCurrString = (iCurrString + 1) % szStringCount;
|
||||||
|
std::memset(szStrings[iCurrString], 0, sizeof(szStrings[0]));
|
||||||
|
auto* pszString = szStrings[iCurrString];
|
||||||
|
auto iLen = 0;
|
||||||
|
auto bLocOn = 1;
|
||||||
|
auto bInsertEnabled = 1;
|
||||||
|
auto iInsertLevel = 0;
|
||||||
|
auto insertIndex = 1;
|
||||||
|
auto bLocSkipped = 0;
|
||||||
|
const auto* pszTokenStart = pszInputBuffer;
|
||||||
|
const auto* pszIn = pszInputBuffer;
|
||||||
|
|
||||||
|
auto i = 0;
|
||||||
|
while (*pszTokenStart)
|
||||||
|
{
|
||||||
|
if (*pszIn && *pszIn != '\x14' && *pszIn != '\x15' && *pszIn != '\x16')
|
||||||
|
{
|
||||||
|
++pszIn;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pszIn > pszTokenStart)
|
||||||
|
{
|
||||||
|
auto iTokenLen = pszIn - pszTokenStart;
|
||||||
|
Game::I_strncpyz_s(szTokenBuf, sizeof(szTokenBuf), pszTokenStart, pszIn - pszTokenStart);
|
||||||
|
if (bLocOn)
|
||||||
|
{
|
||||||
|
if (!Game::SEH_GetLocalizedTokenReference(szTokenBuf, szTokenBuf, pszMessageType, errType))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
iTokenLen = std::strlen(szTokenBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iTokenLen + iLen >= szStringSize)
|
||||||
|
{
|
||||||
|
Game::Com_Printf(Game::CON_CHANNEL_SYSTEM, "%s too long when translated\n", pszMessageType);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < iTokenLen - 2; ++i)
|
||||||
|
{
|
||||||
|
if (!std::strncmp(&szTokenBuf[i], "&&", 2) && std::isdigit(szTokenBuf[i + 2]))
|
||||||
|
{
|
||||||
|
if (bInsertEnabled)
|
||||||
|
{
|
||||||
|
++iInsertLevel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
szTokenBuf[i] = '\x16';
|
||||||
|
bLocSkipped = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iInsertLevel <= 0 || iLen <= 0)
|
||||||
|
{
|
||||||
|
Game::I_strcpy(&pszString[iLen], szStringSize - iLen, szTokenBuf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < iLen - 2; ++i)
|
||||||
|
{
|
||||||
|
if (!std::strncmp(&pszString[i], "&&", 2) && std::isdigit(pszString[i + 2]))
|
||||||
|
{
|
||||||
|
const auto digit = pszString[i + 2] - 48;
|
||||||
|
if (!digit)
|
||||||
|
{
|
||||||
|
Game::Com_Printf(Game::CON_CHANNEL_SYSTEM, "%s cannot have &&0 as conversion format: \"%s\"\n", pszMessageType, pszInputBuffer);
|
||||||
|
}
|
||||||
|
if (digit == insertIndex)
|
||||||
|
{
|
||||||
|
Game::I_strcpy(szInsertBuf, sizeof(szInsertBuf), &pszString[i + 3]);
|
||||||
|
pszString[i] = 0;
|
||||||
|
++insertIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::I_strcpy(&pszString[i], szStringSize - i, szTokenBuf);
|
||||||
|
Game::I_strcpy(&pszString[iTokenLen + i], szStringSize - (iTokenLen + i), szInsertBuf);
|
||||||
|
|
||||||
|
iLen -= 3;
|
||||||
|
--iInsertLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
iLen += iTokenLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
bInsertEnabled = 1;
|
||||||
|
if (*pszIn == '\x14')
|
||||||
|
{
|
||||||
|
bLocOn = 1;
|
||||||
|
++pszIn;
|
||||||
|
}
|
||||||
|
else if (*pszIn == '\x15')
|
||||||
|
{
|
||||||
|
bLocOn = 0;
|
||||||
|
++pszIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pszIn == '\x16')
|
||||||
|
{
|
||||||
|
bInsertEnabled = 0;
|
||||||
|
++pszIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
pszTokenStart = pszIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bLocSkipped)
|
||||||
|
{
|
||||||
|
for (i = 0; i < iLen; ++i)
|
||||||
|
{
|
||||||
|
if (pszString[i] == '\x16')
|
||||||
|
{
|
||||||
|
pszString[i] = '%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pszString;
|
||||||
|
}
|
||||||
|
|
||||||
Localization::Localization()
|
Localization::Localization()
|
||||||
{
|
{
|
||||||
Localization::SetCredits();
|
Localization::SetCredits();
|
||||||
@ -279,6 +417,9 @@ namespace Components
|
|||||||
// Overwrite SetString
|
// Overwrite SetString
|
||||||
Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x49D4A0, Localization::SEH_LocalizeTextMessageStub, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook::Nop(0x49D4A5, 1);
|
||||||
|
|
||||||
Localization::UseLocalization = Dvar::Register<bool>("ui_localize", true, Game::DVAR_NONE, "Use localization strings");
|
Localization::UseLocalization = Dvar::Register<bool>("ui_localize", true, Game::DVAR_NONE, "Use localization strings");
|
||||||
|
|
||||||
// Generate localized entries for custom classes above 10
|
// Generate localized entries for custom classes above 10
|
||||||
|
@ -24,5 +24,7 @@ namespace Components
|
|||||||
static void LoadLanguageStrings();
|
static void LoadLanguageStrings();
|
||||||
static void SELoadLanguageStub();
|
static void SELoadLanguageStub();
|
||||||
static void SetCredits();
|
static void SetCredits();
|
||||||
|
|
||||||
|
static const char* SEH_LocalizeTextMessageStub(const char* pszInputBuffer, const char* pszMessageType, Game::msgLocErrType_t errType);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
std::string out = msg;
|
std::string out = msg;
|
||||||
|
|
||||||
// Filter out coloured strings
|
// Filter out coloured strings for stdout
|
||||||
if (out[0] == '^' && out[1] != '\0')
|
if (out[0] == '^' && out[1] != '\0')
|
||||||
{
|
{
|
||||||
out = out.substr(2);
|
out = out.substr(2);
|
||||||
@ -48,11 +48,11 @@ namespace Components
|
|||||||
|
|
||||||
if (!Game::Sys_IsMainThread())
|
if (!Game::Sys_IsMainThread())
|
||||||
{
|
{
|
||||||
Logger::EnqueueMessage(out);
|
Logger::EnqueueMessage(msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Game::Com_PrintMessage(channel, out.data(), 0);
|
Game::Com_PrintMessage(channel, msg.data(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +284,7 @@ namespace Components
|
|||||||
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[0].size())
|
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[0].size())
|
||||||
{
|
{
|
||||||
auto addr = Logger::LoggingAddresses[0].begin() + num;
|
auto addr = Logger::LoggingAddresses[0].begin() + num;
|
||||||
Logger::Print("Address {} removed\n", addr->getCString());
|
Logger::Print("Address {} removed\n", addr->getString());
|
||||||
Logger::LoggingAddresses[0].erase(addr);
|
Logger::LoggingAddresses[0].erase(addr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -295,11 +295,11 @@ namespace Components
|
|||||||
if (i != Logger::LoggingAddresses[0].end())
|
if (i != Logger::LoggingAddresses[0].end())
|
||||||
{
|
{
|
||||||
Logger::LoggingAddresses[0].erase(i);
|
Logger::LoggingAddresses[0].erase(i);
|
||||||
Logger::Print("Address {} removed\n", addr.getCString());
|
Logger::Print("Address {} removed\n", addr.getString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Print("Address {} not found!\n", addr.getCString());
|
Logger::Print("Address {} not found!\n", addr.getString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -311,7 +311,7 @@ namespace Components
|
|||||||
|
|
||||||
for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i)
|
for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i)
|
||||||
{
|
{
|
||||||
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[0][i].getCString());
|
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[0][i].getString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ namespace Components
|
|||||||
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[1].size())
|
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[1].size())
|
||||||
{
|
{
|
||||||
const auto addr = Logger::LoggingAddresses[1].begin() + num;
|
const auto addr = Logger::LoggingAddresses[1].begin() + num;
|
||||||
Logger::Print("Address {} removed\n", addr->getCString());
|
Logger::Print("Address {} removed\n", addr->getString());
|
||||||
Logger::LoggingAddresses[1].erase(addr);
|
Logger::LoggingAddresses[1].erase(addr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -346,11 +346,11 @@ namespace Components
|
|||||||
if (i != Logger::LoggingAddresses[1].end())
|
if (i != Logger::LoggingAddresses[1].end())
|
||||||
{
|
{
|
||||||
Logger::LoggingAddresses[1].erase(i);
|
Logger::LoggingAddresses[1].erase(i);
|
||||||
Logger::Print("Address {} removed\n", addr.getCString());
|
Logger::Print("Address {} removed\n", addr.getString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Print("Address {} not found!\n", addr.getCString());
|
Logger::Print("Address {} not found!\n", addr.getString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -362,7 +362,7 @@ namespace Components
|
|||||||
|
|
||||||
for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i)
|
for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i)
|
||||||
{
|
{
|
||||||
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[1][i].getCString());
|
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[1][i].getString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -397,9 +397,9 @@ namespace Components
|
|||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// Flush the console log
|
// Flush the console log
|
||||||
if (const auto logfile = *reinterpret_cast<int*>(0x1AD8F28))
|
if (*Game::logfile)
|
||||||
{
|
{
|
||||||
Game::FS_FCloseFile(logfile);
|
Game::FS_FCloseFile(*Game::logfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,6 @@ namespace Components
|
|||||||
Dvar::Var MapRotation::SVRandomMapRotation;
|
Dvar::Var MapRotation::SVRandomMapRotation;
|
||||||
Dvar::Var MapRotation::SVDontRotate;
|
Dvar::Var MapRotation::SVDontRotate;
|
||||||
|
|
||||||
Game::dvar_t** MapRotation::SVMapRotation = reinterpret_cast<Game::dvar_t**>(0x62C7C44);
|
|
||||||
Game::dvar_t** MapRotation::SVMapRotationCurrent = reinterpret_cast<Game::dvar_t**>(0x2098DF0);
|
|
||||||
Game::dvar_t** MapRotation::SVMapname = reinterpret_cast<Game::dvar_t**>(0x2098DDC);
|
|
||||||
|
|
||||||
MapRotation::RotationData MapRotation::DedicatedRotation;
|
MapRotation::RotationData MapRotation::DedicatedRotation;
|
||||||
|
|
||||||
MapRotation::RotationData::RotationData()
|
MapRotation::RotationData::RotationData()
|
||||||
@ -62,7 +58,16 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json MapRotation::RotationData::to_json() const
|
bool MapRotation::RotationData::contains(const std::string& key, const std::string& value) const
|
||||||
|
{
|
||||||
|
return std::ranges::any_of(this->rotationEntries_,
|
||||||
|
[&](const auto& entry)
|
||||||
|
{
|
||||||
|
return entry.first == key && entry.second == value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json MapRotation::RotationData::to_json() const
|
||||||
{
|
{
|
||||||
std::vector<std::string> mapVector;
|
std::vector<std::string> mapVector;
|
||||||
std::vector<std::string> gametypeVector;
|
std::vector<std::string> gametypeVector;
|
||||||
@ -80,7 +85,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
json11::Json mapRotationJson = json11::Json::object
|
nlohmann::json mapRotationJson = nlohmann::json
|
||||||
{
|
{
|
||||||
{"maps", mapVector},
|
{"maps", mapVector},
|
||||||
{"gametypes", gametypeVector},
|
{"gametypes", gametypeVector},
|
||||||
@ -107,12 +112,23 @@ namespace Components
|
|||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*SVMapRotation)->name);
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*Game::sv_mapRotation)->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Debug("DedicatedRotation size after parsing is '{}'", DedicatedRotation.getEntriesSize());
|
Logger::Debug("DedicatedRotation size after parsing is '{}'", DedicatedRotation.getEntriesSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MapRotation::LoadMapRotation()
|
||||||
|
{
|
||||||
|
const std::string mapRotation = (*Game::sv_mapRotation)->current.string;
|
||||||
|
// People may have sv_mapRotation empty because they only use 'addMap' or 'addGametype'
|
||||||
|
if (!mapRotation.empty())
|
||||||
|
{
|
||||||
|
Logger::Debug("sv_mapRotation is not empty. Parsing...");
|
||||||
|
LoadRotation(mapRotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MapRotation::AddMapRotationCommands()
|
void MapRotation::AddMapRotationCommands()
|
||||||
{
|
{
|
||||||
Command::Add("addMap", [](Command::Params* params)
|
Command::Add("addMap", [](Command::Params* params)
|
||||||
@ -138,6 +154,11 @@ namespace Components
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MapRotation::Contains(const std::string& key, const std::string& value)
|
||||||
|
{
|
||||||
|
return DedicatedRotation.contains(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
bool MapRotation::ShouldRotate()
|
bool MapRotation::ShouldRotate()
|
||||||
{
|
{
|
||||||
if (!Dedicated::IsEnabled() && SVDontRotate.get<bool>())
|
if (!Dedicated::IsEnabled() && SVDontRotate.get<bool>())
|
||||||
@ -160,13 +181,13 @@ namespace Components
|
|||||||
{
|
{
|
||||||
assert(!map.empty());
|
assert(!map.empty());
|
||||||
|
|
||||||
if (Dvar::Var("sv_cheats").get<bool>())
|
if ((*Game::sv_cheats)->current.enabled)
|
||||||
{
|
{
|
||||||
Command::Execute(Utils::String::VA("devmap %s", map.data()), true);
|
Command::Execute(std::format("devmap {}", map), true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Command::Execute(Utils::String::VA("map %s", map.data()), true);
|
Command::Execute(std::format("map {}", map), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +199,7 @@ namespace Components
|
|||||||
|
|
||||||
void MapRotation::RestartCurrentMap()
|
void MapRotation::RestartCurrentMap()
|
||||||
{
|
{
|
||||||
std::string svMapname = (*SVMapname)->current.string;
|
std::string svMapname = (*Game::sv_mapname)->current.string;
|
||||||
|
|
||||||
if (svMapname.empty())
|
if (svMapname.empty())
|
||||||
{
|
{
|
||||||
@ -224,24 +245,24 @@ namespace Components
|
|||||||
assert(!data.empty());
|
assert(!data.empty());
|
||||||
|
|
||||||
// Ook, ook, eek
|
// Ook, ook, eek
|
||||||
Logger::Warning(Game::CON_CHANNEL_SERVER, "You are using deprecated {}", (*SVMapRotationCurrent)->name);
|
Logger::Warning(Game::CON_CHANNEL_SERVER, "You are using deprecated {}", (*Game::sv_mapRotationCurrent)->name);
|
||||||
|
|
||||||
RotationData rotationCurrent;
|
RotationData rotationCurrent;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger::Debug("Parsing {}", (*SVMapRotationCurrent)->name);
|
Logger::Debug("Parsing {}", (*Game::sv_mapRotationCurrent)->name);
|
||||||
rotationCurrent.parse(data);
|
rotationCurrent.parse(data);
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*SVMapRotationCurrent)->name);
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*Game::sv_mapRotationCurrent)->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::Dvar_SetString(*SVMapRotationCurrent, "");
|
Game::Dvar_SetString(*Game::sv_mapRotationCurrent, "");
|
||||||
|
|
||||||
if (rotationCurrent.getEntriesSize() == 0)
|
if (rotationCurrent.getEntriesSize() == 0)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*SVMapRotationCurrent)->name);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotationCurrent)->name);
|
||||||
RestartCurrentMap();
|
RestartCurrentMap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -272,25 +293,18 @@ namespace Components
|
|||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Rotating map...\n");
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Rotating map...\n");
|
||||||
|
|
||||||
// This takes priority because of backwards compatibility
|
// This takes priority because of backwards compatibility
|
||||||
const std::string mapRotationCurrent = (*SVMapRotationCurrent)->current.string;
|
const std::string mapRotationCurrent = (*Game::sv_mapRotationCurrent)->current.string;
|
||||||
if (!mapRotationCurrent.empty())
|
if (!mapRotationCurrent.empty())
|
||||||
{
|
{
|
||||||
Logger::Debug("Applying {}", (*SVMapRotationCurrent)->name);
|
Logger::Debug("Applying {}", (*Game::sv_mapRotationCurrent)->name);
|
||||||
ApplyMapRotationCurrent(mapRotationCurrent);
|
ApplyMapRotationCurrent(mapRotationCurrent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string mapRotation = (*SVMapRotation)->current.string;
|
LoadMapRotation();
|
||||||
// People may have sv_mapRotation empty because they only use 'addMap' or 'addGametype'
|
|
||||||
if (!mapRotation.empty())
|
|
||||||
{
|
|
||||||
Logger::Debug("sv_mapRotation is not empty. Parsing...");
|
|
||||||
LoadRotation(mapRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DedicatedRotation.getEntriesSize() == 0)
|
if (DedicatedRotation.getEntriesSize() == 0)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*SVMapRotation)->name);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotation)->name);
|
||||||
RestartCurrentMap();
|
RestartCurrentMap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
MapRotation();
|
MapRotation();
|
||||||
|
|
||||||
|
static bool Contains(const std::string& key, const std::string& value);
|
||||||
|
|
||||||
bool unitTest() override;
|
bool unitTest() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -33,8 +35,9 @@ namespace Components
|
|||||||
|
|
||||||
void parse(const std::string& data);
|
void parse(const std::string& data);
|
||||||
|
|
||||||
// Json11 Implicit constructor
|
[[nodiscard]] bool contains(const std::string& key, const std::string& value) const;
|
||||||
[[nodiscard]] json11::Json to_json() const;
|
|
||||||
|
[[nodiscard]] nlohmann::json to_json() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<rotationEntry> rotationEntries_;
|
std::vector<rotationEntry> rotationEntries_;
|
||||||
@ -45,15 +48,12 @@ namespace Components
|
|||||||
// Rotation Dvars
|
// Rotation Dvars
|
||||||
static Dvar::Var SVRandomMapRotation;
|
static Dvar::Var SVRandomMapRotation;
|
||||||
static Dvar::Var SVDontRotate;
|
static Dvar::Var SVDontRotate;
|
||||||
// Game Dvars
|
|
||||||
static Game::dvar_t** SVMapRotation;
|
|
||||||
static Game::dvar_t** SVMapRotationCurrent;
|
|
||||||
static Game::dvar_t** SVMapname;
|
|
||||||
|
|
||||||
// Holds the parsed data from sv_mapRotation
|
// Holds the parsed data from sv_mapRotation
|
||||||
static RotationData DedicatedRotation;
|
static RotationData DedicatedRotation;
|
||||||
|
|
||||||
static void LoadRotation(const std::string& data);
|
static void LoadRotation(const std::string& data);
|
||||||
|
static void LoadMapRotation();
|
||||||
|
|
||||||
// Use these commands before SV_MapRotate_f is called
|
// Use these commands before SV_MapRotate_f is called
|
||||||
static void AddMapRotationCommands();
|
static void AddMapRotationCommands();
|
||||||
|
@ -96,12 +96,12 @@ namespace Components
|
|||||||
|
|
||||||
const char* Maps::LoadArenaFileStub(const char* name, char* buffer, int size)
|
const char* Maps::LoadArenaFileStub(const char* name, char* buffer, int size)
|
||||||
{
|
{
|
||||||
std::string data = Game::Scr_AddSourceBuffer(nullptr, name, nullptr, false);
|
std::string data = RawFiles::ReadRawFile(name, buffer, size);
|
||||||
|
|
||||||
if(Maps::UserMap.isValid())
|
if (Maps::UserMap.isValid())
|
||||||
{
|
{
|
||||||
std::string mapname = Maps::UserMap.getName();
|
const std::string mapname = Maps::UserMap.getName();
|
||||||
std::string arena = Utils::String::VA("usermaps/%s/%s.arena", mapname.data(), mapname.data());
|
const auto* arena = Utils::String::VA("usermaps/%s/%s.arena", mapname.data(), mapname.data());
|
||||||
|
|
||||||
if (Utils::IO::FileExists(arena))
|
if (Utils::IO::FileExists(arena))
|
||||||
{
|
{
|
||||||
@ -109,7 +109,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy_s(buffer, size, data.data(), data.size());
|
strncpy_s(buffer, size, data.data(), _TRUNCATE);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,7 +768,7 @@ namespace Components
|
|||||||
|
|
||||||
Maps::UpdateDlcStatus();
|
Maps::UpdateDlcStatus();
|
||||||
|
|
||||||
UIScript::Add("downloadDLC", [](UIScript::Token token)
|
UIScript::Add("downloadDLC", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
int dlc = token.get<int>();
|
int dlc = token.get<int>();
|
||||||
|
|
||||||
|
@ -785,80 +785,85 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}, HOOK_CALL).install()->quick();
|
}, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Intercept menu painting
|
// Intercept menu painting
|
||||||
Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// disable the 2 new tokens in ItemParse_rect
|
// disable the 2 new tokens in ItemParse_rect
|
||||||
Utils::Hook::Set<BYTE>(0x640693, 0xEB);
|
Utils::Hook::Set<BYTE>(0x640693, 0xEB);
|
||||||
|
|
||||||
// don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail)
|
// don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail)
|
||||||
Utils::Hook::Nop(0x453406, 5);
|
Utils::Hook::Nop(0x453406, 5);
|
||||||
|
|
||||||
//make Com_Error and similar go back to main_text instead of menu_xboxlive.
|
// make Com_Error and similar go back to main_text instead of menu_xboxlive.
|
||||||
Utils::Hook::SetString(0x6FC790, "main_text");
|
Utils::Hook::SetString(0x6FC790, "main_text");
|
||||||
|
|
||||||
Command::Add("openmenu", [](Command::Params* params)
|
Command::Add("openmenu", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (params->size() != 2)
|
||||||
{
|
{
|
||||||
if (params->size() != 2)
|
Logger::Print("USAGE: openmenu <menu name>\n");
|
||||||
{
|
return;
|
||||||
Logger::Print("USAGE: openmenu <menu name>\n");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus.
|
// Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus.
|
||||||
if (Dvar::Var("cl_ingame").get<bool>())
|
if ((*Game::cl_ingame)->current.enabled)
|
||||||
{
|
|
||||||
Game::Key_SetCatcher(0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::Menus_OpenByName(Game::uiContext, params->get(1));
|
|
||||||
});
|
|
||||||
|
|
||||||
Command::Add("reloadmenus", [](Command::Params*)
|
|
||||||
{
|
{
|
||||||
// Close all menus
|
Game::Key_SetCatcher(0, 16);
|
||||||
Game::Menus_CloseAll(Game::uiContext);
|
}
|
||||||
|
|
||||||
// Free custom menus
|
Game::Menus_OpenByName(Game::uiContext, params->get(1));
|
||||||
Menus::FreeEverything();
|
});
|
||||||
|
|
||||||
// Only disconnect if in-game, context is updated automatically!
|
Command::Add("reloadmenus", [](Command::Params*)
|
||||||
if (Game::CL_IsCgameInitialized())
|
{
|
||||||
{
|
// Close all menus
|
||||||
Game::Cbuf_AddText(0, "disconnect\n");
|
Game::Menus_CloseAll(Game::uiContext);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reinitialize ui context
|
|
||||||
Utils::Hook::Call<void()>(0x401700)();
|
|
||||||
|
|
||||||
// Reopen main menu
|
// Free custom menus
|
||||||
Game::Menus_OpenByName(Game::uiContext, "main_text");
|
Menus::FreeEverything();
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Command::Add("mp_QuickMessage", [](Command::Params*)
|
// Only disconnect if in-game, context is updated automatically!
|
||||||
|
if (Game::CL_IsCgameInitialized())
|
||||||
{
|
{
|
||||||
Command::Execute("openmenu quickmessage");
|
Game::Cbuf_AddText(0, "disconnect\n");
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reinitialize ui context
|
||||||
|
Utils::Hook::Call<void()>(0x401700)();
|
||||||
|
|
||||||
// Define custom menus here
|
// Reopen main menu
|
||||||
Menus::Add("ui_mp/changelog.menu");
|
Game::Menus_OpenByName(Game::uiContext, "main_text");
|
||||||
Menus::Add("ui_mp/theater_menu.menu");
|
}
|
||||||
Menus::Add("ui_mp/pc_options_multi.menu");
|
});
|
||||||
Menus::Add("ui_mp/pc_options_game.menu");
|
|
||||||
Menus::Add("ui_mp/pc_options_gamepad.menu");
|
Command::Add("mp_QuickMessage", [](Command::Params*)
|
||||||
Menus::Add("ui_mp/stats_reset.menu");
|
{
|
||||||
Menus::Add("ui_mp/stats_unlock.menu");
|
Command::Execute("openmenu quickmessage");
|
||||||
Menus::Add("ui_mp/security_increase_popmenu.menu");
|
});
|
||||||
Menus::Add("ui_mp/mod_download_popmenu.menu");
|
|
||||||
Menus::Add("ui_mp/popup_friends.menu");
|
// Define custom menus here
|
||||||
Menus::Add("ui_mp/menu_first_launch.menu");
|
Menus::Add("ui_mp/changelog.menu");
|
||||||
Menus::Add("ui_mp/startup_messages.menu");
|
Menus::Add("ui_mp/theater_menu.menu");
|
||||||
Menus::Add("ui_mp/iw4x_credits.menu");
|
Menus::Add("ui_mp/pc_options_multi.menu");
|
||||||
Menus::Add("ui_mp/resetclass.menu");
|
Menus::Add("ui_mp/pc_options_game.menu");
|
||||||
Menus::Add("ui_mp/popup_customtitle.menu");
|
Menus::Add("ui_mp/pc_options_gamepad.menu");
|
||||||
Menus::Add("ui_mp/popup_customclan.menu");
|
Menus::Add("ui_mp/stats_reset.menu");
|
||||||
|
Menus::Add("ui_mp/stats_unlock.menu");
|
||||||
|
Menus::Add("ui_mp/security_increase_popmenu.menu");
|
||||||
|
Menus::Add("ui_mp/mod_download_popmenu.menu");
|
||||||
|
Menus::Add("ui_mp/popup_friends.menu");
|
||||||
|
Menus::Add("ui_mp/menu_first_launch.menu");
|
||||||
|
Menus::Add("ui_mp/startup_messages.menu");
|
||||||
|
Menus::Add("ui_mp/iw4x_credits.menu");
|
||||||
|
Menus::Add("ui_mp/resetclass.menu");
|
||||||
|
Menus::Add("ui_mp/popup_customtitle.menu");
|
||||||
|
Menus::Add("ui_mp/popup_customclan.menu");
|
||||||
|
|
||||||
|
Menus::Add("ui_mp/scriptmenus/callvote.menu");
|
||||||
|
Menus::Add("ui_mp/scriptmenus/changegametype.menu");
|
||||||
|
Menus::Add("ui_mp/scriptmenus/changemap.menu");
|
||||||
|
Menus::Add("ui_mp/scriptmenus/kickplayer.menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
Menus::~Menus()
|
Menus::~Menus()
|
||||||
|
@ -40,7 +40,7 @@ namespace Components
|
|||||||
ModList::CurrentMod = index;
|
ModList::CurrentMod = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModList::UIScript_LoadMods(UIScript::Token)
|
void ModList::UIScript_LoadMods([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
auto folder = Dvar::Var("fs_basepath").get<std::string>() + "\\mods";
|
auto folder = Dvar::Var("fs_basepath").get<std::string>() + "\\mods";
|
||||||
Logger::Debug("Searching for mods in {}...", folder);
|
Logger::Debug("Searching for mods in {}...", folder);
|
||||||
@ -48,7 +48,7 @@ namespace Components
|
|||||||
Logger::Debug("Found {} mods!", ModList::Mods.size());
|
Logger::Debug("Found {} mods!", ModList::Mods.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModList::UIScript_RunMod(UIScript::Token)
|
void ModList::UIScript_RunMod([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (ModList::CurrentMod < ModList::Mods.size())
|
if (ModList::CurrentMod < ModList::Mods.size())
|
||||||
{
|
{
|
||||||
@ -56,7 +56,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModList::UIScript_ClearMods(UIScript::Token)
|
void ModList::UIScript_ClearMods([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
auto fsGame = Dvar::Var("fs_game");
|
auto fsGame = Dvar::Var("fs_game");
|
||||||
fsGame.set("");
|
fsGame.set("");
|
||||||
|
@ -18,8 +18,8 @@ namespace Components
|
|||||||
static unsigned int GetItemCount();
|
static unsigned int GetItemCount();
|
||||||
static const char* GetItemText(unsigned int index, int column);
|
static const char* GetItemText(unsigned int index, int column);
|
||||||
static void Select(unsigned int index);
|
static void Select(unsigned int index);
|
||||||
static void UIScript_LoadMods(UIScript::Token);
|
static void UIScript_LoadMods([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void UIScript_RunMod(UIScript::Token);
|
static void UIScript_RunMod([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void UIScript_ClearMods(UIScript::Token);
|
static void UIScript_ClearMods([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ namespace Components
|
|||||||
Dvar::Var Movement::CGNoclipScaler;
|
Dvar::Var Movement::CGNoclipScaler;
|
||||||
Dvar::Var Movement::BGBouncesAllAngles;
|
Dvar::Var Movement::BGBouncesAllAngles;
|
||||||
Dvar::Var Movement::BGRocketJump;
|
Dvar::Var Movement::BGRocketJump;
|
||||||
|
Dvar::Var Movement::BGRocketJumpScale;
|
||||||
Dvar::Var Movement::BGPlayerEjection;
|
Dvar::Var Movement::BGPlayerEjection;
|
||||||
Dvar::Var Movement::BGPlayerCollision;
|
Dvar::Var Movement::BGPlayerCollision;
|
||||||
Game::dvar_t* Movement::BGBounces;
|
Game::dvar_t* Movement::BGBounces;
|
||||||
@ -172,15 +173,17 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
Game::gentity_s* Movement::Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex,
|
Game::gentity_s* Movement::Weapon_RocketLauncher_Fire_Hk(Game::gentity_s* ent, unsigned int weaponIndex,
|
||||||
float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool a7)
|
float spread, Game::weaponParms* wp, const float* gunVel, Game::lockonFireParms* lockParms, bool magicBullet)
|
||||||
{
|
{
|
||||||
auto* result = Game::Weapon_RocketLauncher_Fire(ent, weaponIndex, spread, wp, gunVel, lockParms, a7);
|
auto* result = Game::Weapon_RocketLauncher_Fire(ent, weaponIndex, spread, wp, gunVel, lockParms, magicBullet);
|
||||||
|
|
||||||
if (ent->client != nullptr && BGRocketJump.get<bool>())
|
if (ent->client != nullptr && BGRocketJump.get<bool>() &&
|
||||||
|
wp->weapDef->inventoryType != Game::WEAPINVENTORY_EXCLUSIVE)
|
||||||
{
|
{
|
||||||
ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * 64.0f;
|
const auto scale = Movement::BGRocketJumpScale.get<float>();
|
||||||
ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * 64.0f;
|
ent->client->ps.velocity[0] += (0.0f - wp->forward[0]) * scale;
|
||||||
ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * 64.0f;
|
ent->client->ps.velocity[1] += (0.0f - wp->forward[1]) * scale;
|
||||||
|
ent->client->ps.velocity[2] += (0.0f - wp->forward[2]) * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -217,6 +220,42 @@ namespace Components
|
|||||||
return Movement::PlayerSpectateSpeedScale.get<Game::dvar_t*>();
|
return Movement::PlayerSpectateSpeedScale.get<Game::dvar_t*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Movement::RegisterMovementDvars()
|
||||||
|
{
|
||||||
|
Movement::PlayerDuckedSpeedScale = Game::Dvar_RegisterFloat("player_duckedSpeedScale",
|
||||||
|
0.65f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||||
|
"The scale applied to the player speed when ducking");
|
||||||
|
|
||||||
|
Movement::PlayerProneSpeedScale = Game::Dvar_RegisterFloat("player_proneSpeedScale",
|
||||||
|
0.15f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||||
|
"The scale applied to the player speed when crawling");
|
||||||
|
|
||||||
|
// 3arc naming convention
|
||||||
|
Movement::CGUfoScaler = Dvar::Register<float>("cg_ufo_scaler",
|
||||||
|
6.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||||
|
"The speed at which ufo camera moves");
|
||||||
|
|
||||||
|
Movement::CGNoclipScaler = Dvar::Register<float>("cg_noclip_scaler",
|
||||||
|
3.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
||||||
|
"The speed at which noclip camera moves");
|
||||||
|
|
||||||
|
Movement::BGBouncesAllAngles = Dvar::Register<bool>("bg_bouncesAllAngles",
|
||||||
|
false, Game::DVAR_CODINFO, "Force bounce from all angles");
|
||||||
|
|
||||||
|
Movement::BGRocketJump = Dvar::Register<bool>("bg_rocketJump",
|
||||||
|
false, Game::DVAR_CODINFO, "Enable CoD4 rocket jumps");
|
||||||
|
|
||||||
|
Movement::BGRocketJumpScale = Dvar::Register<float>("bg_rocketJumpScale",
|
||||||
|
64.0f, 1.0f, std::numeric_limits<float>::max(), Game::DVAR_CODINFO,
|
||||||
|
"The scale applied to the pushback force of a rocket");
|
||||||
|
|
||||||
|
Movement::BGPlayerEjection = Dvar::Register<bool>("bg_playerEjection",
|
||||||
|
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
||||||
|
|
||||||
|
Movement::BGPlayerCollision = Dvar::Register<bool>("bg_playerCollision",
|
||||||
|
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
||||||
|
}
|
||||||
|
|
||||||
Movement::Movement()
|
Movement::Movement()
|
||||||
{
|
{
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
@ -229,37 +268,8 @@ namespace Components
|
|||||||
nullptr
|
nullptr
|
||||||
};
|
};
|
||||||
|
|
||||||
Movement::PlayerDuckedSpeedScale = Game::Dvar_RegisterFloat("player_duckedSpeedScale",
|
|
||||||
0.65f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
|
||||||
"The scale applied to the player speed when ducking");
|
|
||||||
|
|
||||||
Movement::PlayerProneSpeedScale = Game::Dvar_RegisterFloat("player_proneSpeedScale",
|
|
||||||
0.15f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
|
||||||
"The scale applied to the player speed when crawling");
|
|
||||||
|
|
||||||
// 3arc naming convention
|
|
||||||
Movement::CGUfoScaler = Dvar::Register<float>("cg_ufo_scaler",
|
|
||||||
6.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
|
||||||
"The speed at which ufo camera moves");
|
|
||||||
|
|
||||||
Movement::CGNoclipScaler = Dvar::Register<float>("cg_noclip_scaler",
|
|
||||||
3.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
|
|
||||||
"The speed at which noclip camera moves");
|
|
||||||
|
|
||||||
Movement::BGBounces = Game::Dvar_RegisterEnum("bg_bounces",
|
Movement::BGBounces = Game::Dvar_RegisterEnum("bg_bounces",
|
||||||
bg_bouncesValues, Movement::DISABLED, Game::DVAR_CODINFO, "Bounce glitch settings");
|
bg_bouncesValues, Movement::DISABLED, Game::DVAR_CODINFO, "Bounce glitch settings");
|
||||||
|
|
||||||
Movement::BGBouncesAllAngles = Dvar::Register<bool>("bg_bouncesAllAngles",
|
|
||||||
false, Game::DVAR_CODINFO, "Force bounce from all angles");
|
|
||||||
|
|
||||||
Movement::BGRocketJump = Dvar::Register<bool>("bg_rocketJump",
|
|
||||||
false, Game::DVAR_CODINFO, "Enable CoD4 rocket jumps");
|
|
||||||
|
|
||||||
Movement::BGPlayerEjection = Dvar::Register<bool>("bg_playerEjection",
|
|
||||||
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
|
||||||
|
|
||||||
Movement::BGPlayerCollision = Dvar::Register<bool>("bg_playerCollision",
|
|
||||||
true, Game::DVAR_CODINFO, "Push intersecting players away from each other");
|
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
// Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used.
|
// Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used.
|
||||||
@ -286,5 +296,7 @@ namespace Components
|
|||||||
Utils::Hook(0x5D8153, Movement::StuckInClient_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5D8153, Movement::StuckInClient_Hk, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x45A5BF, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity
|
Utils::Hook(0x45A5BF, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // SV_ClipMoveToEntity
|
||||||
Utils::Hook(0x5A0CAD, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity
|
Utils::Hook(0x5A0CAD, Movement::CM_TransformedCapsuleTrace_Hk, HOOK_CALL).install()->quick(); // CG_ClipMoveToEntity
|
||||||
|
|
||||||
|
Movement::RegisterMovementDvars();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ namespace Components
|
|||||||
static Dvar::Var CGNoclipScaler;
|
static Dvar::Var CGNoclipScaler;
|
||||||
static Dvar::Var BGBouncesAllAngles;
|
static Dvar::Var BGBouncesAllAngles;
|
||||||
static Dvar::Var BGRocketJump;
|
static Dvar::Var BGRocketJump;
|
||||||
|
static Dvar::Var BGRocketJumpScale;
|
||||||
static Dvar::Var BGPlayerEjection;
|
static Dvar::Var BGPlayerEjection;
|
||||||
static Dvar::Var BGPlayerCollision;
|
static Dvar::Var BGPlayerCollision;
|
||||||
// Can't use Var class inside assembly stubs
|
// Can't use Var class inside assembly stubs
|
||||||
@ -40,5 +41,7 @@ namespace Components
|
|||||||
static void CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end, const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles);
|
static void CM_TransformedCapsuleTrace_Hk(Game::trace_t* results, const float* start, const float* end, const Game::Bounds* bounds, const Game::Bounds* capsule, int contents, const float* origin, const float* angles);
|
||||||
|
|
||||||
static Game::dvar_t* Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description);
|
static Game::dvar_t* Dvar_RegisterSpectateSpeedScale(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description);
|
||||||
|
|
||||||
|
static void RegisterMovementDvars();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::string Network::SelectedPacket;
|
|
||||||
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
|
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
|
||||||
// Packet interception
|
// Packet interception
|
||||||
std::unordered_map<std::string, Network::NetworkCallback> Network::Callbacks;
|
std::unordered_map<std::string, Network::NetworkCallback> Network::CL_Callbacks;
|
||||||
|
std::unordered_map<std::string, Network::NetworkCallback> Network::SV_Callbacks;
|
||||||
|
|
||||||
Network::Address::Address(const std::string& addrString)
|
Network::Address::Address(const std::string& addrString)
|
||||||
{
|
{
|
||||||
@ -27,7 +27,7 @@ namespace Components
|
|||||||
this->address.port = htons(port);
|
this->address.port = htons(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short Network::Address::getPort()
|
unsigned short Network::Address::getPort() const
|
||||||
{
|
{
|
||||||
return ntohs(this->address.port);
|
return ntohs(this->address.port);
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ namespace Components
|
|||||||
this->address.ip = ip;
|
this->address.ip = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::netIP_t Network::Address::getIP()
|
Game::netIP_t Network::Address::getIP() const
|
||||||
{
|
{
|
||||||
return this->address.ip;
|
return this->address.ip;
|
||||||
}
|
}
|
||||||
@ -52,7 +52,7 @@ namespace Components
|
|||||||
this->address.type = type;
|
this->address.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::netadrtype_t Network::Address::getType()
|
Game::netadrtype_t Network::Address::getType() const
|
||||||
{
|
{
|
||||||
return this->address.type;
|
return this->address.type;
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ namespace Components
|
|||||||
|
|
||||||
std::string Network::Address::getString() const
|
std::string Network::Address::getString() const
|
||||||
{
|
{
|
||||||
return this->getCString();
|
return {this->getCString()};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Network::Address::isLocal()
|
bool Network::Address::isLocal()
|
||||||
@ -116,7 +116,7 @@ namespace Components
|
|||||||
bool Network::Address::isSelf()
|
bool Network::Address::isSelf()
|
||||||
{
|
{
|
||||||
if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback
|
if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback
|
||||||
if (this->getPort() != Network::GetPort()) return false; // Port not equal
|
if (this->getPort() != GetPort()) return false; // Port not equal
|
||||||
|
|
||||||
for (int i = 0; i < *Game::numIP; ++i)
|
for (int i = 0; i < *Game::numIP; ++i)
|
||||||
{
|
{
|
||||||
@ -129,7 +129,7 @@ namespace Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Network::Address::isLoopback()
|
bool Network::Address::isLoopback() const
|
||||||
{
|
{
|
||||||
if (this->getIP().full == 0x100007f) // 127.0.0.1
|
if (this->getIP().full == 0x100007f) // 127.0.0.1
|
||||||
{
|
{
|
||||||
@ -139,17 +139,17 @@ namespace Components
|
|||||||
return Game::NET_IsLocalAddress(this->address);
|
return Game::NET_IsLocalAddress(this->address);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Network::Address::isValid()
|
bool Network::Address::isValid() const
|
||||||
{
|
{
|
||||||
return (this->getType() != Game::netadrtype_t::NA_BAD && this->getType() >= Game::netadrtype_t::NA_BOT && this->getType() <= Game::netadrtype_t::NA_IP);
|
return (this->getType() != Game::NA_BAD && this->getType() >= Game::NA_BOT && this->getType() <= Game::NA_IP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::OnStart(Utils::Slot<Network::CallbackRaw> callback)
|
void Network::OnStart(const Utils::Slot<CallbackRaw>& callback)
|
||||||
{
|
{
|
||||||
Network::StartupSignal.connect(callback);
|
StartupSignal.connect(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::Send(Game::netsrc_t type, Network::Address target, const std::string& data)
|
void Network::Send(Game::netsrc_t type, Address target, const std::string& data)
|
||||||
{
|
{
|
||||||
// NET_OutOfBandPrint only supports non-binary data!
|
// NET_OutOfBandPrint only supports non-binary data!
|
||||||
//Game::NET_OutOfBandPrint(type, *target.Get(), data.data());
|
//Game::NET_OutOfBandPrint(type, *target.Get(), data.data());
|
||||||
@ -159,15 +159,15 @@ namespace Components
|
|||||||
rawData.append(data);
|
rawData.append(data);
|
||||||
//rawData.append("\0", 1);
|
//rawData.append("\0", 1);
|
||||||
|
|
||||||
Network::SendRaw(type, target, rawData);
|
SendRaw(type, target, rawData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::Send(Network::Address target, const std::string& data)
|
void Network::Send(Address target, const std::string& data)
|
||||||
{
|
{
|
||||||
Network::Send(Game::netsrc_t::NS_CLIENT1, target, data);
|
Send(Game::netsrc_t::NS_CLIENT1, target, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::SendRaw(Game::netsrc_t type, Network::Address target, const std::string& data)
|
void Network::SendRaw(Game::netsrc_t type, Address target, const std::string& data)
|
||||||
{
|
{
|
||||||
if (!target.isValid()) return;
|
if (!target.isValid()) return;
|
||||||
|
|
||||||
@ -176,12 +176,12 @@ namespace Components
|
|||||||
Game::Sys_SendPacket(type, data.size(), data.data(), *target.get());
|
Game::Sys_SendPacket(type, data.size(), data.data(), *target.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::SendRaw(Network::Address target, const std::string& data)
|
void Network::SendRaw(Address target, const std::string& data)
|
||||||
{
|
{
|
||||||
Network::SendRaw(Game::netsrc_t::NS_CLIENT1, target, data);
|
SendRaw(Game::NS_CLIENT1, target, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::SendCommand(Game::netsrc_t type, Network::Address target, const std::string& command, const std::string& data)
|
void Network::SendCommand(Game::netsrc_t type, Address target, const std::string& command, const std::string& data)
|
||||||
{
|
{
|
||||||
// Use space as separator (possible separators are '\n', ' ').
|
// Use space as separator (possible separators are '\n', ' ').
|
||||||
// Though, our handler only needs exactly 1 char as separator and doesn't care which char it is.
|
// Though, our handler only needs exactly 1 char as separator and doesn't care which char it is.
|
||||||
@ -191,12 +191,12 @@ namespace Components
|
|||||||
packet.append("\n", 1);
|
packet.append("\n", 1);
|
||||||
packet.append(data);
|
packet.append(data);
|
||||||
|
|
||||||
Network::Send(type, target, packet);
|
Send(type, target, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::SendCommand(Network::Address target, const std::string& command, const std::string& data)
|
void Network::SendCommand(Address target, const std::string& command, const std::string& data)
|
||||||
{
|
{
|
||||||
Network::SendCommand(Game::netsrc_t::NS_CLIENT1, target, command, data);
|
SendCommand(Game::NS_CLIENT1, target, command, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::Broadcast(unsigned short port, const std::string& data)
|
void Network::Broadcast(unsigned short port, const std::string& data)
|
||||||
@ -207,25 +207,26 @@ namespace Components
|
|||||||
target.setIP(INADDR_BROADCAST);
|
target.setIP(INADDR_BROADCAST);
|
||||||
target.setType(Game::netadrtype_t::NA_BROADCAST);
|
target.setType(Game::netadrtype_t::NA_BROADCAST);
|
||||||
|
|
||||||
Network::Send(Game::netsrc_t::NS_CLIENT1, target, data);
|
Send(Game::netsrc_t::NS_CLIENT1, target, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::BroadcastRange(unsigned int min, unsigned int max, const std::string& data)
|
void Network::BroadcastRange(unsigned int min, unsigned int max, const std::string& data)
|
||||||
{
|
{
|
||||||
for (unsigned int i = min; i < max; ++i)
|
for (unsigned int i = min; i < max; ++i)
|
||||||
{
|
{
|
||||||
Network::Broadcast(static_cast<unsigned short>(i & 0xFFFF), data);
|
Broadcast(static_cast<unsigned short>(i & 0xFFFF), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::BroadcastAll(const std::string& data)
|
void Network::BroadcastAll(const std::string& data)
|
||||||
{
|
{
|
||||||
Network::BroadcastRange(100, 65536, data);
|
BroadcastRange(100, 65536, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::NetworkStart()
|
void Network::NetworkStart()
|
||||||
{
|
{
|
||||||
Network::StartupSignal();
|
StartupSignal();
|
||||||
|
StartupSignal.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short Network::GetPort()
|
unsigned short Network::GetPort()
|
||||||
@ -239,7 +240,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
mov eax, 64D900h
|
mov eax, 64D900h
|
||||||
call eax
|
call eax
|
||||||
jmp Network::NetworkStart
|
jmp NetworkStart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,32 +268,55 @@ namespace Components
|
|||||||
if (client->reliableAcknowledge < 0)
|
if (client->reliableAcknowledge < 0)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::conChannel_t::CON_CHANNEL_NETWORK, "Negative reliableAcknowledge from {} - cl->reliableSequence is {}, reliableAcknowledge is {}\n",
|
Logger::Print(Game::conChannel_t::CON_CHANNEL_NETWORK, "Negative reliableAcknowledge from {} - cl->reliableSequence is {}, reliableAcknowledge is {}\n",
|
||||||
client->name, client->reliableSequence, client->reliableAcknowledge);
|
client->name, client->reliableSequence, client->reliableAcknowledge);
|
||||||
client->reliableAcknowledge = client->reliableSequence;
|
client->reliableAcknowledge = client->reliableSequence;
|
||||||
Network::SendCommand(Game::NS_SERVER, client->netchan.remoteAddress, "error", "EXE_LOSTRELIABLECOMMANDS");
|
SendCommand(Game::NS_SERVER, client->header.netchan.remoteAddress, "error", "EXE_LOSTRELIABLECOMMANDS");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Hook::Call<void(Game::client_t*, Game::msg_t*)>(0x414D40)(client, msg);
|
Utils::Hook::Call<void(Game::client_t*, Game::msg_t*)>(0x414D40)(client, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::OnPacket(const std::string& command, const NetworkCallback& callback)
|
void Network::OnClientPacket(const std::string& command, const NetworkCallback& callback)
|
||||||
{
|
{
|
||||||
Network::Callbacks[Utils::String::ToLower(command)] = callback;
|
CL_Callbacks[Utils::String::ToLower(command)] = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Network::HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
|
void Network::OnServerPacket(const std::string& command, const NetworkCallback& callback)
|
||||||
|
{
|
||||||
|
SV_Callbacks[Utils::String::ToLower(command)] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Network::CL_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
|
||||||
{
|
{
|
||||||
const auto command_ = Utils::String::ToLower(command);
|
const auto command_ = Utils::String::ToLower(command);
|
||||||
const auto handler = Network::Callbacks.find(command_);
|
const auto handler = CL_Callbacks.find(command_);
|
||||||
|
|
||||||
const auto offset = command_.size() + 5;
|
const auto offset = command_.size() + 5;
|
||||||
if (static_cast<std::size_t>(message->cursize) < offset || handler == Network::Callbacks.end())
|
if (static_cast<std::size_t>(message->cursize) < offset || handler == CL_Callbacks.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string data(message->data + offset, message->cursize - offset);
|
const std::string data(reinterpret_cast<char*>(message->data) + offset, message->cursize - offset);
|
||||||
|
|
||||||
|
Address address_ = address;
|
||||||
|
handler->second(address_, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Network::SV_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
|
||||||
|
{
|
||||||
|
const auto command_ = Utils::String::ToLower(command);
|
||||||
|
const auto handler = SV_Callbacks.find(command_);
|
||||||
|
|
||||||
|
const auto offset = command_.size() + 5;
|
||||||
|
if (static_cast<std::size_t>(message->cursize) < offset || handler == SV_Callbacks.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data(reinterpret_cast<char*>(message->data) + offset, message->cursize - offset);
|
||||||
|
|
||||||
Address address_ = address;
|
Address address_ = address;
|
||||||
handler->second(address_, data);
|
handler->second(address_, data);
|
||||||
@ -308,9 +332,9 @@ namespace Components
|
|||||||
pushad
|
pushad
|
||||||
|
|
||||||
push ebp // msg_t
|
push ebp // msg_t
|
||||||
push edi // Command name
|
push edi // command name
|
||||||
push eax // netadr_t pointer
|
push eax // netadr_t pointer
|
||||||
call Network::HandleCommand
|
call CL_HandleCommand
|
||||||
add esp, 0xC
|
add esp, 0xC
|
||||||
|
|
||||||
test al, al
|
test al, al
|
||||||
@ -330,6 +354,37 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Network::SV_HandleCommandStub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
lea eax, [esp + 0x408]
|
||||||
|
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push esi // msg
|
||||||
|
push edi // command name
|
||||||
|
push eax // netadr_t pointer
|
||||||
|
call SV_HandleCommand
|
||||||
|
add esp, 0xC
|
||||||
|
|
||||||
|
test al, al
|
||||||
|
|
||||||
|
popad
|
||||||
|
|
||||||
|
jz unhandled
|
||||||
|
|
||||||
|
// Exit SV_ConnectionlessPacket
|
||||||
|
push 0x6267EB
|
||||||
|
retn
|
||||||
|
|
||||||
|
unhandled:
|
||||||
|
// Proceed
|
||||||
|
push 0x6266E0
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Network::Network()
|
Network::Network()
|
||||||
{
|
{
|
||||||
AssertSize(Game::netadr_t, 20);
|
AssertSize(Game::netadr_t, 20);
|
||||||
@ -355,25 +410,28 @@ namespace Components
|
|||||||
Utils::Hook::Set<const char*>(0x4698E3, "%u.%u.%u.%u:%hu");
|
Utils::Hook::Set<const char*>(0x4698E3, "%u.%u.%u.%u:%hu");
|
||||||
|
|
||||||
// Install startup handler
|
// Install startup handler
|
||||||
Utils::Hook(0x4FD4D4, Network::NetworkStartStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4FD4D4, NetworkStartStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Prevent recvfrom error spam
|
// Prevent recvfrom error spam
|
||||||
Utils::Hook(0x46531A, Network::PacketErrorCheck, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x46531A, PacketErrorCheck, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Fix server freezer exploit
|
// Fix server freezer exploit
|
||||||
Utils::Hook(0x626996, Network::SV_ExecuteClientMessageStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x626996, SV_ExecuteClientMessageStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Handle client packets
|
// Handle client packets
|
||||||
Utils::Hook(0x5AA703, Network::CL_HandleCommandStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5AA703, CL_HandleCommandStub, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook(0x6266CA, SV_HandleCommandStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Disable unused OOB packets handlers just to be sure
|
// Disable unused OOB packets handlers just to be sure
|
||||||
Utils::Hook::Set<BYTE>(0x5AA5B6, 0xEB); // CL_SteamServerAuth
|
Utils::Hook::Set<BYTE>(0x5AA5B6, 0xEB); // CL_SteamServerAuth
|
||||||
Utils::Hook::Set<BYTE>(0x5AA69F, 0xEB); // echo
|
Utils::Hook::Set<BYTE>(0x5AA69F, 0xEB); // echo
|
||||||
Utils::Hook::Set<BYTE>(0x5AAA82, 0xEB); // SP
|
Utils::Hook::Set<BYTE>(0x5AAA82, 0xEB); // SP
|
||||||
|
Utils::Hook::Set<BYTE>(0x5A9F18, 0xEB); // CL_VoiceConnectionTestPacket
|
||||||
|
Utils::Hook::Set<BYTE>(0x5A9FF3, 0xEB); // CL_HandleRelayPacket
|
||||||
|
|
||||||
Network::OnPacket("resolveAddress", [](const Address& address, [[maybe_unused]] const std::string& data)
|
OnClientPacket("resolveAddress", [](const Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Network::SendRaw(address, address.getString());
|
SendRaw(address, address.getString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,26 +21,26 @@ namespace Components
|
|||||||
bool operator==(const Address &obj) const;
|
bool operator==(const Address &obj) const;
|
||||||
|
|
||||||
void setPort(unsigned short port);
|
void setPort(unsigned short port);
|
||||||
unsigned short getPort();
|
[[nodiscard]] unsigned short getPort() const;
|
||||||
|
|
||||||
void setIP(DWORD ip);
|
void setIP(DWORD ip);
|
||||||
void setIP(Game::netIP_t ip);
|
void setIP(Game::netIP_t ip);
|
||||||
Game::netIP_t getIP();
|
[[nodiscard]] Game::netIP_t getIP() const;
|
||||||
|
|
||||||
void setType(Game::netadrtype_t type);
|
void setType(Game::netadrtype_t type);
|
||||||
Game::netadrtype_t getType();
|
[[nodiscard]] Game::netadrtype_t getType() const;
|
||||||
|
|
||||||
sockaddr getSockAddr();
|
[[nodiscard]] sockaddr getSockAddr();
|
||||||
void toSockAddr(sockaddr* addr);
|
void toSockAddr(sockaddr* addr);
|
||||||
void toSockAddr(sockaddr_in* addr);
|
void toSockAddr(sockaddr_in* addr);
|
||||||
Game::netadr_t* get();
|
Game::netadr_t* get();
|
||||||
const char* getCString() const;
|
[[nodiscard]] const char* getCString() const;
|
||||||
std::string getString() const;
|
[[nodiscard]] std::string getString() const;
|
||||||
|
|
||||||
bool isLocal();
|
[[nodiscard]] bool isLocal();
|
||||||
bool isSelf();
|
[[nodiscard]] bool isSelf();
|
||||||
bool isValid();
|
[[nodiscard]] bool isValid() const;
|
||||||
bool isLoopback();
|
[[nodiscard]] bool isLoopback() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Game::netadr_t address;
|
Game::netadr_t address;
|
||||||
@ -54,7 +54,7 @@ namespace Components
|
|||||||
|
|
||||||
static unsigned short GetPort();
|
static unsigned short GetPort();
|
||||||
|
|
||||||
static void OnStart(Utils::Slot<CallbackRaw> callback);
|
static void OnStart(const Utils::Slot<CallbackRaw>& callback);
|
||||||
|
|
||||||
// Send quake-styled binary data
|
// Send quake-styled binary data
|
||||||
static void Send(Address target, const std::string& data);
|
static void Send(Address target, const std::string& data);
|
||||||
@ -72,12 +72,13 @@ namespace Components
|
|||||||
static void BroadcastRange(unsigned int min, unsigned int max, const std::string& data);
|
static void BroadcastRange(unsigned int min, unsigned int max, const std::string& data);
|
||||||
static void BroadcastAll(const std::string& data);
|
static void BroadcastAll(const std::string& data);
|
||||||
|
|
||||||
static void OnPacket(const std::string& command, const NetworkCallback& callback);
|
static void OnClientPacket(const std::string& command, const NetworkCallback& callback);
|
||||||
|
static void OnServerPacket(const std::string& command, const NetworkCallback& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string SelectedPacket;
|
|
||||||
static Utils::Signal<CallbackRaw> StartupSignal;
|
static Utils::Signal<CallbackRaw> StartupSignal;
|
||||||
static std::unordered_map<std::string, NetworkCallback> Callbacks;
|
static std::unordered_map<std::string, NetworkCallback> CL_Callbacks;
|
||||||
|
static std::unordered_map<std::string, NetworkCallback> SV_Callbacks;
|
||||||
|
|
||||||
static void NetworkStart();
|
static void NetworkStart();
|
||||||
static void NetworkStartStub();
|
static void NetworkStartStub();
|
||||||
@ -86,17 +87,19 @@ namespace Components
|
|||||||
|
|
||||||
static void SV_ExecuteClientMessageStub(Game::client_t* client, Game::msg_t* msg);
|
static void SV_ExecuteClientMessageStub(Game::client_t* client, Game::msg_t* msg);
|
||||||
|
|
||||||
static bool HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message);
|
static bool CL_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message);
|
||||||
|
static bool SV_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message);
|
||||||
|
|
||||||
static void CL_HandleCommandStub();
|
static void CL_HandleCommandStub();
|
||||||
|
static void SV_HandleCommandStub();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct std::hash<Components::Network::Address>
|
struct std::hash<Components::Network::Address>
|
||||||
{
|
{
|
||||||
std::size_t operator()(const Components::Network::Address& k) const
|
std::size_t operator()(const Components::Network::Address& k) const noexcept
|
||||||
{
|
{
|
||||||
return (std::hash<std::string>()(k.getString()));
|
return std::hash<std::string>()(k.getString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,7 @@ namespace Components
|
|||||||
|
|
||||||
Dvar::Register<int>("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_INIT, "Current version number.");
|
Dvar::Register<int>("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_INIT, "Current version number.");
|
||||||
|
|
||||||
UIScript::Add("checkFirstLaunch", [](UIScript::Token)
|
UIScript::Add("checkFirstLaunch", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (Dvar::Var("g_firstLaunch").get<bool>())
|
if (Dvar::Var("g_firstLaunch").get<bool>())
|
||||||
{
|
{
|
||||||
@ -52,7 +52,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("visitWebsite", [](UIScript::Token)
|
UIScript::Add("visitWebsite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Utils::OpenUrl(Utils::Cache::GetStaticUrl(""));
|
Utils::OpenUrl(Utils::Cache::GetStaticUrl(""));
|
||||||
});
|
});
|
||||||
|
@ -41,7 +41,7 @@ namespace Components
|
|||||||
|
|
||||||
Session::Send(this->address, "nodeListRequest");
|
Session::Send(this->address, "nodeListRequest");
|
||||||
Node::SendList(this->address);
|
Node::SendList(this->address);
|
||||||
Logger::Debug("Sent request to {}", this->address.getCString());
|
Logger::Debug("Sent request to {}", this->address.getString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::Entry::reset()
|
void Node::Entry::reset()
|
||||||
@ -50,7 +50,7 @@ namespace Components
|
|||||||
this->lastRequest.reset();
|
this->lastRequest.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json Node::Entry::to_json() const
|
nlohmann::json Node::Entry::to_json() const
|
||||||
{
|
{
|
||||||
return this->address.getString();
|
return this->address.getString();
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ namespace Components
|
|||||||
Proto::Node::List list;
|
Proto::Node::List list;
|
||||||
if (!list.ParseFromString(data)) return;
|
if (!list.ParseFromString(data)) return;
|
||||||
|
|
||||||
Logger::Debug("Received response from {}", address.getCString());
|
Logger::Debug("Received response from {}", address.getString());
|
||||||
|
|
||||||
std::lock_guard _(Node::Mutex);
|
std::lock_guard _(Node::Mutex);
|
||||||
|
|
||||||
@ -253,12 +253,12 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && !ServerList::useMasterServer && list.protocol() == PROTOCOL)
|
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && !ServerList::useMasterServer && list.protocol() == PROTOCOL)
|
||||||
{
|
{
|
||||||
Logger::Debug("Inserting {} into the serverlist", address.getCString());
|
Logger::Debug("Inserting {} into the serverlist", address.getString());
|
||||||
ServerList::InsertRequest(address);
|
ServerList::InsertRequest(address);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Debug("Dropping serverlist insertion for {}", address.getCString());
|
Logger::Debug("Dropping serverlist insertion for {}", address.getString());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& node : Node::Nodes)
|
for (auto& node : Node::Nodes)
|
||||||
@ -379,7 +379,7 @@ namespace Components
|
|||||||
std::lock_guard _(Node::Mutex);
|
std::lock_guard _(Node::Mutex);
|
||||||
for (auto& node : Node::Nodes)
|
for (auto& node : Node::Nodes)
|
||||||
{
|
{
|
||||||
Logger::Print("{}\t({})\n", node.address.getCString(), node.isValid() ? "Valid" : "Invalid");
|
Logger::Print("{}\t({})\n", node.address.getString(), node.isValid() ? "Valid" : "Invalid");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ namespace Components
|
|||||||
void sendRequest();
|
void sendRequest();
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
json11::Json to_json() const;
|
nlohmann::json to_json() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Node();
|
Node();
|
||||||
|
@ -106,7 +106,7 @@ namespace Components
|
|||||||
Party::Container.target.setIP(*Game::localIP);
|
Party::Container.target.setIP(*Game::localIP);
|
||||||
Party::Container.target.setType(Game::netadrtype_t::NA_IP);
|
Party::Container.target.setType(Game::netadrtype_t::NA_IP);
|
||||||
|
|
||||||
Logger::Print("Trying to connect to party with loopback address, using a local ip instead: {}\n", Party::Container.target.getCString());
|
Logger::Print("Trying to connect to party with loopback address, using a local ip instead: {}\n", Party::Container.target.getString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -139,7 +139,7 @@ namespace Components
|
|||||||
|
|
||||||
bool Party::IsInLobby()
|
bool Party::IsInLobby()
|
||||||
{
|
{
|
||||||
return (!Dvar::Var("sv_running").get<bool>() && PartyEnable.get<bool>() && Dvar::Var("party_host").get<bool>());
|
return (!(*Game::com_sv_running)->current.enabled && PartyEnable.get<bool>() && Dvar::Var("party_host").get<bool>());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Party::IsInUserMapLobby()
|
bool Party::IsInUserMapLobby()
|
||||||
@ -312,7 +312,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Basic info handler
|
// Basic info handler
|
||||||
Network::OnPacket("getInfo", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnServerPacket("getInfo", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
int botCount = 0;
|
int botCount = 0;
|
||||||
int clientCount = 0;
|
int clientCount = 0;
|
||||||
@ -322,7 +322,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < maxclientCount; ++i)
|
for (int i = 0; i < maxclientCount; ++i)
|
||||||
{
|
{
|
||||||
if (Game::svs_clients[i].state >= 3)
|
if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED)
|
||||||
{
|
{
|
||||||
if (Game::svs_clients[i].bIsTestClient) ++botCount;
|
if (Game::svs_clients[i].bIsTestClient) ++botCount;
|
||||||
else ++clientCount;
|
else ++clientCount;
|
||||||
@ -338,9 +338,9 @@ namespace Components
|
|||||||
Utils::InfoString info;
|
Utils::InfoString info;
|
||||||
info.set("challenge", Utils::ParseChallenge(data));
|
info.set("challenge", Utils::ParseChallenge(data));
|
||||||
info.set("gamename", "IW4");
|
info.set("gamename", "IW4");
|
||||||
info.set("hostname", Dvar::Var("sv_hostname").get<const char*>());
|
info.set("hostname", (*Game::sv_hostname)->current.string);
|
||||||
info.set("gametype", Dvar::Var("g_gametype").get<const char*>());
|
info.set("gametype", (*Game::sv_gametype)->current.string);
|
||||||
info.set("fs_game", Dvar::Var("fs_game").get<const char*>());
|
info.set("fs_game", (*Game::fs_gameDirVar)->current.string);
|
||||||
info.set("xuid", Utils::String::VA("%llX", Steam::SteamUser()->GetSteamID().bits));
|
info.set("xuid", Utils::String::VA("%llX", Steam::SteamUser()->GetSteamID().bits));
|
||||||
info.set("clients", Utils::String::VA("%i", clientCount));
|
info.set("clients", Utils::String::VA("%i", clientCount));
|
||||||
info.set("bots", Utils::String::VA("%i", botCount));
|
info.set("bots", Utils::String::VA("%i", botCount));
|
||||||
@ -352,7 +352,9 @@ namespace Components
|
|||||||
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().size() ? "1" : "0"));
|
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().size() ? "1" : "0"));
|
||||||
info.set("hc", (Dvar::Var("g_hardcore").get<bool>() ? "1" : "0"));
|
info.set("hc", (Dvar::Var("g_hardcore").get<bool>() ? "1" : "0"));
|
||||||
info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get<int>()));
|
info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get<int>()));
|
||||||
info.set("sv_running", (Dvar::Var("sv_running").get<bool>() ? "1" : "0"));
|
info.set("sv_running", ((*Game::com_sv_running)->current.enabled ? "1" : "0"));
|
||||||
|
info.set("aimAssist", (Gamepad::sv_allowAimAssist.get<bool>() ? "1" : "0"));
|
||||||
|
info.set("voiceChat", (Voice::SV_VoiceEnabled() ? "1" : "0"));
|
||||||
|
|
||||||
// Ensure mapname is set
|
// Ensure mapname is set
|
||||||
if (info.get("mapname").empty() || Party::IsInLobby())
|
if (info.get("mapname").empty() || Party::IsInLobby())
|
||||||
@ -398,7 +400,7 @@ namespace Components
|
|||||||
Network::SendCommand(address, "infoResponse", "\\" + info.build());
|
Network::SendCommand(address, "infoResponse", "\\" + info.build());
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnPacket("infoResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnClientPacket("infoResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Utils::InfoString info(data);
|
Utils::InfoString info(data);
|
||||||
|
|
||||||
@ -416,7 +418,7 @@ namespace Components
|
|||||||
bool isUsermap = !info.get("usermaphash").empty();
|
bool isUsermap = !info.get("usermaphash").empty();
|
||||||
unsigned int usermapHash = atoi(info.get("usermaphash").data());
|
unsigned int usermapHash = atoi(info.get("usermaphash").data());
|
||||||
|
|
||||||
std::string mod = Dvar::Var("fs_game").get<std::string>();
|
std::string mod = (*Game::fs_gameDirVar)->current.string;
|
||||||
|
|
||||||
// set fast server stuff here so its updated when we go to download stuff
|
// set fast server stuff here so its updated when we go to download stuff
|
||||||
if (info.get("wwwDownload") == "1"s)
|
if (info.get("wwwDownload") == "1"s)
|
||||||
@ -468,7 +470,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else if (!Dvar::Var("fs_game").get<std::string>().empty() && info.get("fs_game").empty())
|
else if (!Dvar::Var("fs_game").get<std::string>().empty() && info.get("fs_game").empty())
|
||||||
{
|
{
|
||||||
Dvar::Var("fs_game").set("");
|
Game::Dvar_SetString(*Game::fs_gameDirVar, "");
|
||||||
|
|
||||||
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
||||||
{
|
{
|
||||||
|
@ -187,8 +187,8 @@ namespace Components
|
|||||||
Utils::Hook::Set<BYTE>(0x4D6E60, 0xC3);
|
Utils::Hook::Set<BYTE>(0x4D6E60, 0xC3);
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::OnPacket("getPlaylist", PlaylistRequest);
|
Network::OnClientPacket("getPlaylist", PlaylistRequest);
|
||||||
Network::OnPacket("playlistResponse", PlaylistReponse);
|
Network::OnClientPacket("playlistResponse", PlaylistReponse);
|
||||||
Network::OnPacket("playlistInvalidPassword", PlaylistInvalidPassword);
|
Network::OnClientPacket("playlistInvalidPassword", PlaylistInvalidPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void QuickPatch::JavelinResetHookStub()
|
__declspec(naked) void QuickPatch::JavelinResetHook_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -62,7 +62,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
Game::dvar_t* QuickPatch::g_antilag;
|
Game::dvar_t* QuickPatch::g_antilag;
|
||||||
__declspec(naked) void QuickPatch::ClientEventsFireWeaponStub()
|
__declspec(naked) void QuickPatch::ClientEventsFireWeapon_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -78,19 +78,19 @@ namespace Components
|
|||||||
mov ecx, [eax]
|
mov ecx, [eax]
|
||||||
|
|
||||||
fireWeapon:
|
fireWeapon:
|
||||||
push edx
|
push edx
|
||||||
push ecx
|
push ecx
|
||||||
push edi
|
push edi
|
||||||
mov eax, 0x4A4D50 // FireWeapon
|
mov eax, 0x4A4D50 // FireWeapon
|
||||||
call eax
|
call eax
|
||||||
add esp, 0Ch
|
add esp, 0Ch
|
||||||
pop edi
|
pop edi
|
||||||
pop ecx
|
pop ecx
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void QuickPatch::ClientEventsFireWeaponMeleeStub()
|
__declspec(naked) void QuickPatch::ClientEventsFireWeaponMelee_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -106,13 +106,13 @@ namespace Components
|
|||||||
mov edx, [eax]
|
mov edx, [eax]
|
||||||
|
|
||||||
fireWeaponMelee:
|
fireWeaponMelee:
|
||||||
push edx
|
push edx
|
||||||
push edi
|
push edi
|
||||||
mov eax, 0x4F2470 // FireWeaponMelee
|
mov eax, 0x4F2470 // FireWeaponMelee
|
||||||
call eax
|
call eax
|
||||||
add esp, 8
|
add esp, 8
|
||||||
pop edi
|
pop edi
|
||||||
pop ecx
|
pop ecx
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ namespace Components
|
|||||||
Utils::Hook::Set<float>(0x66E1C78, r_customAspectRatio.get<float>());
|
Utils::Hook::Set<float>(0x66E1C78, r_customAspectRatio.get<float>());
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void QuickPatch::SetAspectRatioStub()
|
__declspec(naked) void QuickPatch::SetAspectRatio_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -153,11 +153,11 @@ namespace Components
|
|||||||
je useCustomRatio;
|
je useCustomRatio;
|
||||||
|
|
||||||
// execute switch statement code
|
// execute switch statement code
|
||||||
push 0x005063FC;
|
push 0x5063FC;
|
||||||
retn;
|
retn;
|
||||||
|
|
||||||
goToDefaultCase:
|
goToDefaultCase:
|
||||||
push 0x005064FC;
|
push 0x5064FC;
|
||||||
retn;
|
retn;
|
||||||
|
|
||||||
useCustomRatio:
|
useCustomRatio:
|
||||||
@ -170,12 +170,12 @@ namespace Components
|
|||||||
mov eax, 1;
|
mov eax, 1;
|
||||||
|
|
||||||
// continue execution
|
// continue execution
|
||||||
push 0x00506495;
|
push 0x506495;
|
||||||
retn;
|
retn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL QuickPatch::IsDynClassnameStub(const char* classname)
|
BOOL QuickPatch::IsDynClassname_Stub(const char* classname)
|
||||||
{
|
{
|
||||||
const auto version = Zones::Version();
|
const auto version = Zones::Version();
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
void QuickPatch::CL_KeyEvent_OnEscape()
|
void QuickPatch::CL_KeyEvent_OnEscape()
|
||||||
{
|
{
|
||||||
if (Game::Con_CancelAutoComplete())
|
if (Game::Con_CancelAutoComplete())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -212,11 +212,11 @@ namespace Components
|
|||||||
|
|
||||||
// Close console
|
// Close console
|
||||||
Game::Key_RemoveCatcher(0, ~Game::KEYCATCH_CONSOLE);
|
Game::Key_RemoveCatcher(0, ~Game::KEYCATCH_CONSOLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void QuickPatch::CL_KeyEvent_ConsoleEscape_Stub()
|
__declspec(naked) void QuickPatch::CL_KeyEvent_ConsoleEscape_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
pushad
|
pushad
|
||||||
call CL_KeyEvent_OnEscape
|
call CL_KeyEvent_OnEscape
|
||||||
@ -228,6 +228,68 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuickPatch::R_AddImageToList_Hk(Game::XAssetHeader header, void* data)
|
||||||
|
{
|
||||||
|
auto* imageList = static_cast<Game::ImageList*>(data);
|
||||||
|
|
||||||
|
assert(imageList->count < ARRAYSIZE(imageList->image));
|
||||||
|
|
||||||
|
if (header.image->texture.basemap)
|
||||||
|
{
|
||||||
|
imageList->image[imageList->count++] = header.image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickPatch::Sys_SpawnQuitProcess_Hk()
|
||||||
|
{
|
||||||
|
if (*Game::sys_exitCmdLine[0] == '\0')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto workingDir = std::filesystem::current_path().string();
|
||||||
|
auto binary = FileSystem::GetAppdataPath() / "data" / "iw4x" / *Game::sys_exitCmdLine;
|
||||||
|
|
||||||
|
SetEnvironmentVariableA("XLABS_MW2_INSTALL", workingDir.data());
|
||||||
|
Utils::Library::LaunchProcess(binary.string(), "-singleplayer", workingDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void QuickPatch::SND_GetAliasOffset_Stub()
|
||||||
|
{
|
||||||
|
using namespace Game;
|
||||||
|
|
||||||
|
static const char* msg = "SND_GetAliasOffset: Could not find sound alias '%s'";
|
||||||
|
static const DWORD func = 0x4B22D0; // Com_Error
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
// Check if snd_alias_t* is null immediately after call to Com_FindSoundAlias_FastFile
|
||||||
|
test eax, eax
|
||||||
|
jz error
|
||||||
|
|
||||||
|
// Game code hook skipped
|
||||||
|
mov ecx, eax
|
||||||
|
mov edx, dword ptr [ecx + 0x4]
|
||||||
|
|
||||||
|
// Resume function
|
||||||
|
push 0x437CB2
|
||||||
|
ret
|
||||||
|
|
||||||
|
error:
|
||||||
|
add esp, 0x4 // Com_FindSoundAlias_FastFile takes one argument
|
||||||
|
|
||||||
|
push [esi] // alias->aliasName
|
||||||
|
push msg
|
||||||
|
push ERR_DROP
|
||||||
|
call func // Going to longjmp back to safety
|
||||||
|
add esp, 0xC
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
pop esi
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Game::dvar_t* QuickPatch::Dvar_RegisterConMinicon(const char* dvarName, [[maybe_unused]] bool value, unsigned __int16 flags, const char* description)
|
Game::dvar_t* QuickPatch::Dvar_RegisterConMinicon(const char* dvarName, [[maybe_unused]] bool value, unsigned __int16 flags, const char* description)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
@ -241,7 +303,7 @@ namespace Components
|
|||||||
QuickPatch::QuickPatch()
|
QuickPatch::QuickPatch()
|
||||||
{
|
{
|
||||||
// Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning
|
// Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning
|
||||||
Utils::Hook(0x5FBD6E, QuickPatch::IsDynClassnameStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5FBD6E, QuickPatch::IsDynClassname_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Hook escape handling on open console to change behaviour to close the console instead of only canceling autocomplete
|
// Hook escape handling on open console to change behaviour to close the console instead of only canceling autocomplete
|
||||||
Utils::Hook(0x4F66A3, CL_KeyEvent_ConsoleEscape_Stub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4F66A3, CL_KeyEvent_ConsoleEscape_Stub, HOOK_JUMP).install()->quick();
|
||||||
@ -250,18 +312,26 @@ namespace Components
|
|||||||
Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::DVAR_NONE, "Time in seconds before match server loads the next map");
|
Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::DVAR_NONE, "Time in seconds before match server loads the next map");
|
||||||
|
|
||||||
g_antilag = Game::Dvar_RegisterBool("g_antilag", true, Game::DVAR_CODINFO, "Perform antilag");
|
g_antilag = Game::Dvar_RegisterBool("g_antilag", true, Game::DVAR_CODINFO, "Perform antilag");
|
||||||
Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeaponStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeapon_Stub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x5D6D6A, QuickPatch::ClientEventsFireWeaponMeleeStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5D6D6A, QuickPatch::ClientEventsFireWeaponMelee_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Javelin fix
|
// Javelin fix
|
||||||
Utils::Hook(0x578F52, QuickPatch::JavelinResetHookStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x578F52, QuickPatch::JavelinResetHook_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Add ultrawide support
|
// Add ultrawide support
|
||||||
Utils::Hook(0x51B13B, QuickPatch::Dvar_RegisterAspectRatioDvar, HOOK_CALL).install()->quick();
|
Utils::Hook(0x51B13B, QuickPatch::Dvar_RegisterAspectRatioDvar, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x5063F3, QuickPatch::SetAspectRatioStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5063F3, QuickPatch::SetAspectRatio_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x4FA448, QuickPatch::Dvar_RegisterConMinicon, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4FA448, QuickPatch::Dvar_RegisterConMinicon, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook::Set<void(*)(Game::XAssetHeader, void*)>(0x51FCDD, QuickPatch::R_AddImageToList_Hk);
|
||||||
|
|
||||||
|
Utils::Hook::Set<const char*>(0x41DB8C, "iw4x-sp.exe");
|
||||||
|
Utils::Hook(0x4D6989, QuickPatch::Sys_SpawnQuitProcess_Hk, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
// Fix crash as nullptr goes unchecked
|
||||||
|
Utils::Hook(0x437CAD, QuickPatch::SND_GetAliasOffset_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// protocol version (workaround for hacks)
|
// protocol version (workaround for hacks)
|
||||||
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
|
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
|
||||||
|
|
||||||
@ -349,7 +419,7 @@ namespace Components
|
|||||||
// spawn upnp thread when UPNP_init returns
|
// spawn upnp thread when UPNP_init returns
|
||||||
Utils::Hook::Hook(0x47982B, []()
|
Utils::Hook::Hook(0x47982B, []()
|
||||||
{
|
{
|
||||||
std::thread([]()
|
std::thread([]
|
||||||
{
|
{
|
||||||
// check natpmpstate
|
// check natpmpstate
|
||||||
// state 4 is no more devices to query
|
// state 4 is no more devices to query
|
||||||
@ -440,11 +510,11 @@ namespace Components
|
|||||||
Utils::Hook::Set<const char*>(0x60BBD4, CLIENT_CONFIG);
|
Utils::Hook::Set<const char*>(0x60BBD4, CLIENT_CONFIG);
|
||||||
|
|
||||||
// Disable profile system
|
// Disable profile system
|
||||||
// Utils::Hook::Nop(0x60BEB1, 5); // GamerProfile_InitAllProfiles - Causes an error, when calling a harrier killstreak.
|
// Utils::Hook::Nop(0x60BEB1, 5); // GamerProfile_InitAllProfiles - Causes an error, when calling a harrier killstreak.
|
||||||
// Utils::Hook::Nop(0x60BEB8, 5); // GamerProfile_LogInProfile
|
// Utils::Hook::Nop(0x60BEB8, 5); // GamerProfile_LogInProfile
|
||||||
// Utils::Hook::Nop(0x4059EA, 5); // GamerProfile_RegisterCommands
|
// Utils::Hook::Nop(0x4059EA, 5); // GamerProfile_RegisterCommands
|
||||||
Utils::Hook::Nop(0x4059EF, 5); // GamerProfile_RegisterDvars
|
Utils::Hook::Nop(0x4059EF, 5); // GamerProfile_RegisterDvars
|
||||||
Utils::Hook::Nop(0x47DF9A, 5); // GamerProfile_UpdateSystemDvars
|
Utils::Hook::Nop(0x47DF9A, 5); // GamerProfile_UpdateSystemDvars
|
||||||
Utils::Hook::Set<BYTE>(0x5AF0D0, 0xC3); // GamerProfile_SaveProfile
|
Utils::Hook::Set<BYTE>(0x5AF0D0, 0xC3); // GamerProfile_SaveProfile
|
||||||
Utils::Hook::Set<BYTE>(0x4E6870, 0xC3); // GamerProfile_UpdateSystemVarsFromProfile
|
Utils::Hook::Set<BYTE>(0x4E6870, 0xC3); // GamerProfile_UpdateSystemVarsFromProfile
|
||||||
Utils::Hook::Set<BYTE>(0x4C37F0, 0xC3); // GamerProfile_UpdateProfileAndSaveIfNeeded
|
Utils::Hook::Set<BYTE>(0x4C37F0, 0xC3); // GamerProfile_UpdateProfileAndSaveIfNeeded
|
||||||
@ -483,7 +553,7 @@ namespace Components
|
|||||||
|
|
||||||
// Fix mouse pitch adjustments
|
// Fix mouse pitch adjustments
|
||||||
Dvar::Register<bool>("ui_mousePitch", false, Game::DVAR_ARCHIVE, "");
|
Dvar::Register<bool>("ui_mousePitch", false, Game::DVAR_ARCHIVE, "");
|
||||||
UIScript::Add("updateui_mousePitch", [](UIScript::Token)
|
UIScript::Add("updateui_mousePitch", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (Dvar::Var("ui_mousePitch").get<bool>())
|
if (Dvar::Var("ui_mousePitch").get<bool>())
|
||||||
{
|
{
|
||||||
@ -495,9 +565,6 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ignore call to print 'Offhand class mismatch when giving weapon...'
|
|
||||||
Utils::Hook(0x5D9047, 0x4BB9B0, HOOK_CALL).install()->quick();
|
|
||||||
|
|
||||||
Command::Add("unlockstats", QuickPatch::UnlockStats);
|
Command::Add("unlockstats", QuickPatch::UnlockStats);
|
||||||
|
|
||||||
Command::Add("dumptechsets", [](Command::Params* param)
|
Command::Add("dumptechsets", [](Command::Params* param)
|
||||||
|
@ -12,22 +12,28 @@ namespace Components
|
|||||||
static void UnlockStats();
|
static void UnlockStats();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void JavelinResetHookStub();
|
static void JavelinResetHook_Stub();
|
||||||
|
|
||||||
static Dvar::Var r_customAspectRatio;
|
static Dvar::Var r_customAspectRatio;
|
||||||
static Game::dvar_t* Dvar_RegisterAspectRatioDvar(const char* dvarName, const char** valueList, int defaultIndex, unsigned __int16 flags, const char* description);
|
static Game::dvar_t* Dvar_RegisterAspectRatioDvar(const char* dvarName, const char** valueList, int defaultIndex, unsigned __int16 flags, const char* description);
|
||||||
static void SetAspectRatioStub();
|
static void SetAspectRatio_Stub();
|
||||||
static void SetAspectRatio();
|
static void SetAspectRatio();
|
||||||
|
|
||||||
static Game::dvar_t* g_antilag;
|
static Game::dvar_t* g_antilag;
|
||||||
static void ClientEventsFireWeaponStub();
|
static void ClientEventsFireWeapon_Stub();
|
||||||
static void ClientEventsFireWeaponMeleeStub();
|
static void ClientEventsFireWeaponMelee_Stub();
|
||||||
|
|
||||||
static BOOL IsDynClassnameStub(const char* classname);
|
static BOOL IsDynClassname_Stub(const char* classname);
|
||||||
|
|
||||||
static void CL_KeyEvent_OnEscape();
|
static void CL_KeyEvent_OnEscape();
|
||||||
static void CL_KeyEvent_ConsoleEscape_Stub();
|
static void CL_KeyEvent_ConsoleEscape_Stub();
|
||||||
|
|
||||||
|
static void R_AddImageToList_Hk(Game::XAssetHeader header, void* data);
|
||||||
|
|
||||||
|
static void Sys_SpawnQuitProcess_Hk();
|
||||||
|
|
||||||
|
static void SND_GetAliasOffset_Stub();
|
||||||
|
|
||||||
static Game::dvar_t* Dvar_RegisterConMinicon(const char* dvarName, bool value, unsigned __int16 flags, const char* description);
|
static Game::dvar_t* Dvar_RegisterConMinicon(const char* dvarName, bool value, unsigned __int16 flags, const char* description);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ namespace Components
|
|||||||
RCon::RconLogRequests = Dvar::Register<bool>("rcon_log_requests", false, Game::DVAR_NONE, "Print remote commands in the output log");
|
RCon::RconLogRequests = Dvar::Register<bool>("rcon_log_requests", false, Game::DVAR_NONE, "Print remote commands in the output log");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Network::OnPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnServerPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
std::string data_ = data;
|
std::string data_ = data;
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ namespace Components
|
|||||||
const auto pos = data.find_first_of(' ');
|
const auto pos = data.find_first_of(' ');
|
||||||
if (pos == std::string::npos)
|
if (pos == std::string::npos)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon request from {}\n", address.getCString());
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon request from {}\n", address.getString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ namespace Components
|
|||||||
|
|
||||||
if (svPassword.empty())
|
if (svPassword.empty())
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "RCon request from {} dropped. No password set!\n", address.getCString());
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "RCon request from {} dropped. No password set!\n", address.getString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ namespace Components
|
|||||||
if (RCon::RconLogRequests.get<bool>())
|
if (RCon::RconLogRequests.get<bool>())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "Executing RCon request from {}: {}\n", address.getCString(), command);
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Executing RCon request from {}: {}\n", address.getString(), command);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::PipeOutput([](const std::string& output)
|
Logger::PipeOutput([](const std::string& output)
|
||||||
@ -138,11 +138,11 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon password sent from {}\n", address.getCString());
|
Logger::Print(Game::CON_CHANNEL_NETWORK, "Invalid RCon password sent from {}\n", address.getString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnPacket("rconRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnServerPacket("rconRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
RCon::BackdoorContainer.address = address;
|
RCon::BackdoorContainer.address = address;
|
||||||
RCon::BackdoorContainer.challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
RCon::BackdoorContainer.challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
||||||
@ -151,7 +151,7 @@ namespace Components
|
|||||||
Network::SendCommand(address, "rconAuthorization", RCon::BackdoorContainer.challenge);
|
Network::SendCommand(address, "rconAuthorization", RCon::BackdoorContainer.challenge);
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnPacket("rconExecute", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnServerPacket("rconExecute", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (address != RCon::BackdoorContainer.address) return; // Invalid IP
|
if (address != RCon::BackdoorContainer.address) return; // Invalid IP
|
||||||
if (!RCon::BackdoorContainer.timestamp || (Game::Sys_Milliseconds() - RCon::BackdoorContainer.timestamp) > (1000 * 10)) return; // Timeout
|
if (!RCon::BackdoorContainer.timestamp || (Game::Sys_Milliseconds() - RCon::BackdoorContainer.timestamp) > (1000 * 10)) return; // Timeout
|
||||||
|
@ -7,8 +7,9 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
RawFiles();
|
RawFiles();
|
||||||
|
|
||||||
private:
|
|
||||||
static char* ReadRawFile(const char* filename, char* buf, int size);
|
static char* ReadRawFile(const char* filename, char* buf, int size);
|
||||||
|
|
||||||
|
private:
|
||||||
static char* GetMenuBuffer(const char* filename);
|
static char* GetMenuBuffer(const char* filename);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -266,9 +266,8 @@ namespace Components
|
|||||||
|
|
||||||
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
||||||
|
|
||||||
const auto mapName = Dvar::Var("mapname").get<const char*>();
|
|
||||||
auto scene = Game::scene;
|
auto scene = Game::scene;
|
||||||
auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName));
|
auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", (*Game::sv_mapname)->current.string));
|
||||||
|
|
||||||
if (gfxAsset == nullptr)
|
if (gfxAsset == nullptr)
|
||||||
{
|
{
|
||||||
@ -368,9 +367,8 @@ namespace Components
|
|||||||
|
|
||||||
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
||||||
|
|
||||||
const auto mapName = Dvar::Var("mapname").get<const char*>();
|
|
||||||
auto scene = Game::scene;
|
auto scene = Game::scene;
|
||||||
auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName));
|
auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", (*Game::sv_mapname)->current.string));
|
||||||
|
|
||||||
if (gfxAsset == nullptr)
|
if (gfxAsset == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -102,8 +102,8 @@ namespace Components
|
|||||||
|
|
||||||
void Security::NET_DeferPacketToClientStub(Game::netadr_t* net_from, Game::msg_t* net_message)
|
void Security::NET_DeferPacketToClientStub(Game::netadr_t* net_from, Game::msg_t* net_message)
|
||||||
{
|
{
|
||||||
assert(net_from != nullptr);
|
assert(net_from);
|
||||||
assert(net_message != nullptr);
|
assert(net_message);
|
||||||
|
|
||||||
if (static_cast<std::size_t>(net_message->cursize) >= sizeof(Game::DeferredMsg::data))
|
if (static_cast<std::size_t>(net_message->cursize) >= sizeof(Game::DeferredMsg::data))
|
||||||
{
|
{
|
||||||
|
@ -20,13 +20,10 @@ namespace Components
|
|||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
return Utils::String::VA("%d", index);
|
return Utils::String::VA("%d", index);
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return ServerInfo::PlayerContainer.playerList[index].name.data();
|
return ServerInfo::PlayerContainer.playerList[index].name.data();
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].score);
|
return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].score);
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].ping);
|
return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].ping);
|
||||||
default:
|
default:
|
||||||
@ -42,34 +39,36 @@ namespace Components
|
|||||||
ServerInfo::PlayerContainer.currentPlayer = index;
|
ServerInfo::PlayerContainer.currentPlayer = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerInfo::ServerStatus(UIScript::Token)
|
void ServerInfo::ServerStatus([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
ServerInfo::PlayerContainer.currentPlayer = 0;
|
ServerInfo::PlayerContainer.currentPlayer = 0;
|
||||||
ServerInfo::PlayerContainer.playerList.clear();
|
ServerInfo::PlayerContainer.playerList.clear();
|
||||||
|
|
||||||
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
|
auto* serverInfo = ServerList::GetCurrentServer();
|
||||||
|
|
||||||
if (info)
|
if (info)
|
||||||
{
|
{
|
||||||
Dvar::Var("uiSi_ServerName").set(info->hostname);
|
Dvar::Var("uiSi_ServerName").set(serverInfo->hostname);
|
||||||
Dvar::Var("uiSi_MaxClients").set(info->clients);
|
Dvar::Var("uiSi_MaxClients").set(serverInfo->clients);
|
||||||
Dvar::Var("uiSi_Version").set(info->shortversion);
|
Dvar::Var("uiSi_Version").set(serverInfo->shortversion);
|
||||||
Dvar::Var("uiSi_SecurityLevel").set(info->securityLevel);
|
Dvar::Var("uiSi_SecurityLevel").set(serverInfo->securityLevel);
|
||||||
Dvar::Var("uiSi_isPrivate").set(info->password ? "@MENU_YES" : "@MENU_NO");
|
Dvar::Var("uiSi_isPrivate").set(serverInfo->password ? "@MENU_YES" : "@MENU_NO");
|
||||||
Dvar::Var("uiSi_Hardcore").set(info->hardcore ? "@MENU_ENABLED" : "@MENU_DISABLED");
|
Dvar::Var("uiSi_Hardcore").set(serverInfo->hardcore ? "@MENU_ENABLED" : "@MENU_DISABLED");
|
||||||
Dvar::Var("uiSi_KillCam").set("@MENU_NO");
|
Dvar::Var("uiSi_KillCam").set("@MENU_NO");
|
||||||
Dvar::Var("uiSi_ffType").set("@MENU_DISABLED");
|
Dvar::Var("uiSi_ffType").set("@MENU_DISABLED");
|
||||||
Dvar::Var("uiSi_MapName").set(info->mapname);
|
Dvar::Var("uiSi_MapName").set(serverInfo->mapname);
|
||||||
Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(info->mapname.data()));
|
Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(serverInfo->mapname.data()));
|
||||||
Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(info->gametype.data()));
|
Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(serverInfo->gametype.data()));
|
||||||
Dvar::Var("uiSi_ModName").set("");
|
Dvar::Var("uiSi_ModName").set("");
|
||||||
|
Dvar::Var("uiSi_aimAssist").set(serverInfo->aimassist ? "@MENU_YES" : "@MENU_NO");
|
||||||
|
Dvar::Var("uiSi_voiceChat").set(serverInfo->voice ? "@MENU_YES" : "@MENU_NO");
|
||||||
|
|
||||||
if (info->mod.size() > 5)
|
if (serverInfo->mod.size() > 5)
|
||||||
{
|
{
|
||||||
Dvar::Var("uiSi_ModName").set(info->mod.data() + 5);
|
Dvar::Var("uiSi_ModName").set(serverInfo->mod.data() + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerInfo::PlayerContainer.target = info->addr;
|
ServerInfo::PlayerContainer.target = serverInfo->addr;
|
||||||
Network::SendCommand(ServerInfo::PlayerContainer.target, "getstatus");
|
Network::SendCommand(ServerInfo::PlayerContainer.target, "getstatus");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,22 +129,23 @@ namespace Components
|
|||||||
|
|
||||||
Utils::InfoString ServerInfo::GetInfo()
|
Utils::InfoString ServerInfo::GetInfo()
|
||||||
{
|
{
|
||||||
int maxclientCount = *Game::svs_clientCount;
|
auto maxClientCount = *Game::svs_clientCount;
|
||||||
|
|
||||||
if (!maxclientCount)
|
if (!maxClientCount)
|
||||||
{
|
{
|
||||||
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
|
maxClientCount = Dvar::Var("party_maxplayers").get<int>();
|
||||||
//maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::InfoString info(Game::Dvar_InfoString_Big(1024));
|
Utils::InfoString info(Game::Dvar_InfoString_Big(Game::DVAR_SERVERINFO));
|
||||||
info.set("gamename", "IW4");
|
info.set("gamename", "IW4");
|
||||||
info.set("sv_maxclients", Utils::String::VA("%i", maxclientCount));
|
info.set("sv_maxclients", Utils::String::VA("%i", maxClientCount));
|
||||||
info.set("protocol", Utils::String::VA("%i", PROTOCOL));
|
info.set("protocol", Utils::String::VA("%i", PROTOCOL));
|
||||||
info.set("shortversion", SHORTVERSION);
|
info.set("shortversion", SHORTVERSION);
|
||||||
info.set("mapname", Dvar::Var("mapname").get<const char*>());
|
info.set("mapname", (*Game::sv_mapname)->current.string);
|
||||||
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().empty() ? "0" : "1"));
|
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().empty() ? "0" : "1"));
|
||||||
info.set("checksum", Utils::String::VA("%X", Utils::Cryptography::JenkinsOneAtATime::Compute(Utils::String::VA("%u", Game::Sys_Milliseconds()))));
|
info.set("checksum", Utils::String::VA("%X", Utils::Cryptography::JenkinsOneAtATime::Compute(Utils::String::VA("%u", Game::Sys_Milliseconds()))));
|
||||||
|
info.set("aimAssist", (Gamepad::sv_allowAimAssist.get<bool>() ? "1" : "0"));
|
||||||
|
info.set("voiceChat", (Voice::SV_VoiceEnabled() ? "1" : "0"));
|
||||||
|
|
||||||
// Ensure mapname is set
|
// Ensure mapname is set
|
||||||
if (info.get("mapname").empty())
|
if (info.get("mapname").empty())
|
||||||
@ -162,7 +162,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
info.set("matchtype", "1");
|
info.set("matchtype", "1");
|
||||||
}
|
}
|
||||||
else if (Dvar::Var("sv_running").get<bool>()) // Match hosting
|
else if ((*Game::com_sv_running)->current.enabled) // Match hosting
|
||||||
{
|
{
|
||||||
info.set("matchtype", "2");
|
info.set("matchtype", "2");
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ namespace Components
|
|||||||
// Add uifeeder
|
// Add uifeeder
|
||||||
UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer);
|
UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer);
|
||||||
|
|
||||||
Network::OnPacket("getStatus", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnServerPacket("getStatus", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
std::string playerList;
|
std::string playerList;
|
||||||
|
|
||||||
@ -206,9 +206,9 @@ namespace Components
|
|||||||
auto ping = 0;
|
auto ping = 0;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
if (Dvar::Var("sv_running").get<bool>())
|
if ((*Game::com_sv_running)->current.enabled)
|
||||||
{
|
{
|
||||||
if (Game::svs_clients[i].state < 3) continue;
|
if (Game::svs_clients[i].header.state < Game::CS_CONNECTED) continue;
|
||||||
|
|
||||||
score = Game::SV_GameClientNum_Score(i);
|
score = Game::SV_GameClientNum_Score(i);
|
||||||
ping = Game::svs_clients[i].ping;
|
ping = Game::svs_clients[i].ping;
|
||||||
@ -229,86 +229,87 @@ namespace Components
|
|||||||
Network::SendCommand(address, "statusResponse", "\\" + info.build() + "\n" + playerList + "\n");
|
Network::SendCommand(address, "statusResponse", "\\" + info.build() + "\n" + playerList + "\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
Network::OnPacket("statusResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnClientPacket("statusResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (ServerInfo::PlayerContainer.target == address)
|
if (ServerInfo::PlayerContainer.target != address)
|
||||||
{
|
{
|
||||||
Utils::InfoString info(data.substr(0, data.find_first_of("\n")));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Dvar::Var("uiSi_ServerName").set(info.get("sv_hostname"));
|
const Utils::InfoString info(data.substr(0, data.find_first_of("\n")));
|
||||||
Dvar::Var("uiSi_MaxClients").set(info.get("sv_maxclients"));
|
|
||||||
Dvar::Var("uiSi_Version").set(info.get("shortversion"));
|
|
||||||
Dvar::Var("uiSi_SecurityLevel").set(info.get("sv_securityLevel"));
|
|
||||||
Dvar::Var("uiSi_isPrivate").set(info.get("isPrivate") == "0" ? "@MENU_NO" : "@MENU_YES");
|
|
||||||
Dvar::Var("uiSi_Hardcore").set(info.get("g_hardcore") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED");
|
|
||||||
Dvar::Var("uiSi_KillCam").set(info.get("scr_game_allowkillcam") == "0" ? "@MENU_NO" : "@MENU_YES");
|
|
||||||
Dvar::Var("uiSi_MapName").set(info.get("mapname"));
|
|
||||||
Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(info.get("mapname").data()));
|
|
||||||
Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(info.get("g_gametype").data()));
|
|
||||||
Dvar::Var("uiSi_ModName").set("");
|
|
||||||
|
|
||||||
switch (atoi(info.get("scr_team_fftype").data()))
|
Dvar::Var("uiSi_ServerName").set(info.get("sv_hostname"));
|
||||||
|
Dvar::Var("uiSi_MaxClients").set(info.get("sv_maxclients"));
|
||||||
|
Dvar::Var("uiSi_Version").set(info.get("shortversion"));
|
||||||
|
Dvar::Var("uiSi_SecurityLevel").set(info.get("sv_securityLevel"));
|
||||||
|
Dvar::Var("uiSi_isPrivate").set(info.get("isPrivate") == "0" ? "@MENU_NO" : "@MENU_YES");
|
||||||
|
Dvar::Var("uiSi_Hardcore").set(info.get("g_hardcore") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED");
|
||||||
|
Dvar::Var("uiSi_KillCam").set(info.get("scr_game_allowkillcam") == "0" ? "@MENU_NO" : "@MENU_YES");
|
||||||
|
Dvar::Var("uiSi_MapName").set(info.get("mapname"));
|
||||||
|
Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(info.get("mapname").data()));
|
||||||
|
Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(info.get("g_gametype").data()));
|
||||||
|
Dvar::Var("uiSi_ModName").set("");
|
||||||
|
Dvar::Var("uiSi_aimAssist").set(info.get("aimAssist") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED");
|
||||||
|
Dvar::Var("uiSi_voiceChat").set(info.get("voiceChat") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED");
|
||||||
|
|
||||||
|
switch (atoi(info.get("scr_team_fftype").data()))
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
Dvar::Var("uiSi_ffType").set("@MENU_DISABLED");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Dvar::Var("uiSi_ffType").set("@MENU_ENABLED");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Dvar::Var("uiSi_ffType").set("@MPUI_RULES_REFLECT");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Dvar::Var("uiSi_ffType").set("@MPUI_RULES_SHARED");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.get("fs_game").size() > 5)
|
||||||
|
{
|
||||||
|
Dvar::Var("uiSi_ModName").set(info.get("fs_game").data() + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lines = Utils::String::Split(data, '\n');
|
||||||
|
|
||||||
|
if (lines.size() <= 1) return;
|
||||||
|
|
||||||
|
for (std::size_t i = 1; i < lines.size(); ++i)
|
||||||
|
{
|
||||||
|
ServerInfo::Container::Player player;
|
||||||
|
|
||||||
|
std::string currentData = lines[i];
|
||||||
|
|
||||||
|
if (currentData.size() < 3) continue;
|
||||||
|
|
||||||
|
// Insert score
|
||||||
|
player.score = atoi(currentData.substr(0, currentData.find_first_of(" ")).data());
|
||||||
|
|
||||||
|
// Remove score
|
||||||
|
currentData = currentData.substr(currentData.find_first_of(" ") + 1);
|
||||||
|
|
||||||
|
// Insert ping
|
||||||
|
player.ping = atoi(currentData.substr(0, currentData.find_first_of(" ")).data());
|
||||||
|
|
||||||
|
// Remove ping
|
||||||
|
currentData = currentData.substr(currentData.find_first_of(" ") + 1);
|
||||||
|
|
||||||
|
if (currentData[0] == '\"')
|
||||||
{
|
{
|
||||||
default:
|
currentData = currentData.substr(1);
|
||||||
Dvar::Var("uiSi_ffType").set("@MENU_DISABLED");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
Dvar::Var("uiSi_ffType").set("@MENU_ENABLED");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
Dvar::Var("uiSi_ffType").set("@MPUI_RULES_REFLECT");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
Dvar::Var("uiSi_ffType").set("@MPUI_RULES_SHARED");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.get("fs_game").size() > 5)
|
if (currentData.back() == '\"')
|
||||||
{
|
{
|
||||||
Dvar::Var("uiSi_ModName").set(info.get("fs_game").data() + 5);
|
currentData.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lines = Utils::String::Split(data, '\n');
|
player.name = currentData;
|
||||||
|
|
||||||
if (lines.size() <= 1) return;
|
ServerInfo::PlayerContainer.playerList.push_back(player);
|
||||||
|
|
||||||
for (unsigned int i = 1; i < lines.size(); ++i)
|
|
||||||
{
|
|
||||||
ServerInfo::Container::Player player;
|
|
||||||
|
|
||||||
std::string currentData = lines[i];
|
|
||||||
|
|
||||||
if (currentData.size() < 3) continue;
|
|
||||||
|
|
||||||
// Insert score
|
|
||||||
player.score = atoi(currentData.substr(0, currentData.find_first_of(" ")).data());
|
|
||||||
|
|
||||||
// Remove score
|
|
||||||
currentData = currentData.substr(currentData.find_first_of(" ") + 1);
|
|
||||||
|
|
||||||
// Insert ping
|
|
||||||
player.ping = atoi(currentData.substr(0, currentData.find_first_of(" ")).data());
|
|
||||||
|
|
||||||
// Remove ping
|
|
||||||
currentData = currentData.substr(currentData.find_first_of(" ") + 1);
|
|
||||||
|
|
||||||
if (currentData[0] == '\"')
|
|
||||||
{
|
|
||||||
currentData = currentData.substr(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentData.back() == '\"')
|
|
||||||
{
|
|
||||||
currentData.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
player.name = currentData;
|
|
||||||
|
|
||||||
ServerInfo::PlayerContainer.playerList.push_back(player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ namespace Components
|
|||||||
|
|
||||||
static Container PlayerContainer;
|
static Container PlayerContainer;
|
||||||
|
|
||||||
static void ServerStatus(UIScript::Token);
|
static void ServerStatus([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
|
|
||||||
static unsigned int GetPlayerCount();
|
static unsigned int GetPlayerCount();
|
||||||
static const char* GetPlayerText(unsigned int index, int column);
|
static const char* GetPlayerText(unsigned int index, int column);
|
||||||
|
@ -79,7 +79,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
case Column::Password:
|
case Column::Password:
|
||||||
{
|
{
|
||||||
return (server->password ? "X" : "");
|
return (server->password ? ":icon_locked:" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
case Column::Matchtype:
|
case Column::Matchtype:
|
||||||
@ -87,6 +87,16 @@ namespace Components
|
|||||||
return ((server->matchType == 1) ? "P" : "M");
|
return ((server->matchType == 1) ? "P" : "M");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Column::AimAssist:
|
||||||
|
{
|
||||||
|
return ((server->aimassist == 1) ? ":headshot:" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
case Column::VoiceChat:
|
||||||
|
{
|
||||||
|
return ((server->voice == 1) ? ":voice_on:" : "");
|
||||||
|
}
|
||||||
|
|
||||||
case Column::Hostname:
|
case Column::Hostname:
|
||||||
{
|
{
|
||||||
return server->hostname.data();
|
return server->hostname.data();
|
||||||
@ -171,7 +181,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::UpdateVisibleList(UIScript::Token)
|
void ServerList::UpdateVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
auto list = ServerList::GetList();
|
auto list = ServerList::GetList();
|
||||||
if (!list) return;
|
if (!list) return;
|
||||||
@ -180,7 +190,7 @@ namespace Components
|
|||||||
|
|
||||||
if (tempList.empty())
|
if (tempList.empty())
|
||||||
{
|
{
|
||||||
ServerList::Refresh(UIScript::Token());
|
ServerList::Refresh(UIScript::Token(), info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -198,12 +208,12 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::RefreshVisibleList(UIScript::Token)
|
void ServerList::RefreshVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
ServerList::RefreshVisibleListInternal(UIScript::Token());
|
ServerList::RefreshVisibleListInternal(UIScript::Token(), info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::RefreshVisibleListInternal(UIScript::Token, bool refresh)
|
void ServerList::RefreshVisibleListInternal([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info, bool refresh)
|
||||||
{
|
{
|
||||||
Dvar::Var("ui_serverSelected").set(false);
|
Dvar::Var("ui_serverSelected").set(false);
|
||||||
|
|
||||||
@ -214,38 +224,38 @@ namespace Components
|
|||||||
|
|
||||||
if (refresh)
|
if (refresh)
|
||||||
{
|
{
|
||||||
ServerList::Refresh(UIScript::Token());
|
ServerList::Refresh(UIScript::Token(), info);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ui_browserShowFull = Dvar::Var("ui_browserShowFull").get<bool>();
|
auto ui_browserShowFull = Dvar::Var("ui_browserShowFull").get<bool>();
|
||||||
bool ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get<bool>();
|
auto ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get<bool>();
|
||||||
int ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get<int>();
|
auto ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get<int>();
|
||||||
int ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get<int>();
|
auto ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get<int>();
|
||||||
int ui_browserMod = Dvar::Var("ui_browserMod").get<int>();
|
auto ui_browserMod = Dvar::Var("ui_browserMod").get<int>();
|
||||||
int ui_joinGametype = Dvar::Var("ui_joinGametype").get<int>();
|
auto ui_joinGametype = Dvar::Var("ui_joinGametype").get<int>();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < list->size(); ++i)
|
for (unsigned int i = 0; i < list->size(); ++i)
|
||||||
{
|
{
|
||||||
ServerList::ServerInfo* info = &(*list)[i];
|
auto* serverInfo = &(*list)[i];
|
||||||
|
|
||||||
// Filter full servers
|
// Filter full servers
|
||||||
if (!ui_browserShowFull && info->clients >= info->maxClients) continue;
|
if (!ui_browserShowFull && serverInfo->clients >= serverInfo->maxClients) continue;
|
||||||
|
|
||||||
// Filter empty servers
|
// Filter empty servers
|
||||||
if (!ui_browserShowEmpty && info->clients <= 0) continue;
|
if (!ui_browserShowEmpty && serverInfo->clients <= 0) continue;
|
||||||
|
|
||||||
// Filter hardcore servers
|
// Filter hardcore servers
|
||||||
if ((ui_browserShowHardcore == 0 && info->hardcore) || (ui_browserShowHardcore == 1 && !info->hardcore)) continue;
|
if ((ui_browserShowHardcore == 0 && serverInfo->hardcore) || (ui_browserShowHardcore == 1 && !serverInfo->hardcore)) continue;
|
||||||
|
|
||||||
// Filter servers with password
|
// Filter servers with password
|
||||||
if ((ui_browserShowPassword == 0 && info->password) || (ui_browserShowPassword == 1 && !info->password)) continue;
|
if ((ui_browserShowPassword == 0 && serverInfo->password) || (ui_browserShowPassword == 1 && !serverInfo->password)) continue;
|
||||||
|
|
||||||
// Don't show modded servers
|
// Don't show modded servers
|
||||||
if ((ui_browserMod == 0 && info->mod.size()) || (ui_browserMod == 1 && !info->mod.size())) continue;
|
if ((ui_browserMod == 0 && serverInfo->mod.size()) || (ui_browserMod == 1 && !serverInfo->mod.size())) continue;
|
||||||
|
|
||||||
// Filter by gametype
|
// Filter by gametype
|
||||||
if (ui_joinGametype > 0 && (ui_joinGametype - 1) < *Game::gameTypeCount && Game::gameTypes[(ui_joinGametype - 1)].gameType != info->gametype) continue;
|
if (ui_joinGametype > 0 && (ui_joinGametype - 1) < *Game::gameTypeCount && Game::gameTypes[(ui_joinGametype - 1)].gameType != serverInfo->gametype) continue;
|
||||||
|
|
||||||
ServerList::VisibleList.push_back(i);
|
ServerList::VisibleList.push_back(i);
|
||||||
}
|
}
|
||||||
@ -253,7 +263,7 @@ namespace Components
|
|||||||
ServerList::SortList();
|
ServerList::SortList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::Refresh(UIScript::Token)
|
void ServerList::Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Dvar::Var("ui_serverSelected").set(false);
|
Dvar::Var("ui_serverSelected").set(false);
|
||||||
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
|
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
|
||||||
@ -312,14 +322,12 @@ namespace Components
|
|||||||
|
|
||||||
void ServerList::StoreFavourite(const std::string& server)
|
void ServerList::StoreFavourite(const std::string& server)
|
||||||
{
|
{
|
||||||
//json11::Json::parse()
|
|
||||||
std::vector<std::string> servers;
|
std::vector<std::string> servers;
|
||||||
|
|
||||||
if (Utils::IO::FileExists("players/favourites.json"))
|
const auto parseData = Utils::IO::ReadFile(FavouriteFile);
|
||||||
|
if (!parseData.empty())
|
||||||
{
|
{
|
||||||
std::string data = Utils::IO::ReadFile("players/favourites.json");
|
const nlohmann::json object = nlohmann::json::parse(parseData);
|
||||||
json11::Json object = json11::Json::parse(data, data);
|
|
||||||
|
|
||||||
if (!object.is_array())
|
if (!object.is_array())
|
||||||
{
|
{
|
||||||
Logger::Print("Favourites storage file is invalid!\n");
|
Logger::Print("Favourites storage file is invalid!\n");
|
||||||
@ -327,25 +335,24 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto storedServers = object.array_items();
|
const nlohmann::json::array_t storedServers = object;
|
||||||
|
for (const auto& storedServer : storedServers)
|
||||||
for (unsigned int i = 0; i < storedServers.size(); ++i)
|
|
||||||
{
|
{
|
||||||
if (!storedServers[i].is_string()) continue;
|
if (!storedServer.is_string()) continue;
|
||||||
if (storedServers[i].string_value() == server)
|
if (storedServer.get<std::string>() == server)
|
||||||
{
|
{
|
||||||
Game::ShowMessageBox("Server already marked as favourite.", "Error");
|
Game::ShowMessageBox("Server already marked as favourite.", "Error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
servers.push_back(storedServers[i].string_value());
|
servers.push_back(storedServer.get<std::string>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
servers.push_back(server);
|
servers.push_back(server);
|
||||||
|
|
||||||
json11::Json data = json11::Json(servers);
|
const auto data = nlohmann::json(servers);
|
||||||
Utils::IO::WriteFile("players/favourites.json", data.dump());
|
Utils::IO::WriteFile(FavouriteFile, data.dump());
|
||||||
Game::ShowMessageBox("Server added to favourites.", "Success");
|
Game::ShowMessageBox("Server added to favourites.", "Success");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,10 +360,10 @@ namespace Components
|
|||||||
{
|
{
|
||||||
std::vector<std::string> servers;
|
std::vector<std::string> servers;
|
||||||
|
|
||||||
if (Utils::IO::FileExists("players/favourites.json"))
|
const auto parseData = Utils::IO::ReadFile(FavouriteFile);
|
||||||
|
if (!parseData.empty())
|
||||||
{
|
{
|
||||||
std::string data = Utils::IO::ReadFile("players/favourites.json");
|
const nlohmann::json object = nlohmann::json::parse(parseData);
|
||||||
json11::Json object = json11::Json::parse(data, data);
|
|
||||||
|
|
||||||
if (!object.is_array())
|
if (!object.is_array())
|
||||||
{
|
{
|
||||||
@ -365,50 +372,56 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& storedServer : object.array_items())
|
const nlohmann::json::array_t arr = object;
|
||||||
|
for (auto& storedServer : arr)
|
||||||
{
|
{
|
||||||
if (storedServer.is_string() && storedServer.string_value() != server)
|
if (storedServer.is_string() && storedServer.get<std::string>() != server)
|
||||||
{
|
{
|
||||||
servers.push_back(storedServer.string_value());
|
servers.push_back(storedServer.get<std::string>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json11::Json data = json11::Json(servers);
|
const auto data = nlohmann::json(servers);
|
||||||
Utils::IO::WriteFile("players/favourites.json", data.dump());
|
Utils::IO::WriteFile(FavouriteFile, data.dump());
|
||||||
|
|
||||||
auto list = ServerList::GetList();
|
auto list = ServerList::GetList();
|
||||||
if (list) list->clear();
|
if (list) list->clear();
|
||||||
|
|
||||||
ServerList::RefreshVisibleListInternal(UIScript::Token());
|
ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr);
|
||||||
|
|
||||||
Game::ShowMessageBox("Server removed from favourites.", "Success");
|
Game::ShowMessageBox("Server removed from favourites.", "Success");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::LoadFavourties()
|
void ServerList::LoadFavourties()
|
||||||
{
|
{
|
||||||
if (ServerList::IsFavouriteList() && Utils::IO::FileExists("players/favourites.json"))
|
if (!ServerList::IsFavouriteList())
|
||||||
{
|
{
|
||||||
auto list = ServerList::GetList();
|
return;
|
||||||
if (list) list->clear();
|
}
|
||||||
|
|
||||||
std::string data = Utils::IO::ReadFile("players/favourites.json");
|
auto list = ServerList::GetList();
|
||||||
json11::Json object = json11::Json::parse(data, data);
|
if (list) list->clear();
|
||||||
|
|
||||||
if (!object.is_array())
|
const auto parseData = Utils::IO::ReadFile(FavouriteFile);
|
||||||
{
|
if (parseData.empty())
|
||||||
Logger::Print("Favourites storage file is invalid!\n");
|
{
|
||||||
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto servers = object.array_items();
|
const nlohmann::json object = nlohmann::json::parse(parseData);
|
||||||
|
if (!object.is_array())
|
||||||
|
{
|
||||||
|
Logger::Print("Favourites storage file is invalid!\n");
|
||||||
|
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < servers.size(); ++i)
|
const nlohmann::json::array_t servers = object;
|
||||||
{
|
for (const auto& server : servers)
|
||||||
if (!servers[i].is_string()) continue;
|
{
|
||||||
ServerList::InsertRequest(servers[i].string_value());
|
if (!server.is_string()) continue;
|
||||||
}
|
ServerList::InsertRequest(server.get<std::string>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,6 +495,8 @@ namespace Components
|
|||||||
server.securityLevel = atoi(info.get("securityLevel").data());
|
server.securityLevel = atoi(info.get("securityLevel").data());
|
||||||
server.maxClients = atoi(info.get("sv_maxclients").data());
|
server.maxClients = atoi(info.get("sv_maxclients").data());
|
||||||
server.password = (atoi(info.get("isPrivate").data()) != 0);
|
server.password = (atoi(info.get("isPrivate").data()) != 0);
|
||||||
|
server.aimassist = (atoi(info.get("aimAssist").data()) != 0);
|
||||||
|
server.voice = (atoi(info.get("voiceChat").data()) != 0);
|
||||||
server.hardcore = (atoi(info.get("hc").data()) != 0);
|
server.hardcore = (atoi(info.get("hc").data()) != 0);
|
||||||
server.svRunning = (atoi(info.get("sv_running").data()) != 0);
|
server.svRunning = (atoi(info.get("sv_running").data()) != 0);
|
||||||
server.ping = (Game::Sys_Milliseconds() - i->sendTime);
|
server.ping = (Game::Sys_Milliseconds() - i->sendTime);
|
||||||
@ -538,15 +553,12 @@ namespace Components
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto lList = ServerList::GetList();
|
auto lList = ServerList::GetList();
|
||||||
|
|
||||||
if (lList)
|
if (lList)
|
||||||
{
|
{
|
||||||
lList->push_back(server);
|
lList->push_back(server);
|
||||||
ServerList::RefreshVisibleListInternal(UIScript::Token());
|
ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -705,7 +717,7 @@ namespace Components
|
|||||||
|
|
||||||
netSource.set(source);
|
netSource.set(source);
|
||||||
|
|
||||||
ServerList::RefreshVisibleListInternal(UIScript::Token(), true);
|
ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::UpdateGameType()
|
void ServerList::UpdateGameType()
|
||||||
@ -721,7 +733,7 @@ namespace Components
|
|||||||
|
|
||||||
joinGametype.set(gametype);
|
joinGametype.set(gametype);
|
||||||
|
|
||||||
ServerList::RefreshVisibleListInternal(UIScript::Token());
|
ServerList::RefreshVisibleListInternal(UIScript::Token(), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerList::UpdateVisibleInfo()
|
void ServerList::UpdateVisibleInfo()
|
||||||
@ -796,7 +808,7 @@ namespace Components
|
|||||||
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
|
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
|
||||||
Localization::Set("MPUI_SERVERQUERIED", "Servers: 0\nPlayers: 0 (0)");
|
Localization::Set("MPUI_SERVERQUERIED", "Servers: 0\nPlayers: 0 (0)");
|
||||||
|
|
||||||
Network::OnPacket("getServersResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnClientPacket("getServersResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to
|
if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to
|
||||||
|
|
||||||
@ -841,20 +853,18 @@ namespace Components
|
|||||||
|
|
||||||
UIScript::Add("RefreshServers", ServerList::Refresh);
|
UIScript::Add("RefreshServers", ServerList::Refresh);
|
||||||
|
|
||||||
UIScript::Add("JoinServer", [](UIScript::Token)
|
UIScript::Add("JoinServer", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
ServerList::ServerInfo* info = ServerList::GetServer(ServerList::CurrentServer);
|
auto* serverInfo = ServerList::GetServer(ServerList::CurrentServer);
|
||||||
|
if (serverInfo)
|
||||||
if (info)
|
|
||||||
{
|
{
|
||||||
Party::Connect(info->addr);
|
Party::Connect(serverInfo->addr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("ServerSort", [](UIScript::Token token)
|
UIScript::Add("ServerSort", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
int key = token.get<int>();
|
auto key = token.get<int>();
|
||||||
|
|
||||||
if (ServerList::SortKey == key)
|
if (ServerList::SortKey == key)
|
||||||
{
|
{
|
||||||
ServerList::SortAsc = !ServerList::SortAsc;
|
ServerList::SortAsc = !ServerList::SortAsc;
|
||||||
@ -869,22 +879,21 @@ namespace Components
|
|||||||
ServerList::SortList();
|
ServerList::SortList();
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("CreateListFavorite", [](UIScript::Token)
|
UIScript::Add("CreateListFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
|
auto* serverInfo = ServerList::GetCurrentServer();
|
||||||
|
|
||||||
if (info)
|
if (info)
|
||||||
{
|
{
|
||||||
ServerList::StoreFavourite(info->addr.getString());
|
ServerList::StoreFavourite(serverInfo->addr.getString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("CreateFavorite", [](UIScript::Token)
|
UIScript::Add("CreateFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
ServerList::StoreFavourite(Dvar::Var("ui_favoriteAddress").get<std::string>());
|
ServerList::StoreFavourite(Dvar::Var("ui_favoriteAddress").get<std::string>());
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("CreateCurrentServerFavorite", [](UIScript::Token)
|
UIScript::Add("CreateCurrentServerFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (Game::CL_IsCgameInitialized())
|
if (Game::CL_IsCgameInitialized())
|
||||||
{
|
{
|
||||||
@ -896,14 +905,13 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("DeleteFavorite", [](UIScript::Token)
|
UIScript::Add("DeleteFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
|
auto* serverInfo = ServerList::GetCurrentServer();
|
||||||
|
if (serverInfo)
|
||||||
if (info)
|
|
||||||
{
|
{
|
||||||
ServerList::RemoveFavourite(info->addr.getString());
|
ServerList::RemoveFavourite(serverInfo->addr.getString());
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
|
@ -28,15 +28,17 @@ namespace Components
|
|||||||
int securityLevel;
|
int securityLevel;
|
||||||
bool hardcore;
|
bool hardcore;
|
||||||
bool svRunning;
|
bool svRunning;
|
||||||
|
bool aimassist;
|
||||||
|
bool voice;
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerList();
|
ServerList();
|
||||||
~ServerList();
|
~ServerList();
|
||||||
|
|
||||||
static void Refresh(UIScript::Token);
|
static void Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void RefreshVisibleList(UIScript::Token);
|
static void RefreshVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void RefreshVisibleListInternal(UIScript::Token, bool refresh = false);
|
static void RefreshVisibleListInternal([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info, bool refresh = false);
|
||||||
static void UpdateVisibleList(UIScript::Token);
|
static void UpdateVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void InsertRequest(Network::Address address);
|
static void InsertRequest(Network::Address address);
|
||||||
static void Insert(const Network::Address& address, const Utils::InfoString& info);
|
static void Insert(const Network::Address& address, const Utils::InfoString& info);
|
||||||
|
|
||||||
@ -59,6 +61,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Password,
|
Password,
|
||||||
Matchtype,
|
Matchtype,
|
||||||
|
AimAssist,
|
||||||
|
VoiceChat,
|
||||||
Hostname,
|
Hostname,
|
||||||
Mapname,
|
Mapname,
|
||||||
Players,
|
Players,
|
||||||
@ -67,6 +71,8 @@ namespace Components
|
|||||||
Ping,
|
Ping,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr auto* FavouriteFile = "players/favourites.json";
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
union MasterEntry
|
union MasterEntry
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,7 @@ namespace Components
|
|||||||
void Session::Handle(const std::string& packet, const Network::NetworkCallback& callback)
|
void Session::Handle(const std::string& packet, const Network::NetworkCallback& callback)
|
||||||
{
|
{
|
||||||
#ifdef DISABLE_SESSION
|
#ifdef DISABLE_SESSION
|
||||||
Network::OnPacket(packet, callback);
|
Network::OnClientPacket(packet, callback);
|
||||||
#else
|
#else
|
||||||
std::lock_guard _(Session::Mutex);
|
std::lock_guard _(Session::Mutex);
|
||||||
Session::PacketHandlers[packet] = callback;
|
Session::PacketHandlers[packet] = callback;
|
||||||
|
@ -70,7 +70,7 @@ namespace Components
|
|||||||
// set snapshot num to 1 behind (T6 does this, why shouldn't we?)
|
// set snapshot num to 1 behind (T6 does this, why shouldn't we?)
|
||||||
for (int i = 0; i < *Game::svs_clientCount; ++i)
|
for (int i = 0; i < *Game::svs_clientCount; ++i)
|
||||||
{
|
{
|
||||||
Game::svs_clients[i].snapNum = *Game::svs_time - 1;
|
Game::svs_clients[i].nextSnapshotTime = *Game::svs_time - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define BUTTON_FLAG_LEANLEFT 0x40
|
|
||||||
#define BUTTON_FLAG_LEANRIGHT 0x80
|
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
class SlowMotion : public Component
|
class SlowMotion : public Component
|
||||||
|
@ -14,7 +14,7 @@ namespace Components
|
|||||||
Dvar::Register<const char*>("ui_startupNextButtonText", "", Game::DVAR_EXTERNAL | Game::DVAR_INIT, "");
|
Dvar::Register<const char*>("ui_startupNextButtonText", "", Game::DVAR_EXTERNAL | Game::DVAR_INIT, "");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
UIScript::Add("nextStartupMessage", [](UIScript::Token)
|
UIScript::Add("nextStartupMessage", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (!StartupMessages::MessageList.size()) return;
|
if (!StartupMessages::MessageList.size()) return;
|
||||||
|
|
||||||
|
@ -24,10 +24,8 @@ namespace Components
|
|||||||
Game::Com_Printf(0, "Sending stat packet %i to server.\n", i);
|
Game::Com_Printf(0, "Sending stat packet %i to server.\n", i);
|
||||||
|
|
||||||
// alloc
|
// alloc
|
||||||
Game::msg_t msg;
|
Game::msg_t msg{};
|
||||||
char buffer[2048];
|
unsigned char buffer[2048]{};
|
||||||
ZeroMemory(&msg, sizeof(msg));
|
|
||||||
ZeroMemory(&buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
// init
|
// init
|
||||||
Game::MSG_Init(&msg, buffer, sizeof(buffer));
|
Game::MSG_Init(&msg, buffer, sizeof(buffer));
|
||||||
@ -53,23 +51,23 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send statpacket
|
// send statpacket
|
||||||
Network::SendRaw(Game::NS_CLIENT1, *reinterpret_cast<Game::netadr_t*>(0xA1E888), std::string(msg.data, msg.cursize));
|
Network::SendRaw(Game::NS_CLIENT1, *reinterpret_cast<Game::netadr_t*>(0xA1E888), std::string(reinterpret_cast<char*>(msg.data), msg.cursize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stats::UpdateClasses(UIScript::Token)
|
void Stats::UpdateClasses([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Stats::SendStats();
|
Stats::SendStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Stats::SaveStats(char* dest, const char* folder, const char* buffer, size_t length)
|
int Stats::SaveStats(char* dest, const char* folder, const char* buffer, size_t length)
|
||||||
{
|
{
|
||||||
const auto fs_game = Game::Dvar_FindVar("fs_game");
|
assert(*Game::fs_gameDirVar);
|
||||||
|
|
||||||
if (fs_game && fs_game->current.string && strlen(fs_game->current.string) && !strncmp(fs_game->current.string, "mods/", 5))
|
if (!std::strcmp((*Game::fs_gameDirVar)->current.string, "mods/"))
|
||||||
{
|
{
|
||||||
folder = fs_game->current.string;
|
folder = (*Game::fs_gameDirVar)->current.string;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Utils::Hook::Call<int(char*, const char*, const char*, size_t)>(0x426450)(dest, folder, buffer, length);
|
return Utils::Hook::Call<int(char*, const char*, const char*, size_t)>(0x426450)(dest, folder, buffer, length);
|
||||||
|
@ -10,7 +10,7 @@ namespace Components
|
|||||||
static bool IsMaxLevel();
|
static bool IsMaxLevel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void UpdateClasses(UIScript::Token token);
|
static void UpdateClasses([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void SendStats();
|
static void SendStats();
|
||||||
static int SaveStats(char* dest, const char* folder, const char* buffer, size_t length);
|
static int SaveStats(char* dest, const char* folder, const char* buffer, size_t length);
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ namespace Components
|
|||||||
std::unordered_map<std::string, std::string> otherPatches;
|
std::unordered_map<std::string, std::string> otherPatches;
|
||||||
|
|
||||||
std::string errors;
|
std::string errors;
|
||||||
json11::Json defData = json11::Json::parse(definition.getBuffer(), errors);
|
nlohmann::json defData = nlohmann::json::parse(definition.getBuffer());
|
||||||
|
|
||||||
if (!errors.empty())
|
if (!errors.empty())
|
||||||
{
|
{
|
||||||
@ -228,11 +228,11 @@ namespace Components
|
|||||||
|
|
||||||
if (enumData.is_array())
|
if (enumData.is_array())
|
||||||
{
|
{
|
||||||
for (auto rawEntry : enumData.array_items())
|
for (auto rawEntry : enumData)
|
||||||
{
|
{
|
||||||
if (rawEntry.is_string())
|
if (rawEntry.is_string())
|
||||||
{
|
{
|
||||||
entryData.push_back(rawEntry.string_value());
|
entryData.push_back(rawEntry.get<std::string>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,11 +244,11 @@ namespace Components
|
|||||||
|
|
||||||
if (other.is_object())
|
if (other.is_object())
|
||||||
{
|
{
|
||||||
for (auto& item : other.object_items())
|
for (auto& item : other.items())
|
||||||
{
|
{
|
||||||
if (item.second.is_string())
|
if (item.value().is_string())
|
||||||
{
|
{
|
||||||
otherPatches[item.first] = item.second.string_value();
|
otherPatches[item.key()] = item.value().get<std::string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1287,7 +1287,7 @@ namespace Components
|
|||||||
|
|
||||||
std::string TextRenderer::StripColors(const std::string& in)
|
std::string TextRenderer::StripColors(const std::string& in)
|
||||||
{
|
{
|
||||||
char buffer[1000] = { 0 }; // Should be more than enough
|
char buffer[1024] = {0}; // 1024 is a lucky number in the engine
|
||||||
StripColors(in.data(), buffer, sizeof(buffer));
|
StripColors(in.data(), buffer, sizeof(buffer));
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ namespace Components
|
|||||||
|
|
||||||
void Theatre::WriteBaseline()
|
void Theatre::WriteBaseline()
|
||||||
{
|
{
|
||||||
static char bufData[131072];
|
static unsigned char bufData[131072];
|
||||||
static char cmpData[131072];
|
static char cmpData[131072];
|
||||||
|
|
||||||
Game::msg_t buf;
|
Game::msg_t buf;
|
||||||
@ -56,7 +56,7 @@ namespace Components
|
|||||||
Game::MSG_WriteData(&buf, &Theatre::BaselineSnapshot[Theatre::BaselineSnapshotMsgOff], Theatre::BaselineSnapshotMsgLen - Theatre::BaselineSnapshotMsgOff);
|
Game::MSG_WriteData(&buf, &Theatre::BaselineSnapshot[Theatre::BaselineSnapshotMsgOff], Theatre::BaselineSnapshotMsgLen - Theatre::BaselineSnapshotMsgOff);
|
||||||
Game::MSG_WriteByte(&buf, 6);
|
Game::MSG_WriteByte(&buf, 6);
|
||||||
|
|
||||||
int compressedSize = Game::MSG_WriteBitsCompress(false, buf.data, cmpData, buf.cursize);
|
int compressedSize = Game::MSG_WriteBitsCompress(false, reinterpret_cast<char*>(buf.data), cmpData, buf.cursize);
|
||||||
int fileCompressedSize = compressedSize + 4;
|
int fileCompressedSize = compressedSize + 4;
|
||||||
|
|
||||||
int byte8 = 8;
|
int byte8 = 8;
|
||||||
@ -162,8 +162,8 @@ namespace Components
|
|||||||
Game::Com_Printf(channel, message, file);
|
Game::Com_Printf(channel, message, file);
|
||||||
|
|
||||||
Theatre::CurrentInfo.name = file;
|
Theatre::CurrentInfo.name = file;
|
||||||
Theatre::CurrentInfo.mapname = Dvar::Var("mapname").get<const char*>();
|
Theatre::CurrentInfo.mapname = (*Game::sv_mapname)->current.string;
|
||||||
Theatre::CurrentInfo.gametype = Dvar::Var("g_gametype").get<const char*>();
|
Theatre::CurrentInfo.gametype = (*Game::sv_gametype)->current.string;
|
||||||
Theatre::CurrentInfo.author = Steam::SteamFriends()->GetPersonaName();
|
Theatre::CurrentInfo.author = Steam::SteamFriends()->GetPersonaName();
|
||||||
Theatre::CurrentInfo.length = Game::Sys_Milliseconds();
|
Theatre::CurrentInfo.length = Game::Sys_Milliseconds();
|
||||||
std::time(&Theatre::CurrentInfo.timeStamp);
|
std::time(&Theatre::CurrentInfo.timeStamp);
|
||||||
@ -178,10 +178,10 @@ namespace Components
|
|||||||
|
|
||||||
// Write metadata
|
// Write metadata
|
||||||
FileSystem::FileWriter meta(Utils::String::VA("%s.json", Theatre::CurrentInfo.name.data()));
|
FileSystem::FileWriter meta(Utils::String::VA("%s.json", Theatre::CurrentInfo.name.data()));
|
||||||
meta.write(json11::Json(Theatre::CurrentInfo).dump());
|
meta.write(nlohmann::json(Theatre::CurrentInfo.to_json()).dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Theatre::LoadDemos(UIScript::Token)
|
void Theatre::LoadDemos([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Theatre::CurrentSelection = 0;
|
Theatre::CurrentSelection = 0;
|
||||||
Theatre::Demos.clear();
|
Theatre::Demos.clear();
|
||||||
@ -195,20 +195,20 @@ namespace Components
|
|||||||
if (meta.exists())
|
if (meta.exists())
|
||||||
{
|
{
|
||||||
std::string error;
|
std::string error;
|
||||||
json11::Json metaObject = json11::Json::parse(meta.getBuffer(), error);
|
nlohmann::json metaObject = nlohmann::json::parse(meta.getBuffer());
|
||||||
|
|
||||||
if (metaObject.is_object())
|
if (metaObject.is_object())
|
||||||
{
|
{
|
||||||
Theatre::DemoInfo info;
|
Theatre::DemoInfo demoInfo;
|
||||||
|
demoInfo.name = demo.substr(0, demo.find_last_of("."));
|
||||||
|
demoInfo.author = metaObject["author"].get<std::string>();
|
||||||
|
demoInfo.gametype = metaObject["gametype"].get<std::string>();
|
||||||
|
demoInfo.mapname = metaObject["mapname"].get<std::string>();
|
||||||
|
demoInfo.length = metaObject["length"].get<int>();
|
||||||
|
auto timestamp = metaObject["timestamp"].get<std::string>();
|
||||||
|
demoInfo.timeStamp = _atoi64(timestamp.data());
|
||||||
|
|
||||||
info.name = demo.substr(0, demo.find_last_of("."));
|
Theatre::Demos.push_back(demoInfo);
|
||||||
info.author = metaObject["author"].string_value();
|
|
||||||
info.gametype = metaObject["gametype"].string_value();
|
|
||||||
info.mapname = metaObject["mapname"].string_value();
|
|
||||||
info.length = static_cast<int>(metaObject["length"].number_value());
|
|
||||||
info.timeStamp = _atoi64(metaObject["timestamp"].string_value().data());
|
|
||||||
|
|
||||||
Theatre::Demos.push_back(info);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,16 +217,16 @@ namespace Components
|
|||||||
std::reverse(Theatre::Demos.begin(), Theatre::Demos.end());
|
std::reverse(Theatre::Demos.begin(), Theatre::Demos.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Theatre::DeleteDemo(UIScript::Token)
|
void Theatre::DeleteDemo([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (Theatre::CurrentSelection < Theatre::Demos.size())
|
if (Theatre::CurrentSelection < Theatre::Demos.size())
|
||||||
{
|
{
|
||||||
Theatre::DemoInfo info = Theatre::Demos[Theatre::CurrentSelection];
|
Theatre::DemoInfo demoInfo = Theatre::Demos[Theatre::CurrentSelection];
|
||||||
|
|
||||||
Logger::Print("Deleting demo {}...\n", info.name);
|
Logger::Print("Deleting demo {}...\n", demoInfo.name);
|
||||||
|
|
||||||
FileSystem::DeleteFile("demos", info.name + ".dm_13");
|
FileSystem::_DeleteFile("demos", demoInfo.name + ".dm_13");
|
||||||
FileSystem::DeleteFile("demos", info.name + ".dm_13.json");
|
FileSystem::_DeleteFile("demos", demoInfo.name + ".dm_13.json");
|
||||||
|
|
||||||
// Reset our ui_demo_* dvars here, because the theater menu needs it.
|
// Reset our ui_demo_* dvars here, because the theater menu needs it.
|
||||||
Dvar::Var("ui_demo_mapname").set("");
|
Dvar::Var("ui_demo_mapname").set("");
|
||||||
@ -237,11 +237,11 @@ namespace Components
|
|||||||
Dvar::Var("ui_demo_date").set("");
|
Dvar::Var("ui_demo_date").set("");
|
||||||
|
|
||||||
// Reload demos
|
// Reload demos
|
||||||
Theatre::LoadDemos(UIScript::Token());
|
Theatre::LoadDemos(UIScript::Token(), info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Theatre::PlayDemo(UIScript::Token)
|
void Theatre::PlayDemo([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (Theatre::CurrentSelection < Theatre::Demos.size())
|
if (Theatre::CurrentSelection < Theatre::Demos.size())
|
||||||
{
|
{
|
||||||
@ -309,8 +309,8 @@ namespace Components
|
|||||||
for (int i = 0; i < numDel; ++i)
|
for (int i = 0; i < numDel; ++i)
|
||||||
{
|
{
|
||||||
Logger::Print("Deleting old demo {}\n", files[i]);
|
Logger::Print("Deleting old demo {}\n", files[i]);
|
||||||
FileSystem::DeleteFile("demos", files[i].data());
|
FileSystem::_DeleteFile("demos", files[i].data());
|
||||||
FileSystem::DeleteFile("demos", Utils::String::VA("%s.json", files[i].data()));
|
FileSystem::_DeleteFile("demos", Utils::String::VA("%s.json", files[i].data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::Execute(Utils::String::VA("record auto_%lld", time(nullptr)), true);
|
Command::Execute(Utils::String::VA("record auto_%lld", time(nullptr)), true);
|
||||||
|
@ -20,9 +20,9 @@ namespace Components
|
|||||||
int length;
|
int length;
|
||||||
std::time_t timeStamp;
|
std::time_t timeStamp;
|
||||||
|
|
||||||
json11::Json to_json() const
|
nlohmann::json to_json() const
|
||||||
{
|
{
|
||||||
return json11::Json::object
|
return nlohmann::json
|
||||||
{
|
{
|
||||||
{ "mapname", mapname },
|
{ "mapname", mapname },
|
||||||
{ "gametype", gametype },
|
{ "gametype", gametype },
|
||||||
@ -44,9 +44,9 @@ namespace Components
|
|||||||
static void WriteBaseline();
|
static void WriteBaseline();
|
||||||
static void StoreBaseline(PBYTE snapshotMsg);
|
static void StoreBaseline(PBYTE snapshotMsg);
|
||||||
|
|
||||||
static void LoadDemos(UIScript::Token);
|
static void LoadDemos([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void DeleteDemo(UIScript::Token);
|
static void DeleteDemo([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
static void PlayDemo(UIScript::Token);
|
static void PlayDemo([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
|
||||||
|
|
||||||
static unsigned int GetDemoCount();
|
static unsigned int GetDemoCount();
|
||||||
static const char* GetDemoText(unsigned int item, int column);
|
static const char* GetDemoText(unsigned int item, int column);
|
||||||
|
@ -311,7 +311,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIFeeder::ApplyMap(UIScript::Token)
|
void UIFeeder::ApplyMap([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
const auto mapname = Dvar::Var("ui_map_name").get<std::string>();
|
const auto mapname = Dvar::Var("ui_map_name").get<std::string>();
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ namespace Components
|
|||||||
Utils::Hook::Call<void(const char*)>(0x503B50)(mapname.data()); // Party_SetDisplayMapName
|
Utils::Hook::Call<void(const char*)>(0x503B50)(mapname.data()); // Party_SetDisplayMapName
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIFeeder::ApplyInitialMap(UIScript::Token)
|
void UIFeeder::ApplyInitialMap([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
const auto mapname = Dvar::Var("ui_mapname").get<std::string>();
|
const auto mapname = Dvar::Var("ui_mapname").get<std::string>();
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user