VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
Host.cpp
Go to the documentation of this file.
1#include "BuildStamp.hpp"
16
18#include <VoltMod/Core/Log.hpp>
24#include <cstddef>
25#include <memory>
26#include <string_view>
27
28namespace KHook
29{
30KHook::IKHook* __exported__khook = nullptr;
31}
32
33namespace VoltMod
34{
35
36static constexpr std::string_view GameDataPath = "addons/voltmod/gamedata/gamedata.jsonc";
37
38/** The host's services for the process, built at start and torn down at stop. */
39class Host
40{
41public:
42 ~Host() { Stop(); }
43
45 {
46 KHook::__exported__khook = start.HookDispatcher;
48 SetBaseDir(start.GameDir);
49
50 // Before any plugin, so a broken signature logs once; KHook reads slots another hook holds.
51 _gameData = std::make_unique<GameDataTable>(GameDataPath, ReadOriginalSlot, FindScriptBinding);
52
54 if (!engine)
55 {
56 Stop();
57 return std::unexpected(engine.error());
58 }
59
60 GameDataTable* gameData = _gameData->Ready() ? _gameData.get() : nullptr;
61
62 // The host's own hooks share one pass; each service checks the members it needs.
63 _bindings = {};
64 if (gameData)
65 {
66 (void)_bindings.Bind([gameData](GameDataSection sections, std::string_view name) {
67 return gameData->Lookup(sections, name);
68 });
69 }
70
71 _downloads = std::make_unique<WorkshopDownloads>(_bindings, *engine);
72 _registry = std::make_unique<PluginRegistry>(start, gameData, _downloads.get());
73 _commands = std::make_unique<PlayerCommands>(_bindings, *_registry);
74 _presses = std::make_unique<ButtonPresses>(_bindings, *engine, *_registry);
75 // Once per process: every plugin built with this host carries the same baked offsets.
76 _schema = std::make_unique<SchemaCheck>(*_registry, engine->Schema, engine->Resources);
77 _assets = std::make_unique<ServerAssets>(engine->Files);
78 _plugins = std::make_unique<PluginLoader>(*_registry, *_assets);
79
80 _hooks = std::make_unique<EngineHooks>(
81 *_registry, *engine,
82 [this] {
83 _plugins->RunPending();
84 _downloads->OnFrame();
85 _presses->OnFrame();
86 },
87 [this] { _schema->OnServerStartup(); }, [this](int64_t steamId) { _downloads->OnClientConnected(steamId); },
88 [this](int64_t steamId) { _downloads->OnClientDisconnected(steamId); });
89
90 // Before the plugins load, so a plugin registering `volt` is refused rather than racing it.
91 _command = std::make_unique<VoltCommand>(*_registry, *_plugins);
92 _plugins->LoadAll();
93
94 Log::Info("VoltMod host {} ({}) loaded.", VOLTMOD_VERSION, VOLTMOD_BUILD_STAMP);
95 return {};
96 }
97
98 void Stop()
99 {
100 // Plugins first: their teardown runs while the host's events and services are still there.
101 if (_plugins)
102 {
103 _plugins->UnloadAll();
104 }
105
106 _hooks.reset();
107 _command.reset();
108 _plugins.reset();
109 _assets.reset();
110 _schema.reset();
111 _presses.reset();
112 _commands.reset();
113 _registry.reset();
114 _downloads.reset();
115 _gameData.reset();
116 }
117
118private:
119 std::unique_ptr<GameDataTable> _gameData;
120 Bindings _bindings;
121 std::unique_ptr<WorkshopDownloads> _downloads;
122 std::unique_ptr<PluginRegistry> _registry;
123 std::unique_ptr<PlayerCommands> _commands;
124 std::unique_ptr<ButtonPresses> _presses;
125 std::unique_ptr<SchemaCheck> _schema;
126 std::unique_ptr<ServerAssets> _assets;
127 std::unique_ptr<PluginLoader> _plugins;
128 std::unique_ptr<VoltCommand> _command;
129 std::unique_ptr<EngineHooks> _hooks;
130};
131
133
134} // namespace VoltMod
135
136extern "C" VOLTMOD_EXPORT bool VoltMod_HostStart(const VoltMod::LoaderHandoff* start, char* error, size_t errorSize)
137{
138 VoltMod::Status started = VoltMod::g_host.Start(*start);
139 if (!started)
140 {
141 VoltMod::Strings::CopyToBuffer(error, errorSize, started.error().Detail);
142 }
143 return started.has_value();
144}
145
147{
149}
VOLTMOD_EXPORT void VoltMod_HostStop()
Definition Host.cpp:146
VOLTMOD_EXPORT bool VoltMod_HostStart(const VoltMod::LoaderHandoff *start, char *error, size_t errorSize)
Definition Host.cpp:136
#define VOLTMOD_EXPORT
void Stop()
Definition Host.cpp:98
Status Start(const LoaderHandoff &start)
Definition Host.cpp:44
static void CopyToBuffer(char *buffer, std::size_t size, std::string_view text) noexcept
Definition Strings.cpp:257
KHook::IKHook * __exported__khook
Definition Host.cpp:30
void Info(std::format_string< Args... > fmt, Args &&... args)
Definition Log.hpp:79
void SetHandler(Handler handler)
Definition Logger.cpp:26
Log::Handler MakeConsoleHandler(std::string prefix)
The default handler: tier0 ConColorMsg/Msg, one colour per level.
void SetBaseDir(const std::filesystem::path &baseDir)
Set the base directory for path resolution (the host's csgo directory). Must be called during initial...
Definition Paths.cpp:9
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
Result< ScriptTarget > FindScriptBinding(void *classTable, std::string_view name)
static Host g_host
Definition Host.cpp:132
Result< EngineInterfaces > ResolveEngineInterfaces(const LoaderHandoff &start)
const void * ReadOriginalSlot(void **vtable, int index)
Definition Detours.hpp:21
std::expected< void, Error > Status
Definition Result.hpp:67
static constexpr std::string_view GameDataPath
Definition Host.cpp:36
std::expected< T, Error > Result
Definition Result.hpp:64
Status Bind(const GameDataLookup &lookup)
Definition Bindings.cpp:60