Merge pull request #1045 from XLabsProject/develop

Release r4299
This commit is contained in:
Edo 2023-05-17 13:20:36 +01:00 committed by GitHub
commit a6856b9e90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 1016 additions and 792 deletions

View File

@ -12,7 +12,7 @@ jobs:
name: "Draft a new release"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3.5.2
- name: Normalize version
id: normalize_version

View File

@ -12,7 +12,7 @@ jobs:
steps:
- name: Check out files
if: github.event.pull_request.merged
uses: actions/checkout@v3
uses: actions/checkout@v3.5.2
with:
submodules: false
lfs: false

2
.gitmodules vendored
View File

@ -17,7 +17,7 @@
[submodule "deps/mongoose"]
path = deps/mongoose
url = https://github.com/cesanta/mongoose.git
branch = 7.8
branch = 7.9
[submodule "deps/protobuf"]
path = deps/protobuf
url = https://github.com/google/protobuf.git

View File

@ -4,13 +4,26 @@ 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/).
## r4299 - 2023-05-17
### Added
- Add `dumpZone` ZoneBuilder command (#1033)
- Add `FreezeControlsAllowLook` GSC method (#1026)
### Fixed
- The sound fix was removed as the bug no longer happens (#1007)
- Fix crash when the opening the "server info" menu on an empty server list (#1041)
- Fix patch that was causing the game thread to deadlock (#1038)
## r4251 - 2023-05-03
### Added
- Add GSC client field `ping` as a read-only field (#1002)
- Add GSC client field `address` as a read-only field (#1003)
- Add to the iw4x-rawfiles common_scripts\utility GSC script `getIP` function.
- Add to the iw4x-rawfiles `common_scripts\utility` GSC script `getIP` function.
### Fixed
@ -29,7 +42,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Fixed
- Fix bug with Steam Proxy (#991)
- Fix bug with the `say` GSC notify in regards to hidden commands (#989)
- Fix bug with the `say` GSC notify in regards to hidden chat messages (#989)
### Removed
@ -45,6 +58,11 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Chat system will go back to using `SV_CMD_CAN_IGNORE` commands (#972)
### Security
- Check the address of the sender for the `print` OOB packet (#969)
- Check the address of the sender for the `voice` OOB packet (#973)
### Fixed
- Fix bug with how `sv_mapRotationCurrent` is parsed (#977)
@ -114,7 +132,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Test Clients will no longer receive names from the Xlabs Patreon website. The old behaviour was restored (#852)
- Enabled `OpenFile` GSC function (#862)
- Enabled `CloseFile` GSC function (#862)
- Chat system uses "reliable message" to mitigate message duplication (#873)
- Chat system will now use `SV_CMD_RELIABLE` commands to mitigate message duplication (#873)
- The built-in GSC compiler no longer throws fatal errors when overriding a built-in function or method (IW3 behaviour) (#880)
- `CastFloat` GSC function was renamed to `Float` (#880)
@ -135,10 +153,10 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Clients can unprotect "saved" Dvars using the command line argument `-unprotect-dvars` (#694)
- Clients can protect all Dvars from being modified by the server using the command line argument `-protect-dvars` (#823)
- Add helpful information to script-related errors (#721)
- Add command line `-disable-mongoose` to disable IW4x's built-in webserver (#728)
- Add command line `-disable-rate-limit-check` to disable the rate check on RCon requests (#769)
- Muted clients GUID is saved to the `muted-users.json` file in the `userraw` folder (#732)
- Add helpful information to script-related errors. Must set `developer` Dvar to "2" and `developer_script` Dvar to "1" (#721)
- Add command line `-disable-mongoose` argument to disable IW4x's built-in webserver (#728)
- Add command line `-disable-rate-limit-check` argument to disable the rate check on RCon requests (#769)
- GUID of muted clients is saved to the `muted-users.json` file in the `userraw` folder (#732)
- Add `sv_nextMap` Dvar (#736)
- Add `elifdef` and `elifndef` directives to the menu preprocessor (#747)
- Add `r_drawRunners` Dvar (#758)
@ -167,7 +185,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Fix bug where demo playback would stop when opening a laptop based killstreak (#699)
- Fix bug where mod download speed was inexplicably slow (#707)
- Fix bug where `g_hardcore` could not be set by servers (#708)
- Fix bots "user cmd angles" (#707)
- Fix "user cmd angles" for test clients (#707)
- Fix bug where `FileRead` GSC function could return buffers that are too big (#767)
- Fix bug where the `OnPlayerSay` GSC function would cause issues (#776)
@ -187,7 +205,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Add `IsSprinting` GSC method (#587)
- Add `StorageLoad` GSC function (#595)
- Add `bg_climbAnything` Dvar (#663)
- Add ClanTag support for bots (#645)
- Add "ClanTag" support for bots (#645)
- Add `sv_randomBotNames` Dvar (#665)
- Add support for parsing localized strings files (.str & .json) (#621)
- Add `callvote` menus (#613)
@ -301,7 +319,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Add `DisableWeaponPickup` GSC method (#329)
- Add `EnableWeaponPickup` GSC method (#329)
- Add `protect-saved-dvars` command line argument (#335)
- Add `clanName` dvar. Can be edited in the `barracks` menu (#361)
- Add `clanName` dvar. Can be edited in the "barracks" menu (#361)
- Add DLC9 containing classic maps from CoD4: Backlot, Chinatown, Winter Crash, Pipeline and Downpour.
- Add to the iw4x-rawfiles `common_scripts\iw4x_utility` GSC script, it contains the scripts-based solution for the removed GSC built-in methods.
@ -341,10 +359,10 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Added
- Add SetName GSC method (#288)
- Add ResetName GSC method (#288)
- Add OnPlayerSay GSC function (#265)
- Add Give client command (works with weapons only) (#292)
- Add `SetName` GSC method (#288)
- Add `ResetName` GSC method (#288)
- Add `OnPlayerSay` GSC function (#265)
- Add `Give` client command (works with weapons only) (#292)
- Add `sv_disableChat` Dvar (#290)
- Add `addMap` command (#302)
- Add `addGametype` command (#302)
@ -375,7 +393,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Added
- Add IsArray GSC function (#248)
- Add `IsArray` GSC function (#248)
- Keybind fields in menus work with controller keys (#255)
### Changed
@ -398,14 +416,14 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Added
- Add ToUpper GSC Function (#216)
- Add StrICmp GSC Function (#216)
- Add IsEndStr GSC Function (#216)
- Add DropAllBots GSC Function (#174)
- Add `ToUpper` GSC Function (#216)
- Add `StrICmp` GSC Function (#216)
- Add `IsEndStr` GSC Function (#216)
- Add `DropAllBots` GSC Function (#174)
- Add GSC entity field `entityflags` (#228)
- Add GSC client field `clientflags` (#228)
- Add bg_surfacePenetration Dvar (#241)
- Add bg_bulletRange Dvar (#241)
- Add `bg_surfacePenetration` Dvar (#241)
- Add `bg_bulletRange Dvar` (#241)
### Changed
@ -419,8 +437,8 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Fixed issue with mouse acceleration when polling rate is greater than 125Hz (#230)
- Fixed issue with player speed caused by sprinting from the prone position (#232)
- Fixed client crash when cg_chatHeight was set to 0 (#237)
- Fixed GSC function Scr_TableLookupIStringByRow (#162)
- Fixed client crash when `cg_chatHeight` was set to 0 (#237)
- Fixed GSC function `Scr_TableLookupIStringByRow` (#162)
### Known issues
@ -434,46 +452,46 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Add controller support (#75)
- Add aim assist for controllers (#75)
- Unlock camera_thirdPersonCrosshairOffset Dvar (#68)
- Unlock `camera_thirdPersonCrosshairOffset` Dvar (#68)
- Add support for building custom Fonts with Zonebuilder (#88)
- Add colorblind friendly team colors (#101)
- Add emojis based on titlecards and emblems to use in the chat and server names Example: `:nuke:` (#130)
- Add emojis based on title cards and emblems to use in the chat and server names Example: `:nuke:` (#130)
- Upon leaving a server 'archive' dvars (saved in the config file) will be reset to the value they had prior to joining the server (#134)
- Implement muteClient command for the game chat (#159)
- Implement unmute command for the game chat (#159)
- Add sv_allowAimAssist Dvar (#75)
- Add sv_allowColoredNames (#130)
- Add sv_randomMapRotation Dvar (#146)
- Add rcon_log_requests Dvar (#195)
- Add player_duckedSpeedScale Dvar (#141)
- Add player_proneSpeedScale Dvar (#141)
- Add cg_ufo_scaler Dvar (#158)
- Add cg_noclip_scaler Dvar (#158)
- Add bg_bouncesAllAngles Dvar (#158)
- Add bg_rocketJump Dvar (#158)
- Add bg_elevators Dvar (#156)
- Implement noclip client command (#152)
- Implement ufo client command (#152)
- Implement God client command (#152)
- Implement demigod client command (#152)
- Implement notarget client command (#152)
- Add noclip GSC Function (#152)
- Add ufo GSC Function (#152)
- Add God GSC Function (#152)
- Add demigod GSC Function (#152)
- Add notarget GSC Function (#152)
- Add replaceFunc GSC Function (#144)
- Add `muteClient` command for the game chat (#159)
- Add `unmute` command for the game chat (#159)
- Add `sv_allowAimAssist` Dvar (#75)
- Add `sv_allowColoredNames` (#130)
- Add `sv_randomMapRotation` Dvar (#146)
- Add `rcon_log_requests` Dvar (#195)
- Add `player_duckedSpeedScale` Dvar (#141)
- Add `player_proneSpeedScale` Dvar (#141)
- Add `cg_ufo_scaler` Dvar (#158)
- Add `cg_noclip_scaler` Dvar (#158)
- Add `bg_bouncesAllAngles` Dvar (#158)
- Add `bg_rocketJump` Dvar (#158)
- Add `bg_elevators` Dvar (#156)
- Add `noclip` client command (#152)
- Add `ufo` client command (#152)
- Add `God` client command (#152)
- Add `demigod` client command (#152)
- Add `notarget` client command (#152)
- Add `noclip` GSC Function (#152)
- Add `ufo` GSC Function (#152)
- Add `God` GSC Function (#152)
- Add `demigod` GSC Function (#152)
- Add `notarget` GSC Function (#152)
- Add `replaceFunc` GSC Function (#144)
### Changed
- Renamed sv_enableBounces to bg_bounces (#158)
- Renamed g_playerCollision to bg_playerEjection (#158)
- Renamed g_playerEjection to bg_playerCollision (#158)
- Setviewpos client command works outside private matches (#163)
- Ufo client command works outside of private matches (#152)
- Noclip client command works outside of private matches (#152)
- Renamed `sv_enableBounces` to bg_bounces (#158)
- Renamed `g_playerCollision` to bg_playerEjection (#158)
- Renamed `g_playerEjection` to bg_playerCollision (#158)
- `setviewpos` client command works outside private matches (#163)
- `ufo` client command works outside of private matches (#152)
- `noclip` client command works outside of private matches (#152)
- If a player name is less than 3 characters server will change it to `Unknown Soldier` (#130)
- scr_player_forceautoassign Dvar is false by default
- `scr_player_forceautoassign` Dvar is false by default
### Fixed
@ -481,7 +499,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Fixes and improvements to Zonebuilder
- Fixed issue where the game froze following base game script throwing an error (#74)
- Fixed RCon on party servers (#91 - #95)
- Fixed slow motion during final killcams (#111 - #107)
- Fixed slow motion during final kill cams (#111 - #107)
- 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 with servers displaying an invalid player count (#113)
@ -496,29 +514,29 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Added
- Add host information to /info endpoint (request)
- Add fileWrite GSC Function (#36)
- Add fileRead GSC Function (#36)
- Add fileExists GSC Function (#36)
- Add fileRemove GSC Function (#36)
- Add botMovement GSC Function (#46)
- Add botAction GSC Function (#46)
- Add botWeapon GSC Function (#46)
- Add botStop GSC Function (#46)
- Add isBot GSC Function (#46)
- Add setPing GSC Function (#46)
- Add GetSystemTime and GetSystemTimeMilliseconds GSC Functions (#46)
- Add PrintConsole GSC Function (#46)
- Add Exec GSC Function (#46)
- Add getIP GSC Method (#36)
- Add getPing GSC Method (#36)
- Add scr_intermissionTime GSC Function (#25)
- Add g_playerCollision Dvar (#36)
- Add g_playerEjection Dvar (#36)
- Add r_specularCustomMaps Dvar (#36)
- Unlock safeArea_horizontal and safeArea_vertical Dvars (#42)
- Unlock cg_fovscale Dvar (#47)
- Add g_antilag Dvar (#61)
- Add host information to `/info` endpoint (request)
- Add `fileWrite` GSC Function (#36)
- Add `fileRead` GSC Function (#36)
- Add `fileExists` GSC Function (#36)
- Add `fileRemove` GSC Function (#36)
- Add `botMovement` GSC Function (#46)
- Add `botAction` GSC Function (#46)
- Add `botWeapon` GSC Function (#46)
- Add `botStop` GSC Function (#46)
- Add `isBot` GSC Function (#46)
- Add `setPing` GSC Function (#46)
- Add `GetSystemTime` and `GetSystemTimeMilliseconds` GSC Functions (#46)
- Add `PrintConsole` GSC Function (#46)
- Add `Exec` GSC Function (#46)
- Add `getIP` GSC Method (#36)
- Add `getPing` GSC Method (#36)
- Add `scr_intermissionTime` Dvar (#25)
- Add `g_playerCollision` Dvar (#36)
- Add `g_playerEjection` Dvar (#36)
- Add `r_specularCustomMaps` Dvar (#36)
- Unlock `safeArea_horizontal` and `safeArea_vertical` Dvars (#42)
- Unlock `cg_fovscale` Dvar (#47)
- Add `g_antilag` Dvar (#61)
### Changed
@ -530,7 +548,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Fixed a node system related crash (#45)
- Fixed an issue that made dedicated servers crash when info was requested during map rotation (#43)
- Fixed an issue where the game was trying to decrypt gsc files which caused it to crash when loading mods (#35)
- Fixed an issue where the game was trying to decrypt GSC files which caused it to crash when loading mods (#35)
- Fixed an issue causing the game to crash when Steam was running in the background (#56)
- Fixed slow download speed when using fast download
@ -546,8 +564,8 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Added
- Implement unbanclient command.
- Implement /serverlist api endpoint on dedicated servers
- Add `unbanClient` command.
- Add `/serverlist` api endpoint on dedicated servers
- Add dvar to control the server query rate (net_serverQueryLimit & net_serverFrames)
### Changed
@ -561,7 +579,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
- Fix multiple vulnerability's
- Fix lag spikes on lower end PCs
- Fix invalid name check
- Fix openLink command crash issue
- Fix `openLink` command crash issue
- Fix lobby server map downloading
- Fix steam integration

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit 43d60c5e3891dab6491a76d0bac554a4a89d57f6
Subproject commit afaaa71bcee45d9c90c21f8bd3ba2b12902242e9

@ -1 +1 @@
Subproject commit 266165d21d77dadfe2adb01b7f1bf0fe64832479
Subproject commit acd8015422cab7cbf5b574651d7cb2cf4ecf486e

2
deps/mongoose vendored

@ -1 +1 @@
Subproject commit 0a265e79a67d7bfcdca27f2ccb98ccb474677ec6
Subproject commit 4236405b90e051310aadda818e21c811e404b4d8

2
deps/pdcurses vendored

@ -1 +1 @@
Subproject commit f2d31a2633eb042f7bf1f79cba81522915a04579
Subproject commit 0eb254ae43cdd1f532d515accac59377087883f6

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit 949c771b03de448bdedea80c44a4a5f65284bfeb
Subproject commit 2a1f586ba692ecbbf6d63c8ffbd4d837b1d4a9a4

View File

@ -21,6 +21,7 @@
#include "Modules/Discovery.hpp"
#include "Modules/Download.hpp"
#include "Modules/Elevators.hpp"
#include "Modules/Events.hpp"
#include "Modules/Exception.hpp"
#include "Modules/FastFiles.hpp"
#include "Modules/Friends.hpp"
@ -49,7 +50,6 @@
#include "Modules/ServerList.hpp"
#include "Modules/Session.hpp"
#include "Modules/SlowMotion.hpp"
#include "Modules/SoundMutexFix.hpp"
#include "Modules/StartupMessages.hpp"
#include "Modules/Stats.hpp"
#include "Modules/StringTable.hpp"
@ -163,7 +163,6 @@ namespace Components
Register(new ServerList());
Register(new Session());
Register(new SlowMotion());
Register(new SoundMutexFix());
Register(new StartupMessages());
Register(new Stats());
Register(new StringTable());

View File

@ -73,7 +73,6 @@ namespace Components
#include "Modules/AssetHandler.hpp"
#include "Modules/Dedicated.hpp"
#include "Modules/Events.hpp"
#include "Modules/FileSystem.hpp"
#include "Modules/Localization.hpp"
#include "Modules/Maps.hpp"

View File

@ -47,7 +47,7 @@ namespace Components
std::vector<std::pair<Game::XAssetType, std::string>> AssetHandler::EmptyAssets;
std::map<std::string, Game::XAssetHeader> AssetHandler::TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT];
std::map<std::string, Game::XAssetHeader> AssetHandler::TemporaryAssets[Game::ASSET_TYPE_COUNT];
void AssetHandler::RegisterInterface(IAsset* iAsset)
{
@ -73,7 +73,7 @@ namespace Components
void AssetHandler::ClearTemporaryAssets()
{
for (int i = 0; i < Game::XAssetType::ASSET_TYPE_COUNT; ++i)
for (int i = 0; i < Game::ASSET_TYPE_COUNT; ++i)
{
AssetHandler::TemporaryAssets[i].clear();
}
@ -108,7 +108,7 @@ namespace Components
Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename)
{
Game::XAssetHeader header = { nullptr };
if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header;
if (type >= Game::ASSET_TYPE_COUNT) return header;
auto tempPool = &AssetHandler::TemporaryAssets[type];
auto entry = tempPool->find(filename);
@ -224,7 +224,7 @@ namespace Components
void AssetHandler::ModifyAsset(Game::XAssetType type, Game::XAssetHeader asset, const std::string& name)
{
if (type == Game::XAssetType::ASSET_TYPE_MATERIAL && (name == "gfx_distortion_knife_trail" || name == "gfx_distortion_heat_far" || name == "gfx_distortion_ring_light" || name == "gfx_distortion_heat") && asset.material->info.sortKey >= 43)
if (type == Game::ASSET_TYPE_MATERIAL && (name == "gfx_distortion_knife_trail" || name == "gfx_distortion_heat_far" || name == "gfx_distortion_ring_light" || name == "gfx_distortion_heat") && asset.material->info.sortKey >= 43)
{
if (Zones::Version() >= VERSION_ALPHA2)
{
@ -236,18 +236,18 @@ namespace Components
}
}
if (type == Game::XAssetType::ASSET_TYPE_MATERIAL && (name == "wc/codo_ui_viewer_black_decal3" || name == "wc/codo_ui_viewer_black_decal2" || name == "wc/hint_arrows01" || name == "wc/hint_arrows02"))
if (type == Game::ASSET_TYPE_MATERIAL && (name == "wc/codo_ui_viewer_black_decal3" || name == "wc/codo_ui_viewer_black_decal2" || name == "wc/hint_arrows01" || name == "wc/hint_arrows02"))
{
asset.material->info.sortKey = 0xE;
}
if (type == Game::XAssetType::ASSET_TYPE_VEHICLE && Zones::Version() >= VERSION_ALPHA2)
if (type == Game::ASSET_TYPE_VEHICLE && Zones::Version() >= VERSION_ALPHA2)
{
asset.vehDef->turretWeapon = nullptr;
}
// Fix shader const stuff
if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359 && Zones::Version() < 448)
if (type == Game::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359 && Zones::Version() < 448)
{
for (int i = 0; i < 48; ++i)
{
@ -272,7 +272,7 @@ namespace Components
}
}
if (type == Game::XAssetType::ASSET_TYPE_GFXWORLD && Zones::Version() >= 316)
if (type == Game::ASSET_TYPE_GFXWORLD && Zones::Version() >= 316)
{
asset.gfxWorld->sortKeyEffectDecal = 39;
asset.gfxWorld->sortKeyEffectAuto = 48;
@ -408,7 +408,7 @@ namespace Components
ZoneBuilder::Zone::AssetRecursionMarker _(builder);
Game::XAssetHeader header = { nullptr };
if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header;
if (type >= Game::ASSET_TYPE_COUNT) return header;
auto tempPool = &AssetHandler::TemporaryAssets[type];
auto entry = tempPool->find(filename);
@ -476,7 +476,7 @@ namespace Components
void AssetHandler::MissingAssetError(int severity, const char* format, const char* type, const char* name)
{
if (Dedicated::IsEnabled() && Game::DB_GetXAssetNameType(type) == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET) return;
if (Dedicated::IsEnabled() && Game::DB_GetXAssetNameType(type) == Game::ASSET_TYPE_TECHNIQUE_SET) return;
Utils::Hook::Call<void(int, const char*, const char*, const char*)>(0x4F8C70)(severity, format, type, name); // Print error
}
@ -571,37 +571,37 @@ namespace Components
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*)
{
if (Dvar::Var("r_noVoid").get<bool>() && type == Game::XAssetType::ASSET_TYPE_XMODEL && name == "void")
if (Dvar::Var("r_noVoid").get<bool>() && type == Game::ASSET_TYPE_XMODEL && name == "void")
{
asset.model->numLods = 0;
}
});
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP, 1);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 * 2 : 7168);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, 14000);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PHYSPRESET, 128);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PIXELSHADER, ZoneBuilder::IsEnabled() ? 0x4000 : 10000);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, ZoneBuilder::IsEnabled() ? 0x2000 : 3072);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXDECL, ZoneBuilder::IsEnabled() ? 0x400 : 196);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_WEAPON, WEAPON_LIMIT);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_STRINGTABLE, 800);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMPACT_FX, 8);
Game::ReallocateAssetPool(Game::ASSET_TYPE_GAMEWORLD_SP, 1);
Game::ReallocateAssetPool(Game::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 * 2 : 7168);
Game::ReallocateAssetPool(Game::ASSET_TYPE_LOADED_SOUND, 2700 * 2);
Game::ReallocateAssetPool(Game::ASSET_TYPE_FX, 1200 * 2);
Game::ReallocateAssetPool(Game::ASSET_TYPE_LOCALIZE_ENTRY, 14000);
Game::ReallocateAssetPool(Game::ASSET_TYPE_XANIMPARTS, 8192 * 2);
Game::ReallocateAssetPool(Game::ASSET_TYPE_XMODEL, 5125 * 2);
Game::ReallocateAssetPool(Game::ASSET_TYPE_PHYSPRESET, 128);
Game::ReallocateAssetPool(Game::ASSET_TYPE_PIXELSHADER, ZoneBuilder::IsEnabled() ? 0x4000 : 10000);
Game::ReallocateAssetPool(Game::ASSET_TYPE_VERTEXSHADER, ZoneBuilder::IsEnabled() ? 0x2000 : 3072);
Game::ReallocateAssetPool(Game::ASSET_TYPE_MATERIAL, 8192 * 2);
Game::ReallocateAssetPool(Game::ASSET_TYPE_VERTEXDECL, ZoneBuilder::IsEnabled() ? 0x400 : 196);
Game::ReallocateAssetPool(Game::ASSET_TYPE_WEAPON, WEAPON_LIMIT);
Game::ReallocateAssetPool(Game::ASSET_TYPE_STRINGTABLE, 800);
Game::ReallocateAssetPool(Game::ASSET_TYPE_IMPACT_FX, 8);
// Register asset interfaces
if (ZoneBuilder::IsEnabled())
{
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MAP_ENTS, 10);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, 8192 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, 0x2000);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FONT, 32);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_RAWFILE, 2048);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LEADERBOARD, 500);
Game::ReallocateAssetPool(Game::ASSET_TYPE_MAP_ENTS, 10);
Game::ReallocateAssetPool(Game::ASSET_TYPE_XMODEL_SURFS, 8192 * 2);
Game::ReallocateAssetPool(Game::ASSET_TYPE_TECHNIQUE_SET, 0x2000);
Game::ReallocateAssetPool(Game::ASSET_TYPE_FONT, 32);
Game::ReallocateAssetPool(Game::ASSET_TYPE_RAWFILE, 2048);
Game::ReallocateAssetPool(Game::ASSET_TYPE_LEADERBOARD, 500);
AssetHandler::RegisterInterface(new Assets::IFont_s());
AssetHandler::RegisterInterface(new Assets::IWeapon());

View File

@ -5,7 +5,7 @@ namespace Assets
class IXModel : public Components::AssetHandler::IAsset
{
public:
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_XMODEL; }
Game::XAssetType getType() override { return Game::ASSET_TYPE_XMODEL; }
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;

View File

@ -5,7 +5,7 @@ namespace Assets
class IXModelSurfs : public Components::AssetHandler::IAsset
{
public:
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_XMODEL_SURFS; }
Game::XAssetType getType() override { return Game::ASSET_TYPE_XMODEL_SURFS; }
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;

View File

@ -40,6 +40,24 @@ namespace Assets
}
}
void Isnd_alias_list_t::dump(Game::XAssetHeader header)
{
Components::ZoneBuilder::GetExporter()->write(Game::XAssetType::ASSET_TYPE_SOUND, header.data);
}
Isnd_alias_list_t::Isnd_alias_list_t()
{
Components::Command::Add("dumpSound", [this](const Components::Command::Params* param)
{
const auto header = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_SOUND, param->get(1));
if (header.data)
{
Components::ZoneBuilder::RefreshExporterWorkDirectory();
this->dump(header);
}
});
}
void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
AssertSize(Game::snd_alias_list_t, 12);

View File

@ -10,5 +10,8 @@ namespace Assets
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void dump(Game::XAssetHeader header) override;
Isnd_alias_list_t();
};
}

View File

@ -511,7 +511,7 @@ namespace Components
if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
{
Command::Add("securityLevel", [](Command::Params* params)
Command::Add("securityLevel", [](const Command::Params* params)
{
if (params->size() < 2)
{
@ -547,7 +547,6 @@ namespace Components
TokenContainer.cancel = true;
TokenContainer.generating = false;
// Terminate thread
if (TokenContainer.thread.joinable())
{
TokenContainer.thread.join();

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp>
#include "Bans.hpp"
#include "Events.hpp"
namespace Components
{
@ -232,9 +233,9 @@ namespace Components
SaveBans(&list);
}
Bans::Bans()
void Bans::AddServerCommands()
{
Command::Add("banClient", [](Command::Params* params)
Command::AddSV("banClient", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -278,11 +279,11 @@ namespace Components
return;
}
const std::string reason = params->size() < 3 ? "EXE_ERR_BANNED_PERM" : params->join(2);
const auto reason = params->size() < 3 ? "EXE_ERR_BANNED_PERM"s : params->join(2);
BanClient(cl, reason);
});
Command::Add("unbanClient", [](Command::Params* params)
Command::AddSV("unbanClient", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -317,4 +318,9 @@ namespace Components
}
});
}
Bans::Bans()
{
Events::OnSVInit(AddServerCommands);
}
}

View File

@ -29,5 +29,7 @@ namespace Components
static void LoadBans(BanList* list);
static void SaveBans(const BanList* list);
static void AddServerCommands();
};
}

View File

@ -2,6 +2,7 @@
#include "Bots.hpp"
#include "ClanTags.hpp"
#include "Events.hpp"
#include "GSC/Script.hpp"
@ -407,37 +408,9 @@ namespace Components
}
}
Bots::Bots()
void Bots::AddServerCommands()
{
AssertOffset(Game::client_s, bIsTestClient, 0x41AF0);
AssertOffset(Game::client_s, ping, 0x212C8);
AssertOffset(Game::client_s, gentity, 0x212A0);
// 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\\clanAbbrev\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
// Intercept sprintf for the connect string
Utils::Hook(0x48ADAB, BuildConnectString, HOOK_CALL).install()->quick();
Utils::Hook(0x627021, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x627241, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
Utils::Hook(0x459654, SV_GetClientPing_Hk, HOOK_CALL).install()->quick();
sv_randomBotNames = Game::Dvar_RegisterBool("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names");
sv_replaceBots = Game::Dvar_RegisterBool("sv_replaceBots", false, Game::DVAR_NONE, "Test clients will be replaced by connecting players when the server is full.");
// Reset BotMovementInfo.active when client is dropped
Events::OnClientDisconnect([](const int clientNum) -> void
{
g_botai[clientNum].active = false;
});
CleanBotArray();
Command::Add("spawnBot", [](Command::Params* params)
Command::AddSV("spawnBot", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -467,8 +440,7 @@ namespace Components
if (input == end)
{
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "{} is not a valid input\nUsage: {} optional <number of bots> or optional <\"all\">\n",
input, params->get(0));
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "{} is not a valid input\nUsage: {} optional <number of bots> or optional <\"all\">\n", input, params->get(0));
return;
}
}
@ -480,6 +452,39 @@ namespace Components
Spawn(count);
});
}
Bots::Bots()
{
AssertOffset(Game::client_s, bIsTestClient, 0x41AF0);
AssertOffset(Game::client_s, ping, 0x212C8);
AssertOffset(Game::client_s, gentity, 0x212A0);
// 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\\clanAbbrev\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
// Intercept sprintf for the connect string
Utils::Hook(0x48ADAB, BuildConnectString, HOOK_CALL).install()->quick();
Utils::Hook(0x627021, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x627241, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
Utils::Hook(0x459654, SV_GetClientPing_Hk, HOOK_CALL).install()->quick();
sv_randomBotNames = Game::Dvar_RegisterBool("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names");
sv_replaceBots = Game::Dvar_RegisterBool("sv_replaceBots", false, Game::DVAR_NONE, "Test clients will be replaced by connecting players when the server is full.");
// Reset BotMovementInfo.active when client is dropped
Events::OnClientDisconnect([](const int clientNum) -> void
{
g_botai[clientNum].active = false;
});
Events::OnSVInit(AddServerCommands);
CleanBotArray();
AddScriptMethods();

View File

@ -36,5 +36,7 @@ namespace Components
static bool IsFull();
static void CleanBotArray();
static void AddServerCommands();
};
}

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp>
#include "CardTitles.hpp"
#include "Events.hpp"
#include "ServerCommands.hpp"
namespace Components
@ -202,7 +203,7 @@ namespace Components
std::memset(&CustomTitles, 0, sizeof(char[Game::MAX_CLIENTS][18]));
ServerCommands::OnCommand(21, [](Command::Params* params)
ServerCommands::OnCommand(21, [](const Command::Params* params)
{
if (std::strcmp(params->get(1), "customTitles") == 0)
{
@ -214,7 +215,6 @@ namespace Components
}
return false;
});
Utils::Hook(0x62EB26, GetPlayerCardClientInfoStub).install()->quick();

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp>
#include "Chat.hpp"
#include "Events.hpp"
#include "PlayerName.hpp"
#include "TextRenderer.hpp"
#include "Voice.hpp"
@ -380,9 +381,9 @@ namespace Components
});
}
void Chat::AddChatCommands()
void Chat::AddServerCommands()
{
Command::AddSV("muteClient", [](Command::Params* params)
Command::AddSV("muteClient", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -405,7 +406,7 @@ namespace Components
}
});
Command::AddSV("unmute", [](Command::Params* params)
Command::AddSV("unmute", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -446,7 +447,7 @@ namespace Components
}
});
Command::AddSV("say", [](Command::Params* params)
Command::AddSV("say", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -471,7 +472,7 @@ namespace Components
}
});
Command::AddSV("tell", [](Command::Params* params)
Command::AddSV("tell", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -499,7 +500,7 @@ namespace Components
}
});
Command::AddSV("sayraw", [](Command::Params* params)
Command::AddSV("sayraw", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -514,7 +515,7 @@ namespace Components
Logger::Print("Raw: {}\n", message);
});
Command::AddSV("tellraw", [](Command::Params* params)
Command::AddSV("tellraw", [](const Command::Params* params)
{
if (!Dedicated::IsRunning())
{
@ -610,7 +611,7 @@ namespace Components
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");
Events::OnSVInit(AddChatCommands);
Events::OnSVInit(AddServerCommands);
LoadMutedList();
@ -622,6 +623,12 @@ namespace Components
// Change logic that does word splitting with new lines for chat messages to support fonticons
Utils::Hook(0x592E10, CG_AddToTeamChat_Stub, HOOK_JUMP).install()->quick();
// Add back removed command from CoD4
Command::Add("mp_QuickMessage", []() -> void
{
Command::Execute("openmenu quickmessage");
});
AddScriptFunctions();
// Avoid duplicates

View File

@ -42,7 +42,7 @@ namespace Components
static void SaveMutedList(const muteList& list);
static void LoadMutedList();
static void AddChatCommands();
static void AddServerCommands();
static int GetCallbackReturn();
static int ChatCallback(Game::gentity_s* self, const char* codePos, const char* message, int mode);

View File

@ -1,5 +1,7 @@
#include <STDInclude.hpp>
#include "ClanTags.hpp"
#include "Events.hpp"
#include "PlayerName.hpp"
#include "ServerCommands.hpp"
@ -240,7 +242,7 @@ namespace Components
std::memset(&ClientState, 0, sizeof(char[Game::MAX_CLIENTS][MAX_CLAN_NAME_LENGTH]));
ServerCommands::OnCommand(22, [](Command::Params* params)
ServerCommands::OnCommand(22, [](const Command::Params* params)
{
if (std::strcmp(params->get(1), "clanNames") == 0)
{

View File

@ -488,12 +488,12 @@ namespace Components
if (!CheatsOk(ent))
return;
ent->client->flags ^= Game::PF_NOCLIP;
ent->client->flags ^= Game::CF_BIT_NOCLIP;
const auto entNum = ent->s.number;
Logger::Debug("Noclip toggled for entity {}", entNum);
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PF_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::CF_BIT_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
}
void ClientCommand::Cmd_UFO_f(Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
@ -501,12 +501,12 @@ namespace Components
if (!CheatsOk(ent))
return;
ent->client->flags ^= Game::PF_UFO;
ent->client->flags ^= Game::CF_BIT_UFO;
const auto entNum = ent->s.number;
Logger::Debug("UFO toggled for entity {}", entNum);
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PF_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::CF_BIT_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
}
ClientCommand::ClientCommand()

View File

@ -2,8 +2,8 @@
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::FunctionMapSV;
std::unordered_map<std::string, Command::commandCallback> Command::FunctionMap;
std::unordered_map<std::string, Command::commandCallback> Command::FunctionMapSV;
std::string Command::Params::join(const int index) const
{
@ -68,7 +68,7 @@ namespace Components
});
}
void Command::Add(const char* name, const std::function<void(Params*)>& callback)
void Command::Add(const char* name, const commandCallback& callback)
{
const auto command = Utils::String::ToLower(name);
@ -80,7 +80,7 @@ namespace Components
FunctionMap.insert_or_assign(command, callback);
}
void Command::AddSV(const char* name, const std::function<void(Params*)>& callback)
void Command::AddSV(const char* name, const commandCallback& callback)
{
if (Loader::IsPregame())
{

View File

@ -54,19 +54,21 @@ namespace Components
Command();
static Game::cmd_function_s* Allocate();
using commandCallback = std::function<void(const Params*)>;
static void Add(const char* name, const std::function<void()>& callback);
static void Add(const char* name, const std::function<void(Params*)>& callback);
static void Add(const char* name, const commandCallback& callback);
static void AddRaw(const char* name, void(*callback)(), bool key = false);
static void AddSV(const char* name, const std::function<void(Params*)>& callback);
static void AddSV(const char* name, const commandCallback& callback);
static void Execute(std::string command, bool sync = true);
static Game::cmd_function_s* Find(const std::string& command);
private:
static std::unordered_map<std::string, std::function<void(Params*)>> FunctionMap;
static std::unordered_map<std::string, std::function<void(Params*)>> FunctionMapSV;
static std::unordered_map<std::string, commandCallback> FunctionMap;
static std::unordered_map<std::string, commandCallback> FunctionMapSV;
static Game::cmd_function_s* Allocate();
static void AddRawSV(const char* name, void(*callback)());

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp>
#include "Debug.hpp"
#include "Events.hpp"
#include "TextRenderer.hpp"
#include "Game/Engine/ScopedCriticalSection.hpp"
@ -272,7 +273,7 @@ namespace Components
assert(0 && "a");
}
void Debug::Com_Bug_f(Command::Params* params)
void Debug::Com_Bug_f(const Command::Params* params)
{
char newFileName[MAX_PATH]{};
char to_ospath[MAX_OSPATH]{};

View File

@ -40,7 +40,7 @@ namespace Components
static void CG_DrawDebugOverlays_Hk(int localClientNum);
static void Com_Assert_f();
static void Com_Bug_f(Command::Params* params);
static void Com_Bug_f(const Command::Params* params);
static void Com_BugNameInc_f();
static void CL_InitDebugDvars();

View File

@ -3,6 +3,7 @@
#include "CardTitles.hpp"
#include "ClanTags.hpp"
#include "Events.hpp"
#include "Party.hpp"
#include "ServerCommands.hpp"
@ -267,7 +268,6 @@ namespace Components
Game::DvarValue value;
value.integer = 0;
Game::Dvar_SetVariant(const_cast<Game::dvar_t*>(com_dedicated), value, Game::DVAR_SOURCE_INTERNAL);
}
});
@ -291,14 +291,10 @@ namespace Components
}
else
{
for (int i = 0; i < ARRAYSIZE(PlayerGuids); ++i)
{
PlayerGuids[i][0].bits = 0;
PlayerGuids[i][1].bits = 0;
}
ZeroMemory(PlayerGuids, sizeof(PlayerGuids));
// Intercept server commands
ServerCommands::OnCommand(20, [](Command::Params* params)
ServerCommands::OnCommand(20, [](const Command::Params* params)
{
for (int client = 0; client < 18; client++)
{

View File

@ -3,6 +3,7 @@
#include <Utils/WebIO.hpp>
#include "Download.hpp"
#include "Events.hpp"
#include "MapRotation.hpp"
#include "Party.hpp"
#include "ServerInfo.hpp"
@ -697,7 +698,7 @@ namespace Components
mg_mgr_init(&Mgr);
Network::OnStart([]
Events::OnNetworkInit([]() -> void
{
const auto* nc = mg_http_listen(&Mgr, Utils::String::VA(":%hu", Network::GetPort()), &EventHandler, &Mgr);
if (!nc)
@ -709,7 +710,7 @@ namespace Components
ServerRunning = true;
Terminate = false;
ServerThread = Utils::Thread::CreateNamedThread("Mongoose", []
ServerThread = Utils::Thread::CreateNamedThread("Mongoose", []() -> void
{
Com_InitThreadData();
@ -722,7 +723,7 @@ namespace Components
}
else
{
Events::OnDvarInit([]
Events::OnDvarInit([]() -> void
{
UIDlTimeLeft = Dvar::Register<const char*>("ui_dl_timeLeft", "", Game::DVAR_NONE, "");
UIDlProgress = Dvar::Register<const char*>("ui_dl_progress", "", Game::DVAR_NONE, "");

View File

@ -459,6 +459,21 @@ namespace Components
// un-cheat jump_slowdownEnable
Utils::Hook::Xor<std::uint32_t>(0x4EFABE, Game::DVAR_CHEAT);
// un-cheat jump_height
Utils::Hook::Xor<std::uint32_t>(0x4EFA5C, Game::DVAR_CHEAT);
// un-cheat player_breath_fire_delay
Utils::Hook::Xor<std::uint32_t>(0x448646, Game::DVAR_CHEAT);
// un-cheat player_breath_gasp_scale
Utils::Hook::Xor<std::uint32_t>(0x448678, Game::DVAR_CHEAT);
// un-cheat player_breath_gasp_lerp
Utils::Hook::Xor<std::uint32_t>(0x4486E4, Game::DVAR_CHEAT);
// un-cheat player_breath_gasp_time
Utils::Hook::Xor<std::uint32_t>(0x448612, Game::DVAR_CHEAT);
// Hook dvar 'name' registration
Utils::Hook(0x40531C, Dvar_RegisterName, HOOK_CALL).install()->quick();

View File

@ -1,5 +1,7 @@
#include <STDInclude.hpp>
#include "Elevators.hpp"
#include "Events.hpp"
namespace Components
{

View File

@ -1,48 +1,79 @@
#include <STDInclude.hpp>
#include "Events.hpp"
namespace Components
{
Utils::Signal<Events::ClientCallback> Events::ClientDisconnectSignal;
Utils::Signal<Events::ClientConnectCallback> Events::ClientConnectSignal;
Utils::Signal<Events::Callback> Events::SteamDisconnectSignal;
Utils::Signal<Events::Callback> Events::ShutdownSystemSignal;
Utils::Signal<Events::Callback> Events::ClientInitSignal;
Utils::Signal<Events::Callback> Events::ServerInitSignal;
Utils::Signal<Events::Callback> Events::DvarInitSignal;
Utils::Concurrency::Container<Events::ClientCallback> Events::ClientDisconnectTasks_;
Utils::Concurrency::Container<Events::ClientConnectCallback> Events::ClientConnectTasks_;
Utils::Concurrency::Container<Events::Callback> Events::SteamDisconnectTasks_;
Utils::Concurrency::Container<Events::Callback> Events::ShutdownSystemTasks_;
Utils::Concurrency::Container<Events::Callback> Events::ClientInitTasks_;
Utils::Concurrency::Container<Events::Callback> Events::ServerInitTasks_;
Utils::Concurrency::Container<Events::Callback> Events::DvarInitTasks_;
Utils::Concurrency::Container<Events::Callback> Events::NetworkInitTasks_;
void Events::OnClientDisconnect(const Utils::Slot<ClientCallback>& callback)
void Events::OnClientDisconnect(const std::function<void(int clientNum)>& callback)
{
ClientDisconnectSignal.connect(callback);
ClientDisconnectTasks_.access([&callback](ClientCallback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::OnClientConnect(const Utils::Slot<ClientConnectCallback>& callback)
void Events::OnClientConnect(const std::function<void(Game::client_s* cl)>& callback)
{
ClientConnectSignal.connect(callback);
ClientConnectTasks_.access([&callback](ClientConnectCallback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::OnSteamDisconnect(const Utils::Slot<Callback>& callback)
void Events::OnSteamDisconnect(const std::function<void()>& callback)
{
SteamDisconnectSignal.connect(callback);
SteamDisconnectTasks_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::OnVMShutdown(const Utils::Slot<Callback>& callback)
void Events::OnVMShutdown(const std::function<void()>& callback)
{
ShutdownSystemSignal.connect(callback);
ShutdownSystemTasks_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::OnClientInit(const Utils::Slot<Callback>& callback)
void Events::OnClientInit(const std::function<void()>& callback)
{
ClientInitSignal.connect(callback);
ClientInitTasks_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::OnSVInit(const Utils::Slot<Callback>& callback)
void Events::OnSVInit(const std::function<void()>& callback)
{
ServerInitSignal.connect(callback);
ServerInitTasks_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::OnDvarInit(const Utils::Slot<Callback>& callback)
void Events::OnDvarInit(const std::function<void()>& callback)
{
DvarInitSignal.connect(callback);
DvarInitTasks_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::OnNetworkInit(const std::function<void()>& callback)
{
NetworkInitTasks_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
/*
@ -51,56 +82,124 @@ namespace Components
*/
void Events::ClientDisconnect_Hk(const int clientNum)
{
ClientDisconnectSignal(clientNum);
ClientDisconnectTasks_.access([&clientNum](ClientCallback& tasks)
{
for (const auto& func : tasks)
{
func(clientNum);
}
});
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum); // ClientDisconnect
}
void Events::SV_UserinfoChanged_Hk(Game::client_s* cl)
{
ClientConnectSignal(cl);
ClientConnectTasks_.access([&cl](ClientConnectCallback& tasks)
{
for (const auto& func : tasks)
{
func(cl);
}
});
Utils::Hook::Call<void(Game::client_s*)>(0x401950)(cl); // SV_UserinfoChanged
}
void Events::SteamDisconnect_Hk()
{
SteamDisconnectSignal();
SteamDisconnectTasks_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
});
Utils::Hook::Call<void()>(0x467CC0)(); // LiveSteam_Client_SteamDisconnect
}
void Events::Scr_ShutdownSystem_Hk(unsigned char sys)
{
ShutdownSystemSignal();
ShutdownSystemTasks_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
});
Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys); // Scr_ShutdownSystem
}
void Events::CL_InitOnceForAllClients_HK()
{
ClientInitSignal();
ClientInitSignal.clear();
ClientInitTasks_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
tasks = {}; // Only called once. Clear
});
Utils::Hook::Call<void()>(0x404CA0)(); // CL_InitOnceForAllClients
}
void Events::SV_Init_Hk()
{
ServerInitSignal();
ServerInitSignal.clear();
ServerInitTasks_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
tasks = {}; // Only called once. Clear
});
Utils::Hook::Call<void()>(0x474320)(); // SV_InitGameMode
}
void Events::Com_InitDvars_Hk()
{
DvarInitSignal();
DvarInitSignal.clear();
DvarInitTasks_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
tasks = {}; // Only called once. Clear
});
Utils::Hook::Call<void()>(0x60AD10)(); // Com_InitDvars
}
void Events::NetworkStart()
{
NetworkInitTasks_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
tasks = {}; // Only called once. Clear
});
}
__declspec(naked) void Events::NET_OpenSocks_Hk()
{
__asm
{
mov eax, 64D900h
call eax
jmp NetworkStart
}
}
Events::Events()
{
Utils::Hook(0x625235, ClientDisconnect_Hk, HOOK_CALL).install()->quick(); // SV_FreeClient
@ -117,5 +216,7 @@ namespace Components
Utils::Hook(0x60BB3A, Com_InitDvars_Hk, HOOK_CALL).install()->quick(); // Com_Init_Try_Block_Function
Utils::Hook(0x4D3665, SV_Init_Hk, HOOK_CALL).install()->quick(); // SV_Init
Utils::Hook(0x4FD4D4, NET_OpenSocks_Hk, HOOK_JUMP).install()->quick(); // NET_OpenIP
}
}

View File

@ -5,39 +5,42 @@ namespace Components
class Events : public Component
{
public:
typedef void(ClientCallback)(int clientNum);
typedef void(ClientConnectCallback)(Game::client_s* cl);
typedef void(Callback)();
using Callback = std::vector<std::function<void()>>;
using ClientConnectCallback = std::vector<std::function<void(Game::client_s* cl)>>;
using ClientCallback = std::vector<std::function<void(int clientNum)>>;
Events();
// Server side
static void OnClientDisconnect(const Utils::Slot<ClientCallback>& callback);
static void OnClientDisconnect(const std::function<void(int clientNum)>& callback);
// Server side
static void OnClientConnect(const Utils::Slot<ClientConnectCallback>& callback);
static void OnClientConnect(const std::function<void(Game::client_s* cl)>& callback);
// Client side
static void OnSteamDisconnect(const Utils::Slot<Callback>& callback);
static void OnSteamDisconnect(const std::function<void()>& callback);
static void OnVMShutdown(const Utils::Slot<Callback>& callback);
static void OnVMShutdown(const std::function<void()>& callback);
static void OnClientInit(const Utils::Slot<Callback>& callback);
static void OnClientInit(const std::function<void()>& callback);
// Client & Server (triggered once)
static void OnSVInit(const Utils::Slot<Callback>& callback);
static void OnSVInit(const std::function<void()>& callback);
// Client & Server (triggered once)
static void OnDvarInit(const Utils::Slot<Callback>& callback);
static void OnDvarInit(const std::function<void()>& callback);
static void OnNetworkInit(const std::function<void()>& callback);
private:
static Utils::Signal<ClientCallback> ClientDisconnectSignal;
static Utils::Signal<ClientConnectCallback> ClientConnectSignal;
static Utils::Signal<Callback> SteamDisconnectSignal;
static Utils::Signal<Callback> ShutdownSystemSignal;
static Utils::Signal<Callback> ClientInitSignal;
static Utils::Signal<Callback> ServerInitSignal;
static Utils::Signal<Callback> DvarInitSignal;
static Utils::Concurrency::Container<ClientCallback> ClientDisconnectTasks_;
static Utils::Concurrency::Container<ClientConnectCallback> ClientConnectTasks_;
static Utils::Concurrency::Container<Callback> SteamDisconnectTasks_;
static Utils::Concurrency::Container<Callback> ShutdownSystemTasks_;
static Utils::Concurrency::Container<Callback> ClientInitTasks_;
static Utils::Concurrency::Container<Callback> ServerInitTasks_;
static Utils::Concurrency::Container<Callback> DvarInitTasks_;
static Utils::Concurrency::Container<Callback> NetworkInitTasks_;
static void ClientDisconnect_Hk(int clientNum);
static void SV_UserinfoChanged_Hk(Game::client_s* cl);
@ -46,5 +49,8 @@ namespace Components
static void CL_InitOnceForAllClients_HK();
static void SV_Init_Hk();
static void Com_InitDvars_Hk();
static void NetworkStart();
static void NET_OpenSocks_Hk();
};
}

View File

@ -609,7 +609,7 @@ namespace Components
}, Scheduler::Pipeline::RENDERER);
}
Command::Add("loadzone", [](Command::Params* params)
Command::Add("loadzone", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -621,7 +621,7 @@ namespace Components
Game::DB_LoadXAssets(&info, 1, true);
});
Command::Add("awaitDatabase", [](Command::Params*)
Command::Add("awaitDatabase", []()
{
Logger::Print("Waiting for database...\n");
while (!Game::Sys_IsDatabaseReady()) std::this_thread::sleep_for(100ms);

View File

@ -5,6 +5,7 @@
#include <proto/friends.pb.h>
#pragma warning(pop)
#include "Events.hpp"
#include "Friends.hpp"
#include "Materials.hpp"
#include "Node.hpp"
@ -461,21 +462,6 @@ namespace Components
Friends::CurrentFriend = index;
}
void Friends::AddFriend(SteamID user)
{
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamFriends)
{
if (Steam::Proxy::ClientFriends.invoke<bool>("AddFriend", user))
{
Toast::Show("cardicon_joystick", Steam::Proxy::SteamFriends->GetFriendPersonaName(user), "friend request sent", 3000);
}
else
{
Toast::Show("cardicon_stop", Steam::Proxy::SteamFriends->GetFriendPersonaName(user), "unable to send friend request", 3000);
}
}
}
int Friends::GetGame(SteamID user)
{
int appId = 0;
@ -574,20 +560,6 @@ namespace Components
Friends::CLAnonymous = Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_ARCHIVE, "Enable invisible mode for Steam");
Friends::CLNotifyFriendState = Dvar::Register<bool>("cl_notifyFriendState", true, Game::DVAR_ARCHIVE, "Update friends about current game status");
Command::Add("addFriend", [](Command::Params* params)
{
if (params->size() < 2)
{
Logger::Print("Usage: {} <Steam ID in hexadecimal format>\n", params->get(0));
return;
}
SteamID id;
id.bits = std::strtoull(params->get(1), nullptr, 16);
Friends::AddFriend(id);
});
// Hook Live_ShowFriendsList
Utils::Hook(0x4D6C70, []()
{

View File

@ -19,8 +19,6 @@ namespace Components
static void RequestPresence(SteamID user);
static std::string GetPresence(SteamID user, const std::string& key);
static void AddFriend(SteamID user);
static int GetGame(SteamID user);
static bool IsInvisible();

View File

@ -1,4 +1,7 @@
#include <STDInclude.hpp>
#include <Components/Modules/Events.hpp>
#include "IO.hpp"
#include "Script.hpp"

View File

@ -166,12 +166,12 @@ namespace Components::GSC
if (pName != nullptr)
{
const auto name = Utils::String::ToLower(*pName);
for (const auto& func : CustomScrFunctions)
for (const auto& funcs : CustomScrFunctions)
{
if (Utils::Contains(&func.aliases, name))
if (std::ranges::find(funcs.aliases, name) != funcs.aliases.end())
{
*type = func.type;
return func.actionFunc;
*type = funcs.type;
return funcs.actionFunc;
}
}
}
@ -193,12 +193,12 @@ namespace Components::GSC
if (pName != nullptr)
{
const auto name = Utils::String::ToLower(*pName);
for (const auto& meth : CustomScrMethods)
for (const auto& meths : CustomScrMethods)
{
if (Utils::Contains(&meth.aliases, name))
if (std::ranges::find(meths.aliases, name) != meths.aliases.end())
{
*type = meth.type;
return meth.actionFunc;
*type = meths.type;
return meths.actionFunc;
}
}
}
@ -262,7 +262,7 @@ namespace Components::GSC
auto* ent = &Game::g_entities[entref.entnum];
if (!ent->client)
{
Game::Scr_ObjectError(Utils::String::VA("entity %i is not a player", entref.entnum));
Game::Scr_ObjectError(Utils::String::VA("entity %hu is not a player", entref.entnum));
return nullptr;
}

View File

@ -9,10 +9,10 @@ namespace Components::GSC
{
using namespace Utils::String;
int ScriptError::developer_;
int ScriptError::Developer_;
Game::scrParserGlob_t ScriptError::scrParserGlob;
Game::scrParserPub_t ScriptError::scrParserPub;
Game::scrParserGlob_t ScriptError::ScrParserGlob;
Game::scrParserPub_t ScriptError::ScrParserPub;
int ScriptError::Scr_IsInOpcodeMemory(const char* pos)
{
@ -27,7 +27,7 @@ namespace Components::GSC
Game::OpcodeLookup* opcodeLookup;
Game::SourceLookup* sourcePosLookup;
if (!developer_)
if (!Developer_)
{
return;
}
@ -48,83 +48,83 @@ namespace Components::GSC
type &= ~Game::SOURCE_TYPE_BREAKPOINT;
}
assert(scrParserGlob.opcodeLookup);
assert(scrParserGlob.sourcePosLookup);
assert(ScrParserGlob.opcodeLookup);
assert(ScrParserGlob.sourcePosLookup);
assert(Game::scrCompilePub->opcodePos);
auto size = sizeof(Game::OpcodeLookup) * (scrParserGlob.opcodeLookupLen + 1);
if (size > scrParserGlob.opcodeLookupMaxSize)
auto size = sizeof(Game::OpcodeLookup) * (ScrParserGlob.opcodeLookupLen + 1);
if (size > ScrParserGlob.opcodeLookupMaxSize)
{
if (scrParserGlob.opcodeLookupMaxSize >= Game::MAX_OPCODE_LOOKUP_SIZE)
if (ScrParserGlob.opcodeLookupMaxSize >= Game::MAX_OPCODE_LOOKUP_SIZE)
{
Game::Sys_Error("MAX_OPCODE_LOOKUP_SIZE exceeded");
}
Game::Z_VirtualCommit((char*)scrParserGlob.opcodeLookup + scrParserGlob.opcodeLookupMaxSize, 0x20000);
scrParserGlob.opcodeLookupMaxSize += 0x20000;
assert(size <= scrParserGlob.opcodeLookupMaxSize);
Game::Z_VirtualCommit((char*)ScrParserGlob.opcodeLookup + ScrParserGlob.opcodeLookupMaxSize, 0x20000);
ScrParserGlob.opcodeLookupMaxSize += 0x20000;
assert(size <= ScrParserGlob.opcodeLookupMaxSize);
}
size = sizeof(Game::SourceLookup) * (scrParserGlob.sourcePosLookupLen + 1);
if (size > scrParserGlob.sourcePosLookupMaxSize)
size = sizeof(Game::SourceLookup) * (ScrParserGlob.sourcePosLookupLen + 1);
if (size > ScrParserGlob.sourcePosLookupMaxSize)
{
if (scrParserGlob.sourcePosLookupMaxSize >= Game::MAX_SOURCEPOS_LOOKUP_SIZE)
if (ScrParserGlob.sourcePosLookupMaxSize >= Game::MAX_SOURCEPOS_LOOKUP_SIZE)
{
Game::Sys_Error("MAX_SOURCEPOS_LOOKUP_SIZE exceeded");
}
Game::Z_VirtualCommit((char*)scrParserGlob.sourcePosLookup + scrParserGlob.sourcePosLookupMaxSize, 0x20000);
scrParserGlob.sourcePosLookupMaxSize += 0x20000;
assert(size <= scrParserGlob.sourcePosLookupMaxSize);
Game::Z_VirtualCommit((char*)ScrParserGlob.sourcePosLookup + ScrParserGlob.sourcePosLookupMaxSize, 0x20000);
ScrParserGlob.sourcePosLookupMaxSize += 0x20000;
assert(size <= ScrParserGlob.sourcePosLookupMaxSize);
}
if (scrParserGlob.currentCodePos == Game::scrCompilePub->opcodePos)
if (ScrParserGlob.currentCodePos == Game::scrCompilePub->opcodePos)
{
assert(scrParserGlob.currentSourcePosCount);
--scrParserGlob.opcodeLookupLen;
opcodeLookup = &scrParserGlob.opcodeLookup[scrParserGlob.opcodeLookupLen];
assert(opcodeLookup->sourcePosIndex + scrParserGlob.currentSourcePosCount == scrParserGlob.sourcePosLookupLen);
assert(opcodeLookup->codePos == (char*)scrParserGlob.currentCodePos);
assert(ScrParserGlob.currentSourcePosCount);
--ScrParserGlob.opcodeLookupLen;
opcodeLookup = &ScrParserGlob.opcodeLookup[ScrParserGlob.opcodeLookupLen];
assert(opcodeLookup->sourcePosIndex + ScrParserGlob.currentSourcePosCount == ScrParserGlob.sourcePosLookupLen);
assert(opcodeLookup->codePos == (char*)ScrParserGlob.currentCodePos);
}
else
{
scrParserGlob.currentSourcePosCount = 0;
scrParserGlob.currentCodePos = Game::scrCompilePub->opcodePos;
opcodeLookup = &scrParserGlob.opcodeLookup[scrParserGlob.opcodeLookupLen];
opcodeLookup->sourcePosIndex = scrParserGlob.sourcePosLookupLen;
opcodeLookup->codePos = scrParserGlob.currentCodePos;
ScrParserGlob.currentSourcePosCount = 0;
ScrParserGlob.currentCodePos = Game::scrCompilePub->opcodePos;
opcodeLookup = &ScrParserGlob.opcodeLookup[ScrParserGlob.opcodeLookupLen];
opcodeLookup->sourcePosIndex = ScrParserGlob.sourcePosLookupLen;
opcodeLookup->codePos = ScrParserGlob.currentCodePos;
}
auto sourcePosLookupIndex = scrParserGlob.currentSourcePosCount + opcodeLookup->sourcePosIndex;
sourcePosLookup = &scrParserGlob.sourcePosLookup[sourcePosLookupIndex];
auto sourcePosLookupIndex = ScrParserGlob.currentSourcePosCount + opcodeLookup->sourcePosIndex;
sourcePosLookup = &ScrParserGlob.sourcePosLookup[sourcePosLookupIndex];
sourcePosLookup->sourcePos = sourcePos;
if (sourcePos == static_cast<unsigned int>(-1))
{
assert(scrParserGlob.delayedSourceIndex == -1);
assert(ScrParserGlob.delayedSourceIndex == -1);
assert(type & Game::SOURCE_TYPE_BREAKPOINT);
scrParserGlob.delayedSourceIndex = static_cast<int>(sourcePosLookupIndex);
ScrParserGlob.delayedSourceIndex = static_cast<int>(sourcePosLookupIndex);
}
else if (sourcePos == static_cast<unsigned int>(-2))
{
scrParserGlob.threadStartSourceIndex = static_cast<int>(sourcePosLookupIndex);
ScrParserGlob.threadStartSourceIndex = static_cast<int>(sourcePosLookupIndex);
}
else if (scrParserGlob.delayedSourceIndex >= 0 && (type & Game::SOURCE_TYPE_BREAKPOINT))
else if (ScrParserGlob.delayedSourceIndex >= 0 && (type & Game::SOURCE_TYPE_BREAKPOINT))
{
scrParserGlob.sourcePosLookup[scrParserGlob.delayedSourceIndex].sourcePos = sourcePos;
scrParserGlob.delayedSourceIndex = -1;
ScrParserGlob.sourcePosLookup[ScrParserGlob.delayedSourceIndex].sourcePos = sourcePos;
ScrParserGlob.delayedSourceIndex = -1;
}
sourcePosLookup->type |= type;
++scrParserGlob.currentSourcePosCount;
opcodeLookup->sourcePosCount = static_cast<unsigned short>(scrParserGlob.currentSourcePosCount);
++scrParserGlob.opcodeLookupLen;
++scrParserGlob.sourcePosLookupLen;
++ScrParserGlob.currentSourcePosCount;
opcodeLookup->sourcePosCount = static_cast<unsigned short>(ScrParserGlob.currentSourcePosCount);
++ScrParserGlob.opcodeLookupLen;
++ScrParserGlob.sourcePosLookupLen;
}
void ScriptError::RemoveOpcodePos()
{
if (!developer_)
if (!Developer_)
{
return;
}
@ -135,35 +135,35 @@ namespace Components::GSC
return;
}
assert(scrParserGlob.opcodeLookup);
assert(scrParserGlob.sourcePosLookup);
assert(ScrParserGlob.opcodeLookup);
assert(ScrParserGlob.sourcePosLookup);
assert(Game::scrCompilePub->opcodePos);
assert(scrParserGlob.sourcePosLookupLen);
assert(ScrParserGlob.sourcePosLookupLen);
--scrParserGlob.sourcePosLookupLen;
assert(scrParserGlob.opcodeLookupLen);
--ScrParserGlob.sourcePosLookupLen;
assert(ScrParserGlob.opcodeLookupLen);
--scrParserGlob.opcodeLookupLen;
assert(scrParserGlob.currentSourcePosCount);
--scrParserGlob.currentSourcePosCount;
--ScrParserGlob.opcodeLookupLen;
assert(ScrParserGlob.currentSourcePosCount);
--ScrParserGlob.currentSourcePosCount;
auto* opcodeLookup = &scrParserGlob.opcodeLookup[scrParserGlob.opcodeLookupLen];
auto* opcodeLookup = &ScrParserGlob.opcodeLookup[ScrParserGlob.opcodeLookupLen];
assert(scrParserGlob.currentCodePos == Game::scrCompilePub->opcodePos);
assert(opcodeLookup->sourcePosIndex + scrParserGlob.currentSourcePosCount == scrParserGlob.sourcePosLookupLen);
assert(opcodeLookup->codePos == (char*)scrParserGlob.currentCodePos);
assert(ScrParserGlob.currentCodePos == Game::scrCompilePub->opcodePos);
assert(opcodeLookup->sourcePosIndex + ScrParserGlob.currentSourcePosCount == ScrParserGlob.sourcePosLookupLen);
assert(opcodeLookup->codePos == (char*)ScrParserGlob.currentCodePos);
if (!scrParserGlob.currentSourcePosCount)
if (!ScrParserGlob.currentSourcePosCount)
{
scrParserGlob.currentCodePos = nullptr;
ScrParserGlob.currentCodePos = nullptr;
}
opcodeLookup->sourcePosCount = static_cast<unsigned short>(scrParserGlob.currentSourcePosCount);
opcodeLookup->sourcePosCount = static_cast<unsigned short>(ScrParserGlob.currentSourcePosCount);
}
void ScriptError::AddThreadStartOpcodePos(unsigned int sourcePos)
{
if (!developer_)
if (!Developer_)
{
return;
}
@ -174,12 +174,12 @@ namespace Components::GSC
}
else
{
assert(scrParserGlob.threadStartSourceIndex >= 0);
auto* sourcePosLookup = &scrParserGlob.sourcePosLookup[scrParserGlob.threadStartSourceIndex];
assert(ScrParserGlob.threadStartSourceIndex >= 0);
auto* sourcePosLookup = &ScrParserGlob.sourcePosLookup[ScrParserGlob.threadStartSourceIndex];
sourcePosLookup->sourcePos = sourcePos;
assert(!sourcePosLookup->type);
sourcePosLookup->type = 8;
scrParserGlob.threadStartSourceIndex = -1;
ScrParserGlob.threadStartSourceIndex = -1;
}
}
@ -188,35 +188,35 @@ namespace Components::GSC
const char* startLine;
int col;
assert(developer_);
return Scr_GetLineNumInternal(scrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, sourcePos, &startLine, &col, nullptr);
assert(Developer_);
return Scr_GetLineNumInternal(ScrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, sourcePos, &startLine, &col, nullptr);
}
unsigned int ScriptError::Scr_GetPrevSourcePos(const char* codePos, unsigned int index)
{
return scrParserGlob.sourcePosLookup[index + Scr_GetPrevSourcePosOpcodeLookup(codePos)->sourcePosIndex].sourcePos;
return ScrParserGlob.sourcePosLookup[index + Scr_GetPrevSourcePosOpcodeLookup(codePos)->sourcePosIndex].sourcePos;
}
Game::OpcodeLookup* ScriptError::Scr_GetPrevSourcePosOpcodeLookup(const char* codePos)
{
assert(Scr_IsInOpcodeMemory(codePos));
assert(scrParserGlob.opcodeLookup);
assert(ScrParserGlob.opcodeLookup);
unsigned int low = 0;
unsigned int high = scrParserGlob.opcodeLookupLen - 1;
unsigned int high = ScrParserGlob.opcodeLookupLen - 1;
while (low <= high)
{
unsigned int middle = (high + low) >> 1;
if (codePos < scrParserGlob.opcodeLookup[middle].codePos)
if (codePos < ScrParserGlob.opcodeLookup[middle].codePos)
{
high = middle - 1;
}
else
{
low = middle + 1;
if (low == scrParserGlob.opcodeLookupLen || codePos < scrParserGlob.opcodeLookup[low].codePos)
if (low == ScrParserGlob.opcodeLookupLen || codePos < ScrParserGlob.opcodeLookup[low].codePos)
{
return &scrParserGlob.opcodeLookup[middle];
return &ScrParserGlob.opcodeLookup[middle];
}
}
}
@ -279,16 +279,16 @@ namespace Components::GSC
unsigned int bufferIndex;
assert(Scr_IsInOpcodeMemory(codePos));
assert(scrParserPub.sourceBufferLookupLen > 0);
assert(ScrParserPub.sourceBufferLookupLen > 0);
for (bufferIndex = scrParserPub.sourceBufferLookupLen - 1; bufferIndex; --bufferIndex)
for (bufferIndex = ScrParserPub.sourceBufferLookupLen - 1; bufferIndex; --bufferIndex)
{
if (!scrParserPub.sourceBufferLookup[bufferIndex].codePos)
if (!ScrParserPub.sourceBufferLookup[bufferIndex].codePos)
{
continue;
}
if (scrParserPub.sourceBufferLookup[bufferIndex].codePos > codePos)
if (ScrParserPub.sourceBufferLookup[bufferIndex].codePos > codePos)
{
continue;
}
@ -313,7 +313,7 @@ namespace Components::GSC
return;
}
if (!developer_)
if (!Developer_)
{
if (Scr_IsInOpcodeMemory(codePos - 1))
{
@ -326,7 +326,7 @@ namespace Components::GSC
if (Game::scrVarPub->programBuffer && Scr_IsInOpcodeMemory(codePos))
{
auto bufferIndex = Scr_GetSourceBuffer(codePos - 1);
Scr_PrintSourcePos(channel, scrParserPub.sourceBufferLookup[bufferIndex].buf, scrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, Scr_GetPrevSourcePos(codePos - 1, index));
Scr_PrintSourcePos(channel, ScrParserPub.sourceBufferLookup[bufferIndex].buf, ScrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, Scr_GetPrevSourcePos(codePos - 1, index));
return;
}
}
@ -362,7 +362,7 @@ namespace Components::GSC
assert(filename);
auto lineNum = Scr_GetLineInfo(buf, sourcePos, &col, line, nullptr);
Game::Com_PrintMessage(channel, VA("(file '%s'%s, line %d)\n", filename, scrParserGlob.saveSourceBufferLookup ? " (savegame)" : "", lineNum + 1), 0);
Game::Com_PrintMessage(channel, VA("(file '%s'%s, line %d)\n", filename, ScrParserGlob.saveSourceBufferLookup ? " (savegame)" : "", lineNum + 1), 0);
Game::Com_PrintMessage(channel, VA("%s\n", line), 0);
for (auto i = 0; i < col; ++i)
@ -400,7 +400,7 @@ namespace Components::GSC
bool abort_on_error;
const char* dialogMessageSeparator;
if (!developer_)
if (!Developer_)
{
assert(Scr_IsInOpcodeMemory(codePos));
if (!(*Game::com_developer)->current.enabled)
@ -462,7 +462,7 @@ namespace Components::GSC
Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "\n");
Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n");
if (!developer_ || !scrParserPub.sourceBuf)
if (!Developer_ || !ScrParserPub.sourceBuf)
{
Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "%s\n", text);
line[0] = '\0';
@ -472,12 +472,12 @@ namespace Components::GSC
}
else
{
assert(scrParserPub.sourceBuf);
assert(ScrParserPub.sourceBuf);
Game::Com_PrintError(Game::CON_CHANNEL_PARSERSCRIPT, "%s: ", text);
Scr_PrintSourcePos(Game::CON_CHANNEL_PARSERSCRIPT, scrParserPub.scriptfilename, scrParserPub.sourceBuf, sourcePos);
auto lineNumber = Scr_GetLineInfo(scrParserPub.sourceBuf, sourcePos, &col, line, nullptr);
Game::Com_Error(Game::ERR_SCRIPT_DROP, "\x15" "script compile error\n%s\n%s(%d):\n %s\n(see console for details)\n", text, scrParserPub.scriptfilename, lineNumber, line);
Scr_PrintSourcePos(Game::CON_CHANNEL_PARSERSCRIPT, ScrParserPub.scriptfilename, ScrParserPub.sourceBuf, sourcePos);
const auto lineNumber = Scr_GetLineInfo(ScrParserPub.sourceBuf, sourcePos, &col, line, nullptr);
Game::Com_Error(Game::ERR_SCRIPT_DROP, "\x15" "script compile error\n%s\n%s(%d):\n %s\n(see console for details)\n", text, ScrParserPub.scriptfilename, lineNumber, line);
}
}
}
@ -504,7 +504,7 @@ namespace Components::GSC
Game::Com_Printf(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n");
Scr_GetTextSourcePos(scrParserPub.sourceBuf, codePos, line);
Scr_GetTextSourcePos(ScrParserPub.sourceBuf, codePos, line);
Game::Com_Error(Game::ERR_SCRIPT_DROP, "\x15" "script compile error\n%s\n%s\n(see console for details)\n", text, line);
}
@ -513,10 +513,10 @@ namespace Components::GSC
{
int col;
if (developer_ && codePos && codePos != Game::g_EndPos && Game::scrVarPub->programBuffer && Scr_IsInOpcodeMemory(codePos))
if (Developer_ && codePos && codePos != Game::g_EndPos && Game::scrVarPub->programBuffer && Scr_IsInOpcodeMemory(codePos))
{
auto bufferIndex = Scr_GetSourceBuffer(codePos - 1);
Scr_GetLineInfo(scrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, Scr_GetPrevSourcePos(codePos - 1, 0), &col, line, nullptr);
Scr_GetLineInfo(ScrParserPub.sourceBufferLookup[bufferIndex].sourceBuf, Scr_GetPrevSourcePos(codePos - 1, 0), &col, line, nullptr);
}
else
{
@ -526,68 +526,68 @@ namespace Components::GSC
void ScriptError::Scr_InitOpcodeLookup()
{
assert(!scrParserGlob.opcodeLookup);
assert(!scrParserGlob.sourcePosLookup);
assert(!scrParserPub.sourceBufferLookup);
assert(!ScrParserGlob.opcodeLookup);
assert(!ScrParserGlob.sourcePosLookup);
assert(!ScrParserPub.sourceBufferLookup);
if (!developer_)
if (!Developer_)
{
return;
}
scrParserGlob.delayedSourceIndex = -1;
scrParserGlob.opcodeLookupMaxSize = 0;
scrParserGlob.opcodeLookupLen = 0;
scrParserGlob.opcodeLookup = static_cast<Game::OpcodeLookup*>(Game::Z_VirtualReserve(Game::MAX_OPCODE_LOOKUP_SIZE));
ScrParserGlob.delayedSourceIndex = -1;
ScrParserGlob.opcodeLookupMaxSize = 0;
ScrParserGlob.opcodeLookupLen = 0;
ScrParserGlob.opcodeLookup = static_cast<Game::OpcodeLookup*>(Game::Z_VirtualReserve(Game::MAX_OPCODE_LOOKUP_SIZE));
scrParserGlob.sourcePosLookupMaxSize = 0;
scrParserGlob.sourcePosLookupLen = 0;
scrParserGlob.sourcePosLookup = static_cast<Game::SourceLookup*>(Game::Z_VirtualReserve(Game::MAX_SOURCEPOS_LOOKUP_SIZE));
scrParserGlob.currentCodePos = nullptr;
scrParserGlob.currentSourcePosCount = 0;
scrParserGlob.sourceBufferLookupMaxSize = 0;
ScrParserGlob.sourcePosLookupMaxSize = 0;
ScrParserGlob.sourcePosLookupLen = 0;
ScrParserGlob.sourcePosLookup = static_cast<Game::SourceLookup*>(Game::Z_VirtualReserve(Game::MAX_SOURCEPOS_LOOKUP_SIZE));
ScrParserGlob.currentCodePos = nullptr;
ScrParserGlob.currentSourcePosCount = 0;
ScrParserGlob.sourceBufferLookupMaxSize = 0;
scrParserPub.sourceBufferLookupLen = 0;
scrParserPub.sourceBufferLookup = static_cast<Game::SourceBufferInfo*>(Game::Z_VirtualReserve(Game::MAX_SOURCEBUF_LOOKUP_SIZE));
ScrParserPub.sourceBufferLookupLen = 0;
ScrParserPub.sourceBufferLookup = static_cast<Game::SourceBufferInfo*>(Game::Z_VirtualReserve(Game::MAX_SOURCEBUF_LOOKUP_SIZE));
}
void ScriptError::Scr_ShutdownOpcodeLookup()
{
if (scrParserGlob.opcodeLookup)
if (ScrParserGlob.opcodeLookup)
{
Z_VirtualFree(scrParserGlob.opcodeLookup);
scrParserGlob.opcodeLookup = nullptr;
Z_VirtualFree(ScrParserGlob.opcodeLookup);
ScrParserGlob.opcodeLookup = nullptr;
}
if (scrParserGlob.sourcePosLookup)
if (ScrParserGlob.sourcePosLookup)
{
Z_VirtualFree(scrParserGlob.sourcePosLookup);
scrParserGlob.sourcePosLookup = nullptr;
Z_VirtualFree(ScrParserGlob.sourcePosLookup);
ScrParserGlob.sourcePosLookup = nullptr;
}
if (scrParserPub.sourceBufferLookup)
if (ScrParserPub.sourceBufferLookup)
{
for (unsigned int i = 0; i < scrParserPub.sourceBufferLookupLen; ++i)
for (unsigned int i = 0; i < ScrParserPub.sourceBufferLookupLen; ++i)
{
Game::Engine::Hunk_FreeDebugMem(scrParserPub.sourceBufferLookup[i].buf);
Game::Engine::Hunk_FreeDebugMem(ScrParserPub.sourceBufferLookup[i].buf);
}
Z_VirtualFree(scrParserPub.sourceBufferLookup);
scrParserPub.sourceBufferLookup = nullptr;
Z_VirtualFree(ScrParserPub.sourceBufferLookup);
ScrParserPub.sourceBufferLookup = nullptr;
}
if (scrParserGlob.saveSourceBufferLookup)
if (ScrParserGlob.saveSourceBufferLookup)
{
for (unsigned int i = 0; i < scrParserGlob.saveSourceBufferLookupLen; ++i)
for (unsigned int i = 0; i < ScrParserGlob.saveSourceBufferLookupLen; ++i)
{
if (scrParserGlob.saveSourceBufferLookup[i].sourceBuf)
if (ScrParserGlob.saveSourceBufferLookup[i].sourceBuf)
{
Game::Engine::Hunk_FreeDebugMem(scrParserGlob.saveSourceBufferLookup[i].buf);
Game::Engine::Hunk_FreeDebugMem(ScrParserGlob.saveSourceBufferLookup[i].buf);
}
}
Game::Engine::Hunk_FreeDebugMem(scrParserGlob.saveSourceBufferLookup);
scrParserGlob.saveSourceBufferLookup = nullptr;
Game::Engine::Hunk_FreeDebugMem(ScrParserGlob.saveSourceBufferLookup);
ScrParserGlob.saveSourceBufferLookup = nullptr;
}
}
@ -612,39 +612,39 @@ namespace Components::GSC
Game::SourceBufferInfo* ScriptError::Scr_GetNewSourceBuffer()
{
assert(scrParserPub.sourceBufferLookup);
assert(ScrParserPub.sourceBufferLookup);
auto size = sizeof(Game::SourceBufferInfo) * (scrParserPub.sourceBufferLookupLen + 1);
if (size > scrParserGlob.sourceBufferLookupMaxSize)
auto size = sizeof(Game::SourceBufferInfo) * (ScrParserPub.sourceBufferLookupLen + 1);
if (size > ScrParserGlob.sourceBufferLookupMaxSize)
{
if (scrParserGlob.sourceBufferLookupMaxSize >= Game::MAX_SOURCEBUF_LOOKUP_SIZE)
if (ScrParserGlob.sourceBufferLookupMaxSize >= Game::MAX_SOURCEBUF_LOOKUP_SIZE)
{
Game::Sys_Error("MAX_SOURCEBUF_LOOKUP_SIZE exceeded");
}
Game::Z_VirtualCommit((char*)scrParserPub.sourceBufferLookup + scrParserGlob.sourceBufferLookupMaxSize, 0x20000);
scrParserGlob.sourceBufferLookupMaxSize += 0x20000;
assert(size <= scrParserGlob.sourceBufferLookupMaxSize);
Game::Z_VirtualCommit((char*)ScrParserPub.sourceBufferLookup + ScrParserGlob.sourceBufferLookupMaxSize, 0x20000);
ScrParserGlob.sourceBufferLookupMaxSize += 0x20000;
assert(size <= ScrParserGlob.sourceBufferLookupMaxSize);
}
return &scrParserPub.sourceBufferLookup[scrParserPub.sourceBufferLookupLen++];
return &ScrParserPub.sourceBufferLookup[ScrParserPub.sourceBufferLookupLen++];
}
void ScriptError::Scr_AddSourceBufferInternal(const char* extFilename, const char* codePos, char* sourceBuf, int len, bool doEolFixup, bool archive)
{
int i;
if (!scrParserPub.sourceBufferLookup)
if (!ScrParserPub.sourceBufferLookup)
{
scrParserPub.sourceBuf = nullptr;
ScrParserPub.sourceBuf = nullptr;
return;
}
assert((len >= -1));
assert((len >= 0) || !sourceBuf);
auto strLen = std::strlen(extFilename) + 1;
auto newLen = strLen + len + 2;
const auto strLen = std::strlen(extFilename) + 1;
const auto newLen = strLen + len + 2;
auto* buf = static_cast<char*>(Game::Engine::Hunk_AllocDebugMem(static_cast<int>(newLen))); // Scr_AddSourceBufferInternal
strcpy(buf, extFilename);
@ -689,7 +689,7 @@ namespace Components::GSC
if (sourceBuf2)
{
scrParserPub.sourceBuf = sourceBuf2;
ScrParserPub.sourceBuf = sourceBuf2;
}
}
@ -749,12 +749,12 @@ namespace Components::GSC
{
char* sourceBuf;
if (archive && scrParserGlob.saveSourceBufferLookup)
if (archive && ScrParserGlob.saveSourceBufferLookup)
{
assert(scrParserGlob.saveSourceBufferLookupLen > 0);
--scrParserGlob.saveSourceBufferLookupLen;
assert(ScrParserGlob.saveSourceBufferLookupLen > 0);
--ScrParserGlob.saveSourceBufferLookupLen;
const auto* saveSourceBuffer = scrParserGlob.saveSourceBufferLookup + scrParserGlob.saveSourceBufferLookupLen;
const auto* saveSourceBuffer = ScrParserGlob.saveSourceBufferLookup + ScrParserGlob.saveSourceBufferLookupLen;
const auto len = saveSourceBuffer->len;
assert(len >= -1);
@ -808,7 +808,7 @@ namespace Components::GSC
sprintf_s(extFilename, "%s.gsc", Game::SL_ConvertToString(static_cast<unsigned short>(name)));
const auto* oldSourceBuf = scrParserPub.sourceBuf;
const auto* oldSourceBuf = ScrParserPub.sourceBuf;
const auto* sourceBuffer = Scr_AddSourceBuffer(Game::SL_ConvertToString(static_cast<unsigned short>(name)), extFilename, Game::TempMalloc(0), true);
if (!sourceBuffer)
@ -820,8 +820,8 @@ namespace Components::GSC
Game::scrAnimPub->animTreeNames = 0;
Game::scrCompilePub->far_function_count = 0;
const auto* oldFilename = scrParserPub.scriptfilename;
scrParserPub.scriptfilename = extFilename;
const auto* oldFilename = ScrParserPub.scriptfilename;
ScrParserPub.scriptfilename = extFilename;
Game::scrCompilePub->in_ptr = "+";
Game::scrCompilePub->in_ptr_valid = false;
@ -837,8 +837,8 @@ namespace Components::GSC
Game::RemoveVariable(Game::scrCompilePub->scriptsCount, name);
scrParserPub.scriptfilename = oldFilename;
scrParserPub.sourceBuf = oldSourceBuf;
ScrParserPub.scriptfilename = oldFilename;
ScrParserPub.sourceBuf = oldSourceBuf;
Game::scrAnimPub->animTreeNames = oldAnimTreeNames;
@ -848,7 +848,7 @@ namespace Components::GSC
void ScriptError::Scr_Settings_Hk([[maybe_unused]] int developer, int developer_script, int abort_on_error)
{
assert(!abort_on_error || developer);
developer_ = (*Game::com_developer)->current.enabled;
Developer_ = (*Game::com_developer)->current.enabled;
Game::scrVarPub->developer_script = developer_script != 0;
Game::scrVmPub->abort_on_error = abort_on_error != 0;
}

View File

@ -14,10 +14,10 @@ namespace Components::GSC
private:
// Replacement for variables not present in currently available structs
static int developer_;
static int Developer_;
static Game::scrParserGlob_t scrParserGlob;
static Game::scrParserPub_t scrParserPub;
static Game::scrParserGlob_t ScrParserGlob;
static Game::scrParserPub_t ScrParserPub;
static void AddOpcodePos(unsigned int sourcePos, int type);
static void RemoveOpcodePos();

View File

@ -1,4 +1,7 @@
#include <STDInclude.hpp>
#include <Components/Modules/Events.hpp>
#include "ScriptExtension.hpp"
#include "Script.hpp"
@ -28,9 +31,15 @@ namespace Components::GSC
void ScriptExtension::GetReplacedPos(const char* pos)
{
if (ReplacedFunctions.contains(pos))
if (!pos)
{
ReplacedPos = ReplacedFunctions[pos];
// This seems to happen often and there should not be pointers to NULL in our map
return;
}
if (const auto itr = ReplacedFunctions.find(pos); itr != ReplacedFunctions.end())
{
ReplacedPos = itr->second;
}
}
@ -169,20 +178,9 @@ namespace Components::GSC
});
}
void ScriptExtension::AddMethods()
{
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
{
const auto* ent = Script::Scr_GetPlayerEntity(entref);
Game::Scr_AddBool((ent->client->flags & Game::PF_FROZEN) != 0);
});
}
ScriptExtension::ScriptExtension()
{
AddFunctions();
AddMethods();
Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x61E933, 1);

View File

@ -18,6 +18,5 @@ namespace Components::GSC
static void VMExecuteInternalStub();
static void AddFunctions();
static void AddMethods();
};
}

View File

@ -1,6 +1,8 @@
#include <STDInclude.hpp>
#include <Utils/InfoString.hpp>
#include <Components/Modules/Events.hpp>
#include "Script.hpp"
#include "UserInfo.hpp"

View File

@ -1235,11 +1235,11 @@ namespace Components
if (Game::Key_IsCatcherActive(localClientNum, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated)
{
if (key == Game::K_BUTTON_B || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+actionslot 4") == 0)
if (key == Game::K_BUTTON_B || keyState.keys[key].binding && std::strcmp(keyState.keys[key].binding, "+actionslot 4") == 0)
{
keyState.locSelInputState = Game::LOC_SEL_INPUT_CANCEL;
}
else if (key == Game::K_BUTTON_A || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+attack") == 0)
else if (key == Game::K_BUTTON_A || keyState.keys[key].binding && std::strcmp(keyState.keys[key].binding, "+attack") == 0)
{
keyState.locSelInputState = Game::LOC_SEL_INPUT_CONFIRM;
}
@ -1760,7 +1760,7 @@ namespace Components
return Game::GPAD_MAP_NONE;
}
void Gamepad::Axis_Bind_f(Command::Params* params)
void Gamepad::Axis_Bind_f(const Command::Params* params)
{
if (params->size() < 4)
{
@ -1796,7 +1796,7 @@ namespace Components
Gamepad_BindAxis(0, physicalAxis, virtualAxis, mapping);
}
void Gamepad::Axis_Unbindall_f(Command::Params*)
void Gamepad::Axis_Unbindall_f()
{
auto& gamePadGlobal = gamePadGlobals[0];
@ -1807,19 +1807,19 @@ namespace Components
}
}
void Gamepad::Bind_GP_SticksConfigs_f(Command::Params*)
void Gamepad::Bind_GP_SticksConfigs_f()
{
const auto* stickConfigName = gpad_sticksConfig.get<const char*>();
Game::Cbuf_AddText(0, Utils::String::VA("exec %s\n", stickConfigName));
}
void Gamepad::Bind_GP_ButtonsConfigs_f(Command::Params*)
void Gamepad::Bind_GP_ButtonsConfigs_f()
{
const auto* buttonConfigName = gpad_buttonConfig.get<const char*>();
Game::Cbuf_AddText(0, Utils::String::VA("exec %s\n", buttonConfigName));
}
void Gamepad::Scores_Toggle_f(Command::Params*)
void Gamepad::Scores_Toggle_f()
{
if (Game::cgArray[0].nextSnap)
{
@ -1926,7 +1926,7 @@ namespace Components
continue;
}
if (Game::playerKeys[0].keys[keyNum].binding && strcmp(Game::playerKeys[0].keys[keyNum].binding, gamePadCmd) == 0)
if (Game::playerKeys[0].keys[keyNum].binding && std::strcmp(Game::playerKeys[0].keys[keyNum].binding, gamePadCmd) == 0)
{
(*keys)[keyCount++] = keyNum;
@ -1946,7 +1946,7 @@ namespace Components
continue;
}
if (Game::playerKeys[0].keys[keyNum].binding && strcmp(Game::playerKeys[0].keys[keyNum].binding, cmd) == 0)
if (Game::playerKeys[0].keys[keyNum].binding && std::strcmp(Game::playerKeys[0].keys[keyNum].binding, cmd) == 0)
{
(*keys)[keyCount++] = keyNum;

View File

@ -184,11 +184,11 @@ namespace Components
static Game::GamepadPhysicalAxis StringToPhysicalAxis(const char* str);
static Game::GamepadVirtualAxis StringToVirtualAxis(const char* str);
static Game::GamepadMapping StringToGamePadMapping(const char* str);
static void Axis_Bind_f(Command::Params* params);
static void Axis_Unbindall_f(Command::Params* params);
static void Bind_GP_SticksConfigs_f(Command::Params* params);
static void Bind_GP_ButtonsConfigs_f(Command::Params* params);
static void Scores_Toggle_f(Command::Params* params);
static void Axis_Bind_f(const Command::Params* params);
static void Axis_Unbindall_f();
static void Bind_GP_SticksConfigs_f();
static void Bind_GP_ButtonsConfigs_f();
static void Scores_Toggle_f();
static void InitDvars();
static void CG_RegisterDvars_Hk();

View File

@ -225,7 +225,7 @@ namespace Components
});
// Test pipe functionality by sending pings
Command::Add("ipcping", []([[maybe_unused]] Command::Params* params)
Command::Add("ipcping", []()
{
Logger::Print("Sending ping to pipe!\n");
Write("ping", {});

View File

@ -118,6 +118,7 @@ namespace Components
"/dev/tty0",
"/dev/urandom",
"Dss0",
"Evan/Eve",
"FutureRave",
"H3X1C",
"Homura",
@ -137,7 +138,6 @@ namespace Components
"Dasfonia",
"Deity",
"Dizzy",
"Evan/Eve"
"HardNougat",
"INeedGames",
"JTAG",

View File

@ -1,5 +1,7 @@
#include <STDInclude.hpp>
#include "Console.hpp"
#include "Events.hpp"
namespace Components
{
@ -274,20 +276,20 @@ namespace Components
void Logger::AddServerCommands()
{
Command::AddSV("log_add", [](Command::Params* params)
Command::AddSV("log_add", [](const Command::Params* params)
{
if (params->size() < 2) return;
std::unique_lock lock(LoggingMutex);
Network::Address addr(params->get(1));
if (std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr) == LoggingAddresses[0].end())
if (std::ranges::find(LoggingAddresses[0], addr) == LoggingAddresses[0].end())
{
LoggingAddresses[0].push_back(addr);
}
});
Command::AddSV("log_del", [](Command::Params* params)
Command::AddSV("log_del", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -304,8 +306,7 @@ namespace Components
{
Network::Address addr(params->get(1));
const auto i = std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr);
if (i != LoggingAddresses[0].end())
if (const auto i = std::ranges::find(LoggingAddresses[0], addr); i != LoggingAddresses[0].end())
{
LoggingAddresses[0].erase(i);
Print("Address {} removed\n", addr.getString());
@ -317,7 +318,7 @@ namespace Components
}
});
Command::AddSV("log_list", []([[maybe_unused]] Command::Params* params)
Command::AddSV("log_list", []([[maybe_unused]] const Command::Params* params)
{
Print("# ID: Address\n");
Print("-------------\n");
@ -330,20 +331,20 @@ namespace Components
}
});
Command::AddSV("g_log_add", [](Command::Params* params)
Command::AddSV("g_log_add", [](const Command::Params* params)
{
if (params->size() < 2) return;
std::unique_lock lock(LoggingMutex);
const Network::Address addr(params->get(1));
if (std::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr) == LoggingAddresses[1].end())
if (std::ranges::find(LoggingAddresses[1], addr) == LoggingAddresses[1].end())
{
LoggingAddresses[1].push_back(addr);
}
});
Command::AddSV("g_log_del", [](Command::Params* params)
Command::AddSV("g_log_del", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -372,7 +373,7 @@ namespace Components
}
});
Command::AddSV("g_log_list", [](Command::Params*)
Command::AddSV("g_log_list", []([[maybe_unused]] const Command::Params* params)
{
Print("# ID: Address\n");
Print("-------------\n");

View File

@ -434,7 +434,7 @@ namespace Components
MapDump::MapDump()
{
Command::Add("dumpmap", [](Command::Params*)
Command::Add("dumpmap", []()
{
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
{

View File

@ -1,4 +1,6 @@
#include <STDInclude.hpp>
#include "Events.hpp"
#include "MapRotation.hpp"
#include "Party.hpp"
@ -176,7 +178,7 @@ namespace Components
void MapRotation::AddMapRotationCommands()
{
Command::AddSV("addMap", [](Command::Params* params)
Command::AddSV("addMap", [](const Command::Params* params)
{
if (params->size() < 2)
{
@ -187,7 +189,7 @@ namespace Components
DedicatedRotation.addEntry("map", params->get(1));
});
Command::AddSV("addGametype", [](Command::Params* params)
Command::AddSV("addGametype", [](const Command::Params* params)
{
if (params->size() < 2)
{

View File

@ -838,7 +838,7 @@ namespace Components
Utils::Hook(0x5A9D51, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
Utils::Hook(0x5B34DD, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
Command::Add("delayReconnect", []([[maybe_unused]] Command::Params* params)
Command::Add("delayReconnect", []()
{
Scheduler::Once([]
{
@ -852,7 +852,7 @@ namespace Components
Utils::Hook(0x4A7251, Maps::LoadNewMapCommand, HOOK_CALL).install()->quick();
}
// Download the map before a maprotation if necessary
// Download the map before a map rotation if necessary
// Conflicts with Theater's SV map rotation check, but this one is safer!
Utils::Hook(0x5AA91C, Maps::RotateCheckStub, HOOK_CALL).install()->quick();

View File

@ -828,7 +828,7 @@ namespace Components
// make Com_Error and similar go back to main_text instead of menu_xboxlive.
Utils::Hook::SetString(0x6FC790, "main_text");
Command::Add("openmenu", [](Command::Params* params)
Command::Add("openmenu", [](const Command::Params* params)
{
if (params->size() != 2)
{
@ -845,7 +845,7 @@ namespace Components
Game::Menus_OpenByName(Game::uiContext, params->get(1));
});
Command::Add("reloadmenus", []([[maybe_unused]] Command::Params* params)
Command::Add("reloadmenus", []()
{
// Close all menus
Game::Menus_CloseAll(Game::uiContext);
@ -868,11 +868,6 @@ namespace Components
}
});
Command::Add("mp_QuickMessage", [](Command::Params*)
{
Command::Execute("openmenu quickmessage");
});
// Define custom menus here
Add("ui_mp/changelog.menu");
Add("ui_mp/theater_menu.menu");

View File

@ -39,7 +39,8 @@ namespace Components
Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(const std::string& name)
{
Utils::Memory::Allocator allocator;
FileSystem::FileReader model(std::format("models/{}", name));
const auto path = std::format("models/{}", name);
FileSystem::FileReader model(path);
if (!model.exists())
{
@ -57,7 +58,16 @@ namespace Components
}
#endif
Logger::Error(Game::ERR_FATAL, "Loading model {} failed!", name);
if (ZoneBuilder::IsEnabled())
{
Logger::Print("Loading model surface {} at path \"{}\" failed!", name, path);
}
else
{
Logger::Error(Game::ERR_FATAL, "Loading model {} failed!", name);
}
return nullptr;
}
Game::CModelHeader header;

View File

@ -2,7 +2,6 @@
namespace Components
{
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
// Packet interception
std::unordered_map<std::string, Network::networkCallback> Network::CL_Callbacks;
std::unordered_map<std::string, Network::networkRawCallback> Network::CL_RawCallbacks;
@ -152,11 +151,6 @@ namespace Components
return (this->getType() != Game::NA_BAD && this->getType() >= Game::NA_BOT && this->getType() <= Game::NA_IP);
}
void Network::OnStart(const Utils::Slot<CallbackRaw>& callback)
{
StartupSignal.connect(callback);
}
void Network::Send(Game::netsrc_t type, const Address& target, const std::string& data)
{
// Do not use NET_OutOfBandPrint. It only supports non-binary data!
@ -193,7 +187,7 @@ namespace Components
// EDIT: Most 3rd party tools expect a line break, so let's use that instead!
std::string packet;
packet.append(command);
packet.append("\n", 1);
packet.push_back('\n');
packet.append(data);
Send(type, target, packet);
@ -228,12 +222,6 @@ namespace Components
BroadcastRange(100, 65536, data);
}
void Network::NetworkStart()
{
StartupSignal();
StartupSignal.clear();
}
std::uint16_t Network::GetPort()
{
assert((*Game::port));
@ -241,16 +229,6 @@ namespace Components
return static_cast<std::uint16_t>((*Game::port)->current.unsignedInt);
}
__declspec(naked) void Network::NetworkStartStub()
{
__asm
{
mov eax, 64D900h
call eax
jmp NetworkStart
}
}
__declspec(naked) void Network::PacketErrorCheck()
{
__asm
@ -303,7 +281,7 @@ namespace Components
return false;
}
const std::string data(reinterpret_cast<char*>(message->data) + offset, message->cursize - offset);
const std::string data{ reinterpret_cast<char*>(message->data) + offset, message->cursize - offset };
auto target = Address{ address };
handler->second(target, data);
@ -365,9 +343,6 @@ namespace Components
// Parse port as short in Net_AddrToString
Utils::Hook::Set<const char*>(0x4698E3, "%u.%u.%u.%u:%hu");
// Install startup handler
Utils::Hook(0x4FD4D4, NetworkStartStub, HOOK_JUMP).install()->quick();
// Prevent recvfrom error spam
Utils::Hook(0x46531A, PacketErrorCheck, HOOK_JUMP).install()->quick();
@ -403,24 +378,39 @@ namespace Components
Utils::Hook::Set<std::uint8_t>(0x682170, 0xC3); // Telling LSP that we're playing a private match
Utils::Hook::Nop(0x4FD448, 5); // Don't create lsp_socket
// Do not run UPNP stuff at all
Utils::Hook::Set<std::uint8_t>(0x48A135, 0xC3);
Utils::Hook::Nop(0x48A135 + 1, 4);
Utils::Hook::Set<std::uint8_t>(0x48A151, 0xC3);
Utils::Hook::Nop(0x48A151 + 1, 4);
// Don't spam the console
Utils::Hook(0x684080, Game::Com_DPrintf, HOOK_CALL).install()->quick();
// Disable the IWNet IP detection (default 'got ipdetect' flag to 1)
Utils::Hook::Set<std::uint8_t>(0x649D6F0, 1);
OnClientPacket("resolveAddress", []([[maybe_unused]] const Address& address, [[maybe_unused]] const std::string& data)
{
SendRaw(address, address.getString());
});
OnClientPacket("print", []([[maybe_unused]] const Address& address, [[maybe_unused]] const std::string& data)
OnClientPacketRaw("print", [](Game::netadr_t* address, Game::msg_t* msg)
{
auto* clc = Game::CL_GetLocalClientConnection(0);
if (!Game::NET_CompareBaseAdr(clc->serverAddress, *address.get()))
if (!Game::NET_CompareBaseAdr(clc->serverAddress, *address))
{
Logger::Debug("Ignoring stray 'print' network message from '{}'", Game::NET_AdrToString(*address));
return;
}
char buffer[2048]{};
char printBuf[2048]{};
Game::I_strncpyz(clc->serverMessage, data.data(), sizeof(clc->serverMessage));
Game::Com_sprintf(buffer, sizeof(buffer), "%s", data.data());
Game::Com_PrintMessage(Game::CON_CHANNEL_CLIENT, buffer, 0);
const auto* s = Game::MSG_ReadBigString(msg);
Game::I_strncpyz(clc->serverMessage, s, sizeof(clc->serverMessage));
Game::Com_sprintf(printBuf, sizeof(printBuf), "%s", s);
Game::Com_PrintMessage(Game::CON_CHANNEL_CLIENT, printBuf, false);
});
}
}

View File

@ -46,16 +46,12 @@ namespace Components
Game::netadr_t address;
};
typedef void(CallbackRaw)();
using networkCallback = std::function<void(Address&, const std::string&)>;
using networkRawCallback = std::function<void(Game::netadr_t*, Game::msg_t* msg)>;
Network();
static std::uint16_t GetPort();
static void OnStart(const Utils::Slot<CallbackRaw>& callback);
// Send quake-styled binary data
static void Send(const Address& target, const std::string& data);
@ -77,13 +73,9 @@ namespace Components
static void OnClientPacketRaw(const std::string& command, const networkRawCallback& callback);
private:
static Utils::Signal<CallbackRaw> StartupSignal;
static std::unordered_map<std::string, networkCallback> CL_Callbacks;
static std::unordered_map<std::string, networkRawCallback> CL_RawCallbacks;
static void NetworkStart();
static void NetworkStartStub();
static void PacketErrorCheck();
static bool CL_HandleCommand(Game::netadr_t* address, const char* command, Game::msg_t* message);

View File

@ -13,14 +13,14 @@ namespace Components
std::recursive_mutex Node::Mutex;
std::vector<Node::Entry> Node::Nodes;
bool Node::wasIngame = false;
bool Node::WasIngame = false;
bool Node::Entry::isValid()
bool Node::Entry::isValid() const
{
return (this->lastResponse.has_value() && !this->lastResponse->elapsed(NODE_HALFLIFE * 2));
}
bool Node::Entry::isDead()
bool Node::Entry::isDead() const
{
if (!this->lastResponse.has_value())
{
@ -37,7 +37,7 @@ namespace Components
return false;
}
bool Node::Entry::requiresRequest()
bool Node::Entry::requiresRequest() const
{
return (!this->isDead() && (!this->lastRequest.has_value() || this->lastRequest->elapsed(NODE_HALFLIFE)));
}
@ -49,7 +49,9 @@ namespace Components
Session::Send(this->address, "nodeListRequest");
Node::SendList(this->address);
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Sent request to {}", this->address.getString());
#endif
}
void Node::Entry::reset()
@ -154,14 +156,14 @@ namespace Components
{
if (ServerList::UseMasterServer) return; // don't run node frame if master server is active
if (*Game::clcState > 0)
if (Game::CL_GetLocalClientConnectionState(0) != Game::CA_DISCONNECTED)
{
wasIngame = true;
return; // don't run while ingame because it can still cause lag spikes on lower end PCs
WasIngame = true;
return; // don't run while in-game because it can still cause lag spikes on lower end PCs
}
}
if (wasIngame) // our last frame we were ingame and now we aren't so touch all nodes
if (WasIngame) // our last frame we were in-game and now we aren't so touch all nodes
{
for (auto i = Node::Nodes.begin(); i != Node::Nodes.end();++i)
{
@ -170,7 +172,8 @@ namespace Components
i->lastRequest.reset();
i->lastResponse.reset();
}
wasIngame = false;
WasIngame = false;
}
static Utils::Time::Interval frameLimit;
@ -216,7 +219,9 @@ namespace Components
Proto::Node::List list;
if (!list.ParseFromString(data)) return;
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Received response from {}", address.getString());
#endif
std::lock_guard _(Node::Mutex);
@ -234,12 +239,16 @@ namespace Components
{
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && !ServerList::UseMasterServer && list.protocol() == PROTOCOL)
{
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Inserting {} into the serverlist", address.getString());
#endif
ServerList::InsertRequest(address);
}
else
{
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Dropping serverlist insertion for {}", address.getString());
#endif
}
for (auto& node : Node::Nodes)
@ -303,7 +312,7 @@ namespace Components
{
Scheduler::Once([=]
{
#ifdef DEBUG_NODE
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Sending {} nodeListResponse length to {}\n", nodeListData.length(), address.getCString());
#endif
Session::Send(address, "nodeListResponse", nodeListData);
@ -344,7 +353,7 @@ namespace Components
Scheduler::OnGameInitialized(loadNodes, Scheduler::Pipeline::MAIN);
Command::Add("listnodes", [](Command::Params*)
Command::Add("listnodes", [](const Command::Params*)
{
Logger::Print("Nodes: {}\n", Node::Nodes.size());
@ -355,10 +364,14 @@ namespace Components
}
});
Command::Add("addnode", [](Command::Params* params)
Command::Add("addnode", [](const Command::Params* params)
{
if (params->size() < 2) return;
Node::Add({ params->get(1) });
auto address = Network::Address{ params->get(1) };
if (address.isValid())
{
Node::Add(address);
}
});
}

View File

@ -24,10 +24,10 @@ namespace Components
std::optional<Utils::Time::Point> lastRequest;
std::optional<Utils::Time::Point> lastResponse;
bool isValid();
bool isDead();
[[nodiscard]] bool isValid() const;
[[nodiscard]] bool isDead() const;
bool requiresRequest();
[[nodiscard]] bool requiresRequest() const;
void sendRequest();
void reset();
@ -44,7 +44,7 @@ namespace Components
private:
static std::recursive_mutex Mutex;
static std::vector<Entry> Nodes;
static bool wasIngame;
static bool WasIngame;
static void HandleResponse(Network::Address address, const std::string& data);

View File

@ -9,6 +9,7 @@
#include "Party.hpp"
#include "ServerList.hpp"
#include "Stats.hpp"
#include "TextRenderer.hpp"
#include "Voice.hpp"
#include <version.hpp>
@ -293,7 +294,7 @@ namespace Components
Utils::Hook::Xor<DWORD>(0x4D376D, Game::DVAR_LATCH);
Utils::Hook::Xor<DWORD>(0x5E3789, Game::DVAR_LATCH);
Command::Add("connect", [](Command::Params* params)
Command::Add("connect", [](const Command::Params* params)
{
if (params->size() < 2)
{
@ -311,7 +312,7 @@ namespace Components
}
});
Command::Add("reconnect", [](Command::Params*)
Command::Add("reconnect", []()
{
Connect(Container.target);
});
@ -493,7 +494,7 @@ namespace Components
{
ConnectError("Invalid map or gametype.");
}
else if (Container.info.get("isPrivate") == "1"s && !Dvar::Var("password").get<std::string>().length())
else if (Container.info.get("isPrivate") == "1"s && Dvar::Var("password").get<std::string>().empty())
{
ConnectError("A password is required to join this server! Set it at the bottom of the serverlist.");
}
@ -524,7 +525,7 @@ namespace Components
{
if (!Maps::CheckMapInstalled(Container.info.get("mapname"), true)) return;
Container.motd = info.get("sv_motd");
Container.motd = TextRenderer::StripMaterialTextIcons(info.get("sv_motd"));
if (Container.matchType == 1) // Party
{

View File

@ -1,4 +1,6 @@
#include <STDInclude.hpp>
#include "Events.hpp"
#include "PlayerMovement.hpp"
#include "GSC/Script.hpp"
@ -267,7 +269,7 @@ namespace Components
float min, float max, unsigned __int16 /*flags*/, const char* description)
{
PlayerSpectateSpeedScale = Game::Dvar_RegisterFloat(dvarName, value,
min, max, Game::DVAR_CHEAT | Game::DVAR_CODINFO, description);
min, max, Game::DVAR_CODINFO, description);
return PlayerSpectateSpeedScale;
}
@ -275,20 +277,20 @@ namespace Components
void PlayerMovement::RegisterMovementDvars()
{
PlayerDuckedSpeedScale = Game::Dvar_RegisterFloat("player_duckedSpeedScale",
0.65f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
0.65f, 0.0f, 5.0f, Game::DVAR_CODINFO,
"The scale applied to the player speed when ducking");
PlayerProneSpeedScale = Game::Dvar_RegisterFloat("player_proneSpeedScale",
0.15f, 0.0f, 5.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
0.15f, 0.0f, 5.0f, Game::DVAR_CODINFO,
"The scale applied to the player speed when crawling");
// 3arc naming convention
CGUfoScaler = Game::Dvar_RegisterFloat("cg_ufo_scaler",
6.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
6.0f, 0.001f, 1000.0f, Game::DVAR_CODINFO,
"The speed at which ufo camera moves");
CGNoclipScaler = Game::Dvar_RegisterFloat("cg_noclip_scaler",
3.0f, 0.001f, 1000.0f, Game::DVAR_CHEAT | Game::DVAR_CODINFO,
3.0f, 0.001f, 1000.0f, Game::DVAR_CODINFO,
"The speed at which noclip camera moves");
BGDisableLandingSlowdown = Game::Dvar_RegisterBool("bg_disableLandingSlowdown",

View File

@ -369,31 +369,6 @@ namespace Components
// remove limit on IWD file loading
Utils::Hook::Set<BYTE>(0x642BF3, 0xEB);
// dont run UPNP stuff on main thread
Utils::Hook::Set<BYTE>(0x48A135, 0xC3);
Utils::Hook::Set<BYTE>(0x48A151, 0xC3);
Utils::Hook::Nop(0x684080, 5); // Don't spam the console
// spawn upnp thread when UPNP_init returns
Utils::Hook::Hook(0x47982B, []()
{
std::thread([]
{
Com_InitThreadData();
// check natpmpstate
// state 4 is no more devices to query
while (Utils::Hook::Get<int>(0x66CE200) < 4)
{
Utils::Hook::Call<void()>(0x4D7030)();
Game::Sys_Sleep(500);
}
}).detach();
}, HOOK_JUMP).install()->quick();
// disable the IWNet IP detection (default 'got ipdetect' flag to 1)
Utils::Hook::Set<BYTE>(0x649D6F0, 1);
// Fix stats sleeping
Utils::Hook::Set<BYTE>(0x6832BA, 0xEB);
Utils::Hook::Set<BYTE>(0x4BD190, 0xC3);
@ -522,7 +497,7 @@ namespace Components
Command::Add("unlockstats", QuickPatch::UnlockStats);
Command::Add("dumptechsets", [](Command::Params* param)
Command::Add("dumptechsets", [](const Command::Params* param)
{
if (param->size() != 2)
{

View File

@ -1,6 +1,7 @@
#include <STDInclude.hpp>
#include <proto/rcon.pb.h>
#include "Events.hpp"
#include "RCon.hpp"
#include "Party.hpp"
@ -21,7 +22,7 @@ namespace Components
void RCon::AddCommands()
{
Command::Add("rcon", [](Command::Params* params)
Command::Add("rcon", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -60,7 +61,7 @@ namespace Components
Logger::Print("You are connected to an invalid server\n");
});
Command::Add("remoteCommand", [](Command::Params* params)
Command::Add("remoteCommand", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -79,7 +80,7 @@ namespace Components
}
});
Command::AddSV("RconWhitelistAdd", [](Command::Params* params)
Command::AddSV("RconWhitelistAdd", [](const Command::Params* params)
{
if (params->size() < 2)
{

View File

@ -124,7 +124,7 @@ namespace Components
Utils::Hook(0x631640, GetMenuBuffer, HOOK_JUMP).install()->quick();
Utils::Hook(0x463500, Com_LoadInfoString_Hk, HOOK_JUMP).install()->quick();
Command::Add("dumpraw", [](Command::Params* params)
Command::Add("dumpraw", [](const Command::Params* params)
{
if (params->size() < 2)
{
@ -140,7 +140,7 @@ namespace Components
return;
}
const char* data = Game::Scr_AddSourceBuffer(nullptr, file.getName().data(), nullptr, false);
const auto* data = Game::Scr_AddSourceBuffer(nullptr, file.getName().data(), nullptr, false);
if (data)
{

View File

@ -1,5 +1,7 @@
#include <STDInclude.hpp>
#include "Events.hpp"
namespace Components
{
Utils::Signal<Renderer::BackendCallback> Renderer::BackendFrameSignal;

View File

@ -29,12 +29,6 @@ namespace Components
return size;
}
int Security::SV_CanReplaceServerCommand_Hk([[maybe_unused]] Game::client_s* client, [[maybe_unused]] const char* cmd)
{
// This is a fix copied from V2. As I don't have time to investigate, let's simply trust them
return -1;
}
long Security::AtolAdjustPlayerLimit(const char* string)
{
return std::min<long>(std::atol(string), 18);
@ -140,7 +134,6 @@ namespace Components
// Exploit fixes
Utils::Hook(0x414D92, Msg_ReadBitsCompressCheckSV, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommands
Utils::Hook(0x4A9F56, Msg_ReadBitsCompressCheckCL, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
Utils::Hook(0x407376, SV_CanReplaceServerCommand_Hk, HOOK_CALL).install()->quick(); // SV_CanReplaceServerCommand
Utils::Hook::Set<std::uint8_t>(0x412370, 0xC3); // SV_SteamAuthClient
Utils::Hook::Set<std::uint8_t>(0x5A8C70, 0xC3); // CL_HandleRelayPacket

View File

@ -11,8 +11,6 @@ namespace Components
static int Msg_ReadBitsCompressCheckCL(const unsigned char* from, unsigned char* to, int size);
private:
static int SV_CanReplaceServerCommand_Hk(Game::client_s* client, const char* cmd);
static long AtolAdjustPlayerLimit(const char* string);
static void SelectStringTableEntryInDvar_Stub();

View File

@ -7,7 +7,7 @@ namespace Components
public:
ServerCommands();
using serverCommandHandler = std::function<bool(Command::Params*)>;
using serverCommandHandler = std::function<bool(const Command::Params*)>;
static void OnCommand(std::int32_t cmd, const serverCommandHandler& callback);
private:

View File

@ -54,7 +54,7 @@ namespace Components
auto* serverInfo = ServerList::GetCurrentServer();
if (info)
if (info && serverInfo)
{
Dvar::Var("uiSi_ServerName").set(serverInfo->hostname);
Dvar::Var("uiSi_MaxClients").set(serverInfo->clients);

View File

@ -2,6 +2,7 @@
#include <Utils/InfoString.hpp>
#include "Discovery.hpp"
#include "Events.hpp"
#include "Node.hpp"
#include "Party.hpp"
#include "ServerList.hpp"
@ -176,13 +177,13 @@ namespace Components
{
CurrentServer = index;
auto* info = GetCurrentServer();
auto* serverInfo = GetCurrentServer();
if (info)
if (serverInfo)
{
UIServerSelected.set(true);
UIServerSelectedMap.set(info->mapname);
Dvar::Var("ui_serverSelectedGametype").set(info->gametype);
UIServerSelectedMap.set(serverInfo->mapname);
Dvar::Var("ui_serverSelectedGametype").set(serverInfo->gametype);
}
else
{

View File

@ -1,40 +0,0 @@
#include <STDInclude.hpp>
#include "SoundMutexFix.hpp"
namespace Components
{
// This component is a workaround for issue https://github.com/XLabsProject/iw4x-client/issues/80
// In case the link goes down, this is a "game hangs randomly" issue:
//
// Investigations on the issue pointed out it comes from a situation on Intel processors where
// WaitForSingleObjectA is ignored by a thread, for some (?) reason.
//
// This locks up the game randomly, mostly at the end of rounds or when too many things happen at
// once, due to trying to stop sounds (AIL_Stop_sounds) and playing streams at the same time,
// rushing for the same resource via AIL_lock_mutex.
//
// This bug has been reproduced on mp_terminal, mp_overgrown, mp_rust, with and without bots,
// and so far this has been the only way to circumvent it afaik. This component wraps
// miles' mutex into another mutex, created below, and for some reason (?) that mutex is
// respected when miles' is not.
//
// As soon as a real fix is found, please discard this fix. In the meantime, it should not
// have side effects too bad - worst case it might cause a slight performance drop during
// team switch and intermission.
//
std::mutex SoundMutexFix::CloseStreamMutex;
void WINAPI SoundMutexFix::AIL_close_stream_Stub(int h_stream)
{
std::lock_guard lock(CloseStreamMutex);
const auto ptr = *reinterpret_cast<DWORD*>(0x6D7554); // AIL_close_stream
Utils::Hook::Call<void WINAPI(int)>(ptr)(h_stream);
}
SoundMutexFix::SoundMutexFix()
{
Utils::Hook(0x689EFE, &AIL_close_stream_Stub, HOOK_JUMP).install()->quick();
}
}

View File

@ -1,15 +0,0 @@
#pragma once
#include <mutex>
namespace Components
{
class SoundMutexFix : public Component
{
public:
SoundMutexFix();
private:
static std::mutex CloseStreamMutex;
static void WINAPI AIL_close_stream_Stub(int h_stream);
};
}

View File

@ -1,4 +1,6 @@
#include <STDInclude.hpp>
#include "Events.hpp"
#include "StartupMessages.hpp"
namespace Components

View File

@ -162,7 +162,7 @@ namespace Components
Utils::Hook::Set<BYTE>(0x4CC5F9, 0xEB);
// 'M' Seems to be used on Xbox only for parsing platform specific ranks
ServerCommands::OnCommand('M', [](Command::Params* params)
ServerCommands::OnCommand('M', [](const Command::Params* params)
{
const auto* arg1 = params->get(1);
const auto* arg2 = params->get(2);
@ -171,7 +171,7 @@ namespace Components
return true;
});
Command::Add("statGet", []([[maybe_unused]] Command::Params* params)
Command::Add("statGet", [](const Command::Params* params)
{
if (params->size() < 2)
{

View File

@ -158,25 +158,13 @@ namespace Components
// 15 or more custom classes
Utils::Hook::Set<BYTE>(0x60A2FE, NUM_CUSTOM_CLASSES);
#ifdef _DEBUG
// Reset empty names
Command::Add("checkClasses", [](Command::Params*)
{
for (int i = 0; i < NUM_CUSTOM_CLASSES; ++i)
{
// TODO: Correctly lookup using structured data
char* className = (reinterpret_cast<char*>(0x1AD3694) - 4 + 3003 + (64 * i) + 0x29);
if (!*className) strcpy_s(className, 24, Game::SEH_StringEd_GetString(Utils::String::VA("CLASS_SLOT%i", i + 1)));
}
});
#endif
return;
}
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& filename, bool* /*restrict*/)
{
// Only intercept playerdatadef loading
if (type != Game::XAssetType::ASSET_TYPE_STRUCTURED_DATA_DEF || filename != "mp/playerdata.def") return;
if (type != Game::ASSET_TYPE_STRUCTURED_DATA_DEF || filename != "mp/playerdata.def") return;
// Store asset
Game::StructuredDataDefSet* data = asset.structuredDataDefSet;

View File

@ -1480,7 +1480,7 @@ namespace Components
{
char buffer[1000]{}; // Should be more than enough
StripAllTextIcons(in.data(), buffer, sizeof(buffer));
return {buffer};
return std::string{ buffer };
}
int TextRenderer::SEH_PrintStrlenWithCursor(const char* string, const Game::field_t* field)
@ -1558,8 +1558,7 @@ namespace Components
{
if (r_colorBlind.get<bool>())
{
const auto str = std::string(name);
if (str == "g_TeamColor_EnemyTeam")
if (std::strcmp(name, "g_TeamColor_EnemyTeam") == 0)
{
// Dvar_GetUnpackedColor
const auto* colorblindEnemy = g_ColorBlind_EnemyTeam->current.color;
@ -1570,7 +1569,7 @@ namespace Components
return false;
}
if (str == "g_TeamColor_MyTeam")
if (std::strcmp(name, "g_TeamColor_MyTeam") == 0)
{
// Dvar_GetUnpackedColor
const auto* colorblindAlly = g_ColorBlind_MyTeam->current.color;

View File

@ -1,4 +1,6 @@
#include <STDInclude.hpp>
#include "Events.hpp"
#include "UIFeeder.hpp"
namespace Components

View File

@ -1,5 +1,7 @@
#include <STDInclude.hpp>
#include "Chat.hpp"
#include "Events.hpp"
#include "Voice.hpp"
namespace Components
@ -320,6 +322,7 @@ namespace Components
auto* clc = Game::CL_GetLocalClientConnection(0);
if (!Game::NET_CompareBaseAdr(clc->serverAddress, *address))
{
Logger::Debug("Ignoring stray 'v' network message from '{}'", Game::NET_AdrToString(*address));
return;
}
@ -390,7 +393,7 @@ namespace Components
Events::OnSteamDisconnect(CL_ClearMutedList);
Events::OnClientDisconnect(SV_UnmuteClient);
Events::OnClientConnect([](Game::client_s* cl) -> void
Events::OnClientConnect([](const Game::client_s* cl) -> void
{
if (Chat::IsMuted(cl))
{

View File

@ -548,7 +548,7 @@ namespace Components
}
}
void Weapon::PlayerCmd_initialWeaponRaise(Game::scr_entref_t entref)
void Weapon::PlayerCmd_InitialWeaponRaise(const Game::scr_entref_t entref)
{
auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
const auto* weapon = Game::Scr_GetString(0);
@ -578,23 +578,47 @@ namespace Components
Game::Player_SwitchToWeapon(ent);
}
void Weapon::PlayerCmd_FreezeControlsAllowLook(const Game::scr_entref_t entref)
{
const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
if (Game::Scr_GetInt(0))
{
ent->client->ps.weapCommon.weapFlags |= Game::PWF_DISABLE_WEAPONS;
ent->client->flags |= Game::CF_BIT_DISABLE_USABILITY;
}
else
{
ent->client->ps.weapCommon.weapFlags &= ~Game::PWF_DISABLE_WEAPONS;
ent->client->flags &= ~Game::CF_BIT_DISABLE_USABILITY;
}
}
void Weapon::AddScriptMethods()
{
GSC::Script::AddMethod("DisableWeaponPickup", [](Game::scr_entref_t entref)
GSC::Script::AddMethod("DisableWeaponPickup", [](const Game::scr_entref_t entref)
{
const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
ent->client->ps.weapCommon.weapFlags |= Game::PWF_DISABLE_WEAPON_PICKUP;
});
GSC::Script::AddMethod("EnableWeaponPickup", [](Game::scr_entref_t entref)
GSC::Script::AddMethod("EnableWeaponPickup", [](const Game::scr_entref_t entref)
{
const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
ent->client->ps.weapCommon.weapFlags &= ~Game::PWF_DISABLE_WEAPON_PICKUP;
});
GSC::Script::AddMethod("InitialWeaponRaise", PlayerCmd_initialWeaponRaise);
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
GSC::Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
{
const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
Game::Scr_AddBool((ent->client->flags & Game::CF_BIT_FROZEN) != 0);
});
GSC::Script::AddMethod("InitialWeaponRaise", PlayerCmd_InitialWeaponRaise);
GSC::Script::AddMethod("FreezeControlsAllowLook", PlayerCmd_FreezeControlsAllowLook);
}
Weapon::Weapon()

View File

@ -35,7 +35,8 @@ namespace Components
static void WeaponEntCanBeGrabbed_Stub();
static void PlayerCmd_initialWeaponRaise(Game::scr_entref_t entref);
static void PlayerCmd_InitialWeaponRaise(Game::scr_entref_t entref);
static void PlayerCmd_FreezeControlsAllowLook(Game::scr_entref_t entref);
static void AddScriptMethods();
};

View File

@ -18,6 +18,8 @@ namespace Components
volatile bool ZoneBuilder::CommandThreadTerminate = false;
std::thread ZoneBuilder::CommandThread;
iw4of::api ZoneBuilder::ExporterAPI(GetExporterAPIParams());
std::string ZoneBuilder::DumpingZone{};
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
// Reserve 100MB by default.
@ -757,6 +759,23 @@ namespace Components
return header;
}
void ZoneBuilder::RefreshExporterWorkDirectory()
{
if (ZoneBuilder::DumpingZone.empty())
{
ExporterAPI.set_work_path(std::format("userraw/dump/stray"));
}
else
{
ExporterAPI.set_work_path(std::format("userraw/dump/{}", ZoneBuilder::DumpingZone));
}
}
iw4of::api* ZoneBuilder::GetExporter()
{
return &ExporterAPI;
}
iw4of::params_t ZoneBuilder::Zone::getIW4OfApiParams()
{
iw4of::params_t params{};
@ -815,7 +834,7 @@ namespace Components
void* data = Utils::Memory::GetAllocator()->allocate(size);
std::memcpy(data, *loadDef, size);
image->texture.loadDef = reinterpret_cast<Game::GfxImageLoadDef *>(data);
image->texture.loadDef = static_cast<Game::GfxImageLoadDef*>(data);
return 0;
}
@ -857,7 +876,7 @@ namespace Components
return GetCurrentThreadId() == Utils::Hook::Get<DWORD>(0x1CDE7FC);
}
static Game::XZoneInfo baseZones_old[] =
static Game::XZoneInfo baseZones[] =
{
{ "code_pre_gfx_mp", Game::DB_ZONE_CODE, 0 },
{ "localized_code_pre_gfx_mp", Game::DB_ZONE_CODE_LOC, 0 },
@ -869,17 +888,6 @@ namespace Components
{ "localized_ui_mp", Game::DB_ZONE_GAME, 0 }
};
static Game::XZoneInfo baseZones[] =
{
{ "defaults", Game::DB_ZONE_CODE, 0 },
{ "techsets", Game::DB_ZONE_CODE, 0 },
{ "common_mp", Game::DB_ZONE_COMMON, 0 },
{ "localized_common_mp", Game::DB_ZONE_COMMON_LOC, 0 },
{ "ui_mp", Game::DB_ZONE_GAME, 0 },
{ "localized_ui_mp", Game::DB_ZONE_GAME, 0 }
};
void ZoneBuilder::Com_Quitf_t()
{
ExitProcess(0);
@ -938,15 +946,7 @@ namespace Components
Command::Add("quit", ZoneBuilder::Com_Quitf_t);
// now load default assets and shaders
if (FastFiles::Exists("defaults") && FastFiles::Exists("techsets"))
{
Game::DB_LoadXAssets(baseZones, ARRAYSIZE(baseZones), 0);
}
else
{
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing new init zones (defaults.ff & techsets.ff). You will need to load fastfiles to manually obtain techsets.\n");
Game::DB_LoadXAssets(baseZones_old, ARRAYSIZE(baseZones_old), 0);
}
Game::DB_LoadXAssets(baseZones, ARRAYSIZE(baseZones), 0);
Logger::Print("Waiting for fastiles to load...\n");
while (!Game::Sys_IsDatabaseReady())
@ -983,6 +983,7 @@ namespace Components
Logger::Print("\t-buildzone [zone]: builds a zone from a csv located in zone_source\n");
Logger::Print("\t-buildall: builds all zones in zone_source\n");
Logger::Print("\t-verifyzone [zone]: loads and verifies the specified zone\n");
Logger::Print("\t-dumpzone [zone]: loads and dump the specified zone\n");
Logger::Print("\t-listassets [assettype]: lists all loaded assets of the specified type\n");
Logger::Print("\t-quit: quits the program\n");
Logger::Print(" --------------------------------------------------------------------------------\n");
@ -1097,6 +1098,55 @@ namespace Components
return file;
}
iw4of::params_t ZoneBuilder::GetExporterAPIParams()
{
iw4of::params_t params{};
params.write_only_once = true;
params.find_other_asset = [](int type, const std::string& name) -> void*
{
if (ZoneBuilder::DumpingZone.empty())
{
return Game::DB_FindXAssetHeader(static_cast<Game::XAssetType>(type), name.data()).data;
}
// Do not deadlock the DB
return nullptr;
};
params.fs_read_file = [](const std::string& filename) -> std::string
{
auto file = FileSystem::File(filename);
if (file.exists())
{
return file.getBuffer();
}
return {};
};
params.get_from_string_table = [](const unsigned int& id) -> std::string
{
return Game::SL_ConvertToString(static_cast<Game::scr_string_t>(id));
};
params.print = [](iw4of::params_t::print_type t, const std::string& message) -> void
{
switch (t)
{
case iw4of::params_t::P_ERR:
Logger::Error(Game::ERR_FATAL, "{}", message);
break;
case iw4of::params_t::P_WARN:
Logger::Print("{}", message);
break;
}
};
return params;
}
ZoneBuilder::ZoneBuilder()
{
// ReSharper disable CppStaticAssertFailure
@ -1200,7 +1250,7 @@ namespace Components
// don't remap techsets
Utils::Hook::Nop(0x5BC791, 5);
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader /*asset*/, const std::string& name, bool* /*restrict*/)
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& name, bool* /*restrict*/)
{
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
{
@ -1209,9 +1259,47 @@ namespace Components
OutputDebugStringA(Utils::String::Format("%s\n", name));
#endif
}
if (!ZoneBuilder::DumpingZone.empty())
{
if (ExporterAPI.is_type_supported(type) && name[0] != ',')
{
ExporterAPI.write(type, asset.data);
Components::Logger::Print(".");
}
}
});
Command::Add("verifyzone", [](Command::Params* params)
Command::Add("dumpzone", [](const Command::Params* params)
{
if (params->size() < 2) return;
std::string zone = params->get(1);
ZoneBuilder::DumpingZone = zone;
ZoneBuilder::RefreshExporterWorkDirectory();
Game::XZoneInfo info;
info.name = zone.data();
info.allocFlags = Game::DB_ZONE_MOD;
info.freeFlags = 0;
Logger::Print("Dumping zone '{}'...\n", zone);
Game::DB_LoadXAssets(&info, 1, true);
AssetHandler::FindOriginalAsset(Game::ASSET_TYPE_RAWFILE, zone.data()); // Lock until zone is loaded
Logger::Print("Unloading zone '{}'...\n", zone);
info.freeFlags = Game::DB_ZONE_MOD;
info.allocFlags = 0;
info.name = nullptr;
Game::DB_LoadXAssets(&info, 1, true);
AssetHandler::FindOriginalAsset(Game::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded
Logger::Print("Zone '{}' dumped", ZoneBuilder::DumpingZone);
ZoneBuilder::DumpingZone = std::string();
});
Command::Add("verifyzone", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -1250,7 +1338,7 @@ namespace Components
Logger::Print("\n");
});
Command::Add("buildzone", [](Command::Params* params)
Command::Add("buildzone", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -1260,7 +1348,7 @@ namespace Components
Zone(zoneName).build();
});
Command::Add("buildall", []([[maybe_unused]] Command::Params* params)
Command::Add("buildall", []()
{
auto path = std::format("{}\\zone_source", (*Game::fs_basepath)->current.string);
auto zoneSources = FileSystem::GetSysFileList(path, "csv", false);
@ -1327,7 +1415,7 @@ namespace Components
}
});
Command::Add("buildtechsets", [](Command::Params*)
Command::Add("buildtechsets", [](const Command::Params*)
{
Utils::IO::CreateDir("zone_source/techsets");
Utils::IO::CreateDir("zone/techsets");
@ -1580,7 +1668,7 @@ namespace Components
Zone("techsets/techsets").build();
});
Command::Add("listassets", [](Command::Params* params)
Command::Add("listassets", [](const Command::Params* params)
{
if (params->size() < 2) return;
Game::XAssetType type = Game::DB_GetXAssetNameType(params->get(1));
@ -1595,7 +1683,7 @@ namespace Components
}
});
Command::Add("loadtempzone", [](Command::Params* params)
Command::Add("loadtempzone", [](const Command::Params* params)
{
if (params->size() < 2) return;
@ -1609,7 +1697,7 @@ namespace Components
}
});
Command::Add("unloadtempzones", [](Command::Params*)
Command::Add("unloadtempzones", [](const Command::Params*)
{
Game::XZoneInfo info;
info.name = nullptr;
@ -1619,7 +1707,7 @@ namespace Components
AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded
});
Command::Add("materialInfoDump", [](Command::Params*)
Command::Add("materialInfoDump", [](const Command::Params*)
{
Game::DB_EnumXAssets(Game::ASSET_TYPE_MATERIAL, [](Game::XAssetHeader header, void*)
{
@ -1627,31 +1715,6 @@ namespace Components
header.material->info.name, header.material->info.sortKey & 0xFF, header.material->info.gameFlags & 0xFF, header.material->stateFlags & 0xFF);
}, nullptr, false);
});
Command::Add("iwiDump", [](Command::Params* params)
{
if (params->size() < 2) return;
auto path = std::format("{}\\mods\\{}\\images", (*Game::fs_basepath)->current.string, params->get(1));
auto images = FileSystem::GetSysFileList(path, "iwi", false);
for (auto i = images.begin(); i != images.end();)
{
*i = std::format("images/{}", *i);
if (FileSystem::File(*i).exists())
{
i = images.erase(i);
continue;
}
++i;
}
Logger::Print("------------------- BEGIN IWI DUMP -------------------\n");
Logger::Print("{}\n", nlohmann::json(images).dump());
Logger::Print("------------------- END IWI DUMP -------------------\n");
});
}
}

View File

@ -137,6 +137,9 @@ namespace Components
static std::vector<std::pair<Game::XAssetType, std::string>> EndAssetTrace();
static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder);
static void RefreshExporterWorkDirectory();
static iw4of::api* GetExporter();
private:
static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
@ -155,6 +158,8 @@ namespace Components
static bool IsThreadMainThreadHook();
static Game::Sys_File Sys_CreateFile_Stub(const char* dir, const char* filename);
static iw4of::params_t GetExporterAPIParams();
static void Com_Quitf_t();
static void CommandThreadCallback();
@ -164,5 +169,7 @@ namespace Components
static volatile bool CommandThreadTerminate;
static std::thread CommandThread;
static iw4of::api ExporterAPI;
static std::string DumpingZone;
};
}

View File

@ -1,4 +1,4 @@
#include <STDInclude.hpp>
#include <STDInclude.hpp>
#include <zlib.h>
@ -2947,6 +2947,9 @@ namespace Components
iw4Map->mapEnts = &codolMapEnts;
memcpy(&iw4Map->smodelNodeCount, &codolMap->smodelNodeCount, 48);
// unused on IW4
iw4Map->numLeafSurfaces = 0;
AssetHandler::Relocate(&cancerMap->info.numCPlanes, &iw4Map->planeCount, 8);
AssetHandler::Relocate(&cancerMap->numStaticModels, &iw4Map->numStaticModels, 8);
AssetHandler::Relocate(&cancerMap->info.numMaterials, &iw4Map->numMaterials, 24);
@ -3482,7 +3485,7 @@ namespace Components
if (ZoneBuilder::IsEnabled())
{
Command::Add("decryptImages", [](Command::Params*)
Command::Add("decryptImages", []()
{
auto images = FileSystem::GetSysFileList("iw4x/images", "iwi");
Logger::Print("decrypting {} images...\n", images.size());
@ -3516,7 +3519,7 @@ namespace Components
Logger::Print("decrypted {} images!\n", images.size());
});
Command::Add("decryptSounds", []([[maybe_unused]] Command::Params* params)
Command::Add("decryptSounds", []()
{
auto sounds = FileSystem::GetSysFileList("iw4x/sound", "iwi");
Logger::Print("decrypting {} sounds...\n", sounds.size());
@ -3653,13 +3656,6 @@ namespace Components
Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick();
Utils::Hook(0x471A39, Zones::LoadWindowImage, HOOK_JUMP).install()->quick();
#ifdef DEBUG
// Easy dirty disk debugging
Utils::Hook::Set<WORD>(0x4CF7F0, 0xC3CC);
// disable _invoke_watson to allow debugging
Utils::Hook::Set<WORD>(0x6B9602, 0xCCCC);
#endif
}
}
#pragma optimize( "", on )

View File

@ -58,12 +58,12 @@ BOOL APIENTRY DllMain(HINSTANCE /*hinstDLL*/, DWORD fdwReason, LPVOID /*lpvReser
if (!binary || std::memcmp(binary, BASEGAME_NAME, 14) != 0)
#endif
{
MessageBoxA(nullptr,
"Failed to load game binary.\n"
"You did not install the iw4x-rawfiles!\n"
"Please use the XLabs launcher to run the game. For support, please visit https://xlabs.dev/support_iw4x_client",
"ERROR",
MB_ICONERROR
MessageBoxA(nullptr,
"Failed to load game binary.\n"
"You did not install the iw4x-rawfiles!\n"
"Please use the XLabs launcher to run the game. For support, please visit https://xlabs.dev/support_iw4x_client",
"ERROR",
MB_ICONERROR
);
return FALSE;
}

View File

@ -58,6 +58,7 @@ namespace Game
connstate_t CL_GetLocalClientConnectionState(const int localClientNum)
{
AssertOffset(clientUIActive_t, connectionState, 0x9B8);
AssertIn(localClientNum, STATIC_MAX_LOCAL_CLIENTS);
return clientUIActives[localClientNum].connectionState;

View File

@ -9,6 +9,7 @@ namespace Game
Com_Error_t Com_Error = Com_Error_t(0x4B22D0);
Com_Printf_t Com_Printf = Com_Printf_t(0x402500);
Com_DPrintf_t Com_DPrintf = Com_DPrintf_t(0x413490);
Com_PrintError_t Com_PrintError = Com_PrintError_t(0x4F8C70);
Com_PrintWarning_t Com_PrintWarning = Com_PrintWarning_t(0x4E0200);
Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830);

View File

@ -20,6 +20,9 @@ namespace Game
typedef void(*Com_Printf_t)(int channel, const char* fmt, ...);
extern Com_Printf_t Com_Printf;
typedef void(*Com_DPrintf_t)(int channel, const char* fmt, ...);
extern Com_DPrintf_t Com_DPrintf;
typedef void(*Com_PrintError_t)(int channel, const char* fmt, ...);
extern Com_PrintError_t Com_PrintError;

View File

@ -0,0 +1,39 @@
#include <STDInclude.hpp>
#include "FastCriticalSection.hpp"
namespace Game::Engine
{
FastCriticalSectionScopeRead::FastCriticalSectionScopeRead(FastCriticalSection* cs)
: cs_(cs)
{
if (this->cs_)
{
Sys_LockRead(this->cs_);
}
}
FastCriticalSectionScopeRead::~FastCriticalSectionScopeRead()
{
if (this->cs_)
{
Sys_UnlockRead(this->cs_);
}
}
FastCriticalSectionScopeWrite::FastCriticalSectionScopeWrite(FastCriticalSection* cs)
: cs_(cs)
{
if (this->cs_)
{
Sys_LockWrite(this->cs_);
}
}
FastCriticalSectionScopeWrite::~FastCriticalSectionScopeWrite()
{
if (this->cs_)
{
Sys_UnlockWrite(this->cs_);
}
}
}

View File

@ -0,0 +1,24 @@
#pragma once
namespace Game::Engine
{
class FastCriticalSectionScopeRead
{
public:
FastCriticalSectionScopeRead(FastCriticalSection* cs);
~FastCriticalSectionScopeRead();
private:
FastCriticalSection* cs_;
};
class FastCriticalSectionScopeWrite
{
public:
FastCriticalSectionScopeWrite(FastCriticalSection* cs);
~FastCriticalSectionScopeWrite();
private:
FastCriticalSection* cs_;
};
}

View File

@ -93,7 +93,7 @@ namespace Game
MSG_ReadLong_t MSG_ReadLong = MSG_ReadLong_t(0x4C9550);
MSG_ReadShort_t MSG_ReadShort = MSG_ReadShort_t(0x40BDD0);
MSG_ReadInt64_t MSG_ReadInt64 = MSG_ReadInt64_t(0x4F1850);
MSG_ReadString_t MSG_ReadString = MSG_ReadString_t(0x60E2B0);
MSG_ReadBigString_t MSG_ReadBigString = MSG_ReadBigString_t(0x60E2B0);
MSG_ReadStringLine_t MSG_ReadStringLine = MSG_ReadStringLine_t(0x4FEF30);
MSG_WriteByte_t MSG_WriteByte = MSG_WriteByte_t(0x48C520);
MSG_WriteData_t MSG_WriteData = MSG_WriteData_t(0x4F4120);
@ -238,6 +238,7 @@ namespace Game
I_strncpyz_t I_strncpyz = I_strncpyz_t(0x4D6F80);
I_CleanStr_t I_CleanStr = I_CleanStr_t(0x4AD470);
I_isdigit_t I_isdigit = I_isdigit_t(0x4E71E0);
XNAddrToString_t XNAddrToString = XNAddrToString_t(0x452690);
@ -318,8 +319,6 @@ namespace Game
infoParm_t* infoParams = reinterpret_cast<infoParm_t*>(0x79D260); // Count 0x1E
clientState_t* clcState = reinterpret_cast<clientState_t*>(0xB2C540);
GfxScene* scene = reinterpret_cast<GfxScene*>(0x6944914);
Console* con = reinterpret_cast<Console*>(0x9FCCF8);
@ -1157,7 +1156,7 @@ namespace Game
int SEH_GetLocalizedTokenReference(char* token, const char* reference, const char* messageType, msgLocErrType_t errType)
{
static DWORD SEH_GetLocalizedTokenReference_t = 0x629BB0;
auto answer = 0;
auto result = 0;
__asm
{
@ -1168,11 +1167,11 @@ namespace Game
push token
call SEH_GetLocalizedTokenReference_t
add esp, 0x4
mov answer, eax
mov result, eax
popad
}
return answer;
return result;
}
void Player_SwitchToWeapon(gentity_s* player)

View File

@ -233,8 +233,8 @@ namespace Game
typedef __int64(*MSG_ReadInt64_t)(msg_t* msg);
extern MSG_ReadInt64_t MSG_ReadInt64;
typedef char*(*MSG_ReadString_t)(msg_t* msg, char* string, unsigned int maxChars);
extern MSG_ReadString_t MSG_ReadString;
typedef char*(*MSG_ReadBigString_t)(msg_t* msg);
extern MSG_ReadBigString_t MSG_ReadBigString;
typedef char*(*MSG_ReadStringLine_t)(msg_t *msg, char *string, unsigned int maxChars);
extern MSG_ReadStringLine_t MSG_ReadStringLine;
@ -554,6 +554,9 @@ namespace Game
typedef char*(*I_CleanStr_t)(char* string);
extern I_CleanStr_t I_CleanStr;
typedef bool(*I_isdigit_t)(int c);
extern I_isdigit_t I_isdigit;
typedef void(*XNAddrToString_t)(const XNADDR* xnaddr, char* str);
extern XNAddrToString_t XNAddrToString;
@ -659,8 +662,6 @@ namespace Game
extern infoParm_t* infoParams;
extern clientState_t* clcState;
extern GfxScene* scene;
extern Console* con;

View File

@ -7140,9 +7140,11 @@ namespace Game
enum
{
PF_NOCLIP = 1 << 0,
PF_UFO = 1 << 1,
PF_FROZEN = 1 << 2,
CF_BIT_NOCLIP = (1 << 0),
CF_BIT_UFO = (1 << 1),
CF_BIT_FROZEN = (1 << 2),
CF_BIT_DISABLE_USABILITY = (1 << 3),
CF_BIT_NO_KNOCKBACK = (1 << 4),
};
enum sessionState_t

View File

@ -22,7 +22,7 @@ namespace Game
assert((size >= 0));
#pragma warning(push)
#pragma warning(disable: 6250)
[[maybe_unused]] const auto result = VirtualFree(ptr, size, MEM_DECOMMIT);
VirtualFree(ptr, size, MEM_DECOMMIT);
#pragma warning(pop)
}

View File

@ -2,15 +2,11 @@
#ifndef RC_INVOKED
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _USE_MATH_DEFINES
// Requires Visual Leak Detector plugin: http://vld.codeplex.com/
#define VLD_FORCE_ENABLE
//#include <vld.h>
#include <Windows.h>
#include <WinSock2.h>
#include <ShlObj.h>

View File

@ -25,7 +25,7 @@ namespace Utils
pos += sprintf_s(&buffer[pos], sizeof(buffer) - pos, "%X", ~timeGetTime() ^ GenerateInt());
pos += sprintf_s(&buffer[pos], sizeof(buffer) - pos, "%X", GenerateInt());
return std::string(buffer, static_cast<std::size_t>(pos));
return std::string{ buffer, static_cast<std::size_t>(pos) };
}
std::uint32_t Rand::GenerateInt()
@ -62,13 +62,13 @@ namespace Utils
if (!key.isValid()) return {};
std::uint8_t buffer[512];
DWORD length = sizeof(buffer);
unsigned long length = sizeof(buffer);
ltc_mp = ltm_desc;
register_prng(&sprng_desc);
ecc_sign_hash(reinterpret_cast<const std::uint8_t*>(message.data()), message.size(), buffer, &length, nullptr, find_prng("sprng"), key.getKeyPtr());
return {reinterpret_cast<char*>(buffer), length};
return std::string{ reinterpret_cast<char*>(buffer), length };
}
bool ECC::VerifyMessage(Key key, const std::string& message, const std::string& signature)
@ -78,7 +78,9 @@ namespace Utils
ltc_mp = ltm_desc;
int result = 0;
return (ecc_verify_hash(reinterpret_cast<const std::uint8_t*>(signature.data()), signature.size(), reinterpret_cast<const std::uint8_t*>(message.data()), message.size(), &result, key.getKeyPtr()) == CRYPT_OK && result != 0);
return (ecc_verify_hash(reinterpret_cast<const std::uint8_t*>(signature.data()), signature.size(),
reinterpret_cast<const std::uint8_t*>(message.data()), message.size(),
&result, key.getKeyPtr()) == CRYPT_OK && result != 0);
}
#pragma endregion
@ -104,7 +106,7 @@ namespace Utils
if (!key.isValid()) return {};
std::uint8_t buffer[512];
DWORD length = sizeof(buffer);
unsigned long length = sizeof(buffer);
register_prng(&sprng_desc);
register_hash(&sha1_desc);
@ -113,7 +115,7 @@ namespace Utils
rsa_sign_hash(reinterpret_cast<const std::uint8_t*>(message.data()), message.size(), buffer, &length, NULL, find_prng("sprng"), find_hash("sha1"), 0, key.getKeyPtr());
return {reinterpret_cast<char*>(buffer), length};
return std::string{ reinterpret_cast<char*>(buffer), length };
}
bool RSA::VerifyMessage(Key key, const std::string& message, const std::string& signature)
@ -185,7 +187,7 @@ namespace Utils
tiger_process(&state, data, length);
tiger_done(&state, buffer);
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
std::string hash{ reinterpret_cast<char*>(buffer), sizeof(buffer) };
if (!hex) return hash;
return String::DumpHex(hash, {});
@ -209,7 +211,7 @@ namespace Utils
sha1_process(&state, data, length);
sha1_done(&state, buffer);
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
std::string hash{ reinterpret_cast<char*>(buffer), sizeof(buffer) };
if (!hex) return hash;
return String::DumpHex(hash, {});
@ -233,7 +235,7 @@ namespace Utils
sha256_process(&state, data, length);
sha256_done(&state, buffer);
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
std::string hash{ reinterpret_cast<char*>(buffer), sizeof(buffer) };
if (!hex) return hash;
return String::DumpHex(hash, {});
@ -257,7 +259,7 @@ namespace Utils
sha512_process(&state, data, length);
sha512_done(&state, buffer);
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
std::string hash{ reinterpret_cast<char*>(buffer), sizeof(buffer) };
if (!hex) return hash;
return String::DumpHex(hash, {});

View File

@ -67,12 +67,6 @@ namespace Utils
}
}
template <typename T>
bool Contains(const std::vector<T>* haystack, T needle)
{
return std::ranges::find(*haystack, needle) != haystack->end();
}
template <typename T> using Slot = std::function<T>;
template <typename T>
class Signal