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:
- Fixed the network SuperMelee team configuration protocol - SvdB
- Fixed fuel reserve bounds checks - SvdB
- Fixed a crash when filling fuel tanks over 10 (bug #1082) - Alex
- Got rid of many warnings - SvdB
+138 -33
View File
@@ -5,7 +5,7 @@ of a network connection.
This is used when both parties need to sending information to the
other side, but what each party is doing does not interfere with
what the other party is doing.
- Only speak in your own turn ("Turn")
- Agree on changes ("Update")
This is used when the parties have changes to make to common data.
- Mutual agreement on an action ("Confirm")
This is used to end a state where both parties are modifying
@@ -49,43 +49,148 @@ local decision -> Send READY goto 3
============================================================================
"Turn" negotiation.
"Update" negotiation.
For some actions (like changing a shared configuration option), it is
important that both sides don't just send changes at once.
To handle this, only one party may send these packets at any moment.
If the party whose turn it isn't wants to speak, or if the party whose
turn it is doesn't have anything further to say, he can send an ENDTURN
packet. The other party should confirm this by sending another ENDTURN
packet back.
During configuration, both sides may change the same properties. So that the
two sides don't have to take turns, the changes are made locally, and then
the changes are synchronised.
States:
0. myTurn - I may speak
myTurn && !endTurn
1. endMyTurn - I've given up speaking, waiting for confirmation
myTurn && endTurn
2. yourTurn - You may speak
!myTurn && !endTurn
3. endYourTurn - I want to speak, waiting for confirmation
!myTurn && endTurn
To this end, each side has a state containing two copies of each property:
1. The value of the property as it is locally
2. The last value which it sent to the other side (until it is no longer
relevant for the protocol)
Messages:
- ENDTURN - "this party ready to change turns"
The basic idea of the Update protocol is:
- both sides each send and receive one packet before being allowed to
send another one (we'll call this a "turn" here)
- when a packet has been sent and one has been received in a turn,
and the sent and received values are the same, then that value is the
agreed upon value. If the sent and received values differ, then a
tie breaker determines which one prevails.
- when the first local change of a turn is made, the change is sent to
the other side
- when a local change has been made while a packet has already been
sent this turn, the change will be made locally, but communicating the
change will be postponed
- when a turn ends while a change has been postponed, and this change isn't
negated by a remote change, then the change will be sent
- when a remote change arrives, and a packet has not been sent this turn,
the local state is updated with the change, and the same packet is sent
to the other side to confirm the change
The tie breaker is required to always let one side win, and the other
side lose, given the same property.
Any function satisfying this requirement is usable, but the currently
used one will return true for the side which 'owns' the property, and
false on the other side.
Another tie breaker could be one which always lets the same side win,
regardless of the property.
From state 0 (myTurn):
local decision -> Send ENDTURN, goto 1
received ENDTURN -> Send ENDTURN, goto 2
The protocol:
From state 1 (endMyTurn):
received ENDTURN -> Send ENDTURN, goto 2
From state 1, {own=x0, sent=--}:
1a Local change x1 -> send(x1); state=2:{own=x1,sent=x1}
1b Received UPDATE(x1) -> send(x1); state=1:{own=x1,sent=--}
From state 2 (yourTurn):
local decision -> Send ENDTURN, goto 3
received ENDTURN -> Send ENDTURN, goto 0
From state 2, {own=x0, sent=x0}:
2a Local change x1 -> state=3:{own=x1,sent=x0}
2b Received UPDATE(x0) -> state=1:{own=x0,sent=--}
2c+ Received UPDATE(x1) -> state=1:{own=x0,sent=--} if winTieBreak
2c- Received UPDATE(x1) -> state=1:{own=x1,sent=--} if !winTieBreak
From state 3 (endYourTurn):
received ENDTURN -> Send ENDTURN, goto 0
From state 3, {own=x1, sent=x0}:
3a Local change x0 -> state=2:{own=x0,sent=x0}
3b Local change !x0 -> state=3:{own=xN,sent=x0}
3c Received UPDATE(x0) -> send(x1); state=2:{own=x1,sent=x1}
3d+ Received UPDATE(!x0) -> send(x1); state=2:{own=x1,sent=x1} if winTieBreak
3d- Received UPDATE(!x0) -> state=1:{own=x?,sent=--} if !winTieBreak
Explanation:
We keep track of the local value ('own'), and whether or not we sent a packet
in this turn ('sent' != '--'), and if we did, the last packet which we sent
('sent'). When we proceed to the next turn, 'sent' is set to '--'.
State 1: We have not yet sent a packet this turn (which implies that we
haven't made a local change this turn).
1a. A local change is made. We update our local value, and send this to
the remote side.
1b. A remote change arrives. We don't have any local change ourselves,
so we accept the remote change, and sent it back to confirm.
With both a packet sent and one received, the turn ends, and 'sent'
is set back to '--'.
State 2: We have sent a packet this turn (after making a local change), and
have not changed our local value since (or we have changed it and changed
it back).
2a. A local change is made. We update our local value, but we have already
sent a packet this turn, so we can't report it until the next turn.
2b. A remote change arrives, and it is equal to both our local value and
the value which we sent to the other side this turn.
This remote notification may be a confirmation of our update, or a
coincidental identical remote change.
Either way, the packet acts as a confirmation, and we do not need
to change anything. With both a packet sent and received, the turn ends
and so 'sent' is set back to '--'.
2c. A remote change arrives, and it is not equal to our local value (which
is the same as the value which we sent).
The tie breaker decides which value prevails. The same value will
prevail on the remote side, so there is no need to send any confirmation
packets. The turn ends, and 'sent' is set back to '--'.
State 3: We have sent a packet this turn, and have made a local change since.
3a. A local change is made back to the value which we sent. No action
is required.
3b. A local change is made. We update our local value, but we have already
sent a packet this turn, so we can't report it until the next turn.
3c. A remote change arrives, and it is equal to the value which we sent
(which isn't equal to the current local value).
The sent/received value is the accepted value and the turn ends.
But we have changed our value since already, so as the next turn
starts, we immediately send an update.
3d. A remote change arrives, and it is not equal to the value which we sent
this turn.
+ We win the tie break, so the value which we sent prevails.
But we have changed our value since already, so as the next turn
starts, we immediately send an update.
- We lose the tie break, so the value which the remote value sent
prevails. We accept the value and the turn ends.
(An alternative would be to consider the local change(s) which
we made after our change was sent as made in the following turn,
in which case we would send our current local value in the next turn.
The advantage would be that 3d- would become equal to 3d+,
so these could be joined (with 3c too), which saves a few
if-statements in the code, but this requires another packet to be sent
and replied to in what currently is 3d-.)
Proof outline that this works:
- The states can never get out of sync:
After both a packet has been sent and received, both sides (temporarilly)
have accepted the same value
- There can be no indefinite loop without ongoing local changes.
Once there are no more local changes:
from state 3 we always go to state 2 or 1,
from state 2 we always go to state 1
from state 1 we can only go back to state 1, and only when a packet
has been received.
So eventually, both sides are in states 1. With both sides in state 1,
no packets have been sent in the current turn, so no more packets are
there to be received.
- Both sides can make changes, as long as the side which wins the tie breaks
stops making changes now and then:
Without making local changes, a side will go to state 1 eventually.
If a side makes a local change, and this is received by the other side
while it is in state 1, then that other side will accept the change.
The Confirm negatiation is used to finish the Update negotiation.
This works because local changes triggered by the reception of remote changes
are treated as local modifications for the purpose of the Confirm negotiation.
And this can be done because whenever the Confirm negotiation is in a state
where remote changes may be expected, local modifications are still allowed
(possily after sending a CANCEL packet).
============================================================================
@@ -130,7 +235,7 @@ States:
Handshake messages:
- CONFIRM1 - "the current local configuration OK for me"
- CONFIRM1 - "the current local configuration is OK for me"
- CONFIRM2 - "acknowledging your CONFIRM1; my own configuration is unchanged
since I sent CONFIRM1 (after the last CANCEL)"
- CANCEL - "forget about my earlier CONFIRM1"
@@ -148,7 +253,7 @@ From state 1: (localOk)
local cancel -> Send CANCEL, goto 4
received CONFIRM1 -> Send CONFIRM2, goto 3
received CONFIRM2 -> Send CONFIRM2, goto 8
received MESSAGE(changes) -> Process(changes), Send CANCEL, goto 4
received MESSAGE(changes) -> Send CANCEL, Process(changes), goto 4
From state 2: (remoteOk)
local confirmation -> Send CONFIRM2, goto 3
@@ -204,7 +309,7 @@ aren't expected.
"Reset" negotiation.
See src/sc2code/netplay/proto.c
See src/sc2code/netplay/proto/reset.c
============================================================================
+141 -26
View File
@@ -1,63 +1,178 @@
NetState_unconnected is the initial state. When a connection attempt is made,
the state is set to NetState_connecting.
The state field of a NetConnection is NULL.
== Any connected state ==
Some packets may be sent and received in any state except
NetState_unconnected.
These are: PING, ACK, ABORT, RESET
These are not listed below at each individual state.
NetState_connecting indicates a connection is in progress.
When the connection is established, an INIT packet is sent, the state
is changed to NetState_init and InputFunc is set to DoNetworkInit.
The state field of a NetConnection is a ConnectStateData structure.
Whenever a connection is aborted, the state is returned to
NetState_unconnected. This state transition is not listed below at each
individual state.
== NetState_unconnected ==
NetState_unconnected is the initial state.
NetConnection.state: NULL
Packets ok to send: none
Packets ok to receive: none
Next state:
NetState_connecting -- connection attempt in progress
== NetState_connecting ==
NetState_connecting indicates that a connection is in progress.
When the connection is established, the state is changed to NetState_init
and InputFunc is set to DoNetworkInit.
NetConnection.state: instance of ConnectStateData
Packets ok to send: none
Packets ok to receive: none
Next state:
NetState_init -- connection established
== NetState_init ==
NetState_init is for initialising the connection before actual game
information is sent. When an INIT packet is received, the state is set
to NetState_inSetup and InputFunc is set to DoMelee.
The state field of a NetConnection is a BattleStateData structure.
information is sent.
As this state is entered, an INIT packet is sent. When an INIT packet
has also been received, the state is set to NetState_inSetup and
InputFunc is set to DoMelee.
NetConnection.state: instance of BattleStateData
Packets ok to send: INIT
Packets ok to receive: INIT
Next state:
NetState_inSetup -- received an INIT packet
== NetState_inSetup ==
NetState_inSetup is the state in which the fleet configuration is negotiated.
Only the side who has myTurn set may send this ship information, by means
of FLEET and TEAMNAME packets.
The Turn negotiation is used to change myTurn.
This does not necessarilly mean that the fleet setup screen is visible;
this state is also held after a battle when the battle outcome is still
displayed.
Each side may send fleet configuration changes to the other side, by means of
FLEET and TEAMNAME packets. Agreement on configuration settings is provided
through the Update negotiation.
The Confirm negotiation is used to end this state and go to
NetState_preBattle. At this time InputFunc is set to DoPreMelee.
The state field of a NetConnection is a BattleStateData structure.
NetConnection.state: instance of BattleStateData
Packets ok to send: FLEET, TEAMNAME, HANDSHAKE0, HANDSHAKE1,
HANDSHAKECANCEL, HANDSHAKECANCELACK
Packets ok to receive: FLEET, TEAMNAME, HANDSHAKE0, HANDSHAKE1,
HANDSHAKECANCEL, HANDSHAKECANCELACK
Next state:
NetState_preBattle -- configuration has been confirmed
== NetState_preBattle ==
NetState_preBattle is used for non-interactive battle negotiations.
One side sends the random seed; the other receives it.
Both sides send their input delay value.
The Ready negotiation is used to end this state and go to
NetState_interBattle.
The state field of a NetConnection is a BattleStateData structure.
NetState_interBattle is used to allow either side to do some local
NetConnection.state: instance of BattleStateData
Packets ok to send: SEEDRANDOM, INPUTDELAY, READY
Packets ok to receive: SEEDRANDOM, INPUTDELAY, READY
Next state:
NetState_interBattle -- ready to continue
== NetState_interBattle ==
NetState_interBattle is used to allow each side to do some local
initialisations before moving on.
The Ready negotiation is used to end this state and go to
NetState_selectShip, or if there are no more ships to be selected,
to NetState_inBattle.
The state field of a NetConnection is a BattleStateData structure.
NetState_selectShip, or if all sides have selected a ship, to
NetState_inBattle, or if there are no more ships in a fleet,
to NetState_inSetup.
NetState_selectShip is where a side may select his ship. The other
If there are no more ships, the the Ready negotiation is used to
enter NetState_inSetup.
NetConnection.state: instance of BattleStateData
Packets ok to send: READY
Packets ok to receive: READY
Next state:
NetState_selectShip -- ready to select the next ship
NetState_inBattle -- ready to start the battle
NetState_inSetup -- no more ships; game over
== NetState_selectShip ==
NetState_selectShip is where a side may select their ship. The other
side is waiting for notice of this selection.
As soon as the selection has been sent or received, the state is changed
back to NetState_interBattle.
The state field of a NetConnection is a BattleStateData structure.
NetConnection.state: instance of BattleStateData
Packets ok to send: SELECTSHIP
Packets ok to receive: SELECTSHIP
Next state:
NetState_interBattle -- a selection has been made
== NetState_inBattle ==
NetState_inBattle is where the actual melee takes place.
Both sides send their input until the game is over, at which point
the Ready negotiation is used to end this state and go to
the NetState_endingBattle state. Until the Ready negotiation has been
completed, the simulation is continuing.
The state field of a NetConnection is a BattleStateData structure.
NetConnection.state: instance of BattleStateData
Packets ok to send: BATTLEINPUT, READY
Packets ok to receive: BATTLEINPUT, READY
Next state:
NetState_endingBattle -- ready to end the battle
== NetState_endingBattle ==
NetState_endingBattle is where the local side waits for the remote
battle frame count, after it has sent its own. When it arrives,
the state changes to NetState_endingBattle2.
The state field of a NetConnection is a BattleStateData structure.
NetConnection.state: instance of BattleStateData
Packets ok to send: BATTLEINPUT, FRAMECOUNT
Packets ok to receive: BATTLEINPUT, FRAMECOUNT
Next state:
NetState_endingBattle2 -- we know when to end the battle
== NetState_endingBattle2 ==
NetState_endingBattle2 is where the side with the lowest battle frame count
catches up with the other other side, while the other side waits.
The Ready negotiation is used to signal that each side is ready,
and the state changes back to NetState_interBattle.
The state field of a NetConnection is a BattleStateData structure.
and the state changes to NetState_interBattle.
NetConnection.state: instance of BattleStateData
When a connection is aborted, the state is returned to NetState_unconnected.
Packets ok to send: BATTLEINPUT, READY
Packets ok to receive: BATTLEINPUT, READY
Next state:
NetState_interBattle -- get ready for the next ship
+14 -5
View File
@@ -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);
+1
View File
@@ -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)
{
+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
// 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);
}
+328 -99
View File
@@ -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
{
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))
{
MeleeSetup_setConfirmedShip (setup, side, index, ship);
// State 1
// Change the ship locally.
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.
// 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)
{
// 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))
{
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.
// 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)
{
// 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);
}
}
+9 -5
View File
@@ -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,
+94 -18
View File
@@ -34,6 +34,8 @@ MeleeTeam_init (MeleeTeam *team)
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
team->ships[slotI] = MELEE_NONE;
team->name[0] = '\0';
}
void
@@ -158,7 +160,7 @@ MeleeTeam_getTeamName (const MeleeTeam *team)
// Returns true iff the state has actually changed.
void
MeleeTeam_setName (MeleeTeam *team, const UNICODE *name)
MeleeTeam_setName (MeleeTeam *team, const char *name)
{
strncpy (team->name, name, sizeof team->name - 1);
team->name[sizeof team->name - 1] = '\0';
@@ -196,6 +198,26 @@ MeleeTeam_isEqual (const MeleeTeam *team1, const MeleeTeam *team2)
///////////////////////////////////////////////////////////////////////////
#ifdef NETPLAY
static void
MeleeSetup_initSentTeam (MeleeSetup *setup, size_t teamNr)
{
MeleeTeam *team = &setup->sentTeams[teamNr];
FleetShipIndex slotI;
for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
MeleeTeam_setShip (team, slotI, MELEE_UNSET);
setup->haveSentTeamName[teamNr] = false;
#ifdef DEBUG
// The actual team name should be irrelevant if haveSentTeamName is
// set to false. In a debug build, we set it to invalid, so that
// it is more likely that it will be noticed if it is ever used.
MeleeTeam_setName (team, "<INVALID>");
#endif /* DEBUG */
}
#endif /* NETPLAY */
MeleeSetup *
MeleeSetup_new (void)
{
@@ -209,7 +231,7 @@ MeleeSetup_new (void)
MeleeTeam_init (&result->teams[teamI]);
result->fleetValue[teamI] = 0;
#ifdef NETPLAY
MeleeTeam_init (&result->confirmedTeams[teamI]);
MeleeSetup_initSentTeam (result, teamI);
#endif /* NETPLAY */
}
return result;
@@ -221,6 +243,17 @@ MeleeSetup_delete (MeleeSetup *setup)
HFree (setup);
}
#ifdef NETPLAY
void
MeleeSetup_resetSentTeams (MeleeSetup *setup)
{
size_t teamI;
for (teamI = 0; teamI < NUM_SIDES; teamI++)
MeleeSetup_initSentTeam (setup, teamI);
}
#endif /* NETPLAY */
// Returns true iff the state has actually changed.
bool
MeleeSetup_setShip (MeleeSetup *setup, size_t teamNr, FleetShipIndex slotNr,
@@ -259,10 +292,10 @@ MeleeSetup_getFleet (const MeleeSetup *setup, size_t teamNr)
// Returns true iff the state has actually changed.
bool
MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
const UNICODE *name)
const char *name)
{
MeleeTeam *team = &setup->teams[teamNr];
const UNICODE *oldName = MeleeTeam_getTeamName (team);
const char *oldName = MeleeTeam_getTeamName (team);
if (strcmp (oldName, name) == 0)
return false;
@@ -271,6 +304,8 @@ MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
return true;
}
// NB. This function returns a pointer to a static buffer, which is
// overwritten by calls to MeleeSetup_setTeamName().
const char *
MeleeSetup_getTeamName (const MeleeSetup *setup, size_t teamNr)
{
@@ -301,24 +336,31 @@ MeleeSetup_serializeTeam (const MeleeSetup *setup, size_t teamNr,
#ifdef NETPLAY
MeleeShip
MeleeSetup_getConfirmedShip (const MeleeSetup *setup, size_t teamNr,
MeleeSetup_getSentShip (const MeleeSetup *setup, size_t teamNr,
FleetShipIndex slotNr)
{
return MeleeTeam_getShip (&setup->confirmedTeams[teamNr], slotNr);
return MeleeTeam_getShip (&setup->sentTeams[teamNr], slotNr);
}
// Returns NULL if there is no team name set. This is not the same
// as when an empty (zero-length) team name is set.
// NB. This function returns a pointer to a static buffer, which is
// overwritten by calls to MeleeSetup_setSentTeamName().
const char *
MeleeSetup_getConfirmedTeamName (const MeleeSetup *setup, size_t teamNr)
MeleeSetup_getSentTeamName (const MeleeSetup *setup, size_t teamNr)
{
return MeleeTeam_getTeamName (&setup->confirmedTeams[teamNr]);
if (!setup->haveSentTeamName[teamNr])
return NULL;
return MeleeTeam_getTeamName (&setup->sentTeams[teamNr]);
}
// Returns true iff the state has actually changed.
bool
MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr,
MeleeSetup_setSentShip (MeleeSetup *setup, size_t teamNr,
FleetShipIndex slotNr, MeleeShip ship)
{
MeleeTeam *team = &setup->confirmedTeams[teamNr];
MeleeTeam *team = &setup->sentTeams[teamNr];
MeleeShip oldShip = MeleeTeam_getShip (team, slotNr);
if (ship == oldShip)
@@ -329,28 +371,62 @@ MeleeSetup_setConfirmedShip (MeleeSetup *setup, size_t teamNr,
}
// Returns true iff the state has actually changed.
// 'name' can be NULL to indicate that no team name set. This is not the same
// as when an empty (zero-length) team name is set.
bool
MeleeSetup_setConfirmedTeamName (MeleeSetup *setup, size_t teamNr,
const UNICODE *name)
MeleeSetup_setSentTeamName (MeleeSetup *setup, size_t teamNr,
const char *name)
{
MeleeTeam *team = &setup->confirmedTeams[teamNr];
const UNICODE *oldName = MeleeTeam_getTeamName (team);
bool haveSentName = setup->haveSentTeamName[teamNr];
if (strcmp (oldName, name) == 0)
if (name == NULL)
{
if (!haveSentName)
{
// Had not sent a team name, and still haven't.
return false;
}
#ifdef DEBUG
{
// The actual team name should be irrelevant if haveSentTeamName
// is set to false. In a debug build, we set it to invalid, so
// that it is more likely that it will be noticed if it is ever
// used.
MeleeTeam *team = &setup->sentTeams[teamNr];
MeleeTeam_setName (team, "<INVALID>");
}
#endif
}
else
{
MeleeTeam *team;
if (haveSentName)
{
// Have sent a team name. Check whether it has actually changed.
const char *oldName = MeleeTeam_getTeamName (team);
if (strcmp (oldName, name) == 0)
return false; // Team name has not changed.
}
team = &setup->sentTeams[teamNr];
MeleeTeam_setName (team, name);
}
setup->haveSentTeamName[teamNr] = (name != NULL);
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
+23 -38
View File
@@ -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);
+3
View File
@@ -30,7 +30,10 @@ typedef enum MeleeShip {
MELEE_YEHAT,
MELEE_ZOQFOTPIK,
MELEE_UNSET = ((BYTE) ~0) - 1,
// Used with the Update protocol, to register in the sentTeam
MELEE_NONE = (BYTE) ~0
// Empty fleet position.
} MeleeShip;
#define NUM_MELEE_SHIPS (MELEE_ZOQFOTPIK + 1)
@@ -181,8 +181,6 @@ NetConnection_connectedServerCallback(ListenState *listenState,
// Ignore errors; it's not a big deal. In debug mode, a message
// will already have been printed from the function itself.
conn->stateFlags.myTurn = true;
// The "server" may speak first.
conn->stateFlags.discriminant = true;
NetConnection_connected(conn);
@@ -210,8 +208,6 @@ NetConnection_connectedClientCallback(ConnectState *connectState,
// Ignore errors; it's not a big deal. In debug mode, a message
// will already have been printed from the function itself.
conn->stateFlags.myTurn = false;
// The "server" may speak first.
conn->stateFlags.discriminant = false;
NetConnection_connected(conn);
@@ -129,9 +129,6 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
conn->stateData = NULL;
conn->stateFlags.connected = false;
conn->stateFlags.disconnected = false;
conn->stateFlags.myTurn = false;
conn->stateFlags.endingTurn = false;
conn->stateFlags.pendingTurnChange = false;
conn->stateFlags.discriminant = false;
conn->stateFlags.handshake.localOk = false;
conn->stateFlags.handshake.remoteOk = false;
@@ -350,11 +347,6 @@ NetConnection_isConnected(const NetConnection *conn) {
return conn->stateFlags.connected;
}
bool
NetConnection_isMyTurn(const NetConnection *conn) {
return conn->stateFlags.myTurn;
}
int
NetConnection_getPlayerNr(const NetConnection *conn) {
return conn->player;
+2 -12
View File
@@ -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);
+7 -34
View File
@@ -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();
}
}
+1 -2
View File
@@ -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;
}
+5 -5
View File
@@ -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
+1
View File
@@ -21,6 +21,7 @@
#include "netplay.h"
#include "port.h"
#include "netsend.h"
#include "netconnection.h"
#include "packet.h"
#include "libs/log.h"
@@ -38,7 +38,6 @@ NetStateData netStateData[] = {
DEFINE_NETSTATEDATA(inBattle),
DEFINE_NETSTATEDATA(endingBattle),
DEFINE_NETSTATEDATA(endingBattle2),
DEFINE_NETSTATEDATA(endMelee),
};
void
@@ -36,7 +36,6 @@ typedef enum {
NetState_inBattle, /* Battle has started */
NetState_endingBattle, /* Both sides are prepared to end */
NetState_endingBattle2, /* Waiting for the final synchronisation */
NetState_endMelee, /* Melee ended; remote is not yet in setup */
} NetState;
#include "types.h"
+19 -29
View File
@@ -29,33 +29,31 @@
#include <string.h>
#define DEFINE_PACKETDATA(name, inTurn) \
#define DEFINE_PACKETDATA(name) \
{ \
/* .len = */ sizeof (Packet_##name), \
/* .handler = */ (PacketHandler) PacketHandler_##name, \
/* .name = */ #name, \
/* .inTurn = */ (inTurn) \
}
PacketTypeData packetTypeData[PACKET_NUM] = {
DEFINE_PACKETDATA(Init, false),
DEFINE_PACKETDATA(Ping, false),
DEFINE_PACKETDATA(Ack, false),
DEFINE_PACKETDATA(EndTurn, false),
DEFINE_PACKETDATA(Ready, false),
DEFINE_PACKETDATA(Fleet, true),
DEFINE_PACKETDATA(TeamName, true),
DEFINE_PACKETDATA(Handshake0, false),
DEFINE_PACKETDATA(Handshake1, false),
DEFINE_PACKETDATA(HandshakeCancel, false),
DEFINE_PACKETDATA(HandshakeCancelAck, false),
DEFINE_PACKETDATA(SeedRandom, false),
DEFINE_PACKETDATA(InputDelay, false),
DEFINE_PACKETDATA(SelectShip, false),
DEFINE_PACKETDATA(BattleInput, false),
DEFINE_PACKETDATA(FrameCount, false),
DEFINE_PACKETDATA(Checksum, false),
DEFINE_PACKETDATA(Abort, false),
DEFINE_PACKETDATA(Reset, false),
DEFINE_PACKETDATA(Init),
DEFINE_PACKETDATA(Ping),
DEFINE_PACKETDATA(Ack),
DEFINE_PACKETDATA(Ready),
DEFINE_PACKETDATA(Fleet),
DEFINE_PACKETDATA(TeamName),
DEFINE_PACKETDATA(Handshake0),
DEFINE_PACKETDATA(Handshake1),
DEFINE_PACKETDATA(HandshakeCancel),
DEFINE_PACKETDATA(HandshakeCancelAck),
DEFINE_PACKETDATA(SeedRandom),
DEFINE_PACKETDATA(InputDelay),
DEFINE_PACKETDATA(SelectShip),
DEFINE_PACKETDATA(BattleInput),
DEFINE_PACKETDATA(FrameCount),
DEFINE_PACKETDATA(Checksum),
DEFINE_PACKETDATA(Abort),
DEFINE_PACKETDATA(Reset),
};
static inline void *
@@ -113,14 +111,6 @@ Packet_Ack_create(uint32 id) {
return packet;
}
Packet_EndTurn *
Packet_EndTurn_create(void) {
Packet_EndTurn *packet =
(Packet_EndTurn *) Packet_create(PACKET_ENDTURN, 0);
return packet;
}
Packet_Ready *
Packet_Ready_create(void) {
Packet_Ready *packet = (Packet_Ready *) Packet_create(PACKET_READY, 0);
-9
View File
@@ -25,7 +25,6 @@ typedef enum PacketType {
PACKET_INIT,
PACKET_PING,
PACKET_ACK,
PACKET_ENDTURN,
PACKET_READY,
PACKET_FLEET,
PACKET_TEAMNAME,
@@ -79,7 +78,6 @@ typedef struct {
size_t len; /* Minimal length of a packet of this type */
PacketHandler handler;
const char *name;
bool inTurn; /* Can only be sent if it's this party's turn. */
} PacketTypeData;
extern PacketTypeData packetTypeData[];
@@ -142,12 +140,6 @@ typedef struct {
uint32 id;
} Packet_Ack;
// Used to ask or confirm a turn change.
typedef struct {
PacketHeader header;
// No contents.
} Packet_EndTurn;
// Used to signal that a party is ready to continue.
typedef struct {
PacketHeader header;
@@ -262,7 +254,6 @@ void Packet_delete(Packet *packet);
Packet_Init *Packet_Init_create(void);
Packet_Ping *Packet_Ping_create(uint32 id);
Packet_Ack *Packet_Ack_create(uint32 id);
Packet_EndTurn *Packet_EndTurn_create(void);
Packet_Ready *Packet_Ready_create(void);
Packet_Handshake0 *Packet_Handshake0_create(void);
Packet_Handshake1 *Packet_Handshake1_create(void);
@@ -142,41 +142,6 @@ PacketHandler_Ack(NetConnection *conn, const Packet_Ack *packet) {
return 0;
}
int
PacketHandler_EndTurn(NetConnection *conn, const Packet_EndTurn *packet) {
if (!testNetState(conn->state > NetState_init &&
!conn->stateFlags.pendingTurnChange, PACKET_ENDTURN))
return -1; // errno is set
if (conn->stateFlags.endingTurn) {
// This was the confirmation we were waiting for.
// NB. A remote request while we had sent a request serves as
// a confirmation.
conn->stateFlags.myTurn = !conn->stateFlags.myTurn;
conn->stateFlags.endingTurn = false;
} else {
// The other party wants to change whose turn it is.
// If it would become the other party's turn, we wait until
// the queue is flushed to actually carry out the turn change,
// and send the confirmation. I we wouldn't do that, and we still
// had some data to send in our own turn, we would end up asking
// for our turn back, and the turn would keep changing without
// any progress being made.
if (conn->stateFlags.myTurn) {
// Schedule the turn change until after the next queue flush.
conn->stateFlags.pendingTurnChange = true;
} else {
conn->stateFlags.myTurn = true;
sendEndTurn(conn);
}
}
(void) packet;
// Its contents is not interesting.
return 0;
}
// Convert the side indication relative to a remote party to
// a local player number.
static inline int
@@ -189,17 +154,6 @@ localSide(NetConnection *conn, NetplaySide side) {
return 1 - conn->player;
}
static bool
checkYourTurn(NetConnection *conn, PacketType type) {
if (conn->stateFlags.myTurn) {
log_add(log_Warning, "Packet of type '%s' received in an "
"inappropriate turn.", packetTypeData[type].name);
errno = EBADMSG;
return false;
}
return true;
}
int
PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) {
if (conn->stateFlags.reset.localReset)
@@ -239,11 +193,6 @@ PacketHandler_Fleet(NetConnection *conn, const Packet_Fleet *packet) {
if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
return -1; // errno is set
if (!checkYourTurn(conn, PACKET_FLEET)) {
// errno is set
return -1;
}
player = localSide(conn, (NetplaySide) packet->side);
len = packetLength((const Packet *) packet);
@@ -304,14 +253,9 @@ PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
return -1;
}
if (!testNetState(conn->state == NetState_inSetup, PACKET_TEAMNAME))
if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
return -1; // errno is set
if (!checkYourTurn(conn, PACKET_TEAMNAME)) {
// errno is set
return -1;
}
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
if (conn->stateFlags.handshake.localOk) {
@@ -503,8 +447,7 @@ PacketHandler_InputDelay(NetConnection *conn,
return -1;
}
if (!testNetState(conn->state == NetState_preBattle,
PACKET_INPUTDELAY))
if (!testNetState(conn->state == NetState_preBattle, PACKET_INPUTDELAY))
return -1; // errno is set
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
@@ -28,7 +28,6 @@
DECLARE_PACKETHANDLER(Init);
DECLARE_PACKETHANDLER(Ping);
DECLARE_PACKETHANDLER(Ack);
DECLARE_PACKETHANDLER(EndTurn);
DECLARE_PACKETHANDLER(Ready);
DECLARE_PACKETHANDLER(Fleet);
DECLARE_PACKETHANDLER(TeamName);
+1 -60
View File
@@ -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->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;
}
+2 -19
View File
@@ -37,30 +37,13 @@ struct PacketQueue {
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);
+19 -37
View File
@@ -29,7 +29,7 @@ sendInit(NetConnection *conn) {
Packet_Init *packet;
packet = Packet_Init_create();
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -37,7 +37,7 @@ sendPing(NetConnection *conn, uint32 id) {
Packet_Ping *packet;
packet = Packet_Ping_create(id);
queuePacket(conn, (Packet *) packet, true);
queuePacket(conn, (Packet *) packet);
}
void
@@ -45,15 +45,7 @@ sendAck(NetConnection *conn, uint32 id) {
Packet_Ack *packet;
packet = Packet_Ack_create(id);
queuePacket(conn, (Packet *) packet, true);
}
void
sendEndTurn(NetConnection *conn) {
Packet_EndTurn *packet;
packet = Packet_EndTurn_create();
queuePacket(conn, (Packet *) packet, true);
queuePacket(conn, (Packet *) packet);
}
void
@@ -61,17 +53,7 @@ sendReady(NetConnection *conn) {
Packet_Ready *packet;
packet = Packet_Ready_create();
queuePacket(conn, (Packet *) packet, false);
}
// Bypass the packet queue.
// Should only be called from the packet queue functions themselves.
int
sendEndTurnDirect(NetConnection *conn) {
Packet_EndTurn *packet;
packet = Packet_EndTurn_create();
return sendPacket(conn, (Packet *) packet);
queuePacket(conn, (Packet *) packet);
}
void
@@ -79,7 +61,7 @@ sendHandshake0(NetConnection *conn) {
Packet_Handshake0 *packet;
packet = Packet_Handshake0_create();
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -87,7 +69,7 @@ sendHandshake1(NetConnection *conn) {
Packet_Handshake1 *packet;
packet = Packet_Handshake1_create();
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -95,7 +77,7 @@ sendHandshakeCancel(NetConnection *conn) {
Packet_HandshakeCancel *packet;
packet = Packet_HandshakeCancel_create();
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -103,7 +85,7 @@ sendHandshakeCancelAck(NetConnection *conn) {
Packet_HandshakeCancelAck *packet;
packet = Packet_HandshakeCancelAck_create();
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -112,7 +94,7 @@ sendTeamName(NetConnection *conn, NetplaySide side, const char *name,
Packet_TeamName *packet;
packet = Packet_TeamName_create(side, name, len);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -128,7 +110,7 @@ sendFleet(NetConnection *conn, NetplaySide side, const MeleeShip *ships,
packet->ships[i].ship = (uint8) ships[i];
}
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -141,7 +123,7 @@ sendFleetShip(NetConnection *conn, NetplaySide side,
packet->ships[0].index = (uint8) shipIndex;
packet->ships[0].ship = (uint8) ship;
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -149,7 +131,7 @@ sendSeedRandom(NetConnection *conn, uint32 seed) {
Packet_SeedRandom *packet;
packet = Packet_SeedRandom_create(seed);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -157,7 +139,7 @@ sendInputDelay(NetConnection *conn, uint32 delay) {
Packet_InputDelay *packet;
packet = Packet_InputDelay_create(delay);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -165,7 +147,7 @@ sendSelectShip(NetConnection *conn, FleetShipIndex index) {
Packet_SelectShip *packet;
packet = Packet_SelectShip_create((uint16) index);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -173,7 +155,7 @@ sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
Packet_BattleInput *packet;
packet = Packet_BattleInput_create((uint8) input);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -181,7 +163,7 @@ sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount) {
Packet_FrameCount *packet;
packet = Packet_FrameCount_create((uint32) frameCount);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
#ifdef NETPLAY_CHECKSUM
@@ -191,7 +173,7 @@ sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
Packet_Checksum *packet;
packet = Packet_Checksum_create((uint32) frameNr, (uint32) checksum);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
#endif
@@ -200,7 +182,7 @@ sendAbort(NetConnection *conn, NetplayAbortReason reason) {
Packet_Abort *packet;
packet = Packet_Abort_create((uint16) reason);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
void
@@ -208,7 +190,7 @@ sendReset(NetConnection *conn, NetplayResetReason reason) {
Packet_Reset *packet;
packet = Packet_Reset_create((uint16) reason);
queuePacket(conn, (Packet *) packet, false);
queuePacket(conn, (Packet *) packet);
}
@@ -34,9 +34,7 @@
void sendInit(NetConnection *conn);
void sendPing(NetConnection *conn, uint32 id);
void sendAck(NetConnection *conn, uint32 id);
void sendEndTurn(NetConnection *conn);
void sendReady(NetConnection *conn);
int sendEndTurnDirect(NetConnection *conn);
void sendHandshake0(NetConnection *conn);
void sendHandshake1(NetConnection *conn);
void sendHandshakeCancel(NetConnection *conn);
@@ -82,7 +82,6 @@ Netplay_connectionReset(NetConnection *conn, NetplayResetReason reason,
case NetState_inBattle:
case NetState_endingBattle:
case NetState_endingBattle2:
case NetState_endMelee:
resetFeedback(conn, reason, byRemote);
break;
}
+7 -15
View File
@@ -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;
@@ -662,9 +653,14 @@ MeleeGameOver (void)
for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
DrawPickMeleeFrame (playerI);
TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
UnlockMutex (GraphicsLock);
#ifdef NETPLAY
negotiateReadyConnections(true, NetState_inSetup);
#endif
TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
PressState = PulsedInputState.menu[KEY_MENU_SELECT] ||
PulsedInputState.menu[KEY_MENU_CANCEL];
do
@@ -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)
{
+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
* 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;
+2 -2
View File
@@ -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,