1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
#pragma once
#include "core/convar/convar.h"
#include "core/memalloc.h"
#include "squirrel/squirrel.h"
#include "rapidjson/document.h"
#include <string>
#include <vector>
#include <filesystem>
#include <unordered_set>
#include <regex>
namespace fs = std::filesystem;
const std::string MOD_FOLDER_SUFFIX = "\\mods";
const std::string THUNDERSTORE_MOD_FOLDER_SUFFIX = "\\packages";
const std::string REMOTE_MOD_FOLDER_SUFFIX = "\\runtime\\remote\\mods";
const fs::path MOD_OVERRIDE_DIR = "mod";
const std::string COMPILED_ASSETS_SUFFIX = "\\runtime\\compiled";
const std::set<std::string> MODS_BLACKLIST = {"Mod Settings"};
class Mod;
struct ModConVar
{
public:
std::string Name;
std::string DefaultValue;
std::string HelpString;
int Flags;
};
struct ModConCommand
{
public:
std::string Name;
std::string Function;
std::string HelpString;
ScriptContext Context;
int Flags;
};
struct ModScriptCallback
{
public:
ScriptContext Context;
// called before the codecallback is executed
std::string BeforeCallback;
// called after the codecallback has finished executing
std::string AfterCallback;
// called right before the vm is destroyed.
std::string DestroyCallback;
};
struct ModScript
{
public:
std::string Path;
std::string RunOn;
std::vector<ModScriptCallback> Callbacks;
};
// these are pretty much identical, could refactor to use the same stuff?
struct ModVPKEntry
{
public:
bool m_bAutoLoad;
std::string m_sVpkPath;
};
struct ModRpakEntry
{
public:
ModRpakEntry(Mod& parent)
: m_parent(parent)
, m_loadRegex("^thisMatchesNothing^") // discord couldnt give me a funny string
{
}
Mod& m_parent;
std::string m_pakName;
std::regex m_loadRegex;
// these exist purely for backwards compatibility, i don't really like them anymore
// Preload, loads before the first rpak is loaded
bool m_preload = false;
// Postload, this rpak depends on an rpak with this hash
size_t m_dependentPakHash;
};
class Mod
{
public:
// runtime stuff
bool m_bEnabled = true;
bool m_bWasReadSuccessfully = false;
fs::path m_ModDirectory;
bool m_bIsRemote;
// mod.json stuff:
// the mod's name
std::string Name;
// the mod's description
std::string Description;
// the mod's version, should be in semver
std::string Version;
// a download link to the mod, for clients that try to join without the mod
std::string DownloadLink;
// whether clients need the mod to join servers running this mod
bool RequiredOnClient;
// the priority for this mod's files, mods with prio 0 are loaded first, then 1, then 2, etc
int LoadPriority;
// custom scripts used by the mod
std::vector<ModScript> Scripts;
// convars created by the mod
std::vector<ModConVar*> ConVars;
// concommands created by the mod
std::vector<ModConCommand*> ConCommands;
// custom localisation files created by the mod
std::vector<std::string> LocalisationFiles;
// custom script init.nut
std::string initScript;
// other files:
std::vector<ModVPKEntry> Vpks;
std::unordered_map<size_t, std::string> KeyValues;
std::vector<std::string> BinkVideos;
std::string Pdiff; // only need one per mod
std::vector<ModRpakEntry> Rpaks;
// paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite
std::unordered_map<std::string, std::string> RpakAliases;
// starpaks that this mod contains
// there seems to be no nice way to get the rpak that is causing the load of a starpak?
// hashed with STR_HASH
std::vector<size_t> StarpakPaths;
std::unordered_map<std::string, std::string> DependencyConstants;
std::vector<std::string> PluginDependencyConstants;
public:
Mod(fs::path modPath, char* jsonBuf);
private:
void ParseConVars(rapidjson_document& json);
void ParseConCommands(rapidjson_document& json);
void ParseScripts(rapidjson_document& json);
void ParseLocalization(rapidjson_document& json);
void ParseDependencies(rapidjson_document& json);
void ParsePluginDependencies(rapidjson_document& json);
void ParseInitScript(rapidjson_document& json);
};
struct ModOverrideFile
{
public:
Mod* m_pOwningMod;
fs::path m_Path;
};
class ModManager
{
private:
bool m_bHasLoadedMods = false;
bool m_bHasEnabledModsCfg;
rapidjson_document m_EnabledModsCfg;
// precalculated hashes
size_t m_hScriptsRsonHash;
size_t m_hPdefHash;
size_t m_hKBActHash;
public:
std::vector<Mod> m_LoadedMods;
std::unordered_map<std::string, ModOverrideFile> m_ModFiles;
std::unordered_map<std::string, std::string> m_DependencyConstants;
std::unordered_set<std::string> m_PluginDependencyConstants;
public:
ModManager();
void LoadMods();
void UnloadMods();
std::string NormaliseModFilePath(const fs::path path);
void CompileAssetsForFile(const char* filename);
// compile asset type stuff, these are done in files under runtime/compiled/
void BuildScriptsRson();
void TryBuildKeyValues(const char* filename);
void BuildPdef();
void BuildKBActionsList();
};
fs::path GetModFolderPath();
fs::path GetRemoteModFolderPath();
fs::path GetThunderstoreModFolderPath();
fs::path GetCompiledAssetsPath();
extern ModManager* g_pModManager;
|