Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
ServerScriptEngine.cpp
Go to the documentation of this file.
1
2// =============================================================================
3// This file is adopted from rorserver at commit 4a7109ae2d9a081ccfdad8cc696dc54efe49acb3
4// Changes from the original are marked with "//RIGSOFRODS"
5// =============================================================================
6
7/*
8This file is part of "Rigs of Rods Server" (Relay mode)
9
10Copyright 2009 Thomas Fischer
11Copyright 2014+ Rigs of Rods Community
12
13"Rigs of Rods Server" is free software: you can redistribute it
14and/or modify it under the terms of the GNU General Public License
15as published by the Free Software Foundation, either version 3
16of the License, or (at your option) any later version.
17
18"Rigs of Rods Server" is distributed in the hope that it will
19be useful, but WITHOUT ANY WARRANTY; without even the implied
20warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License
24along with Foobar. If not, see <http://www.gnu.org/licenses/>.
25*/
26
27// created on 22th of June 2009 by Thomas Fischer
28#ifdef USE_ANGELSCRIPT
29
30#include "ServerScriptEngine.h"
31
32#include "scriptstdstring/scriptstdstring.h" // angelscript addon
33#include "scriptmath/scriptmath.h" // angelscript addon
34#include "scriptmath3d/scriptmath3d.h" // angelscript addon
35#include "scriptarray/scriptarray.h" // angelscript addon
36#include "scriptdictionary/scriptdictionary.h" // angelscript addon
37#include "scriptany/scriptany.h" // angelscript addon
38#include "SocketW.h"
39
41#include "ServerScriptConfig.h"
43#include "OgreScriptBuilder.h" // RIGSOFRODS: Use OGRE to load files.
44#include "PlatformUtils.h" // RIGSOFRODS: For PathCombine
45#include "ScriptEngine.h" // RIGSOFRODS: For ScriptEngine::unloadBotByUid()
46
47#include "Console.h" // RIGSOFRODS: For putNetMessage()
48#include "Utils.h"
49
50#include <cstdio>
51#include <stdlib.h>
52
53
54using namespace std;
55using namespace AngelScript; // RIGSOFRODS: Added to match rorserver's style
56using namespace RoR;
57
58#include <assert.h>
59# define assert_net(expr) ROR_ASSERT(expr)
60
61
62#ifdef __GNUC__
63
64#include <string.h>
65
66#endif
67
68#include <thread>
69#include <future>
70
71
72
73
74// Stream_register_t wrapper
76 return std::string(reg->name);
77}
78
79
80
82 seq(sequencer),
83 engine(0),
84 context(0)
85{
86 init();
87}
88
90 // thread must be stopped
92
93 // Clean up
95 if (engine) engine->Release();
96 if (context) context->Release();
97}
98
100 if (!engine) return;
101
102 for (std::map<std::string, callbackList>::iterator typeit = callbacks.begin();
103 typeit != callbacks.end(); ++typeit) {
104 for (callbackList::iterator it = typeit->second.begin(); it != typeit->second.end(); ++it) {
105 if (it->obj)
106 it->obj->Release();
107 }
108 typeit->second.clear();
109 }
110 callbacks.clear();
111}
112
113int ServerScriptEngine::loadScript(std::string scriptname) {
114 if (scriptname.empty()) return 0;
115
116 int r;
117 OgreScriptBuilder builder;
118 builder.SetResourceGroup(RGN_SERVER_SCRIPTS); // RIGSOFRODS: Server scripts only load from restricted location to avoid mixups with client scripts.
119
120 r = builder.StartNewModule(engine, "script");
121 if (r < 0) {
122 Logger::Log(LOG_ERROR, "ScriptEngine: Unknown error while starting a new script module.");
123 return 1;
124 }
125
126 r = builder.AddSectionFromFile(scriptname.c_str());
127 if (r < 0) {
128 Logger::Log(LOG_ERROR, "ScriptEngine: Unknown error while adding a new section from file.");
129 return 1;
130 }
131
132 r = builder.BuildModule();
133 if (r < 0) {
134 if (r == asINVALID_CONFIGURATION)
135 Logger::Log(LOG_ERROR, "ScriptEngine: The engine configuration is invalid.");
136
137 else if (r == asERROR)
138 Logger::Log(LOG_ERROR, "ScriptEngine: The script failed to build.");
139
140 else if (r == asBUILD_IN_PROGRESS)
141 Logger::Log(LOG_ERROR, "ScriptEngine: Another thread is currently building.");
142
143 else if (r == asINIT_GLOBAL_VARS_FAILED)
145 "ScriptEngine: It was not possible to initialize at least one of the global variables.");
146
147 else
148 Logger::Log(LOG_ERROR, "ScriptEngine: Unknown error while building the script.");
149
150 return 1;
151 }
152
153 // Get the newly created module
154 asIScriptModule *mod = builder.GetModule();
155
156 // get some other optional functions
157 asIScriptFunction *func;
158
159 func = mod->GetFunctionByDecl("void frameStep(float)");
160 if (func) addCallback("frameStep", func, NULL);
161
162 func = mod->GetFunctionByDecl("void playerDeleted(int, int)");
163 if (func) addCallback("playerDeleted", func, NULL);
164
165 func = mod->GetFunctionByDecl("void playerAdded(int)");
166 if (func) addCallback("playerAdded", func, NULL);
167
168 func = mod->GetFunctionByDecl("int streamAdded(int, StreamRegister@)");
169 if (func) addCallback("streamAdded", func, NULL);
170
171 func = mod->GetFunctionByDecl("int playerChat(int, string msg)");
172 if (func) addCallback("playerChat", func, NULL);
173
174 func = mod->GetFunctionByDecl("void gameCmd(int, string)");
175 if (func) addCallback("gameCmd", func, NULL);
176
177 func = mod->GetFunctionByDecl("void curlStatus(curlStatusType, int, int, string, string)");
178 if (func) addCallback("curlStatus", func, NULL);
179
180 // Create and configure our context
181 context = engine->CreateContext();
182 //context->SetLineCallback(asMETHOD(ScriptEngine,LineCallback), this, asCALL_THISCALL);
183 context->SetExceptionCallback(asMETHOD(ServerScriptEngine, ExceptionCallback), this, asCALL_THISCALL);
184
185 // Find the function that is to be called.
186 func = mod->GetFunctionByDecl("void main()");
187 if (!func) {
188 // The function couldn't be found. Instruct the script writer to include the
189 // expected function in the script.
191 "ScriptEngine: The script must have the function 'void main()'. Please add it and try again.");
192 return 1;
193 }
194
195 // prepare and execute the main function
196 context->Prepare(func);
197 Logger::Log(LOG_INFO, "ScriptEngine: Executing main()");
198 r = context->Execute();
199 if (r != asEXECUTION_FINISHED) {
200 // The execution didn't complete as expected. Determine what happened.
201 if (r == asEXECUTION_EXCEPTION) {
202 // An exception occurred, let the script writer know what happened so it can be corrected.
204 "ScriptEngine: An exception '%s' occurred. Please correct the code in file '%s' and try again.",
205 context->GetExceptionString(), scriptname.c_str());
206 }
207 }
208
209 return 0;
210}
211
213 // Stop thread first
214 this->StopTimerThread();
216 context->Unprepare();
217 engine->ReturnContext(context);
218 context = nullptr;
219 if (engine) {
220 asIScriptModule *mod = engine->GetModule("script");
221 if (mod) {
222 engine->DiscardModule("script");
223 }
224 }
225
226
227}
228
229void ServerScriptEngine::ExceptionCallback(asIScriptContext *ctx, void *param) {
230 const asIScriptFunction *function = ctx->GetExceptionFunction();
231 Logger::Log(LOG_INFO, "--- exception ---");
232 Logger::Log(LOG_INFO, "desc: %s", ctx->GetExceptionString());
233 Logger::Log(LOG_INFO, "func: %s", function->GetDeclaration());
234 Logger::Log(LOG_INFO, "modl: %s", function->GetModuleName());
235 const char* sectName = nullptr;
236 int col, line = ctx->GetExceptionLineNumber(&col, &sectName);
237 Logger::Log(LOG_INFO, "sect: %s", sectName ? sectName : "NULL");
238 Logger::Log(LOG_INFO, "line: %d,%d", line, col);
239
240 // Show the call stack with the variables
241 Logger::Log(LOG_INFO, "--- call stack ---");
242 char tmp[2048] = "";
243 for (asUINT n = 0; n < ctx->GetCallstackSize(); n++) {
244 function = ctx->GetFunction(n);
245 const char* fnDeclSectionName = nullptr;
246 int fnDeclRow = 0;
247 int fnDeclCol = 0;
248 function->GetDeclaredAt(&fnDeclSectionName, &fnDeclRow, &fnDeclCol);
249 sprintf(tmp, "%s (%d): %s", fnDeclSectionName, fnDeclRow,
250 function->GetDeclaration());
251 Logger::Log(LOG_INFO, tmp);
252 PrintVariables(ctx, n);
253 }
254 Logger::Log(LOG_INFO, "--- end of script exception message ---");
255}
256
257void ServerScriptEngine::LineCallback(asIScriptContext *ctx, void *param) {
258 char tmp[1024] = "";
259 asIScriptEngine *engine = ctx->GetEngine();
260 int col;
261 const char *sectionName;
262 int line = ctx->GetLineNumber(0, &col, &sectionName);
263 int indent = ctx->GetCallstackSize();
264 for (int n = 0; n < indent; n++)
265 sprintf(tmp + n, " ");
266 const asIScriptFunction *function = engine->GetFunctionById(0);
267 sprintf(tmp + indent, "%s:%s:%s:%d,%d", function->GetModuleName(), sectionName,
268 function->GetDeclaration(),
269 line, col);
270
271 strcat(tmp, "");
272 Logger::Log(LOG_INFO, tmp);
273
274// PrintVariables(ctx, -1);
275}
276
277void ServerScriptEngine::PrintVariables(asIScriptContext *ctx, int stackLevel) {
278 asIScriptEngine *engine = ctx->GetEngine();
279
280 // First print the this pointer if this is a class method
281 int typeId = ctx->GetThisTypeId(stackLevel);
282 void *varPointer = ctx->GetThisPointer(stackLevel);
283 if (typeId) {
284 Logger::Log(LOG_INFO, " this = 0x%x", varPointer);
285 }
286
287 // Print the value of each variable, including parameters
288 int numVars = ctx->GetVarCount(stackLevel);
289 for (int n = 0; n < numVars; n++) {
290 int typeId = 0;
291 const char* varName = nullptr;
292 ctx->GetVar(n, stackLevel, &varName, &typeId);
293 void *varPointer = ctx->GetAddressOfVar(n, stackLevel);
294 if (typeId == asTYPEID_INT32) {
295 Logger::Log(LOG_INFO, " %s = %d", ctx->GetVarDeclaration(n, stackLevel), *(int *) varPointer);
296 } else if (typeId == asTYPEID_FLOAT) {
297 Logger::Log(LOG_INFO, " %s = %f", ctx->GetVarDeclaration(n, stackLevel), *(float *) varPointer);
298 } else if (typeId & asTYPEID_SCRIPTOBJECT) {
299 asIScriptObject *obj = (asIScriptObject *) varPointer;
300 if (obj)
301 Logger::Log(LOG_INFO, " %s = {...}", ctx->GetVarDeclaration(n, stackLevel));
302 else
303 Logger::Log(LOG_INFO, " %s = <null>", ctx->GetVarDeclaration(n, stackLevel));
304 } else if (typeId == engine->GetTypeIdByDecl("string")) {
305 std::string *str = (std::string *) varPointer;
306 if (str)
307 Logger::Log(LOG_INFO, " %s = '%s'", ctx->GetVarDeclaration(n, stackLevel), str->c_str());
308 else
309 Logger::Log(LOG_INFO, " %s = <null>", ctx->GetVarDeclaration(n, stackLevel));
310 } else {
311 Logger::Log(LOG_INFO, " %s = {...}", ctx->GetVarDeclaration(n, stackLevel));
312 }
313 }
314}
315
316// continue with initializing everything
318 int result;
319
320 // Create the script engine
321 engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
322
323 // Set the message callback to receive information on errors in human readable form.
324 // It's recommended to do this right after the creation of the engine, because if
325 // some registration fails the engine may send valuable information to the message
326 // stream.
327 result = engine->SetMessageCallback(asMETHOD(ServerScriptEngine, msgCallback), this, asCALL_THISCALL);
328 if (result < 0) {
329 if (result == asINVALID_ARG) {
331 "ScriptEngine: One of the arguments is incorrect, e.g. obj is null for a class method.");
332 return;
333 } else if (result == asNOT_SUPPORTED) {
334 Logger::Log(LOG_ERROR, "ScriptEngine: The arguments are not supported, e.g. asCALL_GENERIC.");
335 return;
336 }
337 Logger::Log(LOG_ERROR, "ScriptEngine: Unknown error while setting up message callback");
338 return;
339 }
340
341 // AngelScript doesn't have a built-in string type, as there is no definite standard
342 // string type for C++ applications. Every developer is free to register it's own string type.
343 // The SDK do however provide a standard add-on for registering a string type, so it's not
344 // necessary to register your own string type if you don't want to.
345 RegisterStdString(engine);
346 RegisterScriptArray(engine,
347 true); // true = allow arrays to be registered as type[] (e.g. int[]). Needed for backwards compatibillity.
348 RegisterStdStringUtils(engine); // depends on string and array
349 RegisterScriptMath3D(engine); // depends on string
350 RegisterScriptMath(engine);
351 RegisterScriptDictionary(engine);
353 RegisterScriptAny(engine);
354
355 Logger::Log(LOG_INFO, "ScriptEngine: Registration of libs done, now custom things");
356
357 // Register ServerScript class
358 result = engine->RegisterObjectType("ServerScriptClass", sizeof(ServerScript), asOBJ_REF | asOBJ_NOCOUNT);
359 assert_net(result >= 0);
360 result = engine->RegisterObjectMethod("ServerScriptClass", "void Log(const string &in)",
361 asMETHOD(ServerScript, log), asCALL_THISCALL);
362 assert_net(result >= 0);
363 result = engine->RegisterObjectMethod("ServerScriptClass", "void say(const string &in, int uid, int type)",
364 asMETHOD(ServerScript, say), asCALL_THISCALL);
365 assert_net(result >= 0);
366 result = engine->RegisterObjectMethod("ServerScriptClass", "void kick(int kuid, const string &in)",
367 asMETHOD(ServerScript, kick), asCALL_THISCALL);
368 assert_net(result >= 0);
369 result = engine->RegisterObjectMethod("ServerScriptClass", "void ban(int buid, const string &in)",
370 asMETHOD(ServerScript, ban), asCALL_THISCALL);
371 assert_net(result >= 0);
372 result = engine->RegisterObjectMethod("ServerScriptClass", "bool unban(int buid)", asMETHOD(ServerScript, unban),
373 asCALL_THISCALL);
374 assert_net(result >= 0);
375 result = engine->RegisterObjectMethod("ServerScriptClass", "int cmd(int uid, string cmd)",
376 asMETHOD(ServerScript, sendGameCommand), asCALL_THISCALL);
377 assert_net(result >= 0);
378 result = engine->RegisterObjectMethod("ServerScriptClass", "void curlRequestAsync(string url, string displayname)",
379 asMETHOD(ServerScript, curlRequestAsync), asCALL_THISCALL);
380
381 assert_net(result >= 0);
382 result = engine->RegisterObjectMethod("ServerScriptClass", "int getNumClients()",
383 asMETHOD(ServerScript, getNumClients), asCALL_THISCALL);
384 assert_net(result >= 0);
385 result = engine->RegisterObjectMethod("ServerScriptClass", "string getUserName(int uid)",
386 asMETHOD(ServerScript, getUserName), asCALL_THISCALL);
387 assert_net(result >= 0);
388 result = engine->RegisterObjectMethod("ServerScriptClass", "void setUserName(int uid, const string &in)",
389 asMETHOD(ServerScript, setUserName), asCALL_THISCALL);
390 assert_net(result >= 0);
391 result = engine->RegisterObjectMethod("ServerScriptClass", "string getUserAuth(int uid)",
392 asMETHOD(ServerScript, getUserAuth), asCALL_THISCALL);
393 assert_net(result >= 0);
394 result = engine->RegisterObjectMethod("ServerScriptClass", "int getUserAuthRaw(int uid)",
395 asMETHOD(ServerScript, getUserAuthRaw), asCALL_THISCALL);
396 assert_net(result >= 0);
397 result = engine->RegisterObjectMethod("ServerScriptClass", "void setUserAuthRaw(int uid, int)",
398 asMETHOD(ServerScript, setUserAuthRaw), asCALL_THISCALL);
399 assert_net(result >= 0);
400 result = engine->RegisterObjectMethod("ServerScriptClass", "int getUserColourNum(int uid)",
401 asMETHOD(ServerScript, getUserColourNum), asCALL_THISCALL);
402 assert_net(result >= 0);
403 result = engine->RegisterObjectMethod("ServerScriptClass", "void setUserColourNum(int uid, int)",
404 asMETHOD(ServerScript, setUserColourNum), asCALL_THISCALL);
405 assert_net(result >= 0);
406 result = engine->RegisterObjectMethod("ServerScriptClass", "void broadcastUserInfo(int)",
407 asMETHOD(ServerScript, broadcastUserInfo), asCALL_THISCALL);
408 assert_net(result >= 0);
409 result = engine->RegisterObjectMethod("ServerScriptClass", "string getUserToken(int uid)",
410 asMETHOD(ServerScript, getUserToken), asCALL_THISCALL);
411 assert_net(result >= 0);
412 result = engine->RegisterObjectMethod("ServerScriptClass", "string getUserVersion(int uid)",
413 asMETHOD(ServerScript, getUserVersion), asCALL_THISCALL);
414 assert_net(result >= 0);
415 result = engine->RegisterObjectMethod("ServerScriptClass", "string getUserIPAddress(int uid)",
416 asMETHOD(ServerScript, getUserIPAddress), asCALL_THISCALL);
417 assert_net(result >= 0);
418 result = engine->RegisterObjectMethod("ServerScriptClass", "string getServerTerrain()",
419 asMETHOD(ServerScript, getServerTerrain), asCALL_THISCALL);
420 assert_net(result >= 0);
421 result = engine->RegisterObjectMethod("ServerScriptClass", "int getTime()", asMETHOD(ServerScript, getTime),
422 asCALL_THISCALL);
423 assert_net(result >= 0);
424 result = engine->RegisterObjectMethod("ServerScriptClass", "int getStartTime()",
425 asMETHOD(ServerScript, getStartTime), asCALL_THISCALL);
426 assert_net(result >= 0);
427 result = engine->RegisterObjectMethod("ServerScriptClass",
428 "void setCallback(const string &in, const string &in, ?&in)",
429 asMETHOD(ServerScript, setCallback), asCALL_THISCALL);
430 assert_net(result >= 0);
431 result = engine->RegisterObjectMethod("ServerScriptClass",
432 "void deleteCallback(const string &in, const string &in, ?&in)",
433 asMETHOD(ServerScript, deleteCallback), asCALL_THISCALL);
434 assert_net(result >= 0);
435 result = engine->RegisterObjectMethod("ServerScriptClass", "void throwException(const string &in)",
436 asMETHOD(ServerScript, throwException), asCALL_THISCALL);
437 assert_net(result >= 0);
438 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_version()",
439 asMETHOD(ServerScript, get_version), asCALL_THISCALL);
440 assert_net(result >= 0);
441 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_asVersion()",
442 asMETHOD(ServerScript, get_asVersion), asCALL_THISCALL);
443 assert_net(result >= 0);
444 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_protocolVersion()",
445 asMETHOD(ServerScript, get_protocolVersion), asCALL_THISCALL);
446 assert_net(result >= 0);
447 result = engine->RegisterObjectMethod("ServerScriptClass", "uint get_maxClients()",
448 asMETHOD(ServerScript, get_maxClients), asCALL_THISCALL);
449 assert_net(result >= 0);
450 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_serverName()",
451 asMETHOD(ServerScript, get_serverName), asCALL_THISCALL);
452 assert_net(result >= 0);
453 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_IPAddr()",
454 asMETHOD(ServerScript, get_IPAddr), asCALL_THISCALL);
455 assert_net(result >= 0);
456 result = engine->RegisterObjectMethod("ServerScriptClass", "uint get_listenPort()",
457 asMETHOD(ServerScript, get_listenPort), asCALL_THISCALL);
458 assert_net(result >= 0);
459 result = engine->RegisterObjectMethod("ServerScriptClass", "int get_serverMode()",
460 asMETHOD(ServerScript, get_serverMode), asCALL_THISCALL);
461 assert_net(result >= 0);
462 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_owner()", asMETHOD(ServerScript, get_owner),
463 asCALL_THISCALL);
464 assert_net(result >= 0);
465 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_website()",
466 asMETHOD(ServerScript, get_website), asCALL_THISCALL);
467 assert_net(result >= 0);
468 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_ircServ()",
469 asMETHOD(ServerScript, get_ircServ), asCALL_THISCALL);
470 assert_net(result >= 0);
471 result = engine->RegisterObjectMethod("ServerScriptClass", "string get_voipServ()",
472 asMETHOD(ServerScript, get_voipServ), asCALL_THISCALL);
473 assert_net(result >= 0);
474 result = engine->RegisterObjectMethod("ServerScriptClass", "int rangeRandomInt(int, int)",
475 asMETHOD(ServerScript, rangeRandomInt), asCALL_THISCALL);
476 assert_net(result >= 0);
477 ServerScript *serverscript = new ServerScript(this, seq);
478 result = engine->RegisterGlobalProperty("ServerScriptClass server", serverscript);
479 assert_net(result >= 0);
480
481 // Register RoRnet::StreamRegister class
482 result = engine->RegisterObjectType("StreamRegister", sizeof(RoRnet::StreamRegister),
483 asOBJ_REF | asOBJ_NOCOUNT);
484 assert_net(result >= 0);
485 result = engine->RegisterObjectMethod("StreamRegister", "string getName()",
486 asFUNCTION(stream_register_get_name), asCALL_CDECL_OBJFIRST);
487 assert_net(result >= 0); // (No property accessor on purpose)
488 result = engine->RegisterObjectProperty("StreamRegister", "int type",
489 offsetof(RoRnet::StreamRegister, type));
490 assert_net(result >= 0);
491 result = engine->RegisterObjectProperty("StreamRegister", "int status",
492 offsetof(RoRnet::StreamRegister, status));
493 assert_net(result >= 0);
494 result = engine->RegisterObjectProperty("StreamRegister", "int origin_sourceid",
495 offsetof(RoRnet::StreamRegister, origin_sourceid));
496 assert_net(result >= 0);
497 result = engine->RegisterObjectProperty("StreamRegister", "int origin_streamid",
498 offsetof(RoRnet::StreamRegister, origin_streamid));
499 assert_net(result >= 0);
500
501
502 // Register ServerType enum for the server.serverMode attribute
503 result = engine->RegisterEnum("ServerType");
504 assert_net(result >= 0);
505 result = engine->RegisterEnumValue("ServerType", "SERVER_LAN", SERVER_LAN);
506 assert_net(result >= 0);
507 result = engine->RegisterEnumValue("ServerType", "SERVER_INET", SERVER_INET);
508 assert_net(result >= 0);
509 result = engine->RegisterEnumValue("ServerType", "SERVER_AUTO", SERVER_AUTO);
510 assert_net(result >= 0);
511
512 // Register publish mode enum for the playerChat callback
513 result = engine->RegisterEnum("broadcastType");
514 assert_net(result >= 0);
515 result = engine->RegisterEnumValue("broadcastType", "BROADCAST_AUTO", BROADCAST_AUTO);
516 assert_net(result >= 0);
517 result = engine->RegisterEnumValue("broadcastType", "BROADCAST_BLOCK", BROADCAST_BLOCK);
518 assert_net(result >= 0);
519 result = engine->RegisterEnumValue("broadcastType", "BROADCAST_NORMAL", BROADCAST_NORMAL);
520 assert_net(result >= 0);
521 result = engine->RegisterEnumValue("broadcastType", "BROADCAST_AUTHED", BROADCAST_AUTHED);
522 assert_net(result >= 0);
523 result = engine->RegisterEnumValue("broadcastType", "BROADCAST_ALL", BROADCAST_ALL);
524 assert_net(result >= 0);
525
526 // Register authorizations
527 result = engine->RegisterEnum("authType");
528 assert_net(result >= 0);
529
530 result = engine->RegisterEnumValue("authType", "AUTH_NONE", RoRnet::AUTH_NONE);
531 assert_net(result >= 0);
532 result = engine->RegisterEnumValue("authType", "AUTH_ADMIN", RoRnet::AUTH_ADMIN);
533 assert_net(result >= 0);
534 result = engine->RegisterEnumValue("authType", "AUTH_RANKED", RoRnet::AUTH_RANKED);
535 assert_net(result >= 0);
536 result = engine->RegisterEnumValue("authType", "AUTH_MOD", RoRnet::AUTH_MOD);
537 assert_net(result >= 0);
538 result = engine->RegisterEnumValue("authType", "AUTH_BOT", RoRnet::AUTH_BOT);
539 assert_net(result >= 0);
540 result = engine->RegisterEnumValue("authType", "AUTH_BANNED", RoRnet::AUTH_BANNED);
541 assert_net(result >= 0);
542 result = engine->RegisterEnumValue("authType", "AUTH_ALL", 0xFFFFFFFF);
543 assert_net(result >= 0);
544
545 // Register serverSayType
546 result = engine->RegisterEnum("serverSayType");
547 assert_net(result >= 0);
548 result = engine->RegisterEnumValue("serverSayType", "FROM_SERVER", FROM_SERVER);
549 assert_net(result >= 0);
550 result = engine->RegisterEnumValue("serverSayType", "FROM_HOST", FROM_HOST);
551 assert_net(result >= 0);
552 result = engine->RegisterEnumValue("serverSayType", "FROM_MOTD", FROM_MOTD);
553 assert_net(result >= 0);
554 result = engine->RegisterEnumValue("serverSayType", "FROM_RULES", FROM_RULES);
555 assert_net(result >= 0);
556
557 // Register curl update type for `curlStatus` callback
558 result = engine->RegisterEnum("curlStatusType");
559 assert_net(result >= 0);
560 result = engine->RegisterEnumValue("curlStatusType", "CURL_STATUS_INVALID", CURL_STATUS_INVALID);
561 assert_net(result >= 0);
562 result = engine->RegisterEnumValue("curlStatusType", "CURL_STATUS_START", CURL_STATUS_START);
563 assert_net(result >= 0);
564 result = engine->RegisterEnumValue("curlStatusType", "CURL_STATUS_PROGRESS", CURL_STATUS_PROGRESS);
565 assert_net(result >= 0);
566 result = engine->RegisterEnumValue("curlStatusType", "CURL_STATUS_SUCCESS", CURL_STATUS_SUCCESS);
567 assert_net(result >= 0);
568 result = engine->RegisterEnumValue("curlStatusType", "CURL_STATUS_FAILURE", CURL_STATUS_FAILURE);
569 assert_net(result >= 0);
570
571 // register constants
572 result = engine->RegisterGlobalProperty("const int TO_ALL", (void *) &TO_ALL);
573 assert_net(result >= 0);
574
575
576 Logger::Log(LOG_INFO, "ScriptEngine: Registration done");
577}
578
579void ServerScriptEngine::msgCallback(const asSMessageInfo *msg) {
580 const char *type = "Error";
581 if (msg->type == asMSGTYPE_INFORMATION)
582 type = "Info";
583 else if (msg->type == asMSGTYPE_WARNING)
584 type = "Warning";
585
586 Logger::Log(LOG_INFO, "ScriptEngine: %s (%d, %d): %s = %s", msg->section, msg->row, msg->col, type, msg->message);
587}
588
590
591
592 if (!engine) return 0;
593 if (!context) context = engine->CreateContext();
594 int r;
595
596 // Copy the callback list, because the callback list itself may get changed while executing the script
597 callbackList queue(callbacks["frameStep"]);
598
599 // loop over all callbacks
600 for (unsigned int i = 0; i < queue.size(); ++i) {
601 // prepare the call
602 r = context->Prepare(queue[i].func);
603 if (r < 0) continue;
604
605 // Set the object if present (if we don't set it, then we call a global function)
606 if (queue[i].obj != NULL) {
607 context->SetObject(queue[i].obj);
608 if (r < 0) continue;
609 }
610
611 // Set the arguments
612 context->SetArgFloat(0, dt);
613
614 // Execute it
615 r = context->Execute();
616 }
617
618 // Collect garbage
619 engine->GarbageCollect(asGC_ONE_STEP);
620
621 return 0;
622}
623
624void ServerScriptEngine::playerDeleted(int uid, int crash) {
625
626
627 if (!engine) return;
628 if (!context) context = engine->CreateContext();
629 int r;
630
631 // Copy the callback list, because the callback list itself may get changed while executing the script
632 callbackList queue(callbacks["playerDeleted"]);
633
634 // loop over all callbacks
635 for (unsigned int i = 0; i < queue.size(); ++i) {
636 // prepare the call
637 r = context->Prepare(queue[i].func);
638 if (r < 0) continue;
639
640 // Set the object if present (if we don't set it, then we call a global function)
641 if (queue[i].obj != NULL) {
642 context->SetObject(queue[i].obj);
643 if (r < 0) continue;
644 }
645
646 // Set the arguments
647 context->SetArgDWord(0, uid);
648 context->SetArgDWord(1, crash);
649
650 // Execute it
651 r = context->Execute();
652 }
653
654
655
656 return;
657}
658
660{
661
662 if (!engine) return;
663 if (!context) context = engine->CreateContext();
664 int r;
665
666 // Copy the callback list, because the callback list itself may get changed while executing the script
667 callbackList queue(callbacks["playerAdded"]);
668
669 // loop over all callbacks
670 for (unsigned int i = 0; i < queue.size(); ++i) {
671 // prepare the call
672 r = context->Prepare(queue[i].func);
673 if (r < 0) continue;
674
675 // Set the object if present (if we don't set it, then we call a global function)
676 if (queue[i].obj != NULL) {
677 context->SetObject(queue[i].obj);
678 if (r < 0) continue;
679 }
680
681 // Set the arguments
682 context->SetArgDWord(0, user.uniqueid);
683
684 // Execute it
685 r = context->Execute();
686 }
687 return;
688}
689
691
692 if (!engine) return 0;
693 if (!context) context = engine->CreateContext();
694 int r;
695 int ret = BROADCAST_AUTO;
696
697 // Copy the callback list, because the callback list itself may get changed while executing the script
698 callbackList queue(callbacks["streamAdded"]);
699
700 // loop over all callbacks
701 for (unsigned int i = 0; i < queue.size(); ++i) {
702 // prepare the call
703 r = context->Prepare(queue[i].func);
704 if (r < 0) continue;
705
706 // Set the object if present (if we don't set it, then we call a global function)
707 if (queue[i].obj != NULL) {
708 context->SetObject(queue[i].obj);
709 if (r < 0) continue;
710 }
711
712 // Set the arguments
713 context->SetArgDWord(0, uid);
714 context->SetArgObject(1, (void *) reg);
715
716 // Execute it
717 r = context->Execute();
718 if (r == asEXECUTION_FINISHED) {
719 int newRet = context->GetReturnDWord();
720
721 // Only use the new result if it's more restrictive than what we already had
722 if (newRet > ret)
723 ret = newRet;
724 }
725 }
726
727 return ret;
728}
729
730int ServerScriptEngine::playerChat(int uid, std::string msg) {
731
732 if (!engine) return 0;
733 if (!context) context = engine->CreateContext();
734 int r;
735 int ret = BROADCAST_AUTO;
736
737 // Copy the callback list, because the callback list itself may get changed while executing the script
738 callbackList queue(callbacks["playerChat"]);
739
740 // loop over all callbacks
741 for (unsigned int i = 0; i < queue.size(); ++i) {
742 // prepare the call
743 r = context->Prepare(queue[i].func);
744 if (r < 0) continue;
745
746 // Set the object if present (if we don't set it, then we call a global function)
747 if (queue[i].obj != NULL) {
748 context->SetObject(queue[i].obj);
749 if (r < 0) continue;
750 }
751
752 // Set the arguments
753 context->SetArgDWord(0, uid);
754 context->SetArgObject(1, (void *) &msg);
755
756 // Execute it
757 r = context->Execute();
758 if (r == asEXECUTION_FINISHED) {
759 int newRet = context->GetReturnDWord();
760
761 // Only use the new result if it's more restrictive than what we already had
762 if (newRet > ret)
763 ret = newRet;
764 }
765 }
766
767 return ret;
768}
769
770void ServerScriptEngine::gameCmd(int uid, const std::string &cmd) {
771
772 if (!engine) return;
773 if (!context) context = engine->CreateContext();
774 int r;
775
776 // Copy the callback list, because the callback list itself may get changed while executing the script
777 callbackList queue(callbacks["gameCmd"]);
778
779 // loop over all callbacks
780 for (unsigned int i = 0; i < queue.size(); ++i) {
781 // prepare the call
782 r = context->Prepare(queue[i].func);
783 if (r < 0) continue;
784
785 // Set the object if present (if we don't set it, then we call a global function)
786 if (queue[i].obj != NULL) {
787 context->SetObject(queue[i].obj);
788 if (r < 0) continue;
789 }
790
791 // Set the arguments
792 context->SetArgDWord(0, uid);
793 context->SetArgObject(1, (void *) &cmd);
794
795 // Execute it
796 r = context->Execute();
797 }
798
799 return;
800}
801
802void ServerScriptEngine::curlStatus(ServerScriptCurlStatusType type, int n1, int n2, string displayname, string message)
803{
804 // Params `n1` and `n2` depend on status type :
805 // - for CURL_STATUS_PROGRESS, n1 = bytes downloaded, n2 = total bytes,
806 // - otherwise, n1 = CURL return code, n2 = HTTP result code.
807 // -------------------------------------------------------------------
808
809 if (!engine) return;
810 if (!context) context = engine->CreateContext();
811 int r;
812
813 // Copy the callback list, because the callback list itself may get changed while executing the script
814 callbackList queue(callbacks["curlStatus"]);
815
816 // loop over all callbacks
817 for (unsigned int i = 0; i < queue.size(); ++i) {
818 // prepare the call
819 r = context->Prepare(queue[i].func);
820 if (r < 0) continue;
821
822 // Set the object if present (if we don't set it, then we call a global function)
823 if (queue[i].obj != NULL) {
824 context->SetObject(queue[i].obj);
825 if (r < 0) continue;
826 }
827
828 // Set the arguments
829 context->SetArgDWord(0, (asDWORD)type);
830 context->SetArgDWord(1, (asDWORD)n1);
831 context->SetArgDWord(2, (asDWORD)n2);
832 context->SetArgObject(3, (void*)&displayname);
833 context->SetArgObject(4, (void*)&message);
834
835 // Execute it
836 r = context->Execute();
837 }
838}
839
841 while (this->GetTimerThreadState() == ThreadState::RUNNING) {
842 // sleep 200 miliseconds
843#ifndef _WIN32
844 usleep(200000);
845#else // _WIN32
846 Sleep(200);
847#endif // _WIN32
848
849 // call script
850 seq->frameStepScripts(200);
851 }
852}
853
855 std::lock_guard<std::mutex> scoped_lock(m_timer_thread_mutex);
857 Logger::Log(LOG_DEBUG, "ScriptEngine: starting framestep thread");
860 }
861}
862
867
869 {
870 std::lock_guard<std::mutex> scoped_lock(m_timer_thread_mutex);
872 return;
874 }
875
876 Logger::Log(LOG_DEBUG, "ScriptEngine: stopping framestep thread...");
877 m_timer_thread.join();
878 Logger::Log(LOG_DEBUG, "ScriptEngine: stopped framestep thread.");
879
880 {
881 std::lock_guard<std::mutex> scoped_lock(m_timer_thread_mutex);
883 }
884}
885
886void ServerScriptEngine::setException(const std::string &message) {
887 if (!engine || !context) {
888 // There's not much we can do, except for logging the message
889 Logger::Log(LOG_INFO, "--- script exception ---");
890 Logger::Log(LOG_INFO, " desc: %s", (message.c_str()));
891 Logger::Log(LOG_INFO, "--- end of script exception message ---");
892 } else
893 context->SetException(message.c_str());
894}
895
896void ServerScriptEngine::addCallbackScript(const std::string &type, const std::string &_func, asIScriptObject *obj) {
897 if (!engine) return;
898
899 // get the function declaration and check the type at the same time
900 std::string funcDecl = "";
901 if (type == "frameStep")
902 funcDecl = "void " + _func + "(float)";
903 else if (type == "playerChat")
904 funcDecl = "int " + _func + "(int, const string &in)";
905 else if (type == "gameCmd")
906 funcDecl = "void " + _func + "(int, const string &in)";
907 else if (type == "playerAdded")
908 funcDecl = "void " + _func + "(int)";
909 else if (type == "playerDeleted")
910 funcDecl = "void " + _func + "(int, int)";
911 else if (type == "streamAdded")
912 funcDecl = "int " + _func + "(int, StreamRegister@)";
913 else if (type == "curlStatus")
914 funcDecl = "void " + _func + "(curlStatusType, int, int, string, string)";
915 else {
916 setException("Type " + type +
917 " does not exist! Possible type strings: 'frameStep', 'playerChat', 'gameCmd', 'playerAdded', 'playerDeleted', 'streamAdded'.");
918 return;
919 }
920
921 asIScriptFunction *func;
922 if (obj) {
923 // search for a method in the class
924 asITypeInfo *objType = obj->GetObjectType();
925 func = objType->GetMethodByDecl(funcDecl.c_str());
926 if (!func) {
927 // give a nice error message that says that the method was not found.
928 func = objType->GetMethodByName(_func.c_str());
929 if (func)
930 setException("Method '" + std::string(func->GetDeclaration(false)) + "' was found in '" +
931 objType->GetName() + "' but the correct declaration is: '" + funcDecl + "'.");
932 else
933 setException("Method '" + funcDecl + "' was not found in '" + objType->GetName() + "'.");
934 return;
935 }
936 } else {
937 // search for a global function
938 asIScriptModule *mod = engine->GetModule("script");
939 func = mod->GetFunctionByDecl(funcDecl.c_str());
940 if (!func) {
941 // give a nice error message that says that the function was not found.
942 func = mod->GetFunctionByName(_func.c_str());
943 if (func)
944 setException("Function '" + std::string(func->GetDeclaration(false)) +
945 "' was found, but the correct declaration is: '" + funcDecl + "'.");
946 else
947 setException("Function '" + funcDecl + "' was not found.");
948 return;
949 }
950 }
951
952 if (callbackExists(type, func, obj))
953 Logger::Log(LOG_INFO, "ScriptEngine: error: Function '" + std::string(func->GetDeclaration(false)) +
954 "' is already a callback for '" + type + "'.");
955 else
956 addCallback(type, func, obj);
957}
958
959void ServerScriptEngine::addCallback(const std::string &type, asIScriptFunction *func, asIScriptObject *obj) {
960 if (!engine) return;
961
962 if (obj) {
963 // We're about to store a reference to the object, so let's tell the script engine about that
964 // This avoids the object from going out of scope while we're still trying to access it.
965 // BUT: it prevents local objects from being destroyed automatically....
966 engine->AddRefScriptObject(obj, obj->GetObjectType());
967 }
968
969 // Add the function to the list
971 tmp.obj = obj;
972 tmp.func = func;
973 callbacks[type].push_back(tmp);
974
975 // Do we need to start the frameStep thread?
976 if (type == "frameStep") {
978 }
979
980 // finished :)
981 Logger::Log(LOG_INFO, "ScriptEngine: success: Added a '" + type + "' callback for: " +
982 std::string(func->GetDeclaration(true)));
983}
984
985void ServerScriptEngine::deleteCallbackScript(const std::string &type, const std::string &_func, asIScriptObject *obj) {
986 if (!engine) return;
987
988 // get the function declaration and check the type at the same time
989 std::string funcDecl = "";
990 if (type == "frameStep")
991 funcDecl = "void " + _func + "(float)";
992 else if (type == "playerChat")
993 funcDecl = "int " + _func + "(int, const string &in)";
994 else if (type == "gameCmd")
995 funcDecl = "void " + _func + "(int, const string &in)";
996 else if (type == "playerAdded")
997 funcDecl = "void " + _func + "(int)";
998 else if (type == "playerDeleted")
999 funcDecl = "void " + _func + "(int, int)";
1000 else if (type == "streamAdded")
1001 funcDecl = "int " + _func + "(int, StreamRegister@)";
1002 else if (type == "curlStatus")
1003 funcDecl = "void " + _func + "(curlStatusType, int, int, string, string)";
1004 else {
1005 setException("Type " + type +
1006 " does not exist! Possible type strings: 'frameStep', 'playerChat', 'gameCmd', 'playerAdded', 'playerDeleted', 'streamAdded'.");
1007 Logger::Log(LOG_INFO, "ScriptEngine: error: Failed to remove callback: " + _func);
1008 return;
1009 }
1010
1011 asIScriptFunction *func;
1012 if (obj) {
1013 // search for a method in the class
1014 asITypeInfo *objType = obj->GetObjectType();
1015 func = objType->GetMethodByDecl(funcDecl.c_str());
1016 if (!func) {
1017 // give a nice error message that says that the method was not found.
1018 func = objType->GetMethodByName(_func.c_str());
1019 if (func)
1020 setException("Method '" + std::string(func->GetDeclaration(false)) + "' was found in '" +
1021 objType->GetName() + "' but the correct declaration is: '" + funcDecl + "'.");
1022 else
1023 setException("Method '" + funcDecl + "' was not found in '" + objType->GetName() + "'.");
1024 Logger::Log(LOG_INFO, "ScriptEngine: error: Failed to remove callback: " + funcDecl);
1025 return;
1026 }
1027 } else {
1028 // search for a global function
1029 asIScriptModule *mod = engine->GetModule("script");
1030 func = mod->GetFunctionByDecl(funcDecl.c_str());
1031 if (!func) {
1032 // give a nice error message that says that the function was not found.
1033 func = mod->GetFunctionByName(_func.c_str());
1034 if (func)
1035 setException("Function '" + std::string(func->GetDeclaration(false)) +
1036 "' was found, but the correct declaration is: '" + funcDecl + "'.");
1037 else
1038 setException("Function '" + funcDecl + "' was not found.");
1039 Logger::Log(LOG_INFO, "ScriptEngine: error: Failed to remove callback: " + funcDecl);
1040 return;
1041 }
1042 }
1043 deleteCallback(type, func, obj);
1044}
1045
1046void ServerScriptEngine::deleteCallback(const std::string &type, asIScriptFunction *func, asIScriptObject *obj) {
1047 if (!engine) return;
1048
1049 for (callbackList::iterator it = callbacks[type].begin(); it != callbacks[type].end(); ++it) {
1050 if (it->obj == obj && it->func == func) {
1051 callbacks[type].erase(it);
1052 Logger::Log(LOG_INFO, "ScriptEngine: success: removed a '" + type + "' callback: " +
1053 std::string(func->GetDeclaration(true)));
1054 if (obj)
1055 engine->ReleaseScriptObject(obj, obj->GetObjectType());
1056 return;
1057 }
1058 }
1059 Logger::Log(LOG_INFO, "ScriptEngine: error: failed to remove callback: " + std::string(func->GetDeclaration(true)));
1060}
1061
1062bool ServerScriptEngine::callbackExists(const std::string &type, asIScriptFunction *func, asIScriptObject *obj) {
1063 if (!engine) return false;
1064
1065 for (callbackList::iterator it = callbacks[type].begin(); it != callbacks[type].end(); ++it) {
1066 if (it->obj == obj && it->func == func)
1067 return true;
1068 }
1069 return false;
1070}
1071#endif // USE_ANGELSCRIPT
#define ROR_ASSERT(_EXPR)
Definition Application.h:40
#define RGN_SERVER_SCRIPTS
Definition Application.h:54
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
@ SERVER_LAN
@ SERVER_INET
@ SERVER_AUTO
#define assert_net(expr)
std::string stream_register_get_name(RoRnet::StreamRegister *reg)
@ LOG_ERROR
@ LOG_INFO
@ LOG_DEBUG
@ LOG_WARN
void SetResourceGroup(const Ogre::String &rg)
bool callbackExists(const std::string &type, AngelScript::asIScriptFunction *func, AngelScript::asIScriptObject *obj)
This checks if a script callback exists.
void init()
This function initialzies the engine and registeres all types.
void TimerThreadMain()
A loop that periodically the frameStep() script callback.
void deleteAllCallbacks()
Deletes all script callbacks.
AngelScript::asIScriptEngine * engine
instance of the scripting engine
void addCallback(const std::string &type, AngelScript::asIScriptFunction *func, AngelScript::asIScriptObject *obj)
Adds a script callback.
void playerDeleted(int uid, int crash=0)
void playerAdded(RoRnet::UserInfo &user)
void deleteCallback(const std::string &type, AngelScript::asIScriptFunction *func, AngelScript::asIScriptObject *obj)
Deletes a script callback.
AngelScript::asIScriptContext * context
context in which all scripting happens
void curlStatus(ServerScriptCurlStatusType type, int n1, int n2, std::string displayname, std::string message)
Params n1, n2 and message depend on status type :
std::map< std::string, callbackList > callbacks
A map containing the script callbacks by type.
void setException(const std::string &message)
Sets an exception that aborts the currently running script and shows the exception in the log file.
void LineCallback(AngelScript::asIScriptContext *ctx, void *param)
unused
void addCallbackScript(const std::string &type, const std::string &func, AngelScript::asIScriptObject *obj)
This method checks and converts the parameters and then adds a script callback.
void gameCmd(int uid, const std::string &cmd)
ServerScriptSequencer * seq
int streamAdded(int uid, RoRnet::StreamRegister *reg)
int loadScript(std::string scriptName)
void ExceptionCallback(AngelScript::asIScriptContext *ctx, void *param)
This callback gets called when an exception occurs in the script.
ServerScriptEngine(ServerScriptSequencer *sequencer)
void PrintVariables(AngelScript::asIScriptContext *ctx, int stackLevel)
This logs all variables and their values at the specified stack level.
int playerChat(int uid, std::string msg)
void msgCallback(const AngelScript::asSMessageInfo *msg)
This is the callback function that gets called when script error occur.
void deleteCallbackScript(const std::string &type, const std::string &_func, AngelScript::asIScriptObject *obj)
This method checks and converts the parameters and then deletes a script callback.
void Log(ServerLogLevel level, const char *format,...)
std::vector< rorserver_callback_t > callbackList
static const int TO_ALL
void RegisterScriptFile_Native(AngelScript::asIScriptEngine *engine)
@ CURL_STATUS_START
New CURL request started, n1/n2 both 0.
@ CURL_STATUS_INVALID
Should never be reported.
@ CURL_STATUS_SUCCESS
CURL request finished, n1 = CURL return code, n2 = HTTP result code, message = received payload.
@ CURL_STATUS_FAILURE
CURL request finished, n1 = CURL return code, n2 = HTTP result code, message = CURL error string.
@ CURL_STATUS_PROGRESS
Download in progress, n1 = bytes downloaded, n2 = total bytes.
@ AUTH_MOD
moderator status
Definition RoRnet.h:80
@ AUTH_NONE
no authentication
Definition RoRnet.h:77
@ AUTH_BANNED
banned
Definition RoRnet.h:82
@ AUTH_BOT
bot status
Definition RoRnet.h:81
@ AUTH_RANKED
ranked status
Definition RoRnet.h:79
@ AUTH_ADMIN
admin on the server
Definition RoRnet.h:78
This struct holds the information for a script callback.
AngelScript::asIScriptObject * obj
The object instance that will need to be used with the function.
AngelScript::asIScriptFunction * func
The function or method pointer that will be called.
< Sent from the client to server and vice versa, to broadcast a new stream
Definition RoRnet.h:160
char name[128]
file name
Definition RoRnet.h:165
uint32_t uniqueid
user unique id
Definition RoRnet.h:191