Merge pull request #535 from XLabsProject/develop

Release v2.1.0
This commit is contained in:
Maurice Heumann 2022-11-12 09:07:59 +01:00 committed by GitHub
commit 648ccc2c23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 6357 additions and 239 deletions

View File

@ -1,47 +0,0 @@
# AppVeyor CI configuration
version: "#{build} ({branch})"
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
PREMAKE_ACTION: vs2019
CI: 1
branches:
only:
- master
- develop
skip_branch_with_pr: true
# Doesn't work :(
#cache:
# - build
configuration:
- Debug
- Release
platform: x64
install:
- ps: |
Write-Host "Updating version information..." -ForegroundColor Cyan
Update-AppveyorBuild -Version $(& tools/premake5.exe version | select -Last 1)
- git submodule update --init --recursive
- ps: |
Write-Host "Generating project files with premake..." -ForegroundColor Cyan
& "./tools/premake5.exe" $env:PREMAKE_ACTION
Write-Host "Generated" -ForegroundColor Green
build:
project: build/s1x.sln
parallel: true
verbosity: minimal
artifacts:
- path: build/version.txt
- path: build/bin/**/s1x.exe
# - path: build/bin/**/*.exe
# - path: build/bin/**/*.pdb

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
init()
{
// define onteamselection callback function used in balanceteams()
level.onteamselection = ::set_team;
}
set_team(team)
{
if (team != self.pers["team"])
{
self.switching_teams = true;
self.joining_team = team;
self.leaving_team = self.pers["team"];
}
if (self.sessionstate == "playing")
{
self suicide();
}
maps\mp\gametypes\_menus::addtoteam(team);
maps\mp\gametypes\_menus::endrespawnnotify();
}

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit 9c4212aca46fb4fe5bda2921c2269d35d6d1860f
Subproject commit 517ed29228d18cf2c5004d10826090108e06f049

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit 8f2c237b8315a7d662e0e67d06807296a7bbe5ab
Subproject commit 0c03ed2f7497441ac0de232bda2e6b8cc041b2dc

2
deps/gsc-tool vendored

@ -1 +1 @@
Subproject commit 842f168a67878f18ea6a42b9d3ce56ff9fafff0d
Subproject commit 7d374025b7675bada64c247ebe9378dd335a33da

2
deps/lua vendored

@ -1 +1 @@
Subproject commit c954db39241a8b21d7b32b42b87a066b4708f969
Subproject commit be908a7d4d8130264ad67c5789169769f824c5d1

2
deps/sol2 vendored

@ -1 +1 @@
Subproject commit 0386513a2d59fefe448f4fc8742455ce1fc152ab
Subproject commit f81643aa0c0c507c0cd8400b8cfedc74a34a19f6

View File

@ -186,7 +186,7 @@ namespace command
std::string params_sv::join(const int index) const
{
std::string result = {};
std::string result;
for (auto i = index; i < this->size(); i++)
{

View File

@ -1,17 +1,16 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include "console.hpp"
#include "command.hpp"
#include "network.hpp"
#include "party.hpp"
#include "scheduler.hpp"
#include <utils/string.hpp>
#include <discord_rpc.h>
#include <component/party.hpp>
namespace discord
{

View File

@ -182,8 +182,7 @@ namespace dvar_cheats
constexpr auto value = false;
#endif
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED,
"Allow cheat commands and dvars on this server");
dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED, "Allow cheat commands and dvars on this server");
}
};
}

View File

@ -65,30 +65,13 @@ namespace filesystem
game::FS_Startup(gamename);
}
std::vector<std::filesystem::path> get_paths(const std::filesystem::path& path)
{
std::vector<std::filesystem::path> paths{};
const auto code = game::SEH_GetCurrentLanguageName();
paths.push_back(path);
paths.push_back(path / code);
return paths;
}
bool can_insert_path(const std::filesystem::path& path)
{
for (const auto& path_ : get_search_paths_internal())
const auto& paths = get_search_paths_internal();
return std::ranges::none_of(paths.cbegin(), paths.cend(), [path](const auto& elem)
{
if (path_ == path)
{
return false;
}
}
return true;
return elem == path;
});
}
}
@ -190,14 +173,10 @@ namespace filesystem
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
if (can_insert_path(path))
{
if (can_insert_path(path_))
{
console::info("[FS] Registering path '%s'\n", path_.generic_string().data());
get_search_paths_internal().push_front(path_);
}
console::info("[FS] Registering path '%s'\n", path.generic_string().data());
get_search_paths_internal().push_front(path);
}
}
@ -208,21 +187,17 @@ namespace filesystem
return;
}
const auto paths = get_paths(path);
for (const auto& path_ : paths)
auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.begin(); i != search_paths.end();)
{
auto& search_paths = get_search_paths_internal();
for (auto i = search_paths.begin(); i != search_paths.end();)
if (*i == path)
{
if (*i == path_)
{
console::info("[FS] Unregistering path '%s'\n", path_.generic_string().data());
i = search_paths.erase(i);
}
else
{
++i;
}
console::info("[FS] Unregistering path '%s'\n", path.generic_string().data());
i = search_paths.erase(i);
}
else
{
++i;
}
}
}

