Synch.. how does it work?

Hello, is it possible to have some primer about how Vassal propagates information from one computer to another in an online game?

Is it normal to see a certain sequence of report action traits printed out for computer A (originating them by some movement/action) and the reverse order for the remote computer B? Or to have some reports not printed out at all for computer B?

It does look like that when “too many” things are done in a sequence by computer A, they end up not showing correctly at all for computer B. If the same sequence is split up (two-three separate commands from computer A) it works better.

The problem is not just a reporting one. I do have wrong behaviour on computer B as well (like: by clicking a button A moves an entire deck from on location to another and then distributes cards from there, B sees only the movement but no distribution at all etc).

Some insight on the order by which information are propagated in an online Vassal game would be much appreciated, so that I could at least exclude there is not some Vassal bug hidden here…

Barbanera,
It sounds like your module has grown quite complex. I’d like to look into these issues, but I haven’t noticed them in my own modules, and there’s no way I’m going to sift through your module (unless you name all your keystrokes in 3.2, perhaps). Can you reproduce the errors with basic example modules?

-Seth

I will work on a sample module, sure, and thanks for your demonstration of interest.

However, for a start, do you know what happens when player A on computer A executes a right-click command like CTRL-P on a piece?

Is the result of what he does (in terms of pieces moved, decks shuffled, global properties updated etc) sent to computer B, as a sort of “snapshot”… or is computer B told to issue CTRL-P himself like player A was sitting right there and had took control following a Retire from player B? Or is it something else entirely?

I’ll be watching this thread closely to see what turns up …if anything.

My current version of To Be King is running into synch problems as well. It is extremely complex but has been working quite well up until recently. If I test it by myself - switching player sides when necessary - it works perfectly. If I’m connected to another player, it skips the character creation process altogether. I finally worked that out so the character creation process is working but now it’s skipping the last player turn in each round.

There’s a check made to see if this is the last player or not (RoundStatus = 0: go to the next player / RoundStatus = 1: Start next round). When the last player’s turn is started, the RoundStatus is switched to 1 so that when they are finished a new round will start. But the game seems to be triggering both triggers, i.e., It goes to the next player (RoundStatus = 0) which causes the RoundStatus to switch to 1 and then immediately triggers a new round. So I’ve been asking myself, “Why would it trigger both …especially considering that the action is transferred to another piece entirely”?

Is the other computer also running this trigger after the RoundStatus has been changed? …or is the other computer just supposed to update itself to match the acting computer?

Unfortunately, it looks like that either no developer is left with a clue on how synchronization is done (old code?) or they don’t care to reply. Even with just a simple answer to clarify how the synchronization mechanism work (I am not asking to debug my module) :frowning:

Anyway, I have to say so far I have had no luck setting up an elementary sample module to reproduce my synch issues.

If there is a bug here, and I say “if”, then I have a strong feeling it is buried deep into how the turn counter works online, at least in my case.

Might your problem be related to the turn counter too? Or maybe you don’t use it at all…

I changed my code to issue, say, CTRL-A, CTRL-B, CTRL-C in that sequence and it works on both pc (even when CTRL-A is something complex moving entire decks etc).

When my code was issuing CTRL-A, followed by advancing the turn counter, which via an hotkey invoked CTRL-B, then advanced the counter, which via another hotkey invoked CTRL-C… no good. Or, better, perfectly good if the cards distributed around via CTRL-B and CTRL-C were already on the table at the start of the game, but no good if they were moved onto the table from another map via the initial CTRL-A. It worked on the computer invoking the command but not for the other player connected remotely… like it was moving too fast on the turn counter (going in reverse?? for sure report traits monitoring progress were printed in reverse!?) and then trying to distribute cards which were not yet there.

I do have to keep using the Turn Counter to trigger stuff, but my rule of thumb I learned from this experience is to minimize what it does and, above all, avoid moving too liberally in the turn order (if you need to trigger stuff at each step)… or suffer immense grief.

