I'm a doctor, not a mechanic

Legacy:UnrealScript Language Test/Discussion

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to: navigation, search

Archived discussions about certain test cases

error006_whileuntil[edit]

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[edit]

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 :D 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[edit]

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.

Xian: I was wondering, why does this drain the performance:

function switchUsage()
{
   switch (1234)
    {
        case 0:
            dummy1();
            break;
        default:
            dummy3();
            break; // useless and hurts performance
    }
}

I'd say perhaps that it is one more line to parse, but surely this can't be the reason (can it?)...

Wormbo: The UnrealScript compiler in UnrealEngine 1 and 2 does no optimization whatsoever. So, let's see the more bytecode-like version of your example:

function switchUsage()
{
  switch (1234) {
    case 0:
      dummy1();
      goto 'EndOfSwitchBlock';
    default:
      dummy3();
      goto 'EndOfSwitchBlock';
  }
  EndOfSwitchBlock:
}

Every break is translated into an unconditional goto statement with the labels actually being translated to code offsets. Putting a break at the end of a switch block is a waste of processing time, because it's like an empty statement that takes time to process. Control flow would fall through to the first statement after the switch block anyway.

This may not have much effect if used rarely, but it's a thing to consider if you want to squeeze the last bit of performance out of your code.

Xian: Thanks for the prompt reply Wormbo (as always :) ). I noticed the break->goto conversion while viewing code in UTPT, so now that I think of it, you're right and I was pretty close to the "extra" stop being more work. But, considering that I heard (and should be true me thinks) that a large nametable uses more resources... is there any difference between

function Stuff ()
{
    local Actor A;
 
    A = SomeActor;
    A.PostBeginPlay(); // run some "whatever" code
}

and

function Stuff ()
{
    local Actor A;
 
    A = SomeActor;
    A.PostBeginPlay(); // run some "whatever" code
    A = None;
}

I have still to move to UE2 so I do miss the bench functions of UE2 (I think I'll switch to UE3 directly), but somebody told me that resetting a local var to None (or maybe even global) after I am done with it might improve performance... and yeah, you did notice I am an optimization hunter, so I am trying to get my "hands" on as many optimiz-es as I can :)

Also, in regard to the 'import' command. All tests failed on UE1. It only supports "import package X" and "import enum from <packagename>", however I didn't notice what import package does... the error (on a missing import type) says "missing enum, struct or package", although it doesn't actually support struct importing... as for imported enums, it works even with enums that doesn't exist and I am not quite sure what it does; As for package importing, it works with any name *sigh*... Might be some unimplemented stuff left over since even on a successful syntax it says that the error is an import in the package, not the struct... well they have lots of experimental, such as Class GUIDs, import, within, Locales and so on... UE1 currently supports only 'within Object'. (still curious why they keep distributing the Core Package with all that unsupported stuff in it )

Wormbo: The difference is, that the second one is slower. It's actually slower that the additional break, because it's an assignment and those are awefully slow in UnrealScript. IMHO resetting locals to None is pointless in UnrealScript as the execution environment (including the locals) of the function is discarded anyway after the function returned.

"Import" must be some kind of left-over. That functionality is done automatically by the compiler. I guess it was intended to be something like Java's import statements, but it's entirely redundant in UnrealScript. To use another package, simply make sure it's loaded (EditPackages or #exec obj load); to use an enum or struct from a different class, simply qualify it with the class name, like "Actor.EDrawType".

"Within" has become an essential feature in UE2. For example, the classes PlayerInput, CheatManager (single-player "cheat" commands) and AdminBase (base class for admin console commands) are all declared as "Within PlayerController". XWebQueryHandler (the base class of web admin handlers) is declared "within UTServerAdmin", the WebApplication class for the webadmin interface. I'm pretty sure this will be further extended in UE3 with stuff like renderable actor components.