mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-04-19 19:40:00 +03:00
feat(whoa): implement the ability to shut down the client gracefully, saving CVars upon exit
This commit is contained in:
parent
17ccf2a8bb
commit
0105c72da0
@ -97,8 +97,6 @@ void ClientRegisterConsoleCommands() {
|
||||
ConsoleCommandRegister("reloadUI", CCommand_ReloadUI, GRAPHICS, nullptr);
|
||||
ConsoleCommandRegister("perf", CCommand_Perf, DEBUG, nullptr);
|
||||
|
||||
const auto game = CATEGORY::GAME;
|
||||
|
||||
Client::g_accountNameVar = CVar::Register(
|
||||
"accountName",
|
||||
"Saved account name",
|
||||
@ -180,7 +178,6 @@ void ClientPostClose(int32_t a1) {
|
||||
|
||||
int32_t DestroyEngineCallback(const void* a1, void* a2) {
|
||||
// TODO
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -625,6 +622,17 @@ int32_t InitializeGlobal() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DestroyGlobal() {
|
||||
// TODO
|
||||
|
||||
OsTimeShutdown();
|
||||
EventDestroy();
|
||||
ConsoleDeviceDestroy();
|
||||
CVar::Destroy();
|
||||
|
||||
// TODO
|
||||
}
|
||||
|
||||
void CommonMain() {
|
||||
StormInitialize();
|
||||
|
||||
@ -659,7 +667,7 @@ void CommonMain() {
|
||||
|
||||
if (InitializeGlobal()) {
|
||||
EventDoMessageLoop();
|
||||
// TODO: DestroyGlobal();
|
||||
DestroyGlobal();
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
const char* s_filename = nullptr;
|
||||
bool CVar::m_needsSave;
|
||||
bool CVar::m_initialized;
|
||||
TSHashTable<CVar, HASHKEY_STRI> CVar::s_registeredCVars;
|
||||
|
||||
CVar* CVar::Lookup(const char* name) {
|
||||
@ -34,6 +35,10 @@ CVar* CVar::LookupRegistered(const char* name) {
|
||||
}
|
||||
|
||||
CVar* CVar::Register(const char* name, const char* help, uint32_t flags, const char* value, HANDLER_FUNC fcn, uint32_t category, bool a7, void* arg, bool a9) {
|
||||
if (!name || !*name || !value) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto cv = s_registeredCVars.Ptr(name);
|
||||
|
||||
if (cv) {
|
||||
@ -280,6 +285,7 @@ int32_t CVar::Load(const char* filename) {
|
||||
void CVar::Initialize(const char* filename) {
|
||||
STORM_ASSERT(filename);
|
||||
s_filename = filename;
|
||||
m_initialized = 1;
|
||||
|
||||
// Get data path
|
||||
char path[STORM_MAX_PATH] = {0};
|
||||
@ -296,6 +302,72 @@ void CVar::Initialize(const char* filename) {
|
||||
CVar::Load(s_filename);
|
||||
}
|
||||
|
||||
int32_t CVar::IterateForArchive(uint32_t a1, uint32_t a2, ITERATE_FUNC cb, void* param) {
|
||||
auto cvar = s_registeredCVars.Head();
|
||||
|
||||
while (cvar) {
|
||||
if (cvar->m_flags & 0x1) {
|
||||
if (!(cvar->m_flags & 0x80) && (cvar->m_flags & 0x30) == a1 && (cvar->m_flags & a2) == 0) {
|
||||
const char* value;
|
||||
if ((value = cvar->m_latchedValue.GetString()) ||
|
||||
(value = cvar->m_stringValue.GetString()) ||
|
||||
(value = cvar->m_defaultValue.GetString())) {
|
||||
auto defaultvalue = cvar->m_defaultValue.GetString();
|
||||
if (!defaultvalue || SStrCmp(value, defaultvalue, STORM_MAX_STR)) {
|
||||
if (!cb(cvar->m_key.m_str, value, param)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cvar = s_registeredCVars.Next(cvar);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool CVar::SaveCvar(const char* key, const char* value, void* param) {
|
||||
char buffer[STORM_MAX_PATH];
|
||||
SStrPrintf(buffer, sizeof(buffer), "SET %s \"%s\"\n", key, value);
|
||||
uint32_t byteswritten = 0;
|
||||
OsWriteFile(static_cast<HOSFILE>(param), buffer, SStrLen(buffer), &byteswritten);
|
||||
return byteswritten != 0;
|
||||
}
|
||||
|
||||
int32_t CVarSaveFile() {
|
||||
if (!CVar::m_needsSave) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char name[STORM_MAX_PATH];
|
||||
|
||||
CVar::m_needsSave = false;
|
||||
SStrCopy(name, "WTF\\", sizeof(name));
|
||||
SStrPack(name, s_filename, sizeof(name));
|
||||
|
||||
int32_t result = 0;
|
||||
|
||||
auto file = OsCreateFile(name, OS_GENERIC_WRITE, 0, OS_CREATE_ALWAYS, OS_FILE_ATTRIBUTE_NORMAL, OS_FILE_TYPE_DEFAULT);
|
||||
if (file != HOSFILE_INVALID) {
|
||||
result = CVar::IterateForArchive(0, 0, CVar::SaveCvar, file);
|
||||
OsCloseFile(file);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CVar::Destroy() {
|
||||
m_initialized = 0;
|
||||
CVarSaveFile();
|
||||
ConsoleCommandUnregister("set");
|
||||
ConsoleCommandUnregister("cvar_reset");
|
||||
ConsoleCommandUnregister("cvar_default");
|
||||
ConsoleCommandUnregister("cvarlist");
|
||||
s_registeredCVars.Clear();
|
||||
}
|
||||
|
||||
int32_t CvarResetCommandHandler(const char* command, const char* arguments) {
|
||||
char cvarName[256] = {0};
|
||||
auto string = arguments;
|
||||
@ -395,7 +467,7 @@ int32_t SetCommandHandler(const char* command, const char* arguments) {
|
||||
}
|
||||
|
||||
cvar->m_modified++;
|
||||
if (!(cvar->m_flags & 0x2)) {
|
||||
if ((cvar->m_flags & 0x2)) {
|
||||
cvar->m_latchedValue.Copy(cvarValue);
|
||||
CVar::m_needsSave = true;
|
||||
} else if (!(cvar->m_flags & 0x4)) {
|
||||
|
@ -1,32 +1,36 @@
|
||||
#ifndef CONSOLE_CVAR_HPP
|
||||
#define CONSOLE_CVAR_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <common/String.hpp>
|
||||
#include <storm/Hash.hpp>
|
||||
#include <bc/os/File.hpp>
|
||||
#include <common/String.hpp>
|
||||
#include <cstdint>
|
||||
#include <storm/Hash.hpp>
|
||||
|
||||
#define CONSOLE_CVAR_MAX_LINE 2048
|
||||
|
||||
|
||||
class CVar : public TSHashObject<CVar, HASHKEY_STRI> {
|
||||
public:
|
||||
typedef bool (*ITERATE_FUNC)(const char*, const char*, void*);
|
||||
typedef bool (*HANDLER_FUNC)(CVar*, const char*, const char*, void*);
|
||||
|
||||
// Static variables
|
||||
static TSHashTable<CVar, HASHKEY_STRI> s_registeredCVars;
|
||||
static bool m_needsSave;
|
||||
static bool m_initialized;
|
||||
|
||||
// Static functions
|
||||
static CVar* Lookup(const char* name);
|
||||
static CVar* LookupRegistered(const char* name);
|
||||
static CVar* Register(const char* name, const char* help, uint32_t flags, const char* value, HANDLER_FUNC fcn, uint32_t category, bool a7, void* arg, bool a9);
|
||||
static void Initialize(const char* filename);
|
||||
static void Initialize(const char* filename);
|
||||
static int32_t IterateForArchive(uint32_t a1, uint32_t a2, ITERATE_FUNC cb, void* param);
|
||||
static void Destroy();
|
||||
static int32_t Load(const char* filename);
|
||||
static int32_t Load(HOSFILE fileHandle);
|
||||
static bool SaveCvar(const char* key, const char* value, void* param);
|
||||
// Member variables
|
||||
uint32_t m_category = 0;
|
||||
uint32_t m_flags = 0;
|
||||
uint32_t m_flags = 0;
|
||||
RCString m_stringValue;
|
||||
float m_floatValue = 0.0;
|
||||
int32_t m_intValue = 0;
|
||||
@ -36,7 +40,7 @@ class CVar : public TSHashObject<CVar, HASHKEY_STRI> {
|
||||
RCString m_latchedValue;
|
||||
RCString m_help;
|
||||
bool (*m_callback)(CVar*, const char*, const char*, void*) = nullptr;
|
||||
void* m_arg = nullptr;
|
||||
void* m_arg = nullptr;
|
||||
|
||||
// Member functions
|
||||
CVar();
|
||||
@ -50,7 +54,6 @@ class CVar : public TSHashObject<CVar, HASHKEY_STRI> {
|
||||
int32_t Update();
|
||||
};
|
||||
|
||||
|
||||
int32_t SetCommandHandler(const char* command, const char* arguments);
|
||||
int32_t CvarResetCommandHandler(const char* command, const char* arguments);
|
||||
int32_t CvarDefaultCommandHandler(const char* command, const char* arguments);
|
||||
|
@ -31,8 +31,6 @@ const char* ConsoleCommandHistory(uint32_t index);
|
||||
|
||||
uint32_t ConsoleCommandHistoryDepth();
|
||||
|
||||
int32_t ConsoleCommandRegister(const char* command, COMMANDHANDLER handler, CATEGORY category, const char* helpText);
|
||||
|
||||
void ConsoleInitializeCommonCommand();
|
||||
|
||||
void ConsoleInitializeDebugCommand();
|
||||
|
@ -35,5 +35,5 @@ void ConsoleSetHotKey(KEY hotkey) {
|
||||
}
|
||||
|
||||
void ConsolePostClose() {
|
||||
EventPostCloseEx(EventGetCurrentContext());
|
||||
EventPostClose();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "event/Context.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "event/Event.hpp"
|
||||
#include "event/EvtThread.hpp"
|
||||
#include <common/Time.hpp>
|
||||
@ -53,8 +54,32 @@ HEVENTCONTEXT AttachContextToThread(EvtContext* context) {
|
||||
return context;
|
||||
}
|
||||
|
||||
void DetachContextFromThread(uint32_t a1, EvtContext* a2) {
|
||||
// TODO
|
||||
void DetachContextFromThread(uint32_t hThread, EvtContext* context) {
|
||||
SInterlockedIncrement(&Event::s_threadListContention);
|
||||
Event::s_threadListCritsect.Enter();
|
||||
|
||||
auto threadSlot = Event::s_threadSlots[hThread];
|
||||
if (threadSlot) {
|
||||
threadSlot->m_weightTotal -= context->m_schedWeight;
|
||||
threadSlot->m_contextCount--;
|
||||
|
||||
threadSlot->m_weightAvg = threadSlot->m_contextCount ? threadSlot->m_weightTotal / threadSlot->m_contextCount : 0;
|
||||
|
||||
auto thread = Event::s_threadList.Head();
|
||||
while (thread) {
|
||||
if (thread != threadSlot && thread->m_weightAvg) {
|
||||
if ((thread->m_weightAvg + threadSlot->m_weightTotal) <= thread->m_weightTotal) {
|
||||
thread->m_rebalance += (thread->m_weightTotal - threadSlot->m_weightTotal) / thread->m_weightAvg;
|
||||
}
|
||||
thread->m_rebalance = std::min(thread->m_rebalance, thread->m_contextCount);
|
||||
}
|
||||
|
||||
thread = thread->Next();
|
||||
}
|
||||
}
|
||||
|
||||
Event::s_threadListCritsect.Leave();
|
||||
SInterlockedDecrement(&Event::s_threadListContention);
|
||||
}
|
||||
|
||||
EvtContext* GetNextContext(uint32_t hThread) {
|
||||
|
@ -86,6 +86,10 @@ HEVENTCONTEXT EventCreateContextEx(int32_t interactive, int32_t (*initializeHand
|
||||
return IEvtSchedulerCreateContext(interactive, initializeHandler, destroyHandler, idleTime, debugFlags);
|
||||
}
|
||||
|
||||
void EventDestroy() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void EventDoMessageLoop() {
|
||||
IEvtSchedulerProcess();
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ namespace Event {
|
||||
|
||||
HEVENTCONTEXT EventCreateContextEx(int32_t interactive, int32_t (*initializeHandler)(const void*, void*), int32_t (*destroyHandler)(const void*, void*), uint32_t idleTime, uint32_t debugFlags);
|
||||
|
||||
void EventDestroy();
|
||||
|
||||
void EventDoMessageLoop();
|
||||
|
||||
HEVENTCONTEXT EventGetCurrentContext();
|
||||
|
@ -51,7 +51,7 @@ void ResetSyncState(EvtContext* context) {
|
||||
|
||||
auto list = &context->m_queueSyncKeyDownList;
|
||||
|
||||
while (node = list->Head()) {
|
||||
while ((node = list->Head())) {
|
||||
list->UnlinkNode(node);
|
||||
list->DeleteNode(node);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "event/Scheduler.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "event/Context.hpp"
|
||||
#include "event/Event.hpp"
|
||||
#include "event/EvtContext.hpp"
|
||||
@ -149,6 +150,18 @@ void IEvtSchedulerProcess() {
|
||||
|
||||
void IEvtSchedulerShutdown() {
|
||||
// TODO
|
||||
Event::s_shutdownEvent.Set();
|
||||
if (Event::s_netServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
Event::s_threadListCritsect.Enter();
|
||||
for (uint32_t i = 0; i < Event::s_threadSlotCount; i++) {
|
||||
if (Event::s_threadSlots[i]) {
|
||||
Event::s_threadSlots[i]->m_wakeEvent.Set();
|
||||
}
|
||||
}
|
||||
Event::s_threadListCritsect.Leave();
|
||||
}
|
||||
|
||||
uint32_t InitializeSchedulerThread() {
|
||||
|
@ -1,19 +1,55 @@
|
||||
#include "event/Synthesize.hpp"
|
||||
#include "event/Event.hpp"
|
||||
#include "event/Queue.hpp"
|
||||
#include "event/Types.hpp"
|
||||
#include "event/Scheduler.hpp"
|
||||
#include "event/EvtContext.hpp"
|
||||
#include "event/Queue.hpp"
|
||||
#include <common/Time.hpp>
|
||||
#include <common/Call.hpp>
|
||||
|
||||
#if defined(WHOA_SYSTEM_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
void SynthesizeDestroy(EvtContext* context) {
|
||||
// TODO
|
||||
#if defined(WHOA_SYSTEM_WIN)
|
||||
ExitProcess(0);
|
||||
#else
|
||||
exit(0);
|
||||
#endif
|
||||
if (!(context->m_schedFlags & 0x1)) {
|
||||
context->m_schedFlags |= 0x1;
|
||||
context->m_schedLastIdle = OsGetAsyncTimeMs();
|
||||
IEvtQueueDispatch(context, EVENT_ID_INITIALIZE, nullptr);
|
||||
}
|
||||
|
||||
if (context->m_schedFlags & 0x2) {
|
||||
SInterlockedDecrement(&Event::s_interactiveCount);
|
||||
if (!Event::s_interactiveCount) {
|
||||
IEvtSchedulerShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
IEvtQueueDispatch(context, EVENT_ID_3, nullptr);
|
||||
context->m_critsect.Enter();
|
||||
context->m_schedState = EvtContext::SCHEDSTATE_DESTROYED;
|
||||
context->m_critsect.Leave();
|
||||
IEvtQueueDispatchAll(context);
|
||||
IEvtQueueDispatch(context, EVENT_ID_DESTROY, nullptr);
|
||||
|
||||
OsCallResetContext(context->m_callContext);
|
||||
PropSelectContext(0);
|
||||
|
||||
int32_t findMask;
|
||||
TSingletonInstanceId<EvtContext, offsetof(EvtContext, m_id)>::s_idTable.Ptr(
|
||||
context->m_id,
|
||||
1,
|
||||
&findMask);
|
||||
|
||||
DEL(context);
|
||||
|
||||
if (findMask != -1) {
|
||||
TSingletonInstanceId<EvtContext, offsetof(EvtContext, m_id)>::s_idTable.Unlock(
|
||||
findMask & (INSTANCE_TABLE_SLOT_COUNT - 1),
|
||||
findMask >= INSTANCE_TABLE_SLOT_COUNT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void SynthesizeIdle(EvtContext* context, uint32_t currTime, float elapsedSec) {
|
||||
|
Loading…
Reference in New Issue
Block a user