• Advertisement
Sign in to follow this  

unregistering global properties

This topic is 3979 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello again ;-) Is there any way to unregister an arbitrary global property? It would be a very nice featrue...

Share this post


Link to post
Share on other sites
Advertisement
You can use the dynamic configuration groups for that.


// Register the property in a defined group
engine->BeginConfigGroup("my dynamic group");
engine->RegisterGlobalProperty("int prop", &prop);
engine->EndConfigGroup();

// Later on the group can be removed
engine->RemoveConfigGroup("my dynamic group");


You'll have to carefully design your application though, as you will only be able to remove the group when no script is referencing it. You'll get a asCONFIG_GROUP_IS_IN_USE (-22) return code if it is still in use when you try to remove it.

Regards,
Andreas

Share this post


Link to post
Share on other sites
A little question...
May I call ExecuteString (or Prepare/Execute) after BeginConfigGroup but before EndConfigGroup?

Share this post


Link to post
Share on other sites
Even though it works I do not recommend it. I may change this behaviour in the future in order to provide better validation of configurations.

Share this post


Link to post
Share on other sites
I'm a bit confused... Is it possible to discard ExecuteString's compilation results, not the whole module?

I'm using AS in such way:

1) Load a large file containing all script functions

2) Register global classes/properties and build this file


Later in the game I'm trying to do such thing:


3) On each new menu screen BeginConfigGroup(screen_name)

4) call RegisterGlobalProperty for every GUI object defined in the XML definition for current menu screen. Imagine following XML definition


// show_player_info is defined in the standalone AS script file
<player_list id='players01' x='10' y='10' width='100' height='300' on_player_selected='show_player_info(players01.get_selected())' />


My goal is to register 'players01' as a global property for current menu screen only, so it is very easy to work with an actual C++ implementation of player_list for every scripted event handler defined on the current screen only.

5) EndConfigGroup()

6) For each defined event handler I try to call ExecuteString and it works perfectly, but!

7) In the case of the current screen's destruction (i.e. player pressed a button and we need to show the next screen) I'm trying to call RemoveConfigGroup but it looks like ExecuteString binds some internal data with the main chunk of code and the call fails with -22, as you predicted...

Any comments/suggestions?

P.S.: my game is for moble devices and it is important to prebuild as much as possible, and it is not a good idea to discard the whole module and rebuild it on an each new screen...

[Edited by - mono2k on March 29, 2007 5:41:42 AM]

Share this post


Link to post
Share on other sites
Perhaps try calling asIScriptEngine::GarbageCollect(true) to free up any remaining references to that config group.

Share this post


Link to post
Share on other sites
Hmm, I don't think I've tested this scenario, so I'm not sure how it will work. But I have a couple of solutions in mind.

The bytecode and context that ExecuteString() uses is normally discarded on the next call to ExecuteString(). But you can also give it a pre allocated context to use for the execution. So the suggestions I have are:

1. Try creating the context yourself with CreateContext(), and then pass it to ExecuteString(). When the execution finishes, you should be able to release the context and remove the config group.

2. If that doesn't work, you may try calling ExecuteString() again, this time with an empty string, i.e. ExecuteString(0, ""). This should also free the previous execution and references to the config group.

You may also need to call the garbage collector, just in case. Because, even though most objects are released immediately as the reference counter goes to zero, some may linger around, especially if they have the potential to form circular references. Though this is likely only a problem if you register new types in the dynamic groups.

Regards,
Andreas

Share this post


Link to post
Share on other sites
oh, both methods do not work... Do I understand right - we're talking about the ability to execute a string in the same module that is already build?
i.e. :

First we need to register player_t struct (string_t is almost identical to your std::string bindings from tests)


// typedef struct
// {
// string_t login;
// string_t screen_name;
//
// uint16_t num_draws;
// uint16_t num_wins;
// uint16_t num_loses;
//
// uint16_t current_rating;
// uint16_t best_rating;
// uint16_t worst_rating;
//
// bool_t chatting;
//
// } player_t;

static void player_t_assign(asIScriptGeneric* gen)
{
player_t* a = (player_t*)gen->GetArgAddress(0);
player_t* this_ptr = (player_t*)gen->GetObject();
*this_ptr = *a;
gen->SetReturnAddress(this_ptr);
};

static void player_t_constructor(asIScriptGeneric *gen)
{
player_t *this_ptr = (player_t*)gen->GetObject();

new(this_ptr) player_t();
};

