Vassal invoking a trait that it shouldn't!

I hesitate blaming Vassal for something that in 99% of the cases is my own fault, but I think I have investigated this bug very carefully, so here goes:

In my module for The Burning Blue 2.3 (Module: The Burning Blue) there is a somewhat complicated function to set up RAF squadrons in their appropriate boxes on a special map called a Tote Board. When this function is called for the first time after the module has been loaded, a Bad Data error message is printed for each squadron:
VassalError chatlog

If we look in the error log, one of these messages looks like this:>

2024-05-25 14:27:03,335 [20752-AWT-EventQueue-0] WARN VASSAL.tools.ErrorDialog - Piece: 54 Sqn (A0) Trait: Trigger Action - Only one of Order, Tally Ho, and Orbit markers for an air unit is allowed - Place Tally Ho! marker => Ctrl+Shift+T => PlaceTallyHoRAF2 - PlaceTallyHoRAF3 - PlaceTallyHoRAF4 Source: {OrderType&&(UnitID==“54 Sqn”)} Error: Expression evaluation error. See the errorlog for more details.
Expression Audit:
Source type=Piece Trait
Source name=Piece: 54 Sqn (A0)
Source description=Trait: Dynamic Property - AssignedAirfield == SetAirfield
Source Expression={OrderType&&(UnitID==“$ShortName$”)}
OrderType=
UnitID=
Error=internal Error: Unimplemented binary String operator

The trait is the same in all of the messages, and it can be identified to be located in [Game Piece Prototype Definitions] - “PlaceOrderTallyBr”:
Vassal Error PlaceOrderTallyBR

The Bad Data comes from the trait in Line 2.

The problem here is that this trait is not called at all in the procedure to set up the RAF squadrons!

It can be proved simply by deleting line 2, in which case the former line 3 will appear in Bad Data messages instead, and if you delete the line, the next line will be causing the messages. It is clear that whatever is at that location in memory will be called, and causes an error - fortunately, because if it had not caused an error, it would be even more difficult to locate.

As I said, this error appears the first time the function is called. If you close the game, and start a new one, the error will not appear. I have taken this as another clue that it is a bug in Vassal, not in the module.

The error must be caused in the GKC that calls a trait in each of the RAF squadrons. The GKC is [Game Piece Prototype Definitions] - “ScenarioActions [Definition]”, line 5:

The command that is called in each RAF squadron is [Game Piece Prototype Definitions] - “SquadronReadiness”, line 1:

If you remove all the lines in the trait so that only a single Report action is called, you will still get the Bad Data error, so the bug does not seem to lie in this trigger action, which is why I think it must be caused by the execution of the Global Key Command.

When I look at the GKC, I am at a loss of how I could test it further. I have tried removing the conditions for pre-selection and additional matching expression altogether, and the bug still appears.

If you want to reproduce the bug, follow these instructions:
1) Load the module (it must not be loaded in advance).
2) Select scenario 1 (any scenario except “Build Your Own” will do.
3) Select Player side RAF.
4) Press Alt+Shift+T to open the RAF Tote Board.
5) Press the blue button “Setup Squadrons”.
This should produce the messages that show the bug - unless you have removed some of the command lines in the Trigger Action.

If you modify anything, don’t forget to Refresh Predefined Setups and save the module and quit, so that the bug will appear after the module has been reloaded.

I have done some more debugging on this bug that makes tons of hideous “Bad Data” messages in the chat screen.

I have managed to isolate an action trait that causes the bug. In fact, there are a number of them, but removing one of these removes the bug completely for the counter involved. The offending action trait is a Report action, and it looks like this:


You think it is the report text that is causing the bug, but no: if I change the text to “Some counter has been moved”, the bug is still there.

The Report action is called from a Trigger action, but that is also not the problem: if I change it to be caused by a key press like CTRL+7, the bug is caused immediately.

If I completely remove the Report action, there is no bug.

As always, once the bug has been triggered for one counter, it will never appear again, with the same, or other triggering commands, even if you close the game, and start a new one. It can only reappear if you quit the module, and restart it.

I have no idea what your problem is, but I did browse through your first message, and I saw something that troubled me. You quoted an error log, and here’s where I have a problem.

The “source description” identifies a problem in a dynamic property trait. It’s the next line where I smell a rat. The “source expression” is a Boolean one because of the && operator. First off, do you mean “(UnitID==ShortName)” in the second half? If so, then writing it as you did can get you in trouble.

It’s the first part. “OrderType&&…” Java is expecting OrderType in this case to hold either true or false, but the next line says it holds null, which is not true or false. I don’t see how this would ever work. It seems to me you need to compare OrderType to something to get a true or false result.

