Replaced the 'Turn' protocol by the Update' protocol.

Bumped the UQM and netplay protocol versions.
Some cleanups.



git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3537 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
Meep-Eep
2010-04-24 20:59:07 +00:00
parent 91fde6f5e7
commit e4886e60ee
32 changed files with 835 additions and 535 deletions
+138 -33
View File
@@ -5,7 +5,7 @@ of a network connection.
This is used when both parties need to sending information to the
other side, but what each party is doing does not interfere with
what the other party is doing.
- Only speak in your own turn ("Turn")
- Agree on changes ("Update")
This is used when the parties have changes to make to common data.
- Mutual agreement on an action ("Confirm")
This is used to end a state where both parties are modifying
@@ -49,43 +49,148 @@ local decision -> Send READY goto 3
============================================================================
"Turn" negotiation.
"Update" negotiation.
For some actions (like changing a shared configuration option), it is
important that both sides don't just send changes at once.
To handle this, only one party may send these packets at any moment.
If the party whose turn it isn't wants to speak, or if the party whose
turn it is doesn't have anything further to say, he can send an ENDTURN
packet. The other party should confirm this by sending another ENDTURN
packet back.
During configuration, both sides may change the same properties. So that the
two sides don't have to take turns, the changes are made locally, and then
the changes are synchronised.
States:
0. myTurn - I may speak
myTurn && !endTurn
1. endMyTurn - I've given up speaking, waiting for confirmation
myTurn && endTurn
2. yourTurn - You may speak
!myTurn && !endTurn
3. endYourTurn - I want to speak, waiting for confirmation
!myTurn && endTurn
To this end, each side has a state containing two copies of each property:
1. The value of the property as it is locally
2. The last value which it sent to the other side (until it is no longer
relevant for the protocol)
Messages:
- ENDTURN - "this party ready to change turns"
The basic idea of the Update protocol is:
- both sides each send and receive one packet before being allowed to
send another one (we'll call this a "turn" here)
- when a packet has been sent and one has been received in a turn,
and the sent and received values are the same, then that value is the
agreed upon value. If the sent and received values differ, then a
tie breaker determines which one prevails.
- when the first local change of a turn is made, the change is sent to
the other side
- when a local change has been made while a packet has already been
sent this turn, the change will be made locally, but communicating the
change will be postponed
- when a turn ends while a change has been postponed, and this change isn't
negated by a remote change, then the change will be sent
- when a remote change arrives, and a packet has not been sent this turn,
the local state is updated with the change, and the same packet is sent
to the other side to confirm the change
The tie breaker is required to always let one side win, and the other
side lose, given the same property.
Any function satisfying this requirement is usable, but the currently
used one will return true for the side which 'owns' the property, and
false on the other side.
Another tie breaker could be one which always lets the same side win,
regardless of the property.
From state 0 (myTurn):
local decision -> Send ENDTURN, goto 1
received ENDTURN -> Send ENDTURN, goto 2
The protocol:
From state 1 (endMyTurn):
received ENDTURN -> Send ENDTURN, goto 2
From state 1, {own=x0, sent=--}:
1a Local change x1 -> send(x1); state=2:{own=x1,sent=x1}
1b Received UPDATE(x1) -> send(x1); state=1:{own=x1,sent=--}
From state 2 (yourTurn):
local decision -> Send ENDTURN, goto 3
received ENDTURN -> Send ENDTURN, goto 0
From state 2, {own=x0, sent=x0}:
2a Local change x1 -> state=3:{own=x1,sent=x0}
2b Received UPDATE(x0) -> state=1:{own=x0,sent=--}
2c+ Received UPDATE(x1) -> state=1:{own=x0,sent=--} if winTieBreak
2c- Received UPDATE(x1) -> state=1:{own=x1,sent=--} if !winTieBreak
From state 3 (endYourTurn):
received ENDTURN -> Send ENDTURN, goto 0
From state 3, {own=x1, sent=x0}:
3a Local change x0 -> state=2:{own=x0,sent=x0}
3b Local change !x0 -> state=3:{own=xN,sent=x0}
3c Received UPDATE(x0) -> send(x1); state=2:{own=x1,sent=x1}
3d+ Received UPDATE(!x0) -> send(x1); state=2:{own=x1,sent=x1} if winTieBreak
3d- Received UPDATE(!x0) -> state=1:{own=x?,sent=--} if !winTieBreak
Explanation:
We keep track of the local value ('own'), and whether or not we sent a packet
in this turn ('sent' != '--'), and if we did, the last packet which we sent
('sent'). When we proceed to the next turn, 'sent' is set to '--'.
State 1: We have not yet sent a packet this turn (which implies that we
haven't made a local change this turn).
1a. A local change is made. We update our local value, and send this to
the remote side.
1b. A remote change arrives. We don't have any local change ourselves,
so we accept the remote change, and sent it back to confirm.
With both a packet sent and one received, the turn ends, and 'sent'
is set back to '--'.
State 2: We have sent a packet this turn (after making a local change), and
have not changed our local value since (or we have changed it and changed
it back).
2a. A local change is made. We update our local value, but we have already
sent a packet this turn, so we can't report it until the next turn.
2b. A remote change arrives, and it is equal to both our local value and
the value which we sent to the other side this turn.
This remote notification may be a confirmation of our update, or a
coincidental identical remote change.
Either way, the packet acts as a confirmation, and we do not need
to change anything. With both a packet sent and received, the turn ends
and so 'sent' is set back to '--'.
2c. A remote change arrives, and it is not equal to our local value (which
is the same as the value which we sent).
The tie breaker decides which value prevails. The same value will
prevail on the remote side, so there is no need to send any confirmation
packets. The turn ends, and 'sent' is set back to '--'.
State 3: We have sent a packet this turn, and have made a local change since.
3a. A local change is made back to the value which we sent. No action
is required.
3b. A local change is made. We update our local value, but we have already
sent a packet this turn, so we can't report it until the next turn.
3c. A remote change arrives, and it is equal to the value which we sent
(which isn't equal to the current local value).
The sent/received value is the accepted value and the turn ends.
But we have changed our value since already, so as the next turn
starts, we immediately send an update.
3d. A remote change arrives, and it is not equal to the value which we sent
this turn.
+ We win the tie break, so the value which we sent prevails.
But we have changed our value since already, so as the next turn
starts, we immediately send an update.
- We lose the tie break, so the value which the remote value sent
prevails. We accept the value and the turn ends.
(An alternative would be to consider the local change(s) which
we made after our change was sent as made in the following turn,
in which case we would send our current local value in the next turn.
The advantage would be that 3d- would become equal to 3d+,
so these could be joined (with 3c too), which saves a few
if-statements in the code, but this requires another packet to be sent
and replied to in what currently is 3d-.)
Proof outline that this works:
- The states can never get out of sync:
After both a packet has been sent and received, both sides (temporarilly)
have accepted the same value
- There can be no indefinite loop without ongoing local changes.
Once there are no more local changes:
from state 3 we always go to state 2 or 1,
from state 2 we always go to state 1
from state 1 we can only go back to state 1, and only when a packet
has been received.
So eventually, both sides are in states 1. With both sides in state 1,
no packets have been sent in the current turn, so no more packets are
there to be received.
- Both sides can make changes, as long as the side which wins the tie breaks
stops making changes now and then:
Without making local changes, a side will go to state 1 eventually.
If a side makes a local change, and this is received by the other side
while it is in state 1, then that other side will accept the change.
The Confirm negatiation is used to finish the Update negotiation.
This works because local changes triggered by the reception of remote changes
are treated as local modifications for the purpose of the Confirm negotiation.
And this can be done because whenever the Confirm negotiation is in a state
where remote changes may be expected, local modifications are still allowed
(possily after sending a CANCEL packet).
============================================================================
@@ -130,7 +235,7 @@ States:
Handshake messages:
- CONFIRM1 - "the current local configuration OK for me"
- CONFIRM1 - "the current local configuration is OK for me"
- CONFIRM2 - "acknowledging your CONFIRM1; my own configuration is unchanged
since I sent CONFIRM1 (after the last CANCEL)"
- CANCEL - "forget about my earlier CONFIRM1"
@@ -148,7 +253,7 @@ From state 1: (localOk)
local cancel -> Send CANCEL, goto 4
received CONFIRM1 -> Send CONFIRM2, goto 3
received CONFIRM2 -> Send CONFIRM2, goto 8
received MESSAGE(changes) -> Process(changes), Send CANCEL, goto 4
received MESSAGE(changes) -> Send CANCEL, Process(changes), goto 4
From state 2: (remoteOk)
local confirmation -> Send CONFIRM2, goto 3
@@ -204,7 +309,7 @@ aren't expected.
"Reset" negotiation.
See src/sc2code/netplay/proto.c
See src/sc2code/netplay/proto/reset.c
============================================================================