Better Netplay reset and abort handling. Some small other fixes.

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2571 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
meep-eep
2006-12-06 03:39:12 +00:00
parent 66a9b84837
commit 7958a9e87f
32 changed files with 815 additions and 160 deletions
+1
View File
@@ -1,4 +1,5 @@
Changes towards version 0.6: Changes towards version 0.6:
- Better abort and disconnect handling for Netplay - SvdB
- Menu sounds in Setup track rest of game (#922), from Nic - Michael - Menu sounds in Setup track rest of game (#922), from Nic - Michael
- Shifted the Mouse Error to a Popup Window, moved the message to - Shifted the Mouse Error to a Popup Window, moved the message to
starcon.txt for translators - Michael starcon.txt for translators - Michael
+35 -3
View File
@@ -2261,8 +2261,8 @@ Awaiting incoming connection
#(Attempting outgoing connection...) #(Attempting outgoing connection...)
Attempting outgoing connection Attempting outgoing connection
#(Connected. Press RIGHT to disconnect.) #(Connected. Change the control method to disconnect.)
Connected. Change setting to disconnect. Connected. Change the control method to disconnect.
#(Confirmation cancelled. Press FIRE to reconfirm.) #(Confirmation cancelled. Press FIRE to reconfirm.)
@@ -2324,20 +2324,52 @@ Top player changed something -- need to reconfirm.
#(Press SPACE to cancel) #(Press SPACE to cancel)
Press SPACE to cancel Press SPACE to cancel
#(Connect to remote host) #(Connect to remote host)
Connect to remote host Connect to remote host
#(Wait for incoming connection) #(Wait for incoming connection)
Wait for incoming connection Wait for incoming connection
#(Cancel) #(Cancel)
Cancel Cancel
#(Host) #(Host)
Host Host
#(Port) #(Port)
Port Port
#(Net Delay) #(Net Delay)
Net Delay Net Delay
#(Disconnect for an unspecified reason.)
Disconnect for an unspecified reason.
#(Connection aborted due to version mismatch.)
Connection aborted due to version mismatch.
#(Connection aborted due to an internal protocol error.)
Connection aborted due to an internal protocol error.
#(Game aborted for an unspecified reason.)
Game aborted for an unspecified reason.
#(Game aborted due to loss of synchronisation.)
Game aborted due to loss of synchronisation.
#(Game aborted by the remote player.)
Game aborted by the remote player.
+10 -2
View File
@@ -1,4 +1,4 @@
There are three types of negotiations used to synchronised the parties There are several types of negotiations used to synchronised the parties
of a network connection. of a network connection.
- Continue when we know the other is ready ("Ready") - Continue when we know the other is ready ("Ready")
@@ -11,7 +11,8 @@ of a network connection.
This is used to end a state where both parties are modifying This is used to end a state where both parties are modifying
common data. Both parties have to agree with the data for either common data. Both parties have to agree with the data for either
party to continue. party to continue.
- Reset a connection. This is used to abort a game in progress and return
to the fleet setup menu.
============================================================================ ============================================================================
@@ -199,6 +200,13 @@ I also check whether it is possible for packets to arrive that
aren't expected. aren't expected.
============================================================================
"Reset" negotiation.
See src/sc2code/netplay/proto.c
============================================================================ ============================================================================
Battle ending negotiation. Battle ending negotiation.
-3
View File
@@ -117,9 +117,6 @@ To put in the announcement of Netplay:
Bugs and todos unrelated to netplay. Bugs and todos unrelated to netplay.
- When you insert a new ship, the cursor moves to the next square,
but the picture at the right doesn't change.
- The "Battle!" icon is not positioned correctly.
- other player being able to choose the next ship after 3 seconds - other player being able to choose the next ship after 3 seconds
of inactivity of inactivity
- DoRunAway() shouldn't be handled in ProcessInput() - DoRunAway() shouldn't be handled in ProcessInput()
+8
View File
@@ -2604,6 +2604,14 @@ SOURCE=..\sc2code\netplay\proto\ready.c
SOURCE=..\sc2code\netplay\proto\ready.h SOURCE=..\sc2code\netplay\proto\ready.h
# End Source File # End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\proto\reset.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\proto\reset.h
# End Source File
# End Group # End Group
# End Group # End Group
+43 -11
View File
@@ -47,6 +47,12 @@ size_t battleInputOrder[NUM_SIDES];
#ifdef NETPLAY #ifdef NETPLAY
BattleFrameCounter battleFrameCount; BattleFrameCounter battleFrameCount;
// Used for synchronisation purposes during netplay. // Used for synchronisation purposes during netplay.
COUNT currentDeadSide;
// When a ship has been destroyed, each side of a network
// connection waits until the other side is ready.
// When two ships die at the same time, this is handled for one
// ship after the other. This variable indicate for which player
// we're currently doing this.
#endif #endif
static BOOLEAN static BOOLEAN
@@ -158,7 +164,7 @@ ProcessInput (void)
JournalInput (InputState); JournalInput (InputState);
#endif /* CREATE_JOURNAL */ #endif /* CREATE_JOURNAL */
#ifdef NETPLAY #ifdef NETPLAY
if (PlayerControl[cur_player] & HUMAN_CONTROL) if (!(PlayerControl[cur_player] & NETWORK_CONTROL))
{ {
BattleInputBuffer *bib = getBattleInputBuffer(cur_player); BattleInputBuffer *bib = getBattleInputBuffer(cur_player);
sendBattleInputConnections (InputState); sendBattleInputConnections (InputState);
@@ -278,8 +284,11 @@ DoBattle (BATTLE_STATE *bs)
if (battleFrameCount >= delay if (battleFrameCount >= delay
&& (battleFrameCount - delay) % NETPLAY_CHECKSUM_INTERVAL == 0) && (battleFrameCount - delay) % NETPLAY_CHECKSUM_INTERVAL == 0)
{ {
if (!verifyChecksums (battleFrameCount - delay)) if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
GLOBAL(CurrentActivity) |= CHECK_ABORT; if (!verifyChecksums (battleFrameCount - delay)) {
GLOBAL(CurrentActivity) |= CHECK_ABORT;
resetConnections(ResetReason_syncLoss);
}
} }
} }
#endif #endif
@@ -440,23 +449,31 @@ Battle (void)
CountLinks (&race_q[1]) CountLinks (&race_q[1])
); );
if (!selectAllShips (num_ships))
goto AbortBattle;
BattleSong (TRUE);
bs.NextTime = 0;
setupBattleInputOrder (); setupBattleInputOrder ();
#ifdef NETPLAY #ifdef NETPLAY
initBattleInputBuffers (); initBattleInputBuffers ();
#ifdef NETPLAY_CHECKSUM #ifdef NETPLAY_CHECKSUM
initChecksumBuffers (); initChecksumBuffers ();
#endif #endif /* NETPLAY_CHECKSUM */
battleFrameCount = 0; battleFrameCount = 0;
currentDeadSide = (COUNT)~0;
#endif /* NETPLAY */
if (!selectAllShips (num_ships)) {
GLOBAL (CurrentActivity) |= CHECK_ABORT;
goto AbortBattle;
}
BattleSong (TRUE);
bs.NextTime = 0;
#ifdef NETPLAY
initBattleStateDataConnections (); initBattleStateDataConnections ();
{ {
bool allOk = negotiateReadyConnections (true, NetState_inBattle); bool allOk = negotiateReadyConnections (true, NetState_inBattle);
if (!allOk) if (!allOk) {
GLOBAL (CurrentActivity) |= CHECK_ABORT;
goto AbortBattle; goto AbortBattle;
}
} }
#endif /* NETPLAY */ #endif /* NETPLAY */
bs.InputFunc = DoBattle; bs.InputFunc = DoBattle;
@@ -468,11 +485,26 @@ Battle (void)
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
AbortBattle: AbortBattle:
if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE &&
(GLOBAL (CurrentActivity) & CHECK_ABORT))
{
// Do not return to the main menu when a game is aborted,
// (just to the supermelee menu).
UnlockMutex (GraphicsLock);
waitResetConnections(NetState_inSetup);
// A connection may already be in inSetup (set from
// GetMeleeStarship). This is not a problem, although
// it will generate a warning in debug mode.
LockMutex (GraphicsLock);
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
}
#ifdef NETPLAY #ifdef NETPLAY
uninitBattleInputBuffers(); uninitBattleInputBuffers();
#ifdef NETPLAY_CHECKSUM #ifdef NETPLAY_CHECKSUM
uninitChecksumBuffers (); uninitChecksumBuffers ();
#endif #endif /* NETPLAY_CHECKSUM */
#endif /* NETPLAY */ #endif /* NETPLAY */
StopMusic (); StopMusic ();
+1
View File
@@ -25,6 +25,7 @@ extern BOOLEAN instantVictory;
#ifdef NETPLAY #ifdef NETPLAY
typedef DWORD BattleFrameCounter; typedef DWORD BattleFrameCounter;
extern BattleFrameCounter battleFrameCount; extern BattleFrameCounter battleFrameCount;
extern COUNT currentDeadSide;
#endif #endif
extern BOOLEAN Battle (void); extern BOOLEAN Battle (void);
+2
View File
@@ -95,6 +95,7 @@ typedef struct state
} STATE; } STATE;
typedef STATE *PSTATE; typedef STATE *PSTATE;
// Any physical object in the simulation.
typedef struct element typedef struct element
{ {
HELEMENT pred, succ; HELEMENT pred, succ;
@@ -116,6 +117,7 @@ typedef struct element
STATE current, next; STATE current, next;
PVOID pParent; PVOID pParent;
// The ship this element belongs to.
HELEMENT hTarget; HELEMENT hTarget;
} ELEMENT; } ELEMENT;
typedef ELEMENT *PELEMENT; typedef ELEMENT *PELEMENT;
+1 -1
View File
@@ -923,7 +923,7 @@ enum
START_INTERPLANETARY = MAKE_WORD (0, (1 << 3)), START_INTERPLANETARY = MAKE_WORD (0, (1 << 3)),
CHECK_LOAD = MAKE_WORD (0, (1 << 4)), CHECK_LOAD = MAKE_WORD (0, (1 << 4)),
CHECK_RESTART = MAKE_WORD (0, (1 << 5)), CHECK_RESTART = MAKE_WORD (0, (1 << 5)),
CHECK_ABORT = MAKE_WORD (0, (1 << 6)) CHECK_ABORT = MAKE_WORD (0, (1 << 6)),
}; };
typedef UWORD ACTIVITY; typedef UWORD ACTIVITY;
+197 -77
View File
@@ -575,6 +575,7 @@ DrawPickIcon (COUNT iship, BYTE DrawErase)
#ifdef NETPLAY #ifdef NETPLAY
// This function is generic. It should probably be moved to elsewhere. // This function is generic. It should probably be moved to elsewhere.
// The caller should hold the GraphicsLock.
static void static void
multiLineDrawText(TEXT *textIn, RECT *clipRect) { multiLineDrawText(TEXT *textIn, RECT *clipRect) {
RECT oldRect; RECT oldRect;
@@ -701,6 +702,7 @@ UpdateMeleeStatusMessage (ssize_t player)
#endif /* NETPLAY */ #endif /* NETPLAY */
// XXX: this function is called when the current selection is blinking off. // XXX: this function is called when the current selection is blinking off.
// The caller should hold the GraphicsLock.
static void static void
Deselect (BYTE opt) Deselect (BYTE opt)
{ {
@@ -760,6 +762,7 @@ Deselect (BYTE opt)
} }
// XXX: this function is called when the current selection is blinking off. // XXX: this function is called when the current selection is blinking off.
// The caller should hold the GraphicsLock.
static void static void
Select (BYTE opt) Select (BYTE opt)
{ {
@@ -2303,7 +2306,6 @@ DoConnectingDialog (PMELEE_STATE pMS)
if ((status == NetState_init) || if ((status == NetState_init) ||
(status == NetState_inSetup)) (status == NetState_inSetup))
{ {
RECT r;
/* Connection complete! */ /* Connection complete! */
PlayerControl[which_side] = NETWORK_CONTROL | STANDARD_RATING; PlayerControl[which_side] = NETWORK_CONTROL | STANDARD_RATING;
SetPlayerInput (); SetPlayerInput ();
@@ -2349,7 +2351,8 @@ check_for_disconnections (PMELEE_STATE pMS)
{ {
PlayerControl[player] = HUMAN_CONTROL & STANDARD_RATING; PlayerControl[player] = HUMAN_CONTROL & STANDARD_RATING;
DrawControls (player, FALSE); DrawControls (player, FALSE);
log_add (log_User, "Player %d has disconnected; shifting controls\n", player); log_add (log_User, "Player %d has disconnected; shifting "
"controls\n", player);
changed = TRUE; changed = TRUE;
} }
} }
@@ -2358,6 +2361,8 @@ check_for_disconnections (PMELEE_STATE pMS)
{ {
SetPlayerInput (); SetPlayerInput ();
} }
(void) pMS;
} }
#endif #endif
@@ -2852,6 +2857,88 @@ InitPreBuilt (PMELEE_STATE pMS)
} }
} }
int
LoadMeleeConfig (PMELEE_STATE pMS)
{
uio_Stream *load_fp;
int status;
load_fp = res_OpenResFile (configDir, "melee.cfg", "rb");
if (!load_fp)
goto err;
if (LengthResFile (load_fp) != (1 + sizeof (TEAM_IMAGE)) * 2)
goto err;
status = GetResFileChar (load_fp);
if (status == -1)
goto err;
PlayerControl[0] = (BYTE)status;
status = ReadTeamImage (&pMS->TeamImage[0], load_fp);
if (status == -1)
goto err;
status = GetResFileChar (load_fp);
if (status == -1)
goto err;
PlayerControl[1] = (BYTE)status;
status = ReadTeamImage (&pMS->TeamImage[1], load_fp);
if (status == -1)
goto err;
res_CloseResFile (load_fp);
/* Do not allow netplay mode at the start. */
if (PlayerControl[0] & NETWORK_CONTROL)
PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
if (PlayerControl[1] & NETWORK_CONTROL)
PlayerControl[1] = HUMAN_CONTROL | STANDARD_RATING;
return 0;
err:
if (load_fp)
res_CloseResFile (load_fp);
return -1;
}
int
WriteMeleeConfig (PMELEE_STATE pMS)
{
uio_Stream *save_fp;
save_fp = res_OpenResFile (configDir, "melee.cfg", "wb");
if (!save_fp)
goto err;
if (PutResFileChar (PlayerControl[0], save_fp) == -1)
goto err;
if (WriteTeamImage (&pMS->TeamImage[0], save_fp) == 0)
goto err;
if (PutResFileChar (PlayerControl[1], save_fp) == -1)
goto err;
if (WriteTeamImage (&pMS->TeamImage[1], save_fp) == 0)
goto err;
if (!res_CloseResFile (save_fp))
goto err;
return 0;
err:
if (save_fp)
{
res_CloseResFile (save_fp);
DeleteResFile (configDir, "melee.cfg");
}
return -1;
}
void void
Melee (void) Melee (void)
{ {
@@ -2880,49 +2967,12 @@ Melee (void)
GameSounds = CaptureSound (LoadSound (GAME_SOUNDS)); GameSounds = CaptureSound (LoadSound (GAME_SOUNDS));
LoadMeleeInfo (&MenuState); LoadMeleeInfo (&MenuState);
if (LoadMeleeConfig (&MenuState) == -1)
{ {
uio_Stream *load_fp; PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
MenuState.TeamImage[0] = MenuState.PreBuiltList[0];
load_fp = res_OpenResFile (configDir, "melee.cfg", "rb"); PlayerControl[1] = COMPUTER_CONTROL | STANDARD_RATING;
if (load_fp) MenuState.TeamImage[1] = MenuState.PreBuiltList[1];
{
int status;
if (LengthResFile (load_fp) != (1 + sizeof (TEAM_IMAGE)) * 2)
status = -1;
else if ((status = GetResFileChar (load_fp)) != -1)
{
PlayerControl[0] = (BYTE)status;
status = ReadTeamImage (&MenuState.TeamImage[0], load_fp);
if (status != -1)
{
status = GetResFileChar (load_fp);
if (status != -1)
{
PlayerControl[1] = (BYTE)status;
status = ReadTeamImage (
&MenuState.TeamImage[1], load_fp);
}
}
}
res_CloseResFile (load_fp);
if (status == -1)
load_fp = 0;
}
if (load_fp == 0)
{
PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
MenuState.TeamImage[0] = MenuState.PreBuiltList[0];
PlayerControl[1] = COMPUTER_CONTROL | STANDARD_RATING;
MenuState.TeamImage[1] = MenuState.PreBuiltList[1];
}
/* Do not allow netplay mode at the start. */
if (PlayerControl[0] & NETWORK_CONTROL)
PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
if (PlayerControl[1] & NETWORK_CONTROL)
PlayerControl[1] = HUMAN_CONTROL | STANDARD_RATING;
} }
SetPlayerInput (); SetPlayerInput ();
teamStringChanged (&MenuState, 0); teamStringChanged (&MenuState, 0);
@@ -2939,35 +2989,7 @@ Melee (void)
StopMusic (); StopMusic ();
WaitForSoundEnd (TFBSOUND_WAIT_ALL); WaitForSoundEnd (TFBSOUND_WAIT_ALL);
{ WriteMeleeConfig (&MenuState);
uio_Stream *save_fp;
BOOLEAN err;
err = FALSE;
save_fp = res_OpenResFile (configDir, "melee.cfg", "wb");
if (save_fp)
{
if (PutResFileChar (PlayerControl[0], save_fp) == -1)
err = TRUE;
if (!err && WriteTeamImage (&MenuState.TeamImage[0],
save_fp) == 0)
err = TRUE;
if (!err && PutResFileChar (PlayerControl[1], save_fp) == -1)
err = TRUE;
if (!err && WriteTeamImage (&MenuState.TeamImage[1],
save_fp) == 0)
err = TRUE;
if (res_CloseResFile (save_fp) == 0)
err = TRUE;
}
else
err = TRUE;
if (err)
{
DeleteResFile (configDir, "melee.cfg");
}
}
FreeMeleeInfo (&MenuState); FreeMeleeInfo (&MenuState);
DestroySound (ReleaseSound (GameSounds)); DestroySound (ReleaseSound (GameSounds));
GameSounds = 0; GameSounds = 0;
@@ -3083,6 +3105,7 @@ updateTeamName (PMELEE_STATE pMS, COUNT side, const char *name,
strncpy (pMS->TeamImage[side].TeamName, name, len); strncpy (pMS->TeamImage[side].TeamName, name, len);
pMS->TeamImage[side].TeamName[len] = '\0'; pMS->TeamImage[side].TeamName[len] = '\0';
LockMutex (GraphicsLock);
#if 0 /* DTSHS_REPAIR does not combine with other options */ #if 0 /* DTSHS_REPAIR does not combine with other options */
if (pMS->MeleeOption == EDIT_MELEE && pMS->side == side if (pMS->MeleeOption == EDIT_MELEE && pMS->side == side
&& pMS->row == NUM_MELEE_ROWS) && pMS->row == NUM_MELEE_ROWS)
@@ -3090,6 +3113,7 @@ updateTeamName (PMELEE_STATE pMS, COUNT side, const char *name,
else else
#endif #endif
DrawTeamString (pMS, side, DTSHS_REPAIR); DrawTeamString (pMS, side, DTSHS_REPAIR);
UnlockMutex (GraphicsLock);
} }
// Update a ship in a fleet as specified by a remote party. // Update a ship in a fleet as specified by a remote party.
@@ -3131,6 +3155,7 @@ updateFleetShip (PMELEE_STATE pMS, COUNT side, COUNT index, BYTE ship)
(pMS->side == side) && (index == selectedShipIndex); (pMS->side == side) && (index == selectedShipIndex);
// Ship to be updated is the currently selected one. // Ship to be updated is the currently selected one.
LockMutex (GraphicsLock);
if (ship == MELEE_NONE) if (ship == MELEE_NONE)
{ {
RECT r; RECT r;
@@ -3148,12 +3173,14 @@ updateFleetShip (PMELEE_STATE pMS, COUNT side, COUNT index, BYTE ship)
// Reprint the team value: // Reprint the team value:
//DrawTeamString (pMeleeState, side, DTSHS_NORMAL); //DrawTeamString (pMeleeState, side, DTSHS_NORMAL);
DrawTeamString (pMS, side, DTSHS_REPAIR); DrawTeamString (pMS, side, DTSHS_REPAIR);
UnlockMutex (GraphicsLock);
return true; return true;
} }
void void
updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed) { updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed)
{
TFB_SeedRandom (seed); TFB_SeedRandom (seed);
(void) pMS; (void) pMS;
(void) side; (void) side;
@@ -3161,51 +3188,144 @@ updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed) {
// The remote player has done something which invalidates our confirmation. // The remote player has done something which invalidates our confirmation.
void void
confirmationCancelled(PMELEE_STATE pMS, COUNT side) { confirmationCancelled(PMELEE_STATE pMS, COUNT side)
{
LockMutex (GraphicsLock);
if (side == 0) if (side == 0)
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 16)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 16));
// "Bottom player changed something -- need to reconfirm." // "Bottom player changed something -- need to reconfirm."
else else
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 17)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 17));
// "Top player changed something -- need to reconfirm." // "Top player changed something -- need to reconfirm."
UnlockMutex (GraphicsLock);
if (pMS->InputFunc == DoConfirmSettings) if (pMS->InputFunc == DoConfirmSettings)
pMS->InputFunc = DoMelee; pMS->InputFunc = DoMelee;
} }
void void
connectedFeedback (PMELEE_STATE pMS, COUNT side) { connectedFeedback (PMELEE_STATE pMS, COUNT side) {
LockMutex (GraphicsLock);
if (side == 0) if (side == 0)
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 8)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 8));
// "Bottom player is connected." // "Bottom player is connected."
else else
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 9)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 9));
// "Top player is connected." // "Top player is connected."
UnlockMutex (GraphicsLock);
PlayMenuSound (MENU_SOUND_INVOKED); PlayMenuSound (MENU_SOUND_INVOKED);
(void) pMS; (void) pMS;
} }
const char *
abortReasonString (NetplayResetReason reason)
{
switch (reason)
{
case AbortReason_unspecified:
return GAME_STRING (NETMELEE_STRING_BASE + 25);
// "Disconnect for an unspecified reason.'
case AbortReason_versionMismatch:
return GAME_STRING (NETMELEE_STRING_BASE + 26);
// "Connection aborted due to version mismatch."
case AbortReason_protocolError:
return GAME_STRING (NETMELEE_STRING_BASE + 27);
// "Connection aborted due to an internal protocol "
// "error."
}
return NULL;
// Should not happen.
}
void void
errorFeedback (PMELEE_STATE pMS, COUNT side) { abortFeedback (COUNT side, NetplayAbortReason reason)
{
const char *msg;
msg = abortReasonString (reason);
if (msg != NULL)
{
LockMutex (GraphicsLock);
DrawMeleeStatusMessage (msg);
UnlockMutex (GraphicsLock);
}
(void) side;
}
const char *
resetReasonString (NetplayResetReason reason)
{
switch (reason)
{
case ResetReason_unspecified:
return GAME_STRING (NETMELEE_STRING_BASE + 28);
// "Game aborted for an unspecified reason."
case ResetReason_syncLoss:
return GAME_STRING (NETMELEE_STRING_BASE + 29);
// "Game aborted due to loss of synchronisation."
case ResetReason_manualReset:
return GAME_STRING (NETMELEE_STRING_BASE + 30);
// "Game aborted by the remote player."
}
return NULL;
// Should not happen.
}
void
resetFeedback (COUNT side, NetplayResetReason reason, bool byRemote)
{
const char *msg;
GLOBAL (CurrentActivity) |= CHECK_ABORT;
if (reason == ResetReason_manualReset && !byRemote) {
// No message needed, the player initiated the reset.
return;
}
msg = resetReasonString (reason);
if (msg != NULL)
{
LockMutex (GraphicsLock);
DrawMeleeStatusMessage (msg);
UnlockMutex (GraphicsLock);
}
(void) side;
}
void
errorFeedback (PMELEE_STATE pMS, COUNT side)
{
LockMutex (GraphicsLock);
if (side == 0) if (side == 0)
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 10)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 10));
// "Bottom player: connection failed." // "Bottom player: connection failed."
else else
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 11)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 11));
// "Top player: connection failed." // "Top player: connection failed."
UnlockMutex (GraphicsLock);
(void) pMS; (void) pMS;
} }
void void
closeFeedback (PMELEE_STATE pMS, COUNT side) { closeFeedback (PMELEE_STATE pMS, COUNT side)
{
LockMutex (GraphicsLock);
if (side == 0) if (side == 0)
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 12)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 12));
// "Bottom player: connection closed." // "Bottom player: connection closed."
else else
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 13)); DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 13));
// "Top player: connection closed." // "Top player: connection closed."
UnlockMutex (GraphicsLock);
(void) pMS; (void) pMS;
} }
#endif /* NETPLAY */ #endif /* NETPLAY */
+4
View File
@@ -24,6 +24,8 @@
#include "libs/gfxlib.h" #include "libs/gfxlib.h"
#include "libs/sndlib.h" #include "libs/sndlib.h"
#include "libs/reslib.h" #include "libs/reslib.h"
#include "netplay/packet.h"
// for NetplayAbortREason and NetplayResetReason.
typedef struct melee_state MELEE_STATE; typedef struct melee_state MELEE_STATE;
@@ -123,6 +125,8 @@ bool updateFleetShip (PMELEE_STATE pMS, COUNT side, COUNT index, BYTE ship);
void updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed); void updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed);
void confirmationCancelled(PMELEE_STATE pMS, COUNT side); void confirmationCancelled(PMELEE_STATE pMS, COUNT side);
void connectedFeedback (PMELEE_STATE pMS, COUNT side); void connectedFeedback (PMELEE_STATE pMS, COUNT side);
void abortFeedback (COUNT side, NetplayAbortReason reason);
void resetFeedback (COUNT side, NetplayResetReason reason, bool byRemote);
void errorFeedback (PMELEE_STATE pMS, COUNT side); void errorFeedback (PMELEE_STATE pMS, COUNT side);
void closeFeedback (PMELEE_STATE pMS, COUNT side); void closeFeedback (PMELEE_STATE pMS, COUNT side);
+38 -5
View File
@@ -47,7 +47,8 @@ NetConnection *
NetConnection_open(int player, const NetplayPeerOptions *options, NetConnection_open(int player, const NetplayPeerOptions *options,
NetConnection_ConnectCallback connectCallback, NetConnection_ConnectCallback connectCallback,
NetConnection_CloseCallback closeCallback, NetConnection_CloseCallback closeCallback,
NetConnection_ErrorCallback errorCallback, void *extra) { NetConnection_ErrorCallback errorCallback,
NetConnection_DeleteCallback deleteCallback, void *extra) {
NetConnection *conn; NetConnection *conn;
conn = malloc(sizeof (NetConnection)); conn = malloc(sizeof (NetConnection));
@@ -62,8 +63,11 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
conn->connectCallback = connectCallback; conn->connectCallback = connectCallback;
conn->closeCallback = closeCallback; conn->closeCallback = closeCallback;
conn->errorCallback = errorCallback; conn->errorCallback = errorCallback;
conn->deleteCallback = deleteCallback;
conn->readyCallback = NULL; conn->readyCallback = NULL;
conn->readyCallbackArg = NULL; conn->readyCallbackArg = NULL;
conn->resetCallback = NULL;
conn->resetCallbackArg = NULL;
conn->readBuf = malloc(NETPLAY_READBUFSIZE); conn->readBuf = malloc(NETPLAY_READBUFSIZE);
conn->readEnd = conn->readBuf; conn->readEnd = conn->readBuf;
@@ -80,6 +84,8 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
conn->stateFlags.handshake.canceling = false; conn->stateFlags.handshake.canceling = false;
conn->stateFlags.ready.localReady = false; conn->stateFlags.ready.localReady = false;
conn->stateFlags.ready.remoteReady = false; conn->stateFlags.ready.remoteReady = false;
conn->stateFlags.reset.localReset = false;
conn->stateFlags.reset.remoteReset = false;
conn->stateFlags.agreement = Agreement_nothingAgreed; conn->stateFlags.agreement = Agreement_nothingAgreed;
conn->stateFlags.inputDelay = 0; conn->stateFlags.inputDelay = 0;
#ifdef NETPLAY_CHECKSUM #ifdef NETPLAY_CHECKSUM
@@ -105,8 +111,22 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
return conn; return conn;
} }
static void
NetConnection_doDeleteCallback(NetConnection *conn) {
if (conn->deleteCallback != NULL) {
//NetConnection_incRef(conn);
conn->deleteCallback(conn);
//NetConnection_decRef(conn);
}
}
static void static void
NetConnection_delete(NetConnection *conn) { NetConnection_delete(NetConnection *conn) {
NetConnection_doDeleteCallback(conn);
if (conn->stateData != NULL) {
NetConnectionStateData_release(conn->stateData);
conn->stateData = NULL;
}
free(conn->readBuf); free(conn->readBuf);
PacketQueue_uninit(&conn->queue); PacketQueue_uninit(&conn->queue);
free(conn); free(conn);
@@ -133,10 +153,6 @@ NetConnection_doClose(NetConnection *conn) {
Netplay_doCloseCallback(conn); Netplay_doCloseCallback(conn);
NetConnection_setState(conn, NetState_unconnected); NetConnection_setState(conn, NetState_unconnected);
if (conn->stateData != NULL) {
NetConnectionStateData_release(conn->stateData);
conn->stateData = NULL;
}
} }
// Called when the NetDescriptor is shut down. // Called when the NetDescriptor is shut down.
@@ -213,6 +229,23 @@ NetConnection_getReadyCallbackArg(const NetConnection *conn) {
return conn->readyCallbackArg; return conn->readyCallbackArg;
} }
void
NetConnection_setResetCallback(NetConnection *conn,
NetConnection_ResetCallback callback, void *arg) {
conn->resetCallback = callback;
conn->resetCallbackArg = arg;
}
NetConnection_ResetCallback
NetConnection_getResetCallback(const NetConnection *conn) {
return conn->resetCallback;
}
void *
NetConnection_getResetCallbackArg(const NetConnection *conn) {
return conn->resetCallbackArg;
}
void void
NetConnection_setState(NetConnection *conn, NetState state) { NetConnection_setState(NetConnection *conn, NetState state) {
#ifdef NETPLAY_DEBUG #ifdef NETPLAY_DEBUG
+26 -1
View File
@@ -33,8 +33,10 @@ typedef void (*NetConnection_ConnectCallback)(NetConnection *nd);
typedef void (*NetConnection_CloseCallback)(NetConnection *nd); typedef void (*NetConnection_CloseCallback)(NetConnection *nd);
typedef void (*NetConnection_ErrorCallback)(NetConnection *nd, typedef void (*NetConnection_ErrorCallback)(NetConnection *nd,
const NetConnectionError *error); const NetConnectionError *error);
typedef void (*NetConnection_DeleteCallback)(NetConnection *nd);
typedef void (*NetConnection_ReadyCallback)(NetConnection *conn, void *arg); typedef void (*NetConnection_ReadyCallback)(NetConnection *conn, void *arg);
typedef void (*NetConnection_ResetCallback)(NetConnection *conn, void *arg);
#include "netstate.h" #include "netstate.h"
#include "netoptions.h" #include "netoptions.h"
@@ -80,6 +82,11 @@ typedef struct {
bool remoteReady : 1; bool remoteReady : 1;
} ReadyFlags; } ReadyFlags;
typedef struct {
bool localReset : 1;
bool remoteReset : 1;
} ResetFlags;
// Which parameters have we both sides of a connection reached agreement on? // Which parameters have we both sides of a connection reached agreement on?
typedef struct { typedef struct {
bool randomSeed : 1; bool randomSeed : 1;
@@ -108,6 +115,7 @@ typedef struct {
* during a connection. Undefined while not connected. */ * during a connection. Undefined while not connected. */
HandShakeFlags handshake; HandShakeFlags handshake;
ReadyFlags ready; ReadyFlags ready;
ResetFlags reset;
Agreement agreement; Agreement agreement;
size_t inputDelay; size_t inputDelay;
/* Used during negotiation of the actual inputDelay. This /* Used during negotiation of the actual inputDelay. This
@@ -135,6 +143,13 @@ struct NetConnection {
// Extra argument for readyCallback(). // Extra argument for readyCallback().
// XXX: when is this cleaned up if a connection is broken? // XXX: when is this cleaned up if a connection is broken?
NetConnection_ResetCallback resetCallback;
// Called when a reset has been signalled and confirmed.
// Set by Netplay_localReset().
void *resetCallbackArg;
// Extra argument for resetCallback().
// XXX: when is this cleaned up if a connection is broken?
const NetplayPeerOptions *options; const NetplayPeerOptions *options;
PacketQueue queue; PacketQueue queue;
#ifdef NETPLAY_STATISTICS #ifdef NETPLAY_STATISTICS
@@ -145,7 +160,10 @@ struct NetConnection {
#endif #endif
NetConnection_ConnectCallback connectCallback; NetConnection_ConnectCallback connectCallback;
NetConnection_CloseCallback closeCallback; NetConnection_CloseCallback closeCallback;
// Called when the NetConnection becomes disconnected.
NetConnection_ErrorCallback errorCallback; NetConnection_ErrorCallback errorCallback;
NetConnection_DeleteCallback deleteCallback;
// Called when the NetConnection is destroyed.
uint8 *readBuf; uint8 *readBuf;
uint8 *readEnd; uint8 *readEnd;
NetConnectionStateData *stateData; NetConnectionStateData *stateData;
@@ -170,7 +188,8 @@ NetConnection *NetConnection_open(int player,
const NetplayPeerOptions *options, const NetplayPeerOptions *options,
NetConnection_ConnectCallback connectCallback, NetConnection_ConnectCallback connectCallback,
NetConnection_CloseCallback closeCallback, NetConnection_CloseCallback closeCallback,
NetConnection_ErrorCallback errorCallback, void *extra); NetConnection_ErrorCallback errorCallback,
NetConnection_DeleteCallback deleteCallback, void *extra);
void NetConnection_close(NetConnection *conn); void NetConnection_close(NetConnection *conn);
bool NetConnection_isConnected(const NetConnection *conn); bool NetConnection_isConnected(const NetConnection *conn);
@@ -203,6 +222,12 @@ NetConnection_ReadyCallback NetConnection_getReadyCallback(
const NetConnection *conn); const NetConnection *conn);
void *NetConnection_getReadyCallbackArg(const NetConnection *conn); void *NetConnection_getReadyCallbackArg(const NetConnection *conn);
void NetConnection_setResetCallback(NetConnection *conn,
NetConnection_ResetCallback callback, void *arg);
NetConnection_ResetCallback NetConnection_getResetCallback(
const NetConnection *conn);
void *NetConnection_getResetCallbackArg(const NetConnection *conn);
#endif /* _NETCONNECTION_H */ #endif /* _NETCONNECTION_H */
+163 -6
View File
@@ -28,6 +28,7 @@
#include "netplay/packetq.h" #include "netplay/packetq.h"
#include "netplay/proto/npconfirm.h" #include "netplay/proto/npconfirm.h"
#include "netplay/proto/ready.h" #include "netplay/proto/ready.h"
#include "netplay/proto/reset.h"
#include "build.h" #include "build.h"
// for StarShipPlayer() // for StarShipPlayer()
@@ -65,8 +66,12 @@ closeAllConnections(void) {
COUNT player; COUNT player;
for (player = 0; player < NUM_PLAYERS; player++) for (player = 0; player < NUM_PLAYERS; player++)
if (netConnections[player] != NULL) {
NetConnection *conn = netConnections[player];
if (conn != NULL && NetConnection_isConnected(conn))
closePlayerNetworkConnection(player); closePlayerNetworkConnection(player);
}
} }
size_t size_t
@@ -100,6 +105,9 @@ netInputBlocking(uint32 timeoutMs) {
timeoutMs = nextAlarmMs; timeoutMs = nextAlarmMs;
NetManager_process(&timeoutMs); NetManager_process(&timeoutMs);
// This may cause more packets to be queued, hence the
// flushPacketQueues().
flushPacketQueues();
Alarm_process(); Alarm_process();
Callback_process(); Callback_process();
@@ -183,6 +191,22 @@ connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg) {
} }
} }
bool
allConnected(void) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
return false;
}
return true;
}
void void
sendBattleInputConnections(BATTLE_INPUT_STATE input) { sendBattleInputConnections(BATTLE_INPUT_STATE input) {
COUNT player; COUNT player;
@@ -274,7 +298,7 @@ networkBattleInput(COUNT player, STARSHIPPTR StarShipPtr) {
{ {
// Connection aborted. // Connection aborted.
GLOBAL(CurrentActivity) |= CHECK_ABORT; GLOBAL(CurrentActivity) |= CHECK_ABORT;
return(BATTLE_INPUT_STATE) 0; return (BATTLE_INPUT_STATE) 0;
} }
#if 0 #if 0
@@ -288,7 +312,7 @@ networkBattleInput(COUNT player, STARSHIPPTR StarShipPtr) {
{ {
// Connection aborted. // Connection aborted.
GLOBAL(CurrentActivity) |= CHECK_ABORT; GLOBAL(CurrentActivity) |= CHECK_ABORT;
return(BATTLE_INPUT_STATE) 0; return (BATTLE_INPUT_STATE) 0;
} }
} }
} }
@@ -297,6 +321,11 @@ networkBattleInput(COUNT player, STARSHIPPTR StarShipPtr) {
return result; return result;
} }
static void
deleteConnectionCallback(NetConnection *conn) {
removeNetConnection(NetConnection_getPlayerNr(conn));
}
NetConnection * NetConnection *
openPlayerNetworkConnection(COUNT player, void *extra) { openPlayerNetworkConnection(COUNT player, void *extra) {
NetConnection *conn; NetConnection *conn;
@@ -305,7 +334,8 @@ openPlayerNetworkConnection(COUNT player, void *extra) {
conn = NetConnection_open(player, conn = NetConnection_open(player,
&netplayOptions.peer[player], NetMelee_connectCallback, &netplayOptions.peer[player], NetMelee_connectCallback,
NetMelee_closeCallback, NetMelee_errorCallback, extra); NetMelee_closeCallback, NetMelee_errorCallback,
deleteConnectionCallback, extra);
addNetConnection(conn, player); addNetConnection(conn, player);
return conn; return conn;
@@ -321,7 +351,6 @@ closePlayerNetworkConnection(COUNT player) {
assert(netConnections[player] != NULL); assert(netConnections[player] != NULL);
NetConnection_close(netConnections[player]); NetConnection_close(netConnections[player]);
removeNetConnection(player);
} }
// If the callback function returns 'false', the function will immediately // If the callback function returns 'false', the function will immediately
@@ -392,6 +421,30 @@ setStateConnections(NetState state) {
(bool(*)(NetConnection *, void *)) setStateConnection, &state); (bool(*)(NetConnection *, void *)) setStateConnection, &state);
} }
static bool
sendAbortConnection(NetConnection *conn, const NetplayAbortReason *reason) {
sendAbort(conn, *reason);
return true;
}
bool
sendAbortConnections(NetplayAbortReason reason) {
return forAllConnectedPlayers(
(bool(*)(NetConnection *, void *)) sendAbortConnection, &reason);
}
static bool
resetConnection(NetConnection *conn, const NetplayResetReason *reason) {
Netplay_localReset(conn, *reason);
return true;
}
bool
resetConnections(NetplayResetReason reason) {
return forAllConnectedPlayers(
(bool(*)(NetConnection *, void *)) resetConnection, &reason);
}
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
typedef struct { typedef struct {
@@ -422,6 +475,7 @@ localReadyConnections(NetConnection_ReadyCallback readyCallback,
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#define NETWORK_POLL_DELAY (ONE_SECOND / 24)
typedef struct NegotiateReadyState NegotiateReadyState; typedef struct NegotiateReadyState NegotiateReadyState;
struct NegotiateReadyState { struct NegotiateReadyState {
@@ -437,7 +491,6 @@ struct NegotiateReadyState {
static BOOLEAN static BOOLEAN
negotiateReadyInputFunc(NegotiateReadyState *state) { negotiateReadyInputFunc(NegotiateReadyState *state) {
#define NETWORK_POLL_DELAY (ONE_SECOND / 24)
netInputBlocking(NETWORK_POLL_DELAY); netInputBlocking(NETWORK_POLL_DELAY);
// The timing out is necessary so that immediate key presses get // The timing out is necessary so that immediate key presses get
// handled while we wait. If we could do without the timeout, // handled while we wait. If we could do without the timeout,
@@ -572,4 +625,108 @@ waitReady(NetConnection *conn) {
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
typedef struct WaitResetState WaitResetState;
struct WaitResetState {
// Common fields of INPUT_STATE_DESC, from which this structure
// "inherits".
BOOLEAN(*InputFunc)(PVOID pInputState);
COUNT MenuRepeatDelay;
NetConnection *conn;
NetState nextState;
bool done;
};
static BOOLEAN
waitResetInputFunc(WaitResetState *state) {
netInputBlocking(NETWORK_POLL_DELAY);
// The timing out is necessary so that immediate key presses get
// handled while we wait. If we could do without the timeout,
// we wouldn't even need waitResetInputFunc() and the
// DoInput() call.
// No need to call flushPacketQueues(); nothing needs to be sent
// right now.
if (!NetConnection_isConnected(state->conn))
return FALSE;
return !state->done;
}
// Called when both sides are reset.
static void
waitResetBothResetCallback(NetConnection *conn, void *arg) {
WaitResetState *state = (WaitResetState *) arg;
if (state->nextState != (NetState) -1) {
NetConnection_setState(conn, state->nextState);
// This has to be done immediately, as more packets in the
// receive queue may be handled by the netInput() call that
// triggered this callback.
// This is the reason for the nextState argument to
// waitReset(); setting the state after the call to
// waitReset() would be too late.
}
state->done = true;
}
bool
waitReset(NetConnection *conn, NetState nextState) {
WaitResetState state;
state.InputFunc = (BOOLEAN(*)(void *)) waitResetInputFunc;
state.MenuRepeatDelay = 0;
state.conn = conn;
state.nextState = nextState;
state.done = false;
Netplay_setResetCallback(conn, waitResetBothResetCallback,
(void *) &state);
if (state.done)
goto out;
if (!Netplay_isLocalReset(conn)) {
Netplay_localReset(conn, ResetReason_manualReset);
flushPacketQueue(conn);
}
if (!state.done)
DoInput(&state, FALSE);
out:
return NetConnection_isConnected(conn);
}
// Wait until we have received a reset packet to all connections. If we
// ourselves have not sent a reset packet, one is sent, with reason
// 'manualReset'.
// XXX: Right now all connections are handled one by one. Handling them all
// at once would be faster but would require more work, which is
// not worth it as the time is minimal and this function is not
// time critical.
// Use '(NetState) -1' for nextState to keep the current state.
bool
waitResetConnections(NetState nextState) {
COUNT player;
size_t numDisconnected = 0;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn)) {
numDisconnected++;
continue;
}
waitReset(conn, nextState);
}
return numDisconnected == 0;
}
////////////////////////////////////////////////////////////////////////////
+8
View File
@@ -22,6 +22,7 @@
#include "netplay.h" #include "netplay.h"
#include "netinput.h" #include "netinput.h"
#include "netconnection.h" #include "netconnection.h"
#include "packetsenders.h"
#include "../controls.h" #include "../controls.h"
// for BATTLE_INPUT_STATE // for BATTLE_INPUT_STATE
@@ -44,6 +45,8 @@ void confirmConnections(void);
void cancelConfirmations(void); void cancelConfirmations(void);
void connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg); void connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg);
bool allConnected(void);
void sendBattleInputConnections(BATTLE_INPUT_STATE input); void sendBattleInputConnections(BATTLE_INPUT_STATE input);
void sendChecksumConnections(uint32 frameNr, uint32 checksum); void sendChecksumConnections(uint32 frameNr, uint32 checksum);
void initBattleStateDataConnections(void); void initBattleStateDataConnections(void);
@@ -59,6 +62,8 @@ bool forAllConnectedPlayers(ForAllCallback callback, void *arg);
bool setupInputDelay(size_t localInputDelay); bool setupInputDelay(size_t localInputDelay);
bool sendInputDelayConnections(size_t delay); bool sendInputDelayConnections(size_t delay);
bool setStateConnections(NetState state); bool setStateConnections(NetState state);
bool sendAbortConnections(NetplayAbortReason reason);
bool resetConnections(NetplayResetReason reason);
bool localReadyConnections(NetConnection_ReadyCallback readyCallback, bool localReadyConnections(NetConnection_ReadyCallback readyCallback,
void *arg, bool notifyRemote); void *arg, bool notifyRemote);
@@ -67,6 +72,9 @@ bool negotiateReady(NetConnection *conn, bool notifyRemote,
bool negotiateReadyConnections(bool notifyRemote, NetState nextState); bool negotiateReadyConnections(bool notifyRemote, NetState nextState);
bool waitReady(NetConnection *conn); bool waitReady(NetConnection *conn);
bool waitReset(NetConnection *conn, NetState nextState);
bool waitResetConnections(NetState nextState);
#endif /* _NETMELEE_H */ #endif /* _NETMELEE_H */
-2
View File
@@ -121,8 +121,6 @@ dataReceivedMulti(NetConnection *conn, const uint8 *data, size_t len) {
return processed; return processed;
} }
// Returns -1 on error (setting errno), or 0 if everything went
// ok, regardless of whether any packets were actually processed.
void void
dataReadyCallback(NetDescriptor *nd) { dataReadyCallback(NetDescriptor *nd) {
NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd); NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
+1 -1
View File
@@ -25,7 +25,7 @@
#define DEFINE_NETSTATEDATA(stateName) \ #define DEFINE_NETSTATEDATA(stateName) \
{ \ { \
/* .name = */ #stateName, \ /* .name = */ #stateName, \
} }
NetStateData netStateData[] = { NetStateData netStateData[] = {
DEFINE_NETSTATEDATA(unconnected), DEFINE_NETSTATEDATA(unconnected),
+1
View File
@@ -24,6 +24,7 @@
typedef struct NetConnectionStateData NetConnectionStateData; typedef struct NetConnectionStateData NetConnectionStateData;
// State of a NetConnection.
typedef enum { typedef enum {
NetState_unconnected, /* No connection initiated */ NetState_unconnected, /* No connection initiated */
NetState_connecting, /* Connection being setup */ NetState_connecting, /* Connection being setup */
+16
View File
@@ -54,6 +54,8 @@ PacketTypeData packetTypeData[PACKET_NUM] = {
DEFINE_PACKETDATA(BattleInput, false), DEFINE_PACKETDATA(BattleInput, false),
DEFINE_PACKETDATA(FrameCount, false), DEFINE_PACKETDATA(FrameCount, false),
DEFINE_PACKETDATA(Checksum, false), DEFINE_PACKETDATA(Checksum, false),
DEFINE_PACKETDATA(Abort, false),
DEFINE_PACKETDATA(Reset, false),
}; };
static inline void * static inline void *
@@ -253,5 +255,19 @@ Packet_Checksum_create(uint32 frameNr, uint32 checksum) {
return packet; return packet;
} }
Packet_Abort *
Packet_Abort_create(uint16 reason) {
Packet_Abort *packet = (Packet_Abort *) Packet_create(PACKET_ABORT, 0);
packet->reason = hton16(reason);
return packet;
}
Packet_Reset *
Packet_Reset_create(uint16 reason) {
Packet_Reset *packet = (Packet_Reset *) Packet_create(PACKET_RESET, 0);
packet->reason = hton16(reason);
return packet;
}
+30
View File
@@ -39,10 +39,26 @@ typedef enum PacketType {
PACKET_BATTLEINPUT, PACKET_BATTLEINPUT,
PACKET_FRAMECOUNT, PACKET_FRAMECOUNT,
PACKET_CHECKSUM, PACKET_CHECKSUM,
PACKET_ABORT,
PACKET_RESET,
PACKET_NUM, /* Number of packet types */ PACKET_NUM, /* Number of packet types */
} PacketType; } PacketType;
// Sent before aborting the connection.
typedef enum NetplayAbortReason {
AbortReason_unspecified,
AbortReason_versionMismatch,
AbortReason_protocolError,
// Network is in an inconsistent state.
} NetplayAbortReason;
// Sent before resetting the connection. A game in progress is terminated.
typedef enum NetplayResetReason {
ResetReason_unspecified,
ResetReason_syncLoss,
ResetReason_manualReset,
} NetplayResetReason;
#ifndef PACKET_H_STANDALONE #ifndef PACKET_H_STANDALONE
#include "netconnection.h" #include "netconnection.h"
@@ -227,6 +243,18 @@ typedef struct {
uint32 checksum; /* Actually Checksum */ uint32 checksum; /* Actually Checksum */
} Packet_Checksum; } Packet_Checksum;
typedef struct {
PacketHeader header;
uint16 reason; /* Actually NetplayAbortReason */
uint16 padding0;
} Packet_Abort;
typedef struct {
PacketHeader header;
uint16 reason; /* Actually NetplayResetReason */
uint16 padding0;
} Packet_Reset;
#ifndef PACKET_H_STANDALONE #ifndef PACKET_H_STANDALONE
void Packet_delete(Packet *packet); void Packet_delete(Packet *packet);
@@ -248,6 +276,8 @@ Packet_SelectShip *Packet_SelectShip_create(uint16 ship);
Packet_BattleInput *Packet_BattleInput_create(uint8 state); Packet_BattleInput *Packet_BattleInput_create(uint8 state);
Packet_FrameCount *Packet_FrameCount_create(uint32 frameCount); Packet_FrameCount *Packet_FrameCount_create(uint32 frameCount);
Packet_Checksum *Packet_Checksum_create(uint32 frameNr, uint32 checksum); Packet_Checksum *Packet_Checksum_create(uint32 frameNr, uint32 checksum);
Packet_Abort *Packet_Abort_create(uint16 reason);
Packet_Reset *Packet_Reset_create(uint16 reason);
#endif #endif
+121
View File
@@ -28,12 +28,15 @@
#include "packetsenders.h" #include "packetsenders.h"
#include "proto/npconfirm.h" #include "proto/npconfirm.h"
#include "proto/ready.h" #include "proto/ready.h"
#include "proto/reset.h"
#include "libs/log.h" #include "libs/log.h"
#include "controls.h" #include "controls.h"
// for BATTLE_INPUT_STATE // for BATTLE_INPUT_STATE
#include "init.h" #include "init.h"
// for NUM_PLAYERS // for NUM_PLAYERS
#include "globdata.h"
// for GLOBAL
#include "melee.h" #include "melee.h"
// for various update functions. // for various update functions.
#include "pickmele.h" #include "pickmele.h"
@@ -56,6 +59,13 @@ testNetState(bool condition, PacketType type) {
int int
PacketHandler_Init(NetConnection *conn, const Packet_Init *packet) { PacketHandler_Init(NetConnection *conn, const Packet_Init *packet) {
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_init && if (!testNetState(conn->state == NetState_init &&
!conn->stateFlags.ready.remoteReady, PACKET_INIT)) !conn->stateFlags.ready.remoteReady, PACKET_INIT))
return -1; // errno is set return -1; // errno is set
@@ -152,6 +162,13 @@ checkYourTurn(NetConnection *conn, PacketType type) {
int int
PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) { PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) {
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(readyFlagsMeaningful(conn->state) && if (!testNetState(readyFlagsMeaningful(conn->state) &&
!conn->stateFlags.ready.remoteReady, PACKET_READY)) !conn->stateFlags.ready.remoteReady, PACKET_READY))
return -1; // errno is set return -1; // errno is set
@@ -172,6 +189,13 @@ PacketHandler_Fleet(NetConnection *conn, const Packet_Fleet *packet) {
int player; int player;
BattleStateData *battleStateData; BattleStateData *battleStateData;
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET)) if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
return -1; // errno is set return -1; // errno is set
@@ -223,6 +247,13 @@ PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
int side; int side;
BattleStateData *battleStateData; BattleStateData *battleStateData;
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_inSetup, PACKET_TEAMNAME)) if (!testNetState(conn->state == NetState_inSetup, PACKET_TEAMNAME))
return -1; // errno is set return -1; // errno is set
@@ -262,6 +293,13 @@ handshakeComplete(NetConnection *conn) {
int int
PacketHandler_Handshake0(NetConnection *conn, PacketHandler_Handshake0(NetConnection *conn,
const Packet_Handshake0 *packet) { const Packet_Handshake0 *packet) {
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(handshakeMeaningful(conn->state) if (!testNetState(handshakeMeaningful(conn->state)
&& !conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKE0)) && !conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKE0))
return -1; // errno is set return -1; // errno is set
@@ -280,6 +318,13 @@ PacketHandler_Handshake0(NetConnection *conn,
int int
PacketHandler_Handshake1(NetConnection *conn, PacketHandler_Handshake1(NetConnection *conn,
const Packet_Handshake1 *packet) { const Packet_Handshake1 *packet) {
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(handshakeMeaningful(conn->state) && if (!testNetState(handshakeMeaningful(conn->state) &&
(conn->stateFlags.handshake.localOk || (conn->stateFlags.handshake.localOk ||
conn->stateFlags.handshake.canceling), PACKET_HANDSHAKE1)) conn->stateFlags.handshake.canceling), PACKET_HANDSHAKE1))
@@ -312,6 +357,13 @@ PacketHandler_Handshake1(NetConnection *conn,
int int
PacketHandler_HandshakeCancel(NetConnection *conn, PacketHandler_HandshakeCancel(NetConnection *conn,
const Packet_HandshakeCancel *packet) { const Packet_HandshakeCancel *packet) {
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(handshakeMeaningful(conn->state) if (!testNetState(handshakeMeaningful(conn->state)
&& conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKECANCEL)) && conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKECANCEL))
return -1; // errno is set return -1; // errno is set
@@ -328,6 +380,13 @@ PacketHandler_HandshakeCancel(NetConnection *conn,
int int
PacketHandler_HandshakeCancelAck(NetConnection *conn, PacketHandler_HandshakeCancelAck(NetConnection *conn,
const Packet_HandshakeCancelAck *packet) { const Packet_HandshakeCancelAck *packet) {
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(handshakeMeaningful(conn->state) if (!testNetState(handshakeMeaningful(conn->state)
&& conn->stateFlags.handshake.canceling, && conn->stateFlags.handshake.canceling,
PACKET_HANDSHAKECANCELACK)) PACKET_HANDSHAKECANCELACK))
@@ -352,6 +411,13 @@ PacketHandler_SeedRandom(NetConnection *conn,
const Packet_SeedRandom *packet) { const Packet_SeedRandom *packet) {
BattleStateData *battleStateData; BattleStateData *battleStateData;
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_preBattle && if (!testNetState(conn->state == NetState_preBattle &&
!conn->stateFlags.discriminant, PACKET_SEEDRANDOM)) !conn->stateFlags.discriminant, PACKET_SEEDRANDOM))
return -1; // errno is set return -1; // errno is set
@@ -370,6 +436,13 @@ PacketHandler_InputDelay(NetConnection *conn,
BattleStateData *battleStateData; BattleStateData *battleStateData;
uint32 delay; uint32 delay;
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_preBattle, if (!testNetState(conn->state == NetState_preBattle,
PACKET_INPUTDELAY)) PACKET_INPUTDELAY))
return -1; // errno is set return -1; // errno is set
@@ -392,6 +465,13 @@ PacketHandler_SelectShip(NetConnection *conn,
bool updateResult; bool updateResult;
BattleStateData *battleStateData; BattleStateData *battleStateData;
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_selectShip, PACKET_SELECTSHIP)) if (!testNetState(conn->state == NetState_selectShip, PACKET_SELECTSHIP))
return -1; // errno is set return -1; // errno is set
@@ -413,6 +493,13 @@ PacketHandler_BattleInput(NetConnection *conn,
BATTLE_INPUT_STATE input; BATTLE_INPUT_STATE input;
BattleInputBuffer *bib; BattleInputBuffer *bib;
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_inBattle || if (!testNetState(conn->state == NetState_inBattle ||
conn->state == NetState_endingBattle || conn->state == NetState_endingBattle ||
conn->state == NetState_endingBattle2, PACKET_BATTLEINPUT)) conn->state == NetState_endingBattle2, PACKET_BATTLEINPUT))
@@ -434,6 +521,13 @@ PacketHandler_FrameCount(NetConnection *conn,
BattleStateData *battleStateData; BattleStateData *battleStateData;
BattleFrameCounter frameCount; BattleFrameCounter frameCount;
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(conn->state == NetState_endingBattle, if (!testNetState(conn->state == NetState_endingBattle,
PACKET_FRAMECOUNT)) PACKET_FRAMECOUNT))
return -1; // errno is set return -1; // errno is set
@@ -461,6 +555,13 @@ PacketHandler_Checksum(NetConnection *conn, const Packet_Checksum *packet) {
size_t interval; size_t interval;
#endif #endif
if (conn->stateFlags.reset.localReset)
return 0;
if (conn->stateFlags.reset.remoteReset) {
errno = EBADMSG;
return -1;
}
if (!testNetState(NetState_battleActive(conn->state), PACKET_CHECKSUM)) if (!testNetState(NetState_battleActive(conn->state), PACKET_CHECKSUM))
return -1; // errno is set return -1; // errno is set
@@ -521,5 +622,25 @@ PacketHandler_Checksum(NetConnection *conn, const Packet_Checksum *packet) {
return 0; return 0;
} }
int
PacketHandler_Abort(NetConnection *conn, const Packet_Abort *packet) {
abortFeedback(conn->player, packet->reason);
return -1;
// Close connection.
}
int
PacketHandler_Reset(NetConnection *conn, const Packet_Reset *packet) {
NetplayResetReason reason;
if (!testNetState(!conn->stateFlags.reset.remoteReset, PACKET_RESET))
return -1; // errno is set
reason = ntoh16(packet->reason);
Netplay_remoteReset(conn, reason);
return 0;
}
+2
View File
@@ -42,6 +42,8 @@ DECLARE_PACKETHANDLER(SelectShip);
DECLARE_PACKETHANDLER(BattleInput); DECLARE_PACKETHANDLER(BattleInput);
DECLARE_PACKETHANDLER(FrameCount); DECLARE_PACKETHANDLER(FrameCount);
DECLARE_PACKETHANDLER(Checksum); DECLARE_PACKETHANDLER(Checksum);
DECLARE_PACKETHANDLER(Abort);
DECLARE_PACKETHANDLER(Reset);
#endif /* _PACKETHANDLERS_H */ #endif /* _PACKETHANDLERS_H */
+16
View File
@@ -195,5 +195,21 @@ sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
} }
#endif #endif
void
sendAbort(NetConnection *conn, NetplayAbortReason reason) {
Packet_Abort *packet;
packet = Packet_Abort_create((uint16) reason);
queuePacket(conn, (Packet *) packet, false);
}
void
sendReset(NetConnection *conn, NetplayResetReason reason) {
Packet_Reset *packet;
packet = Packet_Reset_create((uint16) reason);
queuePacket(conn, (Packet *) packet, false);
}
+2
View File
@@ -49,6 +49,8 @@ void sendSelectShip(NetConnection *conn, COUNT ship);
void sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input); void sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input);
void sendFrameCount(NetConnection *conn, uint32 frameCount); void sendFrameCount(NetConnection *conn, uint32 frameCount);
void sendChecksum(NetConnection *conn, uint32 frameNr, uint32 checksum); void sendChecksum(NetConnection *conn, uint32 frameNr, uint32 checksum);
void sendAbort(NetConnection *conn, NetplayAbortReason reason);
void sendReset(NetConnection *conn, NetplayResetReason reason);
#endif /* _PACKETSENDERS_H */ #endif /* _PACKETSENDERS_H */
+1 -1
View File
@@ -1,2 +1,2 @@
uqm_CFILES="npconfirm.c ready.c" uqm_CFILES="npconfirm.c ready.c reset.c"
+4 -4
View File
@@ -36,11 +36,11 @@ Netplay_bothReady(NetConnection *conn) {
callback = conn->readyCallback; callback = conn->readyCallback;
readyArg = conn->readyCallbackArg; readyArg = conn->readyCallbackArg;
conn->readyCallback = NULL; NetConnection_setReadyCallback(conn, NULL, NULL);
// Clear the readyCallback field before performing the callback, // Clear the readyCallback field before performing the callback,
// so that it can be set again from inside the callback // so that it can be set again from inside the callback
// function. // function.
conn->readyCallbackArg = NULL;
callback(conn, readyArg); callback(conn, readyArg);
} }
@@ -94,12 +94,12 @@ Netplay_remoteReady(NetConnection *conn) {
} }
bool bool
Netplay_isLocalReady(NetConnection *conn) { Netplay_isLocalReady(const NetConnection *conn) {
return conn->stateFlags.ready.localReady; return conn->stateFlags.ready.localReady;
} }
bool bool
Netplay_isRemoteReady(NetConnection *conn) { Netplay_isRemoteReady(const NetConnection *conn) {
return conn->stateFlags.ready.remoteReady; return conn->stateFlags.ready.remoteReady;
} }
+2 -2
View File
@@ -24,8 +24,8 @@
bool Netplay_localReady(NetConnection *conn, bool Netplay_localReady(NetConnection *conn,
NetConnection_ReadyCallback callback, void *arg, bool notifyRemote); NetConnection_ReadyCallback callback, void *arg, bool notifyRemote);
bool Netplay_remoteReady(NetConnection *conn); bool Netplay_remoteReady(NetConnection *conn);
bool Netplay_isLocalReady(NetConnection *conn); bool Netplay_isLocalReady(const NetConnection *conn);
bool Netplay_isRemoteReady(NetConnection *conn); bool Netplay_isRemoteReady(const NetConnection *conn);
#endif /* _READY_H */ #endif /* _READY_H */
+22 -20
View File
@@ -153,7 +153,7 @@ DoGetMelee (GETMELEE_STATE *gms)
gms->remoteSelected = FALSE; gms->remoteSelected = FALSE;
#endif #endif
// We determine upfront which ship would be chosen if the player // We determine in advance which ship would be chosen if the player
// wants a random ship, to keep it simple to keep network parties // wants a random ship, to keep it simple to keep network parties
// synchronised. // synchronised.
gms->randomIndex = (COUNT)TFB_Random () % gms->ships_left; gms->randomIndex = (COUNT)TFB_Random () % gms->ships_left;
@@ -165,8 +165,14 @@ DoGetMelee (GETMELEE_STATE *gms)
SleepThread (ONE_SECOND / 120); SleepThread (ONE_SECOND / 120);
#ifdef NETPLAY #ifdef NETPLAY
netInput (); netInput ();
if (!allConnected())
goto aborted;
#endif #endif
if (GLOBAL (CurrentActivity) & CHECK_ABORT)
goto aborted;
if (PlayerInput[which_player] == ComputerInput) if (PlayerInput[which_player] == ComputerInput)
{ {
/* TODO: Make this a frame-by-frame thing. This code is currently /* TODO: Make this a frame-by-frame thing. This code is currently
@@ -200,16 +206,7 @@ DoGetMelee (GETMELEE_STATE *gms)
select = PulsedInputState.key[template][KEY_WEAPON]; select = PulsedInputState.key[template][KEY_WEAPON];
} }
if (GLOBAL (CurrentActivity) & CHECK_ABORT) if (select)
{
gms->hBattleShip = 0;
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
#ifdef NETPLAY
// TODO: send abort packet
#endif
done = true;
}
else if (select)
{ {
if (gms->col == NUM_MELEE_COLS_ORIG) if (gms->col == NUM_MELEE_COLS_ORIG)
{ {
@@ -225,13 +222,9 @@ DoGetMelee (GETMELEE_STATE *gms)
} }
else else
{ {
// Exit // Selected exit
if (ConfirmExit ()) if (ConfirmExit ())
{ goto aborted;
gms->hBattleShip = 0;
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
done = TRUE;
}
} }
} }
else else
@@ -294,6 +287,14 @@ DoGetMelee (GETMELEE_STATE *gms)
#endif #endif
return !done; return !done;
aborted:
#ifdef NETPLAY
flushPacketQueues ();
#endif
gms->hBattleShip = 0;
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
return FALSE;
} }
#ifdef NETPLAY #ifdef NETPLAY
@@ -495,7 +496,10 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
SetFlashRect (NULL_PTR, (FRAME)0); SetFlashRect (NULL_PTR, (FRAME)0);
if (gmstate.hBattleShip == 0) if (gmstate.hBattleShip == 0)
{
// Aborting.
GLOBAL (CurrentActivity) &= ~IN_BATTLE; GLOBAL (CurrentActivity) &= ~IN_BATTLE;
}
else else
{ {
StarShipPtr = StarShipPtr =
@@ -513,14 +517,12 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
#ifdef NETPLAY #ifdef NETPLAY
{ {
NetConnection *conn = netConnections[which_player]; NetConnection *conn = netConnections[which_player];
if (conn != NULL) if (conn != NULL && NetConnection_isConnected(conn))
{ {
BattleStateData *battleStateData; BattleStateData *battleStateData;
battleStateData = (BattleStateData *) battleStateData = (BattleStateData *)
NetConnection_getStateData(conn); NetConnection_getStateData(conn);
battleStateData->getMeleeState = NULL; battleStateData->getMeleeState = NULL;
if (gmstate.hBattleShip == 0)
NetConnection_setState(conn, NetState_inSetup);
} }
} }
#endif #endif
+1 -2
View File
@@ -393,8 +393,7 @@ GetEncounterStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
if (LastStarShipPtr->special_counter == 0) if (LastStarShipPtr->special_counter == 0)
/* died in the line of duty */ /* died in the line of duty */
GLOBAL_SIS (CrewEnlisted) = (COUNT)~0; GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
else if (GLOBAL_SIS (FuelOnBoard) > else if (GLOBAL_SIS (FuelOnBoard) > RUN_AWAY_FUEL_COST)
RUN_AWAY_FUEL_COST)
GLOBAL_SIS (FuelOnBoard) -= RUN_AWAY_FUEL_COST; GLOBAL_SIS (FuelOnBoard) -= RUN_AWAY_FUEL_COST;
else else
GLOBAL_SIS (FuelOnBoard) = 0; GLOBAL_SIS (FuelOnBoard) = 0;
+6 -3
View File
@@ -387,8 +387,8 @@ spawn_ship (STARSHIPPTR StarShipPtr)
RDPtr->ship_info.var2 = (BYTE)StarShipPtr->ShipFacing; RDPtr->ship_info.var2 = (BYTE)StarShipPtr->ShipFacing;
StarShipPtr->ship_input_state = 0; StarShipPtr->ship_input_state = 0;
StarShipPtr->cur_status_flags = StarShipPtr->cur_status_flags = 0;
StarShipPtr->old_status_flags = 0; StarShipPtr->old_status_flags = 0;
if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER
|| LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) || LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
@@ -420,6 +420,7 @@ spawn_ship (STARSHIPPTR StarShipPtr)
StarShipPtr->hShip = hShip; StarShipPtr->hShip = hShip;
if (StarShipPtr->hShip != 0) if (StarShipPtr->hShip != 0)
{ {
// Construct an ELEMENT for the STARSHIP
ELEMENTPTR ShipElementPtr; ELEMENTPTR ShipElementPtr;
LockElement (hShip, &ShipElementPtr); LockElement (hShip, &ShipElementPtr);
@@ -437,6 +438,7 @@ spawn_ship (STARSHIPPTR StarShipPtr)
if ((ShipElementPtr->state_flags & BAD_GUY) if ((ShipElementPtr->state_flags & BAD_GUY)
&& LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
{ {
// This is the Sa-Matra
StarShipPtr->ShipFacing = 0; StarShipPtr->ShipFacing = 0;
ShipElementPtr->current.image.frame = ShipElementPtr->current.image.frame =
SetAbsFrameIndex (RDPtr->ship_data.ship[0], SetAbsFrameIndex (RDPtr->ship_data.ship[0],
@@ -452,7 +454,8 @@ spawn_ship (STARSHIPPTR StarShipPtr)
{ {
COUNT facing; COUNT facing;
if ((facing = LOWORD (GLOBAL (ShipStamp.frame))) > 0) facing = LOWORD (GLOBAL (ShipStamp.frame));
if (facing > 0)
--facing; --facing;
GLOBAL (ShipStamp.frame) = (FRAME)MAKE_DWORD ( GLOBAL (ShipStamp.frame) = (FRAME)MAKE_DWORD (
+8 -8
View File
@@ -142,7 +142,8 @@ spawn_crew (PELEMENT ElementPtr)
{ {
HELEMENT hCrew; HELEMENT hCrew;
if ((hCrew = AllocElement ()) != 0) hCrew = AllocElement ();
if (hCrew != 0)
{ {
ELEMENTPTR CrewPtr; ELEMENTPTR CrewPtr;
@@ -151,9 +152,7 @@ spawn_crew (PELEMENT ElementPtr)
CrewPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE CrewPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE
| (ElementPtr->state_flags & (GOOD_GUY | BAD_GUY)); | (ElementPtr->state_flags & (GOOD_GUY | BAD_GUY));
CrewPtr->life_span = 0; CrewPtr->life_span = 0;
{ CrewPtr->death_func = spawn_crew;
CrewPtr->death_func = spawn_crew;
}
CrewPtr->pParent = ElementPtr->pParent; CrewPtr->pParent = ElementPtr->pParent;
CrewPtr->hTarget = 0; CrewPtr->hTarget = 0;
UnlockElement (hCrew); UnlockElement (hCrew);
@@ -181,12 +180,13 @@ spawn_crew (PELEMENT ElementPtr)
SIZE dx, dy; SIZE dx, dy;
DWORD d_squared; DWORD d_squared;
if ((dx = ObjPtr->next.location.x dx = ObjPtr->next.location.x - ElementPtr->next.location.x;
- ElementPtr->next.location.x) < 0) if (dx < 0)
dx = -dx; dx = -dx;
if ((dy = ObjPtr->next.location.y dy = ObjPtr->next.location.y - ElementPtr->next.location.y;
- ElementPtr->next.location.y) < 0) if (dy < 0)
dy = -dy; dy = -dy;
dx = WORLD_TO_DISPLAY (dx); dx = WORLD_TO_DISPLAY (dx);
dy = WORLD_TO_DISPLAY (dy); dy = WORLD_TO_DISPLAY (dy);
#define ABANDONER_RANGE 208 /* originally SPACE_HEIGHT */ #define ABANDONER_RANGE 208 /* originally SPACE_HEIGHT */
+45 -8
View File
@@ -25,6 +25,7 @@
# include "netplay/netmisc.h" # include "netplay/netmisc.h"
# include "netplay/notify.h" # include "netplay/notify.h"
# include "netplay/proto/ready.h" # include "netplay/proto/ready.h"
# include "netplay/packet.h"
# include "netplay/packetq.h" # include "netplay/packetq.h"
#endif #endif
#include "races.h" #include "races.h"
@@ -180,7 +181,7 @@ readyForBattleEndPlayer (NetConnection *conn, void *arg)
#endif #endif
static inline bool static inline bool
readyForBattleEnd (void) readyForBattleEnd (COUNT side)
{ {
#ifndef NETPLAY #ifndef NETPLAY
#if DEMO_MODE #if DEMO_MODE
@@ -194,9 +195,26 @@ readyForBattleEnd (void)
if (PLRPlaying ((MUSIC_REF)~0)) if (PLRPlaying ((MUSIC_REF)~0))
return false; return false;
// We can only handle one dead ship at a time. So 'deadSide' is set
// to the side we're handling now. (COUNT)~0 means we're not handling
// any side yet.
if (currentDeadSide == (COUNT)~0)
{
// Not handling any side yet.
currentDeadSide = side;
}
else if (side != currentDeadSide)
{
// We're handing another side at the moment.
return false;
}
if (!forAllConnectedPlayers (readyForBattleEndPlayer, NULL)) if (!forAllConnectedPlayers (readyForBattleEndPlayer, NULL))
return false; return false;
currentDeadSide = (COUNT)~0;
// Another side may be handled.
return true; return true;
#endif /* defined (NETPLAY) */ #endif /* defined (NETPLAY) */
} }
@@ -239,9 +257,9 @@ new_ship (PELEMENT DeadShipPtr)
DeadShipPtr->turn_wait = (BYTE)( DeadShipPtr->turn_wait = (BYTE)(
DeadShipPtr->state_flags & (GOOD_GUY | BAD_GUY)); DeadShipPtr->state_flags & (GOOD_GUY | BAD_GUY));
// DeadShipPtr->turn_wait is abused to store which // DeadShipPtr->turn_wait is abused to store which
// side this element is for, probably because this // side this element is for, because this information
// information will be lost from state_flags (why is this // will be lost from state_flags when the element is
// necessary?). // set up for deletion below.
for (hElement = GetHeadElement (); hElement; hElement = hSuccElement) for (hElement = GetHeadElement (); hElement; hElement = hSuccElement)
{ {
ELEMENTPTR ElementPtr; ELEMENTPTR ElementPtr;
@@ -250,14 +268,20 @@ new_ship (PELEMENT DeadShipPtr)
LockElement (hElement, &ElementPtr); LockElement (hElement, &ElementPtr);
hSuccElement = GetSuccElement (ElementPtr); hSuccElement = GetSuccElement (ElementPtr);
GetElementStarShip (ElementPtr, &StarShipPtr); GetElementStarShip (ElementPtr, &StarShipPtr);
// Get the STARSHIP that this ELEMENT belongs to.
if (StarShipPtr == DeadStarShipPtr) if (StarShipPtr == DeadStarShipPtr)
{ {
// This element belongs to the dead ship; it may be the
// ship's own element.
SetElementStarShip (ElementPtr, 0); SetElementStarShip (ElementPtr, 0);
if (!(ElementPtr->state_flags & CREW_OBJECT) if (!(ElementPtr->state_flags & CREW_OBJECT)
|| ElementPtr->preprocess_func != crew_preprocess) || ElementPtr->preprocess_func != crew_preprocess)
{ {
SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM); // Set the element up for deletion.
SetPrimType (&DisplayArray[ElementPtr->PrimIndex],
NO_PRIM);
ElementPtr->life_span = 0; ElementPtr->life_span = 0;
ElementPtr->state_flags = ElementPtr->state_flags =
NONSOLID | DISAPPEARING | FINITE_LIFE; NONSOLID | DISAPPEARING | FINITE_LIFE;
@@ -271,6 +295,7 @@ new_ship (PELEMENT DeadShipPtr)
if (StarShipPtr if (StarShipPtr
&& (StarShipPtr->cur_status_flags & PLAY_VICTORY_DITTY)) && (StarShipPtr->cur_status_flags & PLAY_VICTORY_DITTY))
{ {
// StarShipPtr points to the surviving ship.
MusicStarted = TRUE; MusicStarted = TRUE;
PlayMusic ((MUSIC_REF)StarShipPtr->RaceDescPtr-> PlayMusic ((MUSIC_REF)StarShipPtr->RaceDescPtr->
ship_data.victory_ditty, FALSE, 3); ship_data.victory_ditty, FALSE, 3);
@@ -287,7 +312,8 @@ new_ship (PELEMENT DeadShipPtr)
SetElementStarShip (DeadShipPtr, DeadStarShipPtr); SetElementStarShip (DeadShipPtr, DeadStarShipPtr);
} }
if (DeadShipPtr->life_span || !readyForBattleEnd ()) if (DeadShipPtr->life_span || !readyForBattleEnd (
WHICH_SIDE (DeadShipPtr->turn_wait)))
{ {
DeadShipPtr->state_flags &= ~DISAPPEARING; DeadShipPtr->state_flags &= ~DISAPPEARING;
++DeadShipPtr->life_span; ++DeadShipPtr->life_span;
@@ -328,7 +354,7 @@ UnbatchGraphics ();
#endif /* NETPLAY */ #endif /* NETPLAY */
if (GetNextStarShip (DeadStarShipPtr, if (GetNextStarShip (DeadStarShipPtr,
WHICH_SIDE (DeadShipPtr->turn_wait)) && RestartMusic) WHICH_SIDE (DeadShipPtr->turn_wait)))
{ {
#ifdef NETPLAY #ifdef NETPLAY
{ {
@@ -343,11 +369,22 @@ UnbatchGraphics ();
} }
} }
#endif #endif
BattleSong (TRUE); if (RestartMusic)
BattleSong (TRUE);
} }
else if (LOBYTE (battle_counter) == 0 else if (LOBYTE (battle_counter) == 0
|| HIBYTE (battle_counter) == 0) || HIBYTE (battle_counter) == 0)
{
// One player is out of ships. The battle is over.
GLOBAL (CurrentActivity) &= ~IN_BATTLE; GLOBAL (CurrentActivity) &= ~IN_BATTLE;
}
#ifdef NETPLAY
else
{
// Battle has been aborted.
GLOBAL (CurrentActivity) |= CHECK_ABORT;
}
#endif
#ifdef NETPLAY #ifdef NETPLAY
// Turn_wait was abused to store the side this element was on. // Turn_wait was abused to store the side this element was on.