Skip to content

Prevent mission exit from disconnecting the next local session - #589

Merged
corepunch merged 1 commit into
corepunch:mainfrom
sookyboo:fix/wc3-mission-quit-relaunch
Oct 6, 2026
Merged

corepunch merged 1 commit into
corepunch:mainfrom
sookyboo:fix/wc3-mission-quit-relaunch

Conversation

@sookyboo

@sookyboo sookyboo commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

When returning to the menu, the client queued a loopback disconnect immediately before shutting down the local server. The packet could remain queued until the next mission started, causing the new session to disconnect.

Skip sending that packet when the local server is shutting down. Keep disconnect notifications enabled for remote servers and ordinary client disconnects.

Adds a regression test confirming that shutdown suppresses the loopback packet while ordinary disconnects still send it.

Tests: make -j4 test (46,687 assertions across 2,337 tests).

@sookyboo
sookyboo requested a review from corepunch as a code owner October 6, 2026 15:28
@corepunch
corepunch merged commit 092ed00 into corepunch:main Oct 6, 2026
7 checks passed
corepunch added a commit that referenced this pull request Oct 6, 2026
Net (#589): restore Q2 CL_Disconnect semantics - the client always tells
its server it left - and fix the stale loopback disconnect at the server's
session boundary instead. Q2 never reads a dead server's queue (SV_Frame
needs svs.initialized) and is protected by its handshake riding the same
FIFO; our local client is admitted straight into slot 0, so SV_InitGame
now discards the dead session's client->server loopback datagrams
(NET_ClearLoopPackets). Drops the send_disconnect flag and the
NA_LOOPBACK special case at the mission-exit call site, and a duplicated
ss_dead branch in SV_ReadPackets.

Movement (#588): decide unreachable-goal ownership once (point orders own a
private waypoint like Q2 movetarget; entity goals are never moved), and
make the gate test prove the attacker actually steers to the approach.

Powerups (#587): one table-driven pickup decision (AB_POWERUP ability flag,
cf. Q2 gitem_t.pickup) replaces two duplicated if-chains; powerups are
always consumed instead of falling into a slot when nobody is affected;
the speed cap follows the ability procedure, so custom AIsp-based runes
are capped; reuse S_SpellApplyTimedTargetStatus.

Ancients/bots (#586): uprooted Ancients retaliate with their uprooted mask
(an air attacker no longer triggers a doomed melee counterattack); one
S_AncientIsMorphing helper and one mask-taking attack predicate shared by
orders and retaliation (cf. Q2 M_CheckAttack); bots iterate shop stock
directly instead of re-parsing a shared static buffer.

No network wire-format or save-format change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
corepunch added a commit that referenced this pull request Oct 6, 2026
Net (#589): restore Q2 CL_Disconnect semantics - the client always tells
its server it left - and fix the stale loopback disconnect at the server's
session boundary instead. Q2 never reads a dead server's queue (SV_Frame
needs svs.initialized) and is protected by its handshake riding the same
FIFO; our local client is admitted straight into slot 0, so SV_InitGame
now discards the dead session's client->server loopback datagrams
(NET_ClearLoopPackets). Drops the send_disconnect flag and the
NA_LOOPBACK special case at the mission-exit call site, and a duplicated
ss_dead branch in SV_ReadPackets.

Movement (#588): decide unreachable-goal ownership once (point orders own a
private waypoint like Q2 movetarget; entity goals are never moved), and
make the gate test prove the attacker actually steers to the approach.

Powerups (#587): one table-driven pickup decision (AB_POWERUP ability flag,
cf. Q2 gitem_t.pickup) replaces two duplicated if-chains; powerups are
always consumed instead of falling into a slot when nobody is affected;
the speed cap follows the ability procedure, so custom AIsp-based runes
are capped; reuse S_SpellApplyTimedTargetStatus.

Ancients/bots (#586): uprooted Ancients retaliate with their uprooted mask
(an air attacker no longer triggers a doomed melee counterattack); one
S_AncientIsMorphing helper and one mask-taking attack predicate shared by
orders and retaliation (cf. Q2 M_CheckAttack); bots iterate shop stock
directly instead of re-parsing a shared static buffer.

No network wire-format or save-format change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants