UnrealScript Language Test/Discussion
Archived discussions about certain test cases
error006_whileuntil
Evolution: (can't remember if this is exactly how this one works, but it's something like this): while ( expr ) { ... } until ( expr ); compiles and works fine, though it's quite unexpected!
Wormbo: Not only "while()...until();" compiles (and evaluates both conditions at their corresponding places), but also "do { ... }", which basically is an unconditional infinite loop.
El Muerte: while (...) ... until(...); is clearly an error, it shouldn't compile and it doesn't even run properly, I think (see error006_whileuntil). while (...) do ... until (...); is the correct form. do ... should also be an error. Come to think of it, shouldn't do require braces? How do other curlybrace languages define this? while could easily accept an block or single line, but do should know where the statements end. For example, what should the following code do?
i=0; do do log("x"@i++); until(i < 5); do log("y"@i++); until(i < 10);
Ofcourse it should keep printing "x number". But I think this goes against the principles of the curlybrance languages (this is not perl). And another idea, shouldn't an null condition evaluate to false instead of true?
Wormbo: The compiler actually seems to handle block start and end individually.
startwhile: if (whilecondition) { // while (whilecondition) { // code block // ... goto('startwhile'); // } } startdountil: // do { // code block // ... if (!untilcondition) goto('startdountil'); // } until (untilcondition); startwhileuntil: if (whilecondition) { // while (whilecondition) { // code block // ... if (!untilcondition) goto('startwhileuntil'); // } until (untilcondition); } startdo: // do { // code block // ... goto('startdo'); // }
In that context it also becomes obvious, why the do loop doesn't require braces – while doesn't require them either and the compiler doesn't seem to make a big difference there.
error009_invalidsuper and error010_superstates
Wormbo: It is possible to specify classes in the Super(ClassName).FunctionCall() construction that are not a super class. On the other hand it's not possible to specify super states.
SuperApe: Wormbo (or anyone), can you give an example of that?
El Muerte: super states?
SuperApe: I guess I wasn't clear what Wormbo was referring to. I was wondering which of "Super(ClassName).FunctionCall()" is code and which is explanation. An example of it's use was what I was after.
OlympusMons: Now Im not as good as you guys, but would "Super(ClassName).FunctionCall()" return the super of the current class or the super of the class in the brackets. "Class(Super.ClassName).FunctionCall()" wouldnt that be correct, because its usually "class(class).function();" and for a super state wouldnt it be "Super.goto("StateTag")" anyways just ignore me if I dont have a clue. unless its "Super.Class(ClassName).FunctionCall()" but that kinda leaves it open for a "Super.Class(Super.ClassName).FunctionCall()" Ok now Im just being silly.
Jimboh:
Class A extends Object; function dothis() { // does something cool }
Class B extends A; function dothis() { // does something uncool }
Class C extends B; function dothis() { // you don't wanna be uncool, so you wanna do something cool... Super(A).dothis(); }
Thats how you use Super().
OlympusMons: Oh a super,super. I thought you were calling the super class not the super of the super. Still shouldnt this "Class(Super.ClassName).FunctionCall()" work?? Damn spawning and firemodes got me confused now!!
Wormbo: What I mean with "super states" is:
class SomeClass extends Actor; // Super() compiler bug: function A() { // super call to sibling class function that doesn't exist in this or the super class Super(DestroyableTrigger).SpawnEffects(); // compiles and even executes! (spawns some visual effects) } // Super states: // new function, does not exist in Actor function X() { log("SomeClass global X"); } // new state, does not exist in Actor state BaseState { function X() { log("SomeClass BaseState X"); // Super.X() -> "Error, Unknown Function 'X' in 'Class Engine.Actor'" Global.X(); // logs the same } } state ExtendedState extends BaseState { function X() { log("SomeClass ExtendedState X"); Super.X(); // "SomeClass BaseState X" Global.X(); // "SomeClass global X" } } state AnotherExtendedState extends ExtendedState { function X() { log("SomeClass AnotherExtendedState X"); Super.X(); // "SomeClass ExtendedState X" // it's not possible to call "SomeClass BaseState X" directly through a Super(Something).X() construction: // Super(BaseState).X() -> "Error, Bad class name 'BaseState'" // Super(SomeClass.BaseState).X() -> "Error, Missing ')' in 'super(classname)'" Global.X(); // "SomeClass global X" } }
If the object is in state AnotherExtendedState, it will log the following when X is called:
SomeClass AnotherExtendedState X SomeClass ExtendedState X SomeClass BaseState X SomeClass global X SomeClass global X SomeClass global X
The calling structure looks like this:
AnotherExtendedState.X() +- ExtendedState.X() | +- BaseState.X() | | +- global X() | +- global X() +- global X()
OlympusMons: Ahh yes thats very, very clever wormbo How did you ever come up with that concept, might be handy for some AI or something.
Wormbo: PlayerController already uses the concept of extending states within the sme class with BaseSpectating being the base state for Spectating, AttractMode and WaitingForPawn. The Bot class makes extensive use of extending states as well:
MoveToGoal +- MoveToGoalWithEnemy | +- Fallback | | +- Retreating | +- Charging | +- VehicleCharging | +- Hunting +- MoveToGoalNoEnemy +- Roaming NoGoal +- RestFormation
El Muerte: then what does the following do (and what should it, logically, do)?:
class B extends A; function X() // override A.X { log("B.X"); super.X(); // calls A.X } state Base { function X() { log("Base.X"); global.X(); // calls B.X } } state ExtendedState1 extends Base { function X() { log("ExtendedState1.X"); super.X(); // calls B{Base}.X } } state ExtendedState2 extends Base { function X() { log("ExtendedState2.X"); super(A).X(); // <-- does this call A.X(); ?? } }
I'm not behind a a computer with UE right now, so I can't test it myself
Switch`: 3369 does this
class sbA extends Actor; function X() // A.X { xlog("A.X"); } simulated final function xLog ( coerce string s ) { Log("[" $GetStateName()$ "]" @S, name); }
class sbB extends sbA; function PostBeginPlay() { X(); gotostate('BaseState'); X(); gotostate('ExtendedState1'); X(); gotostate('ExtendedState2'); X(); } (...)
ScriptLog: Fabricate sandbox.sbb sbB0: [sbB] B.X sbB0: [sbB] A.X sbB0: [BaseState] BaseState.X sbB0: [BaseState] B.X sbB0: [BaseState] A.X sbB0: [ExtendedState1] ExtendedState1.X sbB0: [ExtendedState1] BaseState.X sbB0: [ExtendedState1] B.X sbB0: [ExtendedState1] A.X sbB0: [ExtendedState2] ExtendedState2.X sbB0: [ExtendedState2] A.X
SuperApe: I appreciate the explanations and examples. This is very informative, seeing the abilities and limits. Thank you.
El Muerte: well, I think the result is wrong to the analogy of a normal super call in a state. So, this would be two new error cases (the super(nonParent) and the super(X) in a state. ).
Switch`: What should it call? B.X()?
El Muerte: It depends. The meaning of super has to be clearly defined in states. Either super calls the parent class or parent state. In the former super(A) should be possible and in the latter super(A) should be invalid. Maybe there needs to be a special super for states, but at this point it can't be fixed for UE2 (too much relies on the "broken" functionality).
error011_delegatevisibility
Switch`: Delegates ignore private and protected keywords.
class sbA extends Actor; delegate callpriv(); delegate callprot(); function PostBeginPlay() { local sbB B; B = Spawn(class'sbB'); //B.priv(); // "Error, Can't access private function 'priv' in 'sbB'" //B.prot(); // "Error, Can't access protected function 'prot' in 'sbB'" callpriv = B.priv; callprot = B.prot; callpriv(); // "Private called" callprot(); // "Protected called" }
class sbB extends Actor; private function priv() { Log("Private called",name); } protected function prot() { Log("Protected called",name); }
El Muerte: Well, it's partially correct. You should be able to assign private\protected functions to a delegate, but only from a class that that has access to them to begin with. This is a nice "security" exploit. Will add it as 'bug' as soon as I have the time.