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