Jump to content
The Dark Mod Forums

Feedback on wiki "GUI Scripting Language" Series


Geep

Recommended Posts

The specific test I did used this GUI, that (in my map testFloatInit, using the March 2.11 beta) was applied to an Entity GUI surface:

windowDef test
{
  rect 0, 0, 640, 480
  backcolor 0,1,0,1 // green
  visible 0
  notime 0
  float vis 1

  onTime 0
  {
  }

  onTime 100
  {
//    set "vis" "$gui::gui_parm1";
    if("vis" == 1)
    {
      set "visible" "1";
    }
    else
    {
      set "visible" "0";
    }
//    resetTime 0;
  }
}

Because the resulting surface was not initially green, instead transparent, I concluded that "float vis 1" was initializing to 0. (There was also a script object, used for additional variants, which shouldn't have affected this particular conclusion. I admit that's not the cleanest experimental setup.)

  • Like 1
Link to comment
Share on other sites

19 hours ago, HMart said:

could have been wrong when writing that or it did worked like that but then they updated the gui handling code and forgot to updated the wiki.

Judging from the original state of GUI code, I suggest the following likely explanation.

The GUI scripts and the docs were written by a person different from the one who implemented C++ GUI engine. He tried some stuff, and it worked, or appeared to work, so he decided it was correct. But it was not, but still worked (maybe always, maybe most of the time).

Unfortunately, the original Doom 3 GUI engine is so bad that you cannot deduce what's correct way of doing things by merely experimenting with it.

3 hours ago, Geep said:

Last month, when I tested, I recall I got the opposite result... it was still 0. So something's screwy.

It could be e.g. the weird register deactivation rule, or the obscure "all expression are evaluated at some time before script handler" concept.

Link to comment
Share on other sites

I understand what you are saying about the difficulty of testing in the face of crummy code and unusual evaluation behavior. Still, I need to understand this further. Could you post a .gui (or zip a test fm) that demonstrates initialization of a user variable to a non-zero value?

Link to comment
Share on other sites

On 11/3/2022 at 5:59 PM, stgatilov said:

I recommend going to changelog and roadmap in bugtracker and looking through issues that are about GUI scripting.
I'm pretty sure there were some major changes which are worth taking into account when writing a tutorial.

Maybe even give a list of these tickets somewhere, because if people come from other Doom 3 mods, they can get wrong impression, since stock Doom 3 engine has more bugs and some different behavior.

This would be a reasonable addition. (However, I would not be in a position to extend it to include subsequent changes from Doom3 made by non-TDM mods.)

Link to comment
Share on other sites

@stgatilov, you said, RE string comparisons:

Quote

I think there is no string comparison, because all expressions and their intermediate values are floats, they simply cannot hold strings.

I'm unclear if you are referring just to changes you made in 2.11. I think equality testing of two strings (one of which is "text") was working and is a good and expected capability, and should be supported. Including against an empty string. I noticed in your bug activities that you did remove some comparisons with ... == "".

I see you also removed string concatenation with "\". No problem, but does that mean multiline macros are no longer a thing? (If so, I'll need to change some examples)

BTW, the series so far hasn't really tried to cover the 2.11 changes, since I figured it's a work in progress. But since you did a great deal of GUI work in July, perhaps it's stable enough to try to consider it. I see the logs listed in bugtracker, but don't have access to the private forum threads mentioned there:

https://forums.thedarkmod.com/index.php?/topic/20526-gui-refactoring/&do=findComment&comment=477179

https://forums.thedarkmod.com/index.php?/topic/21535-order-of-evaluation-in-expressions-materials-and-gui/

(Nor do I have SVN currently set up on my newer machine, for changelogs from there.) Any place else I should look?

Link to comment
Share on other sites

3 hours ago, Geep said:

I'm unclear if you are referring just to changes you made in 2.11. I think equality testing of two strings (one of which is "text") was working and is a good and expected capability, and should be supported. Including against an empty string. I noticed in your bug activities that you did remove some comparisons with ... == "".

