Merge remote-tracking branch 'origin/develop' into feature/demonware

This commit is contained in:
momo5502 2021-02-26 16:52:09 +01:00
commit ebd614aaf7
17 changed files with 468 additions and 36 deletions

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit 84aeb59f269ee9747e85d780c8aa180bca3a9a60
Subproject commit ef0ffefe525a6219ff245d19a832ce06f3fd3504

View File

@ -44,7 +44,7 @@ namespace bots
game::SV_SpawnTestClient(&game::mp::g_entities[entity_num]);
if (game::Com_GetCurrentCoDPlayMode() == game::CODPLAYMODE_CORE)
{
bot_team_join(entity_num);
//bot_team_join(entity_num); // super bugger rn
}
}

View File

@ -1,7 +1,7 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
//#include "system_check.hpp"
//#include "scheduler.hpp"
#include "system_check.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
@ -11,7 +11,7 @@
#include <utils/thread.hpp>
#include <utils/compression.hpp>
//#include <exception/minidump.hpp>
#include <exception/minidump.hpp>
#include <version.hpp>
@ -31,6 +31,25 @@ namespace exception
std::atomic<int> recovery_counts = {0};
} recovery_data;
bool is_game_thread()
{
static std::vector<int> allowed_threads =
{
game::THREAD_CONTEXT_MAIN,
};
const auto self_id = GetCurrentThreadId();
for (const auto& index : allowed_threads)
{
if (game::threadIds[index] == self_id)
{
return true;
}
}
return false;
}
bool is_exception_interval_too_short()
{
const auto delta = std::chrono::high_resolution_clock::now() - recovery_data.last_recovery;
@ -48,6 +67,14 @@ namespace exception
return initialized;
}
bool is_recoverable()
{
return is_game_thread()
&& !is_exception_interval_too_short()
&& !too_many_exceptions_occured()
&& is_initialized();
}
void show_mouse_cursor()
{
while (ShowCursor(TRUE) < 0);
@ -55,18 +82,62 @@ namespace exception
void display_error_dialog()
{
std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n\n",
std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n"
"A minidump has been written.\n\n",
exception_data.code, exception_data.address);
error_str += "Make sure to update your graphics card drivers and install operating system updates!";
if (!system_check::is_valid())
{
error_str += "Make sure to get supported game files to avoid such crashes!";
}
else
{
error_str += "Make sure to update your graphics card drivers and install operating system updates!";
}
utils::thread::suspend_other_threads();
show_mouse_cursor();
MessageBoxA(nullptr, error_str.data(), "ERROR", MB_ICONERROR);
MessageBoxA(nullptr, error_str.data(), "S1x ERROR", MB_ICONERROR);
TerminateProcess(GetCurrentProcess(), exception_data.code);
}
void reset_state()
{
// TODO: Add a limit for dedi restarts
if (game::environment::is_dedi())
{
utils::nt::relaunch_self();
utils::nt::terminate(exception_data.code);
}
if (is_recoverable())
{
recovery_data.last_recovery = std::chrono::high_resolution_clock::now();
++recovery_data.recovery_counts;
game::Com_Error(game::ERR_DROP, "Fatal error (0x%08X) at 0x%p.\nA minidump has been written.\n\n"
"S1x has tried to recover your game, but it might not run stable anymore.\n\n"
"Make sure to update your graphics card drivers and install operating system updates!",
exception_data.code, exception_data.address);
}
else
{
display_error_dialog();
}
}
size_t get_reset_state_stub()
{
static auto* stub = utils::hook::assemble([](utils::hook::assembler& a)
{
a.sub(rsp, 0x10);
a.or_(rsp, 0x8);
a.jmp(reset_state);
});
return reinterpret_cast<size_t>(stub);
}
std::string get_timestamp()
{
tm ltime{};
@ -79,6 +150,48 @@ namespace exception
return timestamp;
}
std::string generate_crash_info(const LPEXCEPTION_POINTERS exceptioninfo)
{
std::string info{};
const auto line = [&info](const std::string& text)
{
info.append(text);
info.append("\r\n");
};
line("S1x Crash Dump");
line("");
line("Version: "s + VERSION);
line("Environment: "s + game::environment::get_string());
line("Timestamp: "s + get_timestamp());
line("Clean game: "s + (system_check::is_valid() ? "Yes" : "No"));
line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress));
#pragma warning(push)
#pragma warning(disable: 4996)
OSVERSIONINFOEXA version_info;
ZeroMemory(&version_info, sizeof(version_info));
version_info.dwOSVersionInfoSize = sizeof(version_info);
GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&version_info));
#pragma warning(pop)
line(utils::string::va("OS Version: %u.%u", version_info.dwMajorVersion, version_info.dwMinorVersion));
return info;
}
void write_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
{
const std::string crash_name = utils::string::va("minidumps/s1x-crash-%d-%s.zip",
game::environment::get_real_mode(), get_timestamp().data());
utils::compression::zip::archive zip_file{};
zip_file.add("crash.dmp", create_minidump(exceptioninfo));
zip_file.add("info.txt", generate_crash_info(exceptioninfo));
zip_file.write(crash_name, "S1x Crash Dump");
}
bool is_harmless_error(const LPEXCEPTION_POINTERS exceptioninfo)
{
const auto code = exceptioninfo->ExceptionRecord->ExceptionCode;
@ -87,13 +200,17 @@ namespace exception
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS exceptioninfo)
{
if (!is_harmless_error(exceptioninfo))
if (is_harmless_error(exceptioninfo))
{
exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode;
exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress;
display_error_dialog();
return EXCEPTION_CONTINUE_EXECUTION;
}
write_minidump(exceptioninfo);
exception_data.code = exceptioninfo->ExceptionRecord->ExceptionCode;
exception_data.address = exceptioninfo->ExceptionRecord->ExceptionAddress;
exceptioninfo->ContextRecord->Rip = get_reset_state_stub();
return EXCEPTION_CONTINUE_EXECUTION;
}
@ -111,6 +228,11 @@ namespace exception
{
SetUnhandledExceptionFilter(exception_filter);
utils::hook::jump(SetUnhandledExceptionFilter, set_unhandled_exception_filter_stub, true);
scheduler::on_game_initialized([]()
{
is_initialized() = true;
});
}
};
}