View File

@ -15,6 +15,9 @@ namespace fps
{
namespace
{
const game::dvar_t* cg_drawFPS;
const game::dvar_t* cg_drawPing;
float fps_color_good[4] = {0.6f, 1.0f, 0.0f, 1.0f};
float fps_color_ok[4] = {1.0f, 0.7f, 0.3f, 1.0f};
float fps_color_bad[4] = {1.0f, 0.3f, 0.3f, 1.0f};
@ -45,7 +48,7 @@ namespace fps
{
data->history[data->index % 32] = value;
data->instant = value;
data->min = 0x7FFFFFFF;
data->min = std::numeric_limits<int>::max();
data->max = 0;
data->average = 0.0f;
data->variance = 0.0f;
@ -93,11 +96,9 @@ namespace fps
void cg_draw_fps()
{
const auto* draw_fps = game::Dvar_FindVar("cg_drawFPS");
if (draw_fps && draw_fps->current.integer > 0)
if (cg_drawFPS && cg_drawFPS->current.integer > 0)
{
const auto fps = fps::get_fps();
const auto fps = get_fps();
auto* font = game::R_RegisterFont("fonts/consolefont");
if (!font) return;
@ -107,19 +108,18 @@ namespace fps
const auto scale = 1.0f;
const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 10.0f) - game::R_TextWidth(
fps_string, 0x7FFFFFFF, font) * scale;
fps_string, std::numeric_limits<int>::max(), font) * scale;
const auto y = font->pixelHeight * 1.2f;
const auto fps_color = fps >= 60 ? fps_color_good : (fps >= 30 ? fps_color_ok : fps_color_bad);
game::R_AddCmdDrawText(fps_string, 0x7FFFFFFF, font, x, y, scale, scale, 0.0f, fps_color, 6);
game::R_AddCmdDrawText(fps_string, std::numeric_limits<int>::max(), font, x, y, scale, scale, 0.0f, fps_color, 6);
}
}
void cg_draw_ping()
{
const auto* draw_ping = game::Dvar_FindVar("cg_drawPing");
if (draw_ping && draw_ping->current.integer > 0 && game::CL_IsCgameInitialized())
if (cg_drawPing->current.integer > 0 && game::CL_IsCgameInitialized())
{
auto* font = game::R_RegisterFont("fonts/consolefont");
if (!font) return;
@ -129,26 +129,25 @@ namespace fps
const auto scale = 1.0f;
const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 375.0f) - game::R_TextWidth(
ping_string, 0x7FFFFFFF, font) * scale;
ping_string, std::numeric_limits<int>::max(), font) * scale;
const auto y = font->pixelHeight * 1.2f;
game::R_AddCmdDrawText(ping_string, 0x7FFFFFFF, font, x, y, scale, scale, 0.0f, ping_color, 6);
game::R_AddCmdDrawText(ping_string, std::numeric_limits<int>::max(), font, x, y, scale, scale, 0.0f, ping_color, 6);
}
}
void cg_draw_fps_register_stub(const char* name, const char** _enum, const int value, unsigned int /*flags*/,
const char* desc)
const game::dvar_t* cg_draw_fps_register_stub(const char* dvar_name, const char** value_list, const int default_index, unsigned int /*flags*/, const char* description)
{
game::Dvar_RegisterEnum(name, _enum, value, game::DVAR_FLAG_SAVED, desc);
cg_drawFPS = game::Dvar_RegisterEnum(dvar_name, value_list, default_index, game::DVAR_FLAG_SAVED, description);
return cg_drawFPS;
}
}
int get_fps()
{
return static_cast<std::int32_t>(static_cast<float>(1000.0f / static_cast<float>(cg_perf.
average))
+ 9.313225746154785e-10);
return static_cast<std::int32_t>(static_cast<float>(1000.0f /
static_cast<float>(cg_perf.average)) + 9.313225746154785e-10);
}
class component final : public component_interface
@ -174,7 +173,7 @@ namespace fps
scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer);
if (game::environment::is_mp())
{
game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Choose to draw ping");
cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Choose to draw ping");
scheduler::loop(cg_draw_ping, scheduler::pipeline::renderer);
}

View File

@ -54,7 +54,7 @@ namespace game_log
va_list ap;
va_start(ap, fmt);
vsprintf_s(buffer, fmt, ap);
vsnprintf_s(buffer, _TRUNCATE, fmt, ap);
va_end(ap);

View File