I refer to Doom 3 in general, at least the original game engine from 2003.

All comparisons are operations which are part of expressions. In order to evaluate expression, you need to store temporaries somewhere, and combine them with each other through a sequence of operations. In case of GUI scripts, the temporaries are called "registers". These registers are floats, they simply cannot hold a string.

Actually, I have just found the commit where I removed the comparison, and here is what it says:

Revision: 16537
#5869. Removed incorrect comparisons with empty string ( == "").

The scripting language does NOT have string values, it only has float values.
So when you reference some string variable in expression, it gets value 0.0 if string is empty and 1.0 otherwise.
For this reason, there is no sense in comparing to "" (which is actually replaced with 0 because no variable with empty name is found).

So the "!=" operator is just a weird way of writing != operator, doublequotes don't change anything.

And a string value inside expression is immediately converted into float with 0 or 1 depending on whether it is empty or not. There is no string comparison, but there is checking for emptyness.

Quote

I see you also removed string concatenation with "\". No problem, but does that mean multiline macros are no longer a thing? (If so, I'll need to change some examples)

Multiline macros work as they did, in fact the change is about fixing them!

There are several different but related things, and they get into a confusing mix here:

  • Token concatenation used for multiline macros, which comes directly from C/C++ language.
  • String literal concatenation with backslash is optional feature of idLexer --- it does not exist in C/C++ language.
  • String literal concatenation by writing them one after the other --- standard feature of C/C++, disabled in D3 GUI.

Here is the full commit message:

Revision: 10031
#5869. Don't enable string concatenation by backslash (\) in GUI code.

GUI code uses doublequotes everywhere to make names like gui::my_var atomic, since otherwise parser would break them into many tokens.
For this reason, string concatenation is disabled in lexer (that's when you can write "hello" " " "world", and C will treat it as single string "hello world").

However, ID enabled special string concatenation via backslash to substitute for it, like this:
  "hello" \
  " " \
  "world"
The problem with such usage is that it breaks multiline macros.

Due to limitations of GUI code, we have to write whole window (even with subwindows) into a macro in order to make it reusable.
If any line in such macro ends with a string literal (that's very likely in GUI code), then parsing breaks and whole macro does not work.
This can be worked around by moving the first token on the next line to the end of the current line, but it is very messy and totally not obvious.
Better just forbid string concatenation altogether.

Buy the way, the following way of concatentating string works (at least inside macro):
  "hello" ## " " ## "world"
Note that ## is a standard C way of concatenating tokens, although in C it works on TOKENS, not on string literals --- but here they are mixed anyway.
Also, it is even possible to do this:
  #define HELLO_MESSAGE(index) "Hello, " ## #idx ## "-th user!"
Which is very useful to construct variable names inside macros =)

 

Quote

BTW, the series so far hasn't really tried to cover the 2.11 changes, since I figured it's a work in progress. But since you did a great deal of GUI work in July, perhaps it's stable enough to try to consider it.

The work is finished, although it might get some changes if any related bugs are reported e.g. during beta.

Quote

I see the logs listed in bugtracker, but don't have access to the private forum threads mentioned there:
https://forums.thedarkmod.com/index.php?/topic/20526-gui-refactoring/&do=findComment&comment=477179
https://forums.thedarkmod.com/index.php?/topic/21535-order-of-evaluation-in-expressions-materials-and-gui/

(Nor do I have SVN currently set up on my newer machine, for changelogs from there.) Any place else I should look?

I don't think we can give access to single thread.
If you miss some info, just ask me and I can copy/paste or explain in my own words.

The two references you gave here are about: expression evaluation order fix, expressions in Set command, change in register disabling behavior.
They are described in this public post too:

 

  • Like 1
Link to comment
Share on other sites

@stgatilov:
I'm drafting a wiki page summarizing what's new in 2.11, based on your forum and bugtracker posts.

I need clarification on these topics (i.e., true or false in 2.11):

1) In expressions, comparisons <=, >=, <, > continue to have the same priority as == and !=  . This is unlike C.

