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

Bumped the UQM and netplay protocol versions.
Some cleanups.



git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3537 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
Meep-Eep
2010-04-24 20:59:07 +00:00
parent 91fde6f5e7
commit e4886e60ee
32 changed files with 835 additions and 535 deletions
+1
View File
@@ -1,4 +1,5 @@
Changes towards version 0.7: Changes towards version 0.7:
- Fixed the network SuperMelee team configuration protocol - SvdB
- Fixed fuel reserve bounds checks - SvdB - Fixed fuel reserve bounds checks - SvdB
- Fixed a crash when filling fuel tanks over 10 (bug #1082) - Alex - Fixed a crash when filling fuel tanks over 10 (bug #1082) - Alex
- Got rid of many warnings - SvdB - Got rid of many warnings - SvdB
+138 -33
View File
@@ -5,7 +5,7 @@ of a network connection.
This is used when both parties need to sending information to the This is used when both parties need to sending information to the
other side, but what each party is doing does not interfere with other side, but what each party is doing does not interfere with
what the other party is doing. 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. This is used when the parties have changes to make to common data.
- Mutual agreement on an action ("Confirm") - Mutual agreement on an action ("Confirm")
This is used to end a state where both parties are modifying 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 During configuration, both sides may change the same properties. So that the
important that both sides don't just send changes at once. two sides don't have to take turns, the changes are made locally, and then
To handle this, only one party may send these packets at any moment. the changes are synchronised.
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.
States: To this end, each side has a state containing two copies of each property:
0. myTurn - I may speak 1. The value of the property as it is locally
myTurn && !endTurn 2. The last value which it sent to the other side (until it is no longer
1. endMyTurn - I've given up speaking, waiting for confirmation relevant for the protocol)
myTurn && endTurn
2. yourTurn - You may speak
!myTurn && !endTurn
3. endYourTurn - I want to speak, waiting for confirmation
!myTurn && endTurn
Messages: The basic idea of the Update protocol is:
- ENDTURN - "this party ready to change turns" - 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): The protocol:
local decision -> Send ENDTURN, goto 1
received ENDTURN -> Send ENDTURN, goto 2
From state 1 (endMyTurn): From state 1, {own=x0, sent=--}:
received ENDTURN -> Send ENDTURN, goto 2 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): From state 2, {own=x0, sent=x0}:
local decision -> Send ENDTURN, goto 3 2a Local change x1 -> state=3:{own=x1,sent=x0}
received ENDTURN -> Send ENDTURN, goto 0 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): From state 3, {own=x1, sent=x0}:
received ENDTURN -> Send ENDTURN, goto 0 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: 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 - CONFIRM2 - "acknowledging your CONFIRM1; my own configuration is unchanged
since I sent CONFIRM1 (after the last CANCEL)" since I sent CONFIRM1 (after the last CANCEL)"
- CANCEL - "forget about my earlier CONFIRM1" - CANCEL - "forget about my earlier CONFIRM1"
@@ -148,7 +253,7 @@ From state 1: (localOk)
local cancel -> Send CANCEL, goto 4 local cancel -> Send CANCEL, goto 4
received CONFIRM1 -> Send CONFIRM2, goto 3 received CONFIRM1 -> Send CONFIRM2, goto 3
received CONFIRM2 -> Send CONFIRM2, goto 8 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) From state 2: (remoteOk)
local confirmation -> Send CONFIRM2, goto 3 local confirmation -> Send CONFIRM2, goto 3
@@ -204,7 +309,7 @@ aren't expected.
"Reset" negotiation. "Reset" negotiation.
See src/sc2code/netplay/proto.c See src/sc2code/netplay/proto/reset.c
============================================================================ ============================================================================
+141 -26
View File
@@ -1,63 +1,178 @@
NetState_unconnected is the initial state. When a connection attempt is made, == Any connected state ==
the state is set to NetState_connecting. Some packets may be sent and received in any state except
The state field of a NetConnection is NULL. 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. Whenever a connection is aborted, the state is returned to
When the connection is established, an INIT packet is sent, the state NetState_unconnected. This state transition is not listed below at each
is changed to NetState_init and InputFunc is set to DoNetworkInit. individual state.
The state field of a NetConnection is a ConnectStateData structure.
== 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 NetState_init is for initialising the connection before actual game
information is sent. When an INIT packet is received, the state is set information is sent.
to NetState_inSetup and InputFunc is set to DoMelee.
The state field of a NetConnection is a BattleStateData structure.
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. 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. This does not necessarilly mean that the fleet setup screen is visible;
The Turn negotiation is used to change myTurn. 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 The Confirm negotiation is used to end this state and go to
NetState_preBattle. At this time InputFunc is set to DoPreMelee. 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. NetState_preBattle is used for non-interactive battle negotiations.
One side sends the random seed; the other receives it. 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 The Ready negotiation is used to end this state and go to
NetState_interBattle. 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. initialisations before moving on.
The Ready negotiation is used to end this state and go to The Ready negotiation is used to end this state and go to
NetState_selectShip, or if there are no more ships to be selected, NetState_selectShip, or if all sides have selected a ship, to
to NetState_inBattle. NetState_inBattle, or if there are no more ships in a fleet,
The state field of a NetConnection is a BattleStateData structure. 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. side is waiting for notice of this selection.
As soon as the selection has been sent or received, the state is changed As soon as the selection has been sent or received, the state is changed
back to NetState_interBattle. 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. NetState_inBattle is where the actual melee takes place.
Both sides send their input until the game is over, at which point 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 Ready negotiation is used to end this state and go to
the NetState_endingBattle state. Until the Ready negotiation has been the NetState_endingBattle state. Until the Ready negotiation has been
completed, the simulation is continuing. 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 NetState_endingBattle is where the local side waits for the remote
battle frame count, after it has sent its own. When it arrives, battle frame count, after it has sent its own. When it arrives,
the state changes to NetState_endingBattle2. 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 NetState_endingBattle2 is where the side with the lowest battle frame count
catches up with the other other side, while the other side waits. catches up with the other other side, while the other side waits.
The Ready negotiation is used to signal that each side is ready, The Ready negotiation is used to signal that each side is ready,
and the state changes back to NetState_interBattle. and the state changes to NetState_interBattle.
The state field of a NetConnection is a BattleStateData structure.
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
View File
@@ -302,17 +302,29 @@ SetContext (OldContext);
return (hBattleShip); 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 HSTARSHIP
GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player) GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player)
{ {
HSTARSHIP hBattleShip; HSTARSHIP hBattleShip;
if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE) if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE)
{
// Get the next ship from the battle group. // Get the next ship from the battle group.
hBattleShip = GetHeadLink (&race_q[which_player]); hBattleShip = GetHeadLink (&race_q[which_player]);
}
else if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) 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)) if (!(GLOBAL (CurrentActivity) & IN_BATTLE))
{ {
@@ -367,10 +379,7 @@ GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player)
QUEUE *pQueue; QUEUE *pQueue;
HSHIPFRAG hNextShip; HSHIPFRAG hNextShip;
if (which_player == 0) pQueue = GetShipQueueForSide (which_player);
pQueue = &GLOBAL (built_ship_q);
else
pQueue = &GLOBAL (npc_built_ship_q);
hBattleShip = GetHeadLink (&race_q[which_player]); hBattleShip = GetHeadLink (&race_q[which_player]);
for (hStarShip = GetHeadLink (pQueue); for (hStarShip = GetHeadLink (pQueue);
+1
View File
@@ -493,6 +493,7 @@ spawn_ship (STARSHIP *StarShipPtr)
return (hShip != 0); return (hShip != 0);
} }
// Select a new ship and spawn it.
BOOLEAN BOOLEAN
GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side) GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side)
{ {
+7 -2
View File
@@ -208,7 +208,7 @@ DrawFileString (const MeleeTeam *team, const POINT *origin,
} }
// returns true if there are any entries in the view, in which case // 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. // returns false if not, in which case, the entire view remains unchanged.
static bool static bool
FillFileView (MELEE_STATE *pMS) FillFileView (MELEE_STATE *pMS)
@@ -350,7 +350,7 @@ DoLoadTeam (MELEE_STATE *pMS)
if (PulsedInputState.menu[KEY_MENU_SELECT]) if (PulsedInputState.menu[KEY_MENU_SELECT])
{ {
// Copy the selected fleet to the player. // 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]); pMS->load.view[pMS->load.cur - pMS->load.top]);
} }
@@ -417,11 +417,16 @@ DoLoadTeam (MELEE_STATE *pMS)
if (newIndex != pMS->load.cur) if (newIndex != pMS->load.cur)
{ {
// The cursor has been moved.
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
if (newTop == pMS->load.top) if (newTop == pMS->load.top)
{
// The view itself hasn't changed.
SelectFileString (pMS, false); SelectFileString (pMS, false);
}
else else
{ {
// The view is changed.
pMS->load.top = newTop; pMS->load.top = newTop;
DrawFileStrings (pMS); DrawFileStrings (pMS);
} }
+328 -99
View File
@@ -197,6 +197,7 @@ static BOOLEAN DoConfirmSettings (MELEE_STATE *pMS);
#define DTSHS_BLOCKCUR 8 #define DTSHS_BLOCKCUR 8
static BOOLEAN DrawTeamString (MELEE_STATE *pMS, COUNT side, static BOOLEAN DrawTeamString (MELEE_STATE *pMS, COUNT side,
COUNT HiLiteState, const char *str); 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_fleetValue (MELEE_STATE *pMS, COUNT side);
static void Melee_UpdateView_ship (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); DrawTeamString (pMeleeState, side, DTSHS_NORMAL, NULL);
DrawFleetValue (pMeleeState, side, DTSHS_NORMAL);
} }
} }
@@ -422,6 +424,59 @@ RedrawMeleeFrame (void)
RepairMeleeFrame (&r); 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 // If teamName == NULL, the team name is taken from pMS->meleeSetup
static BOOLEAN static BOOLEAN
DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState, DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
@@ -430,11 +485,7 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
RECT r; RECT r;
TEXT lfText; TEXT lfText;
r.corner.x = MELEE_X_OFFS - 1; GetTeamStringRect (side, &r);
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;
if (HiLiteState == DTSHS_REPAIR) if (HiLiteState == DTSHS_REPAIR)
{ {
RepairMeleeFrame (&r); RepairMeleeFrame (&r);
@@ -446,7 +497,6 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
lfText.pStr = (teamName != NULL) ? teamName : lfText.pStr = (teamName != NULL) ? teamName :
MeleeSetup_getTeamName (pMS->meleeSetup, side); MeleeSetup_getTeamName (pMS->meleeSetup, side);
lfText.baseline.y = r.corner.y + r.extent.height - 3; lfText.baseline.y = r.corner.y + r.extent.height - 3;
lfText.baseline.x = r.corner.x + 1; lfText.baseline.x = r.corner.x + 1;
lfText.align = ALIGN_LEFT; lfText.align = ALIGN_LEFT;
lfText.CharCount = strlen (lfText.pStr); lfText.CharCount = strlen (lfText.pStr);
@@ -454,20 +504,9 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
BatchGraphics (); BatchGraphics ();
if (!(HiLiteState & DTSHS_EDIT)) if (!(HiLiteState & DTSHS_EDIT))
{ // normal or selected state { // 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) SetContextForeGroundColor (!(HiLiteState & DTSHS_SELECTED)
? TEAM_NAME_TEXT_COLOR : TEAM_NAME_EDIT_TEXT_COLOR); ? TEAM_NAME_TEXT_COLOR : TEAM_NAME_EDIT_TEXT_COLOR);
font_DrawText (&lfText); font_DrawText (&lfText);
font_DrawText (&rtText);
} }
else else
{ // editing state { // editing state
@@ -476,9 +515,6 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
BYTE char_deltas[MAX_TEAM_CHARS]; BYTE char_deltas[MAX_TEAM_CHARS];
BYTE *pchar_deltas; BYTE *pchar_deltas;
// not drawing team bucks
r.extent.width -= 29;
TextRect (&lfText, &text_r, char_deltas); TextRect (&lfText, &text_r, char_deltas);
if ((text_r.extent.width + 2) >= r.extent.width) if ((text_r.extent.width + 2) >= r.extent.width)
{ // the text does not fit the input box size and so { // the text does not fit the input box size and so
@@ -712,6 +748,8 @@ Deselect (BYTE opt)
// Not currently editing the team name. // Not currently editing the team name.
DrawTeamString (pMeleeState, pMeleeState->side, DrawTeamString (pMeleeState, pMeleeState->side,
DTSHS_NORMAL, NULL); DTSHS_NORMAL, NULL);
DrawFleetValue (pMeleeState, pMeleeState->side,
DTSHS_NORMAL);
} }
} }
break; break;
@@ -773,6 +811,8 @@ Select (BYTE opt)
// Not currently editing the team name. // Not currently editing the team name.
DrawTeamString (pMeleeState, pMeleeState->side, DrawTeamString (pMeleeState, pMeleeState->side,
DTSHS_SELECTED, NULL); DTSHS_SELECTED, NULL);
DrawFleetValue (pMeleeState, pMeleeState->side,
DTSHS_SELECTED);
} }
} }
break; break;
@@ -942,7 +982,7 @@ static void
DeleteCurrentShip (MELEE_STATE *pMS) DeleteCurrentShip (MELEE_STATE *pMS)
{ {
FleetShipIndex slotI = GetShipIndex (pMS->row, pMS->col); 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 static bool
@@ -992,6 +1032,23 @@ OnTeamNameChange (TEXTENTRY_STATE *pTES)
return ret; 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 static void
BuildPickShipPopup (MELEE_STATE *pMS) BuildPickShipPopup (MELEE_STATE *pMS)
{ {
@@ -1005,7 +1062,7 @@ BuildPickShipPopup (MELEE_STATE *pMS)
// A ship has been selected. // A ship has been selected.
// Add the currently selected ship to the fleet. // Add the currently selected ship to the fleet.
FleetShipIndex index = GetShipIndex (pMS->row, pMS->col); 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); AdvanceCursor (pMS);
} }
@@ -1122,12 +1179,13 @@ DoEdit (MELEE_STATE *pMS)
tes.MaxSize = MAX_TEAM_CHARS + 1; tes.MaxSize = MAX_TEAM_CHARS + 1;
tes.CbParam = pMS; tes.CbParam = pMS;
tes.ChangeCallback = OnTeamNameChange; tes.ChangeCallback = OnTeamNameChange;
tes.FrameCallback = 0; tes.FrameCallback = TeamNameFrameCallback;
DoTextEntry (&tes); DoTextEntry (&tes);
// done entering // done entering
pMS->CurIndex = MELEE_STATE_INDEX_DONE; 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. // The team name was not changed, so it was not redrawn.
// However, because we now leave edit mode, we still // However, because we now leave edit mode, we still
// need to redraw. // need to redraw.
@@ -1253,6 +1311,7 @@ DoConfirmSettings (MELEE_STATE *pMS)
if (PulsedInputState.menu[KEY_MENU_CANCEL]) if (PulsedInputState.menu[KEY_MENU_CANCEL])
{ {
// The connection is explicitely cancelled, locally.
pMS->InputFunc = DoMelee; pMS->InputFunc = DoMelee;
#ifdef NETPLAY #ifdef NETPLAY
cancelConfirmations (); cancelConfirmations ();
@@ -1266,6 +1325,7 @@ DoConfirmSettings (MELEE_STATE *pMS)
PulsedInputState.menu[KEY_MENU_UP] || PulsedInputState.menu[KEY_MENU_UP] ||
PulsedInputState.menu[KEY_MENU_DOWN]) PulsedInputState.menu[KEY_MENU_DOWN])
{ {
// The player moves the cursor; cancel the confirmation.
pMS->InputFunc = DoMelee; pMS->InputFunc = DoMelee;
#ifdef NETPLAY #ifdef NETPLAY
cancelConfirmations (); cancelConfirmations ();
@@ -1278,7 +1338,9 @@ DoConfirmSettings (MELEE_STATE *pMS)
#ifndef NETPLAY #ifndef NETPLAY
pMS->InputFunc = DoMelee; pMS->InputFunc = DoMelee;
SeedRandomNumbers (); SeedRandomNumbers ();
pMS->meleeStarted = TRUE;
StartMelee (pMS); StartMelee (pMS);
pMS->meleeStarted = FALSE;
if (GLOBAL (CurrentActivity) & CHECK_ABORT) if (GLOBAL (CurrentActivity) & CHECK_ABORT)
return FALSE; return FALSE;
return TRUE; return TRUE;
@@ -2041,9 +2103,11 @@ Melee (void)
if (LoadMeleeConfig (&MenuState) == -1) if (LoadMeleeConfig (&MenuState) == -1)
{ {
PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING; 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; 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; MenuState.side = 0;
@@ -2186,7 +2250,6 @@ resetFeedback (NetConnection *conn, NetplayResetReason reason,
{ {
const char *msg; const char *msg;
GLOBAL (CurrentActivity) |= CHECK_ABORT;
flushPacketQueues (); flushPacketQueues ();
// If the local side queued a reset packet as a result of a // If the local side queued a reset packet as a result of a
// remote reset, that packet will not have been sent yet. // remote reset, that packet will not have been sent yet.
@@ -2202,6 +2265,10 @@ resetFeedback (NetConnection *conn, NetplayResetReason reason,
msg = resetReasonString (reason); msg = resetReasonString (reason);
if (msg != NULL) if (msg != NULL)
connectionFeedback (conn, msg, false); connectionFeedback (conn, msg, false);
// End supermelee. This must not be done before connectionFeedback(),
// otherwise the message will immediately disappear.
GLOBAL (CurrentActivity) |= CHECK_ABORT;
} }
void void
@@ -2241,15 +2308,24 @@ closeFeedback (NetConnection *conn)
static void static void
Melee_UpdateView_fleetValue (MELEE_STATE *pMS, COUNT side) Melee_UpdateView_fleetValue (MELEE_STATE *pMS, COUNT side)
{ {
if (pMS->meleeStarted)
return;
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
DrawTeamString (pMS, side, DTSHS_REPAIR, NULL); DrawFleetValue (pMS, side, DTSHS_REPAIR);
// BUG: The fleet value is always drawn as deselected.
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
} }
static void static void
Melee_UpdateView_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index) 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); LockMutex (GraphicsLock);
if (ship == MELEE_NONE) if (ship == MELEE_NONE)
@@ -2266,6 +2342,9 @@ Melee_UpdateView_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index)
static void static void
Melee_UpdateView_teamName (MELEE_STATE *pMS, COUNT side) Melee_UpdateView_teamName (MELEE_STATE *pMS, COUNT side)
{ {
if (pMS->meleeStarted)
return;
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
DrawTeamString (pMS, side, DTSHS_REPAIR, NULL); DrawTeamString (pMS, side, DTSHS_REPAIR, NULL);
UnlockMutex (GraphicsLock); 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 // Melee_Change_xxx() functions are helper functions, called when some value
// fleet setup screen has changed, eithed locally, or remotely. // 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, Melee_Change_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
MeleeShip ship) MeleeShip ship)
{ {
@@ -2300,16 +2380,11 @@ Melee_Change_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
} }
#ifdef NETPLAY
// Notify network connections of the change.
Netplay_NotifyAll_setShip (pMS, side, index);
#endif /* NETPLAY */
return true; return true;
} }
// Pre: 'name' is '\0'-terminated // Pre: 'name' is '\0'-terminated
bool static bool
Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name) Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name)
{ {
MeleeSetup *setup = pMS->meleeSetup; MeleeSetup *setup = pMS->meleeSetup;
@@ -2329,37 +2404,93 @@ Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name)
Melee_UpdateView_teamName (pMS, side); 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 #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 */ #endif /* NETPLAY */
return true; return true;
} }
bool 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; FleetShipIndex slotI;
bool changed = false; bool changed = false;
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++) { for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
if (MeleeSetup_setShip (setup, teamNr, slotI, fleet[slotI])) {
if (Melee_LocalChange_ship (pMS, teamNr, slotI, fleet[slotI]))
changed = true; changed = true;
} }
return changed; return changed;
} }
bool 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 MeleeShip *fleet = MeleeTeam_getFleet (team);
const char *name = MeleeTeam_getTeamName (team);
bool changed = false; bool changed = false;
if (Melee_Change_fleet (pMS, teamNr, fleet)) if (Melee_LocalChange_fleet (pMS, teamNr, fleet))
changed = true; changed = true;
if (MeleeSetup_setTeamName (setup, teamNr, MeleeTeam_getTeamName (team))) if (Melee_LocalChange_teamName (pMS, teamNr, name))
changed = true; changed = true;
return changed; 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 // Melee_RemoteChange_xxx() functions are called when some value in the
// supermelee fleet setup screen has changed remotely. // 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 #ifdef NETPLAY
void void
@@ -2377,73 +2540,139 @@ Melee_RemoteChange_ship (MELEE_STATE *pMS, NetConnection *conn, COUNT side,
{ {
MeleeSetup *setup = pMS->meleeSetup; 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. // State 1
// If we had made the same local change before, then an update
// has already been sent. If not, then there is nothing to change. // Change the ship locally.
// Either way, we do not need to send an update. Melee_Change_ship (pMS, side, index, ship);
MeleeSetup_setConfirmedShip (setup, side, index, ship);
} else { // Notify the remote side.
// The remote side wants to make a change. Netplay_NotifyAll_setShip (pMS, side, index);
// 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. // A packet has now been received and sent. End of turn.
// If we "win" the tie break, we have already sent an update to the return;
// 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 // A packet has been sent and received. End of turn.
// sent update message arrives. MeleeSetup_setSentShip (setup, side, index, MELEE_UNSET);
MeleeShip confirmedShip = MeleeSetup_getConfirmedShip (setup, side,
index); if (ship != sentShip)
if (currentShip == confirmedShip || {
NetConnection_getDiscriminant(conn)) // 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); // Rule 2c+ or 3d+
Melee_Change_ship (pMS, side, index, ship); // We win the tie-breaker. The value which we sent prevails.
// This will also cause a notification of the local
// change to be sent to the remote side, which will act
// as a confirmation.
} }
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 void
Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn, COUNT side, Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn,
const char *name) COUNT side, const char *newName)
{ {
MeleeSetup *setup = pMS->meleeSetup; 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. // State 1
// If we had made the same local change before, then an update
// has already been sent. If not, then there is nothing to change. // Change the team name locally.
// Either way, we do not need to send an update. Melee_Change_teamName (pMS, side, newName);
MeleeSetup_setConfirmedTeamName (setup, side, name);
} else { // Notify the remote side.
// The remote side wants to make a change. Netplay_NotifyAll_setTeamName (pMS, side);
// 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. // A packet has now been received and sent. End of turn.
// If we "win" the tie break, we have already sent an update to the // The sent team name is still unset, so we don't have to reset it.
// remote side when our local change was made, so we do not need to return;
// 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. if (strcmp (newName, sentName) == 0)
const char *confirmedName = {
MeleeSetup_getConfirmedTeamName (setup, side); // Rule 2c or 3d. The value which we sent is different from the value
if (strcmp (currentName, confirmedName) == 0 || // which the opponent sent. We need a tie-breaker to determine which
NetConnection_getDiscriminant(conn)) // value prevails.
if (NetConnection_getPlayerNr (conn) != side)
{ {
MeleeSetup_setConfirmedTeamName (setup, side, name); // Rule 2c+ or 3d+
Melee_Change_teamName (pMS, side, name); // We win the tie-breaker. The value which we sent prevails.
// This will also cause a notification of the local
// change to be sent to the remote side, which will act
// as a confirmation.
} }
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);
} }
} }
+9 -5
View File
@@ -59,6 +59,7 @@ struct melee_state
BOOLEAN (*InputFunc) (struct melee_state *pInputState); BOOLEAN (*InputFunc) (struct melee_state *pInputState);
BOOLEAN Initialized; BOOLEAN Initialized;
BOOLEAN meleeStarted;
MELEE_OPTIONS MeleeOption; MELEE_OPTIONS MeleeOption;
COUNT side; COUNT side;
COUNT row; COUNT row;
@@ -108,14 +109,17 @@ void resetFeedback (NetConnection *conn, NetplayResetReason reason,
void errorFeedback (NetConnection *conn); void errorFeedback (NetConnection *conn);
void closeFeedback (NetConnection *conn); void closeFeedback (NetConnection *conn);
bool Melee_Change_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index, bool Melee_LocalChange_ship (MELEE_STATE *pMS, COUNT side,
MeleeShip ship); FleetShipIndex index, MeleeShip ship);
bool Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name); bool Melee_LocalChange_teamName (MELEE_STATE *pMS, COUNT side,
bool Melee_Change_fleet (MELEE_STATE *pMS, size_t teamNr, const char *name);
bool Melee_LocalChange_fleet (MELEE_STATE *pMS, size_t teamNr,
const MeleeShip *fleet); 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); const MeleeTeam *team);
void Melee_bootstrapSyncTeam (MELEE_STATE *pMS, size_t teamNr);
void Melee_RemoteChange_ship (MELEE_STATE *pMS, NetConnection *conn, void Melee_RemoteChange_ship (MELEE_STATE *pMS, NetConnection *conn,
COUNT side, FleetShipIndex index, MeleeShip ship); COUNT side, FleetShipIndex index, MeleeShip ship);
void Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn, void Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn,
+96 -20
View File
@@ -34,6 +34,8 @@ MeleeTeam_init (MeleeTeam *team)
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++) for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
team->ships[slotI] = MELEE_NONE; team->ships[slotI] = MELEE_NONE;
team->name[0] = '\0';
} }
void void
@@ -158,7 +160,7 @@ MeleeTeam_getTeamName (const MeleeTeam *team)
// Returns true iff the state has actually changed. // Returns true iff the state has actually changed.
void void
MeleeTeam_setName (MeleeTeam *team, const UNICODE *name) MeleeTeam_setName (MeleeTeam *team, const char *name)
{ {
strncpy (team->name, name, sizeof team->name - 1); strncpy (team->name, name, sizeof team->name - 1);
team->name[sizeof team->name - 1] = '\0'; 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 *
MeleeSetup_new (void) MeleeSetup_new (void)
{ {
@@ -209,7 +231,7 @@ MeleeSetup_new (void)
MeleeTeam_init (&result->teams[teamI]); MeleeTeam_init (&result->teams[teamI]);
result->fleetValue[teamI] = 0; result->fleetValue[teamI] = 0;
#ifdef NETPLAY #ifdef NETPLAY
MeleeTeam_init (&result->confirmedTeams[teamI]); MeleeSetup_initSentTeam (result, teamI);
#endif /* NETPLAY */ #endif /* NETPLAY */
} }
return result; return result;
@@ -221,6 +243,17 @@ MeleeSetup_delete (MeleeSetup *setup)
HFree (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. // Returns true iff the state has actually changed.
bool bool
MeleeSetup_setShip (MeleeSetup *setup, size_t teamNr, FleetShipIndex slotNr, 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. // Returns true iff the state has actually changed.
bool bool
MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr, MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
const UNICODE *name) const char *name)
{ {
MeleeTeam *team = &setup->teams[teamNr]; MeleeTeam *team = &setup->teams[teamNr];
const UNICODE *oldName = MeleeTeam_getTeamName (team); const char *oldName = MeleeTeam_getTeamName (team);
if (strcmp (oldName, name) == 0) if (strcmp (oldName, name) == 0)
return false; return false;
@@ -271,6 +304,8 @@ MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
return true; return true;
} }
// NB. This function returns a pointer to a static buffer, which is
// overwritten by calls to MeleeSetup_setTeamName().
const char * const char *
MeleeSetup_getTeamName (const MeleeSetup *setup, size_t teamNr) MeleeSetup_getTeamName (const MeleeSetup *setup, size_t teamNr)
{ {
@@ -301,24 +336,31 @@ MeleeSetup_serializeTeam (const MeleeSetup *setup, size_t teamNr,
#ifdef NETPLAY #ifdef NETPLAY
MeleeShip MeleeShip
MeleeSetup_getConfirmedShip (const MeleeSetup *setup, size_t teamNr, MeleeSetup_getSentShip (const MeleeSetup *setup, size_t teamNr,
FleetShipIndex slotNr) 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 * 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. // Returns true iff the state has actually changed.
bool bool
MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr, MeleeSetup_setSentShip (MeleeSetup *setup, size_t teamNr,
FleetShipIndex slotNr, MeleeShip ship) FleetShipIndex slotNr, MeleeShip ship)
{ {
MeleeTeam *team = &setup->confirmedTeams[teamNr]; MeleeTeam *team = &setup->sentTeams[teamNr];
MeleeShip oldShip = MeleeTeam_getShip (team, slotNr); MeleeShip oldShip = MeleeTeam_getShip (team, slotNr);
if (ship == oldShip) if (ship == oldShip)
@@ -329,28 +371,62 @@ MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr,
} }
// Returns true iff the state has actually changed. // 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 bool
MeleeSetup_setConfirmedTeamName (MeleeSetup *setup, size_t teamNr, MeleeSetup_setSentTeamName (MeleeSetup *setup, size_t teamNr,
const UNICODE *name) const char *name)
{ {
MeleeTeam *team = &setup->confirmedTeams[teamNr]; bool haveSentName = setup->haveSentTeamName[teamNr];
const UNICODE *oldName = MeleeTeam_getTeamName (team);
if (strcmp (oldName, name) == 0) if (name == NULL)
return false; {
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; return true;
} }
#if 0 #if 0
bool bool
MeleeSetup_isTeamConfirmed (MeleeSetup *setup, size_t teamNr) MeleeSetup_isTeamSent (MeleeSetup *setup, size_t teamNr)
{ {
MeleeTeam *localTeam = &setup->teams[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 #endif
+23 -38
View File
@@ -36,47 +36,29 @@ typedef COUNT FleetShipIndex;
struct MeleeTeam struct MeleeTeam
{ {
MeleeShip ships[MELEE_FLEET_SIZE]; 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 /* The +1 is for the terminating \0; the +24 is in case some
* default name in starcon.txt is unknowingly mangled. */ * 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 */ #endif /* MELEETEAM_INTERNAL */
#ifdef MELEESETUP_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 struct MeleeSetup
{ {
MeleeTeam teams[NUM_SIDES]; MeleeTeam teams[NUM_SIDES];
COUNT fleetValue[NUM_SIDES]; COUNT fleetValue[NUM_SIDES];
#ifdef NETPLAY #ifdef NETPLAY
MeleeTeam confirmedTeams[NUM_SIDES]; MeleeTeam sentTeams[NUM_SIDES];
// The least team which both sides agreed upon. // 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. // 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 #endif
}; };
@@ -88,6 +70,9 @@ void MeleeTeam_init (MeleeTeam *team);
void MeleeTeam_uninit (MeleeTeam *team); void MeleeTeam_uninit (MeleeTeam *team);
MeleeTeam *MeleeTeam_new (void); MeleeTeam *MeleeTeam_new (void);
void MeleeTeam_delete (MeleeTeam *team); 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_serialize (const MeleeTeam *team, uio_Stream *stream);
int MeleeTeam_deserialize (MeleeTeam *team, uio_Stream *stream); int MeleeTeam_deserialize (MeleeTeam *team, uio_Stream *stream);
COUNT MeleeTeam_getValue (const MeleeTeam *team); COUNT MeleeTeam_getValue (const MeleeTeam *team);
@@ -96,23 +81,23 @@ void MeleeTeam_setShip (MeleeTeam *team, FleetShipIndex slotNr,
MeleeShip ship); MeleeShip ship);
const MeleeShip *MeleeTeam_getFleet (const MeleeTeam *team); const MeleeShip *MeleeTeam_getFleet (const MeleeTeam *team);
const char *MeleeTeam_getTeamName (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); void MeleeTeam_copy (MeleeTeam *copy, const MeleeTeam *original);
#if 0 #if 0
bool MeleeTeam_isEqual (const MeleeTeam *team1, const MeleeTeam *team2); bool MeleeTeam_isEqual (const MeleeTeam *team1, const MeleeTeam *team2);
#endif #endif
#ifdef NETPLAY #ifdef NETPLAY
MeleeShip MeleeSetup_getConfirmedShip (const MeleeSetup *setup, size_t teamNr, MeleeShip MeleeSetup_getSentShip (const MeleeSetup *setup, size_t teamNr,
FleetShipIndex slotNr); FleetShipIndex slotNr);
const char *MeleeSetup_getConfirmedTeamName (const MeleeSetup *setup, const char *MeleeSetup_getSentTeamName (const MeleeSetup *setup,
size_t teamNr); size_t teamNr);
bool MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr, bool MeleeSetup_setSentShip (MeleeSetup *setup, size_t teamNr,
FleetShipIndex slotNr, MeleeShip ship); FleetShipIndex slotNr, MeleeShip ship);
bool MeleeSetup_setConfirmedTeamName (MeleeSetup *setup, size_t teamNr, bool MeleeSetup_setSentTeamName (MeleeSetup *setup, size_t teamNr,
const UNICODE *name); const char *name);
#if 0 #if 0
bool MeleeSetup_isTeamConfirmed (MeleeSetup *setup, size_t teamNr); bool MeleeSetup_isTeamSent (MeleeSetup *setup, size_t teamNr);
#endif #endif
#endif /* NETPLAY */ #endif /* NETPLAY */
@@ -127,7 +112,7 @@ bool MeleeSetup_setFleet (MeleeSetup *setup, size_t teamNr,
const MeleeShip *fleet); const MeleeShip *fleet);
const MeleeShip *MeleeSetup_getFleet (const MeleeSetup *setup, size_t teamNr); const MeleeShip *MeleeSetup_getFleet (const MeleeSetup *setup, size_t teamNr);
bool MeleeSetup_setTeamName (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, const char *MeleeSetup_getTeamName (const MeleeSetup *setup,
size_t teamNr); size_t teamNr);
COUNT MeleeSetup_getFleetValue (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, void MeleeState_setFleet (MELEE_STATE *pMS, size_t teamNr,
const MeleeShip *fleet); const MeleeShip *fleet);
void MeleeState_setTeamName (MELEE_STATE *pMS, size_t teamNr, void MeleeState_setTeamName (MELEE_STATE *pMS, size_t teamNr,
const UNICODE *name); const char *name);
void MeleeState_setTeam (MELEE_STATE *pMS, size_t teamNr, void MeleeState_setTeam (MELEE_STATE *pMS, size_t teamNr,
const MeleeTeam *team); const MeleeTeam *team);
+3
View File
@@ -30,7 +30,10 @@ typedef enum MeleeShip {
MELEE_YEHAT, MELEE_YEHAT,
MELEE_ZOQFOTPIK, MELEE_ZOQFOTPIK,
MELEE_UNSET = ((BYTE) ~0) - 1,
// Used with the Update protocol, to register in the sentTeam
MELEE_NONE = (BYTE) ~0 MELEE_NONE = (BYTE) ~0
// Empty fleet position.
} MeleeShip; } MeleeShip;
#define NUM_MELEE_SHIPS (MELEE_ZOQFOTPIK + 1) #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 // Ignore errors; it's not a big deal. In debug mode, a message
// will already have been printed from the function itself. // will already have been printed from the function itself.
conn->stateFlags.myTurn = true;
// The "server" may speak first.
conn->stateFlags.discriminant = true; conn->stateFlags.discriminant = true;
NetConnection_connected(conn); NetConnection_connected(conn);
@@ -210,8 +208,6 @@ NetConnection_connectedClientCallback(ConnectState *connectState,
// Ignore errors; it's not a big deal. In debug mode, a message // Ignore errors; it's not a big deal. In debug mode, a message
// will already have been printed from the function itself. // will already have been printed from the function itself.
conn->stateFlags.myTurn = false;
// The "server" may speak first.
conn->stateFlags.discriminant = false; conn->stateFlags.discriminant = false;
NetConnection_connected(conn); NetConnection_connected(conn);
@@ -129,9 +129,6 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
conn->stateData = NULL; conn->stateData = NULL;
conn->stateFlags.connected = false; conn->stateFlags.connected = false;
conn->stateFlags.disconnected = false; conn->stateFlags.disconnected = false;
conn->stateFlags.myTurn = false;
conn->stateFlags.endingTurn = false;
conn->stateFlags.pendingTurnChange = false;
conn->stateFlags.discriminant = false; conn->stateFlags.discriminant = false;
conn->stateFlags.handshake.localOk = false; conn->stateFlags.handshake.localOk = false;
conn->stateFlags.handshake.remoteOk = false; conn->stateFlags.handshake.remoteOk = false;
@@ -350,11 +347,6 @@ NetConnection_isConnected(const NetConnection *conn) {
return conn->stateFlags.connected; return conn->stateFlags.connected;
} }
bool
NetConnection_isMyTurn(const NetConnection *conn) {
return conn->stateFlags.myTurn;
}
int int
NetConnection_getPlayerNr(const NetConnection *conn) { NetConnection_getPlayerNr(const NetConnection *conn) {
return conn->player; return conn->player;
+2 -12
View File
@@ -104,20 +104,11 @@ typedef struct {
/* This NetConnection has been disconnected. This implies /* This NetConnection has been disconnected. This implies
* !connected. It is only set if the NetConnection was once * !connected. It is only set if the NetConnection was once
* connected, but is no longer. */ * 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; bool discriminant;
/* If it is true here, it is false on the remote side /* If it is true here, it is false on the remote side
* of the same connection. It may be used to break ties. * of the same connection. It may be used to break ties.
* Unlike myTurn, this one is guaranteed not to change * It is guaranteed not to change during a connection. Undefined
* during a connection. Undefined while not connected. */ * while not connected. */
HandShakeFlags handshake; HandShakeFlags handshake;
ReadyFlags ready; ReadyFlags ready;
ResetFlags reset; ResetFlags reset;
@@ -204,7 +195,6 @@ bool NetConnection_isConnected(const NetConnection *conn);
void NetConnection_doErrorCallback(NetConnection *nd, int err); void NetConnection_doErrorCallback(NetConnection *nd, int err);
bool NetConnection_isMyTurn(const NetConnection *conn);
void NetConnection_setStateData(NetConnection *conn, void NetConnection_setStateData(NetConnection *conn,
NetConnectionStateData *stateData); NetConnectionStateData *stateData);
NetConnectionStateData *NetConnection_getStateData(const NetConnection *conn); NetConnectionStateData *NetConnection_getStateData(const NetConnection *conn);
+7 -34
View File
@@ -84,6 +84,9 @@ NetMelee_connectCallback(NetConnection *conn) {
NetConnection_setStateData(conn, (void *) battleStateData); NetConnection_setStateData(conn, (void *) battleStateData);
NetConnection_setExtra(conn, NULL); NetConnection_setExtra(conn, NULL);
// We have sent no teams yet. Initialize the state accordingly.
MeleeSetup_resetSentTeams (meleeState->meleeSetup);
sendInit(conn); sendInit(conn);
Netplay_localReady (conn, NetMelee_enterState_inSetup, NULL, false); Netplay_localReady (conn, NetMelee_enterState_inSetup, NULL, false);
} }
@@ -119,43 +122,13 @@ NetMelee_enterState_inSetup(NetConnection *conn, void *arg) {
connectedFeedback(conn); connectedFeedback(conn);
Netplay_NotifyAll_setFleet(meleeState, player); // Send our team to the remote side.
Netplay_NotifyAll_setTeamName(meleeState, player); // XXX This only works with 2 players atm.
assert (NUM_PLAYERS == 2);
Melee_bootstrapSyncTeam (meleeState, player);
flushPacketQueues(); flushPacketQueues();
(void) arg; (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();
}
}
+1 -2
View File
@@ -62,8 +62,7 @@ readyFlagsMeaningful(NetState state) {
state == NetState_interBattle || state == NetState_interBattle ||
state == NetState_inBattle || state == NetState_inBattle ||
state == NetState_endingBattle || state == NetState_endingBattle ||
state == NetState_endingBattle2 || state == NetState_endingBattle2;
state == NetState_endMelee;
} }
+5 -5
View File
@@ -25,15 +25,15 @@
#define NETPLAY_FULL 2 #define NETPLAY_FULL 2
#define NETPLAY_PROTOCOL_VERSION_MAJOR 0 #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_MAJOR 0
#define NETPLAY_MIN_UQM_VERSION_MINOR 5 #define NETPLAY_MIN_UQM_VERSION_MINOR 6
#define NETPLAY_MIN_UQM_VERSION_PATCH 4 #define NETPLAY_MIN_UQM_VERSION_PATCH 9
#define NETPLAY_DEBUG #undef NETPLAY_DEBUG
/* Extra debugging for netplay */ /* Extra debugging for netplay */
#define NETPLAY_DEBUG_FILE #undef NETPLAY_DEBUG_FILE
/* Dump extra debugging information to file. /* Dump extra debugging information to file.
* Implies NETPLAY_DEBUG.*/ * Implies NETPLAY_DEBUG.*/
#define NETPLAY_STATISTICS #define NETPLAY_STATISTICS
+1
View File
@@ -21,6 +21,7 @@
#include "netplay.h" #include "netplay.h"
#include "port.h" #include "port.h"
#include "netsend.h"
#include "netconnection.h" #include "netconnection.h"
#include "packet.h" #include "packet.h"
#include "libs/log.h" #include "libs/log.h"
@@ -38,7 +38,6 @@ NetStateData netStateData[] = {
DEFINE_NETSTATEDATA(inBattle), DEFINE_NETSTATEDATA(inBattle),
DEFINE_NETSTATEDATA(endingBattle), DEFINE_NETSTATEDATA(endingBattle),
DEFINE_NETSTATEDATA(endingBattle2), DEFINE_NETSTATEDATA(endingBattle2),
DEFINE_NETSTATEDATA(endMelee),
}; };
void void
@@ -36,7 +36,6 @@ typedef enum {
NetState_inBattle, /* Battle has started */ NetState_inBattle, /* Battle has started */
NetState_endingBattle, /* Both sides are prepared to end */ NetState_endingBattle, /* Both sides are prepared to end */
NetState_endingBattle2, /* Waiting for the final synchronisation */ NetState_endingBattle2, /* Waiting for the final synchronisation */
NetState_endMelee, /* Melee ended; remote is not yet in setup */
} NetState; } NetState;
#include "types.h" #include "types.h"
+19 -29
View File
@@ -29,33 +29,31 @@
#include <string.h> #include <string.h>
#define DEFINE_PACKETDATA(name, inTurn) \ #define DEFINE_PACKETDATA(name) \
{ \ { \
/* .len = */ sizeof (Packet_##name), \ /* .len = */ sizeof (Packet_##name), \
/* .handler = */ (PacketHandler) PacketHandler_##name, \ /* .handler = */ (PacketHandler) PacketHandler_##name, \
/* .name = */ #name, \ /* .name = */ #name, \
/* .inTurn = */ (inTurn) \
} }
PacketTypeData packetTypeData[PACKET_NUM] = { PacketTypeData packetTypeData[PACKET_NUM] = {
DEFINE_PACKETDATA(Init, false), DEFINE_PACKETDATA(Init),
DEFINE_PACKETDATA(Ping, false), DEFINE_PACKETDATA(Ping),
DEFINE_PACKETDATA(Ack, false), DEFINE_PACKETDATA(Ack),
DEFINE_PACKETDATA(EndTurn, false), DEFINE_PACKETDATA(Ready),
DEFINE_PACKETDATA(Ready, false), DEFINE_PACKETDATA(Fleet),
DEFINE_PACKETDATA(Fleet, true), DEFINE_PACKETDATA(TeamName),
DEFINE_PACKETDATA(TeamName, true), DEFINE_PACKETDATA(Handshake0),
DEFINE_PACKETDATA(Handshake0, false), DEFINE_PACKETDATA(Handshake1),
DEFINE_PACKETDATA(Handshake1, false), DEFINE_PACKETDATA(HandshakeCancel),
DEFINE_PACKETDATA(HandshakeCancel, false), DEFINE_PACKETDATA(HandshakeCancelAck),
DEFINE_PACKETDATA(HandshakeCancelAck, false), DEFINE_PACKETDATA(SeedRandom),
DEFINE_PACKETDATA(SeedRandom, false), DEFINE_PACKETDATA(InputDelay),
DEFINE_PACKETDATA(InputDelay, false), DEFINE_PACKETDATA(SelectShip),
DEFINE_PACKETDATA(SelectShip, false), DEFINE_PACKETDATA(BattleInput),
DEFINE_PACKETDATA(BattleInput, false), DEFINE_PACKETDATA(FrameCount),
DEFINE_PACKETDATA(FrameCount, false), DEFINE_PACKETDATA(Checksum),
DEFINE_PACKETDATA(Checksum, false), DEFINE_PACKETDATA(Abort),
DEFINE_PACKETDATA(Abort, false), DEFINE_PACKETDATA(Reset),
DEFINE_PACKETDATA(Reset, false),
}; };
static inline void * static inline void *
@@ -113,14 +111,6 @@ Packet_Ack_create(uint32 id) {
return packet; return packet;
} }
Packet_EndTurn *
Packet_EndTurn_create(void) {
Packet_EndTurn *packet =
(Packet_EndTurn *) Packet_create(PACKET_ENDTURN, 0);
return packet;
}
Packet_Ready * Packet_Ready *
Packet_Ready_create(void) { Packet_Ready_create(void) {
Packet_Ready *packet = (Packet_Ready *) Packet_create(PACKET_READY, 0); Packet_Ready *packet = (Packet_Ready *) Packet_create(PACKET_READY, 0);
-9
View File
@@ -25,7 +25,6 @@ typedef enum PacketType {
PACKET_INIT, PACKET_INIT,
PACKET_PING, PACKET_PING,
PACKET_ACK, PACKET_ACK,
PACKET_ENDTURN,
PACKET_READY, PACKET_READY,
PACKET_FLEET, PACKET_FLEET,
PACKET_TEAMNAME, PACKET_TEAMNAME,
@@ -79,7 +78,6 @@ typedef struct {
size_t len; /* Minimal length of a packet of this type */ size_t len; /* Minimal length of a packet of this type */
PacketHandler handler; PacketHandler handler;
const char *name; const char *name;
bool inTurn; /* Can only be sent if it's this party's turn. */
} PacketTypeData; } PacketTypeData;
extern PacketTypeData packetTypeData[]; extern PacketTypeData packetTypeData[];
@@ -142,12 +140,6 @@ typedef struct {
uint32 id; uint32 id;
} Packet_Ack; } 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. // Used to signal that a party is ready to continue.
typedef struct { typedef struct {
PacketHeader header; PacketHeader header;
@@ -262,7 +254,6 @@ void Packet_delete(Packet *packet);
Packet_Init *Packet_Init_create(void); Packet_Init *Packet_Init_create(void);
Packet_Ping *Packet_Ping_create(uint32 id); Packet_Ping *Packet_Ping_create(uint32 id);
Packet_Ack *Packet_Ack_create(uint32 id); Packet_Ack *Packet_Ack_create(uint32 id);
Packet_EndTurn *Packet_EndTurn_create(void);
Packet_Ready *Packet_Ready_create(void); Packet_Ready *Packet_Ready_create(void);
Packet_Handshake0 *Packet_Handshake0_create(void); Packet_Handshake0 *Packet_Handshake0_create(void);
Packet_Handshake1 *Packet_Handshake1_create(void); Packet_Handshake1 *Packet_Handshake1_create(void);
@@ -142,41 +142,6 @@ PacketHandler_Ack(NetConnection *conn, const Packet_Ack *packet) {
return 0; 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 // Convert the side indication relative to a remote party to
// a local player number. // a local player number.
static inline int static inline int
@@ -189,17 +154,6 @@ localSide(NetConnection *conn, NetplaySide side) {
return 1 - conn->player; 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 int
PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) { PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) {
if (conn->stateFlags.reset.localReset) 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)) if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
return -1; // errno is set return -1; // errno is set
if (!checkYourTurn(conn, PACKET_FLEET)) {
// errno is set
return -1;
}
player = localSide(conn, (NetplaySide) packet->side); player = localSide(conn, (NetplaySide) packet->side);
len = packetLength((const Packet *) packet); len = packetLength((const Packet *) packet);
@@ -304,14 +253,9 @@ PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
return -1; return -1;
} }
if (!testNetState(conn->state == NetState_inSetup, PACKET_TEAMNAME)) if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
return -1; // errno is set return -1; // errno is set
if (!checkYourTurn(conn, PACKET_TEAMNAME)) {
// errno is set
return -1;
}
battleStateData = (BattleStateData *) NetConnection_getStateData(conn); battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
if (conn->stateFlags.handshake.localOk) { if (conn->stateFlags.handshake.localOk) {
@@ -503,8 +447,7 @@ PacketHandler_InputDelay(NetConnection *conn,
return -1; return -1;
} }
if (!testNetState(conn->state == NetState_preBattle, if (!testNetState(conn->state == NetState_preBattle, PACKET_INPUTDELAY))
PACKET_INPUTDELAY))
return -1; // errno is set return -1; // errno is set
battleStateData = (BattleStateData *) NetConnection_getStateData(conn); battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
@@ -28,7 +28,6 @@
DECLARE_PACKETHANDLER(Init); DECLARE_PACKETHANDLER(Init);
DECLARE_PACKETHANDLER(Ping); DECLARE_PACKETHANDLER(Ping);
DECLARE_PACKETHANDLER(Ack); DECLARE_PACKETHANDLER(Ack);
DECLARE_PACKETHANDLER(EndTurn);
DECLARE_PACKETHANDLER(Ready); DECLARE_PACKETHANDLER(Ready);
DECLARE_PACKETHANDLER(Fleet); DECLARE_PACKETHANDLER(Fleet);
DECLARE_PACKETHANDLER(TeamName); DECLARE_PACKETHANDLER(TeamName);
+3 -62
View File
@@ -47,9 +47,6 @@ PacketQueue_init(PacketQueue *queue) {
queue->size = 0; queue->size = 0;
queue->first = NULL; queue->first = NULL;
queue->end = &queue->first; queue->end = &queue->first;
queue->firstUrgent = NULL;
queue->endUrgent = &queue->first;
} }
static void static void
@@ -64,31 +61,23 @@ PacketQueue_deleteLinks(PacketQueueLink *link) {
void void
PacketQueue_uninit(PacketQueue *queue) { PacketQueue_uninit(PacketQueue *queue) {
PacketQueue_deleteLinks(queue->firstUrgent);
PacketQueue_deleteLinks(queue->first); PacketQueue_deleteLinks(queue->first);
} }
void void
queuePacket(NetConnection *conn, Packet *packet, bool urgent) { queuePacket(NetConnection *conn, Packet *packet) {
PacketQueue *queue; PacketQueue *queue;
PacketQueueLink *link; PacketQueueLink *link;
assert(NetConnection_isConnected(conn)); assert(NetConnection_isConnected(conn));
assert(!urgent || !packetTypeData[packetType(packet)].inTurn);
// Urgent packets should never stall the connection.
queue = &conn->queue; queue = &conn->queue;
link = PacketQueueLink_alloc(); link = PacketQueueLink_alloc();
link->packet = packet; link->packet = packet;
link->next = NULL; link->next = NULL;
if (urgent) { *queue->end = link;
*queue->endUrgent = link; queue->end = &link->next;
queue->endUrgent = &link->next;
} else {
*queue->end = link;
queue->end = &link->next;
}
queue->size++; queue->size++;
// XXX: perhaps check that this queue isn't getting too large? // XXX: perhaps check that this queue isn't getting too large?
@@ -124,29 +113,6 @@ flushPacketQueueLinks(NetConnection *conn, PacketQueueLink **first) {
PacketQueue *queue = &conn->queue; PacketQueue *queue = &conn->queue;
for (link = *first; link != NULL; link = next) { 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) { if (sendPacket(conn, link->packet) == -1) {
// Errno is set. // Errno is set.
*first = link; *first = link;
@@ -170,14 +136,6 @@ flushPacketQueue(NetConnection *conn) {
assert(NetConnection_isConnected(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); flushResult = flushPacketQueueLinks(conn, &queue->first);
if (queue->first == NULL) if (queue->first == NULL)
queue->end = &queue->first; queue->end = &queue->first;
@@ -186,23 +144,6 @@ flushPacketQueue(NetConnection *conn) {
return -1; 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; return 0;
} }
+2 -19
View File
@@ -37,30 +37,13 @@ struct PacketQueue {
PacketQueueLink *first; PacketQueueLink *first;
PacketQueueLink **end; PacketQueueLink **end;
PacketQueueLink *firstUrgent;
PacketQueueLink **endUrgent;
// first points to the first entry in the queue // first points to the first entry in the queue
// end points to the location where the next non-urgent message should // end points to the location where the next message should be inserted.
// 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.
}; };
void PacketQueue_init(PacketQueue *queue); void PacketQueue_init(PacketQueue *queue);
void PacketQueue_uninit(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); int flushPacketQueue(NetConnection *conn);
+19 -37
View File
@@ -29,7 +29,7 @@ sendInit(NetConnection *conn) {
Packet_Init *packet; Packet_Init *packet;
packet = Packet_Init_create(); packet = Packet_Init_create();
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -37,7 +37,7 @@ sendPing(NetConnection *conn, uint32 id) {
Packet_Ping *packet; Packet_Ping *packet;
packet = Packet_Ping_create(id); packet = Packet_Ping_create(id);
queuePacket(conn, (Packet *) packet, true); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -45,15 +45,7 @@ sendAck(NetConnection *conn, uint32 id) {
Packet_Ack *packet; Packet_Ack *packet;
packet = Packet_Ack_create(id); packet = Packet_Ack_create(id);
queuePacket(conn, (Packet *) packet, true); queuePacket(conn, (Packet *) packet);
}
void
sendEndTurn(NetConnection *conn) {
Packet_EndTurn *packet;
packet = Packet_EndTurn_create();
queuePacket(conn, (Packet *) packet, true);
} }
void void
@@ -61,17 +53,7 @@ sendReady(NetConnection *conn) {
Packet_Ready *packet; Packet_Ready *packet;
packet = Packet_Ready_create(); packet = Packet_Ready_create();
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
}
// 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);
} }
void void
@@ -79,7 +61,7 @@ sendHandshake0(NetConnection *conn) {
Packet_Handshake0 *packet; Packet_Handshake0 *packet;
packet = Packet_Handshake0_create(); packet = Packet_Handshake0_create();
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -87,7 +69,7 @@ sendHandshake1(NetConnection *conn) {
Packet_Handshake1 *packet; Packet_Handshake1 *packet;
packet = Packet_Handshake1_create(); packet = Packet_Handshake1_create();
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -95,7 +77,7 @@ sendHandshakeCancel(NetConnection *conn) {
Packet_HandshakeCancel *packet; Packet_HandshakeCancel *packet;
packet = Packet_HandshakeCancel_create(); packet = Packet_HandshakeCancel_create();
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -103,7 +85,7 @@ sendHandshakeCancelAck(NetConnection *conn) {
Packet_HandshakeCancelAck *packet; Packet_HandshakeCancelAck *packet;
packet = Packet_HandshakeCancelAck_create(); packet = Packet_HandshakeCancelAck_create();
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -112,7 +94,7 @@ sendTeamName(NetConnection *conn, NetplaySide side, const char *name,
Packet_TeamName *packet; Packet_TeamName *packet;
packet = Packet_TeamName_create(side, name, len); packet = Packet_TeamName_create(side, name, len);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -128,7 +110,7 @@ sendFleet(NetConnection *conn, NetplaySide side, const MeleeShip *ships,
packet->ships[i].ship = (uint8) ships[i]; packet->ships[i].ship = (uint8) ships[i];
} }
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -141,7 +123,7 @@ sendFleetShip(NetConnection *conn, NetplaySide side,
packet->ships[0].index = (uint8) shipIndex; packet->ships[0].index = (uint8) shipIndex;
packet->ships[0].ship = (uint8) ship; packet->ships[0].ship = (uint8) ship;
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -149,7 +131,7 @@ sendSeedRandom(NetConnection *conn, uint32 seed) {
Packet_SeedRandom *packet; Packet_SeedRandom *packet;
packet = Packet_SeedRandom_create(seed); packet = Packet_SeedRandom_create(seed);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -157,7 +139,7 @@ sendInputDelay(NetConnection *conn, uint32 delay) {
Packet_InputDelay *packet; Packet_InputDelay *packet;
packet = Packet_InputDelay_create(delay); packet = Packet_InputDelay_create(delay);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -165,7 +147,7 @@ sendSelectShip(NetConnection *conn, FleetShipIndex index) {
Packet_SelectShip *packet; Packet_SelectShip *packet;
packet = Packet_SelectShip_create((uint16) index); packet = Packet_SelectShip_create((uint16) index);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -173,7 +155,7 @@ sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
Packet_BattleInput *packet; Packet_BattleInput *packet;
packet = Packet_BattleInput_create((uint8) input); packet = Packet_BattleInput_create((uint8) input);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -181,7 +163,7 @@ sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount) {
Packet_FrameCount *packet; Packet_FrameCount *packet;
packet = Packet_FrameCount_create((uint32) frameCount); packet = Packet_FrameCount_create((uint32) frameCount);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
#ifdef NETPLAY_CHECKSUM #ifdef NETPLAY_CHECKSUM
@@ -191,7 +173,7 @@ sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
Packet_Checksum *packet; Packet_Checksum *packet;
packet = Packet_Checksum_create((uint32) frameNr, (uint32) checksum); packet = Packet_Checksum_create((uint32) frameNr, (uint32) checksum);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
#endif #endif
@@ -200,7 +182,7 @@ sendAbort(NetConnection *conn, NetplayAbortReason reason) {
Packet_Abort *packet; Packet_Abort *packet;
packet = Packet_Abort_create((uint16) reason); packet = Packet_Abort_create((uint16) reason);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
void void
@@ -208,7 +190,7 @@ sendReset(NetConnection *conn, NetplayResetReason reason) {
Packet_Reset *packet; Packet_Reset *packet;
packet = Packet_Reset_create((uint16) reason); packet = Packet_Reset_create((uint16) reason);
queuePacket(conn, (Packet *) packet, false); queuePacket(conn, (Packet *) packet);
} }
@@ -34,9 +34,7 @@
void sendInit(NetConnection *conn); void sendInit(NetConnection *conn);
void sendPing(NetConnection *conn, uint32 id); void sendPing(NetConnection *conn, uint32 id);
void sendAck(NetConnection *conn, uint32 id); void sendAck(NetConnection *conn, uint32 id);
void sendEndTurn(NetConnection *conn);
void sendReady(NetConnection *conn); void sendReady(NetConnection *conn);
int sendEndTurnDirect(NetConnection *conn);
void sendHandshake0(NetConnection *conn); void sendHandshake0(NetConnection *conn);
void sendHandshake1(NetConnection *conn); void sendHandshake1(NetConnection *conn);
void sendHandshakeCancel(NetConnection *conn); void sendHandshakeCancel(NetConnection *conn);
@@ -82,7 +82,6 @@ Netplay_connectionReset(NetConnection *conn, NetplayResetReason reason,
case NetState_inBattle: case NetState_inBattle:
case NetState_endingBattle: case NetState_endingBattle:
case NetState_endingBattle2: case NetState_endingBattle2:
case NetState_endMelee:
resetFeedback(conn, reason, byRemote); resetFeedback(conn, reason, byRemote);
break; break;
} }
+7 -15
View File
@@ -384,15 +384,6 @@ aborted:
return FALSE; return FALSE;
} }
#ifdef NETPLAY
static void
endMeleeCallback (NetConnection *conn, void *arg)
{
NetMelee_reenterState_inSetup (conn);
(void) arg;
}
#endif
static COUNT static COUNT
GetRaceQueueValue (const QUEUE *queue) { GetRaceQueueValue (const QUEUE *queue) {
COUNT result; COUNT result;
@@ -662,9 +653,14 @@ MeleeGameOver (void)
for (playerI = 0; playerI < NUM_PLAYERS; playerI++) for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
DrawPickMeleeFrame (playerI); DrawPickMeleeFrame (playerI);
TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
#ifdef NETPLAY
negotiateReadyConnections(true, NetState_inSetup);
#endif
TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
PressState = PulsedInputState.menu[KEY_MENU_SELECT] || PressState = PulsedInputState.menu[KEY_MENU_SELECT] ||
PulsedInputState.menu[KEY_MENU_CANCEL]; PulsedInputState.menu[KEY_MENU_CANCEL];
do do
@@ -683,11 +679,6 @@ MeleeGameOver (void)
&& (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL) && (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL)
|| GetTimeCounter () < TimeOut))); || GetTimeCounter () < TimeOut)));
#ifdef NETPLAY
setStateConnections (NetState_endMelee);
localReadyConnections (endMeleeCallback, NULL, true);
#endif
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
} }
@@ -898,6 +889,7 @@ GetInitialMeleeStarShips (HSTARSHIP *result)
return GetMeleeStarShips (playerMask, result); return GetMeleeStarShips (playerMask, result);
} }
// Get the next ship to use in SuperMelee.
BOOLEAN BOOLEAN
GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result) GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result)
{ {
+3 -3
View File
@@ -121,7 +121,7 @@ readyToEndCallback (NetConnection *conn, void *arg)
* 3. After a player has both sent and received a frame count, the * 3. After a player has both sent and received a frame count, the
* simulation continues for each party, until the maximum of both * simulation continues for each party, until the maximum of both
* frame counts has been achieved. * 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. * reached the target frame count.
* 5. The battle ends. * 5. The battle ends.
*/ */
@@ -132,7 +132,7 @@ readyForBattleEndPlayer (NetConnection *conn)
battleStateData = (BattleStateData *) NetConnection_getStateData(conn); battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
if (NetConnection_getState (conn) == NetState_interBattle || if (NetConnection_getState (conn) == NetState_interBattle ||
NetConnection_getState (conn) == NetState_endMelee) NetConnection_getState (conn) == NetState_inSetup)
{ {
// This connection is already ready. The entire synchronisation // This connection is already ready. The entire synchronisation
// protocol has already been done for this connection. // 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 // Keep the simulation going as long as the target frame count
// hasn't been reached yet. Note that if the connection state is // 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. // remote frame count, so the target frame count may still rise.
if (battleFrameCount < battleStateData->endFrameCount) if (battleFrameCount < battleStateData->endFrameCount)
return false; return false;
+2 -2
View File
@@ -21,8 +21,8 @@
#define UQM_MAJOR_VERSION_S "0" #define UQM_MAJOR_VERSION_S "0"
#define UQM_MINOR_VERSION 6 #define UQM_MINOR_VERSION 6
#define UQM_MINOR_VERSION_S "6" #define UQM_MINOR_VERSION_S "6"
#define UQM_PATCH_VERSION 8 #define UQM_PATCH_VERSION 9
#define UQM_PATCH_VERSION_S "8" #define UQM_PATCH_VERSION_S "9"
#define UQM_EXTRA_VERSION "" #define UQM_EXTRA_VERSION ""
/* The final version is interpreted as: /* The final version is interpreted as:
* printf ("%d.%d.%d%s", UQM_MAJOR_VERSION, UQM_MINOR_VERSION, * printf ("%d.%d.%d%s", UQM_MAJOR_VERSION, UQM_MINOR_VERSION,