• Create Account

AngelScript 1.8.0 WIP #2 (2004/06/26)

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

47 replies to this topic

#21 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 29 June 2004 - 05:02 AM

very cool. I also found something called ac_string.

How would I register char* object properties using bstr?

class foo { public: char *bar; };

how would I register bar? Would I just make it a bstr and register like this:

engine->RegisterObjectProperty("foo", "bstr bar", offsetof(foo, bar));

Or, would I just make bar a bstr in both my game and the scripting engine?

#22 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 29 June 2004 - 05:52 AM

Since bstr relies on some extra information than a normal C string has, you'll have to make your "char *bar" a bstr in the game as well.

It is ok to convert a bstr to char * with a static cast for reading purposes, and even updates within the string. But it is not ok to convert a char * to bstr with a static cast, to do that conversion you should allocate memory for the bstr and then copy the string to it.

A bstr has a hidden property that gives the length of the string. The functions registered for bstr uses this hidden property to copy string data, etc.

I recognize that strings are somewhat complicated matter to deal with, but I have yet to find a better solution. The real problem lies in that the script writer shouldn't have to worry about memory leaks when using strings. This unfortunately requires a little more work by the application writer. At least with version 1.8.0 users are free to change the way strings are handled if they don't like the way bstr works.

[Edited by - WitchLord on June 29, 2004 12:52:36 PM]

#23Andreas Jonsson  Moderators   -  Reputation: 4363

Like
Likes
Like

Posted 29 June 2004 - 06:50 AM

The last AP was me ;)
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#24 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 29 June 2004 - 07:26 AM

Nice, I got that working, but have some questions...

When I want to make a string in my host app that will hold script filenames, but using BSTR's, would I use asBSTR *file or
asBSTR file? Could I just say file = new(BSTR)?

Also, let's say I have an object "game" from the class CGame. Do I register everything from the class "CGame" that I want the engine to be able to use, and then register a pointer to the object "game" so that the engine can actually change info in the object? Hmm..That was confusing.

btw:
I think angelcode is a very cool scripting language. I tried out Lua, but it is pretty useless unless you can bind stuff to the scripting engine. Then I tried to compile LuaBind forever, but never had any luck. I'm glad you developed angelscript so you could just open a project and build it.

#25 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 29 June 2004 - 09:05 AM

Though I posted right before here, I came up with some more questions:

I am also confused about how to write scripts.

Do I make a "main" function in the script, and that will be the entry point?

I also am wondering how fast the engine compiles scripts. Should I pre-compile all the scripts when the games starts, and then only execute scripts when the time comes, or is that impossible? In larger games with a lot of scripts, I would assume that to be too demanding on memory. But, let's say I want to run the same script several times per frame in my game, I shouldn't recomplile each time, correct? So how can I load a script, compile it, and store it in a buffer or something, and then execute the buffer?

how do scripts execute...Why would I ever suspend the execution of a scripts?

I found this code in one of the example programs. Should I use these functions to load and execute scripts?

{
FILE *f = fopen(filename, "rb");
if( f == 0 )
{
hge->System_Log("Failed to open the script file.");
return -1;
}
int len = _filelength(_fileno(f));
acCString code;
code.Allocate(len, false);
fclose(f);
if( c == 0 )
{
return -1;
}
int r = engine->AddScriptSection(filename, code, len);
if( r System_Log("An error occured while adding the script section.");
return r;
}
return 0;
}

void AS_CALL COutStream::Write(const char *text) { printf(text); }

int CompileScript()
{
COutStream out;
int r = engine->Build(&out);
if( r System_Log("Failed to compile script."); return -1; }
// If we wish to build again, the script sections has to be added again.
// Now we will verify the interface of the script functions we wish to call
acCString str;
str.Allocate(256, false);
if( r System_Log("Failed to retrieve declaration of function 'main'"); return -1; }
if( str != "float main(float, float, float)" ) { hge->System_Log("Function main should be declared as follows 'float main(float, float, float)"); return -1; }
return 0;
}