2) In an event handler body, if(text == "") {/* do this if empty*/} and if(text){/* do this if non-empty */} have always been acceptable forms. What is new is that the first form, which was common, is now deprecated and will give a console warning.

3) Vector indexing is available...
a) on the right side of a set command
b) on the right side of a transition command
c) to initialize properties
d) to initialize user variables
e) on the left side of a set command for a float
f) on the left side of a transition command for a float
g) in an if clause

4) In 5869 activities, this is listed: r10012 Now .gui file must contain single windowDef and nothing more.

Do you mean a single top-level windowDef? Since it is not unusual to have multiple windowDefs in a GUI.

Thanks

Edited by Geep
tweak (2)
Link to comment
Share on other sites

2 hours ago, Geep said:

1) In expressions, comparisons <=, >=, <, > continue to have the same priority as == and !=  . This is unlike C.

If you look at page about C/C++, you'll see that comparison operators == and != have slightly higher priority than <= >= < >. In GUI code, all these 6 comparison operators have same priority.
So if you write a lot of such operators one after the other without parentheses, GUI code will evaluate them left-to-right, while C/C++ will first evaluate == and !=.

This is a minor deviation from C standard.
I don't think this is ever important, since writing many comparisons in a row is a bit absurd by itself.

Quote

2) In an event handler body, if(text == "") {/* do this if empty*/} and if(text){/* do this if non-empty */} have always been acceptable forms. What is new is that the first form, which was common, is now deprecated and will give a console warning.

This was never a correct form.
It's just that previously GUI engine did not report any kind of issues to you.

Also, that form was never common, I have seen it in exactly one place in our main menu GUI.

Quote

3) Vector indexing is available...
a) on the right side of a set command
 b) on the right side of a transition command
c) to initialize properties
d) to initialize user variables
e) on the left side of a set command for a float
f) on the left side of a transition command for a float
g) in an if clause

It works in any expressions.

So it covers a, c, d, e, g.
But speaking of Set command, it will only work if you enclose right side in parentheses and thus switch it to expression mode. Without parentheses, right side does not support expressions.

Transitions don't support expressions, and I don't see anything like indexation support in the code.

Quote

4) In 5869 activities, this is listed: r10012 Now .gui file must contain single windowDef and nothing more.
Do you mean a single top-level windowDef? Since it is not unusual to have multiple windowDefs in a GUI.

Yes, it means top-level windows, you can nest as many as you want.

When engine loads UI, it takes a single .gui file and parses it. There can be #includes inside, which would make preprocessor copy/paste other files too, but for the GUI parser itself it is single file.
This file must contain exactly one GUI window and nothing more (it is usually called Desktop). Inside the top-level window, you can do whatever you did previously, it's just a limitation on what happens outside it.

Link to comment
Share on other sites

Thanks, that's helpful.

Regarding (1), I didn't express it quite right. I was actually just asking if that is unchanged from 2.10 to 2.11. I think you are saying, yes, it's unchanged.

Regarding (2), yes, you are right. FYI, I will be revising my String Comparisons page tomorrow to make it correct.

Regarding (3e), for user variables, let me raise a general point first. Elsewhere we seem to disagree on whether they could be initialized to non-zero values (e.g., using a numeric literal) in 2.10 and earlier. Perhaps it's enough to say that "reliable non-zero initialization" was problematic. Turning to 2.11, do you now feel that your changes have made non-zero initialization reliable? Also, your bugtracker notes mention a change in handling of trailing ";" and also (I think I remember this) some difference between "float" and "definefloat". Could you expand on that? Finally, returning to (3e), I'm thinking syntax would look like this

 float myAlpha matcolor[3] // with or without trailing ;

Regarding (3f), just confirming the correct syntax as:

 set matcolor[3] 0.5;

 set matcolor[3] "$gui:alphaFromScript";

Link to comment
Share on other sites

22 hours ago, Geep said:

