• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
_Vicious_

Assertion failure in as_configgroup.cpp

25 posts in this topic

Hello Andreas,

I hit the following assertion failure upon config group shutdown (v 2.2.2): [url="http://www.foopics.com/showfull/0d39350e85fa24ccd844c3d87c9d3257"]http://www.foopics.c...844c3d87c9d3257[/url]
It seems to be related to string factory method/function somehow. What this assertion failure might indicate?
0

Share this post


Link to post
Share on other sites
It means that something didn't go as I had expected when writing the code. It is quite possible a bug in my code, though as it is in the ValidateNoUsage() method it is possible it doesn't affect your application if you compile the library in release mode.

Do you know how to reproduce the problem? I'll need to know this in order to debug it and fix the code.

Thanks,
Andreas
0

Share this post


Link to post
Share on other sites
Basically, it happens upon each time I close the app in Debug mode. No special steps are required..
0

Share this post


Link to post
Share on other sites
It never happens for me, and I always test in debug mode.

I'll need help to narrow it down. As it is now I have no idea what the problem might be.

Is it possible for you to create a small test that reproduce the problem?
0

Share this post


Link to post
Share on other sites
On a side note, I've also hit an assertion failure in as_array.h:
asASSERT(index < length);

in asCArray<T>::operator []

The values for index and length are 388 and 76 respectively.

This is the faulty line:
return registeredGlobalFuncs[id];

in asCScriptEngine::GetGlobalFunctionByDecl
0

Share this post


Link to post
Share on other sites
This one is easier. The faulty line should be changed to:

return scriptFunctions[id];

I've checked in this fix in revision 1115.

Thanks,
Andreas
0

Share this post


Link to post
Share on other sites
I think the reason for the first error was that I was trying to do something unexpected for the AS :)
Basically, I wanted to be able to call function pointer in this manner:
[code]
other.pain( other, ent, 10.9, 10.0 );
[/code]

for objects that had the .pain callback set in the C code (in the game not all of the game objects are controlled through scripting). For that, I created a proxy function:
[code]
void G_CallPain( edict_t *ent, edict_t *attacker, float kick, float damage )
{
if( ent->pain )
ent->pain( ent, attacker, kick, damage );
else if( ent->scriptSpawned && ent->asPainFunc )
G_asCallMapEntityPain( ent, attacker, kick, damage );
}
[/code]
, registered it in AS as a global function and then set the asPainFunc function pointer in AddRef method like this:
[code]
if( !obj->scriptSpawned ) {
obj->asPainFunc = asEntityCallPainFuncPtr;
[/code]

The key thing is that asPainFunc also holds the .pain function pointer set from the script.
0

Share this post


Link to post
Share on other sites
This should work.

I'll make some new tests with this info and see if it allows me to reproduce the problem.



How is the G_CallPain function registered? It is a global function in the application but you seem to call it as if it was an object method in the script.
0

Share this post


Link to post
Share on other sites
I'm still not able to reproduce the problem. I wrote the following test in the hopes that a funcdef in a dynamic config group may have been the cause.

[code]

{
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

engine->BeginConfigGroup("gr");
engine->RegisterObjectType("type", 0, asOBJ_REF);
engine->RegisterObjectBehaviour("type", asBEHAVE_FACTORY, "type @f()", asFUNCTION(Type::Factory), asCALL_CDECL);
engine->RegisterObjectBehaviour("type", asBEHAVE_ADDREF, "void f()", asMETHOD(Type,AddRef), asCALL_THISCALL);
engine->RegisterObjectBehaviour("type", asBEHAVE_RELEASE, "void f()", asMETHOD(Type,Release), asCALL_THISCALL);
engine->RegisterFuncdef("void fun(type @, int)");
engine->RegisterObjectProperty("type", "fun @callback", asOFFSET(Type,callback));
engine->EndConfigGroup();

asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE);
mod->AddScriptSection("s",
"void func(type @, int) {} \n"
"void main() \n"
"{ \n"
" type t; \n"
" @t.callback = func; \n"
" t.callback(t, 1); \n"
"} \n");

int r = mod->Build();
if( r < 0 )
TEST_FAILED;

r = ExecuteString(engine, "main()", mod);
if( r != asEXECUTION_FINISHED )
TEST_FAILED;

engine->Release();
}
[/code]

The test passed without any problems, so I'm obviously missing something.
0

Share this post


Link to post
Share on other sites
Yes, G_CallPain is a global function but I'm actually calling it as an object method there. Or even, I'm not calling it at all, but rather assign a pointer holding object's 'pain' funcdef property to it. After all, a function pointer is a function pointer, isn't it? ;)
0

Share this post


Link to post
Share on other sites
I tried assigning a registered function to the function pointer and call that instead of the script function. This didn't work as there was a bug in the VM when executing the asBC_CallPtr instruction in that it failed to recognize that the function was a registered function instead of a script function.

However, after fixing that bug, in revision 1117, I'm still not able to reproduce the problem you have. :(
0

Share this post


Link to post
Share on other sites
Not sure what's changed but now I'm getting a crash in asCScriptEngine::CallObjectMethod:
[CODE]
void (asCSimpleDummy::*f)() = p.mthd;
obj = (void*)(size_t(obj) + i->baseOffset);
[b](((asCSimpleDummy*)obj)->*f)();[/b]
[/CODE]

Unwinding the stack a bit:
[CODE]
// Release previous object held by destination pointer
if( *d != 0 )
[b]engine->CallObjectMethod(*d, beh->release);[/b]
// Increase ref counter of wanted object
if( s != 0 )
engine->CallObjectMethod(s, beh->addref);
[/CODE]

The crash is triggered by setting object's function pointers to global functions in the AddReference behavior:
[CODE]
obj->asThinkFunc = asEntityCallThinkFuncPtr;
obj->asTouchFunc = asEntityCallTouchFuncPtr;
obj->asUseFunc = asEntityCallUseFuncPtr;
obj->asStopFunc = asEntityCallStopFuncPtr;
obj->asPainFunc = asEntityCallPainFuncPtr;
obj->asDieFunc = asEntityCallDieFuncPtr;
[/CODE]

All of those asEntityCall*FuncPtr are of type void * and hold return values of asGetGlobalFunctionByDecl.
0

Share this post


Link to post
Share on other sites
Ah, if I change this part of the code:
[CODE]
obj->asThinkFunc = asEntityCallThinkFuncPtr;
obj->asTouchFunc = asEntityCallTouchFuncPtr;
obj->asUseFunc = asEntityCallUseFuncPtr;
obj->asStopFunc = asEntityCallStopFuncPtr;
obj->asPainFunc = asEntityCallPainFuncPtr;
obj->asDieFunc = asEntityCallDieFuncPtr;
[/CODE]

to

[CODE]
// obj->asThinkFunc = asEntityCallThinkFuncPtr;
// obj->asTouchFunc = asEntityCallTouchFuncPtr;
obj->asUseFunc = asEntityCallUseFuncPtr;
// obj->asStopFunc = asEntityCallStopFuncPtr;
// obj->asPainFunc = asEntityCallPainFuncPtr;
// obj->asDieFunc = asEntityCallDieFuncPtr;
[/CODE]

I then hit the assertion from the first post. I'm quite sure the use function is never called though.
0

Share this post


Link to post
Share on other sites
That's the second time you mentioned setting the function pointers in the AddRef behaviour. What exactly do you mean with this?

The method you register with asBEHAVE_ADDREF is not supposed to change anything but the internal reference counter of the object.

The crash you're getting looks like it is caused by a previous invalid pointer cast, e.g. you do a C style cast (or reinterpret_cast) where a static_cast/dynamic_cast is needed to re-evaluate the pointer.

Try setting a break point in the function where you're setting the function pointers and verify that the object pointer is really pointing to what you think it is. I believe you'll find that it is pointing to something else. If I'm right you'll need to track down where the object pointer was misinterpreted as something it was not and fix that.
0

Share this post


Link to post
Share on other sites
Hm... In release build program execution simply stops with 'Pure virtual function call' message. I'll try to investigate this further.

Ok, nvm, fixed this. The assertion failure is still there though. It seems to hit several functions and behavoirs, involving several classes.

I now believe this is somehow related to setting a funcdef property in the script in this manner:

[code]
void trigger_capture_area_think( cEntity @ent )
{
}

void trigger_capture_area( cEntity @ent )
{
@ent.think = trigger_capture_area_think;
}
[/code]

when both 'trigger_capture_area' and the think function are called from the application.
0

Share this post


Link to post
Share on other sites
In the aforementioned case, in ValidateNoUsage, asCObjectType *type is going to reference the 'cEntity' class and 'asCScriptFunction *func', trigger_capture_area_think.

Maybe trigger_capture_area_think isn't removed because it is still being referenced by an object through a funcdef?
0

Share this post


Link to post
Share on other sites
Yes! Finally I was able to reproduce the problem with the following code:

[code]

{
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

engine->BeginConfigGroup("gr");
engine->RegisterObjectType("type", 0, asOBJ_REF);
engine->RegisterObjectBehaviour("type", asBEHAVE_ADDREF, "void f()", asMETHOD(Type,AddRef), asCALL_THISCALL);
engine->RegisterObjectBehaviour("type", asBEHAVE_RELEASE, "void f()", asMETHOD(Type,Release), asCALL_THISCALL);
engine->RegisterFuncdef("void fun(type @)");
engine->RegisterObjectProperty("type", "fun @callback", asOFFSET(Type,callback));
engine->EndConfigGroup();

asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE);
mod->AddScriptSection("s",
"void func(type @) {} \n"
"void main(type @t) \n"
"{ \n"
" @t.callback = func; \n"
"} \n");

int r = mod->Build();
if( r < 0 )
TEST_FAILED;

Type *t = new Type();

// Call the function that sets the callback on the object
asIScriptFunction *m = mod->GetFunctionByName("main");
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(m);
ctx->SetArgObject(0, t);
r = ctx->Execute();
if( r != asEXECUTION_FINISHED )
TEST_FAILED;
ctx->Release();

// Release the engine, while the object holding the callback is still alive
engine->Release();

t->Release();
}
[/code]

The problem happens because the cEntity object is still alive when the engine is released. If you release the cEntity object before releasing the engine then you should be able to avoid the problem in your game.

I'll investigate what needs to be done to fix the bug in the library.


Thanks for helping me figure out how to reproduce the problem.
0

Share this post


Link to post
Share on other sites
Yeah, I had the same conclusion in my mind. Thanks for being so patient with me [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]

However, releasing objects prior to AS shutdown is somewhat problematic due to objects cross-referencing each other, I'm pretty sure that'd totally fuck up AS reference counting.
0

Share this post


Link to post
Share on other sites
I've found a solution in AngelScript. You can get it in revision 1134.

As the script function is still kept alive by the application when the engine is released, the engine will report this with an error message, and will destroy the internals of the script function. It won't destroy the actual function object, so the application can still call the Release() method afterwards without crashing.

It is not correct to release the engine before all other objects that use it has been released. I've tried very hard to avoid memory leaks should this happen, but I cannot guarantee that I've covered all possible scenarios.
0

Share this post


Link to post
Share on other sites
I'm not sure what would be the best way to safely release all objects and also let the AS module about that. How do I do that?
0

Share this post


Link to post
Share on other sites
Just releasing the objects that the application no longer will use should be enough. If AS is not currently referencing the object, then the object will be destroyed, otherwise it will be destroyed when AS releases its reference too.

As each reference counted object is responsible for destroying itself when the refCount reaches 0, the modules that references the objects doesn't need to know about whom might still be referencing the object.

The problem arises when objects may be referencing each other in a circular manner, preventing the refCount from ever reaching 0. That is why AngelScript has the garbage collector, to detect and destroy such occurances.

Perhaps you could explain a bit more about how your application is structured? So I can understand why it is not safe to simply release the objects before releasing the engine.
0

Share this post


Link to post
Share on other sites
In our game AS is the only module that uses reference counting on objects. Basically all objects are stored in a huge preallocated array which is then freed at shutdown. Obviously we can't free that array prior to shutting down the AS engine otherwise it'll just crash upon referencing a freed memory region. Manipulating the reference counters before releasing the AS engine might be a possibility but that doesn't sound too safe.

In your test case you didn't release the test object in the application either and this is why the problem could be reproduced. How'd you modify the test case to avoid hitting the problem?
0

Share this post


Link to post
Share on other sites
In my test I would simply release the 't' variable before I release the engine.

In your case it would probably make more sense to just release the callbacks, before releasing the engine. The actual objects that are in the preallocated array can be kept alive.

Using my test as example, the cleanup would look like this:

[code]
if( t->callback )
{
// Release the callback and any other objects obtained from the script engine before releasing the engine
t->callback->Release();
t->callback = 0;
}

// Release the engine
engine->Release();

// Release the application owned object last
t->Release();
[/code]
However, if it is too much work then you should be able to leave your code as it is now with the fix I made in the library. The engine will complain about the callbacks not being released, but it shouldn't cause any problems for you. Even memory leaks at this moment probably isn't too much to worry about since the application is shutting down anyway, right?
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0