@@ -38,8 +38,6 @@ final class SessionActionDeadlineCoordinator {
3838 private final LongSupplier currentTimeMillis ;
3939 private final Map <UUID , Long > remainingExtraMillis = new HashMap <>();
4040 private final Map <UUID , ActorDeadline > actorDeadlines = new HashMap <>();
41- private final Map <UUID , SuspendedDeadline > suspendedDeadlines = new HashMap <>();
42- private final Set <UUID > suspendedActors = new HashSet <>();
4341 private final Map <UUID , Integer > consecutiveAutomaticDiscards = new HashMap <>();
4442 private final Set <UUID > automaticDiscardActors = new HashSet <>();
4543 private String armedFingerprint ;
@@ -54,10 +52,6 @@ final class SessionActionDeadlineCoordinator {
5452 }
5553
5654 synchronized void beginRound () {
57- // An overhead river view may legitimately span the short gap between hands.
58- // Reset hand-scoped clocks and idle history without dropping the explicit
59- // suspension; the first action window of the new hand must remain frozen
60- // until the player returns with Shift.
6155 this .resetRoundState ();
6256 long extraMillis = this .thinkingBudget ().extraMillis ();
6357 for (UUID playerId : this .session .players ()) {
@@ -121,10 +115,6 @@ synchronized void recordAction(UUID playerId) {
121115 if (deadline != null ) {
122116 this .consumeExtra (playerId , deadline , now );
123117 }
124- SuspendedDeadline suspended = this .suspendedDeadlines .remove (playerId );
125- if (suspended != null ) {
126- this .consumeExtra (playerId , suspended .deadline (), suspended .suspendedAtMillis ());
127- }
128118 ActionWindow currentWindow = this .captureWindow ();
129119 if (currentWindow == null ) {
130120 this .reset ();
@@ -148,91 +138,18 @@ synchronized long secondsRemaining(UUID playerId) {
148138 return 0L ;
149139 }
150140 ActorDeadline deadline = this .actorDeadlines .get (playerId );
151- if (deadline != null ) {
152- return secondsFromMillis (deadline .deadlineMillis () - this .currentTimeMillis .getAsLong ());
153- }
154- SuspendedDeadline suspended = this .suspendedDeadlines .get (playerId );
155- return suspended == null
141+ return deadline == null
156142 ? 0L
157- : secondsFromMillis (suspended .deadline ().deadlineMillis () - suspended .suspendedAtMillis ());
158- }
159-
160- /** Freezes this player's current and subsequent action windows until explicitly resumed. */
161- synchronized void suspend (UUID playerId ) {
162- if (playerId == null || !this .suspendedActors .add (playerId )) {
163- return ;
164- }
165- ActionWindow window = this .captureWindow ();
166- if (window == null ) {
167- return ;
168- }
169- long now = this .currentTimeMillis .getAsLong ();
170- if (!window .fingerprint ().equals (this .armedFingerprint )) {
171- this .armWindow (window , now );
172- } else {
173- this .ensureActorDeadlines (window , now );
174- }
175- }
176-
177- /** Restores the frozen action with exactly the time that remained on entry. */
178- synchronized void resume (UUID playerId ) {
179- if (playerId == null || !this .suspendedActors .remove (playerId )) {
180- return ;
181- }
182- long now = this .currentTimeMillis .getAsLong ();
183- SuspendedDeadline suspended = this .suspendedDeadlines .remove (playerId );
184- ActionWindow window = this .captureWindow ();
185- if (window == null ) {
186- return ;
187- }
188- if (!window .fingerprint ().equals (this .armedFingerprint )) {
189- if (suspended != null ) {
190- this .consumeExtra (playerId , suspended .deadline (), suspended .suspendedAtMillis ());
191- }
192- this .armWindow (window , now );
193- return ;
194- }
195- if (suspended != null
196- && suspended .fingerprint ().equals (window .fingerprint ())
197- && window .actors ().contains (playerId )) {
198- ActorDeadline frozen = suspended .deadline ();
199- long elapsedBeforeSuspension = Math .max (0L , suspended .suspendedAtMillis () - frozen .startedAtMillis ());
200- long remainingMillis = Math .max (0L , frozen .deadlineMillis () - suspended .suspendedAtMillis ());
201- this .actorDeadlines .put (
202- playerId ,
203- new ActorDeadline (
204- Math .max (0L , now - elapsedBeforeSuspension ),
205- frozen .baseMillis (),
206- frozen .extraAtStartMillis (),
207- saturatedAdd (now , remainingMillis )
208- )
209- );
210- } else {
211- if (suspended != null ) {
212- this .consumeExtra (playerId , suspended .deadline (), suspended .suspendedAtMillis ());
213- }
214- this .ensureActorDeadlines (window , now );
215- }
216- }
217-
218- /** Drops a frozen action without restoring it, for disconnect/removal/table teardown. */
219- synchronized void discardSuspension (UUID playerId ) {
220- if (playerId == null ) {
221- return ;
222- }
223- this .suspendedActors .remove (playerId );
224- this .suspendedDeadlines .remove (playerId );
143+ : secondsFromMillis (deadline .deadlineMillis () - this .currentTimeMillis .getAsLong ());
225144 }
226145
227146 synchronized void reset () {
228147 this .armedFingerprint = null ;
229148 this .actorDeadlines .clear ();
230- this .suspendedDeadlines .clear ();
231149 }
232150
233151 synchronized void clear () {
234152 this .resetRoundState ();
235- this .suspendedActors .clear ();
236153 }
237154
238155 private void resetRoundState () {
@@ -249,12 +166,7 @@ private void armWindow(ActionWindow window, long now) {
249166 for (Map .Entry <UUID , ActorDeadline > entry : List .copyOf (this .actorDeadlines .entrySet ())) {
250167 this .consumeExtra (entry .getKey (), entry .getValue (), now );
251168 }
252- for (Map .Entry <UUID , SuspendedDeadline > entry : List .copyOf (this .suspendedDeadlines .entrySet ())) {
253- SuspendedDeadline suspended = entry .getValue ();
254- this .consumeExtra (entry .getKey (), suspended .deadline (), suspended .suspendedAtMillis ());
255- }
256169 this .actorDeadlines .clear ();
257- this .suspendedDeadlines .clear ();
258170 this .armedFingerprint = window .fingerprint ();
259171 this .ensureActorDeadlines (window , now );
260172 }
@@ -271,27 +183,6 @@ private void ensureActorDeadlines(ActionWindow window, long now) {
271183 long extraMillis = window .phase () == ActionPhase .TURN
272184 ? 0L
273185 : this .remainingExtraMillis .computeIfAbsent (playerId , ignored -> budget .extraMillis ());
274- if (this .suspendedActors .contains (playerId )) {
275- ActorDeadline active = this .actorDeadlines .remove (playerId );
276- this .suspendedDeadlines .compute (
277- playerId ,
278- (ignored , current ) -> current != null && current .fingerprint ().equals (window .fingerprint ())
279- ? current
280- : new SuspendedDeadline (
281- window .fingerprint (),
282- active == null
283- ? new ActorDeadline (
284- now ,
285- budget .baseMillis (),
286- extraMillis ,
287- saturatedAdd (now , saturatedAdd (budget .baseMillis (), extraMillis ))
288- )
289- : active ,
290- now
291- )
292- );
293- continue ;
294- }
295186 this .actorDeadlines .computeIfAbsent (
296187 playerId ,
297188 ignored -> new ActorDeadline (
@@ -583,13 +474,6 @@ private record ActorDeadline(
583474 ) {
584475 }
585476
586- private record SuspendedDeadline (
587- String fingerprint ,
588- ActorDeadline deadline ,
589- long suspendedAtMillis
590- ) {
591- }
592-
593477 private record ActionWindow (ActionPhase phase , String fingerprint , List <UUID > actors ) {
594478 }
595479}
0 commit comments