Possible Vassal bug causing extra GKC execution after Send To (Zone) Location

While adding some automation enhancements to the Normandy 44 module, I found a weird but reproducible issue which smells to me like a bug in the Vassal engine itself.

I have created a stripped down demo module called TestCase_JGH with a single Test case scenario which demonstrates the issue. It can be downloaded from my DropBox using this link.

This demo was created using the Normandy 44 map board with a Multi-zoned Grid in which each space of the General Records Track (GRT) is defined as a separate zone (named “General Records Track 0” through “General Records Track 9”). The scenario instantiates just 3 game pieces - the Fortress Cherbourg Marker and GE 7 Army Marker each start in the GRT 0 space, and the Weather Marker starts on the Weather Track space 2.

The functional guts of this demo are found within the TestCaseCtlr prototype (referenced in the Weather Marker) and the TestCaseMarker prototype (referenced in the two HQ markers).

The desired functionality is that the Weather Marker can automatically advance the two HQ markers along the GRT. The number of spaces that they are to advance will ultimately be determined from the Weather Table, but in the demo are user-specified for debug purposes.

The steps to demonstrate the issue are:

  • Open the Testcase_JGH module using Vassal version 3.7.12.
  • Start the Test case scenario.
  • Right-click the Run testcase menu item on the Weather Marker.

The module will first prompt the user (twice) to specify the number of “Supply Points” to be allocated to each of the two HQ markers on the GRT. It will then advance those markers along the GRT the number of spaces equal to their respective Supply Points (SPs).

This mechanism works as expected in most cases. For example, if you run it while specifying 1 SP for Cherbourg and 2 SPs for 7 Army, those two Markers are advanced along the GRT to spaces 1 and 2 respectively, and the following lines are emitted to the Chat Log:

  • STARTING TEST
  • CherbourgSupply=1, 7ArmySupply=2
  • Fortress Cherbourg Marker received AdjustMarker command with GRT_Adjust=1
  • Fortress Cherbourg Marker moved from General Records Track 0 to General Records Track 1
  • GE 7 Army Marker received AdjustMarker command with GRT_Adjust=2
  • GE 7 Army Marker moved from General Records Track 0 to General Records Track 2

Note that the two lines above shown in bold (and which really appear in green) are from the two HQ markers, indicating when they receive the AdjustMarker Global Key Command which is sent by the Weather marker. Each of those two HQ markers received exactly one such GKC, as intended.

To demonstrate the issue, run the test while specifying an equal number of SPs to be sent to each HQ. For example, press the UNDO button to roll back the previous test and return the markers to the 0 space on the GRT, then rerun the test while specifying 1 SP for each HQ. The Chat Log will report the following:

  • STARTING TEST
  • CherbourgSupply=1, 7ArmySupply=1
  • Fortress Cherbourg Marker received AdjustMarker command with GRT_Adjust=1
  • Fortress Cherbourg Marker moved from General Records Track 0 to General Records Track 1
  • GE 7 Army Marker received AdjustMarker command with GRT_Adjust=1
  • GE 7 Army Marker moved from General Records Track 0 to General Records Track 1
  • GE 7 Army Marker received AdjustMarker command with GRT_Adjust=1
  • GE 7 Army Marker moved from General Records Track 1 to General Records Track 2

Note that there are now three bold lines in the log, implying that the GE 7 Army Marker received the AdjustMarker GKC twice. The 7 Army marker was accordingly advanced two spaces instead of one. That is the unexpected behavior I am reporting.

My experimentation suggests the following pattern:

  1. The issue always occurs whenever each HQ is allocated the same number of SPs, and in each case it is the 7 Army HQ which receives the extra GKC (and advanced along the track) twice.

  2. If the TestCaseCtlr prototype is hacked to reverse the order in which the two HQs are sent their GKCs, then it is the Cherbourg HQ which receives the extra GKC (it is always the last HQ which receives the extra GKC).

  3. If the TestCaseMarker prototype is hacked to disable the Send To Location command which actually advances the marker, then the issue with the extra GKC does not occur. This seems important, but I don’t know what to conclude from it.

Some implementation notes about this demo:

To remove unnecessary complications and allow inspection (using /prop show commands), the demo relies on Global Properties to store the user specified supply allocations and to pass those supply values from the Weather Marker to the HQ markers. I had originally passed the supply values directly into Dynamic Variables in the HQ markers using the “Set Dynamic Properties in target pieces” feature of the GKC trait, then tried using the Set Piece Property trait. The same issue occurred with those implementations also.

