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:
@@ -1,4 +1,5 @@
|
||||
Changes towards version 0.7:
|
||||
- Fixed the network SuperMelee team configuration protocol - SvdB
|
||||
- Fixed fuel reserve bounds checks - SvdB
|
||||
- Fixed a crash when filling fuel tanks over 10 (bug #1082) - Alex
|
||||
- Got rid of many warnings - SvdB
|
||||
|
||||
+138
-33
@@ -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
|
||||
|
||||
|
||||
============================================================================
|
||||
|
||||
+141
-26
@@ -1,63 +1,178 @@
|
||||
NetState_unconnected is the initial state. When a connection attempt is made,
|
||||
the state is set to NetState_connecting.
|
||||
The state field of a NetConnection is NULL.
|
||||
== Any connected state ==
|
||||
Some packets may be sent and received in any state except
|
||||
NetState_unconnected.
|
||||
These are: PING, ACK, ABORT, RESET
|
||||
These are not listed below at each individual state.
|
||||
|
||||
NetState_connecting indicates a connection is in progress.
|
||||
When the connection is established, an INIT packet is sent, the state
|
||||
is changed to NetState_init and InputFunc is set to DoNetworkInit.
|
||||
The state field of a NetConnection is a ConnectStateData structure.
|
||||
Whenever a connection is aborted, the state is returned to
|
||||
NetState_unconnected. This state transition is not listed below at each
|
||||
individual state.
|
||||
|
||||
|
||||
== NetState_unconnected ==
|
||||
NetState_unconnected is the initial state.
|
||||
|
||||
NetConnection.state: NULL
|
||||
|
||||
Packets ok to send: none
|
||||
Packets ok to receive: none
|
||||
|
||||
Next state:
|
||||
NetState_connecting -- connection attempt in progress
|
||||
|
||||
|
||||
== NetState_connecting ==
|
||||
NetState_connecting indicates that a connection is in progress.
|
||||
|
||||
When the connection is established, the state is changed to NetState_init
|
||||
and InputFunc is set to DoNetworkInit.
|
||||
|
||||
NetConnection.state: instance of ConnectStateData
|
||||
|
||||
Packets ok to send: none
|
||||
Packets ok to receive: none
|
||||
|
||||
Next state:
|
||||
NetState_init -- connection established
|
||||
|
||||
|
||||
== NetState_init ==
|
||||
NetState_init is for initialising the connection before actual game
|
||||
information is sent. When an INIT packet is received, the state is set
|
||||
to NetState_inSetup and InputFunc is set to DoMelee.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
information is sent.
|
||||
|
||||
As this state is entered, an INIT packet is sent. When an INIT packet
|
||||
has also been received, the state is set to NetState_inSetup and
|
||||
InputFunc is set to DoMelee.
|
||||
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
Packets ok to send: INIT
|
||||
Packets ok to receive: INIT
|
||||
|
||||
Next state:
|
||||
NetState_inSetup -- received an INIT packet
|
||||
|
||||
|
||||
== NetState_inSetup ==
|
||||
NetState_inSetup is the state in which the fleet configuration is negotiated.
|
||||
Only the side who has myTurn set may send this ship information, by means
|
||||
of FLEET and TEAMNAME packets.
|
||||
The Turn negotiation is used to change myTurn.
|
||||
|
||||
This does not necessarilly mean that the fleet setup screen is visible;
|
||||
this state is also held after a battle when the battle outcome is still
|
||||
displayed.
|
||||
|
||||
Each side may send fleet configuration changes to the other side, by means of
|
||||
FLEET and TEAMNAME packets. Agreement on configuration settings is provided
|
||||
through the Update negotiation.
|
||||
The Confirm negotiation is used to end this state and go to
|
||||
NetState_preBattle. At this time InputFunc is set to DoPreMelee.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
Packets ok to send: FLEET, TEAMNAME, HANDSHAKE0, HANDSHAKE1,
|
||||
HANDSHAKECANCEL, HANDSHAKECANCELACK
|
||||
Packets ok to receive: FLEET, TEAMNAME, HANDSHAKE0, HANDSHAKE1,
|
||||
HANDSHAKECANCEL, HANDSHAKECANCELACK
|
||||
|
||||
Next state:
|
||||
NetState_preBattle -- configuration has been confirmed
|
||||
|
||||
|
||||
== NetState_preBattle ==
|
||||
NetState_preBattle is used for non-interactive battle negotiations.
|
||||
|
||||
One side sends the random seed; the other receives it.
|
||||
Both sides send their input delay value.
|
||||
The Ready negotiation is used to end this state and go to
|
||||
NetState_interBattle.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
|
||||
NetState_interBattle is used to allow either side to do some local
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
Packets ok to send: SEEDRANDOM, INPUTDELAY, READY
|
||||
Packets ok to receive: SEEDRANDOM, INPUTDELAY, READY
|
||||
|
||||
Next state:
|
||||
NetState_interBattle -- ready to continue
|
||||
|
||||
|
||||
== NetState_interBattle ==
|
||||
NetState_interBattle is used to allow each side to do some local
|
||||
initialisations before moving on.
|
||||
The Ready negotiation is used to end this state and go to
|
||||
NetState_selectShip, or if there are no more ships to be selected,
|
||||
to NetState_inBattle.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
NetState_selectShip, or if all sides have selected a ship, to
|
||||
NetState_inBattle, or if there are no more ships in a fleet,
|
||||
to NetState_inSetup.
|
||||
|
||||
NetState_selectShip is where a side may select his ship. The other
|
||||
If there are no more ships, the the Ready negotiation is used to
|
||||
enter NetState_inSetup.
|
||||
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
Packets ok to send: READY
|
||||
Packets ok to receive: READY
|
||||
|
||||
Next state:
|
||||
NetState_selectShip -- ready to select the next ship
|
||||
NetState_inBattle -- ready to start the battle
|
||||
NetState_inSetup -- no more ships; game over
|
||||
|
||||
|
||||
== NetState_selectShip ==
|
||||
NetState_selectShip is where a side may select their ship. The other
|
||||
side is waiting for notice of this selection.
|
||||
As soon as the selection has been sent or received, the state is changed
|
||||
back to NetState_interBattle.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
Packets ok to send: SELECTSHIP
|
||||
Packets ok to receive: SELECTSHIP
|
||||
|
||||
Next state:
|
||||
NetState_interBattle -- a selection has been made
|
||||
|
||||
|
||||
== NetState_inBattle ==
|
||||
NetState_inBattle is where the actual melee takes place.
|
||||
Both sides send their input until the game is over, at which point
|
||||
the Ready negotiation is used to end this state and go to
|
||||
the NetState_endingBattle state. Until the Ready negotiation has been
|
||||
completed, the simulation is continuing.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
Packets ok to send: BATTLEINPUT, READY
|
||||
Packets ok to receive: BATTLEINPUT, READY
|
||||
|
||||
Next state:
|
||||
NetState_endingBattle -- ready to end the battle
|
||||
|
||||
|
||||
== NetState_endingBattle ==
|
||||
NetState_endingBattle is where the local side waits for the remote
|
||||
battle frame count, after it has sent its own. When it arrives,
|
||||
the state changes to NetState_endingBattle2.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
Packets ok to send: BATTLEINPUT, FRAMECOUNT
|
||||
Packets ok to receive: BATTLEINPUT, FRAMECOUNT
|
||||
|
||||
Next state:
|
||||
NetState_endingBattle2 -- we know when to end the battle
|
||||
|
||||
|
||||
== NetState_endingBattle2 ==
|
||||
NetState_endingBattle2 is where the side with the lowest battle frame count
|
||||
catches up with the other other side, while the other side waits.
|
||||
The Ready negotiation is used to signal that each side is ready,
|
||||
and the state changes back to NetState_interBattle.
|
||||
The state field of a NetConnection is a BattleStateData structure.
|
||||
and the state changes to NetState_interBattle.
|
||||
|
||||
NetConnection.state: instance of BattleStateData
|
||||
|
||||
When a connection is aborted, the state is returned to NetState_unconnected.
|
||||
Packets ok to send: BATTLEINPUT, READY
|
||||
Packets ok to receive: BATTLEINPUT, READY
|
||||
|
||||
Next state:
|
||||
NetState_interBattle -- get ready for the next ship
|
||||
|
||||
|
||||
|
||||
+14
-5
@@ -302,17 +302,29 @@ SetContext (OldContext);
|
||||
return (hBattleShip);
|
||||
}
|
||||
|
||||
static QUEUE *
|
||||
GetShipQueueForSide (COUNT sideNr)
|
||||
{
|
||||
if (sideNr == 0)
|
||||
return &GLOBAL (built_ship_q);
|
||||
else
|
||||
return &GLOBAL (npc_built_ship_q);
|
||||
}
|
||||
|
||||
// Get the next ship to use.
|
||||
HSTARSHIP
|
||||
GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player)
|
||||
{
|
||||
HSTARSHIP hBattleShip;
|
||||
|
||||
if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE)
|
||||
{
|
||||
// Get the next ship from the battle group.
|
||||
hBattleShip = GetHeadLink (&race_q[which_player]);
|
||||
}
|
||||
else if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
|
||||
{
|
||||
// Let the player chose his own ship. (May be a computer player).
|
||||
// Let the player chose their own ship. (May be a computer player).
|
||||
|
||||
if (!(GLOBAL (CurrentActivity) & IN_BATTLE))
|
||||
{
|
||||
@@ -367,10 +379,7 @@ GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player)
|
||||
QUEUE *pQueue;
|
||||
HSHIPFRAG hNextShip;
|
||||
|
||||
if (which_player == 0)
|
||||
pQueue = &GLOBAL (built_ship_q);
|
||||
else
|
||||
pQueue = &GLOBAL (npc_built_ship_q);
|
||||
pQueue = GetShipQueueForSide (which_player);
|
||||
|
||||
hBattleShip = GetHeadLink (&race_q[which_player]);
|
||||
for (hStarShip = GetHeadLink (pQueue);
|
||||
|
||||
@@ -493,6 +493,7 @@ spawn_ship (STARSHIP *StarShipPtr)
|
||||
return (hShip != 0);
|
||||
}
|
||||
|
||||
// Select a new ship and spawn it.
|
||||
BOOLEAN
|
||||
GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side)
|
||||
{
|
||||
|
||||
@@ -208,7 +208,7 @@ DrawFileString (const MeleeTeam *team, const POINT *origin,
|
||||
}
|
||||
|
||||
// returns true if there are any entries in the view, in which case
|
||||
// pMS->load.bot gets set to the index of the bottom entry in the view.
|
||||
// pMS->load.bot gets set to the index just past the bottom entry in the view.
|
||||
// returns false if not, in which case, the entire view remains unchanged.
|
||||
static bool
|
||||
FillFileView (MELEE_STATE *pMS)
|
||||
@@ -350,7 +350,7 @@ DoLoadTeam (MELEE_STATE *pMS)
|
||||
if (PulsedInputState.menu[KEY_MENU_SELECT])
|
||||
{
|
||||
// Copy the selected fleet to the player.
|
||||
Melee_Change_team (pMS, pMS->side,
|
||||
Melee_LocalChange_team (pMS, pMS->side,
|
||||
pMS->load.view[pMS->load.cur - pMS->load.top]);
|
||||
}
|
||||
|
||||
@@ -417,11 +417,16 @@ DoLoadTeam (MELEE_STATE *pMS)
|
||||
|
||||
if (newIndex != pMS->load.cur)
|
||||
{
|
||||
// The cursor has been moved.
|
||||
LockMutex (GraphicsLock);
|
||||
if (newTop == pMS->load.top)
|
||||
{
|
||||
// The view itself hasn't changed.
|
||||
SelectFileString (pMS, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The view is changed.
|
||||
pMS->load.top = newTop;
|
||||
DrawFileStrings (pMS);
|
||||
}
|
||||
|
||||
+329
-100
@@ -197,6 +197,7 @@ static BOOLEAN DoConfirmSettings (MELEE_STATE *pMS);
|
||||
#define DTSHS_BLOCKCUR 8
|
||||
static BOOLEAN DrawTeamString (MELEE_STATE *pMS, COUNT side,
|
||||
COUNT HiLiteState, const char *str);
|
||||
static void DrawFleetValue (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState);
|
||||
|
||||
static void Melee_UpdateView_fleetValue (MELEE_STATE *pMS, COUNT side);
|
||||
static void Melee_UpdateView_ship (MELEE_STATE *pMS, COUNT side,
|
||||
@@ -363,6 +364,7 @@ DrawTeams (void)
|
||||
}
|
||||
|
||||
DrawTeamString (pMeleeState, side, DTSHS_NORMAL, NULL);
|
||||
DrawFleetValue (pMeleeState, side, DTSHS_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,6 +424,59 @@ RedrawMeleeFrame (void)
|
||||
RepairMeleeFrame (&r);
|
||||
}
|
||||
|
||||
static void
|
||||
GetTeamStringRect (COUNT side, RECT *r)
|
||||
{
|
||||
r->corner.x = MELEE_X_OFFS - 1;
|
||||
r->corner.y = (side + 1) * (MELEE_Y_OFFS
|
||||
+ ((MELEE_BOX_HEIGHT + MELEE_BOX_SPACE) * NUM_MELEE_ROWS + 2));
|
||||
r->extent.width = NUM_MELEE_COLUMNS * (MELEE_BOX_WIDTH + MELEE_BOX_SPACE)
|
||||
- 29;
|
||||
r->extent.height = 13;
|
||||
}
|
||||
|
||||
static void
|
||||
GetFleetValueRect (COUNT side, RECT *r)
|
||||
{
|
||||
r->corner.x = MELEE_X_OFFS
|
||||
+ NUM_MELEE_COLUMNS * (MELEE_BOX_WIDTH + MELEE_BOX_SPACE) - 30;
|
||||
r->corner.y = (side + 1) * (MELEE_Y_OFFS
|
||||
+ ((MELEE_BOX_HEIGHT + MELEE_BOX_SPACE) * NUM_MELEE_ROWS + 2));
|
||||
r->extent.width = 29;
|
||||
r->extent.height = 13;
|
||||
}
|
||||
|
||||
static void
|
||||
DrawFleetValue (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState)
|
||||
{
|
||||
RECT r;
|
||||
TEXT rtText;
|
||||
UNICODE buf[30];
|
||||
COUNT fleetValue;
|
||||
|
||||
GetFleetValueRect (side ,&r);
|
||||
|
||||
if (HiLiteState == DTSHS_REPAIR)
|
||||
{
|
||||
RepairMeleeFrame (&r);
|
||||
return;
|
||||
}
|
||||
|
||||
SetContextFont (MicroFont);
|
||||
|
||||
fleetValue = MeleeSetup_getFleetValue (pMS->meleeSetup, side);
|
||||
sprintf (buf, "%u", fleetValue);
|
||||
rtText.pStr = buf;
|
||||
rtText.align = ALIGN_RIGHT;
|
||||
rtText.CharCount = (COUNT)~0;
|
||||
rtText.baseline.y = r.corner.y + r.extent.height - 3;
|
||||
rtText.baseline.x = r.corner.x + r.extent.width;
|
||||
|
||||
SetContextForeGroundColor (!(HiLiteState & DTSHS_SELECTED)
|
||||
? TEAM_NAME_TEXT_COLOR : TEAM_NAME_EDIT_TEXT_COLOR);
|
||||
font_DrawText (&rtText);
|
||||
}
|
||||
|
||||
// If teamName == NULL, the team name is taken from pMS->meleeSetup
|
||||
static BOOLEAN
|
||||
DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
|
||||
@@ -430,11 +485,7 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
|
||||
RECT r;
|
||||
TEXT lfText;
|
||||
|
||||
r.corner.x = MELEE_X_OFFS - 1;
|
||||
r.corner.y = (side + 1) * (MELEE_Y_OFFS
|
||||
+ ((MELEE_BOX_HEIGHT + MELEE_BOX_SPACE) * NUM_MELEE_ROWS + 2));
|
||||
r.extent.width = NUM_MELEE_COLUMNS * (MELEE_BOX_WIDTH + MELEE_BOX_SPACE);
|
||||
r.extent.height = 13;
|
||||
GetTeamStringRect (side, &r);
|
||||
if (HiLiteState == DTSHS_REPAIR)
|
||||
{
|
||||
RepairMeleeFrame (&r);
|
||||
@@ -446,7 +497,6 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
|
||||
lfText.pStr = (teamName != NULL) ? teamName :
|
||||
MeleeSetup_getTeamName (pMS->meleeSetup, side);
|
||||
lfText.baseline.y = r.corner.y + r.extent.height - 3;
|
||||
|
||||
lfText.baseline.x = r.corner.x + 1;
|
||||
lfText.align = ALIGN_LEFT;
|
||||
lfText.CharCount = strlen (lfText.pStr);
|
||||
@@ -454,20 +504,9 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
|
||||
BatchGraphics ();
|
||||
if (!(HiLiteState & DTSHS_EDIT))
|
||||
{ // normal or selected state
|
||||
TEXT rtText;
|
||||
UNICODE buf[30];
|
||||
|
||||
sprintf (buf, "%u", MeleeSetup_getFleetValue (pMS->meleeSetup, side));
|
||||
rtText.pStr = buf;
|
||||
rtText.align = ALIGN_RIGHT;
|
||||
rtText.CharCount = (COUNT)~0;
|
||||
rtText.baseline.y = lfText.baseline.y;
|
||||
rtText.baseline.x = lfText.baseline.x + r.extent.width - 1;
|
||||
|
||||
SetContextForeGroundColor (!(HiLiteState & DTSHS_SELECTED)
|
||||
? TEAM_NAME_TEXT_COLOR : TEAM_NAME_EDIT_TEXT_COLOR);
|
||||
font_DrawText (&lfText);
|
||||
font_DrawText (&rtText);
|
||||
}
|
||||
else
|
||||
{ // editing state
|
||||
@@ -476,9 +515,6 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
|
||||
BYTE char_deltas[MAX_TEAM_CHARS];
|
||||
BYTE *pchar_deltas;
|
||||
|
||||
// not drawing team bucks
|
||||
r.extent.width -= 29;
|
||||
|
||||
TextRect (&lfText, &text_r, char_deltas);
|
||||
if ((text_r.extent.width + 2) >= r.extent.width)
|
||||
{ // the text does not fit the input box size and so
|
||||
@@ -712,6 +748,8 @@ Deselect (BYTE opt)
|
||||
// Not currently editing the team name.
|
||||
DrawTeamString (pMeleeState, pMeleeState->side,
|
||||
DTSHS_NORMAL, NULL);
|
||||
DrawFleetValue (pMeleeState, pMeleeState->side,
|
||||
DTSHS_NORMAL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -773,6 +811,8 @@ Select (BYTE opt)
|
||||
// Not currently editing the team name.
|
||||
DrawTeamString (pMeleeState, pMeleeState->side,
|
||||
DTSHS_SELECTED, NULL);
|
||||
DrawFleetValue (pMeleeState, pMeleeState->side,
|
||||
DTSHS_SELECTED);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -942,7 +982,7 @@ static void
|
||||
DeleteCurrentShip (MELEE_STATE *pMS)
|
||||
{
|
||||
FleetShipIndex slotI = GetShipIndex (pMS->row, pMS->col);
|
||||
Melee_Change_ship (pMS, pMS->side, slotI, MELEE_NONE);
|
||||
Melee_LocalChange_ship (pMS, pMS->side, slotI, MELEE_NONE);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -992,6 +1032,23 @@ OnTeamNameChange (TEXTENTRY_STATE *pTES)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOLEAN
|
||||
TeamNameFrameCallback (TEXTENTRY_STATE *pTES)
|
||||
{
|
||||
#ifdef NETPLAY
|
||||
// Process incoming packets, so that remote changes are displayed
|
||||
// while we are editing the team name.
|
||||
// The team name itself isn't modified visually due to remote changes
|
||||
// while it is being edited.
|
||||
netInput ();
|
||||
#endif
|
||||
|
||||
(void) pTES;
|
||||
|
||||
return TRUE;
|
||||
// Keep editing
|
||||
}
|
||||
|
||||
static void
|
||||
BuildPickShipPopup (MELEE_STATE *pMS)
|
||||
{
|
||||
@@ -1005,7 +1062,7 @@ BuildPickShipPopup (MELEE_STATE *pMS)
|
||||
// A ship has been selected.
|
||||
// Add the currently selected ship to the fleet.
|
||||
FleetShipIndex index = GetShipIndex (pMS->row, pMS->col);
|
||||
Melee_Change_ship (pMS, pMS->side, index, pMS->currentShip);
|
||||
Melee_LocalChange_ship (pMS, pMS->side, index, pMS->currentShip);
|
||||
AdvanceCursor (pMS);
|
||||
}
|
||||
|
||||
@@ -1122,12 +1179,13 @@ DoEdit (MELEE_STATE *pMS)
|
||||
tes.MaxSize = MAX_TEAM_CHARS + 1;
|
||||
tes.CbParam = pMS;
|
||||
tes.ChangeCallback = OnTeamNameChange;
|
||||
tes.FrameCallback = 0;
|
||||
tes.FrameCallback = TeamNameFrameCallback;
|
||||
DoTextEntry (&tes);
|
||||
|
||||
|
||||
// done entering
|
||||
pMS->CurIndex = MELEE_STATE_INDEX_DONE;
|
||||
if (!Melee_Change_teamName (pMS, pMS->side, buf)) {
|
||||
if (!tes.Success ||
|
||||
!Melee_LocalChange_teamName (pMS, pMS->side, buf)) {
|
||||
// The team name was not changed, so it was not redrawn.
|
||||
// However, because we now leave edit mode, we still
|
||||
// need to redraw.
|
||||
@@ -1253,6 +1311,7 @@ DoConfirmSettings (MELEE_STATE *pMS)
|
||||
|
||||
if (PulsedInputState.menu[KEY_MENU_CANCEL])
|
||||
{
|
||||
// The connection is explicitely cancelled, locally.
|
||||
pMS->InputFunc = DoMelee;
|
||||
#ifdef NETPLAY
|
||||
cancelConfirmations ();
|
||||
@@ -1266,6 +1325,7 @@ DoConfirmSettings (MELEE_STATE *pMS)
|
||||
PulsedInputState.menu[KEY_MENU_UP] ||
|
||||
PulsedInputState.menu[KEY_MENU_DOWN])
|
||||
{
|
||||
// The player moves the cursor; cancel the confirmation.
|
||||
pMS->InputFunc = DoMelee;
|
||||
#ifdef NETPLAY
|
||||
cancelConfirmations ();
|
||||
@@ -1278,7 +1338,9 @@ DoConfirmSettings (MELEE_STATE *pMS)
|
||||
#ifndef NETPLAY
|
||||
pMS->InputFunc = DoMelee;
|
||||
SeedRandomNumbers ();
|
||||
pMS->meleeStarted = TRUE;
|
||||
StartMelee (pMS);
|
||||
pMS->meleeStarted = FALSE;
|
||||
if (GLOBAL (CurrentActivity) & CHECK_ABORT)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
@@ -2041,9 +2103,11 @@ Melee (void)
|
||||
if (LoadMeleeConfig (&MenuState) == -1)
|
||||
{
|
||||
PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
|
||||
Melee_Change_team (&MenuState, 0, MenuState.load.preBuiltList[0]);
|
||||
Melee_LocalChange_team (&MenuState, 0,
|
||||
MenuState.load.preBuiltList[0]);
|
||||
PlayerControl[1] = COMPUTER_CONTROL | STANDARD_RATING;
|
||||
Melee_Change_team (&MenuState, 1, MenuState.load.preBuiltList[1]);
|
||||
Melee_LocalChange_team (&MenuState, 1,
|
||||
MenuState.load.preBuiltList[1]);
|
||||
}
|
||||
|
||||
MenuState.side = 0;
|
||||
@@ -2186,7 +2250,6 @@ resetFeedback (NetConnection *conn, NetplayResetReason reason,
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
GLOBAL (CurrentActivity) |= CHECK_ABORT;
|
||||
flushPacketQueues ();
|
||||
// If the local side queued a reset packet as a result of a
|
||||
// remote reset, that packet will not have been sent yet.
|
||||
@@ -2202,6 +2265,10 @@ resetFeedback (NetConnection *conn, NetplayResetReason reason,
|
||||
msg = resetReasonString (reason);
|
||||
if (msg != NULL)
|
||||
connectionFeedback (conn, msg, false);
|
||||
|
||||
// End supermelee. This must not be done before connectionFeedback(),
|
||||
// otherwise the message will immediately disappear.
|
||||
GLOBAL (CurrentActivity) |= CHECK_ABORT;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2241,15 +2308,24 @@ closeFeedback (NetConnection *conn)
|
||||
static void
|
||||
Melee_UpdateView_fleetValue (MELEE_STATE *pMS, COUNT side)
|
||||
{
|
||||
if (pMS->meleeStarted)
|
||||
return;
|
||||
|
||||
LockMutex (GraphicsLock);
|
||||
DrawTeamString (pMS, side, DTSHS_REPAIR, NULL);
|
||||
DrawFleetValue (pMS, side, DTSHS_REPAIR);
|
||||
// BUG: The fleet value is always drawn as deselected.
|
||||
UnlockMutex (GraphicsLock);
|
||||
}
|
||||
|
||||
static void
|
||||
Melee_UpdateView_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index)
|
||||
{
|
||||
MeleeShip ship = MeleeSetup_getShip (pMS->meleeSetup, side, index);
|
||||
MeleeShip ship;
|
||||
|
||||
if (pMS->meleeStarted)
|
||||
return;
|
||||
|
||||
ship = MeleeSetup_getShip (pMS->meleeSetup, side, index);
|
||||
|
||||
LockMutex (GraphicsLock);
|
||||
if (ship == MELEE_NONE)
|
||||
@@ -2266,6 +2342,9 @@ Melee_UpdateView_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index)
|
||||
static void
|
||||
Melee_UpdateView_teamName (MELEE_STATE *pMS, COUNT side)
|
||||
{
|
||||
if (pMS->meleeStarted)
|
||||
return;
|
||||
|
||||
LockMutex (GraphicsLock);
|
||||
DrawTeamString (pMS, side, DTSHS_REPAIR, NULL);
|
||||
UnlockMutex (GraphicsLock);
|
||||
@@ -2273,10 +2352,11 @@ Melee_UpdateView_teamName (MELEE_STATE *pMS, COUNT side)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Melee_Change_xxx() functions are called when some value in the supermelee
|
||||
// fleet setup screen has changed, eithed locally, or remotely.
|
||||
// Melee_Change_xxx() functions are helper functions, called when some value
|
||||
// in the supermelee fleet setup screen has changed, eithed because of a
|
||||
// local change, or a remote change.
|
||||
|
||||
bool
|
||||
static bool
|
||||
Melee_Change_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
|
||||
MeleeShip ship)
|
||||
{
|
||||
@@ -2300,16 +2380,11 @@ Melee_Change_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
|
||||
UnlockMutex (GraphicsLock);
|
||||
}
|
||||
|
||||
#ifdef NETPLAY
|
||||
// Notify network connections of the change.
|
||||
Netplay_NotifyAll_setShip (pMS, side, index);
|
||||
#endif /* NETPLAY */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pre: 'name' is '\0'-terminated
|
||||
bool
|
||||
static bool
|
||||
Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name)
|
||||
{
|
||||
MeleeSetup *setup = pMS->meleeSetup;
|
||||
@@ -2329,37 +2404,93 @@ Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name)
|
||||
Melee_UpdateView_teamName (pMS, side);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Melee_LocalChange_xxx() functions are called when some value in the
|
||||
// supermelee fleet setup screen has changed because of a local action.
|
||||
// The behavior of these functions (and the comments therein) follow the
|
||||
// description in doc/devel/netplay/protocol.
|
||||
|
||||
bool
|
||||
Melee_LocalChange_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
|
||||
MeleeShip ship)
|
||||
{
|
||||
if (!Melee_Change_ship (pMS, side, index, ship))
|
||||
return false;
|
||||
|
||||
#ifdef NETPLAY
|
||||
Netplay_NotifyAll_setTeamName (pMS, side);
|
||||
{
|
||||
MeleeSetup *setup = pMS->meleeSetup;
|
||||
|
||||
MeleeShip sentShip = MeleeSetup_getSentShip (setup, side, index);
|
||||
if (sentShip == MELEE_UNSET)
|
||||
{
|
||||
// State 1.
|
||||
// Notify network connections of the change.
|
||||
Netplay_NotifyAll_setShip (pMS, side, index);
|
||||
MeleeSetup_setSentShip (setup, side, index, ship);
|
||||
}
|
||||
}
|
||||
#endif /* NETPLAY */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Pre: 'name' is '\0'-terminated
|
||||
bool
|
||||
Melee_LocalChange_teamName (MELEE_STATE *pMS, COUNT side, const char *name)
|
||||
{
|
||||
if (!Melee_Change_teamName (pMS, side, name))
|
||||
return false;
|
||||
|
||||
#ifdef NETPLAY
|
||||
{
|
||||
MeleeSetup *setup = pMS->meleeSetup;
|
||||
|
||||
const char *sentName = MeleeSetup_getSentTeamName (setup, side);
|
||||
if (sentName == NULL)
|
||||
{
|
||||
// State 1.
|
||||
// Notify network connections of the change.
|
||||
Netplay_NotifyAll_setTeamName (pMS, side);
|
||||
MeleeSetup_setSentTeamName (setup, side, name);
|
||||
}
|
||||
}
|
||||
#endif /* NETPLAY */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Melee_Change_fleet (MELEE_STATE *pMS, size_t teamNr, const MeleeShip *fleet)
|
||||
Melee_LocalChange_fleet (MELEE_STATE *pMS, size_t teamNr,
|
||||
const MeleeShip *fleet)
|
||||
{
|
||||
MeleeSetup *setup = pMS->meleeSetup;
|
||||
FleetShipIndex slotI;
|
||||
bool changed = false;
|
||||
|
||||
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++) {
|
||||
if (MeleeSetup_setShip (setup, teamNr, slotI, fleet[slotI]))
|
||||
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
|
||||
{
|
||||
if (Melee_LocalChange_ship (pMS, teamNr, slotI, fleet[slotI]))
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool
|
||||
Melee_Change_team (MELEE_STATE *pMS, size_t teamNr, const MeleeTeam *team)
|
||||
Melee_LocalChange_team (MELEE_STATE *pMS, size_t teamNr,
|
||||
const MeleeTeam *team)
|
||||
{
|
||||
MeleeSetup *setup = pMS->meleeSetup;
|
||||
const MeleeShip *fleet = MeleeTeam_getFleet (team);
|
||||
const char *name = MeleeTeam_getTeamName (team);
|
||||
bool changed = false;
|
||||
|
||||
if (Melee_Change_fleet (pMS, teamNr, fleet))
|
||||
if (Melee_LocalChange_fleet (pMS, teamNr, fleet))
|
||||
changed = true;
|
||||
if (MeleeSetup_setTeamName (setup, teamNr, MeleeTeam_getTeamName (team)))
|
||||
if (Melee_LocalChange_teamName (pMS, teamNr, name))
|
||||
changed = true;
|
||||
|
||||
return changed;
|
||||
@@ -2367,8 +2498,40 @@ Melee_Change_team (MELEE_STATE *pMS, size_t teamNr, const MeleeTeam *team)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Send the entire team to the remote side. Used when the connection has
|
||||
// just been established, or after the setup menu is reentered after battle.
|
||||
void
|
||||
Melee_bootstrapSyncTeam (MELEE_STATE *meleeState, size_t teamNr)
|
||||
{
|
||||
MeleeSetup *setup = meleeState->meleeSetup;
|
||||
FleetShipIndex slotI;
|
||||
const char *teamName;
|
||||
|
||||
// Send the current fleet.
|
||||
Netplay_NotifyAll_setFleet(meleeState, teamNr);
|
||||
|
||||
// Update the last sent fleet.
|
||||
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
|
||||
{
|
||||
MeleeShip ship = MeleeSetup_getShip (setup, teamNr, slotI);
|
||||
assert (MeleeSetup_getSentShip (setup, teamNr, slotI) == MELEE_UNSET);
|
||||
MeleeSetup_setSentShip (setup, teamNr, slotI, ship);
|
||||
}
|
||||
|
||||
// Send the current team name.
|
||||
Netplay_NotifyAll_setTeamName (meleeState, teamNr);
|
||||
|
||||
// Update the last sent team name.
|
||||
teamName = MeleeSetup_getTeamName (setup, teamNr);
|
||||
MeleeSetup_setSentTeamName (setup, teamNr, teamName);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Melee_RemoteChange_xxx() functions are called when some value in the
|
||||
// supermelee fleet setup screen has changed remotely.
|
||||
// The behavior of these functions (and the comments therein) follow the
|
||||
// description in doc/devel/netplay/protocol.
|
||||
|
||||
#ifdef NETPLAY
|
||||
void
|
||||
@@ -2377,73 +2540,139 @@ Melee_RemoteChange_ship (MELEE_STATE *pMS, NetConnection *conn, COUNT side,
|
||||
{
|
||||
MeleeSetup *setup = pMS->meleeSetup;
|
||||
|
||||
MeleeShip currentShip = MeleeSetup_getShip (setup, side, index);
|
||||
MeleeShip sentShip = MeleeSetup_getSentShip (setup, side, index);
|
||||
MeleeShip currentShip;
|
||||
|
||||
if (ship == currentShip)
|
||||
if (sentShip == MELEE_UNSET)
|
||||
{
|
||||
// The remote side has confirmed what we want the value to be.
|
||||
// If we had made the same local change before, then an update
|
||||
// has already been sent. If not, then there is nothing to change.
|
||||
// Either way, we do not need to send an update.
|
||||
MeleeSetup_setConfirmedShip (setup, side, index, ship);
|
||||
} else {
|
||||
// The remote side wants to make a change.
|
||||
// We accept the change when we don't have any changes of our own,
|
||||
// or when we do have changes, but we "lose" the tie break.
|
||||
// If we "win" the tie break, we have already sent an update to the
|
||||
// remote side when our local change was made, so we do not need to
|
||||
// send anything. The remote side still needs to confirm this local
|
||||
// change, of which it will become aware shortly, when the already
|
||||
// sent update message arrives.
|
||||
MeleeShip confirmedShip = MeleeSetup_getConfirmedShip (setup, side,
|
||||
index);
|
||||
if (currentShip == confirmedShip ||
|
||||
NetConnection_getDiscriminant(conn))
|
||||
// State 1
|
||||
|
||||
// Change the ship locally.
|
||||
Melee_Change_ship (pMS, side, index, ship);
|
||||
|
||||
// Notify the remote side.
|
||||
Netplay_NotifyAll_setShip (pMS, side, index);
|
||||
|
||||
// A packet has now been received and sent. End of turn.
|
||||
return;
|
||||
}
|
||||
|
||||
// A packet has been sent and received. End of turn.
|
||||
MeleeSetup_setSentShip (setup, side, index, MELEE_UNSET);
|
||||
|
||||
if (ship != sentShip)
|
||||
{
|
||||
// Rule 2c or 3d. The value which we sent is different from the value
|
||||
// which the opponent sent. We need a tie-breaker to determine which
|
||||
// value prevails.
|
||||
if (NetConnection_getPlayerNr (conn) != side)
|
||||
{
|
||||
MeleeSetup_setConfirmedShip (setup, side, index, ship);
|
||||
Melee_Change_ship (pMS, side, index, ship);
|
||||
// This will also cause a notification of the local
|
||||
// change to be sent to the remote side, which will act
|
||||
// as a confirmation.
|
||||
// Rule 2c+ or 3d+
|
||||
// We win the tie-breaker. The value which we sent prevails.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rule 2c- or 3d-.
|
||||
// We lose the tie-breaker. We adopt the remote value.
|
||||
Melee_Change_ship (pMS, side, index, ship);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
else
|
||||
{
|
||||
// Rule 2b or 3c. The value which we sent is the value which
|
||||
// the opponent sent. This confirms the value.
|
||||
}
|
||||
*/
|
||||
|
||||
// Rule 2b, 2c+, 3c, or 3d+. The value which we sent is confirmed.
|
||||
|
||||
currentShip = MeleeSetup_getShip (setup, side, index);
|
||||
if (currentShip != sentShip)
|
||||
{
|
||||
// Rule 3c or 3d+. We had a local change which was yet
|
||||
// unreported.
|
||||
|
||||
// Notify the remote side and keep track of what we sent.
|
||||
Netplay_NotifyAll_setShip (pMS, side, index);
|
||||
MeleeSetup_setSentShip (setup, side, index, ship);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn, COUNT side,
|
||||
const char *name)
|
||||
Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn,
|
||||
COUNT side, const char *newName)
|
||||
{
|
||||
MeleeSetup *setup = pMS->meleeSetup;
|
||||
|
||||
const char *currentName = MeleeSetup_getTeamName (setup, side);
|
||||
const char *sentName = MeleeSetup_getSentTeamName (setup, side);
|
||||
const char *currentName;
|
||||
|
||||
if (strcmp (name, currentName) == 0)
|
||||
if (sentName == NULL)
|
||||
{
|
||||
// The remote side has confirmed what we want the value to be.
|
||||
// If we had made the same local change before, then an update
|
||||
// has already been sent. If not, then there is nothing to change.
|
||||
// Either way, we do not need to send an update.
|
||||
MeleeSetup_setConfirmedTeamName (setup, side, name);
|
||||
} else {
|
||||
// The remote side wants to make a change.
|
||||
// We accept the change when we don't have any changes of our own,
|
||||
// or when we do have changes, but we "lose" the tie break.
|
||||
// If we "win" the tie break, we have already sent an update to the
|
||||
// remote side when our local change was made, so we do not need to
|
||||
// send anything. The remote side still needs to confirm this local
|
||||
// change, of which it will become aware shortly, when the already
|
||||
// sent update message arrives.
|
||||
const char *confirmedName =
|
||||
MeleeSetup_getConfirmedTeamName (setup, side);
|
||||
if (strcmp (currentName, confirmedName) == 0 ||
|
||||
NetConnection_getDiscriminant(conn))
|
||||
// State 1
|
||||
|
||||
// Change the team name locally.
|
||||
Melee_Change_teamName (pMS, side, newName);
|
||||
|
||||
// Notify the remote side.
|
||||
Netplay_NotifyAll_setTeamName (pMS, side);
|
||||
|
||||
// A packet has now been received and sent. End of turn.
|
||||
// The sent team name is still unset, so we don't have to reset it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp (newName, sentName) == 0)
|
||||
{
|
||||
// Rule 2c or 3d. The value which we sent is different from the value
|
||||
// which the opponent sent. We need a tie-breaker to determine which
|
||||
// value prevails.
|
||||
if (NetConnection_getPlayerNr (conn) != side)
|
||||
{
|
||||
MeleeSetup_setConfirmedTeamName (setup, side, name);
|
||||
Melee_Change_teamName (pMS, side, name);
|
||||
// This will also cause a notification of the local
|
||||
// change to be sent to the remote side, which will act
|
||||
// as a confirmation.
|
||||
// Rule 2c+ or 3d+
|
||||
// We win the tie-breaker. The value which we sent prevails.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rule 2c- or 3d-.
|
||||
// We lose the tie-breaker. We adopt the remote value.
|
||||
Melee_Change_teamName (pMS, side, newName);
|
||||
MeleeSetup_setSentTeamName (setup, side, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
else
|
||||
{
|
||||
// Rule 2b or 3c. The value which we sent is the value which
|
||||
// the opponent sent. This confirms the value.
|
||||
}
|
||||
*/
|
||||
|
||||
// Rule 2b, 2c+, 3c, or 3d+. The value which we sent is confirmed.
|
||||
|
||||
currentName = MeleeSetup_getTeamName (setup, side);
|
||||
if (strcmp (currentName, sentName) != 0)
|
||||
{
|
||||
// Rule 3c or 3d+. We had a local change which was yet
|
||||
// unreported.
|
||||
|
||||
// A packet has been sent and received, which ends the turn.
|
||||
// We don't bother clearing the sent team name, as we're going
|
||||
// to send a new packet immediately.
|
||||
|
||||
// Notify the remote side and keep track of what we sent.
|
||||
Netplay_NotifyAll_setTeamName (pMS, side);
|
||||
|
||||
// Update the last sent message.
|
||||
MeleeSetup_setSentTeamName (setup, side, newName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A packet has been sent and received. End of turn.
|
||||
MeleeSetup_setSentTeamName (setup, side, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ struct melee_state
|
||||
BOOLEAN (*InputFunc) (struct melee_state *pInputState);
|
||||
|
||||
BOOLEAN Initialized;
|
||||
BOOLEAN meleeStarted;
|
||||
MELEE_OPTIONS MeleeOption;
|
||||
COUNT side;
|
||||
COUNT row;
|
||||
@@ -108,14 +109,17 @@ void resetFeedback (NetConnection *conn, NetplayResetReason reason,
|
||||
void errorFeedback (NetConnection *conn);
|
||||
void closeFeedback (NetConnection *conn);
|
||||
|
||||
bool Melee_Change_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
|
||||
MeleeShip ship);
|
||||
bool Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name);
|
||||
bool Melee_Change_fleet (MELEE_STATE *pMS, size_t teamNr,
|
||||
bool Melee_LocalChange_ship (MELEE_STATE *pMS, COUNT side,
|
||||
FleetShipIndex index, MeleeShip ship);
|
||||
bool Melee_LocalChange_teamName (MELEE_STATE *pMS, COUNT side,
|
||||
const char *name);
|
||||
bool Melee_LocalChange_fleet (MELEE_STATE *pMS, size_t teamNr,
|
||||
const MeleeShip *fleet);
|
||||
bool Melee_Change_team (MELEE_STATE *pMS, size_t teamNr,
|
||||
bool Melee_LocalChange_team (MELEE_STATE *pMS, size_t teamNr,
|
||||
const MeleeTeam *team);
|
||||
|
||||
void Melee_bootstrapSyncTeam (MELEE_STATE *pMS, size_t teamNr);
|
||||
|
||||
void Melee_RemoteChange_ship (MELEE_STATE *pMS, NetConnection *conn,
|
||||
COUNT side, FleetShipIndex index, MeleeShip ship);
|
||||
void Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn,
|
||||
|
||||
@@ -34,6 +34,8 @@ MeleeTeam_init (MeleeTeam *team)
|
||||
|
||||
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
|
||||
team->ships[slotI] = MELEE_NONE;
|
||||
|
||||
team->name[0] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
@@ -158,7 +160,7 @@ MeleeTeam_getTeamName (const MeleeTeam *team)
|
||||
|
||||
// Returns true iff the state has actually changed.
|
||||
void
|
||||
MeleeTeam_setName (MeleeTeam *team, const UNICODE *name)
|
||||
MeleeTeam_setName (MeleeTeam *team, const char *name)
|
||||
{
|
||||
strncpy (team->name, name, sizeof team->name - 1);
|
||||
team->name[sizeof team->name - 1] = '\0';
|
||||
@@ -196,6 +198,26 @@ MeleeTeam_isEqual (const MeleeTeam *team1, const MeleeTeam *team2)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef NETPLAY
|
||||
static void
|
||||
MeleeSetup_initSentTeam (MeleeSetup *setup, size_t teamNr)
|
||||
{
|
||||
MeleeTeam *team = &setup->sentTeams[teamNr];
|
||||
FleetShipIndex slotI;
|
||||
|
||||
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
|
||||
MeleeTeam_setShip (team, slotI, MELEE_UNSET);
|
||||
|
||||
setup->haveSentTeamName[teamNr] = false;
|
||||
#ifdef DEBUG
|
||||
// The actual team name should be irrelevant if haveSentTeamName is
|
||||
// set to false. In a debug build, we set it to invalid, so that
|
||||
// it is more likely that it will be noticed if it is ever used.
|
||||
MeleeTeam_setName (team, "<INVALID>");
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
#endif /* NETPLAY */
|
||||
|
||||
MeleeSetup *
|
||||
MeleeSetup_new (void)
|
||||
{
|
||||
@@ -209,7 +231,7 @@ MeleeSetup_new (void)
|
||||
MeleeTeam_init (&result->teams[teamI]);
|
||||
result->fleetValue[teamI] = 0;
|
||||
#ifdef NETPLAY
|
||||
MeleeTeam_init (&result->confirmedTeams[teamI]);
|
||||
MeleeSetup_initSentTeam (result, teamI);
|
||||
#endif /* NETPLAY */
|
||||
}
|
||||
return result;
|
||||
@@ -221,6 +243,17 @@ MeleeSetup_delete (MeleeSetup *setup)
|
||||
HFree (setup);
|
||||
}
|
||||
|
||||
#ifdef NETPLAY
|
||||
void
|
||||
MeleeSetup_resetSentTeams (MeleeSetup *setup)
|
||||
{
|
||||
size_t teamI;
|
||||
|
||||
for (teamI = 0; teamI < NUM_SIDES; teamI++)
|
||||
MeleeSetup_initSentTeam (setup, teamI);
|
||||
}
|
||||
#endif /* NETPLAY */
|
||||
|
||||
// Returns true iff the state has actually changed.
|
||||
bool
|
||||
MeleeSetup_setShip (MeleeSetup *setup, size_t teamNr, FleetShipIndex slotNr,
|
||||
@@ -259,10 +292,10 @@ MeleeSetup_getFleet (const MeleeSetup *setup, size_t teamNr)
|
||||
// Returns true iff the state has actually changed.
|
||||
bool
|
||||
MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
|
||||
const UNICODE *name)
|
||||
const char *name)
|
||||
{
|
||||
MeleeTeam *team = &setup->teams[teamNr];
|
||||
const UNICODE *oldName = MeleeTeam_getTeamName (team);
|
||||
const char *oldName = MeleeTeam_getTeamName (team);
|
||||
|
||||
if (strcmp (oldName, name) == 0)
|
||||
return false;
|
||||
@@ -271,6 +304,8 @@ MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
|
||||
return true;
|
||||
}
|
||||
|
||||
// NB. This function returns a pointer to a static buffer, which is
|
||||
// overwritten by calls to MeleeSetup_setTeamName().
|
||||
const char *
|
||||
MeleeSetup_getTeamName (const MeleeSetup *setup, size_t teamNr)
|
||||
{
|
||||
@@ -301,24 +336,31 @@ MeleeSetup_serializeTeam (const MeleeSetup *setup, size_t teamNr,
|
||||
|
||||
#ifdef NETPLAY
|
||||
MeleeShip
|
||||
MeleeSetup_getConfirmedShip (const MeleeSetup *setup, size_t teamNr,
|
||||
MeleeSetup_getSentShip (const MeleeSetup *setup, size_t teamNr,
|
||||
FleetShipIndex slotNr)
|
||||
{
|
||||
return MeleeTeam_getShip (&setup->confirmedTeams[teamNr], slotNr);
|
||||
return MeleeTeam_getShip (&setup->sentTeams[teamNr], slotNr);
|
||||
}
|
||||
|
||||
// Returns NULL if there is no team name set. This is not the same
|
||||
// as when an empty (zero-length) team name is set.
|
||||
// NB. This function returns a pointer to a static buffer, which is
|
||||
// overwritten by calls to MeleeSetup_setSentTeamName().
|
||||
const char *
|
||||
MeleeSetup_getConfirmedTeamName (const MeleeSetup *setup, size_t teamNr)
|
||||
MeleeSetup_getSentTeamName (const MeleeSetup *setup, size_t teamNr)
|
||||
{
|
||||
return MeleeTeam_getTeamName (&setup->confirmedTeams[teamNr]);
|
||||
if (!setup->haveSentTeamName[teamNr])
|
||||
return NULL;
|
||||
|
||||
return MeleeTeam_getTeamName (&setup->sentTeams[teamNr]);
|
||||
}
|
||||
|
||||
// Returns true iff the state has actually changed.
|
||||
bool
|
||||
MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr,
|
||||
MeleeSetup_setSentShip (MeleeSetup *setup, size_t teamNr,
|
||||
FleetShipIndex slotNr, MeleeShip ship)
|
||||
{
|
||||
MeleeTeam *team = &setup->confirmedTeams[teamNr];
|
||||
MeleeTeam *team = &setup->sentTeams[teamNr];
|
||||
MeleeShip oldShip = MeleeTeam_getShip (team, slotNr);
|
||||
|
||||
if (ship == oldShip)
|
||||
@@ -329,28 +371,62 @@ MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr,
|
||||
}
|
||||
|
||||
// Returns true iff the state has actually changed.
|
||||
// 'name' can be NULL to indicate that no team name set. This is not the same
|
||||
// as when an empty (zero-length) team name is set.
|
||||
bool
|
||||
MeleeSetup_setConfirmedTeamName (MeleeSetup *setup, size_t teamNr,
|
||||
const UNICODE *name)
|
||||
MeleeSetup_setSentTeamName (MeleeSetup *setup, size_t teamNr,
|
||||
const char *name)
|
||||
{
|
||||
MeleeTeam *team = &setup->confirmedTeams[teamNr];
|
||||
const UNICODE *oldName = MeleeTeam_getTeamName (team);
|
||||
bool haveSentName = setup->haveSentTeamName[teamNr];
|
||||
|
||||
if (strcmp (oldName, name) == 0)
|
||||
return false;
|
||||
if (name == NULL)
|
||||
{
|
||||
if (!haveSentName)
|
||||
{
|
||||
// Had not sent a team name, and still haven't.
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
// The actual team name should be irrelevant if haveSentTeamName
|
||||
// is set to false. In a debug build, we set it to invalid, so
|
||||
// that it is more likely that it will be noticed if it is ever
|
||||
// used.
|
||||
MeleeTeam *team = &setup->sentTeams[teamNr];
|
||||
MeleeTeam_setName (team, "<INVALID>");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MeleeTeam *team;
|
||||
|
||||
if (haveSentName)
|
||||
{
|
||||
// Have sent a team name. Check whether it has actually changed.
|
||||
const char *oldName = MeleeTeam_getTeamName (team);
|
||||
if (strcmp (oldName, name) == 0)
|
||||
return false; // Team name has not changed.
|
||||
}
|
||||
|
||||
team = &setup->sentTeams[teamNr];
|
||||
MeleeTeam_setName (team, name);
|
||||
}
|
||||
|
||||
setup->haveSentTeamName[teamNr] = (name != NULL);
|
||||
|
||||
MeleeTeam_setName (team, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool
|
||||
MeleeSetup_isTeamConfirmed (MeleeSetup *setup, size_t teamNr)
|
||||
MeleeSetup_isTeamSent (MeleeSetup *setup, size_t teamNr)
|
||||
{
|
||||
MeleeTeam *localTeam = &setup->teams[teamNr];
|
||||
MeleeTeam *confirmedTeam = &setup->confirmedTeams[teamNr];
|
||||
MeleeTeam *sentTeam = &setup->sentTeams[teamNr];
|
||||
|
||||
return MeleeTeam_isEqual (localTeam, confirmedTeam);
|
||||
return MeleeTeam_isEqual (localTeam, sentTeam);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -36,47 +36,29 @@ typedef COUNT FleetShipIndex;
|
||||
struct MeleeTeam
|
||||
{
|
||||
MeleeShip ships[MELEE_FLEET_SIZE];
|
||||
UNICODE name[MAX_TEAM_CHARS + 1 + 24];
|
||||
char name[MAX_TEAM_CHARS + 1 + 24];
|
||||
/* The +1 is for the terminating \0; the +24 is in case some
|
||||
* default name in starcon.txt is unknowingly mangled. */
|
||||
// XXX: SvdB: Why would it be mangled? Why don't we just reject
|
||||
// it if it is? Is this so that we have some space
|
||||
// for multibyte UTF-8 chars?
|
||||
};
|
||||
#endif /* MELEETEAM_INTERNAL */
|
||||
|
||||
#ifdef MELEESETUP_INTERNAL
|
||||
// When Netplay is enabled, we keep two copies of the teams: the team as we
|
||||
// consider it to be locally, and last confirmed team.
|
||||
// There are no separate change and confirmation messages; a confirmation
|
||||
// message is a message reporting a change to the value which was specified
|
||||
// by the remote side.
|
||||
// When a local change is made, the current team is updated, and a message
|
||||
// is sent to the other side.
|
||||
// When we receive a message of a remote update:
|
||||
// - if the current value is the same as the received value, we change
|
||||
// the confirmed value to the current/received value
|
||||
// - if the current value is different from the received value, and
|
||||
// the current value is the same as the confirmed value, we change both
|
||||
// the current value and the confirmed value to the received value,
|
||||
// and sent a message to the remote side of our modification.
|
||||
// - if the current value is different from the received value, and
|
||||
// the current value is not the same as the confirmed value, our
|
||||
// action depends on who "owns" the value:
|
||||
// - if the change is to the remote fleet, we change the current and
|
||||
// confirmed value to the received value, and send a message to the
|
||||
// remote side of our modification (as above)
|
||||
// - if the change is to the local fleet, we take no action.
|
||||
// (In this situation, a local change has been made, and a message of
|
||||
// this has already been sent to the other side, and a confirmation
|
||||
// from the other side will arrive eventually.)
|
||||
// XXX: put this in docs/netplay/protocols.
|
||||
// XXX: this not only works for teams, but also for other properties.
|
||||
struct MeleeSetup
|
||||
{
|
||||
MeleeTeam teams[NUM_SIDES];
|
||||
COUNT fleetValue[NUM_SIDES];
|
||||
#ifdef NETPLAY
|
||||
MeleeTeam confirmedTeams[NUM_SIDES];
|
||||
// The least team which both sides agreed upon.
|
||||
MeleeTeam sentTeams[NUM_SIDES];
|
||||
// The last sent (parts of) teams.
|
||||
// Used in the Update protocol. See doc/devel/netplay/protocol
|
||||
// XXX: this may actually be deallocated when the battle starts.
|
||||
bool haveSentTeamName[NUM_SIDES];
|
||||
// Whether we have sent a team name this 'turn'.
|
||||
// Used in the Update protocol. See doc/devel/netplay/protocol
|
||||
// (also for the term 'turn').
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -88,6 +70,9 @@ void MeleeTeam_init (MeleeTeam *team);
|
||||
void MeleeTeam_uninit (MeleeTeam *team);
|
||||
MeleeTeam *MeleeTeam_new (void);
|
||||
void MeleeTeam_delete (MeleeTeam *team);
|
||||
#ifdef NETPLAY
|
||||
void MeleeSetup_resetSentTeams (MeleeSetup *setup);
|
||||
#endif /* NETPLAY */
|
||||
int MeleeTeam_serialize (const MeleeTeam *team, uio_Stream *stream);
|
||||
int MeleeTeam_deserialize (MeleeTeam *team, uio_Stream *stream);
|
||||
COUNT MeleeTeam_getValue (const MeleeTeam *team);
|
||||
@@ -96,23 +81,23 @@ void MeleeTeam_setShip (MeleeTeam *team, FleetShipIndex slotNr,
|
||||
MeleeShip ship);
|
||||
const MeleeShip *MeleeTeam_getFleet (const MeleeTeam *team);
|
||||
const char *MeleeTeam_getTeamName (const MeleeTeam *team);
|
||||
void MeleeTeam_setName (MeleeTeam *team, const UNICODE *name);
|
||||
void MeleeTeam_setName (MeleeTeam *team, const char *name);
|
||||
void MeleeTeam_copy (MeleeTeam *copy, const MeleeTeam *original);
|
||||
#if 0
|
||||
bool MeleeTeam_isEqual (const MeleeTeam *team1, const MeleeTeam *team2);
|
||||
#endif
|
||||
|
||||
#ifdef NETPLAY
|
||||
MeleeShip MeleeSetup_getConfirmedShip (const MeleeSetup *setup, size_t teamNr,
|
||||
MeleeShip MeleeSetup_getSentShip (const MeleeSetup *setup, size_t teamNr,
|
||||
FleetShipIndex slotNr);
|
||||
const char *MeleeSetup_getConfirmedTeamName (const MeleeSetup *setup,
|
||||
const char *MeleeSetup_getSentTeamName (const MeleeSetup *setup,
|
||||
size_t teamNr);
|
||||
bool MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr,
|
||||
bool MeleeSetup_setSentShip (MeleeSetup *setup, size_t teamNr,
|
||||
FleetShipIndex slotNr, MeleeShip ship);
|
||||
bool MeleeSetup_setConfirmedTeamName (MeleeSetup *setup, size_t teamNr,
|
||||
const UNICODE *name);
|
||||
bool MeleeSetup_setSentTeamName (MeleeSetup *setup, size_t teamNr,
|
||||
const char *name);
|
||||
#if 0
|
||||
bool MeleeSetup_isTeamConfirmed (MeleeSetup *setup, size_t teamNr);
|
||||
bool MeleeSetup_isTeamSent (MeleeSetup *setup, size_t teamNr);
|
||||
#endif
|
||||
#endif /* NETPLAY */
|
||||
|
||||
@@ -127,7 +112,7 @@ bool MeleeSetup_setFleet (MeleeSetup *setup, size_t teamNr,
|
||||
const MeleeShip *fleet);
|
||||
const MeleeShip *MeleeSetup_getFleet (const MeleeSetup *setup, size_t teamNr);
|
||||
bool MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
|
||||
const UNICODE *name);
|
||||
const char *name);
|
||||
const char *MeleeSetup_getTeamName (const MeleeSetup *setup,
|
||||
size_t teamNr);
|
||||
COUNT MeleeSetup_getFleetValue (const MeleeSetup *setup, size_t teamNr);
|
||||
@@ -142,7 +127,7 @@ void MeleeState_setShip (MELEE_STATE *pMS, size_t teamNr,
|
||||
void MeleeState_setFleet (MELEE_STATE *pMS, size_t teamNr,
|
||||
const MeleeShip *fleet);
|
||||
void MeleeState_setTeamName (MELEE_STATE *pMS, size_t teamNr,
|
||||
const UNICODE *name);
|
||||
const char *name);
|
||||
void MeleeState_setTeam (MELEE_STATE *pMS, size_t teamNr,
|
||||
const MeleeTeam *team);
|
||||
|
||||
|
||||
@@ -30,7 +30,10 @@ typedef enum MeleeShip {
|
||||
MELEE_YEHAT,
|
||||
MELEE_ZOQFOTPIK,
|
||||
|
||||
MELEE_UNSET = ((BYTE) ~0) - 1,
|
||||
// Used with the Update protocol, to register in the sentTeam
|
||||
MELEE_NONE = (BYTE) ~0
|
||||
// Empty fleet position.
|
||||
} MeleeShip;
|
||||
#define NUM_MELEE_SHIPS (MELEE_ZOQFOTPIK + 1)
|
||||
|
||||
|
||||
@@ -181,8 +181,6 @@ NetConnection_connectedServerCallback(ListenState *listenState,
|
||||
// Ignore errors; it's not a big deal. In debug mode, a message
|
||||
// will already have been printed from the function itself.
|
||||
|
||||
conn->stateFlags.myTurn = true;
|
||||
// The "server" may speak first.
|
||||
conn->stateFlags.discriminant = true;
|
||||
|
||||
NetConnection_connected(conn);
|
||||
@@ -210,8 +208,6 @@ NetConnection_connectedClientCallback(ConnectState *connectState,
|
||||
// Ignore errors; it's not a big deal. In debug mode, a message
|
||||
// will already have been printed from the function itself.
|
||||
|
||||
conn->stateFlags.myTurn = false;
|
||||
// The "server" may speak first.
|
||||
conn->stateFlags.discriminant = false;
|
||||
|
||||
NetConnection_connected(conn);
|
||||
|
||||
@@ -129,9 +129,6 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
|
||||
conn->stateData = NULL;
|
||||
conn->stateFlags.connected = false;
|
||||
conn->stateFlags.disconnected = false;
|
||||
conn->stateFlags.myTurn = false;
|
||||
conn->stateFlags.endingTurn = false;
|
||||
conn->stateFlags.pendingTurnChange = false;
|
||||
conn->stateFlags.discriminant = false;
|
||||
conn->stateFlags.handshake.localOk = false;
|
||||
conn->stateFlags.handshake.remoteOk = false;
|
||||
@@ -350,11 +347,6 @@ NetConnection_isConnected(const NetConnection *conn) {
|
||||
return conn->stateFlags.connected;
|
||||
}
|
||||
|
||||
bool
|
||||
NetConnection_isMyTurn(const NetConnection *conn) {
|
||||
return conn->stateFlags.myTurn;
|
||||
}
|
||||
|
||||
int
|
||||
NetConnection_getPlayerNr(const NetConnection *conn) {
|
||||
return conn->player;
|
||||
|
||||
@@ -104,20 +104,11 @@ typedef struct {
|
||||
/* This NetConnection has been disconnected. This implies
|
||||
* !connected. It is only set if the NetConnection was once
|
||||
* connected, but is no longer. */
|
||||
bool myTurn;
|
||||
/* This party is the only one who may send specific packets
|
||||
* at the moment. */
|
||||
bool endingTurn;
|
||||
/* Request to change the turn from/to the other party. */
|
||||
bool pendingTurnChange;
|
||||
/* It is our turn and the other party wants our turn,
|
||||
* but we're not ready to give it up just yet; we will first
|
||||
* send our pending packets. */
|
||||
bool discriminant;
|
||||
/* If it is true here, it is false on the remote side
|
||||
* of the same connection. It may be used to break ties.
|
||||
* Unlike myTurn, this one is guaranteed not to change
|
||||
* during a connection. Undefined while not connected. */
|
||||
* It is guaranteed not to change during a connection. Undefined
|
||||
* while not connected. */
|
||||
HandShakeFlags handshake;
|
||||
ReadyFlags ready;
|
||||
ResetFlags reset;
|
||||
@@ -204,7 +195,6 @@ bool NetConnection_isConnected(const NetConnection *conn);
|
||||
|
||||
void NetConnection_doErrorCallback(NetConnection *nd, int err);
|
||||
|
||||
bool NetConnection_isMyTurn(const NetConnection *conn);
|
||||
void NetConnection_setStateData(NetConnection *conn,
|
||||
NetConnectionStateData *stateData);
|
||||
NetConnectionStateData *NetConnection_getStateData(const NetConnection *conn);
|
||||
|
||||
@@ -84,6 +84,9 @@ NetMelee_connectCallback(NetConnection *conn) {
|
||||
NetConnection_setStateData(conn, (void *) battleStateData);
|
||||
NetConnection_setExtra(conn, NULL);
|
||||
|
||||
// We have sent no teams yet. Initialize the state accordingly.
|
||||
MeleeSetup_resetSentTeams (meleeState->meleeSetup);
|
||||
|
||||
sendInit(conn);
|
||||
Netplay_localReady (conn, NetMelee_enterState_inSetup, NULL, false);
|
||||
}
|
||||
@@ -119,43 +122,13 @@ NetMelee_enterState_inSetup(NetConnection *conn, void *arg) {
|
||||
|
||||
connectedFeedback(conn);
|
||||
|
||||
Netplay_NotifyAll_setFleet(meleeState, player);
|
||||
Netplay_NotifyAll_setTeamName(meleeState, player);
|
||||
// Send our team to the remote side.
|
||||
// XXX This only works with 2 players atm.
|
||||
assert (NUM_PLAYERS == 2);
|
||||
Melee_bootstrapSyncTeam (meleeState, player);
|
||||
|
||||
flushPacketQueues();
|
||||
|
||||
(void) arg;
|
||||
}
|
||||
|
||||
// Callback function for when both sides have confirmed that the battle
|
||||
// has ended.
|
||||
void
|
||||
NetMelee_reenterState_inSetup(NetConnection *conn) {
|
||||
BattleStateData *battleStateData;
|
||||
struct melee_state *meleeState;
|
||||
|
||||
NetConnection_setState(conn, NetState_inSetup);
|
||||
|
||||
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
|
||||
meleeState = battleStateData->meleeState;
|
||||
|
||||
// The player who entered the menu first should send his changes over
|
||||
// to the other side when the other player enters setup too.
|
||||
// As myTurn is set when a change is made, and will never be unset
|
||||
// until the other side makes a change, it can be used to determine
|
||||
// which side was first. In the case there were no changes made, myTurn
|
||||
// may be the second player, but in this case it doesn't matter
|
||||
// who sends what to whom.
|
||||
if (NetConnection_isMyTurn(conn))
|
||||
{
|
||||
size_t side;
|
||||
|
||||
for (side = 0; side < NUM_SIDES; side++)
|
||||
{
|
||||
Netplay_NotifyAll_setFleet(meleeState, side);
|
||||
Netplay_NotifyAll_setTeamName(meleeState, side);
|
||||
}
|
||||
flushPacketQueues();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,8 +62,7 @@ readyFlagsMeaningful(NetState state) {
|
||||
state == NetState_interBattle ||
|
||||
state == NetState_inBattle ||
|
||||
state == NetState_endingBattle ||
|
||||
state == NetState_endingBattle2 ||
|
||||
state == NetState_endMelee;
|
||||
state == NetState_endingBattle2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,15 +25,15 @@
|
||||
#define NETPLAY_FULL 2
|
||||
|
||||
#define NETPLAY_PROTOCOL_VERSION_MAJOR 0
|
||||
#define NETPLAY_PROTOCOL_VERSION_MINOR 3
|
||||
#define NETPLAY_PROTOCOL_VERSION_MINOR 4
|
||||
|
||||
#define NETPLAY_MIN_UQM_VERSION_MAJOR 0
|
||||
#define NETPLAY_MIN_UQM_VERSION_MINOR 5
|
||||
#define NETPLAY_MIN_UQM_VERSION_PATCH 4
|
||||
#define NETPLAY_MIN_UQM_VERSION_MINOR 6
|
||||
#define NETPLAY_MIN_UQM_VERSION_PATCH 9
|
||||
|
||||
#define NETPLAY_DEBUG
|
||||
#undef NETPLAY_DEBUG
|
||||
/* Extra debugging for netplay */
|
||||
#define NETPLAY_DEBUG_FILE
|
||||
#undef NETPLAY_DEBUG_FILE
|
||||
/* Dump extra debugging information to file.
|
||||
* Implies NETPLAY_DEBUG.*/
|
||||
#define NETPLAY_STATISTICS
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "netplay.h"
|
||||
#include "port.h"
|
||||
|
||||
#include "netsend.h"
|
||||
#include "netconnection.h"
|
||||
#include "packet.h"
|
||||
#include "libs/log.h"
|
||||
|
||||
@@ -38,7 +38,6 @@ NetStateData netStateData[] = {
|
||||
DEFINE_NETSTATEDATA(inBattle),
|
||||
DEFINE_NETSTATEDATA(endingBattle),
|
||||
DEFINE_NETSTATEDATA(endingBattle2),
|
||||
DEFINE_NETSTATEDATA(endMelee),
|
||||
};
|
||||
|
||||
void
|
||||
|
||||
@@ -36,7 +36,6 @@ typedef enum {
|
||||
NetState_inBattle, /* Battle has started */
|
||||
NetState_endingBattle, /* Both sides are prepared to end */
|
||||
NetState_endingBattle2, /* Waiting for the final synchronisation */
|
||||
NetState_endMelee, /* Melee ended; remote is not yet in setup */
|
||||
} NetState;
|
||||
|
||||
#include "types.h"
|
||||
|
||||
@@ -29,33 +29,31 @@
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define DEFINE_PACKETDATA(name, inTurn) \
|
||||
#define DEFINE_PACKETDATA(name) \
|
||||
{ \
|
||||
/* .len = */ sizeof (Packet_##name), \
|
||||
/* .handler = */ (PacketHandler) PacketHandler_##name, \
|
||||
/* .name = */ #name, \
|
||||
/* .inTurn = */ (inTurn) \
|
||||
}
|
||||
PacketTypeData packetTypeData[PACKET_NUM] = {
|
||||
DEFINE_PACKETDATA(Init, false),
|
||||
DEFINE_PACKETDATA(Ping, false),
|
||||
DEFINE_PACKETDATA(Ack, false),
|
||||
DEFINE_PACKETDATA(EndTurn, false),
|
||||
DEFINE_PACKETDATA(Ready, false),
|
||||
DEFINE_PACKETDATA(Fleet, true),
|
||||
DEFINE_PACKETDATA(TeamName, true),
|
||||
DEFINE_PACKETDATA(Handshake0, false),
|
||||
DEFINE_PACKETDATA(Handshake1, false),
|
||||
DEFINE_PACKETDATA(HandshakeCancel, false),
|
||||
DEFINE_PACKETDATA(HandshakeCancelAck, false),
|
||||
DEFINE_PACKETDATA(SeedRandom, false),
|
||||
DEFINE_PACKETDATA(InputDelay, false),
|
||||
DEFINE_PACKETDATA(SelectShip, false),
|
||||
DEFINE_PACKETDATA(BattleInput, false),
|
||||
DEFINE_PACKETDATA(FrameCount, false),
|
||||
DEFINE_PACKETDATA(Checksum, false),
|
||||
DEFINE_PACKETDATA(Abort, false),
|
||||
DEFINE_PACKETDATA(Reset, false),
|
||||
DEFINE_PACKETDATA(Init),
|
||||
DEFINE_PACKETDATA(Ping),
|
||||
DEFINE_PACKETDATA(Ack),
|
||||
DEFINE_PACKETDATA(Ready),
|
||||
DEFINE_PACKETDATA(Fleet),
|
||||
DEFINE_PACKETDATA(TeamName),
|
||||
DEFINE_PACKETDATA(Handshake0),
|
||||
DEFINE_PACKETDATA(Handshake1),
|
||||
DEFINE_PACKETDATA(HandshakeCancel),
|
||||
DEFINE_PACKETDATA(HandshakeCancelAck),
|
||||
DEFINE_PACKETDATA(SeedRandom),
|
||||
DEFINE_PACKETDATA(InputDelay),
|
||||
DEFINE_PACKETDATA(SelectShip),
|
||||
DEFINE_PACKETDATA(BattleInput),
|
||||
DEFINE_PACKETDATA(FrameCount),
|
||||
DEFINE_PACKETDATA(Checksum),
|
||||
DEFINE_PACKETDATA(Abort),
|
||||
DEFINE_PACKETDATA(Reset),
|
||||
};
|
||||
|
||||
static inline void *
|
||||
@@ -113,14 +111,6 @@ Packet_Ack_create(uint32 id) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
Packet_EndTurn *
|
||||
Packet_EndTurn_create(void) {
|
||||
Packet_EndTurn *packet =
|
||||
(Packet_EndTurn *) Packet_create(PACKET_ENDTURN, 0);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
Packet_Ready *
|
||||
Packet_Ready_create(void) {
|
||||
Packet_Ready *packet = (Packet_Ready *) Packet_create(PACKET_READY, 0);
|
||||
|
||||
@@ -25,7 +25,6 @@ typedef enum PacketType {
|
||||
PACKET_INIT,
|
||||
PACKET_PING,
|
||||
PACKET_ACK,
|
||||
PACKET_ENDTURN,
|
||||
PACKET_READY,
|
||||
PACKET_FLEET,
|
||||
PACKET_TEAMNAME,
|
||||
@@ -79,7 +78,6 @@ typedef struct {
|
||||
size_t len; /* Minimal length of a packet of this type */
|
||||
PacketHandler handler;
|
||||
const char *name;
|
||||
bool inTurn; /* Can only be sent if it's this party's turn. */
|
||||
} PacketTypeData;
|
||||
|
||||
extern PacketTypeData packetTypeData[];
|
||||
@@ -142,12 +140,6 @@ typedef struct {
|
||||
uint32 id;
|
||||
} Packet_Ack;
|
||||
|
||||
// Used to ask or confirm a turn change.
|
||||
typedef struct {
|
||||
PacketHeader header;
|
||||
// No contents.
|
||||
} Packet_EndTurn;
|
||||
|
||||
// Used to signal that a party is ready to continue.
|
||||
typedef struct {
|
||||
PacketHeader header;
|
||||
@@ -262,7 +254,6 @@ void Packet_delete(Packet *packet);
|
||||
Packet_Init *Packet_Init_create(void);
|
||||
Packet_Ping *Packet_Ping_create(uint32 id);
|
||||
Packet_Ack *Packet_Ack_create(uint32 id);
|
||||
Packet_EndTurn *Packet_EndTurn_create(void);
|
||||
Packet_Ready *Packet_Ready_create(void);
|
||||
Packet_Handshake0 *Packet_Handshake0_create(void);
|
||||
Packet_Handshake1 *Packet_Handshake1_create(void);
|
||||
|
||||
@@ -142,41 +142,6 @@ PacketHandler_Ack(NetConnection *conn, const Packet_Ack *packet) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
PacketHandler_EndTurn(NetConnection *conn, const Packet_EndTurn *packet) {
|
||||
if (!testNetState(conn->state > NetState_init &&
|
||||
!conn->stateFlags.pendingTurnChange, PACKET_ENDTURN))
|
||||
return -1; // errno is set
|
||||
|
||||
if (conn->stateFlags.endingTurn) {
|
||||
// This was the confirmation we were waiting for.
|
||||
// NB. A remote request while we had sent a request serves as
|
||||
// a confirmation.
|
||||
conn->stateFlags.myTurn = !conn->stateFlags.myTurn;
|
||||
conn->stateFlags.endingTurn = false;
|
||||
} else {
|
||||
// The other party wants to change whose turn it is.
|
||||
// If it would become the other party's turn, we wait until
|
||||
// the queue is flushed to actually carry out the turn change,
|
||||
// and send the confirmation. I we wouldn't do that, and we still
|
||||
// had some data to send in our own turn, we would end up asking
|
||||
// for our turn back, and the turn would keep changing without
|
||||
// any progress being made.
|
||||
if (conn->stateFlags.myTurn) {
|
||||
// Schedule the turn change until after the next queue flush.
|
||||
conn->stateFlags.pendingTurnChange = true;
|
||||
} else {
|
||||
conn->stateFlags.myTurn = true;
|
||||
sendEndTurn(conn);
|
||||
}
|
||||
}
|
||||
|
||||
(void) packet;
|
||||
// Its contents is not interesting.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert the side indication relative to a remote party to
|
||||
// a local player number.
|
||||
static inline int
|
||||
@@ -189,17 +154,6 @@ localSide(NetConnection *conn, NetplaySide side) {
|
||||
return 1 - conn->player;
|
||||
}
|
||||
|
||||
static bool
|
||||
checkYourTurn(NetConnection *conn, PacketType type) {
|
||||
if (conn->stateFlags.myTurn) {
|
||||
log_add(log_Warning, "Packet of type '%s' received in an "
|
||||
"inappropriate turn.", packetTypeData[type].name);
|
||||
errno = EBADMSG;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) {
|
||||
if (conn->stateFlags.reset.localReset)
|
||||
@@ -239,11 +193,6 @@ PacketHandler_Fleet(NetConnection *conn, const Packet_Fleet *packet) {
|
||||
if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
|
||||
return -1; // errno is set
|
||||
|
||||
if (!checkYourTurn(conn, PACKET_FLEET)) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
|
||||
player = localSide(conn, (NetplaySide) packet->side);
|
||||
|
||||
len = packetLength((const Packet *) packet);
|
||||
@@ -304,14 +253,9 @@ PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!testNetState(conn->state == NetState_inSetup, PACKET_TEAMNAME))
|
||||
if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
|
||||
return -1; // errno is set
|
||||
|
||||
if (!checkYourTurn(conn, PACKET_TEAMNAME)) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
|
||||
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
|
||||
|
||||
if (conn->stateFlags.handshake.localOk) {
|
||||
@@ -503,8 +447,7 @@ PacketHandler_InputDelay(NetConnection *conn,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!testNetState(conn->state == NetState_preBattle,
|
||||
PACKET_INPUTDELAY))
|
||||
if (!testNetState(conn->state == NetState_preBattle, PACKET_INPUTDELAY))
|
||||
return -1; // errno is set
|
||||
|
||||
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
DECLARE_PACKETHANDLER(Init);
|
||||
DECLARE_PACKETHANDLER(Ping);
|
||||
DECLARE_PACKETHANDLER(Ack);
|
||||
DECLARE_PACKETHANDLER(EndTurn);
|
||||
DECLARE_PACKETHANDLER(Ready);
|
||||
DECLARE_PACKETHANDLER(Fleet);
|
||||
DECLARE_PACKETHANDLER(TeamName);
|
||||
|
||||
@@ -47,9 +47,6 @@ PacketQueue_init(PacketQueue *queue) {
|
||||
queue->size = 0;
|
||||
queue->first = NULL;
|
||||
queue->end = &queue->first;
|
||||
|
||||
queue->firstUrgent = NULL;
|
||||
queue->endUrgent = &queue->first;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -64,31 +61,23 @@ PacketQueue_deleteLinks(PacketQueueLink *link) {
|
||||
|
||||
void
|
||||
PacketQueue_uninit(PacketQueue *queue) {
|
||||
PacketQueue_deleteLinks(queue->firstUrgent);
|
||||
PacketQueue_deleteLinks(queue->first);
|
||||
}
|
||||
|
||||
void
|
||||
queuePacket(NetConnection *conn, Packet *packet, bool urgent) {
|
||||
queuePacket(NetConnection *conn, Packet *packet) {
|
||||
PacketQueue *queue;
|
||||
PacketQueueLink *link;
|
||||
|
||||
assert(NetConnection_isConnected(conn));
|
||||
assert(!urgent || !packetTypeData[packetType(packet)].inTurn);
|
||||
// Urgent packets should never stall the connection.
|
||||
|
||||
queue = &conn->queue;
|
||||
|
||||
link = PacketQueueLink_alloc();
|
||||
link->packet = packet;
|
||||
link->next = NULL;
|
||||
if (urgent) {
|
||||
*queue->endUrgent = link;
|
||||
queue->endUrgent = &link->next;
|
||||
} else {
|
||||
*queue->end = link;
|
||||
queue->end = &link->next;
|
||||
}
|
||||
*queue->end = link;
|
||||
queue->end = &link->next;
|
||||
|
||||
queue->size++;
|
||||
// XXX: perhaps check that this queue isn't getting too large?
|
||||
@@ -124,29 +113,6 @@ flushPacketQueueLinks(NetConnection *conn, PacketQueueLink **first) {
|
||||
PacketQueue *queue = &conn->queue;
|
||||
|
||||
for (link = *first; link != NULL; link = next) {
|
||||
if (packetTypeData[packetType(link->packet)].inTurn &&
|
||||
(!conn->stateFlags.myTurn || conn->stateFlags.endingTurn)) {
|
||||
// This packet requires it to be 'our turn', and it isn't,
|
||||
// or we've already told the other party we wanted to end our
|
||||
// turn.
|
||||
// This should never happen in the urgent queue.
|
||||
assert(first != &queue->firstUrgent);
|
||||
|
||||
if (!conn->stateFlags.myTurn && !conn->stateFlags.endingTurn) {
|
||||
conn->stateFlags.endingTurn = true;
|
||||
if (sendEndTurnDirect(conn) == -1) {
|
||||
// errno is set
|
||||
*first = link;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*first = link;
|
||||
errno = EAGAIN;
|
||||
// We need to wait for the reply to the turn change.
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sendPacket(conn, link->packet) == -1) {
|
||||
// Errno is set.
|
||||
*first = link;
|
||||
@@ -170,14 +136,6 @@ flushPacketQueue(NetConnection *conn) {
|
||||
|
||||
assert(NetConnection_isConnected(conn));
|
||||
|
||||
flushResult = flushPacketQueueLinks(conn, &queue->firstUrgent);
|
||||
if (queue->firstUrgent == NULL)
|
||||
queue->endUrgent = &queue->firstUrgent;
|
||||
if (flushResult == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
|
||||
flushResult = flushPacketQueueLinks(conn, &queue->first);
|
||||
if (queue->first == NULL)
|
||||
queue->end = &queue->first;
|
||||
@@ -186,23 +144,6 @@ flushPacketQueue(NetConnection *conn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If a turn change had been requested by the other side while it was
|
||||
// our turn, we first sent everything we still had to send in our turn.
|
||||
// Now that is done, it is the time to actually give up the turn.
|
||||
if (conn->stateFlags.pendingTurnChange) {
|
||||
assert(conn->stateFlags.myTurn);
|
||||
conn->stateFlags.myTurn = false;
|
||||
conn->stateFlags.pendingTurnChange = false;
|
||||
|
||||
// Send the confirmation to the other side:
|
||||
if (sendEndTurnDirect(conn) == -1) {
|
||||
// errno is set
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,31 +36,14 @@ struct PacketQueue {
|
||||
size_t size;
|
||||
PacketQueueLink *first;
|
||||
PacketQueueLink **end;
|
||||
|
||||
PacketQueueLink *firstUrgent;
|
||||
PacketQueueLink **endUrgent;
|
||||
|
||||
// first points to the first entry in the queue
|
||||
// end points to the location where the next non-urgent message should
|
||||
// be inserted.
|
||||
// 'firstUrgent' and 'endUrgent' are analogous to 'first' and 'end'.
|
||||
|
||||
// Urgent packets should only be used for packets that may not
|
||||
// be delayed while we wait for some remote confirmation.
|
||||
// As such, these messages themselves should not require a state change
|
||||
// which needs to be remotely confirmed.
|
||||
// For example, the endTurn message can be sent when we want to change
|
||||
// which party is allowed to transmit specific packets. That message
|
||||
// should not be delayed by messages which require that *we* are that
|
||||
// party, as without the endTurn message, it will never become our turn.
|
||||
// Also, ping and ack packets should give some indication of the round
|
||||
// trip time, which they can only do if they aren't delayed by other
|
||||
// packets.
|
||||
// end points to the location where the next message should be inserted.
|
||||
};
|
||||
|
||||
void PacketQueue_init(PacketQueue *queue);
|
||||
void PacketQueue_uninit(PacketQueue *queue);
|
||||
void queuePacket(NetConnection *conn, Packet *packet, bool urgent);
|
||||
void queuePacket(NetConnection *conn, Packet *packet);
|
||||
int flushPacketQueue(NetConnection *conn);
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ sendInit(NetConnection *conn) {
|
||||
Packet_Init *packet;
|
||||
|
||||
packet = Packet_Init_create();
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -37,7 +37,7 @@ sendPing(NetConnection *conn, uint32 id) {
|
||||
Packet_Ping *packet;
|
||||
|
||||
packet = Packet_Ping_create(id);
|
||||
queuePacket(conn, (Packet *) packet, true);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -45,15 +45,7 @@ sendAck(NetConnection *conn, uint32 id) {
|
||||
Packet_Ack *packet;
|
||||
|
||||
packet = Packet_Ack_create(id);
|
||||
queuePacket(conn, (Packet *) packet, true);
|
||||
}
|
||||
|
||||
void
|
||||
sendEndTurn(NetConnection *conn) {
|
||||
Packet_EndTurn *packet;
|
||||
|
||||
packet = Packet_EndTurn_create();
|
||||
queuePacket(conn, (Packet *) packet, true);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -61,17 +53,7 @@ sendReady(NetConnection *conn) {
|
||||
Packet_Ready *packet;
|
||||
|
||||
packet = Packet_Ready_create();
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
}
|
||||
|
||||
// Bypass the packet queue.
|
||||
// Should only be called from the packet queue functions themselves.
|
||||
int
|
||||
sendEndTurnDirect(NetConnection *conn) {
|
||||
Packet_EndTurn *packet;
|
||||
|
||||
packet = Packet_EndTurn_create();
|
||||
return sendPacket(conn, (Packet *) packet);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -79,7 +61,7 @@ sendHandshake0(NetConnection *conn) {
|
||||
Packet_Handshake0 *packet;
|
||||
|
||||
packet = Packet_Handshake0_create();
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -87,7 +69,7 @@ sendHandshake1(NetConnection *conn) {
|
||||
Packet_Handshake1 *packet;
|
||||
|
||||
packet = Packet_Handshake1_create();
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -95,7 +77,7 @@ sendHandshakeCancel(NetConnection *conn) {
|
||||
Packet_HandshakeCancel *packet;
|
||||
|
||||
packet = Packet_HandshakeCancel_create();
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -103,7 +85,7 @@ sendHandshakeCancelAck(NetConnection *conn) {
|
||||
Packet_HandshakeCancelAck *packet;
|
||||
|
||||
packet = Packet_HandshakeCancelAck_create();
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -112,7 +94,7 @@ sendTeamName(NetConnection *conn, NetplaySide side, const char *name,
|
||||
Packet_TeamName *packet;
|
||||
|
||||
packet = Packet_TeamName_create(side, name, len);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -128,7 +110,7 @@ sendFleet(NetConnection *conn, NetplaySide side, const MeleeShip *ships,
|
||||
packet->ships[i].ship = (uint8) ships[i];
|
||||
}
|
||||
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -141,7 +123,7 @@ sendFleetShip(NetConnection *conn, NetplaySide side,
|
||||
packet->ships[0].index = (uint8) shipIndex;
|
||||
packet->ships[0].ship = (uint8) ship;
|
||||
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -149,7 +131,7 @@ sendSeedRandom(NetConnection *conn, uint32 seed) {
|
||||
Packet_SeedRandom *packet;
|
||||
|
||||
packet = Packet_SeedRandom_create(seed);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -157,7 +139,7 @@ sendInputDelay(NetConnection *conn, uint32 delay) {
|
||||
Packet_InputDelay *packet;
|
||||
|
||||
packet = Packet_InputDelay_create(delay);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -165,7 +147,7 @@ sendSelectShip(NetConnection *conn, FleetShipIndex index) {
|
||||
Packet_SelectShip *packet;
|
||||
|
||||
packet = Packet_SelectShip_create((uint16) index);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -173,7 +155,7 @@ sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
|
||||
Packet_BattleInput *packet;
|
||||
|
||||
packet = Packet_BattleInput_create((uint8) input);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -181,7 +163,7 @@ sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount) {
|
||||
Packet_FrameCount *packet;
|
||||
|
||||
packet = Packet_FrameCount_create((uint32) frameCount);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
#ifdef NETPLAY_CHECKSUM
|
||||
@@ -191,7 +173,7 @@ sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
|
||||
Packet_Checksum *packet;
|
||||
|
||||
packet = Packet_Checksum_create((uint32) frameNr, (uint32) checksum);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -200,7 +182,7 @@ sendAbort(NetConnection *conn, NetplayAbortReason reason) {
|
||||
Packet_Abort *packet;
|
||||
|
||||
packet = Packet_Abort_create((uint16) reason);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -208,7 +190,7 @@ sendReset(NetConnection *conn, NetplayResetReason reason) {
|
||||
Packet_Reset *packet;
|
||||
|
||||
packet = Packet_Reset_create((uint16) reason);
|
||||
queuePacket(conn, (Packet *) packet, false);
|
||||
queuePacket(conn, (Packet *) packet);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,9 +34,7 @@
|
||||
void sendInit(NetConnection *conn);
|
||||
void sendPing(NetConnection *conn, uint32 id);
|
||||
void sendAck(NetConnection *conn, uint32 id);
|
||||
void sendEndTurn(NetConnection *conn);
|
||||
void sendReady(NetConnection *conn);
|
||||
int sendEndTurnDirect(NetConnection *conn);
|
||||
void sendHandshake0(NetConnection *conn);
|
||||
void sendHandshake1(NetConnection *conn);
|
||||
void sendHandshakeCancel(NetConnection *conn);
|
||||
|
||||
@@ -82,7 +82,6 @@ Netplay_connectionReset(NetConnection *conn, NetplayResetReason reason,
|
||||
case NetState_inBattle:
|
||||
case NetState_endingBattle:
|
||||
case NetState_endingBattle2:
|
||||
case NetState_endMelee:
|
||||
resetFeedback(conn, reason, byRemote);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -384,15 +384,6 @@ aborted:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef NETPLAY
|
||||
static void
|
||||
endMeleeCallback (NetConnection *conn, void *arg)
|
||||
{
|
||||
NetMelee_reenterState_inSetup (conn);
|
||||
(void) arg;
|
||||
}
|
||||
#endif
|
||||
|
||||
static COUNT
|
||||
GetRaceQueueValue (const QUEUE *queue) {
|
||||
COUNT result;
|
||||
@@ -661,9 +652,14 @@ MeleeGameOver (void)
|
||||
// Show the battle result.
|
||||
for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
|
||||
DrawPickMeleeFrame (playerI);
|
||||
|
||||
UnlockMutex (GraphicsLock);
|
||||
|
||||
#ifdef NETPLAY
|
||||
negotiateReadyConnections(true, NetState_inSetup);
|
||||
#endif
|
||||
|
||||
TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
|
||||
UnlockMutex (GraphicsLock);
|
||||
|
||||
PressState = PulsedInputState.menu[KEY_MENU_SELECT] ||
|
||||
PulsedInputState.menu[KEY_MENU_CANCEL];
|
||||
@@ -683,11 +679,6 @@ MeleeGameOver (void)
|
||||
&& (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL)
|
||||
|| GetTimeCounter () < TimeOut)));
|
||||
|
||||
#ifdef NETPLAY
|
||||
setStateConnections (NetState_endMelee);
|
||||
localReadyConnections (endMeleeCallback, NULL, true);
|
||||
#endif
|
||||
|
||||
LockMutex (GraphicsLock);
|
||||
}
|
||||
|
||||
@@ -898,6 +889,7 @@ GetInitialMeleeStarShips (HSTARSHIP *result)
|
||||
return GetMeleeStarShips (playerMask, result);
|
||||
}
|
||||
|
||||
// Get the next ship to use in SuperMelee.
|
||||
BOOLEAN
|
||||
GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result)
|
||||
{
|
||||
|
||||
@@ -121,7 +121,7 @@ readyToEndCallback (NetConnection *conn, void *arg)
|
||||
* 3. After a player has both sent and received a frame count, the
|
||||
* simulation continues for each party, until the maximum of both
|
||||
* frame counts has been achieved.
|
||||
* 4. The Ready protocol is used to let each side signal that the it has
|
||||
* 4. The Ready protocol is used to let each side signal that it has
|
||||
* reached the target frame count.
|
||||
* 5. The battle ends.
|
||||
*/
|
||||
@@ -132,7 +132,7 @@ readyForBattleEndPlayer (NetConnection *conn)
|
||||
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
|
||||
|
||||
if (NetConnection_getState (conn) == NetState_interBattle ||
|
||||
NetConnection_getState (conn) == NetState_endMelee)
|
||||
NetConnection_getState (conn) == NetState_inSetup)
|
||||
{
|
||||
// This connection is already ready. The entire synchronisation
|
||||
// protocol has already been done for this connection.
|
||||
@@ -161,7 +161,7 @@ readyForBattleEndPlayer (NetConnection *conn)
|
||||
|
||||
// Keep the simulation going as long as the target frame count
|
||||
// hasn't been reached yet. Note that if the connection state is
|
||||
// NetState_endingBattle, that we haven't yet received the
|
||||
// NetState_endingBattle, then we haven't yet received the
|
||||
// remote frame count, so the target frame count may still rise.
|
||||
if (battleFrameCount < battleStateData->endFrameCount)
|
||||
return false;
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#define UQM_MAJOR_VERSION_S "0"
|
||||
#define UQM_MINOR_VERSION 6
|
||||
#define UQM_MINOR_VERSION_S "6"
|
||||
#define UQM_PATCH_VERSION 8
|
||||
#define UQM_PATCH_VERSION_S "8"
|
||||
#define UQM_PATCH_VERSION 9
|
||||
#define UQM_PATCH_VERSION_S "9"
|
||||
#define UQM_EXTRA_VERSION ""
|
||||
/* The final version is interpreted as:
|
||||
* printf ("%d.%d.%d%s", UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
|
||||
|
||||
Reference in New Issue
Block a user