Regarding (3e), for user variables, let me raise a general point first. Elsewhere we seem to disagree on whether they could be initialized to non-zero values (e.g., using a numeric literal) in 2.10 and earlier. Perhaps it's enough to say that "reliable non-zero initialization" was problematic. Turning to 2.11, do you now feel that your changes have made non-zero initialization reliable?

No idea.

There is some difference between window properties, but I don't think I fully understand the whole picture.
I think there are three types:

  1. "nocursor", "font", "forceaspectwidth", etc. --- these are not properties at all. They are like flags: they are applied immediately at the moment of reading, and their parameters must be literal constants, no links/referenced from them and to them are allowed, so no auto-update, no possibility of changing them with Set, Transition. Some specific window types have specific flags, e.g. "stepsize"/"step" in SliderWindow.
  2. "backcolor", "rect", "notime", "visible", etc. --- these are builtin properties. They are hardcoded in Window code, and may probably have different behavior. Some specific types of windows have specific flags, like e.g. "cvar" in SliderWindow.
  3. Custom window properties, defined with "definefloat", "definevec4", "float".

In my understanding, 2 and 3 work the same way and can be assigned expressions or constants.
Maybe there is some difference which breaks initialization of one of these, I did not hit any myself. So I don't know.

Quote

Also, your bugtracker notes mention a change in handling of trailing ";"

In the original engine, every property must be assigned some value/expression.
Writing something like "float myvar;" was illegal by how the code was implemented, because it tried to read a value from semicolon. And since it could not find a variable/property with name ";", it introduced a new variable with such name and zero value, and you got zero initialization. Maybe I am wrong in some detail, but this zero-initialization feature was a lucky side effect from parsing unknown tokens.

I legalized this practice: now you can add semicolon instead of value, and parser will just replace it with zero value.
Otherwise you'd get a lot of warnings about it now.

Quote

and also (I think I remember this) some difference between "float" and "definefloat". Could you expand on that?

I don't understand why two keywords "float" and "definefloat" exist.
In the code, there are two identical pieces of code for handling them.

I found a commit where I changed one for the other, but it seems I did so merely because "float" was used in at least one more case, while "definefloat" was not used anywhere else.

Revision: 16558
#5869. Use "float" keyword instead of "definefloat" for compass noshadows register.
Note: I have no idea what's the difference.
Looking at C++ code, both cases are the same...

 

Quote

Finally, returning to (3e), I'm thinking syntax would look like this
float myAlpha matcolor[3] // with or without trailing ;