int ExecuteScript()
{
// Create a context in which the script will be executed.
// Several contexts may exist in parallel, holding the execution
// of various scripts in the same engine. At the moment contexts are not
// thread safe though so you should make sure that only one executes
// at a time. An execution can be suspended to allow another
// context to execute.
asIScriptContext *ctx;
int r = engine->CreateContext(&ctx);
if( r System_Log("Failed to create a context"); return -1; }
// Prepare the context for execution
// When a context has finished executing the context can be reused by calling
// PrepareContext on it again. If the same stack size is used as the last time
// there will not be any new allocation thus saving some time.
r = ctx->Prepare(engine->GetFunctionIDByName("main"), 1000);
if( r System_Log("Failed to prepare context"); return -1; }
// If the script function takes any parameters we need to
// copy them to the context's stack by using SetArguments()
float a = 1.0f, b = 2.0f, c = 3.0f;
r = ctx->SetArguments(0, (asDWORD*)&a, 1);
r = ctx->SetArguments(1, (asDWORD*)&b, 1);
r = ctx->SetArguments(2, (asDWORD*)&c, 1);
// Execute script
hge->System_Log("Starting script execution");
r = ctx->Execute();
if( r System_Log("Unexpected error during script execution"); return -1; }
if( r == asEXECUTION_FINISHED )
{
hge->System_Log("Execution finished successfully\n");
// If the script function is returning any
// data we can get it with GetReturnValue()
float retVal;
r = ctx->GetReturnValue((asDWORD*)&retVal, 1);
hge->System_Log("Returned: %f", retVal);
}
else if( r == asEXECUTION_SUSPENDED )
{
hge->System_Log("Execution was suspended.");
// In this case we can call Execute again to continue
// execution where it last stopped.
acCString str;
str.Allocate(256, false);
int funcID = ctx->GetCurrentFunction();
hge->System_Log("line : %d", ctx->GetCurrentLineNumber());
}
else if( r == asEXECUTION_ABORTED )
{
hge->System_Log("Execution was aborted.");
}
else if( r == asEXECUTION_EXCEPTION )
{
hge->System_Log("An exception occured during execution");
// Print exception description
acCString str;
str.Allocate(256, false);
int funcID = ctx->GetExceptionFunction();
hge->System_Log("line : %d", ctx->GetExceptionLineNumber());
}
// Don't forget to release the context when you are finished with it
ctx->Release();
return 0;
}

#26Andreas Jonsson  Moderators   -  Reputation: 4363

Like
Likes
Like

Posted 29 June 2004 - 12:50 PM

That's quite a lot of questions. Let's see if I can satisfy your curiousity. By the way, have you read the overview that I recently wrote? http://www.angelcode.com/angelscript/articles/overview.html

asBSTR is perhaps not the most convenient way of working with strings when it comes to C++. You're better of using std::string or perhaps even acCString (that you'll find in the AngelScript library source). Here's an example on how you would use asBSTR in C++:

 #include "bstr.h"void TestBStr(){ // Allocate a string with 10 characters asBSTR aString = asBStrAlloc(10); // Copy data to the string buffer memcpy(aString, "Hello there", asBStrLength(aString)); // asBStr guarantees a null character after the allocated length printf("%s\n", aString); // Free the allocated memory asBStrFree(aString);}

This code would output

Hello ther

Exactly 10 characters as the bstr puts a null character after the allocated length.

-----

You should register all the methods of CGame object you wish to give the scripts access to. You may also register member variables for direct manipulation. The member variables could be registered as const so that the script cannot alter the value.

Once you've registered the interface to CGame you could register a global property with either a reference to the CGame object or a pointer. Or you could register a function that returns the reference or pointer to the object.

Whatever you choose you can be sure that the script will not be able to do anything with the object that you haven't allowed it to do. That is, the script cannot change any internal states of the CGame except through the registered functions/methods or properties.

----

I'm happy you like AngelScript. I started developing AngelScript for the same reason you started using it. I took a look at Lua and Python but didn't like what I saw, and thought that I could do it better myself. Perhaps not better, but more to my liking. As it turns out a lot of other people also think it is easier to use, which makes me happy with my decision. :)

----

There is no fixed entry point for the scripts. It is the application that decides what it needs. You could for example want to write an application that runs one script function and lets the script control the rest of the program flow. In that case it makes sense to call the script function "main". But other situations may need an initialization function and a termination function and nothing between. In those cases the functions might be called "init" and "uninit". It's all up to you. There is no "fits-all" solution.

The engine compiles the script pretty fast. The exact time depends on size of the script of course. But you should try to compile the scripts once, and then call the compiled functions.