static void player_t_destructor(asIScriptGeneric *gen)
{
player_t *this_ptr = (player_t*)gen->GetObject();

this_ptr->~player_t();
};

void register_player_t_bindings(asIScriptEngine *engine)
{
int r;

r = engine->RegisterObjectType("player_t", sizeof(player_t), asOBJ_CLASS_CDA); assert( r >= 0 );

r = engine->RegisterObjectBehaviour("player_t", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(player_t_constructor), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("player_t", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(player_t_destructor), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("player_t", asBEHAVE_ASSIGNMENT, "player_t &f(const player_t &in)", asFUNCTION(player_t_assign), asCALL_GENERIC); assert( r >= 0 );

r = engine->RegisterObjectProperty("player_t", "string_t login", offsetof(player_t, login)); assert( r >= 0 );
r = engine->RegisterObjectProperty("player_t", "string_t screen_name", offsetof(player_t, screen_name)); assert( r >= 0 );

r = engine->RegisterObjectProperty("player_t", "uint16 num_draws", offsetof(player_t, num_draws)); assert( r >= 0 );
r = engine->RegisterObjectProperty("player_t", "uint16 num_wins", offsetof(player_t, num_wins)); assert( r >= 0 );
r = engine->RegisterObjectProperty("player_t", "uint16 num_loses", offsetof(player_t, num_loses)); assert( r >= 0 );

r = engine->RegisterObjectProperty("player_t", "uint16 current_rating", offsetof(player_t, current_rating)); assert( r >= 0 );
r = engine->RegisterObjectProperty("player_t", "uint16 best_rating", offsetof(player_t, best_rating)); assert( r >= 0 );
r = engine->RegisterObjectProperty("player_t", "uint16 worst_rating", offsetof(player_t, worst_rating)); assert( r >= 0 );

r = engine->RegisterObjectProperty("player_t", "bool chatting", offsetof(player_t, chatting)); assert( r >= 0 );
};



The next thing is to register a GUI object representing a list of players on the screen:



static void players_list_t_get_selected(asIScriptGeneric* gen)
{
//
// player_t& players_list_t::get_selected()
//
players_list_t* this_ptr = (players_list_t*)gen->GetObject();
gen->SetReturnObject(&this_ptr->get_selected());
};

void register_players_list_t_bindings(asIScriptEngine *engine)
{
int r;

r = engine->RegisterObjectType("players_list_t", 0, asOBJ_CLASS); assert( r >= 0 );

r = engine->RegisterObjectMethod("players_list_t", "player_t get_selected() const", asFUNCTION(players_list_t_get_selected), asCALL_GENERIC); assert( r >= 0 );
};


The global AS script file:



// currently selected player
player_t selected;


//
// shows player info in the message box on the on_player_selected event
//
void on_player_selected(const player_t &in p)
{
// saving selected player
selected = p;

// generating message
string_t info = selected.screen_name + "\\n" + selected.login +
"\\n\\nCurrent rating: " + selected.current_rating +
"\\nBest rating: " + selected.best_rating +
"\\nWorst rating: " + selected.worst_rating +
"\\n\\nTotal games: " + (selected.num_draws + selected.num_loses + selected.num_wins) +
"\\nGames won: " + selected.num_wins +
"\\nGames lost: " + selected.num_loses;

// showing the message box with two buttons
message_box.show(info, "Invite", "Cancel");
};


I load this script at the startup, bind player_t and player_list_t and build the script in the default module.

Later in the game:


...
engine->BeginConfigGroup("MAIN SCREEN"); // works
...

players_list_t* player_list = new players_list_t();
engine->RegisterGlobalProperty("players_list_t players01", players_list); // works

....
engine->EndConfigGroup(); // ok
....

engine->ExecuteString(0, "on_player_selected(players01.get_selected())"); // works pretty nice, shows the info in the message box

....
engine->GarbageCollect(true);
engine->RemoveConfigGroup("MAIN SCREEN"); // fails with -22, group->refCount == 1 !!!



... any comments on this???

I'm thinking of two modules, one for global functions and properties and another for current screen but it looks like it is not possible to use import statement with ExecuteString, am I right?

Share this post


Link to post
Share on other sites
As I said, I haven't made any tests like this scenario before. I'll have to make some improvements to the library to make this work.

What is happening is that when you call ExecuteString() that string is compiled as a temporary function in the module, which means that any dynamic groups it references will be attached to the module. Unfortunately, currently the only way to release those references is to remove the module and recompile it.

You're right. ExecuteString() cannot import functions from another module, so that won't work.

I suggest you use a more dynamic approach. Register an application method to obtain the GUI elements dynamically. The event handler scripts won't be as nice, but shouldn't be too bad. The upside is that you won't have to use dynamic configurations.

Example event handler:


engine->ExecuteString(0, "on_player_selected(GetGuiElement("players01").get_selected())");


I'll make improvements for a future version of AngelScript, to allow implementation as you originally wanted. ExecuteString() is meant to be completely dynamic, and shouldn't lock resources like this.

Regards,
Andreas

Share this post


Link to post
Share on other sites
Thanks for the reply :)
I've already thought of GetGuiElements() but there lies a problem - since AS is a statically typed language, I can't just implement GetGuiElements(), rather I need to implement GetPlayersList(), GetRadioButton(), etc. for every existing GUI element. I'm a lazy programmer (I hope you not :) and this approach requires additional work and looks too ugly compared to the elegant solution we've discussed above... But it seems there's no other way, I'm doomed to do such non-perfect things :-)


