unregistering global properties

Started by
18 comments, last by mono2k 17 years ago
Thanks, Andreas, will try it and post results!
Advertisement
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 playerplayer_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?
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

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

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...
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.

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

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.
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"

???
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

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

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.
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.

This topic is closed to new replies.

Advertisement