1 #ifndef _SCRIPTING_H
2 #define _SCRIPTING_H
3 
4 #include "globalincs/globals.h"
5 #include "globalincs/pstypes.h"
6 
7 #include "graphics/2d.h"
8 #include "scripting/ade.h"
9 #include "scripting/ade_args.h"
10 #include "scripting/lua/LuaFunction.h"
11 #include "utils/event.h"
12 
13 //**********Scripting languages that are possible
14 #define SC_LUA			(1<<0)
15 
16 //*************************Scripting structs*************************
17 #define SCRIPT_END_LIST		NULL
18 
19 namespace scripting {
20 struct ScriptingDocumentation;
21 }
22 
23 struct image_desc
24 {
25 	char fname[MAX_FILENAME_LEN];
26 	int handle;
27 };
28 
29 struct script_function {
30 	int language = 0;
31 	luacpp::LuaFunction function;
32 };
33 
34 //-WMC
35 struct script_hook
36 {
37 	//Override
38 	script_function override_function;
39 
40 	//Actual hook
41 	script_function hook_function;
42 };
43 
44 extern bool script_hook_valid(script_hook *hook);
45 
46 //**********Main Conditional Hook stuff
47 
48 #define MAX_HOOK_CONDITIONS	8
49 
50 // Conditionals
51 enum ConditionalType {
52 	CHC_NONE        = -1,
53 	CHC_MISSION     = 0,
54 	CHC_SHIP        = 1,
55 	CHC_SHIPCLASS   = 2,
56 	CHC_SHIPTYPE    = 3,
57 	CHC_STATE       = 4,
58 	CHC_CAMPAIGN    = 5,
59 	CHC_WEAPONCLASS = 6,
60 	CHC_OBJECTTYPE  = 7,
61 	CHC_KEYPRESS    = 8,
62 	CHC_ACTION      = 9,
63 	CHC_VERSION     = 10,
64 	CHC_APPLICATION = 11,
65 };
66 
67 //Actions
68 enum ConditionalActions : int32_t {
69 	CHA_NONE    = -1,
70 	CHA_DEATH,
71 	CHA_ONFRAME,
72 	CHA_COLLIDESHIP,
73 	CHA_COLLIDEWEAPON,
74 	CHA_COLLIDEDEBRIS,
75 	CHA_COLLIDEASTEROID,
76 	CHA_HUDDRAW,
77 	CHA_OBJECTRENDER,
78 	CHA_SPLASHSCREEN,
79 	CHA_GAMEINIT,
80 	CHA_MISSIONSTART,
81 	CHA_MISSIONEND,
82 	CHA_MOUSEMOVED,
83 	CHA_MOUSEPRESSED,
84 	CHA_MOUSERELEASED,
85 	CHA_KEYPRESSED,
86 	CHA_KEYRELEASED,
87 	CHA_ONSTATESTART,
88 	CHA_ONSTATEEND,
89 	CHA_ONWEAPONDELETE,
90 	CHA_ONWPEQUIPPED,
91 	CHA_ONWPFIRED,
92 	CHA_ONWPSELECTED,
93 	CHA_ONWPDESELECTED,
94 	CHA_GAMEPLAYSTART,
95 	CHA_ONTURRETFIRED,
96 	CHA_PRIMARYFIRE,
97 	CHA_SECONDARYFIRE,
98 	CHA_ONSHIPARRIVE,
99 	CHA_COLLIDEBEAM,
100 	CHA_ONACTION,
101 	CHA_ONACTIONSTOPPED,
102 	CHA_MSGRECEIVED,
103 	CHA_HUDMSGRECEIVED,
104 	CHA_AFTERBURNSTART,
105 	CHA_AFTERBURNEND,
106 	CHA_BEAMFIRE,
107 	CHA_SIMULATION,
108 	CHA_LOADSCREEN,
109 	CHA_CMISSIONACCEPT,
110 	CHA_ONSHIPDEPART,
111 	CHA_ONWEAPONCREATED,
112 	CHA_ONWAYPOINTSDONE,
113 	CHA_ONSUBSYSDEATH,
114 	CHA_ONGOALSCLEARED,
115 	CHA_ONBRIEFSTAGE,
116 
117 	// DO NOT ADD NEW HOOKS HERE
118 	// There is a new Lua Hook API, see hook_api.h
119 	// There you use either scripting::Hook for non-overridable or scripting::OverridableHook for overridable hooks
120 	// while also having the option to document when the hook is called and what hook variables are set for it.
121 
122 	CHA_LAST = CHA_ONBRIEFSTAGE,
123 };
124 
125 // management stuff
126 void scripting_state_init();
127 void scripting_state_close();
128 void scripting_state_do_frame(float frametime);
129 
130 int32_t scripting_string_to_action(const char* action);
131 ConditionalType scripting_string_to_condition(const char* condition);
132 
133 struct script_condition
134 {
135 	ConditionalType condition_type = CHC_NONE;
136 	SCP_string condition_string;
137 	// stores values evaluated at hook load to optimize later condition checking.
138 	// currently mostly olg done for the highest impact condition types, according to performance profiling
139 	// the exact type of information stored varries between hook types.
140 	// CHC_STATE, CHC_OBJECTTYPE - stores the value of enum matching the name requested by the condition string.
141 	// CHC_SHIPCLASS, CHC_WEAPONCLASS - stores the index of the info object requested by the condition
142 	// 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.
143 	// see ConditionedHook::AddCondition for exact implimentation
144 	int condition_cached_value = -1;
145 };
146 
147 struct script_action
148 {
149 	int32_t action_type {CHA_NONE};
150 	script_hook hook;
151 };
152 
153 class ConditionedHook
154 {
155 public:
156 	SCP_vector<script_action> Actions;
157 	SCP_vector<script_condition> Conditions;
158 	bool AddCondition(script_condition *sc);
159 	bool AddAction(script_action *sa);
160 
161 	bool ConditionsValid(int action, class object *objp=NULL, int more_data = 0);
162 	bool IsOverride(class script_state *sys, int action);
163 	bool Run(class script_state* sys, int action);
164 };
165 
166 //**********Main script_state function
167 class script_state
168 {
169   private:
170 	char StateName[32];
171 
172 	int Langs;
173 	struct lua_State *LuaState;
174 	const struct script_lua_lib_list *LuaLibs;
175 
176 	//Utility variables
177 	SCP_vector<image_desc> ScriptImages;
178 	SCP_vector<ConditionedHook> ConditionalHooks;
179 
180 	SCP_vector<script_function> GameInitFunctions;
181 
182 	// Stores references to the Lua values for the hook variables. Uses a raw reference since we do not need the more
183 	// advanced features of LuaValue
184 	// values are a vector to provide a stack of values. This is necessary to ensure consistent behavior if a scripting
185 	// hook is called from within another script (e.g. calls to createShip)
186 	SCP_unordered_map<SCP_string, SCP_vector<luacpp::LuaReference>> HookVariableValues;
187 	// ActiveActions lets code that might run scripting hooks know whether any scripts are even registered for it.
188 	// AssayActions is responsible for keeping it up to date.
189 	bool ActiveActions[ConditionalActions::CHA_LAST+1];
190 
191 	void ParseChunkSub(script_function& out_func, const char* debug_str=NULL);
192 
193 	void SetLuaSession(struct lua_State *L);
194 
195 	static void OutputLuaDocumentation(scripting::ScriptingDocumentation& doc,
196 		const scripting::DocumentationErrorReporter& errorReporter);
197 
198 	//Internal Lua helper functions
199 	void EndLuaFrame();
200 
201 public:
202 	//***Init/Deinit
203 	script_state(const char *name);
204 
205 	script_state(const script_state&) = delete;
206 	script_state& operator=(script_state &i) = delete;
207 
208 	~script_state();
209 
210 	/**
211 	 * @brief Resets the lua state and frees all resources
212 	 */
213 	void Clear();
214 
215 	//***Internal scripting stuff
216 	int LoadBm(const char* name);
217 	void UnloadImages();
218 
GetLuaSession()219 	lua_State *GetLuaSession(){return LuaState;}
220 
221 	//***Init functions for langs
222 	int CreateLuaState();
223 
224 	//***Get data
225 	scripting::ScriptingDocumentation OutputDocumentation(const scripting::DocumentationErrorReporter& errorReporter);
226 
227 	//***Moves data
228 	//void MoveData(script_state &in);
229 
230 	template<typename T>
231 	void SetHookVar(const char *name, char format, T&& value);
232 	void SetHookObject(const char *name, object *objp);
233 	void SetHookObjects(int num, ...);
234 	void RemHookVar(const char *name);
235 	void RemHookVars(std::initializer_list<SCP_string> names);
236 
237 	const SCP_unordered_map<SCP_string, SCP_vector<luacpp::LuaReference>>& GetHookVariableReferences();
238 
239 	//***Hook creation functions
240 	template <typename T>
241 	bool EvalStringWithReturn(const char* string, const char* format = nullptr, T* rtn = NULL,
242 	                          const char* debug_str = nullptr);
243 	bool EvalString(const char* string, const char* debug_str = nullptr);
244 	void ParseChunk(script_hook *dest, const char* debug_str=NULL);
245 	void ParseGlobalChunk(ConditionalActions hookType, const char* debug_str=nullptr);
246 	bool ParseCondition(const char *filename="<Unknown>");
247 	void AddConditionedHook(ConditionedHook hook);
248 	void AssayActions();
249 	bool IsActiveAction(ConditionalActions action_id);
250 
251 	void AddGameInitFunction(script_function func);
252 
253 	//***Hook running functions
254 	template <typename T>
255 	int RunBytecode(script_function& hd, char format = '\0', T* data = nullptr);
256 	int RunBytecode(script_function& hd);
257 	bool IsOverride(script_hook &hd);
258 	int RunCondition(int condition, object* objp = nullptr, int more_data = 0);
259 	bool IsConditionOverride(int action, object *objp=NULL);
260 
261 	void RunInitFunctions();
262 
263 	//*****Other functions
264 	void EndFrame();
265 
266 	static script_state* GetScriptState(lua_State* L);
267 
268 	util::event<void, lua_State*> OnStateDestroy;
269 };
270 
271 template<typename T>
SetHookVar(const char * name,char format,T && value)272 void script_state::SetHookVar(const char *name, char format, T&& value)
273 {
274 	if(format == '\0')
275 		return;
276 
277 	if(LuaState != nullptr)
278 	{
279 		char fmt[2] = {format, '\0'};
280 		::scripting::ade_set_args(LuaState, fmt, std::forward<T>(value));
281 		auto reference = luacpp::UniqueLuaReference::create(LuaState);
282 		lua_pop(LuaState, 1); // Remove object value from the stack
283 
284 		HookVariableValues[name].push_back(std::move(reference));
285 	}
286 }
287 
288 template <typename T>
EvalStringWithReturn(const char * string,const char * format,T * rtn,const char * debug_str)289 bool script_state::EvalStringWithReturn(const char* string, const char* format, T* rtn, const char* debug_str)
290 {
291 	using namespace luacpp;
292 
293 	size_t string_size = strlen(string);
294 	char lastchar      = string[string_size - 1];
295 
296 	if (string[0] == '{') {
297 		return false;
298 	}
299 
300 	if (string[0] == '[' && lastchar != ']') {
301 		return false;
302 	}
303 
304 	size_t s_bufSize = string_size + 8;
305 	std::string s;
306 	s.reserve(s_bufSize);
307 	if (string[0] != '[') {
308 		if (rtn != nullptr) {
309 			s = "return ";
310 		}
311 		s += string;
312 	} else {
313 		s.assign(string + 1, string + string_size);
314 	}
315 
316 	SCP_string debug_name;
317 	if (debug_str == nullptr) {
318 		debug_name = "String: ";
319 		debug_name += s;
320 	} else {
321 		debug_name = debug_str;
322 	}
323 
324 	try {
325 		auto function = LuaFunction::createFromCode(LuaState, s, debug_name);
326 		function.setErrorFunction(LuaFunction::createFromCFunction(LuaState, scripting::ade_friendly_error));
327 
328 		try {
329 			auto ret = function.call(LuaState);
330 
331 			if (rtn != nullptr && ret.size() >= 1) {
332 				auto stack_start = lua_gettop(LuaState);
333 
334 				auto val = ret.front();
335 				val.pushValue(LuaState);
336 
337 				scripting::internal::Ade_get_args_skip      = stack_start;
338 				scripting::internal::Ade_get_args_lfunction = true;
339 				scripting::ade_get_args(LuaState, format, rtn);
340 			}
341 		} catch (const LuaException&) {
342 			return false;
343 		}
344 	} catch (const LuaException& e) {
345 		LuaError(GetLuaSession(), "%s", e.what());
346 
347 		return false;
348 	}
349 
350 	return true;
351 }
352 
353 template <typename T>
RunBytecode(script_function & hd,char format,T * data)354 int script_state::RunBytecode(script_function& hd, char format, T* data)
355 {
356 	using namespace luacpp;
357 
358 	if (!hd.function.isValid()) {
359 		return 1;
360 	}
361 
362 	GR_DEBUG_SCOPE("Lua code");
363 
364 	try {
365 		auto ret = hd.function.call(LuaState);
366 
367 		if (data != nullptr && ret.size() >= 1) {
368 			auto stack_start = lua_gettop(LuaState);
369 
370 			auto val = ret.front();
371 			val.pushValue(LuaState);
372 
373 			char fmt[2]                                 = {format, '\0'};
374 			scripting::internal::Ade_get_args_skip      = stack_start;
375 			scripting::internal::Ade_get_args_lfunction = true;
376 			scripting::ade_get_args(LuaState, fmt, data);
377 
378 			// Reset stack again
379 			lua_settop(LuaState, stack_start);
380 		}
381 	} catch (const LuaException&) {
382 		return 0;
383 	}
384 
385 	return 1;
386 }
387 
388 //**********Script registration functions
389 void script_init();
390 
391 //**********Script globals
392 extern class script_state Script_system;
393 extern bool Output_scripting_meta;
394 extern bool Output_scripting_json;
395 
396 //*************************Conditional scripting*************************
397 
398 #endif //_SCRIPTING_H
399