Should you so many scripts that you are having trouble with memory, you might need to rethink the design of your game. Perhaps it would be possible divide the game in sections each with a smaller amount of scripts. When the player moves from one section to another the respective scripts are loaded and compiled.

-----

Scripts are compiled into bytecode and executed sequentially in separate contexts. When you call Execute() the function doesn't return until the script execution finishes or is suspended. If you have a controlling script that runs in an endless loop, you might need to suspend the script in order to allow other tasks to run once in a while. A suspended script can be resumed by calling Execute() again.

You may also want to allow AI to execute a certain number of instructions per frame. You could do that by calling ExecuteStep() the number of times desired per object and per frame. ExecuteStep() executes one script statement then returns. This way you could have several script contexts running in parallel, effectively making a multitasking scripting engine.

----

That code is a good example on how to load a script, compile it and execute it. You do well to learn from it. But again, it might not suit your purpose so don't follow it blindly.

----

Regards,
Andreas
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#27 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 30 June 2004 - 05:29 AM

Great answer. I just want to tell you the basic idea of what I want to do, and maybe you could point me in the right direction.

My game will be an arcade game. It will contain lots of enemies that each have scripts that are executed when they die. I would like to lump all the enemy scripts together in one script (seperated by functions maybe). Then each enemy would have a string containing the name of the function to call when it dies ("sharkDie()"). Then I would call something to execute "sharkDie()" using the compiled script. I would also use the scripting for loading/configuration, and maybe a menu system.

Read the overview. The first part about compiling scripts: should I just compile all my scripts together using AddScriptSection? Then call each function by using the method on the overview? So I gather there is no way to have different compiled scripts that work independantly from one another?

#28Andreas Jonsson  Moderators   -  Reputation: 4363

Like
Likes
Like

Posted 30 June 2004 - 10:35 AM

My recommendation is that you have one engine for each type of task, for example: One for AI routines, one for GUI management, one for loading/configuration.

The AI routines should be divided in groups depending on type of object. You could for example have a soldier type and a shark type, both of them have the same interface to interact with the game engine so they belong in the same script engine. Both types have the same script interface so your application needs a way to differentiate between the die function for the soldier and the shark. In version 1.7.1a the recommended way was to append the name of the type to each of the functions giving soldierDie() and shartDie(). But with 1.8.0 I recommend that you use the modules instead which gives soldier::Die() and shark::Die(). In 1.7.1a you would compile all the AI scripts together by first adding all the script sections then calling Build(), but in 1.8.0 you compile each module separately.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#29 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 30 June 2004 - 12:27 PM

with 1.8.0 I recommend that you use the modules instead which gives soldier::Die() and shark::Die().

How do I use the modules in the scripting language? like
soldier::Die()
{
}

just like that?

And, in the host program, what do you mean compile each module seperately? I wouldn't compile a whole script (by script I mean a complete textfile containing various functions), but just parts of a script? Why not just compile the whole script and call individual functions from the script? But, if I have to or should use modules, the enemies die script function would be something like: "soldier::die()"?

#30Andreas Jonsson  Moderators   -  Reputation: 4363

Like
Likes
Like

Posted 30 June 2004 - 02:17 PM

Modules is a new feature in version 1.8.0 WIP #3 (released yesterday).

Say that you have two script files. One for the soldier AI and
one for the shark AI.

// Code for soldier AIvoid Die(){  // Do the soldier death animation}

// Code for shark AIvoid Die(){  // Do the shark death animation}

Now load these scripts into the engine and compile them.