Yes, it should look like this, but semicolon here is illegal.
Script command must end with semicolon, property definition must not end with semicolon. Except for the zero-initialization case noted above (which I'd be happy to not introduce, but too much existing code).

Quote

Regarding (3f), just confirming the correct syntax as:
set matcolor[3] 0.5;
set matcolor[3] "$gui:alphaFromScript";

Sorry, I did not notice the left side of Set.
No, it is not possible to Set only one component of a vector variable.

  • Like 1
Link to comment
Share on other sites

id Studio did a poor job in defining its categorization of variable nomenclature, so in subsequent documentation and discussions there are divergent views (or just slop). In my series, I had to choose something, and went with what I thought would be clearest for the GUI programmer:

  • Properties, which are either
    • Registers (like true variables)
    • Non-registers (like tags)
  • User Variables (also true variables)

I see that your view is more along these lines (which perhaps reflects C++ internals?):

  • Flags (like my non-registers)
  • Properties, which are either
    • Built-in (like my registers)
    • Custom (like user Variables)

Also, elsewhere, you refer to "registers" as temporaries. I am willing to consider that there could be temporary registers during expression evaluation, but by my interpretation those would be in addition to named property registers.

I'm not sure where to go next with this particular aspect, but at least can state it.

 

 

Link to comment
Share on other sites

42 minutes ago, Geep said:

Also, elsewhere, you refer to "registers" as temporaries. I am willing to consider that there could be temporary registers during expression evaluation, but by my interpretation those would be in addition to named property registers.

I'm not sure where to go next with this particular aspect, but at least can state it.

Well... the temporaries are called "registers" in the code.
But since mappers don't see temporaries anyway, I don't think you have to worry.

This is just some minor difference in terminology.

Link to comment
Share on other sites

FYI. Just updated these sections, about syntax for user variable initialization, potentially with non-zero values:

https://wiki.thedarkmod.com/index.php?title=GUI_Scripting:_User_Variables#Declared_in_Property_List
https://wiki.thedarkmod.com/index.php?title=GUI_Scripting:_Syntax,_Semantics,_&_Usage#User_Variables

Also corrected string comparison section in If Statements.

And added a mention of property deactivation to:

https://wiki.thedarkmod.com/index.php?title=GUI_Scripting:_GUI::_Parameters#Bound_In_a_Property_List

Edited by Geep
Link to comment
Share on other sites

On 11/13/2022 at 4:10 PM, stgatilov said:

...

I don't understand why two keywords "float" and "definefloat" exist.
In the code, there are two identical pieces of code for handling them.

I don't have a link and don't take my word for it but I remember this being discussed in the old Doom3World forum and I clearly remember a idSoftware dev saying that definefloat keyword came early, while the script and gui language was being developed, later they decided to support the keyword float alone but because many in world GUI's, were already made with the older keyword, to not waste time converting things that already worked they decided to let it stay, despite doing the exact same thing.

Why they made "float" to replace "definefloat" but not "vec4" to replace "definevec4", is something that I always wondered why. 

What would be your opinion and doing that yourself? Meaning instead of "definevec4" users can write just "vec4" in new GUI's, just like in QuakeWars, thou this last one also supports, vec2 and vec3 but not sure if those are something useful in TDM case.

Edited by HMart
Link to comment
Share on other sites

7 minutes ago, HMart said:

I don't have a link and don't take my word for it but I remember this being discussed in the old Doom3World forum and I clearly remember a idSoftware dev saying that definefloat keyword came early, while the script and gui language was being developed, later they decided to support the keyword float alone but because many in world GUI's, were already made with the older keyword, to not waste time converting things that already worked they decided to let it stay, despite doing the exact same thing.

Good story, but it does not explain why they copy/pasted the whole section of C++ code instead of adding second string comparison with OR operator in the condition for the existing section.

Link to comment
Share on other sites

32 minutes ago, stgatilov said:

Good story, but it does not explain why they copy/pasted the whole section of C++ code instead of adding second string comparison with OR operator in the condition for the existing section.

Don't know if it was your intention but this reply makes it sound like I'm lying or something! Or I'm just reading it wrong? If I am, sorry for my reaction, but is hard to detect intention in text. If I'm not, then I'm not asking for you or anyone to believe me and I even said that.

Why they copy pasted the entire C++ code instead of reusing? Only the original programmer knows that, but perhaps they were to be different in some special case that never came to happen? Just assuming here.

And yes they totally could just have parsed the same keywords in the same c++ code, and in the Quake4 editor (made by Raven software) that comes with Doom 3, they do that.

else if ( !token.Icmp ( "definefloat" ) || !token.Icmp ( "float" ) )
			{
				idToken token2;
				idStr	result;

				if ( !src.ReadToken ( &token2 ) )
				{
					src.Error ( "expected define name" );
					return false;
				}

				idWinFloat				var;
				idUserInterfaceLocal	ui;
				idWindow				tempwin ( &ui );
				idStr					out;

				src.SetMarker ( );
				tempwin.ParseExpression ( &src, &var );
				src.GetStringFromMarker ( out, true );

				wrapper->GetVariableDict().Set ( token + "\t\"" + token2 + "\"", out );

				continue;
			}

I Just know for certain is that Quake4 removed definefloat (at lest is not discussed in their wiki) and QuakeWars removed definefloat and definevec4 entirely, for float and vec4 respectively.

And what about what I asked about supporting vec4 keyword in TDM guis?  

Edited by HMart
Link to comment
Share on other sites

5 minutes ago, HMart said:

Don't know if it was your intention but this reply makes it sound like I'm lying or something! Or I'm just reading it wrong? If I am, sorry for my reaction, but is hard to detect intention in text. If I'm not, then I'm not asking for you or anyone to believe me and I even said that.

Yeah, my reaction is directed not towards you, but towards the original author of Doom 3 GUI engine.

Quote

And what about what I asked about supporting vec4 keyword in TDM guis?  

I won't mind.
Perhaps the third thing, along with calling named event and runScript command.

https://bugs.thedarkmod.com/view.php?id=6164

  • Like 1
Link to comment
Share on other sites

@stgatilov, I want to be clear about the new ";" support in 2.11. The following could cause problems (often quietly) in 2.10. Which ones are now legal in 2.11? Which ones are still illegal but now have warnings?

float <user_variable> 0; // has both numeric literal and semicolon

<register_property> 0; // has both numeric literal and semicolon

<non_register_property> 0; // has both numeric literal and semicolon

Link to comment
Share on other sites

51 minutes ago, Geep said:

float <user_variable> 0; // has both numeric literal and semicolon
<register_property> 0; // has both numeric literal and semicolon
<non_register_property> 0; // has both numeric literal and semicolon

I think none of them are correct in 2.11 and all will produce a warning.
To be honest, it's better to test if you want to know that level of detail.

For the sake of tutorial, the only way that should be advertised is:

float <user_variable> 0
<property> 0

This is what Doom 3 expected, and it works reliably both in Doom 3 and all versions of TDM without warning.
All the other syntax is playing with fire, and people should not know about it 😉

  • Like 1
Link to comment
Share on other sites

@stgatilov. I'm attempting a draft (not for the first time... it's hard to organize this) of a page specifically about evaluating expressions & variables. Here are two statements you made, about a property with a non-constant expression on the right side:

Quote

 

1) When you declare a property, you can write any expression on the right side, potentially referencing some non-constant things there. Normally, when some of these things change, the property is reevaluated automatically.

2) I believe all the expressions are evaluated at the same time, regardless of whether they are on the right side of property definition (aka 'visible "gui::LootIconVisible"') or inside script.

 

These imply two different things about the value of a property (that is not deactivated):

#1 indicates that re-evaluation occurs only when an expression (or an item within it) changes.

#2 indicates that re-evaluation occurs at a different time, which might be:
  (a) about every frame, or
  (b) whenever any event handler is about to run.
  (c) whenever an event handler whose body specifically references that property is about to run

(There is also the far-less-important question of whether, if the property is set by a constant value or expression, evaluation is ever done more than once. This is just a performance issue that has no effect on program logic.)

Your thoughts?

Link to comment
Share on other sites

OK, I'll keep it a bit vague. I just like to present a mental model that reflects reality as much as possible.

I worry that sometimes the simple models can lead you astray. For instance, the simple model of preprocessor instructions are that they are done in a separate first pass. As far as I can tell from the source code (and I may be misreading this), they are actually parsed and handled mixed in the all the other keyword tokens.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recent Status Updates

    • Petike the Taffer

      I've finally managed to log in to The Dark Mod Wiki. I'm back in the saddle and before the holidays start in full, I'll be adding a few new FM articles and doing other updates. Written in Stone is already done.
      · 4 replies
    • nbohr1more

      TDM 15th Anniversary Contest is now active! Please declare your participation: https://forums.thedarkmod.com/index.php?/topic/22413-the-dark-mod-15th-anniversary-contest-entry-thread/
       
      · 0 replies
    • JackFarmer

      @TheUnbeholden
      You cannot receive PMs. Could you please be so kind and check your mailbox if it is full (or maybe you switched off the function)?
      · 1 reply
    • OrbWeaver

      I like the new frob highlight but it would nice if it was less "flickery" while moving over objects (especially barred metal doors).
      · 4 replies
    • nbohr1more

      Please vote in the 15th Anniversary Contest Theme Poll
       
      · 0 replies
×
×
  • Create New...