Merge pull request #964 from XLabsProject/develop

Release r4208
This commit is contained in:
Edo 2023-04-22 19:04:21 +02:00 committed by GitHub
commit fcec8b2779
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 218 additions and 148 deletions

View File

@ -24,7 +24,7 @@ jobs:
- Release - Release
steps: steps:
- name: Check out files - name: Check out files
uses: actions/checkout@v3.3.0 uses: actions/checkout@v3.5.2
with: with:
submodules: true submodules: true
fetch-depth: 0 fetch-depth: 0
@ -72,7 +72,7 @@ jobs:
# Set up committer info and GPG key # Set up committer info and GPG key
- name: Install SSH key - name: Install SSH key
uses: shimataro/ssh-key-action@v2.5.0 uses: shimataro/ssh-key-action@v2.5.1
with: with:
key: ${{ secrets.XLABS_MASTER_SSH_PRIVATE_KEY }} key: ${{ secrets.XLABS_MASTER_SSH_PRIVATE_KEY }}
known_hosts: 'just-a-placeholder-so-we-dont-get-errors' known_hosts: 'just-a-placeholder-so-we-dont-get-errors'

View File

@ -2,9 +2,26 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/).
## r4190 - 2023-04-19 ## r4208 - 2023-04-22
### Changed
- `Noclip` GSC method does not require `sv_cheats` to be set to "1" for it to work (#962)
- `Ufo` GSC method does not require `sv_cheats` to be set to "1" for it to work (#962)
### Fixed
- Fix `InfoString` output (#961)
- Fix parsing of the server info (client-side) (#953)
- Fix bug in the /info TCP endpoint (#955)
### Known issues
- Sound issue fix is experimental as the bug is not fully understood.
## r4193 - 2023-04-19
### Added ### Added
@ -14,11 +31,13 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Changed ### Changed
- `sv_mapRotationCurrent` supports `exec` directive for executing cfg scripts from the `game_settings` folder - `sv_mapRotationCurrent` supports `exec` directive for executing cfg scripts from the `game_settings` folder (#916)
- `SetPing` GSC method is now deprecated.
### Fixed ### Fixed
- `sv_privatePassword` will work as intended (#908) - `sv_privatePassword` will work as intended (#908)
- Fix crash when loading bots.txt file (#927)
### Known issues ### Known issues

17
SECURITY.md Normal file
View File

@ -0,0 +1,17 @@
## iw4x-client Security
We take security very seriously at XLabsProject. We welcome any peer review of our 100% free software source code to ensure nobody's IW4x clients or servers are ever compromised or hacked.
### Where should I report security issues?
In order to give the community time to respond and upgrade we strongly urge you report all security issues privately.
Please e-mail MauriceHeumann@gmail.com directly to provide details and repro steps.
### Other key people
In the exceptional case that you do not receive a response within a reasonable time frame from our lead developer, please contact any of the following people:
- Future/diamante0018: iw4x-sp@proton.me
We will try respond as soon as possible, but please note:
### This is an entirely free software project. It is maintained in our spare time.
### We cannot guarantee quick responses but we very much appreciate your discretion when reporting security vulnerabilities.

View File

@ -17,19 +17,6 @@ function cstrquote(value)
return result return result
end end
-- Converts tags in "vX.X.X" format to an array of numbers {X,X,X}.
-- In the case where the format does not work fall back to old {4,2,REVISION}.
function vertonumarr(value, vernumber)
vernum = {}
for num in string.gmatch(value, "%d+") do
table.insert(vernum, tonumber(num))
end
if #vernum < 3 then
return {4,2,tonumber(vernumber)}
end
return vernum
end
dependencies = { dependencies = {
basePath = "./deps" basePath = "./deps"
} }
@ -108,7 +95,7 @@ newaction {
newaction { newaction {
trigger = "generate-buildinfo", trigger = "generate-buildinfo",
description = "Sets up build information file like version.h.", description = "Sets up build information file. Output will be stored in version.h.",
onWorkspace = function(wks) onWorkspace = function(wks)
-- get revision number via git -- get revision number via git
local proc = assert(io.popen("git rev-list --count HEAD", "r")) local proc = assert(io.popen("git rev-list --count HEAD", "r"))
@ -129,6 +116,26 @@ newaction {
proc = assert(io.popen("git describe --tags --abbrev=0")) proc = assert(io.popen("git describe --tags --abbrev=0"))
local tagName = assert(proc:read('*l')) local tagName = assert(proc:read('*l'))
-- get current branch name
proc = assert(io.popen("git branch --show-current"))
local branchName = proc:read('*l')
-- branch for ci
if branchName == nil or branchName == '' then
proc = assert(io.popen("git show -s --pretty=%d HEAD"))
local branchInfo = proc:read('*l')
m = string.match(branchInfo, ".+,.+, ([^)]+)")
if m ~= nil then
branchName = m
end
end
if branchName == nil then
branchName = "develop"
end
print("Detected branch: " .. branchName)
-- get old version number from version.hpp if any -- get old version number from version.hpp if any
local oldVersion = "(none)" local oldVersion = "(none)"
local oldVersionHeader = io.open(wks.location .. "/src/version.h", "r") local oldVersionHeader = io.open(wks.location .. "/src/version.h", "r")
@ -157,9 +164,16 @@ newaction {
versionHeader:write("#define GIT_DESCRIBE " .. gitDescribeOutputQuoted .. "\n") versionHeader:write("#define GIT_DESCRIBE " .. gitDescribeOutputQuoted .. "\n")
versionHeader:write("#define GIT_DIRTY " .. revDirty .. "\n") versionHeader:write("#define GIT_DIRTY " .. revDirty .. "\n")
versionHeader:write("#define GIT_TAG " .. cstrquote(tagName) .. "\n") versionHeader:write("#define GIT_TAG " .. cstrquote(tagName) .. "\n")
versionHeader:write("#define GIT_BRANCH " .. cstrquote(branchName) .. "\n")
versionHeader:write("\n") versionHeader:write("\n")
versionHeader:write("// New revision definition. Will be used from now on\n") versionHeader:write("// New revision definition. Will be used from now on\n")
versionHeader:write("#define REVISION " .. revNumber .. "\n") versionHeader:write("#define REVISION " .. revNumber .. "\n")
versionHeader:write("#define REVISION_STR \"r" .. revNumber .. "\"\n")
if branchName == "develop" then
versionHeader:write("#define EXPERIMENTAL_BUILD" .. "\n")
end
versionHeader:write("\n") versionHeader:write("\n")
versionHeader:write("// Alias definitions\n") versionHeader:write("// Alias definitions\n")
versionHeader:write("#define VERSION GIT_DESCRIBE\n") versionHeader:write("#define VERSION GIT_DESCRIBE\n")

View File

@ -13,11 +13,11 @@ namespace Components
{ {
constexpr std::size_t MAX_NAME_LENGTH = 16; constexpr std::size_t MAX_NAME_LENGTH = 16;
std::vector<Bots::botData> Bots::BotNames;
const Game::dvar_t* Bots::sv_randomBotNames; const Game::dvar_t* Bots::sv_randomBotNames;
const Game::dvar_t* Bots::sv_replaceBots; const Game::dvar_t* Bots::sv_replaceBots;
std::size_t Bots::botDataIndex;
struct BotMovementInfo struct BotMovementInfo
{ {
std::int32_t buttons; // Actions std::int32_t buttons; // Actions
@ -54,27 +54,21 @@ namespace Components
{ "activate", Game::CMD_BUTTON_ACTIVATE }, { "activate", Game::CMD_BUTTON_ACTIVATE },
}; };
void Bots::RandomizeBotNames() std::vector<Bots::botData> Bots::LoadBotNames()
{ {
std::random_device rd; std::vector<botData> result;
std::mt19937 gen(rd());
std::ranges::shuffle(BotNames, gen);
}
void Bots::LoadBotNames()
{
FileSystem::File bots("bots.txt"); FileSystem::File bots("bots.txt");
if (!bots.exists()) if (!bots.exists())
{ {
return; return result;
} }
auto data = Utils::String::Split(bots.getBuffer(), '\n'); auto data = Utils::String::Split(bots.getBuffer(), '\n');
for (auto& entry : data) for (auto& entry : data)
{ {
// Take into account for CR line endings // Take into account CR line endings
Utils::String::Replace(entry, "\r", ""); Utils::String::Replace(entry, "\r", "");
// Remove whitespace // Remove whitespace
Utils::String::Trim(entry); Utils::String::Trim(entry);
@ -100,38 +94,41 @@ namespace Components
entry = entry.substr(0, MAX_NAME_LENGTH - 1); entry = entry.substr(0, MAX_NAME_LENGTH - 1);
BotNames.emplace_back(entry, clanAbbrev); result.emplace_back(entry, clanAbbrev);
} }
if (sv_randomBotNames->current.enabled) return result;
{
RandomizeBotNames();
}
} }
int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port) int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
{ {
static std::size_t botId = 0; // Loop over the BotNames vector
static bool loadedNames = false; // Load file only once
std::string botName; std::string botName;
std::string clanName; std::string clanName;
if (!loadedNames) static const auto botNames = []() -> std::vector<botData>
{ {
loadedNames = true; auto names = LoadBotNames();
LoadBotNames();
}
if (!BotNames.empty()) if (sv_randomBotNames->current.enabled)
{
std::random_device rd;
std::mt19937 gen(rd());
std::ranges::shuffle(names, gen);
}
return names;
}();
if (!botNames.empty())
{ {
botId %= BotNames.size(); botDataIndex %= botNames.size();
const auto index = botId++; const auto index = botDataIndex++;
botName = BotNames[index].first; botName = botNames[index].first;
clanName = BotNames[index].second; clanName = botNames[index].second;
} }
else else
{ {
botName = std::format("bot{}", ++botId); botName = std::format("bot{}", num);
clanName = "BOT"s; clanName = "BOT"s;
} }

View File

@ -11,13 +11,13 @@ namespace Components
private: private:
using botData = std::pair< std::string, std::string>; using botData = std::pair< std::string, std::string>;
static std::vector<botData> BotNames;
static const Game::dvar_t* sv_randomBotNames; static const Game::dvar_t* sv_randomBotNames;
static const Game::dvar_t* sv_replaceBots; static const Game::dvar_t* sv_replaceBots;
static void RandomizeBotNames(); static std::size_t botDataIndex;
static void LoadBotNames();
static std::vector<botData> LoadBotNames();
static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port); static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
static void Spawn(unsigned int count); static void Spawn(unsigned int count);

View File

@ -40,42 +40,45 @@ namespace Components
{ {
Utils::Hook::Call<void(int)>(0x4EFF80)(localClientNum); Utils::Hook::Call<void(int)>(0x4EFF80)(localClientNum);
if (Branding::CGDrawVersion.get<bool>()) if (CGDrawVersion.get<bool>())
{ {
Branding::CG_DrawVersion(); CG_DrawVersion();
} }
} }
const char* Branding::GetBuildNumber() const char* Branding::GetBuildNumber()
{ {
return VERSION " latest " __DATE__ " " __TIME__; #ifdef EXPERIMENTAL_BUILD
return REVISION_STR "-develop latest " __DATE__ " " __TIME__;
#else
return REVISION_STR " latest " __DATE__ " " __TIME__;
#endif
} }
const char* Branding::GetVersionString() const char* Branding::GetVersionString()
{ {
// IW4x is technically a beta // IW4x is technically a beta
return Utils::String::VA("%s %s build %s %s", return Utils::String::VA("%s %s build %s %s",
BUILD_TYPE, "(Beta)", Branding::GetBuildNumber(), reinterpret_cast<const char*>(0x7170A0)); BUILD_TYPE, "(Beta)", GetBuildNumber(), reinterpret_cast<const char*>(0x7170A0));
} }
void Branding::Dvar_SetVersionString(const Game::dvar_t* dvar, [[maybe_unused]] const char* value) void Branding::Dvar_SetVersionString(const Game::dvar_t* dvar, [[maybe_unused]] const char* value)
{ {
const auto* result = Branding::GetVersionString(); const auto* result = GetVersionString();
Utils::Hook::Call<void(const Game::dvar_t*, const char*)>(0x4A9580)(dvar, result); Utils::Hook::Call<void(const Game::dvar_t*, const char*)>(0x4A9580)(dvar, result);
} }
// Branding this might be a good idea in case this LSP logging ever gets turned on for some reason // Branding this might be a good idea in case this LSP logging ever gets turned on for some reason
void Branding::MSG_WriteVersionStringHeader(Game::msg_t* msg, [[maybe_unused]] const char* string) void Branding::MSG_WriteVersionStringHeader(Game::msg_t* msg, [[maybe_unused]] const char* string)
{ {
const auto* result = Branding::GetVersionString(); const auto* result = GetVersionString();
Utils::Hook::Call<void(Game::msg_t*, const char*)>(0x463820)(msg, result); Utils::Hook::Call<void(Game::msg_t*, const char*)>(0x463820)(msg, result);
} }
Game::dvar_t* Branding::Dvar_RegisterUIBuildLocation(const char* dvarName, [[maybe_unused]] float x, Game::dvar_t* Branding::Dvar_RegisterUIBuildLocation(const char* dvarName, [[maybe_unused]] float x,
[[maybe_unused]] float y, float min, float max, [[maybe_unused]] int flags, const char* description) [[maybe_unused]] float y, float min, float max, [[maybe_unused]] int flags, const char* description)
{ {
return Game::Dvar_RegisterVec2(dvarName, -60.0f, return Game::Dvar_RegisterVec2(dvarName, -60.0f, 472.0f, min, max, Game::DVAR_ROM, description);
474.0f, min, max, Game::DVAR_ROM, description);
} }
void Branding::RegisterBrandingDvars() void Branding::RegisterBrandingDvars()
@ -95,50 +98,58 @@ namespace Components
Branding::Branding() Branding::Branding()
{ {
Branding::RegisterBrandingDvars(); RegisterBrandingDvars();
// UI version string // UI version string
Utils::Hook::Set<const char*>(0x43F73B, "IW4x - " GIT_TAG); Utils::Hook::Set<const char*>(0x43F73B, "IW4x - " REVISION_STR);
// Short version dvar // Short version dvar
Utils::Hook::Set<const char*>(0x60BD91, GIT_TAG); Utils::Hook::Set<const char*>(0x60BD91, REVISION_STR);
// Com_Init_Try_Block_Function // Com_Init_Try_Block_Function
Utils::Hook::Set<const char*>(0x60BAF4, BUILD_TYPE); Utils::Hook::Set<const char*>(0x60BAF4, BUILD_TYPE);
Utils::Hook::Set<const char*>(0x60BAEf, GIT_TAG); #ifdef EXPERIMENTAL_BUILD
Utils::Hook::Set<const char*>(0x60BAEf, REVISION_STR "-develop");
#else
Utils::Hook::Set<const char*>(0x60BAEf, REVISION_STR);
#endif
Utils::Hook::Set<const char*>(0x60BAE5, __DATE__); Utils::Hook::Set<const char*>(0x60BAE5, __DATE__);
// G_InitGame // G_InitGame
Utils::Hook::Set<const char*>(0x48EBA1, __DATE__); Utils::Hook::Set<const char*>(0x48EBA1, __DATE__);
Utils::Hook(0x4B12B0, Branding::GetBuildNumber, HOOK_JUMP).install()->quick(); Utils::Hook(0x4B12B0, GetBuildNumber, HOOK_JUMP).install()->quick();
// Version string color // Version string color
static Game::vec4_t buildLocColor = {1.0f, 1.0f, 1.0f, 0.8f}; static Game::vec4_t buildLocColor = {1.0f, 1.0f, 1.0f, 0.8f};
Utils::Hook::Set<float*>(0x43F710, buildLocColor); Utils::Hook::Set<float*>(0x43F710, buildLocColor);
// Place ui version string to bottom right corner (ui_buildlocation) // Place ui version string to bottom right corner (ui_buildlocation)
Utils::Hook(0x6310A0, Branding::Dvar_RegisterUIBuildLocation, HOOK_CALL).install()->quick(); // Dvar_RegisterVec2 Utils::Hook(0x6310A0, Dvar_RegisterUIBuildLocation, HOOK_CALL).install()->quick(); // Dvar_RegisterVec2
Utils::Hook(0x60BD81, Branding::Dvar_SetVersionString, HOOK_CALL).install()->quick(); Utils::Hook(0x60BD81, Dvar_SetVersionString, HOOK_CALL).install()->quick();
Utils::Hook(0x4DA842, Branding::MSG_WriteVersionStringHeader, HOOK_CALL).install()->quick(); Utils::Hook(0x4DA842, MSG_WriteVersionStringHeader, HOOK_CALL).install()->quick();
// Hook CG_DrawFullScreenDebugOverlays so we may render the version when it's appropriate // Hook CG_DrawFullScreenDebugOverlays so we may render the version when it's appropriate
Utils::Hook(0x5AC975, Branding::CG_DrawVersion_Hk, HOOK_CALL).install()->quick(); Utils::Hook(0x5AC975, CG_DrawVersion_Hk, HOOK_CALL).install()->quick();
// Console title // Console title
if (ZoneBuilder::IsEnabled()) if (ZoneBuilder::IsEnabled())
{ {
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" GIT_TAG "): ZoneBuilder"); Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" REVISION_STR "): ZoneBuilder");
} }
else if (Dedicated::IsEnabled()) else if (Dedicated::IsEnabled())
{ {
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" GIT_TAG "): Dedicated"); #ifdef EXPERIMENTAL_BUILD
Utils::Hook::Set<const char*>(0x4289E8, "IW4x " REVISION_STR "-develop: Dedicated");
#else
Utils::Hook::Set<const char*>(0x4289E8, "IW4x " REVISION_STR ": Dedicated");
#endif
} }
else else
{ {
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" GIT_TAG "): Console"); Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" REVISION_STR "): Console");
} }
} }
} }

View File

@ -9,11 +9,23 @@ namespace Components
{ {
std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> ClientCommand::HandlersSV; std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> ClientCommand::HandlersSV;
bool ClientCommand::CheatsEnabled;
ClientCommand::CheatsScopedLock::CheatsScopedLock()
{
CheatsEnabled = true;
}
ClientCommand::CheatsScopedLock::~CheatsScopedLock()
{
CheatsEnabled = false;
}
bool ClientCommand::CheatsOk(const Game::gentity_s* ent) bool ClientCommand::CheatsOk(const Game::gentity_s* ent)
{ {
const auto entNum = ent->s.number; const auto entNum = ent->s.number;
if (!(*Game::g_cheats)->current.enabled) if (!(*Game::g_cheats)->current.enabled && !CheatsEnabled)
{ {
Logger::Debug("Cheats are disabled!"); Logger::Debug("Cheats are disabled!");
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65)); Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
@ -351,12 +363,16 @@ namespace Components
GSC::Script::AddMethod("Noclip", [](const Game::scr_entref_t entref) // gsc: self Noclip(); GSC::Script::AddMethod("Noclip", [](const Game::scr_entref_t entref) // gsc: self Noclip();
{ {
auto* ent = GSC::Script::Scr_GetPlayerEntity(entref); auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
CheatsScopedLock cheatsLock;
Cmd_Noclip_f(ent, nullptr); Cmd_Noclip_f(ent, nullptr);
}); });
GSC::Script::AddMethod("Ufo", [](const Game::scr_entref_t entref) // gsc: self Ufo(); GSC::Script::AddMethod("Ufo", [](const Game::scr_entref_t entref) // gsc: self Ufo();
{ {
auto* ent = GSC::Script::Scr_GetPlayerEntity(entref); auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
CheatsScopedLock cheatsLock;
Cmd_UFO_f(ent, nullptr); Cmd_UFO_f(ent, nullptr);
}); });
} }
@ -502,6 +518,8 @@ namespace Components
// Hook call to ClientCommand in SV_ExecuteClientCommand so we may add custom commands // Hook call to ClientCommand in SV_ExecuteClientCommand so we may add custom commands
Utils::Hook(0x6259FA, ClientCommandStub, HOOK_CALL).install()->quick(); Utils::Hook(0x6259FA, ClientCommandStub, HOOK_CALL).install()->quick();
CheatsEnabled = false;
AddCheatCommands(); AddCheatCommands();
AddScriptFunctions(); AddScriptFunctions();
AddScriptMethods(); AddScriptMethods();

View File

@ -13,6 +13,15 @@ namespace Components
private: private:
static std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> HandlersSV; static std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> HandlersSV;
static bool CheatsEnabled;
class CheatsScopedLock
{
public:
CheatsScopedLock();
~CheatsScopedLock();
};
static void ClientCommandStub(int clientNum); static void ClientCommandStub(int clientNum);
static void AddCheatCommands(); static void AddCheatCommands();
static void AddDevelopmentCommands(); static void AddDevelopmentCommands();

View File

@ -96,14 +96,22 @@ namespace Components
} }
else if (IsWindow(GetWindow()) != FALSE) else if (IsWindow(GetWindow()) != FALSE)
{ {
SetWindowTextA(GetWindow(), Utils::String::VA("IW4x(" GIT_TAG ") : %s", hostname.data())); #ifdef EXPERIMENTAL_BUILD
SetWindowTextA(GetWindow(), Utils::String::Format("IW4x " REVISION_STR "-develop : %s", hostname));
#else
SetWindowTextA(GetWindow(), Utils::String::Format("IW4x " REVISION_STR " : %s", hostname));
#endif
} }
} }
void Console::ShowPrompt() void Console::ShowPrompt()
{ {
wattron(InputWindow, COLOR_PAIR(10) | A_BOLD); wattron(InputWindow, COLOR_PAIR(10) | A_BOLD);
wprintw(InputWindow, "%s> ", GIT_TAG); #ifdef EXPERIMENTAL_BUILD
wprintw(InputWindow, "%s-develop> ", REVISION_STR);
#else
wprintw(InputWindow, "%s> ", REVISION_STR);
#endif
} }
void Console::RefreshOutput() void Console::RefreshOutput()
@ -427,38 +435,15 @@ namespace Components
RefreshOutput(); RefreshOutput();
} }
HFONT CALLBACK Console::ReplaceFont( HFONT CALLBACK Console::ReplaceFont([[maybe_unused]] int cHeight, int cWidth, int cEscapement, int cOrientation, [[maybe_unused]] int cWeight, DWORD bItalic, DWORD bUnderline,
[[maybe_unused]] int cHeight, DWORD bStrikeOut, DWORD iCharSet, [[maybe_unused]] DWORD iOutPrecision, DWORD iClipPrecision, [[maybe_unused]] DWORD iQuality,
int cWidth, [[maybe_unused]] DWORD iPitchAndFamily, [[maybe_unused]] LPCSTR pszFaceName)
int cEscapement,
int cOrientation,
[[maybe_unused]] int cWeight,
DWORD bItalic,
DWORD bUnderline,
DWORD bStrikeOut,
DWORD iCharSet,
[[maybe_unused]] DWORD iOutPrecision,
DWORD iClipPrecision,
[[maybe_unused]] DWORD iQuality,
[[maybe_unused]] DWORD iPitchAndFamily,
[[maybe_unused]] LPCSTR pszFaceName)
{ {
HFONT font = CreateFontA( HFONT font = CreateFontA(12, cWidth, cEscapement, cOrientation, 700, bItalic,
12, bUnderline, bStrikeOut, iCharSet, OUT_RASTER_PRECIS,
cWidth, iClipPrecision, NONANTIALIASED_QUALITY, 0x31,
cEscapement, "Terminus (TTF)"
cOrientation, );
700,
bItalic,
bUnderline,
bStrikeOut,
iCharSet,
OUT_RASTER_PRECIS,
iClipPrecision,
NONANTIALIASED_QUALITY,
0x31,
"Terminus (TTF)"
); // Terminus (TTF)
return font; return font;
} }
@ -860,7 +845,11 @@ namespace Components
AssertOffset(Game::clientUIActive_t, keyCatchers, 0x9B0); AssertOffset(Game::clientUIActive_t, keyCatchers, 0x9B0);
// Console '%s: %s> ' string // Console '%s: %s> ' string
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x_MP: " GIT_TAG "> "); #ifdef EXPERIMENTAL_BUILD
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x MP: " REVISION_STR "-develop> ");
#else
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x MP: " REVISION_STR "> ");
#endif
// Patch console color // Patch console color
static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f }; static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f };