Barbanera,
I apologize for not responding sooner, but I believe the issue is that this is not at all a trivial process! I’d like to address it (soon) when I have an opportunity to examine this process more closely, but I want to be sure the information I’m giving is accurate. In the meantime, I’ve sent you a PM with a very basic summary as I understand it.

-Seth

Thus spake barbanera:

Unfortunately, it looks like that either no developer is left with a
clue on how synchronization is done (old code?) or they don’t care to
reply. Even with just a simple answer to clarify how the synchronization
mechanism work (I am not asking to debug my module) :frowning:

I don’t know the answer, otherwise I’d tell you at this point. I was
hoping you guys would be able to tell me what’s wrong, if anything, by
the time I worked my way back to this thread.


J.

Can you describe this in more detail? What traits, in what order, and what pieces?

-Seth

I’m using Vassal 3.2

Here’s how the turn and round sequence works:

Player turns during a round are played out in random order. This is done by having a deck in a hidden window called “Resources” which contains a player shield icon for each active player. When a new player turn begins, an icon is drawn from this deck and sent to a turn display area on the map. Next to this shield icon is a phase button. The player clicks the phase button to go from one phase to the next (or right-clicks their token and selects “Next Phase”, which sends a command to the phase button to switch to the next phase as if it were clicked).

The last phase is the “Personal Phase”. When a player clicks the “Personal Phase” button, they are essentially ending their turn. The game then checks the RoundStatus GP. If it’s 0, it gets the next player shield and starts the new player’s turn. If the RoundStatus GP is 1, the games knows this is the last player and starts a new round.

The switching of the RoundStatus GP from 0 to 1 is controlled by the deck that contains the player shield icons. When the last shield is drawn, the deck fires a hotkey to set the RoundStatus GP to 1.

The “Personal Phase” button triggers are as follows. The order shown here is the order of execution on the piece, i.e., from the bottom of the trait list up:


Trigger Action - Start new round

Trigger when properties match
$ActivePlayerSide$Phase = 6 && RoundStatus = 1

Keyboard Command
RunNextCheck

Perform These Keystrokes
BeginANewRound


Global Key Command - Begin a new round

Keyboard Command
BeginANewRound

Global Key Command
BeginNewRound

Matching Properties
CurrentMap = Kingdom && BasicName = Round Control

(This sends a command to “Round Control” to set up a new round)


Trigger Action - Get next player

Trigger when properties match
$ActivePlayerSide$Phase = 6 && RoundStatus = 0

Keyboard Command
RunNextCheck

Perform These Keystrokes
GetNewPlayerShield


Global Key Command - Get next player shield

Keyboard Command
GetNewPlayerShield

Global Key Command
SendShieldToTurnTrack

Matching Properties
CurrentMap = Resources && DeckName = Shield Active

Apply to 1 piece

(This sends a command to “Shield Active” deck, draws a new player shield and starts that player’s turn)


Let’s say you have 4 players in the game. This works perfectly for the first 2 players. When the third player is ending his “Personal Phase”, the RoundStatus is obviously still 0 as there is still one more shield in the deck. But, the 4th players turn is skipped entirely and a new round begins. This is telling me that the RoundStatus = 0 trigger is activated to get the next player. Since this is the last player shield in the deck, RoundStatus is switched to 1. Then it appears the trigger for RoundStatus = 1 is triggered (even though it’s below the RoundStatus = 0 trigger in the trait list) and a new round starts. Therefore, the last player get’s skipped.

As I’ve noted above, if I’m testing the game by myself (switching player sides when necessary) this all works perfectly. If I’m connected to another computer, the last player is always skipped. That’s why I was wondering if the other computer is just matching the game state of the computer that’s generating the action or if the other computer is actually triggering the same commands. If that’s the case, then I can see why the error occurs: The first computer sees RoundStatus as 0 and draws the last shield. The other computer may be excuting the same trigger but, by this point, the RoundStatus GP has already switched to 1 by the removal of the last card and, therefore, the second computer is starting a new round.