View File

@ -82,6 +82,17 @@ namespace fastfiles
}, 0, false);
});
command::add("rawfilelist", [](const command::params& params)
{
game::DB_EnumXAssets_FastFile(game::ASSET_TYPE_RAWFILE, [](const game::XAssetHeader header, void*)
{
if (header.rawfile && header.rawfile->name)
{
printf("%s\n", header.rawfile->name);
}
}, 0, false);
});
command::add("g_poolSizes", []()
{
for (auto i = 0; i < game::ASSET_TYPE_COUNT; i++)

View File

@ -12,6 +12,8 @@
#include <utils/string.hpp>
#include <utils/hook.hpp>
#include "version.hpp"
namespace patches
{
namespace
@ -265,6 +267,12 @@ namespace patches
// disable codPointStore
dvars::override::Dvar_RegisterInt("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE);
// don't register every replicated dvar as a network dvar
utils::hook::nop(0x1403534BE, 5); // dvar_foreach
// patch "Server is different version" to show the server client version
utils::hook::inject(0x1404398B2, VERSION);
}
static void patch_sp()

View File

@ -66,6 +66,15 @@ namespace renderer
r_init_draw_method_hook.create(SELECT_VALUE(0x14046C150, 0x140588B00), &r_init_draw_method_stub);
r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404A5330, 0x1405C3AE0), &r_update_front_end_dvar_options_stub);
// use "saved" flags for "r_normalMap"
utils::hook::set<uint8_t>(SELECT_VALUE(0x14047E0B8, 0x14059AD71), game::DVAR_FLAG_SAVED);
// use "saved" flags for "r_specularMap"
utils::hook::set<uint8_t>(SELECT_VALUE(0x14047E0DA, 0x14059AD99), game::DVAR_FLAG_SAVED);
// use "saved" flags for "r_specOccMap"
utils::hook::set<uint8_t>(SELECT_VALUE(0x14047E0FC, 0x14059ADC1), game::DVAR_FLAG_SAVED);
}
};
}

View File