View File

@ -317,7 +317,7 @@ namespace Components
void Debug::Com_BugNameInc_f() void Debug::Com_BugNameInc_f()
{ {
char buf[260]{}; char buf[512]{};
if (std::strlen(BugName->current.string) < 4) if (std::strlen(BugName->current.string) < 4)
{ {

View File

@ -442,7 +442,7 @@ namespace Components
info["status"] = status.to_json(); info["status"] = status.to_json();
info["host"] = host.to_json(); info["host"] = host.to_json();
info["map_rotation"] = MapRotation::to_json(); info["map_rotation"] = MapRotation::to_json();
info["dedicated"] = Dedicated::com_dedicated->current.value; info["dedicated"] = Dedicated::com_dedicated->current.integer;
std::vector<nlohmann::json> players; std::vector<nlohmann::json> players;

View File

@ -137,6 +137,7 @@ namespace Components
"Dasfonia", "Dasfonia",
"Deity", "Deity",
"Dizzy", "Dizzy",
"Evan/Eve"
"HardNougat", "HardNougat",
"INeedGames", "INeedGames",
"JTAG", "JTAG",

View File

@ -386,7 +386,7 @@ namespace Components
info.set("bots", std::to_string(botCount)); info.set("bots", std::to_string(botCount));
info.set("sv_maxclients", std::to_string(maxClientCount)); info.set("sv_maxclients", std::to_string(maxClientCount));
info.set("protocol", std::to_string(PROTOCOL)); info.set("protocol", std::to_string(PROTOCOL));
info.set("version", GIT_TAG); info.set("version", REVISION_STR);
info.set("checksum", std::to_string(Game::Sys_Milliseconds())); info.set("checksum", std::to_string(Game::Sys_Milliseconds()));
info.set("mapname", Dvar::Var("mapname").get<std::string>()); info.set("mapname", Dvar::Var("mapname").get<std::string>());
info.set("isPrivate", *password ? "1" : "0"); info.set("isPrivate", *password ? "1" : "0");

View File

@ -58,7 +58,7 @@ namespace Components
{ {
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);
Dvar::Var("uiSi_Version").set(serverInfo->shortversion); Dvar::Var("uiSi_Version").set(serverInfo->version);
Dvar::Var("uiSi_SecurityLevel").set(serverInfo->securityLevel); Dvar::Var("uiSi_SecurityLevel").set(serverInfo->securityLevel);
Dvar::Var("uiSi_isPrivate").set(serverInfo->password ? "@MENU_YES" : "@MENU_NO"); Dvar::Var("uiSi_isPrivate").set(serverInfo->password ? "@MENU_YES" : "@MENU_NO");
Dvar::Var("uiSi_Hardcore").set(serverInfo->hardcore ? "@MENU_ENABLED" : "@MENU_DISABLED"); Dvar::Var("uiSi_Hardcore").set(serverInfo->hardcore ? "@MENU_ENABLED" : "@MENU_DISABLED");
@ -149,7 +149,7 @@ namespace Components
info.set("gamename", "IW4"); info.set("gamename", "IW4");
info.set("sv_maxclients", std::to_string(maxClientCount)); info.set("sv_maxclients", std::to_string(maxClientCount));
info.set("protocol", std::to_string(PROTOCOL)); info.set("protocol", std::to_string(PROTOCOL));
info.set("version", GIT_TAG); info.set("version", REVISION_STR);
info.set("version", (*Game::version)->current.string); info.set("version", (*Game::version)->current.string);
info.set("mapname", (*Game::sv_mapname)->current.string); info.set("mapname", (*Game::sv_mapname)->current.string);
info.set("isPrivate", *password ? "1" : "0"); info.set("isPrivate", *password ? "1" : "0");
@ -261,7 +261,7 @@ namespace Components
Dvar::Var("uiSi_ServerName").set(info.get("sv_hostname")); Dvar::Var("uiSi_ServerName").set(info.get("sv_hostname"));
Dvar::Var("uiSi_MaxClients").set(info.get("sv_maxclients")); Dvar::Var("uiSi_MaxClients").set(info.get("sv_maxclients"));
Dvar::Var("uiSi_Version").set(info.get("shortversion")); Dvar::Var("uiSi_Version").set(info.get("version"));
Dvar::Var("uiSi_SecurityLevel").set(info.get("sv_securityLevel")); Dvar::Var("uiSi_SecurityLevel").set(info.get("sv_securityLevel"));
Dvar::Var("uiSi_isPrivate").set(info.get("isPrivate") == "0" ? "@MENU_NO" : "@MENU_YES"); Dvar::Var("uiSi_isPrivate").set(info.get("isPrivate") == "0" ? "@MENU_NO" : "@MENU_YES");
Dvar::Var("uiSi_Hardcore").set(info.get("g_hardcore") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED"); Dvar::Var("uiSi_Hardcore").set(info.get("g_hardcore") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED");

View File

@ -522,7 +522,7 @@ namespace Components
server.hostname = info.get("hostname"); server.hostname = info.get("hostname");
server.mapname = info.get("mapname"); server.mapname = info.get("mapname");
server.gametype = info.get("gametype"); server.gametype = info.get("gametype");
server.shortversion = info.get("shortversion"); server.version = info.get("version");
server.mod = info.get("fs_game"); server.mod = info.get("fs_game");
server.matchType = std::strtol(info.get("matchtype").data(), nullptr, 10); server.matchType = std::strtol(info.get("matchtype").data(), nullptr, 10);
server.clients = std::strtol(info.get("clients").data(), nullptr, 10); server.clients = std::strtol(info.get("clients").data(), nullptr, 10);

View File

@ -1,8 +1,5 @@
#pragma once #pragma once
// This enables version filtering
#define VERSION_FILTER
namespace Components namespace Components
{ {
class ServerList : public Component class ServerList : public Component
@ -17,7 +14,7 @@ namespace Components
std::string mapname; std::string mapname;
std::string gametype; std::string gametype;
std::string mod; std::string mod;
std::string shortversion; std::string version;
std::size_t hash; std::size_t hash;
int clients; int clients;
int bots; int bots;

View File

@ -18,7 +18,12 @@ namespace Components
if (Flags::HasFlag("version")) if (Flags::HasFlag("version"))
{ {
printf("%s", "IW4x " VERSION " (built " __DATE__ " " __TIME__ ")\n"); printf("%s", "IW4x " VERSION " (built " __DATE__ " " __TIME__ ")\n");
#ifdef EXPERIMENTAL_BUILD
printf("Revision: %i - develop\n", REVISION);
#else
printf("Revision: %i\n", REVISION); printf("Revision: %i\n", REVISION);
#endif
ExitProcess(EXIT_SUCCESS); ExitProcess(EXIT_SUCCESS);
} }

View File

@ -23,18 +23,18 @@ namespace Components
// team switch and intermission. // team switch and intermission.
// //
std::mutex SoundMutexFix::SNDMutex; std::mutex SoundMutexFix::CloseStreamMutex;
void __stdcall SoundMutexFix::LockSoundMutex(int unk) void WINAPI SoundMutexFix::AIL_close_stream_Stub(int h_stream)
{ {
std::lock_guard lock(SNDMutex); std::lock_guard lock(CloseStreamMutex);
DWORD funcPtr = *reinterpret_cast<DWORD*>(0x6D7554); // AIL_close_stream const auto ptr = *reinterpret_cast<DWORD*>(0x6D7554); // AIL_close_stream
Utils::Hook::Call<void __stdcall(int)>(funcPtr)(unk); Utils::Hook::Call<void WINAPI(int)>(ptr)(h_stream);
} }
SoundMutexFix::SoundMutexFix() SoundMutexFix::SoundMutexFix()
{ {
Utils::Hook(0x689EFE, &LockSoundMutex, HOOK_JUMP).install()->quick(); Utils::Hook(0x689EFE, &AIL_close_stream_Stub, HOOK_JUMP).install()->quick();
} }
} }

View File

@ -9,7 +9,7 @@ namespace Components
SoundMutexFix(); SoundMutexFix();
private: private:
static std::mutex SNDMutex; static std::mutex CloseStreamMutex;
static void _stdcall LockSoundMutex(int unk); static void WINAPI AIL_close_stream_Stub(int h_stream);
}; };
} }

View File

@ -978,7 +978,7 @@ namespace Components
} }
Logger::Print(" --------------------------------------------------------------------------------\n"); Logger::Print(" --------------------------------------------------------------------------------\n");
Logger::Print(" IW4x ZoneBuilder - {}\n", VERSION); Logger::Print(" IW4x ZoneBuilder - {}\n", REVISION_STR);
Logger::Print(" Commands:\n"); Logger::Print(" Commands:\n");
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");