@ -10,6 +10,8 @@
#include <utils/hook.hpp>
#include <utils/string.hpp>
using namespace utils::string;
namespace gsc
{
namespace
@ -20,6 +22,38 @@ namespace gsc
std::string unknown_function_error;
// Array count confirmed at 0x1409BE0D0
std::array<const char*, 27> var_typename =
{
"undefined",
"object",
"string",
"localized string",
"vector",
"float",
"int",
"codepos",
"precodepos",
"function",
"builtin function",
"builtin method",
"stack",
"animation",
"pre animation",
"thread",
"thread",
"thread",
"thread",
"struct",
"removed entity",
"entity",
"array",
"removed thread",
"<free>",
"thread list",
"endon list",
};
void scr_emit_function_stub(unsigned int filename, unsigned int thread_name, char* code_pos)
{
current_filename = filename;
@ -82,6 +116,23 @@ namespace gsc
return res;
}
unsigned int scr_get_object(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_POINTER)
{
return value->u.pointerValue;
}
scr_error(va("Type %s is not an object", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
unsigned int scr_get_const_string(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
@ -89,16 +140,146 @@ namespace gsc
auto* value = game::scr_VmPub->top - index;
if (game::Scr_CastString(value))
{
assert(value->type == game::SCRIPT_STRING);
assert(value->type == game::VAR_STRING);
return value->u.stringValue;
}
game::Scr_ErrorInternal();
}
scr_error(utils::string::va("Parameter %u does not exist", index + 1));
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
unsigned int scr_get_const_istring(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_ISTRING)
{
return value->u.stringValue;
}
scr_error(va("Type %s is not a localized string", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
void scr_validate_localized_string_ref(int parm_index, const char* token, int token_len)
{
assert(token);
assert(token_len >= 0);
if (token_len < 2)
{
return;
}
for (auto char_iter = 0; char_iter < token_len; ++char_iter)
{
if (!std::isalnum(static_cast<unsigned char>(token[char_iter])) && token[char_iter] != '_')
{
scr_error(va("Illegal localized string reference: %s must contain only alpha-numeric characters and underscores", token));
}
}
}
void scr_get_vector(unsigned int index, float* vector_value)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_VECTOR)
{
std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3]));
return;
}
scr_error(va("Type %s is not a vector", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
}
int scr_get_int(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_INTEGER)
{
return value->u.intValue;
}
scr_error(va("Type %s is not an int", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
float scr_get_float(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
auto* value = game::scr_VmPub->top - index;
if (value->type == game::VAR_FLOAT)
{
return value->u.floatValue;
}
if (value->type == game::VAR_INTEGER)
{
return static_cast<float>(value->u.intValue);
}
scr_error(va("Type %s is not a float", var_typename[value->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0.0f;
}
int scr_get_pointer_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER)
{
return static_cast<int>(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue));
}
scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type]));
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
int scr_get_type(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return (game::scr_VmPub->top - index)->type;
}
scr_error(va("Parameter %u does not exist", index + 1));
return 0;
}
const char* scr_get_type_name(unsigned int index)
{
if (index < game::scr_VmPub->outparamcount)
{
return var_typename[(game::scr_VmPub->top - index)->type];
}
scr_error(va("Parameter %u does not exist", index + 1));
return nullptr;
}
}
std::optional<std::pair<std::string, std::string>> find_function(const char* pos)
@ -134,8 +315,18 @@ namespace gsc
utils::hook::call(0x1403ED8E8, compile_error_stub); // LinkFile
utils::hook::call(0x1403ED9DB, find_variable_stub); // Scr_EmitFunction
// Restore basic error messages to scr functions
// Restore basic error messages for commonly used scr functions
utils::hook::jump(0x1403F8990, scr_get_object);
utils::hook::jump(0x1403F8510, scr_get_const_string);
utils::hook::jump(0x1403F82F0, scr_get_const_istring);
utils::hook::jump(0x140327870, scr_validate_localized_string_ref);
utils::hook::jump(0x1403F8EC0, scr_get_vector);
utils::hook::jump(0x1403F88D0, scr_get_int);
utils::hook::jump(0x1403F8820, scr_get_float);
utils::hook::jump(0x1403F8BA0, scr_get_pointer_type);
utils::hook::jump(0x1403F8D70, scr_get_type);
utils::hook::jump(0x1403F8DE0, scr_get_type_name);
}
void pre_destroy() override

View File

