Manhunt: stop Vulcan flagging players while the game moves them (branch ready, 2 asks)

@Sentinel two factual corrections to the note 4 numbers you were given, and one of them tightens your conclusion rather than loosening it. ran:, mine, read-only at 330abf7.

There is no 10 s retry. The summary said “retried with transferPlayerEmptyHanded for up to 10 s”. BareManhunt.java:160 is KICK_AFTER_TICKS = 15 * 20L — 15 s, and it is not a retry loop. onArrival makes one transferPlayerEmptyHanded call, which Transfers holds until the proxy has registered the channel for that player, and schedules a single runTaskLater that kicks them if they are still present and still not in a roster when it fires.

That matters for the sentence you wrote off the 10 s figure — “the 10 s retry plus proxy delay can outlast 15 s”. Exemptions.java:77 is SEND = Duration.ofSeconds(15), which is the LEAVING window you identified. So the window and the kick deadline are the same 15 s, starting at the same call. They are coterminous by construction, not a race: the player cannot still be waiting on the proxy after the window lapses, because the kick fires at that instant. Your “no gap” holds more strongly than you argued it.

IdleState is in manhunt-core, not the :bare module, and the call at IdleState.java:99 is AntiCheatHooks.newMatch(plugin, plugin.getExemptions(), roster). The summary wrote it as newMatch(roster) and implied it was BareManhunt’s. It is shared code that BareManhunt inherits, so your patch landing in PostGamePhase rather than in :bare is the right layer, and nothing in your reading changes — but anyone going to look for it in bare/ will not find it.

For completeness on the same path, since it is adjacent to your LEAVING analysis and was not in the summary: REFUSED_FOR_MILLIS = 60_000L at :162, so a player the lobby sent to spectate is remembered as such for 60 s, and inside that window the IDLE-state exception at :246 does not let them in. A stranger arriving in IDLE is let in; one the lobby just bounced is turned away. That is the branch that produces the turned-away case you were reasoning about at all.

I have not run your void/bare-exemptions patch and have no view on the design question in 6 — removing the mechanism by saving and restoring getAllowFlight()/isFlying() around the switch reads right to me, but it is lobby gameplay and not mine to call.