File: scripting.h

package info (click to toggle)
freespace2 24.2.0%2Brepack-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 43,716 kB
  • sloc: cpp: 595,001; ansic: 21,741; python: 1,174; sh: 457; makefile: 248; xml: 181
file content (358 lines) | stat: -rw-r--r-- 10,319 bytes parent folder | download
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#ifndef _SCRIPTING_H
#define _SCRIPTING_H

#include "globalincs/globals.h"
#include "globalincs/pstypes.h"

#include "graphics/2d.h"
#include "scripting/ade.h"
#include "scripting/ade_args.h"
#include "scripting/hook_conditions.h"
#include "scripting/lua/LuaFunction.h"
#include "utils/event.h"

//**********Scripting languages that are possible
#define SC_LUA			(1<<0)

//*************************Scripting structs*************************
#define SCRIPT_END_LIST		NULL

namespace scripting {
struct ScriptingDocumentation;
class HookBase;
}

struct image_desc
{
	char fname[MAX_FILENAME_LEN];
	int handle;
};

struct script_function {
	int language = 0;
	luacpp::LuaFunction function;
};

//-WMC
struct script_hook
{
	//Override
	script_function override_function;

	//Actual hook
	script_function hook_function;
};

extern bool script_hook_valid(script_hook *hook);

//**********Main Conditional Hook stuff

#define MAX_HOOK_CONDITIONS	8

// Conditionals
enum ConditionalType {
	//These conditionals must ONLY contain global conditions.
	//That is, conditions that do not depend on anything specific per-hook (such as a ship firing a weapon),
	//but only conditions that can evaluate exclusively on a global state, identically for all actions. 
	//Per-Hook specific conditionals are added through hook_conditions.h/.cpp and the respective template argument of the hook.
	CHC_NONE        = -1,
	CHC_MISSION,     
	CHC_STATE,       
	CHC_CAMPAIGN,    
	CHC_KEYPRESS,    
	CHC_VERSION,    
	CHC_APPLICATION, 
	CHC_MULTI_SERVER,
	CHC_VR_MODE
};

//Actions
enum ConditionalActions : int32_t {
	CHA_NONE    = -1,
	CHA_ONFRAME,
	CHA_SPLASHSCREEN,
	CHA_HUDDRAW,
	CHA_GAMEINIT,
	CHA_SIMULATION,

	// DO NOT ADD NEW HOOKS HERE. THESE HOOKS ARE EXCLUSIVELY FOR COMPATIBILITY WITH NON-CONDITIONAL GLOBAL HOOKS
	// There is a new Lua Hook API, see hook_api.h
	// There you use either scripting::Hook for non-overridable or scripting::OverridableHook for overridable hooks
	// while also having the option to document when the hook is called and what hook variables are set for it.

	CHA_LAST = CHA_SIMULATION
};

// management stuff
void scripting_state_init();
void scripting_state_close();
void scripting_state_do_frame(float frametime, bool doKeys);

const scripting::HookBase* scripting_string_to_action(const char* action);
ConditionalType scripting_string_to_condition(const char* condition);

struct script_condition
{
	ConditionalType condition_type;
	SCP_string condition_string;
	// stores values evaluated at hook load to optimize later condition checking.
	// currently mostly olg done for the highest impact condition types, according to performance profiling
	// the exact type of information stored varries between hook types.
	// CHC_STATE, CHC_OBJECTTYPE - stores the value of enum matching the name requested by the condition string.
	// CHC_SHIPCLASS, CHC_WEAPONCLASS - stores the index of the info object requested by the condition
	// CHC_VERSION, CHC_APPLICATION - stores validity of the check in 1 for true or 0 for false, as the condition will not change after load.
	// CHC_KEYPRESS - stores the keycode
	// see ConditionedHook::AddCondition for exact implimentation
	int condition_cached_value;
};

class script_action {
public:
	SCP_vector<script_condition> global_conditions;
	SCP_vector<std::unique_ptr<scripting::EvaluatableCondition>> local_conditions;

	script_hook hook;

	bool ConditionsValid(const linb::any& local_condition_data) const;
};

//**********Main script_state function
class script_state
{
  private:
	char StateName[32];

	int Langs;
	struct lua_State *LuaState;
	const struct script_lua_lib_list *LuaLibs;

	//Utility variables
	SCP_vector<image_desc> ScriptImages;
	SCP_unordered_map<int, SCP_vector<script_action>> ConditionalHooks;
	// Scripts can add new hooks at runtime; we collect all hooks to be added here and add them at the end of the current
	// frame to avoid corrupting any iterators that the script system may be using.
	SCP_unordered_map<int, SCP_vector<script_action>> AddedHooks;

	SCP_vector<script_function> GameInitFunctions;

	// Stores references to the Lua values for the hook variables. Uses a raw reference since we do not need the more
	// advanced features of LuaValue
	// values are a vector to provide a stack of values. This is necessary to ensure consistent behavior if a scripting
	// hook is called from within another script (e.g. calls to createShip)
	SCP_unordered_map<SCP_string, SCP_vector<luacpp::LuaReference>> HookVariableValues;

	// ActiveActions lets code that might run scripting hooks know whether any scripts are even registered for it.
	// AssayActions is responsible for keeping it up to date.
	SCP_unordered_map<int, bool> ActiveActions;

	void ParseChunkSub(script_function& out_func, const char* debug_str=NULL);

	void SetLuaSession(struct lua_State *L);

	static void OutputLuaDocumentation(scripting::ScriptingDocumentation& doc,
		const scripting::DocumentationErrorReporter& errorReporter);

public:
	//***Init/Deinit
	script_state(const char *name);