soldierScript = LoadScript("soldier.as");engine->AddScriptSection("soldier", soldierScript, strlen(soldierScript);engine->Build("soldier");sharkScript = LoadScript("shark.as");engine->AddScriptSection("shark", sharkScript, strlen(sharkScript);

Once you've loaded and compiled the scripts. You can get the id's of the functions like so:

soldierDieFunc = engine->GetFunctionIDByName("soldier", "Die");sharkDieFunc = engine->GetFunctionIDByName("shark", "Die");

The modules just makes it easier to manage all your AI types. And you could also load script modules dynamically as the player encounters new AI types.

Of course, you are not prohibited from compiling everything into one large script, should it suit you better.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#31 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 30 June 2004 - 05:05 PM

I get what you are saying but I don't get how to implement it.

the soldier will have a function ID to tell what happens when he dies. Since it is a soldier, the module to be used is assumed to be "PEOPLE" or something. So the function ID could be obtained like so:

acCString str; str.Allocate(256, false);
soldier->ID = engine->GetFunctionDeclaration(engine->GetFunctionIDByName(mod, "main"), str.AddressOf(), 256);

To load and get the script file ready to be accessed by the scripting engie I would call LoadScript? Okay, here is my loadscript code:

int LoadandCompile ( asIScriptEngine *engine, const char *filename, char *mod )
{
FILE *f = fopen(filename, "rb");
int len = _filelength(_fileno(f));
acCString code; code.Allocate(len, false);
fclose(f);
int r = engine->AddScriptSection(mod, code, len);
COutStream out; r = engine->Build(mod, &out);
}

but AddScriptSection does not take 3 parameters.

Then to call the soldiers die function you call
int ExecuteFunction ( asIScriptEngine *engine, const char *mode, int function )
{
asIScriptContext *ctx;
int r = engine->CreateContext(&ctx);
r = ctx->Prepare(function);
r = ctx->Execute();
return 0;
}

but, Prepare does not take 1 parameter.

I guess that would be the way to do it, right? What is the most efficient way? Obviously writing all that code to load a script and set functions and whatnot is a lot of writing. That is why I think making functions like the ones above would help, but I don't think they are right. I admit that I don't understand how to use as, but I am kinda picking up on it.

#32Andreas Jonsson  Moderators   -  Reputation: 4363

Like
Likes
Like

Posted 01 July 2004 - 10:39 AM

Ok, I can't tell you how exactly you should implement things. Everyone have their own preferred solution. Not even I know what is best since I don't use AngelScript. I don't have the time to write any projects using AngelScript.

The problem about the wrong number of arguments is probably because you haven't downloaded the latest 1.8.0 WIP #3. Or you haven't updated the angelscript.h file in your project.

Your function for loading and compiling the script is fine. If you want to compile more than one script section into the same module you should only call Build() after you have added all the sections.

GetFunctionDeclaration() is only there so that you can get the function interface declared by the script. You don't need to store this string. To get the function ID you ought to use GetFunctionIDByDecl() since it guarantees that the function you're calling is using the interface that you expect.

int funcID = engine->GetFunctionIDByDecl("mymodule", "void DoSomething(float)");

This call is relatively slow so it is recommended that you store the funcID for later use.

To call a script function you need to prepare a context. Then call Execute() on it.

asIScriptContext *ctx;
engine->CreateContext(&ctx);
ctx->Prepare(funcID, 100); // Can't remember right now if there are more parameters, check angelscript.h or the app writer's manual
float arg = 1.0;
ctx->SetArguments(0, &arg, 1);
ctx->Execute();
ctx->Release();

You could optimize it some more by reusing the context for another execution. Just call Prepare() again, and then Execute().

Regards,
Andreas
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#33 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 01 July 2004 - 04:52 PM

I can't thank you enough for being one of the most patient guys I've ever communicated with. This whole thread is pretty informative for anyone trying to learn angelscript.

#34 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 01 July 2004 - 05:21 PM

Problem when calling this function. The thing it craps out on is the r = engine->AddScriptSection(); What is the difference between mod and name, and what should I put for len? I downloaded the sources just now, and it didn't change anything for the parameters.

int LoadandCompile ( asIScriptEngine *engine, asIScriptContext *ctx, const char *filename, char *mod )
{
FILE *f = fopen(filename, "rb");
int len = _filelength(_fileno(f));
acCString code; code.Allocate(len, false);
fclose(f);
hge->System_Log("%d size adding script section..", len);
int r = engine->AddScriptSection(mod, "flatch", code, len);
//COutStream out;
hge->System_Log("building engine");
r = engine->Build(mod, NULL);
hge->System_Log("creating context");
r = engine->CreateContext(&ctx);
if( r System_Log("Failed to create a context"); return -1; }
return 0;
}

#35Andreas Jonsson  Moderators   -  Reputation: 4363

Like
Likes
Like

Posted 02 July 2004 - 01:52 AM

You're most welcome :)

The module parameter is the name of the module, which basically is the namespace for the script section.

The name parameter is the name of the script section. This name will only be used during the build for writing error messages. A good choice for this parameter is the filename of the script.

I can't see any obvious errors in your code. Does AddScriptSection() return a value, or does the application crash? Is the crash an assert failure, or something else?

If you receive a return value your parameters are probably wrong somehow.

If you receive an assert failure, it could mean a bug in AngelScript.

If you application crashes for other reasons my guess is that somehow you are linking to the wrong version of AS. Remember that differences between WIP versions are not detected by asCreateEngine() as the version number is the same.

- Andreas
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#36 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 02 July 2004 - 03:43 AM

I also completely removed any other version of the lib, but still the parameters from what you say it should be and the parameters I have for some functions are different. I rebuilt the lib and everything (msvc) so I don't know what's wrong. Maybe you didn't update the msvc workspace or something? Maybe I need to do something to my project?

#37 Anonymous Poster_Anonymous Poster_*   Guests   -  Reputation:

Likes

Posted 02 July 2004 - 06:44 AM

just using the version I have, here is what I am having trouble with. When I try the GetFunctionIDbyDecl() I get different error codes. The last I tried I got -1 which is just error.
When the program gets to the line ctx->Prepare(f, 1000); it crashes (in debug mode) and points to that line of code. You said the crashing may be a result of version conflicts, but I have downloaded the most recent WIP.
Here is my code and how it basically is in my program:

/* script file */
void main()
{
SND_PWRUP = 5;
return 0;
}

engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
ConfigureEngine(engine);
ctx = NULL;
LoadandCompile( engine, ctx, "flatch.as", "" );

int LoadandCompile ( asIScriptEngine *engine, asIScriptContext *ctx, const char *filename, char *mod )
{
FILE *f = fopen(filename, "rb");
int len = _filelength(_fileno(f));
acCString code; code.Allocate(len, false);
fclose(f);
int r = engine->AddScriptSection(mod, filename, code, len);
//COutStream out;
r = engine->Build(mod, NULL);
r = engine->CreateContext(&ctx);
context"); return -1; }
return 0;
}

/* execute the script */

int f = engine->GetFunctionIDByDecl("", "void main()");
int r = ctx->Prepare(f, 1000); assert ( r > 0);
ctx->Execute();

Like
Likes
Like

Posted 02 July 2004 - 06:15 PM

I wont be using WIP#3 for now(will wait for the full release), but i wonder if adding different script sections would help debugging, i.e, if there is an error on line 3 col 45 of the script file, would the error be poitted out relative to the file or to the section.

Wonder how many changes i would have to make for 1.8.0 from 1.7.1 :)
Jayanth.KRaptor Entertainment Pvt. Ltd.http://www.raptorentertainment.com---------------------------------------------------------Why Mr. Anderson? Why? ...Why keep fighting? Do you think you're fighting for something - for more than your survival? Can you tell me what it is? Do you even know? Is it freedom, or truth, perhaps peace, could it be for love? Illusions Mr. Anderson, vagaries of perception. Temporary constructs of a feeble human intellect trying desperately to justify an existence without meaning or purpose.

#39Andreas Jonsson  Moderators   -  Reputation: 4363

Like
Likes
Like

Posted 03 July 2004 - 09:11 AM

Since AngelScript doesn't know anything about files it reports errors relative to the start of script sections.

I will do my best to make the upgrade to 1.8.0 as smooth as possible.

Anonymous Poster:

I can't see any obvious errors in your code. Could you tell me what the Build() method returned? And what GetFunctionIDByDecl() returned?

I'm not ruling out a bug in AngelScript so I'll be making some tests here, but you can help me by answering the above questions.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#40Ped Xing  Members   -  Reputation: 122

Like
Likes
Like

Posted 03 July 2004 - 10:33 AM

build returned 0. Create context returned 0. GetFunctionIDbyDecl returned -1.

If you want I can send you the full source and workspace. I probably am doing something wrong.

Here is where I try to call a function in the script ctx:
 if (ctx) { int function = engine->GetFunctionIDByDecl("", "void main(void)"); hge->System_Log("function ID for main: %d.. Ctx: %d", function, ctx); if ( function > -1 ) { int r = ctx->Prepare(function, 1000); hge->System_Log("prepare %d = %d", function, r); hge->System_Log("SND_PWRUP: %d", SND_PWRUP); ctx->Execute(); hge->System_Log("SND_PWRUP: %d", SND_PWRUP); } }

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS