TorqueScript package problem
by Heath Kehoe · in Torque Game Builder · 05/09/2011 (12:49 pm) · 8 replies
I am trying to implement some game rules using functions defined in packages; with the intent of being able to swap out the packages using ActivatePackage() and DeactivatePackage() to dynamically modify the game's behavior.
But it isn't working the way I expect. Here's a simplified example that shows what's going on:
And here's the console output when I invoke TestIt():
Notice that the second call to SomeGameFunction() still calls Rule() from RuleSet1 even though RuleSet2 should be active there.
But it isn't working the way I expect. Here's a simplified example that shows what's going on:
function Rule()
{
echo("In base Rule");
}
package RuleSet1
{
function Rule()
{
echo("In RuleSet1::Rule()");
}
};
package RuleSet2
{
function Rule()
{
echo("In RuleSet2::Rule()");
}
};
function SomeGameFunction()
{
Rule();
}
function TestIt()
{
ActivatePackage("RuleSet1");
echo("Expecting RuleSet1::Rule()");
SomeGameFunction();
echo(" ");
DeactivatePackage("RuleSet1");
ActivatePackage("RuleSet2");
echo("Expecting RuleSet2::Rule()");
SomeGameFunction();
echo(" ");
}And here's the console output when I invoke TestIt():
Quote:
==>TestIt();
Expecting RuleSet1::Rule()
In RuleSet1::Rule()
Expecting RuleSet2::Rule()
In RuleSet1::Rule()
Notice that the second call to SomeGameFunction() still calls Rule() from RuleSet1 even though RuleSet2 should be active there.
#2
05/09/2011 (4:00 pm)
Why not just create globals? So if $package1Active == true, then this etc. Pop all these conditions in your on level loaded.
#3
05/11/2011 (9:13 am)
The point is, the package mechanism doesn't work as advertised. The test case in the first post demonstrates the problem.
#4
05/11/2011 (9:23 am)
right. I am just saying why not create your own packages? Own conditions etc? Not sure why you have to use a template.
#5
I'm a bit surprised that it is resolved to RuleSet1 namespace and not to global namespace, but there is no way for compiler to know that you expecting dynamical resolution here. This function is a piece of static code, so it is working like one.
You can force dynamical resolution using
05/13/2011 (4:57 am)
Indeed it is not obvious, but withfunction SomeGameFunction()
{
Rule();
}a call to Rule() gets resolved at compile time, actually before any package is activated.I'm a bit surprised that it is resolved to RuleSet1 namespace and not to global namespace, but there is no way for compiler to know that you expecting dynamical resolution here. This function is a piece of static code, so it is working like one.
You can force dynamical resolution using
eval("Rule();");
#6
I think it's more likely that a function call is resolved at run-time and then the calling function caches a function pointer (or something) which is re-used the next time the calling function runs. Apparently, if you use ActivatePackage() or DeactivatePackage() in a function, that function's cached references are invalidated which is why the code example in the docs works. But since other functions' cached references are not automatically invalidated, it breaks in more real-world cases.
Sure, wrapping every possible packaged function call in eval() may be another way to work around the issue, but that feels like even more of a code maintenance nightmare than my workaround in the second post of this thread.
05/13/2011 (8:57 am)
I don't think it's true that the function calls are resolved at compile-time. The docs say that packages provide dynamic polymorphism; it wouldn't be very dynamic if the function call references were locked in at compile-time.I think it's more likely that a function call is resolved at run-time and then the calling function caches a function pointer (or something) which is re-used the next time the calling function runs. Apparently, if you use ActivatePackage() or DeactivatePackage() in a function, that function's cached references are invalidated which is why the code example in the docs works. But since other functions' cached references are not automatically invalidated, it breaks in more real-world cases.
Sure, wrapping every possible packaged function call in eval() may be another way to work around the issue, but that feels like even more of a code maintenance nightmare than my workaround in the second post of this thread.
#7
www.garagegames.com/community/forums/viewthread/8825
www.garagegames.com/community/forums/viewthread/20601
This thread has a potential solution (basically by putting all of your functions into a class).
05/13/2011 (2:00 pm)
I wish I had used packages recently so I could better answer this question. I know that packages have had problems in the past:www.garagegames.com/community/forums/viewthread/8825
www.garagegames.com/community/forums/viewthread/20601
This thread has a potential solution (basically by putting all of your functions into a class).
#8
Consider using classes to define different rulesets, and a variable to hold reference to the current one. No workarounds needed here. Why are you trying to use packages in the first place?
I wonder if packages were ever meant to be used for _dynamical_ namepace lookup. I can't find single working example, but there are some people's complaints about it from back 2002. Oh, by the way, that doc example does not resolve anything dynamically either.
Packages can, however, be used to override particular functions for entire game session. Useful for making add-ins and such.
05/13/2011 (4:36 pm)
Yes, looks like you are right. Namespaces are resolved during the first call to the function and then stored, right in the compiled code in memory from what I see.Consider using classes to define different rulesets, and a variable to hold reference to the current one. No workarounds needed here. Why are you trying to use packages in the first place?
I wonder if packages were ever meant to be used for _dynamical_ namepace lookup. I can't find single working example, but there are some people's complaints about it from back 2002. Oh, by the way, that doc example does not resolve anything dynamically either.
Packages can, however, be used to override particular functions for entire game session. Useful for making add-ins and such.
Heath Kehoe
So my workaround is to create a package which contains wrappers all the functions in the other packages (the wrappers just call their Parent), and keep that package always on the top of the stack.
package DummyTopLevel { // contains wrapper functions for all the functions in the // packages which will be pushed and popped function Rule() { return Parent::Rule(); } function Rule2() { return Parent::Rule2(); } function anotherOne(%arg) { return Parent::anotherOne(%arg); } }; function MyActivatePackage(%pkgname) { DeactivatePackage("DummyTopLevel"); ActivatePackage(%pkgname); ActivatePackage("DummyTopLevel"); } function MyDeactivatePackage(%pkgname) { DeactivatePackage("DummyTopLevel"); DeactivatePackage(%pkgname); ActivatePackage("DummyTopLevel"); }As long as I use the My[de]activatePackage functions to manipulate the package stack*, I get the results that I expect.
* [edit] And also make sure that the DummyTopLevel package contains wrappers for the union of all functions that appear in my other packages.