Three things:

  1. Thank you for a thorough response.
  2. This works in offline mode? My understanding is that Trigger Actions, for whatever reason, execute top-down. In response to a keystroke, traits are examined from the bottom up, but Trigger Actions sort of…‘hold the door’, allowing the traits above them to perform their executions, and then they execute themselves. So it seems that even in offline mode, this should set RoundStatus = 1 and then pass the RoundStatus match, changing the round.
  3. How is the round status actually changed? I’m sure it will only raise more questions, so may I take a look at your vmod?
  4. As a very quick answer to how synch works - I have never observed that the remote client is instructed to issue keystrokes. Traits are, however, associated with Commands, which are string representations of state changes. Ideally, these should accurately reflect the state changes executed /by keystrokes/ on the issuing client, and should also be decoded in such a way that the remote client is able to effect the same changes. I haven’t had an opportunity to look for the complete mechanism that builds, sends, and decodes the synch, but I believe this is how clients stay synchronized after the initial synch.
  5. Does it work if you split up RunNextCheck to cache the value of RoundStatus before doing anything else:

Trigger Action - Sequence CheckRoundStatus, ChangePlayer

Trigger when properties match
$ActivePlayerSide$Phase = 6

Keyboard Command
RunNextCheck

Perform These Keystrokes
SetMyRoundStatus
ChangePlayer

Trigger Action - Get next player

Trigger when properties match
MyRoundStatus = 0

Keyboard Command
ChangePlayer

Perform These Keystrokes
GetNewPlayerShield

Trigger Action - Start new round

Trigger when properties match
MyRoundStatus = 1

Keyboard Command
ChangePlayer

Perform These Keystrokes
BeginANewRound

Dynamic Property - MyRoundStatus
Keyboard Command: SetMyRoundStatus
Set directly: $RoundStatus$

I like how you set that up using a Dynamic Property. I was going to try to do something like that next so that may very well work. As far as one of you other notes:

As I mentioned above, the round status is changed when the last player shield is drawn from the deck. The deck uses the trait “Hot key to send when deck empties” and it’s set to fire RoundTrigger.

I have a Global Key Command set to trigger on RoundTrigger and it sends the command SetRoundStatusTo1
Property match
CurrentMap = Resources && BasicName = Controller

The Controller is just a piece that I use to adjust things when needed. In this case, it has a Set Global Property that changes the RoundStatus GP to 1 triggered by SetRoundStatusTo1

I can send you the vmod if you like but I don’t know if it’s going to help you much. It’s huge and very complex. In fact, I’m surprised at how well it’s operated so far and this is the first major issue I’ve had with it. If you’d like to get online sometime, though (I have a Ventrilo server we can talk on) we can go through it and try to see if we can pick apart the problem.

In the meantime, I’ll try the DP suggestion and see if that works.

I made the change using a DP and it got stranger.

I’m testing this using a 2 player game with 2 computers (one is linux Ubuntu and the other is Windows XP). For the sake of simplicity, I’ll call the machine that initially runs the game as the Host (linux) and the machine that synchs to it as the Client (windows).

If the Host is the first player chosen in a round to take a turn and they end the Personal Phase of their turn, the Client’s turn begins …just like it’s supposed to. However, if the Client is the first player chosen in the round to take their turn and they end the Personal Phase of their turn, the host player is skipped and a new round starts. Is that odd or what?

It can’t be a trait problem on a player’s phase button as all the phase buttons use the same prototype so they should behave exactly the same.

Also, once again, everything works fine if I’m testing on one computer and just switching between player sides.

Yeah, that’s…not what I would have expected, certainly. Responded via PM!

Bug 4300: Deck Empty Hotkey fired by remote clients when adding first card to deck.

