aboutsummaryrefslogtreecommitdiff
path: root/primedev/mods/modmanager.h
blob: 5f6d07e7514368fdcf4fff764a0df0e3b25b3192 (plain)
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
#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>

const std::string CORE_MOD_FOLDER_SUFFIX = "\\mods\\core";
const std::string MANUAL_MOD_FOLDER_SUFFIX = "\\mods\\manual";
const std::string THUNDERSTORE_LEGACY_MOD_FOLDER_SUFFIX = "\\mods\\thunderstore-legacy";
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"};

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:
	bool m_bAutoLoad;
	std::string m_sPakName;
	std::string m_sLoadAfterPak;
};

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;
	std::unordered_map<std::string, std::string>
		RpakAliases; // paks we alias to other rpaks, e.g. to load sp_crashsite paks on the map mp_crashsite
	std::vector<size_t> StarpakPaths; // 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::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 GetCoreModFolderPath();
fs::path GetManualModFolderPath();
fs::path GetRemoteModFolderPath();
fs::path GetThunderstoreLegacyModFolderPath();
fs::path GetCompiledAssetsPath();

extern ModManager* g_pModManager;