View File

@ -10,23 +10,22 @@ namespace Game::Engine
{ {
Sys_EnterCriticalSection(this->s_); Sys_EnterCriticalSection(this->s_);
this->hasOwnership_ = true; this->hasOwnership_ = true;
return;
}
if (type == SCOPED_CRITSECT_TRY)
{
this->hasOwnership_ = Sys_TryEnterCriticalSection(this->s_);
} }
else else
{ {
if (type == SCOPED_CRITSECT_TRY) if (type == SCOPED_CRITSECT_RELEASE)
{ {
this->hasOwnership_ = Sys_TryEnterCriticalSection(this->s_); Sys_LeaveCriticalSection(this->s_);
this->isScopedRelease_ = true;
} }
else
{
if (type == SCOPED_CRITSECT_RELEASE)
{
Sys_LeaveCriticalSection(this->s_);
this->isScopedRelease_ = true;
}
this->hasOwnership_ = false; this->hasOwnership_ = false;
}
} }
} }

View File

@ -69,12 +69,12 @@ BEGIN
#else #else
VALUE "FileDescription", "IW4 client modification" VALUE "FileDescription", "IW4 client modification"
#endif #endif
VALUE "FileVersion", GIT_TAG VALUE "FileVersion", REVISION_STR
VALUE "InternalName", "iw4x" VALUE "InternalName", "iw4x"
VALUE "LegalCopyright", "Copyright 2023 The XLabsProject Team. All rights reserved." VALUE "LegalCopyright", "Copyright 2023 The XLabsProject Team. All rights reserved."
VALUE "OriginalFilename", "iw4x.dll" VALUE "OriginalFilename", "iw4x.dll"
VALUE "ProductName", "IW4x" VALUE "ProductName", "IW4x"
VALUE "ProductVersion", GIT_TAG VALUE "ProductVersion", REVISION_STR
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -51,13 +51,9 @@ namespace Utils
std::string InfoString::build() const std::string InfoString::build() const
{ {
std::string infoString; std::string infoString;
auto first = true;
for (const auto& [key, value] : this->keyValuePairs_) for (const auto& [key, value] : this->keyValuePairs_)
{ {
if (first) first = false; infoString.append("\\");
else infoString.append("\\");
infoString.append(key); infoString.append(key);
infoString.append("\\"); infoString.append("\\");
infoString.append(value); infoString.append(value);

View File

@ -90,10 +90,8 @@ namespace Utils::String
[[nodiscard]] const char* Format(std::string_view fmt, Args&&... args) [[nodiscard]] const char* Format(std::string_view fmt, Args&&... args)
{ {
static thread_local std::string vaBuffer; static thread_local std::string vaBuffer;
vaBuffer.clear();
(SanitizeFormatArgs(args), ...); (SanitizeFormatArgs(args), ...);
std::vformat_to(std::back_inserter(vaBuffer), fmt, std::make_format_args(args...)); std::vformat(fmt, std::make_format_args(args...)).swap(vaBuffer);
return vaBuffer.data(); return vaBuffer.data();
} }