Using Enums From another class

Discussions about Coding and Scripting
User avatar
sektor2111
Godlike
Posts: 5209
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Using Enums From another class

Post by sektor2111 » Sat Jan 30, 2021 2:25 pm

Without instructions I don't get what is supposed to do. I've compiled last time all kind of UScript rebel codes without any "pre-processor".

1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev » Sat Jan 30, 2021 9:24 pm

Barbie wrote:
Sat Jan 30, 2021 12:34 pm
Maybe an ucc-preprocessor can help to eliminate the mistake of different definitions of enums (and similar). Doesn't have the ucc helper such?
How easy is this to build into the build process...

I would prefer to avoid a preprocessor of I can.... It's another layer to debug or know what it's doing if code acts up.... :/

I think I'll just have to settle for just passing an integer, and then validating that integer and if it doesn't match a value, either default it to an empty value or the first value in my enum

:noidea

Sigh....

Or I could make an "enum" package, and then ensure that compiles first?

1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev » Sat Jan 30, 2021 9:32 pm

sektor2111 wrote:
Sat Jan 30, 2021 9:29 am
1337GameDev wrote:
Sat Jan 30, 2021 6:08 am
PS: I couldn't find the code you mentioned in MH.... I tried looking for the latest "utjmh" but couldnt extract the uc files using wotgreal....
PPS: I got the decompile to work. Found the code in "UTJMHBase" on line 256 using grepwin.

Hmm. I don't think this code should even compile. It shouldn't. I try the exact same thing, and my code complains.... Maybe it's because it's setting the enum to one declared inside the same class?
Then I'm sorry, dude, but this code is compiled already X times in X MH2 versions, here syntax was a problem not accessing vars is problem when UCC doesn't even compile these. And then, a MH compilation needs a clean MH package for coding not the original one and it works as it always worked...
I don't even recall how many times I was addressing enum vars from various actors.
The hard way for me was working with "invisible" stuff (like reachSpecs - unseen objects) which I wanted to morph into a user friendly format, because I think those are enum vars too but in another Level, UScript is not really having functions for screwing this data - here XC_Engine has the power, in essence when something goes limited with UScript it's time for C+, but C++ it's not my domain.

I have to admit that working with "strings" is expensive generally. I'm stating on this based on MapVoteLA type, major hit - those attempting to sort 2047 maps and iterating to the death, there I turned a function into a state code letting engine to work more slower and breaking iterations with latent "Sleep" else MapVoteLA++ types addressing more than 1500 maps would be myths as some of them are useless because... they weren't tested and are lagging game during maps reload. During this "slow" sorting process in my machine tick-rate which is sitting at 24/25 is going to 13/25. It's heavy to wrap 1705 names after all...

Edit: Let me introduce a fresh code which I used a few time ago.
It's addressing enum from LevelInfo class:

Code: Select all

class LevelInfo extends ZoneInfo
	native
	nativereplication;
...
var() enum ENetMode
{
	NM_Standalone,        // Standalone game.
	NM_DedicatedServer,   // Dedicated server, no local client.
	NM_ListenServer,      // Listen server.
	NM_Client             // Client only, no local server.
} NetMode;
Then... happily embedding in some EMB map a sort of Teleporter which will work depending on settings - visible or not - it's self-morphing in desired environment:

Code: Select all

...
simulated function PostBeginPlay()
{
	Super.PostBeginPlay();
	if ( bIAmVisible )
	if ( URL != "" && URL != "None" && Level.NetMode != NM_DedicatedServer )
	{
		bHidden = False;
		Mesh = LodMesh'BotPack.Tele2';
		DrawType = DT_Mesh;
		Texture = None;
		LoopAnim('Teleport', 2.0, 0.0);
		T = spawn(class'UTTeleeffect');
		if ( T != None )
		{
			T.RemoteRole = ROLE_None;
			T.lifespan = 0.0;
		}
	}
	bSpecialCost = True;
}
....
Only a woman can be so beautiful as this code... :D
Yup....

My code will run in the main game update loop....

So strings are a no go.

And your code works because it references base game code enums, and compiles AFTER them.

Having compilation other matter pisses me off. It's like ucc had no 2nd precompilation lexing phase.... :/

User avatar
Feralidragon
Godlike
Posts: 5300
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: Using Enums From another class

