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:
@@ -1,4 +1,5 @@
|
||||
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
|
||||
- Shifted the Mouse Error to a Popup Window, moved the message to
|
||||
starcon.txt for translators - Michael
|
||||
|
||||
@@ -2261,8 +2261,8 @@ Awaiting incoming connection
|
||||
#(Attempting outgoing connection...)
|
||||
Attempting outgoing connection
|
||||
|
||||
#(Connected. Press RIGHT to disconnect.)
|
||||
Connected. Change setting to disconnect.
|
||||
#(Connected. Change the control method to disconnect.)
|
||||
Connected. Change the control method to disconnect.
|
||||
|
||||
|
||||
#(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
|
||||
|
||||
|
||||
#(Connect to remote host)
|
||||
Connect to remote host
|
||||
|
||||
|
||||
#(Wait for incoming connection)
|
||||
Wait for incoming connection
|
||||
|
||||
|
||||
#(Cancel)
|
||||
Cancel
|
||||
|
||||
|
||||
#(Host)
|
||||
Host
|
||||
|
||||
|
||||
#(Port)
|
||||
Port
|
||||
|
||||
|
||||
#(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.
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
- 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
|
||||
common data. Both parties have to agree with the data for either
|
||||
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.
|
||||
|
||||
|
||||
============================================================================
|
||||
|
||||
"Reset" negotiation.
|
||||
|
||||
See src/sc2code/netplay/proto.c
|
||||
|
||||
|
||||
============================================================================
|
||||
|
||||
Battle ending negotiation.
|
||||
|
||||
@@ -117,9 +117,6 @@ To put in the announcement of 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
|
||||
of inactivity
|
||||
- DoRunAway() shouldn't be handled in ProcessInput()
|
||||
|
||||
@@ -2604,6 +2604,14 @@ SOURCE=..\sc2code\netplay\proto\ready.c
|
||||
|
||||
SOURCE=..\sc2code\netplay\proto\ready.h
|
||||
# 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
|
||||
|
||||
+42
-10
@@ -47,6 +47,12 @@ size_t battleInputOrder[NUM_SIDES];
|
||||
#ifdef NETPLAY
|
||||
BattleFrameCounter battleFrameCount;
|
||||
// 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
|
||||
|
||||
static BOOLEAN
|
||||
@@ -158,7 +164,7 @@ ProcessInput (void)
|
||||
JournalInput (InputState);
|
||||
#endif /* CREATE_JOURNAL */
|
||||
#ifdef NETPLAY
|
||||
if (PlayerControl[cur_player] & HUMAN_CONTROL)
|
||||
if (!(PlayerControl[cur_player] & NETWORK_CONTROL))
|
||||
{
|
||||
BattleInputBuffer *bib = getBattleInputBuffer(cur_player);
|
||||
sendBattleInputConnections (InputState);
|
||||
@@ -278,8 +284,11 @@ DoBattle (BATTLE_STATE *bs)
|
||||
if (battleFrameCount >= delay
|
||||
&& (battleFrameCount - delay) % NETPLAY_CHECKSUM_INTERVAL == 0)
|
||||
{
|
||||
if (!verifyChecksums (battleFrameCount - delay))
|
||||
if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
|
||||
if (!verifyChecksums (battleFrameCount - delay)) {
|
||||
GLOBAL(CurrentActivity) |= CHECK_ABORT;
|
||||
resetConnections(ResetReason_syncLoss);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -440,24 +449,32 @@ Battle (void)
|
||||
CountLinks (&race_q[1])
|
||||
);
|
||||
|
||||
if (!selectAllShips (num_ships))
|
||||
goto AbortBattle;
|
||||
|
||||
BattleSong (TRUE);
|
||||
bs.NextTime = 0;
|
||||
setupBattleInputOrder ();
|
||||
#ifdef NETPLAY
|
||||
initBattleInputBuffers ();
|
||||
#ifdef NETPLAY_CHECKSUM
|
||||
initChecksumBuffers ();
|
||||
#endif
|
||||
#endif /* NETPLAY_CHECKSUM */
|
||||
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 ();
|
||||
{
|
||||
bool allOk = negotiateReadyConnections (true, NetState_inBattle);
|
||||
if (!allOk)
|
||||
if (!allOk) {
|
||||
GLOBAL (CurrentActivity) |= CHECK_ABORT;
|
||||
goto AbortBattle;
|
||||
}
|
||||
}
|
||||
#endif /* NETPLAY */
|
||||
bs.InputFunc = DoBattle;
|
||||
bs.first_time = (BOOLEAN)(LOBYTE (GLOBAL (CurrentActivity)) ==
|
||||
@@ -468,11 +485,26 @@ Battle (void)
|
||||
LockMutex (GraphicsLock);
|
||||
|
||||
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
|
||||
uninitBattleInputBuffers();
|
||||
#ifdef NETPLAY_CHECKSUM
|
||||
uninitChecksumBuffers ();
|
||||
#endif
|
||||
#endif /* NETPLAY_CHECKSUM */
|
||||
#endif /* NETPLAY */
|
||||
|
||||
StopMusic ();
|
||||
|
||||
@@ -25,6 +25,7 @@ extern BOOLEAN instantVictory;
|
||||
#ifdef NETPLAY
|
||||
typedef DWORD BattleFrameCounter;
|
||||
extern BattleFrameCounter battleFrameCount;
|
||||
extern COUNT currentDeadSide;
|
||||
#endif
|
||||
|
||||
extern BOOLEAN Battle (void);
|
||||
|
||||
@@ -95,6 +95,7 @@ typedef struct state
|
||||
} STATE;
|
||||
typedef STATE *PSTATE;
|
||||
|
||||
// Any physical object in the simulation.
|
||||
typedef struct element
|
||||
{
|
||||
HELEMENT pred, succ;
|
||||
@@ -116,6 +117,7 @@ typedef struct element
|
||||
STATE current, next;
|
||||
|
||||
PVOID pParent;
|
||||
// The ship this element belongs to.
|
||||
HELEMENT hTarget;
|
||||
} ELEMENT;
|
||||
typedef ELEMENT *PELEMENT;
|
||||
|
||||
@@ -923,7 +923,7 @@ enum
|
||||
START_INTERPLANETARY = MAKE_WORD (0, (1 << 3)),
|
||||
CHECK_LOAD = MAKE_WORD (0, (1 << 4)),
|
||||
CHECK_RESTART = MAKE_WORD (0, (1 << 5)),
|
||||
CHECK_ABORT = MAKE_WORD (0, (1 << 6))
|
||||
CHECK_ABORT = MAKE_WORD (0, (1 << 6)),
|
||||
};
|
||||
typedef UWORD ACTIVITY;
|
||||
|
||||
|
||||
+193
-73
@@ -575,6 +575,7 @@ DrawPickIcon (COUNT iship, BYTE DrawErase)
|
||||
|
||||
#ifdef NETPLAY
|
||||
// This function is generic. It should probably be moved to elsewhere.
|
||||
// The caller should hold the GraphicsLock.
|
||||
static void
|
||||
multiLineDrawText(TEXT *textIn, RECT *clipRect) {
|
||||
RECT oldRect;
|
||||
@@ -701,6 +702,7 @@ UpdateMeleeStatusMessage (ssize_t player)
|
||||
#endif /* NETPLAY */
|
||||
|
||||
// XXX: this function is called when the current selection is blinking off.
|
||||
// The caller should hold the GraphicsLock.
|
||||
static void
|
||||
Deselect (BYTE opt)
|
||||
{
|
||||
@@ -760,6 +762,7 @@ Deselect (BYTE opt)
|
||||
}
|
||||
|
||||
// XXX: this function is called when the current selection is blinking off.
|
||||
// The caller should hold the GraphicsLock.
|
||||
static void
|
||||
Select (BYTE opt)
|
||||
{
|
||||
@@ -2303,7 +2306,6 @@ DoConnectingDialog (PMELEE_STATE pMS)
|
||||
if ((status == NetState_init) ||
|
||||
(status == NetState_inSetup))
|
||||
{
|
||||
RECT r;
|
||||
/* Connection complete! */
|
||||
PlayerControl[which_side] = NETWORK_CONTROL | STANDARD_RATING;
|
||||
SetPlayerInput ();
|
||||
@@ -2349,7 +2351,8 @@ check_for_disconnections (PMELEE_STATE pMS)
|
||||
{
|
||||
PlayerControl[player] = HUMAN_CONTROL & STANDARD_RATING;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -2358,6 +2361,8 @@ check_for_disconnections (PMELEE_STATE pMS)
|
||||
{
|
||||
SetPlayerInput ();
|
||||
}
|
||||
|
||||
(void) pMS;
|
||||
}
|
||||
|
||||
#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
|
||||
Melee (void)
|
||||
{
|
||||
@@ -2880,50 +2967,13 @@ Melee (void)
|
||||
|
||||
GameSounds = CaptureSound (LoadSound (GAME_SOUNDS));
|
||||
LoadMeleeInfo (&MenuState);
|
||||
{
|
||||
uio_Stream *load_fp;
|
||||
|
||||
load_fp = res_OpenResFile (configDir, "melee.cfg", "rb");
|
||||
if (load_fp)
|
||||
{
|
||||
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)
|
||||
if (LoadMeleeConfig (&MenuState) == -1)
|
||||
{
|
||||
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 ();
|
||||
teamStringChanged (&MenuState, 0);
|
||||
teamStringChanged (&MenuState, 1);
|
||||
@@ -2939,35 +2989,7 @@ Melee (void)
|
||||
StopMusic ();
|
||||
WaitForSoundEnd (TFBSOUND_WAIT_ALL);
|
||||
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
WriteMeleeConfig (&MenuState);
|
||||
FreeMeleeInfo (&MenuState);
|
||||
DestroySound (ReleaseSound (GameSounds));
|
||||
GameSounds = 0;
|
||||
@@ -3083,6 +3105,7 @@ updateTeamName (PMELEE_STATE pMS, COUNT side, const char *name,
|
||||
strncpy (pMS->TeamImage[side].TeamName, name, len);
|
||||
pMS->TeamImage[side].TeamName[len] = '\0';
|
||||
|
||||
LockMutex (GraphicsLock);
|
||||
#if 0 /* DTSHS_REPAIR does not combine with other options */
|
||||
if (pMS->MeleeOption == EDIT_MELEE && pMS->side == side
|
||||
&& pMS->row == NUM_MELEE_ROWS)
|
||||
@@ -3090,6 +3113,7 @@ updateTeamName (PMELEE_STATE pMS, COUNT side, const char *name,
|
||||
else
|
||||
#endif
|
||||
DrawTeamString (pMS, side, DTSHS_REPAIR);
|
||||
UnlockMutex (GraphicsLock);
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Ship to be updated is the currently selected one.
|
||||
|
||||
LockMutex (GraphicsLock);
|
||||
if (ship == MELEE_NONE)
|
||||
{
|
||||
RECT r;
|
||||
@@ -3148,12 +3173,14 @@ updateFleetShip (PMELEE_STATE pMS, COUNT side, COUNT index, BYTE ship)
|
||||
// Reprint the team value:
|
||||
//DrawTeamString (pMeleeState, side, DTSHS_NORMAL);
|
||||
DrawTeamString (pMS, side, DTSHS_REPAIR);
|
||||
UnlockMutex (GraphicsLock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed) {
|
||||
updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed)
|
||||
{
|
||||
TFB_SeedRandom (seed);
|
||||
(void) pMS;
|
||||
(void) side;
|
||||
@@ -3161,51 +3188,144 @@ updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed) {
|
||||
|
||||
// The remote player has done something which invalidates our confirmation.
|
||||
void
|
||||
confirmationCancelled(PMELEE_STATE pMS, COUNT side) {
|
||||
confirmationCancelled(PMELEE_STATE pMS, COUNT side)
|
||||
{
|
||||
LockMutex (GraphicsLock);
|
||||
if (side == 0)
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 16));
|
||||
// "Bottom player changed something -- need to reconfirm."
|
||||
else
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 17));
|
||||
// "Top player changed something -- need to reconfirm."
|
||||
UnlockMutex (GraphicsLock);
|
||||
|
||||
if (pMS->InputFunc == DoConfirmSettings)
|
||||
pMS->InputFunc = DoMelee;
|
||||
}
|
||||
|
||||
void
|
||||
connectedFeedback (PMELEE_STATE pMS, COUNT side) {
|
||||
LockMutex (GraphicsLock);
|
||||
if (side == 0)
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 8));
|
||||
// "Bottom player is connected."
|
||||
else
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 9));
|
||||
// "Top player is connected."
|
||||
UnlockMutex (GraphicsLock);
|
||||
|
||||
PlayMenuSound (MENU_SOUND_INVOKED);
|
||||
(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
|
||||
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)
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 10));
|
||||
// "Bottom player: connection failed."
|
||||
else
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 11));
|
||||
// "Top player: connection failed."
|
||||
UnlockMutex (GraphicsLock);
|
||||
(void) pMS;
|
||||
}
|
||||
|
||||
void
|
||||
closeFeedback (PMELEE_STATE pMS, COUNT side) {
|
||||
closeFeedback (PMELEE_STATE pMS, COUNT side)
|
||||
{
|
||||
LockMutex (GraphicsLock);
|
||||
if (side == 0)
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 12));
|
||||
// "Bottom player: connection closed."
|
||||
else
|
||||
DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 13));
|
||||
// "Top player: connection closed."
|
||||
UnlockMutex (GraphicsLock);
|
||||
(void) pMS;
|
||||
}
|
||||
|
||||
#endif /* NETPLAY */
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "libs/gfxlib.h"
|
||||
#include "libs/sndlib.h"
|
||||
#include "libs/reslib.h"
|
||||
#include "netplay/packet.h"
|
||||
// for NetplayAbortREason and NetplayResetReason.
|
||||
|
||||
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 confirmationCancelled(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 closeFeedback (PMELEE_STATE pMS, COUNT side);
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ NetConnection *
|
||||
NetConnection_open(int player, const NetplayPeerOptions *options,
|
||||
NetConnection_ConnectCallback connectCallback,
|
||||
NetConnection_CloseCallback closeCallback,
|
||||
NetConnection_ErrorCallback errorCallback, void *extra) {
|
||||
NetConnection_ErrorCallback errorCallback,
|
||||
NetConnection_DeleteCallback deleteCallback, void *extra) {
|
||||
NetConnection *conn;
|
||||
|
||||
conn = malloc(sizeof (NetConnection));
|
||||
@@ -62,8 +63,11 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
|
||||
conn->connectCallback = connectCallback;
|
||||
conn->closeCallback = closeCallback;
|
||||
conn->errorCallback = errorCallback;
|
||||
conn->deleteCallback = deleteCallback;
|
||||
conn->readyCallback = NULL;
|
||||
conn->readyCallbackArg = NULL;
|
||||
conn->resetCallback = NULL;
|
||||
conn->resetCallbackArg = NULL;
|
||||
|
||||
conn->readBuf = malloc(NETPLAY_READBUFSIZE);
|
||||
conn->readEnd = conn->readBuf;
|
||||
@@ -80,6 +84,8 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
|
||||
conn->stateFlags.handshake.canceling = false;
|
||||
conn->stateFlags.ready.localReady = false;
|
||||
conn->stateFlags.ready.remoteReady = false;
|
||||
conn->stateFlags.reset.localReset = false;
|
||||
conn->stateFlags.reset.remoteReset = false;
|
||||
conn->stateFlags.agreement = Agreement_nothingAgreed;
|
||||
conn->stateFlags.inputDelay = 0;
|
||||
#ifdef NETPLAY_CHECKSUM
|
||||
@@ -105,8 +111,22 @@ NetConnection_open(int player, const NetplayPeerOptions *options,
|
||||
return conn;
|
||||
}
|
||||
|
||||
static void
|
||||
NetConnection_doDeleteCallback(NetConnection *conn) {
|
||||
if (conn->deleteCallback != NULL) {
|
||||
//NetConnection_incRef(conn);
|
||||
conn->deleteCallback(conn);
|
||||
//NetConnection_decRef(conn);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
NetConnection_delete(NetConnection *conn) {
|
||||
NetConnection_doDeleteCallback(conn);
|
||||
if (conn->stateData != NULL) {
|
||||
NetConnectionStateData_release(conn->stateData);
|
||||
conn->stateData = NULL;
|
||||
}
|
||||
free(conn->readBuf);
|
||||
PacketQueue_uninit(&conn->queue);
|
||||
free(conn);
|
||||
@@ -133,10 +153,6 @@ NetConnection_doClose(NetConnection *conn) {
|
||||
Netplay_doCloseCallback(conn);
|
||||
|
||||
NetConnection_setState(conn, NetState_unconnected);
|
||||
if (conn->stateData != NULL) {
|
||||
NetConnectionStateData_release(conn->stateData);
|
||||
conn->stateData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the NetDescriptor is shut down.
|
||||
@@ -213,6 +229,23 @@ NetConnection_getReadyCallbackArg(const NetConnection *conn) {
|
||||
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
|
||||
NetConnection_setState(NetConnection *conn, NetState state) {
|
||||
#ifdef NETPLAY_DEBUG
|
||||
|
||||
@@ -33,8 +33,10 @@ typedef void (*NetConnection_ConnectCallback)(NetConnection *nd);
|
||||
typedef void (*NetConnection_CloseCallback)(NetConnection *nd);
|
||||
typedef void (*NetConnection_ErrorCallback)(NetConnection *nd,
|
||||
const NetConnectionError *error);
|
||||
typedef void (*NetConnection_DeleteCallback)(NetConnection *nd);
|
||||
|
||||
typedef void (*NetConnection_ReadyCallback)(NetConnection *conn, void *arg);
|
||||
typedef void (*NetConnection_ResetCallback)(NetConnection *conn, void *arg);
|
||||
|
||||
#include "netstate.h"
|
||||
#include "netoptions.h"
|
||||
@@ -80,6 +82,11 @@ typedef struct {
|
||||
bool remoteReady : 1;
|
||||
} ReadyFlags;
|
||||
|
||||
typedef struct {
|
||||
bool localReset : 1;
|
||||
bool remoteReset : 1;
|
||||
} ResetFlags;
|
||||
|
||||
// Which parameters have we both sides of a connection reached agreement on?
|
||||
typedef struct {
|
||||
bool randomSeed : 1;
|
||||
@@ -108,6 +115,7 @@ typedef struct {
|
||||
* during a connection. Undefined while not connected. */
|
||||
HandShakeFlags handshake;
|
||||
ReadyFlags ready;
|
||||
ResetFlags reset;
|
||||
Agreement agreement;
|
||||
size_t inputDelay;
|
||||
/* Used during negotiation of the actual inputDelay. This
|
||||
@@ -135,6 +143,13 @@ struct NetConnection {
|
||||
// Extra argument for readyCallback().
|
||||
// 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;
|
||||
PacketQueue queue;
|
||||
#ifdef NETPLAY_STATISTICS
|
||||
@@ -145,7 +160,10 @@ struct NetConnection {
|
||||
#endif
|
||||
NetConnection_ConnectCallback connectCallback;
|
||||
NetConnection_CloseCallback closeCallback;
|
||||
// Called when the NetConnection becomes disconnected.
|
||||
NetConnection_ErrorCallback errorCallback;
|
||||
NetConnection_DeleteCallback deleteCallback;
|
||||
// Called when the NetConnection is destroyed.
|
||||
uint8 *readBuf;
|
||||
uint8 *readEnd;
|
||||
NetConnectionStateData *stateData;
|
||||
@@ -170,7 +188,8 @@ NetConnection *NetConnection_open(int player,
|
||||
const NetplayPeerOptions *options,
|
||||
NetConnection_ConnectCallback connectCallback,
|
||||
NetConnection_CloseCallback closeCallback,
|
||||
NetConnection_ErrorCallback errorCallback, void *extra);
|
||||
NetConnection_ErrorCallback errorCallback,
|
||||
NetConnection_DeleteCallback deleteCallback, void *extra);
|
||||
void NetConnection_close(NetConnection *conn);
|
||||
bool NetConnection_isConnected(const NetConnection *conn);
|
||||
|
||||
@@ -203,6 +222,12 @@ NetConnection_ReadyCallback NetConnection_getReadyCallback(
|
||||
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 */
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "netplay/packetq.h"
|
||||
#include "netplay/proto/npconfirm.h"
|
||||
#include "netplay/proto/ready.h"
|
||||
#include "netplay/proto/reset.h"
|
||||
|
||||
#include "build.h"
|
||||
// for StarShipPlayer()
|
||||
@@ -65,9 +66,13 @@ closeAllConnections(void) {
|
||||
COUNT player;
|
||||
|
||||
for (player = 0; player < NUM_PLAYERS; player++)
|
||||
if (netConnections[player] != NULL)
|
||||
{
|
||||
NetConnection *conn = netConnections[player];
|
||||
|
||||
if (conn != NULL && NetConnection_isConnected(conn))
|
||||
closePlayerNetworkConnection(player);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
getNumNetConnections(void) {
|
||||
@@ -100,6 +105,9 @@ netInputBlocking(uint32 timeoutMs) {
|
||||
timeoutMs = nextAlarmMs;
|
||||
|
||||
NetManager_process(&timeoutMs);
|
||||
// This may cause more packets to be queued, hence the
|
||||
// flushPacketQueues().
|
||||
flushPacketQueues();
|
||||
|
||||
Alarm_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
|
||||
sendBattleInputConnections(BATTLE_INPUT_STATE input) {
|
||||
COUNT player;
|
||||
@@ -297,6 +321,11 @@ networkBattleInput(COUNT player, STARSHIPPTR StarShipPtr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
deleteConnectionCallback(NetConnection *conn) {
|
||||
removeNetConnection(NetConnection_getPlayerNr(conn));
|
||||
}
|
||||
|
||||
NetConnection *
|
||||
openPlayerNetworkConnection(COUNT player, void *extra) {
|
||||
NetConnection *conn;
|
||||
@@ -305,7 +334,8 @@ openPlayerNetworkConnection(COUNT player, void *extra) {
|
||||
|
||||
conn = NetConnection_open(player,
|
||||
&netplayOptions.peer[player], NetMelee_connectCallback,
|
||||
NetMelee_closeCallback, NetMelee_errorCallback, extra);
|
||||
NetMelee_closeCallback, NetMelee_errorCallback,
|
||||
deleteConnectionCallback, extra);
|
||||
|
||||
addNetConnection(conn, player);
|
||||
return conn;
|
||||
@@ -321,7 +351,6 @@ closePlayerNetworkConnection(COUNT player) {
|
||||
assert(netConnections[player] != NULL);
|
||||
|
||||
NetConnection_close(netConnections[player]);
|
||||
removeNetConnection(player);
|
||||
}
|
||||
|
||||
// If the callback function returns 'false', the function will immediately
|
||||
@@ -392,6 +421,30 @@ setStateConnections(NetState 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 {
|
||||
@@ -422,6 +475,7 @@ localReadyConnections(NetConnection_ReadyCallback readyCallback,
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define NETWORK_POLL_DELAY (ONE_SECOND / 24)
|
||||
|
||||
typedef struct NegotiateReadyState NegotiateReadyState;
|
||||
struct NegotiateReadyState {
|
||||
@@ -437,7 +491,6 @@ struct NegotiateReadyState {
|
||||
|
||||
static BOOLEAN
|
||||
negotiateReadyInputFunc(NegotiateReadyState *state) {
|
||||
#define NETWORK_POLL_DELAY (ONE_SECOND / 24)
|
||||
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,
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "netplay.h"
|
||||
#include "netinput.h"
|
||||
#include "netconnection.h"
|
||||
#include "packetsenders.h"
|
||||
|
||||
#include "../controls.h"
|
||||
// for BATTLE_INPUT_STATE
|
||||
@@ -44,6 +45,8 @@ void confirmConnections(void);
|
||||
void cancelConfirmations(void);
|
||||
void connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg);
|
||||
|
||||
bool allConnected(void);
|
||||
|
||||
void sendBattleInputConnections(BATTLE_INPUT_STATE input);
|
||||
void sendChecksumConnections(uint32 frameNr, uint32 checksum);
|
||||
void initBattleStateDataConnections(void);
|
||||
@@ -59,6 +62,8 @@ bool forAllConnectedPlayers(ForAllCallback callback, void *arg);
|
||||
bool setupInputDelay(size_t localInputDelay);
|
||||
bool sendInputDelayConnections(size_t delay);
|
||||
bool setStateConnections(NetState state);
|
||||
bool sendAbortConnections(NetplayAbortReason reason);
|
||||
bool resetConnections(NetplayResetReason reason);
|
||||
bool localReadyConnections(NetConnection_ReadyCallback readyCallback,
|
||||
void *arg, bool notifyRemote);
|
||||
|
||||
@@ -67,6 +72,9 @@ bool negotiateReady(NetConnection *conn, bool notifyRemote,
|
||||
bool negotiateReadyConnections(bool notifyRemote, NetState nextState);
|
||||
bool waitReady(NetConnection *conn);
|
||||
|
||||
bool waitReset(NetConnection *conn, NetState nextState);
|
||||
bool waitResetConnections(NetState nextState);
|
||||
|
||||
#endif /* _NETMELEE_H */
|
||||
|
||||
|
||||
|
||||
@@ -121,8 +121,6 @@ dataReceivedMulti(NetConnection *conn, const uint8 *data, size_t len) {
|
||||
return processed;
|
||||
}
|
||||
|
||||
// Returns -1 on error (setting errno), or 0 if everything went
|
||||
// ok, regardless of whether any packets were actually processed.
|
||||
void
|
||||
dataReadyCallback(NetDescriptor *nd) {
|
||||
NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
typedef struct NetConnectionStateData NetConnectionStateData;
|
||||
|
||||
// State of a NetConnection.
|
||||
typedef enum {
|
||||
NetState_unconnected, /* No connection initiated */
|
||||
NetState_connecting, /* Connection being setup */
|
||||
|
||||
@@ -54,6 +54,8 @@ PacketTypeData packetTypeData[PACKET_NUM] = {
|
||||
DEFINE_PACKETDATA(BattleInput, false),
|
||||
DEFINE_PACKETDATA(FrameCount, false),
|
||||
DEFINE_PACKETDATA(Checksum, false),
|
||||
DEFINE_PACKETDATA(Abort, false),
|
||||
DEFINE_PACKETDATA(Reset, false),
|
||||
};
|
||||
|
||||
static inline void *
|
||||
@@ -253,5 +255,19 @@ Packet_Checksum_create(uint32 frameNr, uint32 checksum) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,10 +39,26 @@ typedef enum PacketType {
|
||||
PACKET_BATTLEINPUT,
|
||||
PACKET_FRAMECOUNT,
|
||||
PACKET_CHECKSUM,
|
||||
PACKET_ABORT,
|
||||
PACKET_RESET,
|
||||
|
||||
PACKET_NUM, /* Number of packet types */
|
||||
} 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
|
||||
#include "netconnection.h"
|
||||
@@ -227,6 +243,18 @@ typedef struct {
|
||||
uint32 checksum; /* Actually 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
|
||||
void Packet_delete(Packet *packet);
|
||||
@@ -248,6 +276,8 @@ Packet_SelectShip *Packet_SelectShip_create(uint16 ship);
|
||||
Packet_BattleInput *Packet_BattleInput_create(uint8 state);
|
||||
Packet_FrameCount *Packet_FrameCount_create(uint32 frameCount);
|
||||
Packet_Checksum *Packet_Checksum_create(uint32 frameNr, uint32 checksum);
|
||||
Packet_Abort *Packet_Abort_create(uint16 reason);
|
||||
Packet_Reset *Packet_Reset_create(uint16 reason);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -28,12 +28,15 @@
|
||||
#include "packetsenders.h"
|
||||
#include "proto/npconfirm.h"
|
||||
#include "proto/ready.h"
|
||||
#include "proto/reset.h"
|
||||
#include "libs/log.h"
|
||||
|
||||
#include "controls.h"
|
||||
// for BATTLE_INPUT_STATE
|
||||
#include "init.h"
|
||||
// for NUM_PLAYERS
|
||||
#include "globdata.h"
|
||||
// for GLOBAL
|
||||
#include "melee.h"
|
||||
// for various update functions.
|
||||
#include "pickmele.h"
|
||||
@@ -56,6 +59,13 @@ testNetState(bool condition, PacketType type) {
|
||||
|
||||
int
|
||||
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 &&
|
||||
!conn->stateFlags.ready.remoteReady, PACKET_INIT))
|
||||
return -1; // errno is set
|
||||
@@ -152,6 +162,13 @@ checkYourTurn(NetConnection *conn, PacketType type) {
|
||||
|
||||
int
|
||||
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) &&
|
||||
!conn->stateFlags.ready.remoteReady, PACKET_READY))
|
||||
return -1; // errno is set
|
||||
@@ -172,6 +189,13 @@ PacketHandler_Fleet(NetConnection *conn, const Packet_Fleet *packet) {
|
||||
int player;
|
||||
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))
|
||||
return -1; // errno is set
|
||||
|
||||
@@ -223,6 +247,13 @@ PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
|
||||
int side;
|
||||
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))
|
||||
return -1; // errno is set
|
||||
|
||||
@@ -262,6 +293,13 @@ handshakeComplete(NetConnection *conn) {
|
||||
int
|
||||
PacketHandler_Handshake0(NetConnection *conn,
|
||||
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)
|
||||
&& !conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKE0))
|
||||
return -1; // errno is set
|
||||
@@ -280,6 +318,13 @@ PacketHandler_Handshake0(NetConnection *conn,
|
||||
int
|
||||
PacketHandler_Handshake1(NetConnection *conn,
|
||||
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) &&
|
||||
(conn->stateFlags.handshake.localOk ||
|
||||
conn->stateFlags.handshake.canceling), PACKET_HANDSHAKE1))
|
||||
@@ -312,6 +357,13 @@ PacketHandler_Handshake1(NetConnection *conn,
|
||||
int
|
||||
PacketHandler_HandshakeCancel(NetConnection *conn,
|
||||
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)
|
||||
&& conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKECANCEL))
|
||||
return -1; // errno is set
|
||||
@@ -328,6 +380,13 @@ PacketHandler_HandshakeCancel(NetConnection *conn,
|
||||
int
|
||||
PacketHandler_HandshakeCancelAck(NetConnection *conn,
|
||||
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)
|
||||
&& conn->stateFlags.handshake.canceling,
|
||||
PACKET_HANDSHAKECANCELACK))
|
||||
@@ -352,6 +411,13 @@ PacketHandler_SeedRandom(NetConnection *conn,
|
||||
const Packet_SeedRandom *packet) {
|
||||
BattleStateData *battleStateData;
|
||||
|
||||
if (conn->stateFlags.reset.localReset)
|
||||
return 0;
|
||||
if (conn->stateFlags.reset.remoteReset) {
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!testNetState(conn->state == NetState_preBattle &&
|
||||
!conn->stateFlags.discriminant, PACKET_SEEDRANDOM))
|
||||
return -1; // errno is set
|
||||
@@ -370,6 +436,13 @@ PacketHandler_InputDelay(NetConnection *conn,
|
||||
BattleStateData *battleStateData;
|
||||
uint32 delay;
|
||||
|
||||
if (conn->stateFlags.reset.localReset)
|
||||
return 0;
|
||||
if (conn->stateFlags.reset.remoteReset) {
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!testNetState(conn->state == NetState_preBattle,
|
||||
PACKET_INPUTDELAY))
|
||||
return -1; // errno is set
|
||||
@@ -392,6 +465,13 @@ PacketHandler_SelectShip(NetConnection *conn,
|
||||
bool updateResult;
|
||||
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))
|
||||
return -1; // errno is set
|
||||
|
||||
@@ -413,6 +493,13 @@ PacketHandler_BattleInput(NetConnection *conn,
|
||||
BATTLE_INPUT_STATE input;
|
||||
BattleInputBuffer *bib;
|
||||
|
||||
if (conn->stateFlags.reset.localReset)
|
||||
return 0;
|
||||
if (conn->stateFlags.reset.remoteReset) {
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!testNetState(conn->state == NetState_inBattle ||
|
||||
conn->state == NetState_endingBattle ||
|
||||
conn->state == NetState_endingBattle2, PACKET_BATTLEINPUT))
|
||||
@@ -434,6 +521,13 @@ PacketHandler_FrameCount(NetConnection *conn,
|
||||
BattleStateData *battleStateData;
|
||||
BattleFrameCounter frameCount;
|
||||
|
||||
if (conn->stateFlags.reset.localReset)
|
||||
return 0;
|
||||
if (conn->stateFlags.reset.remoteReset) {
|
||||
errno = EBADMSG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!testNetState(conn->state == NetState_endingBattle,
|
||||
PACKET_FRAMECOUNT))
|
||||
return -1; // errno is set
|
||||
@@ -461,6 +555,13 @@ PacketHandler_Checksum(NetConnection *conn, const Packet_Checksum *packet) {
|
||||
size_t interval;
|
||||
#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))
|
||||
return -1; // errno is set
|
||||
|
||||
@@ -521,5 +622,25 @@ PacketHandler_Checksum(NetConnection *conn, const Packet_Checksum *packet) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@ DECLARE_PACKETHANDLER(SelectShip);
|
||||
DECLARE_PACKETHANDLER(BattleInput);
|
||||
DECLARE_PACKETHANDLER(FrameCount);
|
||||
DECLARE_PACKETHANDLER(Checksum);
|
||||
DECLARE_PACKETHANDLER(Abort);
|
||||
DECLARE_PACKETHANDLER(Reset);
|
||||
|
||||
|
||||
#endif /* _PACKETHANDLERS_H */
|
||||
|
||||
@@ -195,5 +195,21 @@ sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ void sendSelectShip(NetConnection *conn, COUNT ship);
|
||||
void sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input);
|
||||
void sendFrameCount(NetConnection *conn, uint32 frameCount);
|
||||
void sendChecksum(NetConnection *conn, uint32 frameNr, uint32 checksum);
|
||||
void sendAbort(NetConnection *conn, NetplayAbortReason reason);
|
||||
void sendReset(NetConnection *conn, NetplayResetReason reason);
|
||||
|
||||
|
||||
#endif /* _PACKETSENDERS_H */
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
uqm_CFILES="npconfirm.c ready.c"
|
||||
uqm_CFILES="npconfirm.c ready.c reset.c"
|
||||
|
||||
|
||||
@@ -36,11 +36,11 @@ Netplay_bothReady(NetConnection *conn) {
|
||||
callback = conn->readyCallback;
|
||||
readyArg = conn->readyCallbackArg;
|
||||
|
||||
conn->readyCallback = NULL;
|
||||
NetConnection_setReadyCallback(conn, NULL, NULL);
|
||||
// Clear the readyCallback field before performing the callback,
|
||||
// so that it can be set again from inside the callback
|
||||
// function.
|
||||
conn->readyCallbackArg = NULL;
|
||||
|
||||
callback(conn, readyArg);
|
||||
}
|
||||
|
||||
@@ -94,12 +94,12 @@ Netplay_remoteReady(NetConnection *conn) {
|
||||
}
|
||||
|
||||
bool
|
||||
Netplay_isLocalReady(NetConnection *conn) {
|
||||
Netplay_isLocalReady(const NetConnection *conn) {
|
||||
return conn->stateFlags.ready.localReady;
|
||||
}
|
||||
|
||||
bool
|
||||
Netplay_isRemoteReady(NetConnection *conn) {
|
||||
Netplay_isRemoteReady(const NetConnection *conn) {
|
||||
return conn->stateFlags.ready.remoteReady;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
bool Netplay_localReady(NetConnection *conn,
|
||||
NetConnection_ReadyCallback callback, void *arg, bool notifyRemote);
|
||||
bool Netplay_remoteReady(NetConnection *conn);
|
||||
bool Netplay_isLocalReady(NetConnection *conn);
|
||||
bool Netplay_isRemoteReady(NetConnection *conn);
|
||||
bool Netplay_isLocalReady(const NetConnection *conn);
|
||||
bool Netplay_isRemoteReady(const NetConnection *conn);
|
||||
|
||||
#endif /* _READY_H */
|
||||
|
||||
|
||||
+22
-20
@@ -153,7 +153,7 @@ DoGetMelee (GETMELEE_STATE *gms)
|
||||
gms->remoteSelected = FALSE;
|
||||
#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
|
||||
// synchronised.
|
||||
gms->randomIndex = (COUNT)TFB_Random () % gms->ships_left;
|
||||
@@ -165,8 +165,14 @@ DoGetMelee (GETMELEE_STATE *gms)
|
||||
SleepThread (ONE_SECOND / 120);
|
||||
#ifdef NETPLAY
|
||||
netInput ();
|
||||
|
||||
if (!allConnected())
|
||||
goto aborted;
|
||||
#endif
|
||||
|
||||
if (GLOBAL (CurrentActivity) & CHECK_ABORT)
|
||||
goto aborted;
|
||||
|
||||
if (PlayerInput[which_player] == ComputerInput)
|
||||
{
|
||||
/* 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];
|
||||
}
|
||||
|
||||
if (GLOBAL (CurrentActivity) & CHECK_ABORT)
|
||||
{
|
||||
gms->hBattleShip = 0;
|
||||
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
|
||||
#ifdef NETPLAY
|
||||
// TODO: send abort packet
|
||||
#endif
|
||||
done = true;
|
||||
}
|
||||
else if (select)
|
||||
if (select)
|
||||
{
|
||||
if (gms->col == NUM_MELEE_COLS_ORIG)
|
||||
{
|
||||
@@ -225,13 +222,9 @@ DoGetMelee (GETMELEE_STATE *gms)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exit
|
||||
// Selected exit
|
||||
if (ConfirmExit ())
|
||||
{
|
||||
gms->hBattleShip = 0;
|
||||
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
|
||||
done = TRUE;
|
||||
}
|
||||
goto aborted;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -294,6 +287,14 @@ DoGetMelee (GETMELEE_STATE *gms)
|
||||
#endif
|
||||
|
||||
return !done;
|
||||
|
||||
aborted:
|
||||
#ifdef NETPLAY
|
||||
flushPacketQueues ();
|
||||
#endif
|
||||
gms->hBattleShip = 0;
|
||||
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef NETPLAY
|
||||
@@ -495,7 +496,10 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
|
||||
SetFlashRect (NULL_PTR, (FRAME)0);
|
||||
|
||||
if (gmstate.hBattleShip == 0)
|
||||
{
|
||||
// Aborting.
|
||||
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
StarShipPtr =
|
||||
@@ -513,14 +517,12 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
|
||||
#ifdef NETPLAY
|
||||
{
|
||||
NetConnection *conn = netConnections[which_player];
|
||||
if (conn != NULL)
|
||||
if (conn != NULL && NetConnection_isConnected(conn))
|
||||
{
|
||||
BattleStateData *battleStateData;
|
||||
battleStateData = (BattleStateData *)
|
||||
NetConnection_getStateData(conn);
|
||||
battleStateData->getMeleeState = NULL;
|
||||
if (gmstate.hBattleShip == 0)
|
||||
NetConnection_setState(conn, NetState_inSetup);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -393,8 +393,7 @@ GetEncounterStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
|
||||
if (LastStarShipPtr->special_counter == 0)
|
||||
/* died in the line of duty */
|
||||
GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
|
||||
else if (GLOBAL_SIS (FuelOnBoard) >
|
||||
RUN_AWAY_FUEL_COST)
|
||||
else if (GLOBAL_SIS (FuelOnBoard) > RUN_AWAY_FUEL_COST)
|
||||
GLOBAL_SIS (FuelOnBoard) -= RUN_AWAY_FUEL_COST;
|
||||
else
|
||||
GLOBAL_SIS (FuelOnBoard) = 0;
|
||||
|
||||
@@ -387,7 +387,7 @@ spawn_ship (STARSHIPPTR StarShipPtr)
|
||||
RDPtr->ship_info.var2 = (BYTE)StarShipPtr->ShipFacing;
|
||||
|
||||
StarShipPtr->ship_input_state = 0;
|
||||
StarShipPtr->cur_status_flags =
|
||||
StarShipPtr->cur_status_flags = 0;
|
||||
StarShipPtr->old_status_flags = 0;
|
||||
|
||||
if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER
|
||||
@@ -420,6 +420,7 @@ spawn_ship (STARSHIPPTR StarShipPtr)
|
||||
StarShipPtr->hShip = hShip;
|
||||
if (StarShipPtr->hShip != 0)
|
||||
{
|
||||
// Construct an ELEMENT for the STARSHIP
|
||||
ELEMENTPTR ShipElementPtr;
|
||||
|
||||
LockElement (hShip, &ShipElementPtr);
|
||||
@@ -437,6 +438,7 @@ spawn_ship (STARSHIPPTR StarShipPtr)
|
||||
if ((ShipElementPtr->state_flags & BAD_GUY)
|
||||
&& LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
|
||||
{
|
||||
// This is the Sa-Matra
|
||||
StarShipPtr->ShipFacing = 0;
|
||||
ShipElementPtr->current.image.frame =
|
||||
SetAbsFrameIndex (RDPtr->ship_data.ship[0],
|
||||
@@ -452,7 +454,8 @@ spawn_ship (STARSHIPPTR StarShipPtr)
|
||||
{
|
||||
COUNT facing;
|
||||
|
||||
if ((facing = LOWORD (GLOBAL (ShipStamp.frame))) > 0)
|
||||
facing = LOWORD (GLOBAL (ShipStamp.frame));
|
||||
if (facing > 0)
|
||||
--facing;
|
||||
|
||||
GLOBAL (ShipStamp.frame) = (FRAME)MAKE_DWORD (
|
||||
|
||||
@@ -142,7 +142,8 @@ spawn_crew (PELEMENT ElementPtr)
|
||||
{
|
||||
HELEMENT hCrew;
|
||||
|
||||
if ((hCrew = AllocElement ()) != 0)
|
||||
hCrew = AllocElement ();
|
||||
if (hCrew != 0)
|
||||
{
|
||||
ELEMENTPTR CrewPtr;
|
||||
|
||||
@@ -151,9 +152,7 @@ spawn_crew (PELEMENT ElementPtr)
|
||||
CrewPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE
|
||||
| (ElementPtr->state_flags & (GOOD_GUY | BAD_GUY));
|
||||
CrewPtr->life_span = 0;
|
||||
{
|
||||
CrewPtr->death_func = spawn_crew;
|
||||
}
|
||||
CrewPtr->pParent = ElementPtr->pParent;
|
||||
CrewPtr->hTarget = 0;
|
||||
UnlockElement (hCrew);
|
||||
@@ -181,12 +180,13 @@ spawn_crew (PELEMENT ElementPtr)
|
||||
SIZE dx, dy;
|
||||
DWORD d_squared;
|
||||
|
||||
if ((dx = ObjPtr->next.location.x
|
||||
- ElementPtr->next.location.x) < 0)
|
||||
dx = ObjPtr->next.location.x - ElementPtr->next.location.x;
|
||||
if (dx < 0)
|
||||
dx = -dx;
|
||||
if ((dy = ObjPtr->next.location.y
|
||||
- ElementPtr->next.location.y) < 0)
|
||||
dy = ObjPtr->next.location.y - ElementPtr->next.location.y;
|
||||
if (dy < 0)
|
||||
dy = -dy;
|
||||
|
||||
dx = WORLD_TO_DISPLAY (dx);
|
||||
dy = WORLD_TO_DISPLAY (dy);
|
||||
#define ABANDONER_RANGE 208 /* originally SPACE_HEIGHT */
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
# include "netplay/netmisc.h"
|
||||
# include "netplay/notify.h"
|
||||
# include "netplay/proto/ready.h"
|
||||
# include "netplay/packet.h"
|
||||
# include "netplay/packetq.h"
|
||||
#endif
|
||||
#include "races.h"
|
||||
@@ -180,7 +181,7 @@ readyForBattleEndPlayer (NetConnection *conn, void *arg)
|
||||
#endif
|
||||
|
||||
static inline bool
|
||||
readyForBattleEnd (void)
|
||||
readyForBattleEnd (COUNT side)
|
||||
{
|
||||
#ifndef NETPLAY
|
||||
#if DEMO_MODE
|
||||
@@ -194,9 +195,26 @@ readyForBattleEnd (void)
|
||||
if (PLRPlaying ((MUSIC_REF)~0))
|
||||
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))
|
||||
return false;
|
||||
|
||||
currentDeadSide = (COUNT)~0;
|
||||
// Another side may be handled.
|
||||
|
||||
return true;
|
||||
#endif /* defined (NETPLAY) */
|
||||
}
|
||||
@@ -239,9 +257,9 @@ new_ship (PELEMENT DeadShipPtr)
|
||||
DeadShipPtr->turn_wait = (BYTE)(
|
||||
DeadShipPtr->state_flags & (GOOD_GUY | BAD_GUY));
|
||||
// DeadShipPtr->turn_wait is abused to store which
|
||||
// side this element is for, probably because this
|
||||
// information will be lost from state_flags (why is this
|
||||
// necessary?).
|
||||
// side this element is for, because this information
|
||||
// will be lost from state_flags when the element is
|
||||
// set up for deletion below.
|
||||
for (hElement = GetHeadElement (); hElement; hElement = hSuccElement)
|
||||
{
|
||||
ELEMENTPTR ElementPtr;
|
||||
@@ -250,14 +268,20 @@ new_ship (PELEMENT DeadShipPtr)
|
||||
LockElement (hElement, &ElementPtr);
|
||||
hSuccElement = GetSuccElement (ElementPtr);
|
||||
GetElementStarShip (ElementPtr, &StarShipPtr);
|
||||
// Get the STARSHIP that this ELEMENT belongs to.
|
||||
|
||||
if (StarShipPtr == DeadStarShipPtr)
|
||||
{
|
||||
// This element belongs to the dead ship; it may be the
|
||||
// ship's own element.
|
||||
SetElementStarShip (ElementPtr, 0);
|
||||
|
||||
if (!(ElementPtr->state_flags & CREW_OBJECT)
|
||||
|| 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->state_flags =
|
||||
NONSOLID | DISAPPEARING | FINITE_LIFE;
|
||||
@@ -271,6 +295,7 @@ new_ship (PELEMENT DeadShipPtr)
|
||||
if (StarShipPtr
|
||||
&& (StarShipPtr->cur_status_flags & PLAY_VICTORY_DITTY))
|
||||
{
|
||||
// StarShipPtr points to the surviving ship.
|
||||
MusicStarted = TRUE;
|
||||
PlayMusic ((MUSIC_REF)StarShipPtr->RaceDescPtr->
|
||||
ship_data.victory_ditty, FALSE, 3);
|
||||
@@ -287,7 +312,8 @@ new_ship (PELEMENT DeadShipPtr)
|
||||
SetElementStarShip (DeadShipPtr, DeadStarShipPtr);
|
||||
}
|
||||
|
||||
if (DeadShipPtr->life_span || !readyForBattleEnd ())
|
||||
if (DeadShipPtr->life_span || !readyForBattleEnd (
|
||||
WHICH_SIDE (DeadShipPtr->turn_wait)))
|
||||
{
|
||||
DeadShipPtr->state_flags &= ~DISAPPEARING;
|
||||
++DeadShipPtr->life_span;
|
||||
@@ -328,7 +354,7 @@ UnbatchGraphics ();
|
||||
#endif /* NETPLAY */
|
||||
|
||||
if (GetNextStarShip (DeadStarShipPtr,
|
||||
WHICH_SIDE (DeadShipPtr->turn_wait)) && RestartMusic)
|
||||
WHICH_SIDE (DeadShipPtr->turn_wait)))
|
||||
{
|
||||
#ifdef NETPLAY
|
||||
{
|
||||
@@ -343,11 +369,22 @@ UnbatchGraphics ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (RestartMusic)
|
||||
BattleSong (TRUE);
|
||||
}
|
||||
else if (LOBYTE (battle_counter) == 0
|
||||
|| HIBYTE (battle_counter) == 0)
|
||||
{
|
||||
// One player is out of ships. The battle is over.
|
||||
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
|
||||
}
|
||||
#ifdef NETPLAY
|
||||
else
|
||||
{
|
||||
// Battle has been aborted.
|
||||
GLOBAL (CurrentActivity) |= CHECK_ABORT;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NETPLAY
|
||||
// Turn_wait was abused to store the side this element was on.
|
||||
|
||||
Reference in New Issue
Block a user