Thank you for giving this some thought. I have looked it through again, and still see no fault here. The reason why OrderType and UnitId have null values is because the faulty trigger action is not supposed to be called at this stage at all.

OrderType is a marker trait defined for certain counters, and it can have the value of true or false. As far as I can see, the trigger action is a random call caused by something like a buffer or stack overflow.

If this trigger action is simply deleted from the prototype, the next line is executed and results in a similar error message. And this trigger action is not even the first in the prototype. If you delete the entire prototype, you get an error message from another prototype that is not called at this stage.

As I pointed put in my second post, the command that causes this behaviour is in fact a simple report action. There is no way that I can think of that the execution of a report action cause the execution of the trigger action that results in the error message.

This doesn’t make sense. If OrderType is a Boolean marker with set value of true or false, then a null value is impossible no matter how or when the trigger was executed. The only conclusion I come to is OrderType is undefined in some piece. This means either (1) some piece includes the trigger when it shouldn’t, (2) OrderType is misspelled in some piece, or (3) the “OrderType” string contains an unprintable, invisible character.

I spent a bit of time trying to dig into this a couple weeks ago and just found it very difficult to get a mental picture of what’s occurring (and meant to occur)–not surprising, given it’s a very complex module and I’m just a passerby poking his head in!

My one lingering thought was that OrderType was a property that did not appear to be on the aircraft counters themselves, but rather on the order/rally markers that get attached to the aircraft counters. I too had immediately homed in on the description of the faulty expression in the errorlog and wanted to figure out why OrderType was coming back null when it the expression is premised on it having a value. Unfortunately I just didn’t get any farther or draw any firm conclusions, though I was poking around with removing various traits to see if the error would change or crop up from a different source.

[quote=“shilinski, post:5, topic:79685”]
This doesn’t make sense. If OrderType is a Boolean marker with set value of true or false, then a null value is impossible no matter how or when the trigger was executed. The only conclusion I come to is OrderType is undefined in some piece.[/quote]
That is correct. It is only defined for Order, Tally-Ho, and Orbit. I am open to the idea that it is in fact another counter that makes this call, but then I need to understand how a report action can make this call. And I also need to understand why You can play an entire game not seeing this error, provided that it is the second game played when the module is open (and all involved counters have been moved in the first game).

[quote=“JoelCFC25, post:6, topic:79685”]
My one lingering thought was that OrderType was a property that did not appear to be on the aircraft counters themselves, but rather on the order/rally markers that get attached to the aircraft counters. I too had immediately homed in on the description of the faulty expression in the errorlog and wanted to figure out why OrderType was coming back null when it the expression is premised on it having a value.[/quote]

That is correct. But we see this error on moves, where no markers are supposed to be attached to any counters.

I understand why you are reluctant to dive deep into the workings of the module. It is usually only the original programmer who can do this without a huge effort. The questions that you both pose to me could lead me to locate the bug when I am forced to look beyond my own bias.

I have found a new and easier way to provoke the error message. The error occurs whenever an RAF squadron or a Luftwaffe Hostile Raid moves for the first time. So if you start a game, you can simply move an RAF squadron from the Tote Board or palette, or move a Luftwaffe Hostile Raid from the palette to the main ma, and the error comes up immediately. If you use the “Design your Own” scenario, you can modify the counters and still get the error without unloading the module, as long as you pick a new counter each time.

Actually, the RAF and Luftwaffe use different prototypes, so the error message originates from the corresponding action trait in each case - this definitely is an argument against my theory that the call is random. If you remove the report actions in the “geunit” prototype then there will no longer be a message when a German unit is created or moved, but there will also be no error messages any more - and there is no order marker involved.

Do you have any suggestions that I should test?

I repeat. If OrderType is undefined, it will have a null value, and that expression will blow up with an error message.

[quote=“shilinski, post:8, topic:79685, full:true”]
I repeat. If OrderType is undefined, it will have a null value, and that expression will blow up with an error message.[/quote]

Of course! And I am happy it is so, because if the result had not been an error message, the erroneous call would have been unnoticed, and might have caused much more difficult-to-detect problems down the line.

The question is, why is this call being made, when it shouldn’t?

I have tried to investigate this. I am now testing with my latest v3 of the module, and the behaviour is different from before, but it seems more consistent, so here it goes:

The error message comes from a trigger action in prototype PlaceOrderTallyGe that is called by the key command Ctrl+Shift+T. If I delete this trigger so that there is nothing in that prototype that is executed on Ctrl+Shift+T, then I get a similar error message from the other trigger action in that prototype, this time called on Ctrl+Shift+O.

If I delete that trigger action also so that there is nothing in that prototype that is executed on Ctrl+Shift+O, I get an error message from a trigger action in the prototype PlaceOrbitLW executed on Ctrl+Shift+R. If I delete this trigger, there will be no more error messages, presumably because there are no more uses of OrderType.

Why are these trigger actions called when there is no key press of Ctrl+Shift+T, O, or R? I have used the Search function to look for calls with the key commands, but there are none: they are only called by a real key press, and not by a call inside the module.

I am at a loss here. It is quite clear why there is an error message. I can understand why I only get the message the first time the counter is moved (presumably OrderType is given a value at the first call, and that survives even when the game is closed, and a new one is started) but I can’t understand why these trigger actions are testing OrderType, when they are not called with their proper key commands. Also, I can make the error go away by removing a simple Report action.

Man, I’m talking to a brick wall here. It is NOT a question of an erroneous trigger call. It’s a question of why do you have an expression with bad syntax. Vassal is telling you that it expects to see a Boolean OrderType with a true or false value, and that’s not what it sees. Either OrderType is set to null, or more likely it is undefined. That’s your problem. It doesn’t matter when, how, or if it is called, erroneously or otherwise.

If this were a proper language, C, c++, Java, Fortran, etc, there would be a pre-compile step that would find all errors before it ran anything. In those cases, you’d be getting this error message, and your triggers would never have been fired. Vassal doesn’t seem to work that way. It says “OrderType is undefined? Oh, well, let’s pretend it is with a null value.” In fact, it MAY be flagging this error in a semi-pre-compile step, much like it flags nonexistent image files. I don’t know because I haven’t dug into Vassal source code.

So for the last time, there are two points I make here. (1) You have a syntax error in that expression because OrderType is not Boolean. And (2) No one may be executing those triggers at all.

Tone it down a bit please, shilinski. You both have valid points; yes, the error is occurring because OrderType is apparently null (and therefore not a boolean) when the expression is being evaluated. However, steenkh is also correct as the expression should not be evaluated before the Marker is initialized to true or false, making it a boolean.

If steenkh gets tired of troubleshooting, you could simply change the expression and replace OrderType with OrderType == true (it may need to be OrderType == "true", instead), or alternatively OrderType != false, to stop the Bad Data error from triggering.

Thanks jrwatts, that is exactly my point. OrderType is only defined for those three counters that represent orders, or similar. I am at a loss to understand how another counter can get to execute this trigger.

Nevertheless, your workaround to replace “OrderType” with “OrderType == true” is very interesting. I don’t know if it is possible to test for null, but I think that will be even better, because I can avoid the execution of the triggers entirely. If dinner wasn’t at the table, I’d try it out now!

Well, it certainly works! I couldn’t find out how to test for null, so I ended up just checking that “OrderType==true”. I have tried everything I could to see if there were strange side effects, but I could not find any.

I wonder if this bug is really caused by a syntax check at pre-compile step such as shilinski suggested, where the code is not actually executed but just checked for errors such as treating null values as Booelan. If this is so, then this is not just a workaround, but a real solution, and I am going to treat it as such.

(I still can’t understand why the error also goes away if I remove a report action - but it is probably best that I do not think too much about it)

Finally! I was getting very frustrated with you because I thought you were ignoring what I wrote. I thought you were chasing down the wrong rabbit hole—thinking you had erroneous trigger calls. I was pretty sure that was not the problem because otherwise it would bring a lot of other modules to their knees.

I think your problem was happening during a pre-processing step, but I would not have bet money on it. If you search old threads and look for one about the “alert()” function, you will discover a lot of very useful information. One thing I learned was that Vassal DOES some level of pre-processing. The bean shell interpreter does not know how to handle Vassal properties (for one), so Vassal needs to massage the bean shell expressions before they can be run. I think this is when it saw your problem. It didn’t know how to handle a string that was supposed to be a Boolean. I repeat; this is theory, and I can’t explain any other side effects you encountered, but I know that sometimes errors can ripple through code with unseen consequences.

Oh,. One more thing. I think you can test for null by comparing against the null string: { thing == “” }

Thanks, shilinski. I think this is a final solution, because if we are right, the code is not actually executed, but only checked. But I might have another use for comparing against the null string. There is one more Bad Data error that I see in my module, but it occurs so rarely that I have not got a grip on it. Armed with knowledge that it could also be connected to a pre-processing step, I might be able to quash the bug even though I don’t know how to reproduce it.