Post by Feralidragon » Sat Jan 30, 2021 11:02 pm

Like I mentioned before, UE1 is extremely old and incomplete, and this being the first UScript version it is bound to have all sorts of limitations you wouldn't dream of seeing in modern languages.

By the time the language was becoming somewhat decent (UE3), they dropped it (UE4), but apparently are creating a new one (for UE5, although it's more specifically for Fortnite, at least for now, in the same sense UScript was created mostly for the Unreal game itself at first).

The biggest issue with enums in UScript is that they are declared inside classes, rather than being first-class types like classes, making them exclusive properties of those classes.
The same goes for structs.

Meaning that even from a standard coding point of view, declaring a variable as an enumeration which belongs exclusively to another class is actually incredibly weird in principle.

Of course, you could treat that class as the class you would declare all enums, meaning that such a class could be defined as simply a source of all your global enumerations, and then use those enumerations strictly in other classes, to make it clear that those enumerations are meant to be seen as global and not local to the class they were declared in, which would make it less weird.

Thus, you could do exactly that if you wish: create a class, maybe one extending from Object, which you could give a name that you are sure will make it to be picked first for compilation (which seems to be alphabetical order?), which could be something like simply A, and from there you won't really have to worry about that limitation at all.

For the sake of "neatness", you could then create an empty class extending from that one with a better name to use in your code, like Enums (for example), and then you could declare stuff as Enums.MyEnumeration without a problem, since at that point the enumeration would be already loaded from the compilation of A itself (the parent class).

From there, in order to use the values, you can use the values directly, since apparently the enumeration values themselves become global, so you can use something like VALUE2 directly, at which point I strongly recommend for you to keep using Epic's enumeration value notation as <prefix>_<value name>, to prevent collisions with other references.

I just validated all of this myself, and it works.

1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev » Sat Jan 30, 2021 11:41 pm

Feralidragon wrote:
Sat Jan 30, 2021 11:02 pm
Like I mentioned before, UE1 is extremely old and incomplete, and this being the first UScript version it is bound to have all sorts of limitations you wouldn't dream of seeing in modern languages.

By the time the language was becoming somewhat decent (UE3), they dropped it (UE4), but apparently are creating a new one (for UE5, although it's more specifically for Fortnite, at least for now, in the same sense UScript was created mostly for the Unreal game itself at first).

The biggest issue with enums in UScript is that they are declared inside classes, rather than being first-class types like classes, making them exclusive properties of those classes.
The same goes for structs.

Meaning that even from a standard coding point of view, declaring a variable as an enumeration which belongs exclusively to another class is actually incredibly weird in principle.

Of course, you could treat that class as the class you would declare all enums, meaning that such a class could be defined as simply a source of all your global enumerations, and then use those enumerations strictly in other classes, to make it clear that those enumerations are meant to be seen as global and not local to the class they were declared in, which would make it less weird.

Thus, you could do exactly that if you wish: create a class, maybe one extending from Object, which you could give a name that you are sure will make it to be picked first for compilation (which seems to be alphabetical order?), which could be something like simply A, and from there you won't really have to worry about that limitation at all.

For the sake of "neatness", you could then create an empty class extending from that one with a better name to use in your code, like Enums (for example), and then you could declare stuff as Enums.MyEnumeration without a problem, since at that point the enumeration would be already loaded from the compilation of A itself (the parent class).

From there, in order to use the values, you can use the values directly, since apparently the enumeration values themselves become global, so you can use something like VALUE2 directly, at which point I strongly recommend for you to keep using Epic's enumeration value notation as <prefix>_<value name>, to prevent collisions with other references.

I just validated all of this myself, and it works.
This might work... but what about global class names? or are they scopped in a package name? eg: if i make a class A, and then a subclass just for changing the name, what if another package uses the same names? Is this an issue?

EDIT:

I made the following classes, and used it in a 3rd class that began with the word "General" and encountered a compilation issue.

Code: Select all

//-----------------------------------------------------------
//  A dummy class to be compiled first. Used to store Enums.
//-----------------------------------------------------------
class A1 extends Object;

var enum Enum1 {
    VALUE1, VALUE2
};

Code: Select all

class GlobalEnums extends A1;

defaultproperties
{

}

Code: Select all

local GlobalEnums.Enum1 e1;
It complained about not being able to find the enum... and did NOT parse A1 first....

Code: Select all

Analyzing...
Parsing PlayerSpawnNotify
Parsing GeneralClass1
GeneralClass1.uc(96) : Error, Unrecognized type 'Enum1' within 'GlobalEnums'
Critical error while compiling

User avatar
Feralidragon
Godlike
Posts: 5300
Joined: Wed Feb 27, 2008 6:24 pm
Personal rank: Work In Progress
Location: Liandri

Re: Using Enums From another class

Post by Feralidragon » Sun Jan 31, 2021 1:17 am

1337GameDev wrote:
Sat Jan 30, 2021 11:41 pm
This might work... but what about global class names? or are they scopped in a package name? eg: if i make a class A, and then a subclass just for changing the name, what if another package uses the same names? Is this an issue?
I was going to mention that, and provide some further suggestions, but I wasn't sure if it was something you already knew, so I preferred for you to ask first, since this is a subject of its own and I didn't want to clutter the post.

So... classes are effectively isolated within their own packages, so you could eventually even have A in 2 different packages without them colliding, since the class full qualified name would actually be something like MyPackage.A and MyOtherPackage.A.

This actually already happens within the game itself, where you have 2 separate classes with the same name: TriggerLight.
One of them is actually Engine.TriggerLight and the other UnrealShare.TriggerLight, and they both coexist without any issues really.

So that means you can have 2 classes with the same name in different packages: the engine allows that, since it sees them as different classes.

However, it does become a problem when it's ambiguous.
For example, using the example above, which TriggerLight am I referring to here?
var TriggerLight TL;
or even:
TL = TriggerLight(SomeActor);

This does compile just fine, the engine does not really complain about any ambiguity, but it will likely compile it as being the Engine.TriggerLight, and it will work.
And you cannot really disambiguate with the full name like var UnrealShare.TriggerLight TL;, since it will not compile that way (yet another UE1 UScript major flaw).

However, worse than that you have the function IsA, so you can also have something like this:

Code: Select all

if (TL.IsA('TriggerLight')) {
    //do stuff
}
because here you're not really binding the code to either one of the classes, nor their packages, so both will enter the condition, which may cause problems.

Therefore the best practice here would be to give completely unique names to all your classes, enum values, etc, in such a way that they do never collide with other packages, if possible.
One obvious way to do this is the way everything else in every other language with no concept of namespaces do: use a prefix in every class you create, like GameDev1337_MyClass for example.

If you check any of my stuff I actually only did this pretty late in the very last stuff I did for the game (just in 1 package I believe, one I made for mappers), because I didn't really think about this beforehand, like 95% of the people who ever developed anything for this game.

But I never had any problems even with my older stuff, especially NW3 which is highly used with other mods as well, since apparently the names of my classes were unique enough, and I have been using the TriggerLight(TL) way instead of TL.IsA('TriggerLight') of doing things, meaning that if another package with the same class names is executed along my stuff, it shouldn't pose a problem at least for me, so I never heard reports of stuff breaking over it.

So my suggestion is to use <package prefix>_<class name>, which is verbose, but gets the job done in an effective way.

1337GameDev wrote:
Sat Jan 30, 2021 11:41 pm
It complained about not being able to find the enum... and did NOT parse A1 first....

Code: Select all

Analyzing...
Parsing PlayerSpawnNotify
Parsing GeneralClass1
GeneralClass1.uc(96) : Error, Unrecognized type 'Enum1' within 'GlobalEnums'
Critical error while compiling
Hmmm... in my tests I did test the order of things, and the classes were being always compiled in alphabetical order.

One thing you can do is ask Anthrax directly about what the compiling order logic currently is, and maybe ask for a feature to force some classes to be compiled first.
If the latter is done, you wouldn't need to do all this juggling with class names and whatnot (was going to suggest this too, but since the order seemed to be consistently alphabetical, the need didn't seem as strong).

1337GameDev
Experienced
Posts: 85
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Using Enums From another class

Post by 1337GameDev » Sun Jan 31, 2021 3:51 am

Thanks for the information. Very helpful.

I figured it could work.

Is there any way to instantiate a specific class, fully qualifying it?

What if I actually want to refer to "Unreal share.TriggerLight?"

Is there no way to detect the proper instance of a class, or look at its package name?

And are package names only one level deep? Can I specify multiple packages?

And yeah, I was annoyed that it wasn't alphabetical....