	script_state(const script_state&) = delete;
	script_state& operator=(script_state &i) = delete;

	~script_state();

	/**
	 * @brief Resets the lua state and frees all resources
	 */
	void Clear();

	//***Internal scripting stuff
	int LoadBm(const char* name);
	void UnloadImages();

	lua_State *GetLuaSession(){return LuaState;}

	//***Init functions for langs
	int CreateLuaState();

	//***Get data
	scripting::ScriptingDocumentation OutputDocumentation(const scripting::DocumentationErrorReporter& errorReporter);

	//***Moves data
	//void MoveData(script_state &in);

	template<typename T>
	void SetHookVar(const char *name, char format, T&& value);
	void SetHookObject(const char *name, object *objp);
	void SetHookObjects(int num, ...);
	void RemHookVar(const char *name);
	void RemHookVars(std::initializer_list<SCP_string> names);

	const SCP_unordered_map<SCP_string, SCP_vector<luacpp::LuaReference>>& GetHookVariableReferences();

	//***Hook creation functions
	template <typename T>
	bool EvalStringWithReturn(const char* string, const char* format = nullptr, T* rtn = NULL,
	                          const char* debug_str = nullptr);
	bool EvalString(const char* string, const char* debug_str = nullptr);
	void ParseChunk(script_hook *dest, const char* debug_str=NULL);
	void ParseGlobalChunk(ConditionalActions hookType, const char* debug_str=nullptr, const std::shared_ptr<scripting::HookBase> parentHook=nullptr);
	bool ParseCondition(const char *filename="<Unknown>");
	void AddConditionedHook(int action_id, script_action hook);
	void AssayActions();
	bool IsActiveAction(int hookId);

	void AddGameInitFunction(script_function func);

	//***Hook running functions
	template <typename T>
	int RunBytecode(const script_function& hd, char format = '\0', T* data = nullptr);
	int RunBytecode(const script_function& hd);
	bool IsOverride(const script_hook &hd);
	int RunCondition(int action_type, linb::any local_condition_data);
	bool IsConditionOverride(int action_type, linb::any local_condition_data);

	void RunInitFunctions();

	void ProcessAddedHooks();

	//*****Other functions
	static script_state* GetScriptState(lua_State* L);
	util::event<void, lua_State*> OnStateDestroy;
};

template<typename T>
void script_state::SetHookVar(const char *name, char format, T&& value)
{
	if(format == '\0')
		return;

	if(LuaState != nullptr)
	{
		char fmt[2] = {format, '\0'};
		::scripting::ade_set_args(LuaState, fmt, std::forward<T>(value));
		auto reference = luacpp::UniqueLuaReference::create(LuaState);
		lua_pop(LuaState, 1); // Remove object value from the stack

		HookVariableValues[name].push_back(std::move(reference));
	}
}

template <typename T>
bool script_state::EvalStringWithReturn(const char* string, const char* format, T* rtn, const char* debug_str)
{
	using namespace luacpp;

	size_t string_size = strlen(string);
	char lastchar      = string[string_size - 1];

	if (string[0] == '{') {
		return false;
	}

	if (string[0] == '[' && lastchar != ']') {
		return false;
	}

	size_t s_bufSize = string_size + 8;
	std::string s;
	s.reserve(s_bufSize);
	if (string[0] != '[') {
		if (rtn != nullptr) {
			s = "return ";
		}
		s += string;
	} else {
		s.assign(string + 1, string + string_size);
	}

	SCP_string debug_name;
	if (debug_str == nullptr) {
		debug_name = "String: ";
		debug_name += s;
	} else {
		debug_name = debug_str;
	}

	try {
		auto function = LuaFunction::createFromCode(LuaState, s, debug_name);
		function.setErrorFunction(LuaFunction::createFromCFunction(LuaState, scripting::ade_friendly_error));

		try {
			auto ret = function.call(LuaState);

			if (rtn != nullptr && ret.size() >= 1) {
				auto stack_start = lua_gettop(LuaState);

				auto val = ret.front();
				val.pushValue(LuaState);

				scripting::internal::Ade_get_args_skip      = stack_start;
				scripting::internal::Ade_get_args_lfunction = true;
				scripting::ade_get_args(LuaState, format, rtn);
			}
		} catch (const LuaException&) {
			lua_pop(LuaState, 1);

			return false;
		}
	} catch (const LuaException& e) {
		LuaError(GetLuaSession(), "%s", e.what());

		lua_pop(LuaState, 1);

		return false;
	}

	lua_pop(LuaState, 1);

	return true;
}

template <typename T>
int script_state::RunBytecode(const script_function& hd, char format, T* data)
{
	using namespace luacpp;

	if (!hd.function.isValid()) {
		return 1;
	}

	GR_DEBUG_SCOPE("Lua code");

	try {
		auto ret = hd.function.call(LuaState);

		if (data != nullptr && ret.size() >= 1) {
			auto stack_start = lua_gettop(LuaState);

			auto val = ret.front();
			val.pushValue(LuaState);

			char fmt[2]                                 = {format, '\0'};
			scripting::internal::Ade_get_args_skip      = stack_start;
			scripting::internal::Ade_get_args_lfunction = true;
			scripting::ade_get_args(LuaState, fmt, data);

			// Reset stack again
			lua_settop(LuaState, stack_start);
		}
	} catch (const LuaException&) {
		return 0;
	}

	return 1;
}

//**********Script registration functions
void script_init();

//**********Script globals
extern class script_state Script_system;
extern bool Output_scripting_meta;
extern bool Output_scripting_json;
extern bool Scripting_game_init_run;

//*************************Conditional scripting*************************

#endif //_SCRIPTING_H