@ -275,7 +275,7 @@ namespace gsc
override_function("assert", &assert_cmd);
override_function("assertex", &assert_ex_cmd);
override_function("assertmsg", &assert_ex_cmd);
override_function("assertmsg", &assert_msg_cmd);
add_function("executecommand", []
{

View File

@ -242,6 +242,11 @@ namespace gsc
{
utils::hook::invoke<void>(0x140323F20);
if (game::VirtualLobby_Loaded())
{
return;
}
clear();
fastfiles::enum_assets(game::ASSET_TYPE_RAWFILE, [](const game::XAssetHeader header)
@ -276,11 +281,14 @@ namespace gsc
void g_load_structs_stub()
{
for (auto& function_handle : main_handles)
if (!game::VirtualLobby_Loaded())
{
console::info("Executing '%s::main'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(function_handle.second, 0);
game::RemoveRefToObject(thread);
for (auto& function_handle : main_handles)
{
console::info("Executing '%s::main'\n", function_handle.first.data());
const auto thread = game::Scr_ExecThread(function_handle.second, 0);
game::RemoveRefToObject(thread);
}
}
utils::hook::invoke<void>(0x1403380D0);
@ -290,6 +298,11 @@ namespace gsc
{
utils::hook::invoke<void>(0x140325B90);
if (game::VirtualLobby_Loaded())
{
return;
}
for (auto& function_handle : init_handles)
{
console::info("Executing '%s::init'\n", function_handle.first.data());
@ -322,6 +335,9 @@ namespace gsc
public:
void post_unpack() override
{
// Load our scripts with an uncompressed stack
utils::hook::call(SELECT_VALUE(0x14031ABB0, 0x1403F7380), db_get_raw_buffer_stub);
if (game::environment::is_sp())
{
return;
@ -357,9 +373,6 @@ namespace gsc
// GScr_LoadScripts
utils::hook::call(0x140330B97, gscr_post_load_scripts_stub);
// Load our scripts with an uncompressed stack
utils::hook::call(0x1403F7380, db_get_raw_buffer_stub);
// Exec script handles
utils::hook::call(0x1402F71AE, g_load_structs_stub);
utils::hook::call(0x1402F71C7, scr_load_level_stub);

View File

@ -1,6 +1,5 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game_console.hpp"

View File

@ -1,5 +1,7 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "motd.hpp"
#include "images.hpp"
@ -15,6 +17,7 @@ namespace motd
{
std::string motd_resource = utils::nt::load_resource(DW_MOTD);
std::future<std::optional<std::string>> motd_future;
std::string marketing_featured;
}
std::string get_text()
@ -30,21 +33,55 @@ namespace motd
return motd_resource;
}
utils::hook::detour marketing_get_message_hook;
bool marketing_get_message_stub(int /*controller_index*/, int /*location-id*/, char* message_text, int message_text_length)
{
if (marketing_featured.empty()) return false;
strncpy_s(message_text, message_text_length, marketing_featured.data(), _TRUNCATE);
return true;
}
class component final : public component_interface
{
public:
void post_load() override
{
motd_future = utils::http::get_data_async("https://xlabs.dev/s1/motd.txt");
std::thread([]()
std::thread([]
{
auto data = utils::http::get_data("https://xlabs.dev/s1/motd.png");
if(data)
if (data.has_value())
{
images::override_texture("iotd_image", data.value());
}
auto featured_optional = utils::http::get_data("https://xlabs.dev/s1/featured_msg.json");
if (featured_optional.has_value())
{
marketing_featured = featured_optional.value();
}
}).detach();
}
void post_unpack() override
{
if (game::environment::is_sp())
{
return;
}
// Not sure why but in S1x, client doesn't ask for maketing messages from demonware even though marketing_active set to true
marketing_get_message_hook.create(0x140126930, marketing_get_message_stub);
}
void pre_destroy() override
{
marketing_get_message_hook.clear();
}
};
}

View File

@ -10,6 +10,7 @@
#include "scheduler.hpp"
#include "notifies.hpp"
#include "scripting.hpp"
#include "game_log.hpp"
#include <utils/hook.hpp>
@ -164,10 +165,10 @@ namespace notifies
if (params[0] == "say"s || params[0] == "say_team"s)
{
std::string message(game::ConcatArgs(1));
std::string message(params.join(1));
auto msg_index = 0;
if (message[msg_index] == '\x15')
if (message[msg_index] == '\x1F')
{
msg_index = 1;
}
@ -179,7 +180,7 @@ namespace notifies
if (msg_index == 1)
{
// Overwrite / with \x15 only if present
// Overwrite / with \x1F only if present
message[msg_index] = message[msg_index - 1];
}
// Skip over the first character
@ -191,8 +192,17 @@ namespace notifies
const scripting::entity level{*game::levelEntityId};
const auto player = scripting::call("getEntByNum", {client_num}).as<scripting::entity>();
scripting::notify(level, params[0], {player, message});
scripting::notify(player, params[0], {message});
notify(level, params[0], {player, message});
notify(player, params[0], {message});
game_log::g_log_printf("%s;%s;%i;%s;%s\n",
params[0],
player.call("getguid").as<const char*>(),
client_num,
player.get("name").as<const char*>(),
message.data()
);
}, scheduler::pipeline::server);
if (hidden)
@ -235,7 +245,7 @@ namespace notifies
std::vector<sol::lua_value> args;
const auto top = game::scr_function_stack->top;
for (auto* value = top; value->type != game::SCRIPT_END; --value)
for (auto* value = top; value->type != game::VAR_PRECODEPOS; --value)
{
args.push_back(scripting::lua::convert(state, *value));
}

View File

@ -1,5 +1,7 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "party.hpp"
#include "console.hpp"
#include "command.hpp"
@ -28,6 +30,7 @@ namespace party
} connect_state;
std::string sv_motd;
int sv_maxclients;
void perform_game_initialization()
@ -277,7 +280,7 @@ namespace party
int server_client_count()
{
return party::sv_maxclients;
return sv_maxclients;
}
class component final : public component_interface
@ -431,7 +434,7 @@ namespace party
{
for (auto i = 0; i < *game::mp::svs_numclients; ++i)
{
scheduler::once([i, reason]()
scheduler::once([i, reason]
{
game::SV_KickClientNum(i, reason.data());
}, scheduler::pipeline::server);
@ -519,10 +522,10 @@ namespace party
utils::hook::call(0x14048811C, didyouknow_stub); // allow custom didyouknow based on sv_motd
network::on("getInfo", [](const game::netadr_s& target, const std::string_view& data)
network::on("getInfo", [](const game::netadr_s& target, const std::string& data)
{
utils::info_string info{};
info.set("challenge", std::string{data});
info.set("challenge", data);
info.set("gamename", "S1");
info.set("hostname", get_dvar_string("sv_hostname"));
info.set("gametype", get_dvar_string("g_gametype"));
@ -530,21 +533,26 @@ namespace party
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
info.set("mapname", get_dvar_string("mapname"));
info.set("isPrivate", get_dvar_string("g_password").empty() ? "0" : "1");
info.set("clients", utils::string::va("%i", get_client_count()));
info.set("bots", utils::string::va("%i", get_bot_count()));
info.set("sv_maxclients", utils::string::va("%i", *game::mp::svs_numclients));
info.set("protocol", utils::string::va("%i", PROTOCOL));
info.set("clients", std::to_string(get_client_count()));
info.set("bots", std::to_string(get_bot_count()));
info.set("sv_maxclients", std::to_string(*game::mp::svs_numclients));
info.set("protocol", std::to_string(PROTOCOL));
info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode()));
info.set("sv_running", utils::string::va("%i", get_dvar_bool("sv_running")));
info.set("dedicated", utils::string::va("%i", get_dvar_bool("dedicated")));
info.set("sv_running", std::to_string(get_dvar_bool("sv_running")));
info.set("dedicated", std::to_string(get_dvar_bool("dedicated")));
info.set("shortversion", SHORTVERSION);
network::send(target, "infoResponse", info.build(), '\n');
});
network::on("infoResponse", [](const game::netadr_s& target, const std::string_view& data)
if (game::environment::is_dedi())
{
const utils::info_string info{data};
return;
}
network::on("infoResponse", [](const game::netadr_s& target, const std::string& data)
{
const utils::info_string info(data);
server_list::handle_info_response(target, info);
if (connect_state.host != target)
@ -554,59 +562,67 @@ namespace party
if (info.get("challenge") != connect_state.challenge)
{
const auto str = "Invalid challenge.";
printf("%s\n", str);
game::Com_Error(game::ERR_DROP, str);
const auto* error_msg = "Invalid challenge.";
console::error("%s\n", error_msg);
game::Com_Error(game::ERR_DROP, "%s", error_msg);
return;
}
const auto gamename = info.get("gamename");
if (gamename != "S1"s)
{
const auto str = "Invalid gamename.";
printf("%s\n", str);
game::Com_Error(game::ERR_DROP, str);
const auto* error_msg = "Invalid gamename.";
console::error("%s\n", error_msg);
game::Com_Error(game::ERR_DROP, "%s", error_msg);
return;
}
const auto playmode = info.get("playmode");
if (game::CodPlayMode(std::atoi(playmode.data())) != game::Com_GetCurrentCoDPlayMode())
if (std::atoi(playmode.data()) != game::Com_GetCurrentCoDPlayMode())
{
const auto str = "Invalid playmode.";
printf("%s\n", str);
game::Com_Error(game::ERR_DROP, str);
const auto* error_msg = "Invalid playmode.";
console::error("%s\n", error_msg);
game::Com_Error(game::ERR_DROP, "%s", error_msg);
return;
}
const auto sv_running = info.get("sv_running");
if (!std::atoi(sv_running.data()))
if (sv_running != "1"s)
{
const auto str = "Server not running.";
printf("%s\n", str);
game::Com_Error(game::ERR_DROP, str);
const auto* error_msg = "Server not running.";
console::error("%s\n", error_msg);
game::Com_Error(game::ERR_DROP, "%s", error_msg);
return;
}
const auto mapname = info.get("mapname");
if (mapname.empty())
{
const auto str = "Invalid map.";
printf("%s\n", str);
game::Com_Error(game::ERR_DROP, str);
const auto* error_msg = "Invalid map.";
console::error("%s\n", error_msg);
game::Com_Error(game::ERR_DROP, "%s", error_msg);
return;
}
const auto gametype = info.get("gametype");
if (gametype.empty())
{
const auto str = "Invalid gametype.";
printf("%s\n", str);
game::Com_Error(game::ERR_DROP, str);
const auto* error_msg = "Invalid gametype.";
console::error("%s\n", error_msg);
game::Com_Error(game::ERR_DROP, "%s", error_msg);
return;
}
party::sv_motd = info.get("sv_motd");
party::sv_maxclients = std::stoi(info.get("sv_maxclients"));
sv_motd = info.get("sv_motd");
try
{
sv_maxclients = std::stoi(info.get("sv_maxclients"));
}
catch([[maybe_unused]] const std::exception& ex)
{
sv_maxclients = 1;
}
connect_to_party(target, mapname, gametype);
});

View File

@ -1,5 +1,4 @@
#pragma once
#include "game/game.hpp"
namespace party
{

View File

@ -52,7 +52,7 @@ namespace scripting
e.name = string;
e.entity = notify_list_owner_id;
for (auto* value = top; value->type != game::SCRIPT_END; --value)
for (auto* value = top; value->type != game::VAR_PRECODEPOS; --value)
{
e.arguments.emplace_back(*value);
}

View File

@ -1,11 +1,12 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "server_list.hpp"
#include "localized_strings.hpp"
#include "network.hpp"
#include "scheduler.hpp"
#include "party.hpp"
#include "game/game.hpp"
#include <utils/cryptography.hpp>
#include <utils/string.hpp>
@ -157,7 +158,7 @@ namespace server_list
void sort_serverlist()
{
std::stable_sort(servers.begin(), servers.end(), [](const server_info& a, const server_info& b)
std::ranges::stable_sort(servers, [](const server_info& a, const server_info& b)
{
if (a.clients == b.clients)
{
@ -310,21 +311,21 @@ namespace server_list
void handle_info_response(const game::netadr_s& address, const utils::info_string& info)
{
// Don't show servers that aren't dedicated!
const auto dedicated = std::atoi(info.get("dedicated").data());
if (!dedicated)
const auto dedicated = info.get("dedicated");
if (dedicated != "1"s)
{
return;
}
// Don't show servers that aren't running!
const auto sv_running = std::atoi(info.get("sv_running").data());
if (!sv_running)
const auto sv_running = info.get("sv_running");
if (sv_running != "1"s)
{
return;
}
// Only handle servers of the same playmode!
const auto playmode = game::CodPlayMode(std::atoi(info.get("playmode").data()));
const auto playmode = static_cast<game::CodPlayMode>(std::atoi(info.get("playmode").data()));
if (game::Com_GetCurrentCoDPlayMode() != playmode)
{
return;
@ -352,9 +353,9 @@ namespace server_list
server.map_name = game::UI_GetMapDisplayName(info.get("mapname").data());
server.game_type = game::UI_GetGameTypeDisplayName(info.get("gametype").data());
server.play_mode = playmode;
server.clients = atoi(info.get("clients").data());
server.max_clients = atoi(info.get("sv_maxclients").data());
server.bots = atoi(info.get("bots").data());
server.clients = std::atoi(info.get("clients").data());
server.max_clients = std::atoi(info.get("sv_maxclients").data());
server.bots = std::atoi(info.get("bots").data());
server.ping = std::min(now - start_time, 999);
server.in_game = 1;

View File

@ -1,6 +1,4 @@
#pragma once
#include "game/game.hpp"
#include <utils/info_string.hpp>
namespace server_list

View File

@ -90,7 +90,7 @@ namespace scripting
{
if (this->entity_id_)
{
game::AddRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
game::AddRefToValue(game::VAR_POINTER, {static_cast<int>(this->entity_id_)});
}
}
@ -98,7 +98,7 @@ namespace scripting
{
if (this->entity_id_)
{
game::RemoveRefToValue(game::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
game::RemoveRefToValue(game::VAR_POINTER, {static_cast<int>(this->entity_id_)});
}
}

View File

@ -30,7 +30,7 @@ namespace scripting
const auto field_str = game::SL_GetString(field.data(), 0);
const auto _ = gsl::finally([field_str]()
{
game::RemoveRefToValue(game::SCRIPT_STRING, {static_cast<int>(field_str)});
game::RemoveRefToValue(game::VAR_STRING, {static_cast<int>(field_str)});
});
const auto offset = game::FindVariable(class_id, field_str);

View File

@ -31,7 +31,7 @@ namespace scripting::lua
{
const auto var = game::scr_VarGlob->childVariableValue[i];
if (var.type == game::SCRIPT_NONE)
if (var.type == game::VAR_UNDEFINED)
{
current = var.nextSibling;
continue;
@ -125,7 +125,7 @@ namespace scripting::lua
notifies::set_lua_hook(index, function);
game::VariableValue func;
func.type = game::SCRIPT_FUNCTION;
func.type = game::VAR_FUNCTION;
func.u.codePosValue = index;
return func;
@ -208,7 +208,7 @@ namespace scripting::lua
game::VariableValue result{};
result.u = variable.u.u;
result.type = (game::scriptType_e)variable.type;
result.type = variable.type;
return convert(s, result);
};

View File

@ -56,7 +56,7 @@ namespace scripting::safe_execution
*game::g_script_error_level += 1;
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level]))
{
value->type = game::SCRIPT_NONE;
value->type = game::VAR_UNDEFINED;
value->u.intValue = 0;
*game::g_script_error_level -= 1;
return false;

View File

@ -17,7 +17,7 @@ namespace scripting
script_value::script_value(const int value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_INTEGER;
variable.type = game::VAR_INTEGER;
variable.u.intValue = value;
this->value_ = variable;
@ -26,7 +26,7 @@ namespace scripting
script_value::script_value(const unsigned int value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_INTEGER;
variable.type = game::VAR_INTEGER;
variable.u.uintValue = value;
this->value_ = variable;
@ -40,7 +40,7 @@ namespace scripting
script_value::script_value(const float value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_FLOAT;
variable.type = game::VAR_FLOAT;
variable.u.floatValue = value;
this->value_ = variable;
@ -54,7 +54,7 @@ namespace scripting
script_value::script_value(const char* value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_STRING;
variable.type = game::VAR_STRING;
variable.u.stringValue = game::SL_GetString(value, 0);
const auto _ = gsl::finally([&variable]()
@ -73,7 +73,7 @@ namespace scripting
script_value::script_value(const entity& value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_OBJECT;
variable.type = game::VAR_POINTER;
variable.u.pointerValue = value.get_entity_id();
this->value_ = variable;
@ -82,7 +82,7 @@ namespace scripting
script_value::script_value(const vector& value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_VECTOR;
variable.type = game::VAR_VECTOR;
variable.u.vectorValue = game::Scr_AllocVector(value);
const auto _ = gsl::finally([&variable]()
@ -100,7 +100,7 @@ namespace scripting
template <>
bool script_value::is<int>() const
{
return this->get_raw().type == game::SCRIPT_INTEGER;
return this->get_raw().type == game::VAR_INTEGER;
}
template <>
@ -140,7 +140,7 @@ namespace scripting
template <>
bool script_value::is<float>() const
{
return this->get_raw().type == game::SCRIPT_FLOAT;
return this->get_raw().type == game::VAR_FLOAT;
}
template <>
@ -158,7 +158,7 @@ namespace scripting
template <>
double script_value::get() const
{
return static_cast<double>(this->get_raw().u.floatValue);
return this->get_raw().u.floatValue;
}
/***************************************************************
@ -168,7 +168,7 @@ namespace scripting
template <>
bool script_value::is<const char*>() const
{
return this->get_raw().type == game::SCRIPT_STRING;
return this->get_raw().type == game::VAR_STRING;
}
template <>
@ -196,7 +196,7 @@ namespace scripting
template <>
bool script_value::is<std::vector<script_value>>() const
{
if (this->get_raw().type != game::SCRIPT_OBJECT)
if (this->get_raw().type != game::VAR_POINTER)
{
return false;
}
@ -204,7 +204,7 @@ namespace scripting
const auto id = this->get_raw().u.uintValue;
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
return type == game::SCRIPT_ARRAY;
return type == game::VAR_ARRAY;
}
/***************************************************************
@ -214,7 +214,7 @@ namespace scripting
template <>
bool script_value::is<std::map<std::string, script_value>>() const
{
if (this->get_raw().type != game::SCRIPT_OBJECT)
if (this->get_raw().type != game::VAR_POINTER)
{
return false;
}
@ -222,7 +222,7 @@ namespace scripting
const auto id = this->get_raw().u.uintValue;
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type;
return type == game::SCRIPT_STRUCT;
return type == game::VAR_OBJECT;
}
/***************************************************************
@ -232,7 +232,7 @@ namespace scripting
template <>
bool script_value::is<std::function<void()>>() const
{
return this->get_raw().type == game::SCRIPT_FUNCTION;
return this->get_raw().type == game::VAR_FUNCTION;
}
/***************************************************************
@ -242,7 +242,7 @@ namespace scripting
template <>
bool script_value::is<entity>() const
{
return this->get_raw().type == game::SCRIPT_OBJECT;
return this->get_raw().type == game::VAR_POINTER;
}
template <>
@ -258,7 +258,7 @@ namespace scripting
template <>
bool script_value::is<vector>() const
{
return this->get_raw().type == game::SCRIPT_VECTOR;
return this->get_raw().type == game::VAR_VECTOR;
}
template <>

View File

@ -35,7 +35,7 @@ namespace scripting
{
this->release();
this->value_ = other.value_;
other.value_.type = game::SCRIPT_NONE;
other.value_.type = game::VAR_UNDEFINED;
}
return *this;
@ -59,10 +59,10 @@ namespace scripting
void variable_value::release()
{
if (this->value_.type != game::SCRIPT_NONE)
if (this->value_.type != game::VAR_UNDEFINED)
{
game::RemoveRefToValue(this->value_.type, this->value_.u);
this->value_.type = game::SCRIPT_NONE;
this->value_.type = game::VAR_UNDEFINED;
}
}
}

View File

@ -22,6 +22,6 @@ namespace scripting
void assign(const game::VariableValue& value);
void release();
game::VariableValue value_{{0}, game::SCRIPT_NONE};
game::VariableValue value_{{0}, game::VAR_UNDEFINED};
};
}

View File

@ -25,19 +25,39 @@ namespace game
typedef void(*BuiltinMethod)(scr_entref_t);
typedef void(*BuiltinFunction)();
enum scriptType_e
enum
{
SCRIPT_NONE = 0,
SCRIPT_OBJECT = 1,
SCRIPT_STRING = 2,
SCRIPT_ISTRING = 3,
SCRIPT_VECTOR = 4,
SCRIPT_FLOAT = 5,
SCRIPT_INTEGER = 6,
SCRIPT_END = 8,
SCRIPT_FUNCTION = 9,
SCRIPT_STRUCT = 19,
SCRIPT_ARRAY = 22
VAR_UNDEFINED = 0x0,
VAR_BEGIN_REF = 0x1,
VAR_POINTER = 0x1,
VAR_STRING = 0x2,
VAR_ISTRING = 0x3,
VAR_VECTOR = 0x4,
VAR_END_REF = 0x5,
VAR_FLOAT = 0x5,
VAR_INTEGER = 0x6,
VAR_CODEPOS = 0x7,
VAR_PRECODEPOS = 0x8,
VAR_FUNCTION = 0x9,
VAR_BUILTIN_FUNCTION = 0xA,
VAR_BUILTIN_METHOD = 0xB,
VAR_STACK = 0xC,
VAR_ANIMATION = 0xD,
VAR_PRE_ANIMATION = 0xE,
VAR_THREAD = 0xF,
VAR_NOTIFY_THREAD = 0x10,
VAR_TIME_THREAD = 0x11,
VAR_CHILD_THREAD = 0x12,
VAR_OBJECT = 0x13,
VAR_DEAD_ENTITY = 0x14,
VAR_ENTITY = 0x15,
VAR_ARRAY = 0x16,
VAR_DEAD_THREAD = 0x17,
VAR_COUNT = 0x18,
VAR_FREE = 0x18,
VAR_THREAD_LIST = 0x19,
VAR_ENDON_LIST = 0x1A,
VAR_TOTAL_COUNT = 0x1B,
};
struct VariableStackBuffer

View File

@ -46,17 +46,17 @@ namespace game
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1400EE500, 0x1401A3050};
WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg, const char* dvar, const char* value)> CG_SetClientDvarFromServer{0, 0x1401BF0A0};
WEAK symbol<void(XAssetType type, void (__cdecl* func)(XAssetHeader, void*), void* inData, bool includeOverride)> DB_EnumXAssets_FastFile{0x14017D7C0, 0x14026EC10};
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)> DB_EnumXAssets_Internal{0x14017D830, 0x14026EC80};
WEAK symbol<void(XAssetType type, void (*func)(XAssetHeader, void*), void* inData, bool includeOverride)> DB_EnumXAssets_FastFile{0x14017D7C0, 0x14026EC10};
WEAK symbol<void(XAssetType type, void(*func)(XAssetHeader, void*), const void* inData, bool includeOverride)> DB_EnumXAssets_Internal{0x14017D830, 0x14026EC80};
WEAK symbol<XAssetEntry(XAssetType type, const char* name)> DB_FindXAssetEntry{0x14017D830, 0x14026F020};
WEAK symbol<XAssetHeader(XAssetType type, const char *name, int allowCreateDefault)> DB_FindXAssetHeader{0x14017DCA0, 0x14026F0F0};
WEAK symbol<const char* (const XAsset* asset)> DB_GetXAssetName{0x140151C00, 0x140240DD0};
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x140151C00, 0x140240DD0};
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x140151C20, 0x140240DF0};
WEAK symbol<void(XZoneInfo* zoneInfo, unsigned int zoneCount, DBSyncMode syncMode)> DB_LoadXAssets{0x1402F8B50, 0x140270F30};
WEAK symbol<int(XAssetType type, const char* name)> DB_XAssetExists{0x0, 0x1402750F0};
WEAK symbol<int(XAssetType type, const char* name)> DB_IsXAssetDefault{0x0, 0x140270320};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x0, 0x14026FCC0};
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x0, 0x14026FB90};
WEAK symbol<int(XAssetType type, const char* name)> DB_XAssetExists{0x140182190, 0x1402750F0};
WEAK symbol<int(XAssetType type, const char* name)> DB_IsXAssetDefault{0x14017EEF0, 0x140270320};
WEAK symbol<int(const RawFile* rawfile)> DB_GetRawFileLen{0x14017E890, 0x14026FCC0};
WEAK symbol<void(const RawFile* rawfile, char* buf, int size)> DB_GetRawBuffer{0x14017E750, 0x14026FB90};
WEAK symbol<char*(const char* filename, char* buf, int size)> DB_ReadRawFile{0x140180E30, 0x140273080};
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x140370860, 0x1404BF8B0};

View File

@ -163,7 +163,7 @@ void limit_parallel_dll_loading()
void apply_environment()
{
wchar_t* buffer{};
size_t size{};
std::size_t size{};
if (_wdupenv_s(&buffer, &size, L"XLABS_AW_INSTALL") != 0 || buffer == nullptr)
{
throw std::runtime_error("Please use the X Labs launcher to run the game!");
@ -171,12 +171,11 @@ void apply_environment()
const auto _ = gsl::finally([&]
{
free(buffer);
std::free(buffer);
});
const std::wstring dir{buffer, size};
SetCurrentDirectoryW(dir.data());
SetDllDirectoryW(dir.data());
SetCurrentDirectoryW(buffer);
SetDllDirectoryW(buffer);
}
int main()
@ -188,11 +187,11 @@ int main()
// people will start with admin rights if it crashes.
limit_parallel_dll_loading();
srand(uint32_t(time(nullptr)));
std::srand(uint32_t(time(nullptr)));
{
auto premature_shutdown = true;
const auto _ = gsl::finally([&premature_shutdown]()
const auto _ = gsl::finally([&premature_shutdown]
{
if (premature_shutdown)
{