-
Notifications
You must be signed in to change notification settings - Fork 0
/
as_connect.cpp
455 lines (372 loc) · 13.5 KB
/
as_connect.cpp
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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
#include <iostream> // cout
#include <assert.h> // assert()
#include <string.h> // strstr()
#include <stdlib.h> // malloc(), free()
#if !(defined(_WIN32) || defined(_WIN64))
#include <sys/time.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#else
#include <conio.h> // kbhit(), getch()
#include <windows.h> // timeGetTime()
#endif
#include "as_connect.h"
// Add Ons
#include "scriptstdstring/scriptstdstring.h"
#include "scriptbuilder/scriptbuilder.h"
using namespace std;
#if !(defined(_WIN32) || defined(_WIN64))
#define UINT unsigned int
// Linux doesn't have timeGetTime(), this essentially does the same
// thing, except this is milliseconds since Epoch (Jan 1st 1970) instead
// of system start. It will work the same though...
DWORD timeGetTime() {
timeval time;
gettimeofday(&time, NULL);
return time.tv_sec*1000 + time.tv_usec/1000;
}
// Linux does have a getch() function in the curses library, but it doesn't
// work like it does on DOS. So this does the same thing, with out the need
// of the curses library.
int getch() {
struct termios oldt, newt;
int ch;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}
#endif
// Function prototypes
static void ConfigureEngine(asIScriptEngine *engine, registerGlobalFuntions rgf);
static int CompileScript(asIScriptEngine *engine, const char *script);
void PrintString(string &str);
void PrintString_Generic(asIScriptGeneric *gen);
void timeGetTime_Generic(asIScriptGeneric *gen);
void LineCallback(asIScriptContext *ctx, DWORD *timeOut);
void MessageCallback(const asSMessageInfo *msg, void *param);
int ExecuteMain(asData *data, const char *decl);
/**
* Initialize AngelScript Engine
*
* @return
*/
asData *asEngineInit(const char *script, registerGlobalFuntions rgf) {
int r;
asData *data = (asData *)malloc(sizeof(asData));
// Create the script engine
data->engine = asCreateScriptEngine();
if( data->engine == 0 ) {
cout << "Failed to create script engine." << endl;
free(data);
return NULL;
}
// The script compiler will write any compiler messages to the callback.
data->engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);
// Configure the script engine with all the functions,
// and variables that the script should be able to use.
ConfigureEngine(data->engine, rgf);
// Compile the script code
r = CompileScript(data->engine, script);
if( r < 0 ) {
data->engine->Release();
free(data);
return NULL;
}
// Create a context that will execute the script.
data->ctx = data->engine->CreateContext();
if( data->ctx == 0 ) {
cout << "Failed to create the context." << endl;
data->engine->Release();
free(data);
return NULL;
}
// We don't want to allow the script to hang the application, e.g. with an
// infinite loop, so we'll use the line callback function to set a timeout
// that will abort the script after a certain time. Before executing the
// script the timeOut variable will be set to the time when the script must
// stop executing.
r = data->ctx->SetLineCallback(asFUNCTION(LineCallback), &data->timeOut,
asCALL_CDECL);
if( r < 0 ) {
cout << "Failed to set the line callback function." << endl;
data->ctx->Release();
data->engine->Release();
free(data);
return NULL;
}
// Execute main function
//ExecuteMain(data, "void main()");
return data;
}
/**
* Destroy AngelScript Engine
*
* @param data Pointer to asData
*/
void asEngineDestroy(asData *data) {
if(data != NULL) {
data->ctx->Release();
data->engine->Release();
free(data);
}
}
/**
* Check execution errors
*
* @param data pointer to asData
* @param func pointer to asIScriptFunction
* @param r Value returned by ctx->Execute function
*
* @return 0 - there is not execution errors; 1 - error was found
*/
int asCheckFuncError(asData *data, asIScriptFunction *func, int r) {
// Check execution result and get return value
if( r != asEXECUTION_FINISHED ) {
// The execution didn't finish as we had planned. Determine why.
if( r == asEXECUTION_ABORTED ) {
cout << "The script was aborted before it could finish. "
"Probably it timed out." << endl;
}
else if( r == asEXECUTION_EXCEPTION )
{
cout << "The script ended with an exception." << endl;
// Write some information about the script exception
asIScriptFunction *func = data->ctx->GetExceptionFunction();
cout << "func: " << func->GetDeclaration() << endl;
cout << "modl: " << func->GetModuleName() << endl;
cout << "sect: " << func->GetScriptSectionName() << endl;
cout << "line: " << data->ctx->GetExceptionLineNumber() << endl;
cout << "desc: " << data->ctx->GetExceptionString() << endl;
}
else {
cout << "The script ended for some unforeseen reason (" << r << ")."
<< endl;
}
return 1;
}
return 0;
}
/**
* Compiler messages callback
*
* @param msg
* @param param
*/
void MessageCallback(const asSMessageInfo *msg, void *param) {
const char *type = "ERR ";
if( msg->type == asMSGTYPE_WARNING )
type = "WARN";
else if( msg->type == asMSGTYPE_INFORMATION )
type = "INFO";
printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col,
type, msg->message);
}
/**
* ConfigureEngine
*
* @param engine
*/
static void ConfigureEngine(asIScriptEngine *engine, registerGlobalFuntions rgf) {
int r;
// Register the script string type
// Look at the implementation for this function for more information
// on how to register a custom string type, and other object types.
RegisterStdString(engine);
if( !strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
{
// Register the functions that the scripts will be allowed to use.
// Note how the return code is validated with an assert(). This helps
// us discover where a problem occurs, and doesn't pollute the code
// with a lot of if's. If an error occurs in release mode it will
// be caught when a script is being built, so it is not necessary
// to do the verification here as well.
r = engine->RegisterGlobalFunction("void Print(string &in)",
asFUNCTION(PrintString), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint GetSystemTime()",
asFUNCTION(timeGetTime), asCALL_STDCALL); assert( r >= 0 );
}
else
{
// Notice how the registration is almost identical to the above.
r = engine->RegisterGlobalFunction("void Print(string &in)",
asFUNCTION(PrintString_Generic), asCALL_GENERIC);
assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint GetSystemTime()",
asFUNCTION(timeGetTime_Generic), asCALL_GENERIC);
assert( r >= 0 );
}
// Register custom global functions
if(rgf != NULL) rgf(engine);
// It is possible to register the functions, properties, and types in
// configuration groups as well. When compiling the scripts it then
// be defined which configuration groups should be available for that
// script. If necessary a configuration group can also be removed from
// the engine, so that the engine configuration could be changed
// without having to recompile all the scripts.
}
/**
* Execute main function
*
* @param data
* @return
*/
int ExecuteMain(asData *data, const char* decl) {
int r;
// Find the function for the function we want to execute.
asIScriptFunction *func = data->engine->GetModule(0)->GetFunctionByDecl(decl);
if( func == 0 ) {
cout << "The function " << decl << " was not found." << endl;
return -1;
}
// Prepare the script context with the function we wish to execute. Prepare()
// must be called on the context before each new script function that will be
// executed. Note, that if you intend to execute the same function several
// times, it might be a good idea to store the function returned by
// GetFunctionByDecl(), so that this relatively slow call can be skipped.
r = data->ctx->Prepare(func);
if( r < 0 ) {
cout << "Failed to prepare the context." << endl;
return -1;
}
// Set the timeout before executing the function. Give the function 1 sec
// to return before we'll abort it.
data->timeOut = timeGetTime() + 1000;
// Execute the function
const char *line = "===========";
cout << "AngelScript initialize..." << endl << line << endl
<< "Executing script function '" << decl << "':" << endl;
cout << "---" << endl;
r = data->ctx->Execute();
cout << "---" << endl;
if(!asCheckFuncError(data, func, r)) {
cout << line << endl << "The script finished successfully" << endl;
}
return 0;
}
int regScript(asIScriptEngine *engine, const char* script) {
int r;
// The builder is a helper class that will load the script file,
// search for #include directives, and load any included files as
// well.
CScriptBuilder builder;
// Build the script. If there are any compiler messages they will
// be written to the message stream that we set right after creating the
// script engine. If there are no errors, and no warnings, nothing will
// be written to the stream.
r = builder.StartNewModule(engine, 0);
if( r < 0 )
{
cout << "Failed to start new module" << endl;
return r;
}
r = builder.AddSectionFromMemory("GOVNO", script);
if( r < 0 )
{
cout << "Failed to add script file" << endl;
return r;
}
r = builder.BuildModule();
if( r < 0 )
{
cout << "Failed to build the module" << endl;
return r;
}
// The engine doesn't keep a copy of the script sections after Build() has
// returned. So if the script needs to be recompiled, then all the script
// sections must be added again.
// If we want to have several scripts executing at different times but
// that have no direct relation with each other, then we can compile them
// into separate script modules. Each module use their own namespace and
// scope, so function names, and global variables will not conflict with
// each other.
return 0;
}
/**
* CompileScript
*
* @param engine
* @return
*/
static int CompileScript(asIScriptEngine *engine, const char* script) {
int r;
// The builder is a helper class that will load the script file,
// search for #include directives, and load any included files as
// well.
CScriptBuilder builder;
// Build the script. If there are any compiler messages they will
// be written to the message stream that we set right after creating the
// script engine. If there are no errors, and no warnings, nothing will
// be written to the stream.
r = builder.StartNewModule(engine, 0);
if( r < 0 )
{
cout << "Failed to start new module" << endl;
return r;
}
r = builder.AddSectionFromFile(script);
if( r < 0 )
{
cout << "Failed to add script file" << endl;
return r;
}
r = builder.BuildModule();
if( r < 0 )
{
cout << "Failed to build the module" << endl;
return r;
}
// The engine doesn't keep a copy of the script sections after Build() has
// returned. So if the script needs to be recompiled, then all the script
// sections must be added again.
// If we want to have several scripts executing at different times but
// that have no direct relation with each other, then we can compile them
// into separate script modules. Each module use their own namespace and
// scope, so function names, and global variables will not conflict with
// each other.
return 0;
}
/**
* line callback function
*
* @param ctx
* @param timeOut
*/
void LineCallback(asIScriptContext *ctx, DWORD *timeOut) {
// If the time out is reached we abort the script
if( *timeOut < timeGetTime() )
ctx->Abort();
// It would also be possible to only suspend the script,
// instead of aborting it. That would allow the application
// to resume the execution where it left of at a later
// time, by simply calling Execute() again.
}
/**
* Function implementation with native calling convention
*
* @param str
*/
void PrintString(string &str) {
cout << str;
}
/**
* Function implementation with generic script interface
*
* @param gen
*/
void PrintString_Generic(asIScriptGeneric *gen) {
string *str = (string*)gen->GetArgAddress(0);
cout << *str;
}
/**
* Function wrapper is needed when native calling conventions are not supported
*
* @param gen
*/
void timeGetTime_Generic(asIScriptGeneric *gen) {
gen->SetReturnDWord(timeGetTime());
}