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" name: "Draft a new release"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3.5.2
- name: Normalize version - name: Normalize version
id: normalize_version id: normalize_version

View File

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

2
.gitmodules vendored
View File

@ -17,7 +17,7 @@
[submodule "deps/mongoose"] [submodule "deps/mongoose"]
path = deps/mongoose path = deps/mongoose
url = https://github.com/cesanta/mongoose.git url = https://github.com/cesanta/mongoose.git
branch = 7.8 branch = 7.9
[submodule "deps/protobuf"] [submodule "deps/protobuf"]
path = deps/protobuf path = deps/protobuf
url = https://github.com/google/protobuf.git 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/). 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 ## r4251 - 2023-05-03
### Added ### Added
- Add GSC client field `ping` as a read-only field (#1002) - Add GSC client field `ping` as a read-only field (#1002)
- Add GSC client field `address` as a read-only field (#1003) - 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 ### Fixed
@ -29,7 +42,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Fixed ### Fixed
- Fix bug with Steam Proxy (#991) - 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 ### 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) - 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 ### Fixed
- Fix bug with how `sv_mapRotationCurrent` is parsed (#977) - 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) - Test Clients will no longer receive names from the Xlabs Patreon website. The old behaviour was restored (#852)
- Enabled `OpenFile` GSC function (#862) - Enabled `OpenFile` GSC function (#862)
- Enabled `CloseFile` 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) - 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) - `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 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) - 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 helpful information to script-related errors. Must set `developer` Dvar to "2" and `developer_script` Dvar to "1" (#721)
- Add command line `-disable-mongoose` to disable IW4x's built-in webserver (#728) - Add command line `-disable-mongoose` argument to disable IW4x's built-in webserver (#728)
- Add command line `-disable-rate-limit-check` to disable the rate check on RCon requests (#769) - Add command line `-disable-rate-limit-check` argument 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) - GUID of muted clients is saved to the `muted-users.json` file in the `userraw` folder (#732)
- Add `sv_nextMap` Dvar (#736) - Add `sv_nextMap` Dvar (#736)
- Add `elifdef` and `elifndef` directives to the menu preprocessor (#747) - Add `elifdef` and `elifndef` directives to the menu preprocessor (#747)
- Add `r_drawRunners` Dvar (#758) - 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 demo playback would stop when opening a laptop based killstreak (#699)
- Fix bug where mod download speed was inexplicably slow (#707) - Fix bug where mod download speed was inexplicably slow (#707)
- Fix bug where `g_hardcore` could not be set by servers (#708) - 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 `FileRead` GSC function could return buffers that are too big (#767)
- Fix bug where the `OnPlayerSay` GSC function would cause issues (#776) - 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 `IsSprinting` GSC method (#587)
- Add `StorageLoad` GSC function (#595) - Add `StorageLoad` GSC function (#595)
- Add `bg_climbAnything` Dvar (#663) - Add `bg_climbAnything` Dvar (#663)
- Add ClanTag support for bots (#645) - Add "ClanTag" support for bots (#645)
- Add `sv_randomBotNames` Dvar (#665) - Add `sv_randomBotNames` Dvar (#665)
- Add support for parsing localized strings files (.str & .json) (#621) - Add support for parsing localized strings files (.str & .json) (#621)
- Add `callvote` menus (#613) - 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 `DisableWeaponPickup` GSC method (#329)
- Add `EnableWeaponPickup` GSC method (#329) - Add `EnableWeaponPickup` GSC method (#329)
- Add `protect-saved-dvars` command line argument (#335) - 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 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. - 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 ### Added
- Add SetName GSC method (#288) - Add `SetName` GSC method (#288)
- Add ResetName GSC method (#288) - Add `ResetName` GSC method (#288)
- Add OnPlayerSay GSC function (#265) - Add `OnPlayerSay` GSC function (#265)
- Add Give client command (works with weapons only) (#292) - Add `Give` client command (works with weapons only) (#292)
- Add `sv_disableChat` Dvar (#290) - Add `sv_disableChat` Dvar (#290)
- Add `addMap` command (#302) - Add `addMap` command (#302)
- Add `addGametype` 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 ### Added
- Add IsArray GSC function (#248) - Add `IsArray` GSC function (#248)
- Keybind fields in menus work with controller keys (#255) - Keybind fields in menus work with controller keys (#255)
### Changed ### Changed
@ -398,14 +416,14 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Added ### Added
- Add ToUpper GSC Function (#216) - Add `ToUpper` GSC Function (#216)
- Add StrICmp GSC Function (#216) - Add `StrICmp` GSC Function (#216)
- Add IsEndStr GSC Function (#216) - Add `IsEndStr` GSC Function (#216)
- Add DropAllBots GSC Function (#174) - Add `DropAllBots` GSC Function (#174)
- Add GSC entity field `entityflags` (#228) - Add GSC entity field `entityflags` (#228)
- Add GSC client field `clientflags` (#228) - Add GSC client field `clientflags` (#228)
- Add bg_surfacePenetration Dvar (#241) - Add `bg_surfacePenetration` Dvar (#241)
- Add bg_bulletRange Dvar (#241) - Add `bg_bulletRange Dvar` (#241)
### Changed ### 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 mouse acceleration when polling rate is greater than 125Hz (#230)
- Fixed issue with player speed caused by sprinting from the prone position (#232) - 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 client crash when `cg_chatHeight` was set to 0 (#237)
- Fixed GSC function Scr_TableLookupIStringByRow (#162) - Fixed GSC function `Scr_TableLookupIStringByRow` (#162)
### Known issues ### 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 controller support (#75)
- Add aim assist for controllers (#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 support for building custom Fonts with Zonebuilder (#88)
- Add colorblind friendly team colors (#101) - 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) - 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) - Add `muteClient` command for the game chat (#159)
- Implement unmute command for the game chat (#159) - Add `unmute` command for the game chat (#159)
- Add sv_allowAimAssist Dvar (#75) - Add `sv_allowAimAssist` Dvar (#75)
- Add sv_allowColoredNames (#130) - Add `sv_allowColoredNames` (#130)
- Add sv_randomMapRotation Dvar (#146) - Add `sv_randomMapRotation` Dvar (#146)
- Add rcon_log_requests Dvar (#195) - Add `rcon_log_requests` Dvar (#195)
- Add player_duckedSpeedScale Dvar (#141) - Add `player_duckedSpeedScale` Dvar (#141)
- Add player_proneSpeedScale Dvar (#141) - Add `player_proneSpeedScale` Dvar (#141)
- Add cg_ufo_scaler Dvar (#158) - Add `cg_ufo_scaler` Dvar (#158)
- Add cg_noclip_scaler Dvar (#158) - Add `cg_noclip_scaler` Dvar (#158)
- Add bg_bouncesAllAngles Dvar (#158) - Add `bg_bouncesAllAngles` Dvar (#158)
- Add bg_rocketJump Dvar (#158) - Add `bg_rocketJump` Dvar (#158)
- Add bg_elevators Dvar (#156) - Add `bg_elevators` Dvar (#156)
- Implement noclip client command (#152) - Add `noclip` client command (#152)
- Implement ufo client command (#152) - Add `ufo` client command (#152)
- Implement God client command (#152) - Add `God` client command (#152)
- Implement demigod client command (#152) - Add `demigod` client command (#152)
- Implement notarget client command (#152) - Add `notarget` client command (#152)
- Add noclip GSC Function (#152) - Add `noclip` GSC Function (#152)
- Add ufo GSC Function (#152) - Add `ufo` GSC Function (#152)
- Add God GSC Function (#152) - Add `God` GSC Function (#152)
- Add demigod GSC Function (#152) - Add `demigod` GSC Function (#152)
- Add notarget GSC Function (#152) - Add `notarget` GSC Function (#152)
- Add replaceFunc GSC Function (#144) - Add `replaceFunc` GSC Function (#144)
### Changed ### Changed
- Renamed sv_enableBounces to bg_bounces (#158) - Renamed `sv_enableBounces` to bg_bounces (#158)
- Renamed g_playerCollision to bg_playerEjection (#158) - Renamed `g_playerCollision` to bg_playerEjection (#158)
- Renamed g_playerEjection to bg_playerCollision (#158) - Renamed `g_playerEjection` to bg_playerCollision (#158)
- Setviewpos client command works outside private matches (#163) - `setviewpos` client command works outside private matches (#163)
- Ufo client command works outside of private matches (#152) - `ufo` client command works outside of private matches (#152)
- Noclip 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) - 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 ### 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 - Fixes and improvements to Zonebuilder
- Fixed issue where the game froze following base game script throwing an error (#74) - Fixed issue where the game froze following base game script throwing an error (#74)
- Fixed RCon on party servers (#91 - #95) - 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 sound issue that causes the game to freeze (#106)
- Fixed issue where materials strings found in hostnames, player names, chat etc. caused the game to crash (#113) - Fixed issue where materials strings found in hostnames, player names, chat etc. caused the game to crash (#113)
- Fixed issue with servers displaying an invalid player count (#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 ### Added
- Add host information to /info endpoint (request) - Add host information to `/info` endpoint (request)
- Add fileWrite GSC Function (#36) - Add `fileWrite` GSC Function (#36)
- Add fileRead GSC Function (#36) - Add `fileRead` GSC Function (#36)
- Add fileExists GSC Function (#36) - Add `fileExists` GSC Function (#36)
- Add fileRemove GSC Function (#36) - Add `fileRemove` GSC Function (#36)
- Add botMovement GSC Function (#46) - Add `botMovement` GSC Function (#46)
- Add botAction GSC Function (#46) - Add `botAction` GSC Function (#46)
- Add botWeapon GSC Function (#46) - Add `botWeapon` GSC Function (#46)
- Add botStop GSC Function (#46) - Add `botStop` GSC Function (#46)
- Add isBot GSC Function (#46) - Add `isBot` GSC Function (#46)
- Add setPing GSC Function (#46) - Add `setPing` GSC Function (#46)
- Add GetSystemTime and GetSystemTimeMilliseconds GSC Functions (#46) - Add `GetSystemTime` and `GetSystemTimeMilliseconds` GSC Functions (#46)
- Add PrintConsole GSC Function (#46) - Add `PrintConsole` GSC Function (#46)
- Add Exec GSC Function (#46) - Add `Exec` GSC Function (#46)
- Add getIP GSC Method (#36) - Add `getIP` GSC Method (#36)
- Add getPing GSC Method (#36) - Add `getPing` GSC Method (#36)
- Add scr_intermissionTime GSC Function (#25) - Add `scr_intermissionTime` Dvar (#25)
- Add g_playerCollision Dvar (#36) - Add `g_playerCollision` Dvar (#36)
- Add g_playerEjection Dvar (#36) - Add `g_playerEjection` Dvar (#36)
- Add r_specularCustomMaps Dvar (#36) - Add `r_specularCustomMaps` Dvar (#36)
- Unlock safeArea_horizontal and safeArea_vertical Dvars (#42) - Unlock `safeArea_horizontal` and `safeArea_vertical` Dvars (#42)
- Unlock cg_fovscale Dvar (#47) - Unlock `cg_fovscale` Dvar (#47)
- Add g_antilag Dvar (#61) - Add `g_antilag` Dvar (#61)
### Changed ### 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 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 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 an issue causing the game to crash when Steam was running in the background (#56)
- Fixed slow download speed when using fast download - 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 ### Added
- Implement unbanclient command. - Add `unbanClient` command.
- Implement /serverlist api endpoint on dedicated servers - Add `/serverlist` api endpoint on dedicated servers
- Add dvar to control the server query rate (net_serverQueryLimit & net_serverFrames) - Add dvar to control the server query rate (net_serverQueryLimit & net_serverFrames)
### Changed ### 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 multiple vulnerability's
- Fix lag spikes on lower end PCs - Fix lag spikes on lower end PCs
- Fix invalid name check - Fix invalid name check
- Fix openLink command crash issue - Fix `openLink` command crash issue
- Fix lobby server map downloading - Fix lobby server map downloading
- Fix steam integration - 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/Discovery.hpp"
#include "Modules/Download.hpp" #include "Modules/Download.hpp"
#include "Modules/Elevators.hpp" #include "Modules/Elevators.hpp"
#include "Modules/Events.hpp"
#include "Modules/Exception.hpp" #include "Modules/Exception.hpp"
#include "Modules/FastFiles.hpp" #include "Modules/FastFiles.hpp"
#include "Modules/Friends.hpp" #include "Modules/Friends.hpp"
@ -49,7 +50,6 @@
#include "Modules/ServerList.hpp" #include "Modules/ServerList.hpp"
#include "Modules/Session.hpp" #include "Modules/Session.hpp"
#include "Modules/SlowMotion.hpp" #include "Modules/SlowMotion.hpp"
#include "Modules/SoundMutexFix.hpp"
#include "Modules/StartupMessages.hpp" #include "Modules/StartupMessages.hpp"
#include "Modules/Stats.hpp" #include "Modules/Stats.hpp"
#include "Modules/StringTable.hpp" #include "Modules/StringTable.hpp"
@ -163,7 +163,6 @@ namespace Components
Register(new ServerList()); Register(new ServerList());
Register(new Session()); Register(new Session());
Register(new SlowMotion()); Register(new SlowMotion());
Register(new SoundMutexFix());
Register(new StartupMessages()); Register(new StartupMessages());
Register(new Stats()); Register(new Stats());
Register(new StringTable()); Register(new StringTable());

View File

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

View File

@ -47,7 +47,7 @@ namespace Components
std::vector<std::pair<Game::XAssetType, std::string>> AssetHandler::EmptyAssets; 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) void AssetHandler::RegisterInterface(IAsset* iAsset)
{ {
@ -73,7 +73,7 @@ namespace Components
void AssetHandler::ClearTemporaryAssets() 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(); AssetHandler::TemporaryAssets[i].clear();
} }
@ -108,7 +108,7 @@ namespace Components
Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename) Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename)
{ {
Game::XAssetHeader header = { nullptr }; 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 tempPool = &AssetHandler::TemporaryAssets[type];
auto entry = tempPool->find(filename); auto entry = tempPool->find(filename);
@ -224,7 +224,7 @@ namespace Components
void AssetHandler::ModifyAsset(Game::XAssetType type, Game::XAssetHeader asset, const std::string& name) 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) 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; 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; asset.vehDef->turretWeapon = nullptr;
} }
// Fix shader const stuff // 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) 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->sortKeyEffectDecal = 39;
asset.gfxWorld->sortKeyEffectAuto = 48; asset.gfxWorld->sortKeyEffectAuto = 48;
@ -408,7 +408,7 @@ namespace Components
ZoneBuilder::Zone::AssetRecursionMarker _(builder); ZoneBuilder::Zone::AssetRecursionMarker _(builder);
Game::XAssetHeader header = { nullptr }; 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 tempPool = &AssetHandler::TemporaryAssets[type];
auto entry = tempPool->find(filename); 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) 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 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*) 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; asset.model->numLods = 0;
} }
}); });
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP, 1); Game::ReallocateAssetPool(Game::ASSET_TYPE_GAMEWORLD_SP, 1);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 * 2 : 7168); Game::ReallocateAssetPool(Game::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 * 2 : 7168);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700 * 2); Game::ReallocateAssetPool(Game::ASSET_TYPE_LOADED_SOUND, 2700 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200 * 2); Game::ReallocateAssetPool(Game::ASSET_TYPE_FX, 1200 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, 14000); Game::ReallocateAssetPool(Game::ASSET_TYPE_LOCALIZE_ENTRY, 14000);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192 * 2); Game::ReallocateAssetPool(Game::ASSET_TYPE_XANIMPARTS, 8192 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125 * 2); Game::ReallocateAssetPool(Game::ASSET_TYPE_XMODEL, 5125 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PHYSPRESET, 128); Game::ReallocateAssetPool(Game::ASSET_TYPE_PHYSPRESET, 128);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PIXELSHADER, ZoneBuilder::IsEnabled() ? 0x4000 : 10000); Game::ReallocateAssetPool(Game::ASSET_TYPE_PIXELSHADER, ZoneBuilder::IsEnabled() ? 0x4000 : 10000);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, ZoneBuilder::IsEnabled() ? 0x2000 : 3072); Game::ReallocateAssetPool(Game::ASSET_TYPE_VERTEXSHADER, ZoneBuilder::IsEnabled() ? 0x2000 : 3072);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192 * 2); Game::ReallocateAssetPool(Game::ASSET_TYPE_MATERIAL, 8192 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXDECL, ZoneBuilder::IsEnabled() ? 0x400 : 196); Game::ReallocateAssetPool(Game::ASSET_TYPE_VERTEXDECL, ZoneBuilder::IsEnabled() ? 0x400 : 196);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_WEAPON, WEAPON_LIMIT); Game::ReallocateAssetPool(Game::ASSET_TYPE_WEAPON, WEAPON_LIMIT);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_STRINGTABLE, 800); Game::ReallocateAssetPool(Game::ASSET_TYPE_STRINGTABLE, 800);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMPACT_FX, 8); Game::ReallocateAssetPool(Game::ASSET_TYPE_IMPACT_FX, 8);
// Register asset interfaces // Register asset interfaces
if (ZoneBuilder::IsEnabled()) if (ZoneBuilder::IsEnabled())
{ {
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MAP_ENTS, 10); Game::ReallocateAssetPool(Game::ASSET_TYPE_MAP_ENTS, 10);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, 8192 * 2); Game::ReallocateAssetPool(Game::ASSET_TYPE_XMODEL_SURFS, 8192 * 2);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, 0x2000); Game::ReallocateAssetPool(Game::ASSET_TYPE_TECHNIQUE_SET, 0x2000);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FONT, 32); Game::ReallocateAssetPool(Game::ASSET_TYPE_FONT, 32);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_RAWFILE, 2048); Game::ReallocateAssetPool(Game::ASSET_TYPE_RAWFILE, 2048);
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LEADERBOARD, 500); Game::ReallocateAssetPool(Game::ASSET_TYPE_LEADERBOARD, 500);
AssetHandler::RegisterInterface(new Assets::IFont_s()); AssetHandler::RegisterInterface(new Assets::IFont_s());
AssetHandler::RegisterInterface(new Assets::IWeapon()); AssetHandler::RegisterInterface(new Assets::IWeapon());

View File

@ -5,7 +5,7 @@ namespace Assets
class IXModel : public Components::AssetHandler::IAsset class IXModel : public Components::AssetHandler::IAsset
{ {
public: 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 save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void mark(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 class IXModelSurfs : public Components::AssetHandler::IAsset
{ {
public: 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; 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) void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{ {
AssertSize(Game::snd_alias_list_t, 12); 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 load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
void save(Game::XAssetHeader header, 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 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()) if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
{ {
Command::Add("securityLevel", [](Command::Params* params) Command::Add("securityLevel", [](const Command::Params* params)
{ {
if (params->size() < 2) if (params->size() < 2)
{ {
@ -547,7 +547,6 @@ namespace Components
TokenContainer.cancel = true; TokenContainer.cancel = true;
TokenContainer.generating = false; TokenContainer.generating = false;
// Terminate thread
if (TokenContainer.thread.joinable()) if (TokenContainer.thread.joinable())
{ {
TokenContainer.thread.join(); TokenContainer.thread.join();

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "Bans.hpp" #include "Bans.hpp"
#include "Events.hpp"
namespace Components namespace Components
{ {
@ -232,9 +233,9 @@ namespace Components
SaveBans(&list); SaveBans(&list);
} }
Bans::Bans() void Bans::AddServerCommands()
{ {
Command::Add("banClient", [](Command::Params* params) Command::AddSV("banClient", [](const Command::Params* params)
{ {
if (!Dedicated::IsRunning()) if (!Dedicated::IsRunning())
{ {
@ -278,11 +279,11 @@ namespace Components
return; 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); BanClient(cl, reason);
}); });
Command::Add("unbanClient", [](Command::Params* params) Command::AddSV("unbanClient", [](const Command::Params* params)
{ {
if (!Dedicated::IsRunning()) 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 LoadBans(BanList* list);
static void SaveBans(const BanList* list); static void SaveBans(const BanList* list);
static void AddServerCommands();
}; };
} }

View File

@ -2,6 +2,7 @@
#include "Bots.hpp" #include "Bots.hpp"
#include "ClanTags.hpp" #include "ClanTags.hpp"
#include "Events.hpp"
#include "GSC/Script.hpp" #include "GSC/Script.hpp"
@ -407,37 +408,9 @@ namespace Components
} }
} }
Bots::Bots() void Bots::AddServerCommands()
{ {
AssertOffset(Game::client_s, bIsTestClient, 0x41AF0); Command::AddSV("spawnBot", [](const Command::Params* params)
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)
{ {
if (!Dedicated::IsRunning()) if (!Dedicated::IsRunning())
{ {
@ -467,8 +440,7 @@ namespace Components
if (input == end) if (input == end)
{ {
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "{} is not a valid input\nUsage: {} optional <number of bots> or optional <\"all\">\n", 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));
input, params->get(0));
return; return;
} }
} }
@ -480,6 +452,39 @@ namespace Components
Spawn(count); 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(); AddScriptMethods();

View File

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

View File

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

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "Chat.hpp" #include "Chat.hpp"
#include "Events.hpp"
#include "PlayerName.hpp" #include "PlayerName.hpp"
#include "TextRenderer.hpp" #include "TextRenderer.hpp"
#include "Voice.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()) 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()) 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()) 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()) 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()) if (!Dedicated::IsRunning())
{ {
@ -514,7 +515,7 @@ namespace Components
Logger::Print("Raw: {}\n", message); Logger::Print("Raw: {}\n", message);
}); });
Command::AddSV("tellraw", [](Command::Params* params) Command::AddSV("tellraw", [](const Command::Params* params)
{ {
if (!Dedicated::IsRunning()) 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"); cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients"); sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");
Events::OnSVInit(AddChatCommands); Events::OnSVInit(AddServerCommands);
LoadMutedList(); LoadMutedList();
@ -622,6 +623,12 @@ namespace Components
// Change logic that does word splitting with new lines for chat messages to support fonticons // 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(); 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(); AddScriptFunctions();
// Avoid duplicates // Avoid duplicates

View File

@ -42,7 +42,7 @@ namespace Components
static void SaveMutedList(const muteList& list); static void SaveMutedList(const muteList& list);
static void LoadMutedList(); static void LoadMutedList();
static void AddChatCommands(); static void AddServerCommands();
static int GetCallbackReturn(); static int GetCallbackReturn();
static int ChatCallback(Game::gentity_s* self, const char* codePos, const char* message, int mode); 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 <STDInclude.hpp>
#include "ClanTags.hpp" #include "ClanTags.hpp"
#include "Events.hpp"
#include "PlayerName.hpp" #include "PlayerName.hpp"
#include "ServerCommands.hpp" #include "ServerCommands.hpp"
@ -240,7 +242,7 @@ namespace Components
std::memset(&ClientState, 0, sizeof(char[Game::MAX_CLIENTS][MAX_CLAN_NAME_LENGTH])); 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) if (std::strcmp(params->get(1), "clanNames") == 0)
{ {

View File

@ -488,12 +488,12 @@ namespace Components
if (!CheatsOk(ent)) if (!CheatsOk(ent))
return; return;
ent->client->flags ^= Game::PF_NOCLIP; ent->client->flags ^= Game::CF_BIT_NOCLIP;
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
Logger::Debug("Noclip toggled for entity {}", entNum); 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) void ClientCommand::Cmd_UFO_f(Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
@ -501,12 +501,12 @@ namespace Components
if (!CheatsOk(ent)) if (!CheatsOk(ent))
return; return;
ent->client->flags ^= Game::PF_UFO; ent->client->flags ^= Game::CF_BIT_UFO;
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
Logger::Debug("UFO toggled for entity {}", entNum); 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() ClientCommand::ClientCommand()

View File

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

View File

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

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "Debug.hpp" #include "Debug.hpp"
#include "Events.hpp"
#include "TextRenderer.hpp" #include "TextRenderer.hpp"
#include "Game/Engine/ScopedCriticalSection.hpp" #include "Game/Engine/ScopedCriticalSection.hpp"
@ -272,7 +273,7 @@ namespace Components
assert(0 && "a"); 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 newFileName[MAX_PATH]{};
char to_ospath[MAX_OSPATH]{}; char to_ospath[MAX_OSPATH]{};

View File

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

View File

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

View File

@ -3,6 +3,7 @@
#include <Utils/WebIO.hpp> #include <Utils/WebIO.hpp>
#include "Download.hpp" #include "Download.hpp"
#include "Events.hpp"
#include "MapRotation.hpp" #include "MapRotation.hpp"
#include "Party.hpp" #include "Party.hpp"
#include "ServerInfo.hpp" #include "ServerInfo.hpp"
@ -697,7 +698,7 @@ namespace Components
mg_mgr_init(&Mgr); mg_mgr_init(&Mgr);
Network::OnStart([] Events::OnNetworkInit([]() -> void
{ {
const auto* nc = mg_http_listen(&Mgr, Utils::String::VA(":%hu", Network::GetPort()), &EventHandler, &Mgr); const auto* nc = mg_http_listen(&Mgr, Utils::String::VA(":%hu", Network::GetPort()), &EventHandler, &Mgr);
if (!nc) if (!nc)
@ -709,7 +710,7 @@ namespace Components
ServerRunning = true; ServerRunning = true;
Terminate = false; Terminate = false;
ServerThread = Utils::Thread::CreateNamedThread("Mongoose", [] ServerThread = Utils::Thread::CreateNamedThread("Mongoose", []() -> void
{ {
Com_InitThreadData(); Com_InitThreadData();
@ -722,7 +723,7 @@ namespace Components
} }
else else
{ {
Events::OnDvarInit([] Events::OnDvarInit([]() -> void
{ {
UIDlTimeLeft = Dvar::Register<const char*>("ui_dl_timeLeft", "", Game::DVAR_NONE, ""); UIDlTimeLeft = Dvar::Register<const char*>("ui_dl_timeLeft", "", Game::DVAR_NONE, "");
UIDlProgress = Dvar::Register<const char*>("ui_dl_progress", "", 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 // un-cheat jump_slowdownEnable
Utils::Hook::Xor<std::uint32_t>(0x4EFABE, Game::DVAR_CHEAT); 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 // Hook dvar 'name' registration
Utils::Hook(0x40531C, Dvar_RegisterName, HOOK_CALL).install()->quick(); Utils::Hook(0x40531C, Dvar_RegisterName, HOOK_CALL).install()->quick();

View File

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

View File

@ -1,48 +1,79 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "Events.hpp"
namespace Components namespace Components
{ {
Utils::Signal<Events::ClientCallback> Events::ClientDisconnectSignal; Utils::Concurrency::Container<Events::ClientCallback> Events::ClientDisconnectTasks_;
Utils::Signal<Events::ClientConnectCallback> Events::ClientConnectSignal; Utils::Concurrency::Container<Events::ClientConnectCallback> Events::ClientConnectTasks_;
Utils::Signal<Events::Callback> Events::SteamDisconnectSignal; Utils::Concurrency::Container<Events::Callback> Events::SteamDisconnectTasks_;
Utils::Signal<Events::Callback> Events::ShutdownSystemSignal; Utils::Concurrency::Container<Events::Callback> Events::ShutdownSystemTasks_;
Utils::Signal<Events::Callback> Events::ClientInitSignal; Utils::Concurrency::Container<Events::Callback> Events::ClientInitTasks_;
Utils::Signal<Events::Callback> Events::ServerInitSignal; Utils::Concurrency::Container<Events::Callback> Events::ServerInitTasks_;
Utils::Signal<Events::Callback> Events::DvarInitSignal; 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) 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 Utils::Hook::Call<void(int)>(0x4AA430)(clientNum); // ClientDisconnect
} }
void Events::SV_UserinfoChanged_Hk(Game::client_s* cl) 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 Utils::Hook::Call<void(Game::client_s*)>(0x401950)(cl); // SV_UserinfoChanged
} }
void Events::SteamDisconnect_Hk() void Events::SteamDisconnect_Hk()
{ {
SteamDisconnectSignal(); SteamDisconnectTasks_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
});
Utils::Hook::Call<void()>(0x467CC0)(); // LiveSteam_Client_SteamDisconnect Utils::Hook::Call<void()>(0x467CC0)(); // LiveSteam_Client_SteamDisconnect
} }
void Events::Scr_ShutdownSystem_Hk(unsigned char sys) 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 Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys); // Scr_ShutdownSystem
} }
void Events::CL_InitOnceForAllClients_HK() void Events::CL_InitOnceForAllClients_HK()
{ {
ClientInitSignal(); ClientInitTasks_.access([](Callback& tasks)
ClientInitSignal.clear(); {
for (const auto& func : tasks)
{
func();
}
tasks = {}; // Only called once. Clear
});
Utils::Hook::Call<void()>(0x404CA0)(); // CL_InitOnceForAllClients Utils::Hook::Call<void()>(0x404CA0)(); // CL_InitOnceForAllClients
} }
void Events::SV_Init_Hk() void Events::SV_Init_Hk()
{ {
ServerInitSignal(); ServerInitTasks_.access([](Callback& tasks)
ServerInitSignal.clear(); {
for (const auto& func : tasks)
{
func();
}
tasks = {}; // Only called once. Clear
});
Utils::Hook::Call<void()>(0x474320)(); // SV_InitGameMode Utils::Hook::Call<void()>(0x474320)(); // SV_InitGameMode
} }
void Events::Com_InitDvars_Hk() void Events::Com_InitDvars_Hk()
{ {
DvarInitSignal(); DvarInitTasks_.access([](Callback& tasks)
DvarInitSignal.clear(); {
for (const auto& func : tasks)
{
func();
}
tasks = {}; // Only called once. Clear
});
Utils::Hook::Call<void()>(0x60AD10)(); // Com_InitDvars 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() Events::Events()
{ {
Utils::Hook(0x625235, ClientDisconnect_Hk, HOOK_CALL).install()->quick(); // SV_FreeClient 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(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(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 class Events : public Component
{ {
public: public:
typedef void(ClientCallback)(int clientNum); using Callback = std::vector<std::function<void()>>;
typedef void(ClientConnectCallback)(Game::client_s* cl); using ClientConnectCallback = std::vector<std::function<void(Game::client_s* cl)>>;
typedef void(Callback)(); using ClientCallback = std::vector<std::function<void(int clientNum)>>;
Events(); Events();
// Server side // Server side
static void OnClientDisconnect(const Utils::Slot<ClientCallback>& callback); static void OnClientDisconnect(const std::function<void(int clientNum)>& callback);
// Server side // Server side
static void OnClientConnect(const Utils::Slot<ClientConnectCallback>& callback); static void OnClientConnect(const std::function<void(Game::client_s* cl)>& callback);
// Client side // 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) // Client & Server (triggered once)
static void OnSVInit(const Utils::Slot<Callback>& callback); static void OnSVInit(const std::function<void()>& callback);
// Client & Server (triggered once) // 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: private:
static Utils::Signal<ClientCallback> ClientDisconnectSignal; static Utils::Concurrency::Container<ClientCallback> ClientDisconnectTasks_;
static Utils::Signal<ClientConnectCallback> ClientConnectSignal; static Utils::Concurrency::Container<ClientConnectCallback> ClientConnectTasks_;
static Utils::Signal<Callback> SteamDisconnectSignal; static Utils::Concurrency::Container<Callback> SteamDisconnectTasks_;
static Utils::Signal<Callback> ShutdownSystemSignal; static Utils::Concurrency::Container<Callback> ShutdownSystemTasks_;
static Utils::Signal<Callback> ClientInitSignal; static Utils::Concurrency::Container<Callback> ClientInitTasks_;
static Utils::Signal<Callback> ServerInitSignal; static Utils::Concurrency::Container<Callback> ServerInitTasks_;
static Utils::Signal<Callback> DvarInitSignal; static Utils::Concurrency::Container<Callback> DvarInitTasks_;
static Utils::Concurrency::Container<Callback> NetworkInitTasks_;
static void ClientDisconnect_Hk(int clientNum); static void ClientDisconnect_Hk(int clientNum);
static void SV_UserinfoChanged_Hk(Game::client_s* cl); static void SV_UserinfoChanged_Hk(Game::client_s* cl);
@ -46,5 +49,8 @@ namespace Components
static void CL_InitOnceForAllClients_HK(); static void CL_InitOnceForAllClients_HK();
static void SV_Init_Hk(); static void SV_Init_Hk();
static void Com_InitDvars_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); }, Scheduler::Pipeline::RENDERER);
} }
Command::Add("loadzone", [](Command::Params* params) Command::Add("loadzone", [](const Command::Params* params)
{ {
if (params->size() < 2) return; if (params->size() < 2) return;
@ -621,7 +621,7 @@ namespace Components
Game::DB_LoadXAssets(&info, 1, true); Game::DB_LoadXAssets(&info, 1, true);
}); });
Command::Add("awaitDatabase", [](Command::Params*) Command::Add("awaitDatabase", []()
{ {
Logger::Print("Waiting for database...\n"); Logger::Print("Waiting for database...\n");
while (!Game::Sys_IsDatabaseReady()) std::this_thread::sleep_for(100ms); while (!Game::Sys_IsDatabaseReady()) std::this_thread::sleep_for(100ms);

View File

@ -5,6 +5,7 @@
#include <proto/friends.pb.h> #include <proto/friends.pb.h>
#pragma warning(pop) #pragma warning(pop)
#include "Events.hpp"
#include "Friends.hpp" #include "Friends.hpp"
#include "Materials.hpp" #include "Materials.hpp"
#include "Node.hpp" #include "Node.hpp"
@ -461,21 +462,6 @@ namespace Components
Friends::CurrentFriend = index; 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 Friends::GetGame(SteamID user)
{ {
int appId = 0; 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::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"); 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 // Hook Live_ShowFriendsList
Utils::Hook(0x4D6C70, []() Utils::Hook(0x4D6C70, []()
{ {

View File

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

View File

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

View File

@ -166,12 +166,12 @@ namespace Components::GSC
if (pName != nullptr) if (pName != nullptr)
{ {
const auto name = Utils::String::ToLower(*pName); 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; *type = funcs.type;
return func.actionFunc; return funcs.actionFunc;
} }
} }
} }
@ -193,12 +193,12 @@ namespace Components::GSC
if (pName != nullptr) if (pName != nullptr)
{ {
const auto name = Utils::String::ToLower(*pName); 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; *type = meths.type;
return meth.actionFunc; return meths.actionFunc;
} }
} }
} }
@ -262,7 +262,7 @@ namespace Components::GSC
auto* ent = &Game::g_entities[entref.entnum]; auto* ent = &Game::g_entities[entref.entnum];
if (!ent->client) 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; return nullptr;
} }

View File

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

View File

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

View File

@ -1,4 +1,7 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include <Components/Modules/Events.hpp>
#include "ScriptExtension.hpp" #include "ScriptExtension.hpp"
#include "Script.hpp" #include "Script.hpp"
@ -28,9 +31,15 @@ namespace Components::GSC
void ScriptExtension::GetReplacedPos(const char* pos) 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() ScriptExtension::ScriptExtension()
{ {
AddFunctions(); AddFunctions();
AddMethods();
Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x61E933, 1); Utils::Hook::Nop(0x61E933, 1);

View File

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

View File

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

View File

@ -1235,11 +1235,11 @@ namespace Components
if (Game::Key_IsCatcherActive(localClientNum, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated) 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; 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; keyState.locSelInputState = Game::LOC_SEL_INPUT_CONFIRM;
} }
@ -1760,7 +1760,7 @@ namespace Components
return Game::GPAD_MAP_NONE; 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) if (params->size() < 4)
{ {
@ -1796,7 +1796,7 @@ namespace Components
Gamepad_BindAxis(0, physicalAxis, virtualAxis, mapping); Gamepad_BindAxis(0, physicalAxis, virtualAxis, mapping);
} }
void Gamepad::Axis_Unbindall_f(Command::Params*) void Gamepad::Axis_Unbindall_f()
{ {
auto& gamePadGlobal = gamePadGlobals[0]; 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*>(); const auto* stickConfigName = gpad_sticksConfig.get<const char*>();
Game::Cbuf_AddText(0, Utils::String::VA("exec %s\n", stickConfigName)); 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*>(); const auto* buttonConfigName = gpad_buttonConfig.get<const char*>();
Game::Cbuf_AddText(0, Utils::String::VA("exec %s\n", buttonConfigName)); 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) if (Game::cgArray[0].nextSnap)
{ {
@ -1926,7 +1926,7 @@ namespace Components
continue; 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; (*keys)[keyCount++] = keyNum;
@ -1946,7 +1946,7 @@ namespace Components
continue; 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; (*keys)[keyCount++] = keyNum;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -838,7 +838,7 @@ namespace Components
Utils::Hook(0x5A9D51, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick(); Utils::Hook(0x5A9D51, Maps::LoadMapLoadscreenStub, HOOK_CALL).install()->quick();
Utils::Hook(0x5B34DD, 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([] Scheduler::Once([]
{ {
@ -852,7 +852,7 @@ namespace Components
Utils::Hook(0x4A7251, Maps::LoadNewMapCommand, HOOK_CALL).install()->quick(); 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! // Conflicts with Theater's SV map rotation check, but this one is safer!
Utils::Hook(0x5AA91C, Maps::RotateCheckStub, HOOK_CALL).install()->quick(); 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. // make Com_Error and similar go back to main_text instead of menu_xboxlive.
Utils::Hook::SetString(0x6FC790, "main_text"); Utils::Hook::SetString(0x6FC790, "main_text");
Command::Add("openmenu", [](Command::Params* params) Command::Add("openmenu", [](const Command::Params* params)
{ {
if (params->size() != 2) if (params->size() != 2)
{ {
@ -845,7 +845,7 @@ namespace Components
Game::Menus_OpenByName(Game::uiContext, params->get(1)); Game::Menus_OpenByName(Game::uiContext, params->get(1));
}); });
Command::Add("reloadmenus", []([[maybe_unused]] Command::Params* params) Command::Add("reloadmenus", []()
{ {
// Close all menus // Close all menus
Game::Menus_CloseAll(Game::uiContext); 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 // Define custom menus here
Add("ui_mp/changelog.menu"); Add("ui_mp/changelog.menu");
Add("ui_mp/theater_menu.menu"); Add("ui_mp/theater_menu.menu");

View File

@ -39,7 +39,8 @@ namespace Components
Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(const std::string& name) Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(const std::string& name)
{ {
Utils::Memory::Allocator allocator; 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()) if (!model.exists())
{ {
@ -57,9 +58,18 @@ namespace Components
} }
#endif #endif
if (ZoneBuilder::IsEnabled())
{
Logger::Print("Loading model surface {} at path \"{}\" failed!", name, path);
}
else
{
Logger::Error(Game::ERR_FATAL, "Loading model {} failed!", name); Logger::Error(Game::ERR_FATAL, "Loading model {} failed!", name);
} }
return nullptr;
}
Game::CModelHeader header; Game::CModelHeader header;
if (!model.read(&header, sizeof header)) if (!model.read(&header, sizeof header))
{ {

View File

@ -2,7 +2,6 @@
namespace Components namespace Components
{ {
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
// Packet interception // Packet interception
std::unordered_map<std::string, Network::networkCallback> Network::CL_Callbacks; std::unordered_map<std::string, Network::networkCallback> Network::CL_Callbacks;
std::unordered_map<std::string, Network::networkRawCallback> Network::CL_RawCallbacks; 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); 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) 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! // 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! // EDIT: Most 3rd party tools expect a line break, so let's use that instead!
std::string packet; std::string packet;
packet.append(command); packet.append(command);
packet.append("\n", 1); packet.push_back('\n');
packet.append(data); packet.append(data);
Send(type, target, packet); Send(type, target, packet);
@ -228,12 +222,6 @@ namespace Components
BroadcastRange(100, 65536, data); BroadcastRange(100, 65536, data);
} }
void Network::NetworkStart()
{
StartupSignal();
StartupSignal.clear();
}
std::uint16_t Network::GetPort() std::uint16_t Network::GetPort()
{ {
assert((*Game::port)); assert((*Game::port));
@ -241,16 +229,6 @@ namespace Components
return static_cast<std::uint16_t>((*Game::port)->current.unsignedInt); 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() __declspec(naked) void Network::PacketErrorCheck()
{ {
__asm __asm
@ -303,7 +281,7 @@ namespace Components
return false; 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 }; auto target = Address{ address };
handler->second(target, data); handler->second(target, data);
@ -365,9 +343,6 @@ namespace Components
// Parse port as short in Net_AddrToString // Parse port as short in Net_AddrToString
Utils::Hook::Set<const char*>(0x4698E3, "%u.%u.%u.%u:%hu"); Utils::Hook::Set<const char*>(0x4698E3, "%u.%u.%u.%u:%hu");
// Install startup handler
Utils::Hook(0x4FD4D4, NetworkStartStub, HOOK_JUMP).install()->quick();
// Prevent recvfrom error spam // Prevent recvfrom error spam
Utils::Hook(0x46531A, PacketErrorCheck, HOOK_JUMP).install()->quick(); 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::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 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) OnClientPacket("resolveAddress", []([[maybe_unused]] const Address& address, [[maybe_unused]] const std::string& data)
{ {
SendRaw(address, address.getString()); 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); 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; return;
} }
char buffer[2048]{}; char printBuf[2048]{};
Game::I_strncpyz(clc->serverMessage, data.data(), sizeof(clc->serverMessage)); const auto* s = Game::MSG_ReadBigString(msg);
Game::Com_sprintf(buffer, sizeof(buffer), "%s", data.data()); Game::I_strncpyz(clc->serverMessage, s, sizeof(clc->serverMessage));
Game::Com_PrintMessage(Game::CON_CHANNEL_CLIENT, buffer, 0); Game::Com_sprintf(printBuf, sizeof(printBuf), "%s", s);
Game::Com_PrintMessage(Game::CON_CHANNEL_CLIENT, printBuf, false);
}); });
} }
} }

View File

@ -46,8 +46,6 @@ namespace Components
Game::netadr_t address; Game::netadr_t address;
}; };
typedef void(CallbackRaw)();
using networkCallback = std::function<void(Address&, const std::string&)>; using networkCallback = std::function<void(Address&, const std::string&)>;
using networkRawCallback = std::function<void(Game::netadr_t*, Game::msg_t* msg)>; using networkRawCallback = std::function<void(Game::netadr_t*, Game::msg_t* msg)>;
@ -55,8 +53,6 @@ namespace Components
static std::uint16_t GetPort(); static std::uint16_t GetPort();
static void OnStart(const Utils::Slot<CallbackRaw>& callback);
// Send quake-styled binary data // Send quake-styled binary data
static void Send(const Address& target, const std::string& data); static void Send(const Address& target, const std::string& data);
static void Send(Game::netsrc_t type, const Address& target, const std::string& data); static void Send(Game::netsrc_t type, const Address& target, const std::string& data);
@ -77,13 +73,9 @@ namespace Components
static void OnClientPacketRaw(const std::string& command, const networkRawCallback& callback); static void OnClientPacketRaw(const std::string& command, const networkRawCallback& callback);
private: private:
static Utils::Signal<CallbackRaw> StartupSignal;
static std::unordered_map<std::string, networkCallback> CL_Callbacks; static std::unordered_map<std::string, networkCallback> CL_Callbacks;
static std::unordered_map<std::string, networkRawCallback> CL_RawCallbacks; static std::unordered_map<std::string, networkRawCallback> CL_RawCallbacks;
static void NetworkStart();
static void NetworkStartStub();
static void PacketErrorCheck(); static void PacketErrorCheck();
static bool CL_HandleCommand(Game::netadr_t* address, const char* command, Game::msg_t* message); 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::recursive_mutex Node::Mutex;
std::vector<Node::Entry> Node::Nodes; 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)); 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()) if (!this->lastResponse.has_value())
{ {
@ -37,7 +37,7 @@ namespace Components
return false; return false;
} }
bool Node::Entry::requiresRequest() bool Node::Entry::requiresRequest() const
{ {
return (!this->isDead() && (!this->lastRequest.has_value() || this->lastRequest->elapsed(NODE_HALFLIFE))); return (!this->isDead() && (!this->lastRequest.has_value() || this->lastRequest->elapsed(NODE_HALFLIFE)));
} }
@ -49,7 +49,9 @@ namespace Components
Session::Send(this->address, "nodeListRequest"); Session::Send(this->address, "nodeListRequest");
Node::SendList(this->address); Node::SendList(this->address);
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Sent request to {}", this->address.getString()); Logger::Debug("Sent request to {}", this->address.getString());
#endif
} }
void Node::Entry::reset() 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 (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; WasIngame = true;
return; // don't run while ingame because it can still cause lag spikes on lower end PCs 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) for (auto i = Node::Nodes.begin(); i != Node::Nodes.end();++i)
{ {
@ -170,7 +172,8 @@ namespace Components
i->lastRequest.reset(); i->lastRequest.reset();
i->lastResponse.reset(); i->lastResponse.reset();
} }
wasIngame = false;
WasIngame = false;
} }
static Utils::Time::Interval frameLimit; static Utils::Time::Interval frameLimit;
@ -216,7 +219,9 @@ namespace Components
Proto::Node::List list; Proto::Node::List list;
if (!list.ParseFromString(data)) return; if (!list.ParseFromString(data)) return;
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Received response from {}", address.getString()); Logger::Debug("Received response from {}", address.getString());
#endif
std::lock_guard _(Node::Mutex); std::lock_guard _(Node::Mutex);
@ -234,12 +239,16 @@ namespace Components
{ {
if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && !ServerList::UseMasterServer && list.protocol() == PROTOCOL) if (!Dedicated::IsEnabled() && ServerList::IsOnlineList() && !ServerList::UseMasterServer && list.protocol() == PROTOCOL)
{ {
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Inserting {} into the serverlist", address.getString()); Logger::Debug("Inserting {} into the serverlist", address.getString());
#endif
ServerList::InsertRequest(address); ServerList::InsertRequest(address);
} }
else else
{ {
#ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Dropping serverlist insertion for {}", address.getString()); Logger::Debug("Dropping serverlist insertion for {}", address.getString());
#endif
} }
for (auto& node : Node::Nodes) for (auto& node : Node::Nodes)
@ -303,7 +312,7 @@ namespace Components
{ {
Scheduler::Once([=] Scheduler::Once([=]
{ {
#ifdef DEBUG_NODE #ifdef NODE_SYSTEM_DEBUG
Logger::Debug("Sending {} nodeListResponse length to {}\n", nodeListData.length(), address.getCString()); Logger::Debug("Sending {} nodeListResponse length to {}\n", nodeListData.length(), address.getCString());
#endif #endif
Session::Send(address, "nodeListResponse", nodeListData); Session::Send(address, "nodeListResponse", nodeListData);
@ -344,7 +353,7 @@ namespace Components
Scheduler::OnGameInitialized(loadNodes, Scheduler::Pipeline::MAIN); Scheduler::OnGameInitialized(loadNodes, Scheduler::Pipeline::MAIN);
Command::Add("listnodes", [](Command::Params*) Command::Add("listnodes", [](const Command::Params*)
{ {
Logger::Print("Nodes: {}\n", Node::Nodes.size()); 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; 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> lastRequest;
std::optional<Utils::Time::Point> lastResponse; std::optional<Utils::Time::Point> lastResponse;
bool isValid(); [[nodiscard]] bool isValid() const;
bool isDead(); [[nodiscard]] bool isDead() const;
bool requiresRequest(); [[nodiscard]] bool requiresRequest() const;
void sendRequest(); void sendRequest();
void reset(); void reset();
@ -44,7 +44,7 @@ namespace Components
private: private:
static std::recursive_mutex Mutex; static std::recursive_mutex Mutex;
static std::vector<Entry> Nodes; static std::vector<Entry> Nodes;
static bool wasIngame; static bool WasIngame;
static void HandleResponse(Network::Address address, const std::string& data); static void HandleResponse(Network::Address address, const std::string& data);

View File

@ -9,6 +9,7 @@
#include "Party.hpp" #include "Party.hpp"
#include "ServerList.hpp" #include "ServerList.hpp"
#include "Stats.hpp" #include "Stats.hpp"
#include "TextRenderer.hpp"
#include "Voice.hpp" #include "Voice.hpp"
#include <version.hpp> #include <version.hpp>
@ -293,7 +294,7 @@ namespace Components
Utils::Hook::Xor<DWORD>(0x4D376D, Game::DVAR_LATCH); Utils::Hook::Xor<DWORD>(0x4D376D, Game::DVAR_LATCH);
Utils::Hook::Xor<DWORD>(0x5E3789, 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) if (params->size() < 2)
{ {
@ -311,7 +312,7 @@ namespace Components
} }
}); });
Command::Add("reconnect", [](Command::Params*) Command::Add("reconnect", []()
{ {
Connect(Container.target); Connect(Container.target);
}); });
@ -493,7 +494,7 @@ namespace Components
{ {
ConnectError("Invalid map or gametype."); 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."); 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; 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 if (Container.matchType == 1) // Party
{ {

View File

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

View File

@ -369,31 +369,6 @@ namespace Components
// remove limit on IWD file loading // remove limit on IWD file loading
Utils::Hook::Set<BYTE>(0x642BF3, 0xEB); 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 // Fix stats sleeping
Utils::Hook::Set<BYTE>(0x6832BA, 0xEB); Utils::Hook::Set<BYTE>(0x6832BA, 0xEB);
Utils::Hook::Set<BYTE>(0x4BD190, 0xC3); Utils::Hook::Set<BYTE>(0x4BD190, 0xC3);
@ -522,7 +497,7 @@ namespace Components
Command::Add("unlockstats", QuickPatch::UnlockStats); Command::Add("unlockstats", QuickPatch::UnlockStats);
Command::Add("dumptechsets", [](Command::Params* param) Command::Add("dumptechsets", [](const Command::Params* param)
{ {
if (param->size() != 2) if (param->size() != 2)
{ {

View File

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

View File

@ -124,7 +124,7 @@ namespace Components
Utils::Hook(0x631640, GetMenuBuffer, HOOK_JUMP).install()->quick(); Utils::Hook(0x631640, GetMenuBuffer, HOOK_JUMP).install()->quick();
Utils::Hook(0x463500, Com_LoadInfoString_Hk, 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) if (params->size() < 2)
{ {
@ -140,7 +140,7 @@ namespace Components
return; 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) if (data)
{ {

View File

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

View File

@ -29,12 +29,6 @@ namespace Components
return size; 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) long Security::AtolAdjustPlayerLimit(const char* string)
{ {
return std::min<long>(std::atol(string), 18); return std::min<long>(std::atol(string), 18);
@ -140,7 +134,6 @@ namespace Components
// Exploit fixes // Exploit fixes
Utils::Hook(0x414D92, Msg_ReadBitsCompressCheckSV, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommands Utils::Hook(0x414D92, Msg_ReadBitsCompressCheckSV, HOOK_CALL).install()->quick(); // SV_ExecuteClientCommands
Utils::Hook(0x4A9F56, Msg_ReadBitsCompressCheckCL, HOOK_CALL).install()->quick(); // CL_ParseServerMessage 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>(0x412370, 0xC3); // SV_SteamAuthClient
Utils::Hook::Set<std::uint8_t>(0x5A8C70, 0xC3); // CL_HandleRelayPacket 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); static int Msg_ReadBitsCompressCheckCL(const unsigned char* from, unsigned char* to, int size);
private: private:
static int SV_CanReplaceServerCommand_Hk(Game::client_s* client, const char* cmd);
static long AtolAdjustPlayerLimit(const char* string); static long AtolAdjustPlayerLimit(const char* string);
static void SelectStringTableEntryInDvar_Stub(); static void SelectStringTableEntryInDvar_Stub();

View File

@ -7,7 +7,7 @@ namespace Components
public: public:
ServerCommands(); 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); static void OnCommand(std::int32_t cmd, const serverCommandHandler& callback);
private: private:

View File

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

View File

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

View File

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

View File

@ -158,25 +158,13 @@ namespace Components
// 15 or more custom classes // 15 or more custom classes
Utils::Hook::Set<BYTE>(0x60A2FE, NUM_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; return;
} }
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& filename, bool* /*restrict*/) AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& filename, bool* /*restrict*/)
{ {
// Only intercept playerdatadef loading // 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 // Store asset
Game::StructuredDataDefSet* data = asset.structuredDataDefSet; Game::StructuredDataDefSet* data = asset.structuredDataDefSet;

View File

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

View File

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

View File

@ -1,5 +1,7 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "Chat.hpp" #include "Chat.hpp"
#include "Events.hpp"
#include "Voice.hpp" #include "Voice.hpp"
namespace Components namespace Components
@ -320,6 +322,7 @@ namespace Components
auto* clc = Game::CL_GetLocalClientConnection(0); auto* clc = Game::CL_GetLocalClientConnection(0);
if (!Game::NET_CompareBaseAdr(clc->serverAddress, *address)) if (!Game::NET_CompareBaseAdr(clc->serverAddress, *address))
{ {
Logger::Debug("Ignoring stray 'v' network message from '{}'", Game::NET_AdrToString(*address));
return; return;
} }
@ -390,7 +393,7 @@ namespace Components
Events::OnSteamDisconnect(CL_ClearMutedList); Events::OnSteamDisconnect(CL_ClearMutedList);
Events::OnClientDisconnect(SV_UnmuteClient); Events::OnClientDisconnect(SV_UnmuteClient);
Events::OnClientConnect([](Game::client_s* cl) -> void Events::OnClientConnect([](const Game::client_s* cl) -> void
{ {
if (Chat::IsMuted(cl)) 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); auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
const auto* weapon = Game::Scr_GetString(0); const auto* weapon = Game::Scr_GetString(0);
@ -578,23 +578,47 @@ namespace Components
Game::Player_SwitchToWeapon(ent); 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() 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); const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
ent->client->ps.weapCommon.weapFlags |= Game::PWF_DISABLE_WEAPON_PICKUP; 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); const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
ent->client->ps.weapCommon.weapFlags &= ~Game::PWF_DISABLE_WEAPON_PICKUP; 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() Weapon::Weapon()

View File

@ -35,7 +35,8 @@ namespace Components
static void WeaponEntCanBeGrabbed_Stub(); 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(); static void AddScriptMethods();
}; };

View File

@ -18,6 +18,8 @@ namespace Components
volatile bool ZoneBuilder::CommandThreadTerminate = false; volatile bool ZoneBuilder::CommandThreadTerminate = false;
std::thread ZoneBuilder::CommandThread; std::thread ZoneBuilder::CommandThread;
iw4of::api ZoneBuilder::ExporterAPI(GetExporterAPIParams());
std::string ZoneBuilder::DumpingZone{};
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0), ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
// Reserve 100MB by default. // Reserve 100MB by default.
@ -757,6 +759,23 @@ namespace Components
return header; 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 ZoneBuilder::Zone::getIW4OfApiParams()
{ {
iw4of::params_t params{}; iw4of::params_t params{};
@ -815,7 +834,7 @@ namespace Components
void* data = Utils::Memory::GetAllocator()->allocate(size); void* data = Utils::Memory::GetAllocator()->allocate(size);
std::memcpy(data, *loadDef, size); std::memcpy(data, *loadDef, size);
image->texture.loadDef = reinterpret_cast<Game::GfxImageLoadDef *>(data); image->texture.loadDef = static_cast<Game::GfxImageLoadDef*>(data);
return 0; return 0;
} }
@ -857,7 +876,7 @@ namespace Components
return GetCurrentThreadId() == Utils::Hook::Get<DWORD>(0x1CDE7FC); 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 }, { "code_pre_gfx_mp", Game::DB_ZONE_CODE, 0 },
{ "localized_code_pre_gfx_mp", Game::DB_ZONE_CODE_LOC, 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 } { "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() void ZoneBuilder::Com_Quitf_t()
{ {
ExitProcess(0); ExitProcess(0);
@ -938,15 +946,7 @@ namespace Components
Command::Add("quit", ZoneBuilder::Com_Quitf_t); Command::Add("quit", ZoneBuilder::Com_Quitf_t);
// now load default assets and shaders // now load default assets and shaders
if (FastFiles::Exists("defaults") && FastFiles::Exists("techsets"))
{
Game::DB_LoadXAssets(baseZones, ARRAYSIZE(baseZones), 0); 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);
}
Logger::Print("Waiting for fastiles to load...\n"); Logger::Print("Waiting for fastiles to load...\n");
while (!Game::Sys_IsDatabaseReady()) 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-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-buildall: builds all zones in zone_source\n");
Logger::Print("\t-verifyzone [zone]: loads and verifies the specified zone\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-listassets [assettype]: lists all loaded assets of the specified type\n");
Logger::Print("\t-quit: quits the program\n"); Logger::Print("\t-quit: quits the program\n");
Logger::Print(" --------------------------------------------------------------------------------\n"); Logger::Print(" --------------------------------------------------------------------------------\n");
@ -1097,6 +1098,55 @@ namespace Components
return file; 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() ZoneBuilder::ZoneBuilder()
{ {
// ReSharper disable CppStaticAssertFailure // ReSharper disable CppStaticAssertFailure
@ -1200,7 +1250,7 @@ namespace Components
// don't remap techsets // don't remap techsets
Utils::Hook::Nop(0x5BC791, 5); 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()) if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
{ {
@ -1209,9 +1259,47 @@ namespace Components
OutputDebugStringA(Utils::String::Format("%s\n", name)); OutputDebugStringA(Utils::String::Format("%s\n", name));
#endif #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; if (params->size() < 2) return;
@ -1250,7 +1338,7 @@ namespace Components
Logger::Print("\n"); Logger::Print("\n");
}); });
Command::Add("buildzone", [](Command::Params* params) Command::Add("buildzone", [](const Command::Params* params)
{ {
if (params->size() < 2) return; if (params->size() < 2) return;
@ -1260,7 +1348,7 @@ namespace Components
Zone(zoneName).build(); 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 path = std::format("{}\\zone_source", (*Game::fs_basepath)->current.string);
auto zoneSources = FileSystem::GetSysFileList(path, "csv", false); 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_source/techsets");
Utils::IO::CreateDir("zone/techsets"); Utils::IO::CreateDir("zone/techsets");
@ -1580,7 +1668,7 @@ namespace Components
Zone("techsets/techsets").build(); Zone("techsets/techsets").build();
}); });
Command::Add("listassets", [](Command::Params* params) Command::Add("listassets", [](const Command::Params* params)
{ {
if (params->size() < 2) return; if (params->size() < 2) return;
Game::XAssetType type = Game::DB_GetXAssetNameType(params->get(1)); 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; 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; Game::XZoneInfo info;
info.name = nullptr; info.name = nullptr;
@ -1619,7 +1707,7 @@ namespace Components
AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded 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*) 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); header.material->info.name, header.material->info.sortKey & 0xFF, header.material->info.gameFlags & 0xFF, header.material->stateFlags & 0xFF);
}, nullptr, false); }, 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 std::vector<std::pair<Game::XAssetType, std::string>> EndAssetTrace();
static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder); static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder);
static void RefreshExporterWorkDirectory();
static iw4of::api* GetExporter();
private: private:
static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image); static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
@ -155,6 +158,8 @@ namespace Components
static bool IsThreadMainThreadHook(); static bool IsThreadMainThreadHook();
static Game::Sys_File Sys_CreateFile_Stub(const char* dir, const char* filename); 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 Com_Quitf_t();
static void CommandThreadCallback(); static void CommandThreadCallback();
@ -164,5 +169,7 @@ namespace Components
static volatile bool CommandThreadTerminate; static volatile bool CommandThreadTerminate;
static std::thread CommandThread; 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> #include <zlib.h>
@ -2947,6 +2947,9 @@ namespace Components
iw4Map->mapEnts = &codolMapEnts; iw4Map->mapEnts = &codolMapEnts;
memcpy(&iw4Map->smodelNodeCount, &codolMap->smodelNodeCount, 48); memcpy(&iw4Map->smodelNodeCount, &codolMap->smodelNodeCount, 48);
// unused on IW4
iw4Map->numLeafSurfaces = 0;
AssetHandler::Relocate(&cancerMap->info.numCPlanes, &iw4Map->planeCount, 8); AssetHandler::Relocate(&cancerMap->info.numCPlanes, &iw4Map->planeCount, 8);
AssetHandler::Relocate(&cancerMap->numStaticModels, &iw4Map->numStaticModels, 8); AssetHandler::Relocate(&cancerMap->numStaticModels, &iw4Map->numStaticModels, 8);
AssetHandler::Relocate(&cancerMap->info.numMaterials, &iw4Map->numMaterials, 24); AssetHandler::Relocate(&cancerMap->info.numMaterials, &iw4Map->numMaterials, 24);
@ -3482,7 +3485,7 @@ namespace Components
if (ZoneBuilder::IsEnabled()) if (ZoneBuilder::IsEnabled())
{ {
Command::Add("decryptImages", [](Command::Params*) Command::Add("decryptImages", []()
{ {
auto images = FileSystem::GetSysFileList("iw4x/images", "iwi"); auto images = FileSystem::GetSysFileList("iw4x/images", "iwi");
Logger::Print("decrypting {} images...\n", images.size()); Logger::Print("decrypting {} images...\n", images.size());
@ -3516,7 +3519,7 @@ namespace Components
Logger::Print("decrypted {} images!\n", images.size()); 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"); auto sounds = FileSystem::GetSysFileList("iw4x/sound", "iwi");
Logger::Print("decrypting {} sounds...\n", sounds.size()); Logger::Print("decrypting {} sounds...\n", sounds.size());
@ -3653,13 +3656,6 @@ namespace Components
Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick(); Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick();
Utils::Hook(0x471A39, Zones::LoadWindowImage, HOOK_JUMP).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 ) #pragma optimize( "", on )

View File

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

View File

@ -9,6 +9,7 @@ namespace Game
Com_Error_t Com_Error = Com_Error_t(0x4B22D0); Com_Error_t Com_Error = Com_Error_t(0x4B22D0);
Com_Printf_t Com_Printf = Com_Printf_t(0x402500); 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_PrintError_t Com_PrintError = Com_PrintError_t(0x4F8C70);
Com_PrintWarning_t Com_PrintWarning = Com_PrintWarning_t(0x4E0200); Com_PrintWarning_t Com_PrintWarning = Com_PrintWarning_t(0x4E0200);
Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830); 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, ...); typedef void(*Com_Printf_t)(int channel, const char* fmt, ...);
extern Com_Printf_t Com_Printf; 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, ...); typedef void(*Com_PrintError_t)(int channel, const char* fmt, ...);
extern Com_PrintError_t Com_PrintError; 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_ReadLong_t MSG_ReadLong = MSG_ReadLong_t(0x4C9550);
MSG_ReadShort_t MSG_ReadShort = MSG_ReadShort_t(0x40BDD0); MSG_ReadShort_t MSG_ReadShort = MSG_ReadShort_t(0x40BDD0);
MSG_ReadInt64_t MSG_ReadInt64 = MSG_ReadInt64_t(0x4F1850); 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_ReadStringLine_t MSG_ReadStringLine = MSG_ReadStringLine_t(0x4FEF30);
MSG_WriteByte_t MSG_WriteByte = MSG_WriteByte_t(0x48C520); MSG_WriteByte_t MSG_WriteByte = MSG_WriteByte_t(0x48C520);
MSG_WriteData_t MSG_WriteData = MSG_WriteData_t(0x4F4120); 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_strncpyz_t I_strncpyz = I_strncpyz_t(0x4D6F80);
I_CleanStr_t I_CleanStr = I_CleanStr_t(0x4AD470); I_CleanStr_t I_CleanStr = I_CleanStr_t(0x4AD470);
I_isdigit_t I_isdigit = I_isdigit_t(0x4E71E0);
XNAddrToString_t XNAddrToString = XNAddrToString_t(0x452690); XNAddrToString_t XNAddrToString = XNAddrToString_t(0x452690);
@ -318,8 +319,6 @@ namespace Game
infoParm_t* infoParams = reinterpret_cast<infoParm_t*>(0x79D260); // Count 0x1E infoParm_t* infoParams = reinterpret_cast<infoParm_t*>(0x79D260); // Count 0x1E
clientState_t* clcState = reinterpret_cast<clientState_t*>(0xB2C540);
GfxScene* scene = reinterpret_cast<GfxScene*>(0x6944914); GfxScene* scene = reinterpret_cast<GfxScene*>(0x6944914);
Console* con = reinterpret_cast<Console*>(0x9FCCF8); 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) int SEH_GetLocalizedTokenReference(char* token, const char* reference, const char* messageType, msgLocErrType_t errType)
{ {
static DWORD SEH_GetLocalizedTokenReference_t = 0x629BB0; static DWORD SEH_GetLocalizedTokenReference_t = 0x629BB0;
auto answer = 0; auto result = 0;
__asm __asm
{ {
@ -1168,11 +1167,11 @@ namespace Game
push token push token
call SEH_GetLocalizedTokenReference_t call SEH_GetLocalizedTokenReference_t
add esp, 0x4 add esp, 0x4
mov answer, eax mov result, eax
popad popad
} }
return answer; return result;
} }
void Player_SwitchToWeapon(gentity_s* player) void Player_SwitchToWeapon(gentity_s* player)

View File

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

View File

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

View File

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

View File

@ -2,15 +2,11 @@
#ifndef RC_INVOKED #ifndef RC_INVOKED
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _USE_MATH_DEFINES #define _USE_MATH_DEFINES
// Requires Visual Leak Detector plugin: http://vld.codeplex.com/
#define VLD_FORCE_ENABLE
//#include <vld.h>
#include <Windows.h> #include <Windows.h>
#include <WinSock2.h> #include <WinSock2.h>
#include <ShlObj.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", ~timeGetTime() ^ GenerateInt());
pos += sprintf_s(&buffer[pos], sizeof(buffer) - pos, "%X", 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() std::uint32_t Rand::GenerateInt()
@ -62,13 +62,13 @@ namespace Utils
if (!key.isValid()) return {}; if (!key.isValid()) return {};
std::uint8_t buffer[512]; std::uint8_t buffer[512];
DWORD length = sizeof(buffer); unsigned long length = sizeof(buffer);
ltc_mp = ltm_desc; ltc_mp = ltm_desc;
register_prng(&sprng_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()); 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) bool ECC::VerifyMessage(Key key, const std::string& message, const std::string& signature)
@ -78,7 +78,9 @@ namespace Utils
ltc_mp = ltm_desc; ltc_mp = ltm_desc;
int result = 0; 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 #pragma endregion
@ -104,7 +106,7 @@ namespace Utils
if (!key.isValid()) return {}; if (!key.isValid()) return {};
std::uint8_t buffer[512]; std::uint8_t buffer[512];
DWORD length = sizeof(buffer); unsigned long length = sizeof(buffer);
register_prng(&sprng_desc); register_prng(&sprng_desc);
register_hash(&sha1_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()); 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) 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_process(&state, data, length);
tiger_done(&state, buffer); 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; if (!hex) return hash;
return String::DumpHex(hash, {}); return String::DumpHex(hash, {});
@ -209,7 +211,7 @@ namespace Utils
sha1_process(&state, data, length); sha1_process(&state, data, length);
sha1_done(&state, buffer); 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; if (!hex) return hash;
return String::DumpHex(hash, {}); return String::DumpHex(hash, {});
@ -233,7 +235,7 @@ namespace Utils
sha256_process(&state, data, length); sha256_process(&state, data, length);
sha256_done(&state, buffer); 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; if (!hex) return hash;
return String::DumpHex(hash, {}); return String::DumpHex(hash, {});
@ -257,7 +259,7 @@ namespace Utils
sha512_process(&state, data, length); sha512_process(&state, data, length);
sha512_done(&state, buffer); 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; if (!hex) return hash;
return String::DumpHex(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> using Slot = std::function<T>;
template <typename T> template <typename T>
class Signal class Signal