To pass the supply value to an HQ Marker the TestCaseCtlr prototype stores that value into the GRT_Adjust Global Parameter, then uses the Global Key Command trait to send the AdjustMarker command to the HQ, targeting the HQ by specifying its BasicName in the Additional matching expression field. That GKC originally used the Pre-select (Fast Match) field for that purpose; the issue occurred with either variation.

Any advice on how to proceed would be appreciated. I am available to conduct additional experiments if needed.

Cheers,
Jim Hunter.

1 Like

Thanks for the detailed detailed repro instructions, I’ll have a look tomorrow.

I thought I’d take a look at this for fun yesterday. I downloaded the module, and I streamlined it ever further. I dumped the saved game file. Instead, all I needed to do was drag off the palette 3 pieces. Everything is driven by right-clicking the weather piece. He has a trigger there to do a list of commands. I added right-click menu text for each command so I could skip stuff. I hardcoded the key global property to 1. I tossed extraneous reports, and I changed a few things that I would never do myself.

What I finally ended up with is: right click piece A to send it one GKC. Then do it again for piece B. What happened? Piece A moved once. Piece B moved twice. Frankly, I’m stumped. Either weather is sending an extra GKC to B, or B is acting like it got two GKCs. If I change the order, then A goes twice. And no, it doesn’t always happen. If I send yet another GKC to B, it handles it only once as it should. There’s a couple more things I may try like replacing the move with a simple report, but I’m stumped.

Okay, one last thing, and I’m done.
I changed the pieces so they don’t move when they receive the GKC. Instead, all they do is report that they received it. This time it worked as expected–no more double GKCs, so it appears that the problem may have something to do with zones. I don’t use zones this way, and I don’t understand how they are used here, so I’m done. Weird behavior in any case.

I had previously ran that same experiment, and observed that the issue goes away when the Send To Location step is omitted. That led me to suspect the Send To Location (Zone) functionality, as opposed to the use of zones.

FWIW, I also ran the experiment where the GRT_Adjust Global Variable passed to the markers is interpreted as the literal destination zone number, rather than an addition to it; the issue remained. This eliminated the use of the Zone_Num Zone Property as found in the starting zone - and eliminated another complexity.

Cheers,
Jim Hunter.

I think I figured out a way to fix this.
In the piece prototype, add the “does not stack” trait.

Stacking can cause an undesired effect in some cases, and it has bitten me in the past. With stacking on, if you move one piece, then Vassal also likes to move all pieces in the stack. Why is this significant in your case? I have no idea, but disabling stacking seemed to work for me. But maybe somehow I got lucky and it doesn’t.

BTW, if you don’t want people to drag and drop those pieces off the track, I’d disable moving too.

1 Like

I agree that stacking can cause these kind of issues. I encountered a very similar-sounding problem a while ago, that I described in this post.

Update: I can see from @Brent_Easton’s explanation below that I must have been working around the same bug, without exposing the root cause with sufficient clarity at that time.

OK.

Very interesting, I have got to the root of the problem and it is indeed a subtle Vassal GKC bug.

Firstly, your cut-down module and repro instructions where OUTSTANDING and made it very easy for me to track down the issue.

One technique I often use in these cases is to Add menu items to all the counters for all the individual Trigger steps so I can can fire them off manually one-by-one to help isolate the problem.

With this, while running Vassal under the debugger, I was able to find the GKC to the 7th HQ is being sent just once, but it indeed being received twice by the counter.

The bug is caused by the fact that the first thing Vassal does it collect up all the pieces it needs to act on, based on after applying any Fast-matches. (No Fast Match in this case). It then runs through and applies the GKC to each piece.

However, the list of pieces to be run through is a list of STACKS of counters, not the individual counters. And when the 7th Headquarters is given the the GKC, it jumps forward one space and stacks with the Cherbourg HQ piece. The GKC processing then moves on to the next Stack in the list, which is the Cherbourg piece’s stack, but that Stack now has the 7th piece in it as well. BOOM, the GJC gets re-applied to the 7th HQ.

This explains why removing the Send to Location or changing the pieces to Do Not Stack fixes the problem, It prevents the 7th HQ from changing Stacks during the running of the GKC. It also depends on what order the Stacks happen to be stored in with Vassal.

This bug will potentially raise it’s head whenever a Stacking piece is moved during the execution of a GKC and depends on the exact layout of pieces on the board and the exact order they are stored within Vassal, so will be intermittent and unpredictable.

I am hesitant to hack into the GKC code too viciously, the simple fix is to maintain a list of all pieces that have already received the GKC skip any that appear for a second time.

Thanks for the report!

2 Likes

Thanks for investigating this. Nothing beats “looking under the hood” to observe directly what is happening.

One technique I often use in these cases is to Add menu items to all the counters for all the individual Trigger steps so I can can fire them off manually one-by-one to help isolate the problem.

Good idea! I’ll make a point of doing that in the future.

For my follow up questions (if you have more time to spare), I’ve hacked my demo module to give me that capability.

The bug is caused by the fact that the first thing Vassal does it collect up all the pieces it needs to act on, based on after applying any Fast-matches. (No Fast Match in this case). It then runs through and applies the GKC to each piece.

However, the list of pieces to be run through is a list of STACKS of counters, not the individual counters. And when the 7th Headquarters is given the the GKC, it jumps forward one space and stacks with the Cherbourg HQ piece. The GKC processing then moves on to the next Stack in the list, which is the Cherbourg piece’s stack, but that Stack now has the 7th piece in it as well. BOOM, the GJC gets re-applied to the 7th HQ.

I’m probably not understanding you properly, but this description of what is happening seems contradicted by a couple of other data points I observe.

The first data point is this:

  1. Load a new test case scenario. Both HQs start in Zone 0, but not in a stack.
  2. Manually move one of them to stack with the other.
  3. Set each HQ’s supply to 1.
  4. Allocate supply to just one of HQ (either one). The other one remains in place and does not receive that GKC. That is in spite of the fact that it is presumably a member of the stack which appears on the list, so should have been processed by the GKC.

Perhaps when traversing the list of stacks, and all pieces within each stack are processed, that processing re-runs the matching rules against each such piece (per BasicName in this case), so non-matched pieces are skipped and NOT sent the GKC. This would explain this data point, so I’ll guess that is how it works.

The second data point is:

  1. Load a new test case scenario. No need to stack the HQs in Zone 0.
  2. Set each HQ’s supply to 1.
  3. Allocate supply to Cherbourg HQ. It advances to Zone 1 as expected.
  4. Allocate supply to 7 Army HQ. It advances to Zone 1, but receives a 2nd GKC and ends up in Zone 2.
    This is similar to the originally reported test case, but in this case only the 7 Army HQ was matched by the GKC. Therefore, the only “stack” that should have been on the list is the starting location of the 7 Army HQ in Zone 0. The stack in Zone 1 should have not been on that list.

Interestingly, if I UNDO step 4, then redo it, the 7 Army HQ this time does NOT receive the extra GKC.

Is the list of stacks completely generated before any GKCs are sent, and remains static during that processing, or are the stacks actually traversed “live” amidst the GKC processing?

My final question is this - why is the list of matched pieces transformed into a list of stacks prior to sending the GKCs? That seems to be the root cause of all of these issues. Your idea of maintaining a list of pieces which have already received the GKC once (to avoid duplicate GKCs) would fix the originally posted issue, but may not address other issues arising from the “stack granularity” of this implementation. I have no knowledge of how the code is structured, but from my position of ignorance, it seems to me that transforming the list of matched pieces into a list of the stacks they reside in is just extra work.

OK, my curiosity required me to raise those questions. Having done so, if you are confident that your idea for a fix will resolve all issues, then that’s good enough for me.

I will of course be very happy to try any test build that you make available.

Cheers,
Jim Hunter.

1 Like

Also thanks to shilinski and marktb1961 for helping me to understand this issue.

Cheers,
Jim Hunter.

1 Like

First up, I have created a test build VASSAL-3.8.0-SNAPSHOT-edd3920-13398-GKC-Double-Trouble (available from Builds of vassalengine/vassal). Can you see if this resolves the problem and then we can look at any remaining weirdness. My explanation of the GKC processing was simplified, it is significantly more complex.

My final question is this - why is the list of matched pieces transformed into a list of stacks prior to sending the GKCs?

They are not transformed, the pieces exist in Stacks to begin with and are maintained in Stacks during selection and the code used by Vassal to iterate over those pieces requires that they be in Stacks (An implementation of a Visitor/Dispatcher pattern).

This is very old, very complex, and much modified code and while it’s not a great implementation, any attempt to change it risks changing the behaviour in existing modules by changing the default order that pieces get processed in.We are fairly certain that GKC’s in general are otherwise working properly, so I have decided to take the lightest touch possible to resolve the issue.

Regards,
Brent.

OK, that all makes sense to me.

I tried your test build and confirmed that it fixes my originally reported problem, and also fixes the abnormal behavior I reported as the “second data point”. I know of no other issues at this time.

Nice work!

Unless you advise otherwise, I plan to continue my Normandy 44 development using this test build, with an expectation of releasing the module at some point after Vassal 3.8 gets released. If I uncover any new related issues, I’ll be sure to let you know.

With gratitude,
Jim Hunter.

1 Like

That’s good news.

We will get this fix into a 3.7.13 release.

1 Like