@ -103,7 +103,7 @@ namespace scheduler
{
schedule([=]()
{
const auto dw_init = game::Live_SyncOnlineDataFlags(0) == 0;
const auto dw_init = game::environment::is_sp() ? true : game::Live_SyncOnlineDataFlags(0) == 0;
if (dw_init && game::Sys_IsDatabaseReady2())
{
once(callback, type, delay);

View File

@ -100,8 +100,8 @@ namespace server_list
return count > 15 ? 15 : count;
}
const char* ui_feeder_item_text(int /*localClientNum*/, void* /*a2*/, void* /*a3*/, const size_t index,
const size_t column)
const char* ui_feeder_item_text(int /*localClientNum*/, void* /*a2*/, void* /*a3*/, const int index,
const int column)
{
std::lock_guard<std::mutex> _(mutex);

View File

@ -42,7 +42,7 @@ namespace steam_proxy
#ifndef DEV_BUILD
try
{
this->start_mod("\xF0\x9F\x90\xA4" " S1x: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 209650 : 209660);
this->start_mod("\xF0\x9F\x90\xA4" " S1x: "s + game::environment::get_string(), game::environment::is_sp() ? 209650 : 209660);
}
catch (std::exception& e)
{

View File

@ -0,0 +1,88 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "system_check.hpp"
#include "game/game.hpp"
#include <utils/io.hpp>
#include <utils/cryptography.hpp>
namespace system_check
{
namespace
{
std::string read_zone(const std::string& name)
{
std::string data{};
if (utils::io::read_file(name, &data))
{
return data;
}
if (utils::io::read_file("zone/" + name, &data))
{
return data;
}
return {};
}
std::string hash_zone(const std::string& name)
{
const auto data = read_zone(name);
return utils::cryptography::sha256::compute(data, true);
}
bool verify_hashes(const std::unordered_map<std::string, std::string>& zone_hashes)
{
for (const auto& zone_hash : zone_hashes)
{
const auto hash = hash_zone(zone_hash.first);
if (hash != zone_hash.second)
{
return false;
}
}
return true;
}
bool is_system_valid()
{
static std::unordered_map<std::string, std::string> mp_zone_hashes =
{
{"patch_common_mp.ff", "23B15B4EF0AC9B52B3C6F9F681290B25B6B24B49F17238076A3D7F3CCEF9A0E1"},
};
static std::unordered_map<std::string, std::string> sp_zone_hashes =
{
{"patch_common.ff", "4624A974C6C7F8BECD9C343E7951722D8378889AC08ED4F2B22459B171EC553C"},
{"patch_common_zm_mp.ff", "DA16B546B7233BBC4F48E1E9084B49218CB9271904EA7120A0EB4CB8723C19CF"},
};
return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes));
}
}
bool is_valid()
{
static auto valid = is_system_valid();
return valid;
}
class component final : public component_interface
{
public:
void post_load() override
{
if (!is_valid())
{
MessageBoxA(nullptr, "Your game files are outdated or unsupported.\n"
"Please get the latest officially supported Call of Duty: Advanced Warfare files, or you will get random crashes and issues.",
"Invalid game files!", MB_ICONINFORMATION);
}
}
};
}
REGISTER_COMPONENT(system_check::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace system_check
{
bool is_valid();
}

View File

@ -0,0 +1,60 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include <utils/thread.hpp>
namespace thread_names
{
namespace
{
void set_thread_names()
{
static std::unordered_map<int, std::string> thread_names =
{
{game::THREAD_CONTEXT_MAIN, "Main"},
{game::THREAD_CONTEXT_BACKEND, "Backend"}, // Renderer
{game::THREAD_CONTEXT_WORKER0, "Worker0"},
{game::THREAD_CONTEXT_WORKER1, "Worker1"},
{game::THREAD_CONTEXT_WORKER2, "Worker2"},
{game::THREAD_CONTEXT_WORKER3, "Worker3"},
{game::THREAD_CONTEXT_WORKER4, "Worker4"},
{game::THREAD_CONTEXT_WORKER5, "Worker5"},
{game::THREAD_CONTEXT_WORKER6, "Worker6"},
{game::THREAD_CONTEXT_WORKER7, "Worker7"},
{game::THREAD_CONTEXT_SERVER, "Server"},
{game::THREAD_CONTEXT_CINEMATIC, "Cinematic"},
{game::THREAD_CONTEXT_DATABASE, "Database"},
{game::THREAD_CONTEXT_STREAM, "Stream"},
{game::THREAD_CONTEXT_SNDSTREAMPACKETCALLBACK, "Snd stream packet callback"},
{game::THREAD_CONTEXT_STATS_WRITE, "Stats write"},
};
for (const auto& thread_name : thread_names)
{
const auto id = game::threadIds[thread_name.first];
if (id)
{
utils::thread::set_name(id, thread_name.second);
}
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
set_thread_names();
scheduler::once(set_thread_names, scheduler::pipeline::main);
scheduler::once(set_thread_names, scheduler::pipeline::renderer);
scheduler::once(set_thread_names, scheduler::pipeline::server);
}
};
}
REGISTER_COMPONENT(thread_names::component)

View File

@ -76,12 +76,18 @@ namespace game
std::string get_string()
{
const auto current_mode = get_mode();
const auto current_mode = get_real_mode();
switch (current_mode)
{
case launcher::mode::server:
return "Dedicated Server";
case launcher::mode::zombies:
return "Zombies";
case launcher::mode::survival:
return "Survival";
case launcher::mode::multiplayer:
return "Multiplayer";
@ -95,24 +101,5 @@ namespace game
return "Unknown (" + std::to_string(static_cast<int>(mode)) + ")";
}
}
std::string playmode_to_string(game::CodPlayMode playmode)
{
switch (playmode)
{
case CODPLAYMODE_CORE:
return "Core";
case CODPLAYMODE_ZOMBIES:
return "Zombies";
case CODPLAYMODE_SURVIVAL:
return "Survival";
case CODPLAYMODE_SP:
return "Singleplayer";
case CODPLAYMODE_NONE:
return "None";
default:
return "Unknown (" + std::to_string(static_cast<int>(playmode)) + ")";
}
}
}
}

View File

@ -779,6 +779,29 @@ namespace game
SV_CMD_RELIABLE = 0x1,
};
enum threadType
{
THREAD_CONTEXT_MAIN = 0x0,
THREAD_CONTEXT_BACKEND = 0x1,
THREAD_CONTEXT_WORKER0 = 0x2,
THREAD_CONTEXT_WORKER1 = 0x3,
THREAD_CONTEXT_WORKER2 = 0x4,
THREAD_CONTEXT_WORKER3 = 0x5,
THREAD_CONTEXT_WORKER4 = 0x6,
THREAD_CONTEXT_WORKER5 = 0x7,
THREAD_CONTEXT_WORKER6 = 0x8,
THREAD_CONTEXT_WORKER7 = 0x9,
THREAD_CONTEXT_SERVER = 0xA,
THREAD_CONTEXT_TRACE_COUNT = 0xB,
THREAD_CONTEXT_TRACE_LAST = 0xA,
THREAD_CONTEXT_CINEMATIC = 0xB,
THREAD_CONTEXT_DATABASE = 0xC,
THREAD_CONTEXT_STREAM = 0xD,
THREAD_CONTEXT_SNDSTREAMPACKETCALLBACK = 0xE,
THREAD_CONTEXT_STATS_WRITE = 0xF,
THREAD_CONTEXT_COUNT = 0x10,
};
enum DBSyncMode
{
DB_LOAD_ASYNC = 0x0,
@ -981,12 +1004,27 @@ namespace game
const char* buffer;
};
struct StringTableCell
{
const char* string;
int hash;
};
struct StringTable
{
const char* name;
int columnCount;
int rowCount;
StringTableCell* values;
};
union XAssetHeader
{
void* data;
Material* material;
Font_s* font;
RawFile* rawfile;
StringTable* stringTable;
};
enum TestClientType

View File

@ -174,6 +174,8 @@ namespace game
WEAK symbol<int> g_poolSize{ 0x140804140, 0x1409B4B90 };
WEAK symbol<const char*> g_assetNames{ 0x140803C90 , 0x1409B3180 };
WEAK symbol<DWORD> threadIds{ 0x149632EC0, 0x147DCEA30 };
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{ 0x14CDFAFE8, 0x14D80FD98 };
namespace mp

View File

@ -0,0 +1,93 @@
#include "minidump.hpp"
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
#include <gsl/gsl>
namespace exception
{
namespace
{
constexpr MINIDUMP_TYPE get_minidump_type()
{
const auto type = MiniDumpIgnoreInaccessibleMemory //
| MiniDumpWithHandleData //
| MiniDumpScanMemory //
| MiniDumpWithProcessThreadData //
| MiniDumpWithFullMemoryInfo //
| MiniDumpWithThreadInfo //
| MiniDumpWithUnloadedModules;
return static_cast<MINIDUMP_TYPE>(type);
}
std::string get_temp_filename()
{
char filename[MAX_PATH] = {0};
char pathname[MAX_PATH] = {0};
GetTempPathA(sizeof(pathname), pathname);
GetTempFileNameA(pathname, "S1x-", 0, filename);
return filename;
}
HANDLE write_dump_to_temp_file(const LPEXCEPTION_POINTERS exceptioninfo)
{
MINIDUMP_EXCEPTION_INFORMATION minidump_exception_info = {GetCurrentThreadId(), exceptioninfo, FALSE};
auto* const file_handle = CreateFileA(get_temp_filename().data(), GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
nullptr);
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file_handle, get_minidump_type(),
&minidump_exception_info,
nullptr,
nullptr))
{
MessageBoxA(nullptr, "There was an error creating the minidump! Hit OK to close the program.",
"Minidump Error", MB_OK | MB_ICONERROR);
TerminateProcess(GetCurrentProcess(), 123);
}
return file_handle;
}
std::string read_file(HANDLE file_handle)
{
FlushFileBuffers(file_handle);
SetFilePointer(file_handle, 0, nullptr, FILE_BEGIN);
std::string buffer{};
DWORD bytes_read = 0;
char temp_bytes[0x2000];
do
{
if (!ReadFile(file_handle, temp_bytes, sizeof(temp_bytes), &bytes_read, nullptr))
{
return {};
}
buffer.append(temp_bytes, bytes_read);
}
while (bytes_read == sizeof(temp_bytes));
return buffer;
}
}
std::string create_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
{
auto* const file_handle = write_dump_to_temp_file(exceptioninfo);
const auto _ = gsl::finally([file_handle]()
{
CloseHandle(file_handle);
});
return read_file(file_handle);
}
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "../utils/nt.hpp"
namespace exception
{
std::string create_minidump(LPEXCEPTION_POINTERS exceptioninfo);
}