Dr. Nostromo, RoundStatus is being set to 1 by the first player shield token returned to the active shields deck. You then call ResetRoundStatus from the Round Control piece, which should overwrite RoundStatus with 0. This works on the remote client - but since the deck empty hotkey is originated by the remote client after the local client has finished processing the complete BeginNewRound sequence, the local client receives the RoundStatus = 1 Command after it’s run ResetRoundStatus. Then all the players end up with mismatched RoundStatuses, and the rounds end prematurely.
Since you’re in the middle of a key sequence anyway, I imagine a possible workaround would be to have SendShieldToTurnTrack set the GP when Shield Active_numPieces = 1 before continuing to the turn track - and leave the deck empty hotkey, err, empty.

So, Barbanera, the synchronize mechanism - there are three ways that I have found that state changes in the game are effected:

  1. Each trait - as well as other change listeners such as toolbar buttons - is responsible for its own immediate execution behavior (local behavior)
  2. Each trait is responsible for its own encoding and decoding to and from a Command string, which is passed through the game server to other players (remote behavior)
  3. Each Game Component is responsible for producing a restore command which will bring a new player up to speed - whether by synching or by restoring a saved game. (synch behavior)
    So yes, it seems that what is passed is essentially a snapshot, and not a sequential list of all the Commands passed. This is specified in VASSAL.chat.SynchCommand.

Of course, this is only based on my own observations as I try to uncover bugs and beat my stupid module into submission. When one of the developers has time - especially Tim - I also would appreciate any clarification of these processes, as I believe understanding them makes it easier to understand what might be going on when things don’t work the way they should.

Cheers,
Seth

Thanks Seth.

However, that only scratches the surface of the process, I think. What about when several traits are executed in succession? Each sends a “snapshot” of the results of what it did, one by one till the sequence finishes or just a snapshot at the end of it all? And is it/are they a complete snapshot of the game or just of the affected pieces, global properties etc?

It seems plausible that it’s not a complete snapshot at the end of the sequence because I, for one, but also Dr. Nostromo say, are seeing different results on the remote computer compared to the local computer (triggering the sequence).

Thus, either there are bugs in the way each trait encodes its own “snapshot” and sends it through (see bug 4278, for example: failure to transmit local properties to the right map) or there are conflicts in the timing the various snapshots are transmitted, received and/or decoded (see for example my issue with moving decks and distributing cards which works on both computers when the deck is not moved, but seems to lag behind when is initially moved and then fails to distribute on the remote pc only). Or both type of bugs are there somewhere.

Yes, it would be nice to have some more insight by Tim or others. Starting maybe from a walkthrough to the whole bottom-up (traits) / top-down (triggers) / obscure-obscure (turn counter) functionality.

Barbanera,
I apologize for the confusion. I thought you were originally asking about how the ‘Synchronize’ command works - that works by asking each of the Game Components to build an appropriate restore command to be able to reproduce the game state on the remote computer, and it has nothing to do with keystrokes.
Trait execution is different. When a keystroke is issued, traits are evaluated from the bottom to the top, and as they are evaluated they have the option of responding to that keystroke. If they make a state change as a result of the keystroke, they are also then responsible for building a Command to inform the remote computer how to replicate that state change. Commands are encoded into a string which is uploaded to the game server and then broadcast to all the other connected clients; they are then decoded by the clients and executed to make the appropriate changes.
I mentioned elsewhere that Trigger Action and Report Action are fired after all other traits, and from the top down. This is because, when either of these is evaluated in response to a keystroke, it delays and insists that all of the traits above it finish before it will evaluate itself. Triggers and Reports are polite, like that.
As far as sequences of traits, as the individual traits are evaluated they append their own Commands to a list of Commands which is then encoded and sent, as a unit, to the game server. They are decoded, separated into individual traits again, and executed in the same sequence in which they were built.
It’s possible that there are bugs with the interactions between all of these processes - execution, sequencing, encoding, decoding, and remote execution - and in fact (I believe) bugs 4278 and 4300 are examples of such bugs. However, it’s very hard to know what to look for without a clear description of the problems you’re experiencing.

Cheers,
Seth