P.S.: another question: is it possible to re-register a global property?
i.e.:


menu_screen_t* current_screen = new menu_screen_t();
engine->RegisterGlobalProperty("menu_screen_t screen", current_screen);

...

delete current_screen;
current_screen = new menu_screen_t();
engine->RegisterGlobalProperty("menu_screen_t screen", current_screen);


will this work?

P.P.S.: sorry for my dumb questions, just have no time to look deeper into AS code...

Share this post


Link to post
Share on other sites
No, it's not possible to re-register a property. The engine cannot permit this, because there may be objects or bytecode referencing the original property.

Unfortunately I haven't completed the casting operator feature yet. Once that is done you could simply return an Object super class, and then cast it to whatever type you wanted. You can work around the lack of casting operators by registering global functions that take an Object as parameter and return another type.

Many of the items on top of my to-do list will aid designs like yours. If only I had more time to implement these already. But I'm afraid AngelScript won't be able to do what you want for some time yet.

Share this post


Link to post
Share on other sites
For me AS is already great, maybe except ExecuteString's current behavior ;-)
Looks like it is better to polish current design and don't bother about new features.

Share this post


Link to post
Share on other sites
Just sat down, started programming and obviously realized that writing all those wrappers and messing up with type casting is much much more than I can stand. Yes, I'm lazy.

So, what are you thinking about next approach:

1) Load my large script

2) register globals

3) Build it

4) Save the byte code

5) on each menu screen load the byte code (is it fast? I hope it is a lot faster than building script again and again)

6) do BeginConfigGroup()

7) register current screen's globals

8) ExecuteString on my event handlers


on screen's destruction:

9) discard module

10) RemoveConfigGroup()

11) go to step "5"

???

Share this post


Link to post
Share on other sites
I'm in a polishing phase right now. I haven't really been adding any features of late, only those that really bring forward the already existing one. This will probably continue for a while yet. My biggest problem right now is time, I don't seem to find the time to really sit down and work with AngelScript.

I promise that I'll fix the behaviour of ExecuteString() as well. I just hadn't thought of the scenarios that you came up with before.

Saving the byte code should work and shouldn't be too slow, especially if you save the byte code to a memory block instead of to a file on disk. Though if I were you, I would try simply compiling the script each time first. If you can skip the file loading, then perhaps that is fast enough for you (at least until I can get the ExecuteString() to work). Of course, I do not know the complexity of your GUI scripts.

You could of course also take a look at the AngelScript implementation and see if you can fix the behaviour of ExecuteString() yourself. ;)

Regards,
Andreas

Share this post


Link to post
Share on other sites
Using previously saved bytecode gives you the advantage of not having to parse and compile the script again, so it should be much faster. The only possible disadvantage is that the bytecode could be larger than the source (but the read time should be much less than parsing and compiling).

That being said I haven't tried the Saving/Loading of bytecode yet, and have no idea if your idea would work for you. But in theory it should be a lot faster.

Share this post


Link to post
Share on other sites
Just wish to report: saving byte code to a heap/loading on each screen works nice. Looks like it is faster than compiling the script again and again, especially if the script is large. I hope ExecuteString will be fixed one time since for a really large script the saved byte code takes a lot of heap and a lot of time to load it back into module.
I still have no time to look myself into AS code but I'll definitely do that in the future.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement