@dimcool/sdk 0.1.30 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/index.cjs +209 -25
- package/dist/index.d.cts +53 -2
- package/dist/index.d.ts +53 -2
- package/dist/index.js +208 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2971,6 +2971,59 @@ const sdk = new SDK({
|
|
|
2971
2971
|
These stores are updated by the `WsRouter` and are the single source of truth for
|
|
2972
2972
|
realtime UI. Room membership is ref-counted and mapped to backend join/leave emits.
|
|
2973
2973
|
|
|
2974
|
+
### `gameActionsStore` Selectors
|
|
2975
|
+
|
|
2976
|
+
Pure functions that extract derived state from `GameActionsStoreState` without any React dependency. Pass them to `useSdkSelector` in React or call them directly in Node.
|
|
2977
|
+
|
|
2978
|
+
#### `selectGameLifecycleState(gameState)`
|
|
2979
|
+
|
|
2980
|
+
Extracts the common lifecycle fields from any `GameStateResponse`. Accepts the state directly so it can be called after a stable `useSdkSelector` call — the selector itself returns the existing store reference, then you project outside it. This avoids creating a new object inside `getSnapshot`, which would break `useSyncExternalStore`'s referential-equality contract.
|
|
2981
|
+
|
|
2982
|
+
```ts
|
|
2983
|
+
import { selectGameLifecycleState } from '@dimcool/sdk';
|
|
2984
|
+
|
|
2985
|
+
// Correct: selector returns stable store ref, projection happens outside
|
|
2986
|
+
const rawState = useSdkSelector(store, (s) => s.statesByGameId[gameId] ?? null);
|
|
2987
|
+
const lifecycle = selectGameLifecycleState(rawState);
|
|
2988
|
+
// { status, winnerId, betAmount, wonAmount, totalPotMinor, currentPlayerId }
|
|
2989
|
+
```
|
|
2990
|
+
|
|
2991
|
+
#### `getCountdownDigit(gameId, nowMs)`
|
|
2992
|
+
|
|
2993
|
+
Returns the current pre-game countdown digit (`3 | 2 | 1`) or `null` when the buffer has elapsed. Works for all game types (turn-based: reads `bufferEndsAt`; RPS: reads `roundState.selectionEndsAt` during the `starting` phase).
|
|
2994
|
+
|
|
2995
|
+
```ts
|
|
2996
|
+
const digit = sdk.gameActionsStore.getCountdownDigit(gameId, Date.now());
|
|
2997
|
+
```
|
|
2998
|
+
|
|
2999
|
+
#### `getChessClockTimes(gameId, nowMs)` / `getTicTacToeClockTimes` / `getConnect4ClockTimes`
|
|
3000
|
+
|
|
3001
|
+
Returns live per-color clock values deducting elapsed time from the current player's budget.
|
|
3002
|
+
|
|
3003
|
+
```ts
|
|
3004
|
+
const clocks = sdk.gameActionsStore.getChessClockTimes(gameId, Date.now());
|
|
3005
|
+
// { whiteTimeMs, blackTimeMs }
|
|
3006
|
+
|
|
3007
|
+
const tttClocks = sdk.gameActionsStore.getTicTacToeClockTimes(
|
|
3008
|
+
gameId,
|
|
3009
|
+
Date.now(),
|
|
3010
|
+
);
|
|
3011
|
+
// { xTimeMs, oTimeMs }
|
|
3012
|
+
|
|
3013
|
+
const c4Clocks = sdk.gameActionsStore.getConnect4ClockTimes(gameId, Date.now());
|
|
3014
|
+
// { redTimeMs, yellowTimeMs }
|
|
3015
|
+
```
|
|
3016
|
+
|
|
3017
|
+
#### `getChessCapturedPieces(gameId)`
|
|
3018
|
+
|
|
3019
|
+
Derives captured pieces from the FEN placement string — no external chess library needed.
|
|
3020
|
+
|
|
3021
|
+
```ts
|
|
3022
|
+
const pieces = sdk.gameActionsStore.getChessCapturedPieces(gameId);
|
|
3023
|
+
// { capturedByWhite: string[], capturedByBlack: string[] }
|
|
3024
|
+
// e.g. capturedByWhite: ['bp', 'bp', 'br'] means white captured 2 black pawns + 1 rook
|
|
3025
|
+
```
|
|
3026
|
+
|
|
2974
3027
|
### Event Bus (low-level)
|
|
2975
3028
|
|
|
2976
3029
|
If you need raw event streams, use `sdk.events.subscribe(eventName, handler)`.
|
package/dist/index.cjs
CHANGED
|
@@ -242,6 +242,7 @@ __export(index_exports, {
|
|
|
242
242
|
formatMoneyMinor: () => import_utils.formatMoneyMinor,
|
|
243
243
|
isRetryableError: () => isRetryableError,
|
|
244
244
|
logger: () => logger,
|
|
245
|
+
selectGameLifecycleState: () => selectGameLifecycleState,
|
|
245
246
|
toMajor: () => import_utils.toMajor,
|
|
246
247
|
withRetry: () => withRetry
|
|
247
248
|
});
|
|
@@ -796,6 +797,9 @@ var Admin = class {
|
|
|
796
797
|
this.http = http;
|
|
797
798
|
this.logger = logger2;
|
|
798
799
|
}
|
|
800
|
+
async getInternalBots() {
|
|
801
|
+
return this.http.get("/admin/internal-bots");
|
|
802
|
+
}
|
|
799
803
|
async getUserById(id) {
|
|
800
804
|
return this.http.get(`/admin/users/${id}`);
|
|
801
805
|
}
|
|
@@ -3463,6 +3467,19 @@ function createGameStore(transport) {
|
|
|
3463
3467
|
}
|
|
3464
3468
|
|
|
3465
3469
|
// src/stores/game-actions-store.ts
|
|
3470
|
+
function selectGameLifecycleState(gameState) {
|
|
3471
|
+
const s = gameState;
|
|
3472
|
+
if (!s) return null;
|
|
3473
|
+
const raw = s;
|
|
3474
|
+
return {
|
|
3475
|
+
status: raw.status,
|
|
3476
|
+
winnerId: raw.winnerId ?? null,
|
|
3477
|
+
betAmount: raw.betAmount ?? 0,
|
|
3478
|
+
wonAmount: raw.wonAmount ?? null,
|
|
3479
|
+
totalPotMinor: raw.totalPotMinor,
|
|
3480
|
+
currentPlayerId: raw.currentPlayerId ?? null
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3466
3483
|
var isRpsCompletionPayload = (payload) => payload.gameType === "rock-paper-scissors";
|
|
3467
3484
|
function createGameActionsStore(transport) {
|
|
3468
3485
|
const store = createSdkStore({
|
|
@@ -3484,8 +3501,21 @@ function createGameActionsStore(transport) {
|
|
|
3484
3501
|
});
|
|
3485
3502
|
};
|
|
3486
3503
|
const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
|
|
3504
|
+
const pendingEvents = /* @__PURE__ */ new Map();
|
|
3505
|
+
function enqueue(gameId, event) {
|
|
3506
|
+
const q = pendingEvents.get(gameId) ?? [];
|
|
3507
|
+
q.push(event);
|
|
3508
|
+
pendingEvents.set(gameId, q);
|
|
3509
|
+
}
|
|
3510
|
+
function drainQueue(gameId) {
|
|
3511
|
+
const q = pendingEvents.get(gameId);
|
|
3512
|
+
if (!q?.length) return;
|
|
3513
|
+
pendingEvents.delete(gameId);
|
|
3514
|
+
for (const ev of q) applyWsEvent(ev);
|
|
3515
|
+
}
|
|
3487
3516
|
const setBaseState = (gameId, state) => {
|
|
3488
3517
|
updateState(gameId, state);
|
|
3518
|
+
drainQueue(gameId);
|
|
3489
3519
|
};
|
|
3490
3520
|
const clearState = (gameId) => {
|
|
3491
3521
|
store.updateState((state) => {
|
|
@@ -3495,6 +3525,7 @@ function createGameActionsStore(transport) {
|
|
|
3495
3525
|
const { [gameId]: _, ...rest } = state.statesByGameId;
|
|
3496
3526
|
return { ...state, statesByGameId: rest };
|
|
3497
3527
|
});
|
|
3528
|
+
pendingEvents.delete(gameId);
|
|
3498
3529
|
};
|
|
3499
3530
|
const applyWsEvent = (event) => {
|
|
3500
3531
|
switch (event.event) {
|
|
@@ -3513,7 +3544,10 @@ function createGameActionsStore(transport) {
|
|
|
3513
3544
|
}
|
|
3514
3545
|
case "game:rps:starting": {
|
|
3515
3546
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3516
|
-
if (!current || !isRpsState(current))
|
|
3547
|
+
if (!current || !isRpsState(current)) {
|
|
3548
|
+
enqueue(event.payload.gameId, event);
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3517
3551
|
const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
|
|
3518
3552
|
const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
|
|
3519
3553
|
const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
|
|
@@ -3534,7 +3568,10 @@ function createGameActionsStore(transport) {
|
|
|
3534
3568
|
}
|
|
3535
3569
|
case "game:rps:round:started": {
|
|
3536
3570
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3537
|
-
if (!current || !isRpsState(current))
|
|
3571
|
+
if (!current || !isRpsState(current)) {
|
|
3572
|
+
enqueue(event.payload.gameId, event);
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3538
3575
|
const actions = {};
|
|
3539
3576
|
const baseUsers = /* @__PURE__ */ new Set();
|
|
3540
3577
|
Object.keys(current.roundState.actions).forEach(
|
|
@@ -3568,7 +3605,10 @@ function createGameActionsStore(transport) {
|
|
|
3568
3605
|
}
|
|
3569
3606
|
case "game:rps:action:received": {
|
|
3570
3607
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3571
|
-
if (!current || !isRpsState(current))
|
|
3608
|
+
if (!current || !isRpsState(current)) {
|
|
3609
|
+
enqueue(event.payload.gameId, event);
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3572
3612
|
const updated = {
|
|
3573
3613
|
...current,
|
|
3574
3614
|
roundState: {
|
|
@@ -3587,7 +3627,10 @@ function createGameActionsStore(transport) {
|
|
|
3587
3627
|
}
|
|
3588
3628
|
case "game:rps:timer:cutoff": {
|
|
3589
3629
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3590
|
-
if (!current || !isRpsState(current))
|
|
3630
|
+
if (!current || !isRpsState(current)) {
|
|
3631
|
+
enqueue(event.payload.gameId, event);
|
|
3632
|
+
return;
|
|
3633
|
+
}
|
|
3591
3634
|
const updated = {
|
|
3592
3635
|
...current,
|
|
3593
3636
|
roundState: {
|
|
@@ -3601,7 +3644,10 @@ function createGameActionsStore(transport) {
|
|
|
3601
3644
|
}
|
|
3602
3645
|
case "game:rps:round:reveal": {
|
|
3603
3646
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3604
|
-
if (!current || !isRpsState(current))
|
|
3647
|
+
if (!current || !isRpsState(current)) {
|
|
3648
|
+
enqueue(event.payload.gameId, event);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3605
3651
|
const actions = {};
|
|
3606
3652
|
const payloadActions = event.payload.actions;
|
|
3607
3653
|
Object.keys(payloadActions || {}).forEach((userId) => {
|
|
@@ -3625,7 +3671,10 @@ function createGameActionsStore(transport) {
|
|
|
3625
3671
|
}
|
|
3626
3672
|
case "game:rps:round:completed": {
|
|
3627
3673
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3628
|
-
if (!current || !isRpsState(current))
|
|
3674
|
+
if (!current || !isRpsState(current)) {
|
|
3675
|
+
enqueue(event.payload.gameId, event);
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3629
3678
|
const roundHistory = [
|
|
3630
3679
|
...current.roundHistory || [],
|
|
3631
3680
|
{
|
|
@@ -3650,7 +3699,10 @@ function createGameActionsStore(transport) {
|
|
|
3650
3699
|
}
|
|
3651
3700
|
case "game:rps:timeout": {
|
|
3652
3701
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3653
|
-
if (!current || !isRpsState(current))
|
|
3702
|
+
if (!current || !isRpsState(current)) {
|
|
3703
|
+
enqueue(event.payload.gameId, event);
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3654
3706
|
const timedOutUser = event.payload.playerId;
|
|
3655
3707
|
const action = event.payload.action;
|
|
3656
3708
|
const updated = {
|
|
@@ -3675,7 +3727,10 @@ function createGameActionsStore(transport) {
|
|
|
3675
3727
|
const payload = event.payload;
|
|
3676
3728
|
const { gameId } = payload;
|
|
3677
3729
|
const current = store.getState().statesByGameId[gameId];
|
|
3678
|
-
if (!current)
|
|
3730
|
+
if (!current) {
|
|
3731
|
+
enqueue(gameId, event);
|
|
3732
|
+
return;
|
|
3733
|
+
}
|
|
3679
3734
|
const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
|
|
3680
3735
|
...current,
|
|
3681
3736
|
status: "completed",
|
|
@@ -3724,11 +3779,15 @@ function createGameActionsStore(transport) {
|
|
|
3724
3779
|
const current = store.getState().statesByGameId[gameId];
|
|
3725
3780
|
const updated = current ? { ...current, ...incoming } : incoming;
|
|
3726
3781
|
updateState(gameId, updated);
|
|
3782
|
+
drainQueue(gameId);
|
|
3727
3783
|
break;
|
|
3728
3784
|
}
|
|
3729
3785
|
case "game:rematch:requested": {
|
|
3730
3786
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3731
|
-
if (!current)
|
|
3787
|
+
if (!current) {
|
|
3788
|
+
enqueue(event.payload.gameId, event);
|
|
3789
|
+
return;
|
|
3790
|
+
}
|
|
3732
3791
|
const requestedBy = event.payload.requestedBy;
|
|
3733
3792
|
const userId = event.payload.userId;
|
|
3734
3793
|
const requested = new Set(
|
|
@@ -3744,7 +3803,10 @@ function createGameActionsStore(transport) {
|
|
|
3744
3803
|
}
|
|
3745
3804
|
case "game:rematch:cancelled": {
|
|
3746
3805
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3747
|
-
if (!current)
|
|
3806
|
+
if (!current) {
|
|
3807
|
+
enqueue(event.payload.gameId, event);
|
|
3808
|
+
return;
|
|
3809
|
+
}
|
|
3748
3810
|
const requestedBy = event.payload.requestedBy ?? [];
|
|
3749
3811
|
const updated = {
|
|
3750
3812
|
...current,
|
|
@@ -3755,7 +3817,10 @@ function createGameActionsStore(transport) {
|
|
|
3755
3817
|
}
|
|
3756
3818
|
case "game:rematch:started": {
|
|
3757
3819
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3758
|
-
if (!current)
|
|
3820
|
+
if (!current) {
|
|
3821
|
+
enqueue(event.payload.gameId, event);
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3759
3824
|
const updated = {
|
|
3760
3825
|
...current,
|
|
3761
3826
|
rematchRequestedBy: event.payload.playerIds ?? []
|
|
@@ -3766,7 +3831,10 @@ function createGameActionsStore(transport) {
|
|
|
3766
3831
|
case "game:pot:updated": {
|
|
3767
3832
|
const { gameId, totalPotMinor } = event.payload;
|
|
3768
3833
|
const current = store.getState().statesByGameId[gameId];
|
|
3769
|
-
if (!current)
|
|
3834
|
+
if (!current) {
|
|
3835
|
+
enqueue(gameId, event);
|
|
3836
|
+
return;
|
|
3837
|
+
}
|
|
3770
3838
|
const updated = {
|
|
3771
3839
|
...current,
|
|
3772
3840
|
totalPotMinor
|
|
@@ -3779,12 +3847,123 @@ function createGameActionsStore(transport) {
|
|
|
3779
3847
|
}
|
|
3780
3848
|
};
|
|
3781
3849
|
const joinGame = (gameId) => transport.joinRoom(`game:${gameId}`);
|
|
3850
|
+
const getCountdownDigit = (gameId, nowMs) => {
|
|
3851
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3852
|
+
if (!state) return null;
|
|
3853
|
+
if (isRpsState(state)) {
|
|
3854
|
+
if (state.roundState.phase !== "starting") return null;
|
|
3855
|
+
const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
|
|
3856
|
+
if (remaining <= 0) return null;
|
|
3857
|
+
return Math.ceil(remaining / 1e3);
|
|
3858
|
+
}
|
|
3859
|
+
const bufferEndsAt = state.bufferEndsAt;
|
|
3860
|
+
if (bufferEndsAt) {
|
|
3861
|
+
const remaining = new Date(bufferEndsAt).getTime() - nowMs;
|
|
3862
|
+
if (remaining <= 0) return null;
|
|
3863
|
+
return Math.ceil(remaining / 1e3);
|
|
3864
|
+
}
|
|
3865
|
+
return null;
|
|
3866
|
+
};
|
|
3867
|
+
const getChessClockTimes = (gameId, nowMs) => {
|
|
3868
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3869
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3870
|
+
const s = state;
|
|
3871
|
+
let whiteMs = s.whiteTimeMs ?? 0;
|
|
3872
|
+
let blackMs = s.blackTimeMs ?? 0;
|
|
3873
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3874
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3875
|
+
if (!Number.isNaN(startedAt)) {
|
|
3876
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3877
|
+
if (s.currentPlayerId === s.whitePlayerId) {
|
|
3878
|
+
whiteMs = Math.max(0, whiteMs - elapsed);
|
|
3879
|
+
} else if (s.currentPlayerId === s.blackPlayerId) {
|
|
3880
|
+
blackMs = Math.max(0, blackMs - elapsed);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
|
|
3885
|
+
};
|
|
3886
|
+
const getChessCapturedPieces = (gameId) => {
|
|
3887
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3888
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3889
|
+
const fen = state.fen;
|
|
3890
|
+
if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
|
|
3891
|
+
const placement = fen.split(" ")[0];
|
|
3892
|
+
const white = {};
|
|
3893
|
+
const black = {};
|
|
3894
|
+
for (const char of placement) {
|
|
3895
|
+
if (char === "/" || char >= "1" && char <= "8") continue;
|
|
3896
|
+
const lower = char.toLowerCase();
|
|
3897
|
+
if (char === lower) {
|
|
3898
|
+
black[lower] = (black[lower] ?? 0) + 1;
|
|
3899
|
+
} else {
|
|
3900
|
+
white[lower] = (white[lower] ?? 0) + 1;
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
const INITIAL = {
|
|
3904
|
+
p: 8,
|
|
3905
|
+
r: 2,
|
|
3906
|
+
n: 2,
|
|
3907
|
+
b: 2,
|
|
3908
|
+
q: 1,
|
|
3909
|
+
k: 1
|
|
3910
|
+
};
|
|
3911
|
+
const capturedByWhite = [];
|
|
3912
|
+
const capturedByBlack = [];
|
|
3913
|
+
for (const [type, initial] of Object.entries(INITIAL)) {
|
|
3914
|
+
const missingBlack = initial - (black[type] ?? 0);
|
|
3915
|
+
for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type}`);
|
|
3916
|
+
const missingWhite = initial - (white[type] ?? 0);
|
|
3917
|
+
for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type}`);
|
|
3918
|
+
}
|
|
3919
|
+
return { capturedByWhite, capturedByBlack };
|
|
3920
|
+
};
|
|
3921
|
+
const getTicTacToeClockTimes = (gameId, nowMs) => {
|
|
3922
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3923
|
+
if (!state || state.gameType !== "tic-tac-toe") return null;
|
|
3924
|
+
const s = state;
|
|
3925
|
+
let xMs = s.xTimeMs ?? 0;
|
|
3926
|
+
let oMs = s.oTimeMs ?? 0;
|
|
3927
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3928
|
+
const currentMark = s.playerMarks[s.currentPlayerId];
|
|
3929
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3930
|
+
if (!Number.isNaN(startedAt)) {
|
|
3931
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3932
|
+
if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
|
|
3933
|
+
else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
return { xTimeMs: xMs, oTimeMs: oMs };
|
|
3937
|
+
};
|
|
3938
|
+
const getConnect4ClockTimes = (gameId, nowMs) => {
|
|
3939
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3940
|
+
if (!state || state.gameType !== "connect-four") return null;
|
|
3941
|
+
const s = state;
|
|
3942
|
+
let redMs = s.redTimeMs ?? 0;
|
|
3943
|
+
let yellowMs = s.yellowTimeMs ?? 0;
|
|
3944
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3945
|
+
const currentColor = s.playerColors[s.currentPlayerId];
|
|
3946
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3947
|
+
if (!Number.isNaN(startedAt)) {
|
|
3948
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3949
|
+
if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
|
|
3950
|
+
else if (currentColor === "YELLOW")
|
|
3951
|
+
yellowMs = Math.max(0, yellowMs - elapsed);
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
return { redTimeMs: redMs, yellowTimeMs: yellowMs };
|
|
3955
|
+
};
|
|
3782
3956
|
return {
|
|
3783
3957
|
store,
|
|
3784
3958
|
setBaseState,
|
|
3785
3959
|
clearState,
|
|
3786
3960
|
applyWsEvent,
|
|
3787
|
-
joinGame
|
|
3961
|
+
joinGame,
|
|
3962
|
+
getCountdownDigit,
|
|
3963
|
+
getChessClockTimes,
|
|
3964
|
+
getChessCapturedPieces,
|
|
3965
|
+
getTicTacToeClockTimes,
|
|
3966
|
+
getConnect4ClockTimes
|
|
3788
3967
|
};
|
|
3789
3968
|
}
|
|
3790
3969
|
function isRpsState(state) {
|
|
@@ -4454,12 +4633,14 @@ var WsRouter = class {
|
|
|
4454
4633
|
}
|
|
4455
4634
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4456
4635
|
if (!decoded) return;
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
this.deps.
|
|
4460
|
-
this.deps.
|
|
4461
|
-
this.deps.
|
|
4462
|
-
this.deps.
|
|
4636
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4637
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4638
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4639
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4640
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4641
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4642
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4643
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4463
4644
|
});
|
|
4464
4645
|
}
|
|
4465
4646
|
stop() {
|
|
@@ -4471,12 +4652,14 @@ var WsRouter = class {
|
|
|
4471
4652
|
this.transport.subscribeEvent(eventName, (payload) => {
|
|
4472
4653
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4473
4654
|
if (!decoded) return;
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
this.deps.
|
|
4477
|
-
this.deps.
|
|
4478
|
-
this.deps.
|
|
4479
|
-
this.deps.
|
|
4655
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4656
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4657
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4658
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4659
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4660
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4661
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4662
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4480
4663
|
});
|
|
4481
4664
|
}
|
|
4482
4665
|
}
|
|
@@ -4877,6 +5060,7 @@ var import_utils = __toESM(require_dist(), 1);
|
|
|
4877
5060
|
formatMoneyMinor,
|
|
4878
5061
|
isRetryableError,
|
|
4879
5062
|
logger,
|
|
5063
|
+
selectGameLifecycleState,
|
|
4880
5064
|
toMajor,
|
|
4881
5065
|
withRetry
|
|
4882
5066
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1121,6 +1121,11 @@ declare class Admin {
|
|
|
1121
1121
|
private http;
|
|
1122
1122
|
private logger?;
|
|
1123
1123
|
constructor(http: IHttpClient, logger?: ILogger | undefined);
|
|
1124
|
+
getInternalBots(): Promise<(User & {
|
|
1125
|
+
sol: number;
|
|
1126
|
+
usdc: number;
|
|
1127
|
+
publicKey: string;
|
|
1128
|
+
})[]>;
|
|
1124
1129
|
getUserById(id: string): Promise<User>;
|
|
1125
1130
|
getUserBalance(userId: string): Promise<{
|
|
1126
1131
|
sol: number;
|
|
@@ -1434,12 +1439,55 @@ declare function createGameStore(transport: WsTransport): GameStore;
|
|
|
1434
1439
|
type GameActionsStoreState = {
|
|
1435
1440
|
statesByGameId: Record<string, GameStateResponse>;
|
|
1436
1441
|
};
|
|
1442
|
+
interface ChessClockTimes {
|
|
1443
|
+
whiteTimeMs: number;
|
|
1444
|
+
blackTimeMs: number;
|
|
1445
|
+
}
|
|
1446
|
+
interface ChessCapturedPieces {
|
|
1447
|
+
capturedByWhite: string[];
|
|
1448
|
+
capturedByBlack: string[];
|
|
1449
|
+
}
|
|
1450
|
+
interface TicTacToeClockTimes {
|
|
1451
|
+
xTimeMs: number;
|
|
1452
|
+
oTimeMs: number;
|
|
1453
|
+
}
|
|
1454
|
+
interface Connect4ClockTimes {
|
|
1455
|
+
redTimeMs: number;
|
|
1456
|
+
yellowTimeMs: number;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Common lifecycle fields extracted from any game state.
|
|
1460
|
+
* Used by useGameLifecycle to avoid casting the raw union type.
|
|
1461
|
+
*/
|
|
1462
|
+
interface GameLifecycleState {
|
|
1463
|
+
status: string;
|
|
1464
|
+
winnerId: string | null;
|
|
1465
|
+
betAmount: number;
|
|
1466
|
+
wonAmount: number | null;
|
|
1467
|
+
totalPotMinor: number | undefined;
|
|
1468
|
+
currentPlayerId: string | null;
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Pure selector — extracts lifecycle fields from any game state variant.
|
|
1472
|
+
* Accepts a single GameStateResponse so callers can use it after selecting
|
|
1473
|
+
* the stable store entry via useSdkSelector, avoiding new-object-per-call issues.
|
|
1474
|
+
*/
|
|
1475
|
+
declare function selectGameLifecycleState(gameState: GameStateResponse | null | undefined): GameLifecycleState | null;
|
|
1437
1476
|
type GameActionsStore = {
|
|
1438
1477
|
store: SdkStore<GameActionsStoreState>;
|
|
1439
1478
|
setBaseState: (gameId: string, state: GameStateResponse) => void;
|
|
1440
1479
|
clearState: (gameId: string) => void;
|
|
1441
1480
|
applyWsEvent: (event: WsEvent) => void;
|
|
1442
1481
|
joinGame: (gameId: string) => () => void;
|
|
1482
|
+
getCountdownDigit: (gameId: string, nowMs: number) => number | null;
|
|
1483
|
+
/** Live chess clock — deducts elapsed time since turnStartedAt. */
|
|
1484
|
+
getChessClockTimes: (gameId: string, nowMs: number) => ChessClockTimes | null;
|
|
1485
|
+
/** Captured pieces derived from the current FEN position. */
|
|
1486
|
+
getChessCapturedPieces: (gameId: string) => ChessCapturedPieces | null;
|
|
1487
|
+
/** Live TTT clock — deducts elapsed from the current player's mark (X or O). */
|
|
1488
|
+
getTicTacToeClockTimes: (gameId: string, nowMs: number) => TicTacToeClockTimes | null;
|
|
1489
|
+
/** Live Connect Four clock — deducts elapsed from the current player's color. */
|
|
1490
|
+
getConnect4ClockTimes: (gameId: string, nowMs: number) => Connect4ClockTimes | null;
|
|
1443
1491
|
};
|
|
1444
1492
|
declare function createGameActionsStore(transport: WsTransport): GameActionsStore;
|
|
1445
1493
|
|
|
@@ -1743,7 +1791,7 @@ type LobbyDepositUpdatedPayload = {
|
|
|
1743
1791
|
status: 'confirmed' | 'failed' | 'refunded' | 'refund_failed';
|
|
1744
1792
|
allConfirmed: boolean;
|
|
1745
1793
|
};
|
|
1746
|
-
type
|
|
1794
|
+
type WsEventBase = {
|
|
1747
1795
|
event: 'lobby:created';
|
|
1748
1796
|
payload: Lobby;
|
|
1749
1797
|
} | {
|
|
@@ -1941,6 +1989,9 @@ type WsEvent = {
|
|
|
1941
1989
|
event: 'notification';
|
|
1942
1990
|
payload: unknown;
|
|
1943
1991
|
};
|
|
1992
|
+
type WsEvent = WsEventBase & {
|
|
1993
|
+
_serverTs?: number;
|
|
1994
|
+
};
|
|
1944
1995
|
|
|
1945
1996
|
type LobbyMatchedEvent = {
|
|
1946
1997
|
gameId: string;
|
|
@@ -3006,4 +3057,4 @@ declare class HttpClient implements IHttpClient {
|
|
|
3006
3057
|
private payChallenge;
|
|
3007
3058
|
}
|
|
3008
3059
|
|
|
3009
|
-
export { type AcceptChallengeResponse, type Achievement, Activity, type ActivityFeedItem, type ActivityFeedItemType, type ActivityFeedResponse, type ActivityFeedUser, Admin, type AdminDailyStats, type AdminDailyStatsItem, type AdminFeatureFlag, type AdminGameHistory, type AdminGameHistoryFeeBreakdown, type AdminGameHistoryFeePlayer, type AdminGameHistoryPlayer, type AdminMarketDailyStats, type AdminMarketDailyStatsItem, type AdminMarketDetail, type AdminMarketStats, type AdminStats, type AdminWalletActivityItem, type AdminWalletActivityResponse, type AnalyticsUserData, type ApiError, type AppNotification, type AppNotificationType, type ApplyReferralCodeResponse, type BalanceResponse, type BetOption, type BroadcastTipRequest, BrowserLocalStorage, Challenges, Chat, type ChatContext, type ChatContextType, type ChatMessage, type ChatMessageReply, type ChatMessageType, type ChatReaction, type ChatReadBy, type ChatState, type ChatStore, type ChatStoreState, type ClaimReferralRewardsResponse, type CreateChallengeRequest, type CreateChallengeResponse, type CreateLobbyRequest, type CreateTicketData, type CriticalIncident, type CriticalIncidentCategory, type CriticalIncidentImpactType, type CriticalIncidentSeverity, type CriticalIncidentStatus, type CriticalIncidentSummary, type CurrentGame, Daily, type DailyParticipant, type DailyRoom, type DailyToken, type DepositForLobbyResponse, type DepositStatus, type DepositStatusResponse, type DmThread, type DmThreadsStore, type DmThreadsStoreState, type DonateToGameResponse, ESTIMATED_SOL_FEE_LAMPORTS, Escrow, type EscrowSweepPreview, type EscrowSweepRecord, type FaucetResponse, type FeatureFlag, type FriendRequestItem, type FriendsStore, type FriendsStoreState, type FriendshipStatus, type Game, type GameActionsStore, type GameActionsStoreState, type GameHistoryItem, type GameMetrics, type GamePlayer, type GameStateResponse, type GameStore, type GameStoreState, type GameType, Games, type GenerateHandshakeResponse, type GetTicketsOptions, HttpClient, type IAnalyticsClient, type IHttpClient, type ILogger, type IStorage, type ImageUploadPayload, type InviteFriendRequest, type LeaderboardEntry, type LeaderboardQuery, type LeaderboardRange, type LeaderboardResponse, Leaderboards, type LivePlayer, type LivePlayersPage, Lobbies, type Lobby, type LobbyDepositUpdatedPayload, type LobbyMatchedEvent, type LobbyPlayer, type LobbyStore, type LobbyStoreState, type LogLevel, type LoginResponse, MIN_SOL_TRANSFER_AMOUNT, MIN_TRANSFER_AMOUNT, type MarketBuyResult, type MarketPosition, type MarketSellResult, type MarketState, Markets, type MoneyMinor, NodeStorage, NoopAnalyticsClient, type NotificationEvent, type NotificationsStore, type NotificationsStoreState, type PaginatedCriticalIncidents, type PaginatedFriendRequests, type PaginatedFriends, type PaginatedNotificationsResponse, type PaginatedPlatformFees, type PaginatedReports, type PaginatedSearchUsers, type PaginatedSessions, type PaginatedSupportTickets, type PaginatedTransactionJobs, type PaginatedUsers, type PaymentRequiredChallenge, type PlatformFeeItem, type PrepareDepositResponse, type PrepareTipRequest, type PrepareTipResponse, type PrepareTransferRequest, type PrepareTransferResponse, type PublicUser, type QueueStats, type RedeemResult, type ReferralRewardItem, type ReferralRewardStatus, type ReferralRewardsResponse, type ReferralSummary, type ReferralTreeItem, type ReferralTreeResponse, Referrals, type Report, type ReportCount, type ReportStatus, type ReportUser, Reports, type RetryOptions, SDK, type SDKConfig, SDK_VERSION, SOL_DECIMALS, SOL_MINT, type SdkStore, type SdkUpgradeInfo, type SearchUser, type SendMessageRequest, type SendTipResponse, type SendTransferResponse, type Session, type SessionStats, SharedWorkerTransport, Spectate, type SpectatorMetrics, type SpectatorMetricsByUser, StandaloneWsTransport, type SubmitDepositResponse, type SubmitTransferRequest, type SubmitTransferResponse, Support, type SupportMessage, type SupportMessageSenderRole, type SupportTicket, type SupportTicketCategory, type SupportTicketPriority, type SupportTicketStatus, type SupportTicketUser, type SystemMessageType, TOKEN_KEY, TRANSFER_FEE_MINOR, Tips, type TransactionJob, type TransactionJobStatus, type TransactionJobType, type TransactionQueueStats, type TransferToken, type TypingUser, type UpdateTicketData, type User, type UserAchievement, type UserActivity, type UserActivityStatus, type UserStats, type UsernameAvailabilityResponse, Users, type ValidAction, Wallet, type WalletActivityCounterpartyType, type WalletActivityDirection, type WalletActivityItem, type WalletActivityKind, type WalletActivityResponse, type WalletActivityStatus, type WalletClaimAction, type WalletMeta, type WalletResponse, type WalletSigner, type WsEvent, WsEventBus, type WsEventName, type WsTransport, type ConnectionState as WsTransportState, createChatStore, createDmThreadsStore, createFriendsStore, createGameActionsStore, createGameStore, createLobbyStore, createLogger, createNotificationsStore, createSdkStore, isRetryableError, logger, withRetry };
|
|
3060
|
+
export { type AcceptChallengeResponse, type Achievement, Activity, type ActivityFeedItem, type ActivityFeedItemType, type ActivityFeedResponse, type ActivityFeedUser, Admin, type AdminDailyStats, type AdminDailyStatsItem, type AdminFeatureFlag, type AdminGameHistory, type AdminGameHistoryFeeBreakdown, type AdminGameHistoryFeePlayer, type AdminGameHistoryPlayer, type AdminMarketDailyStats, type AdminMarketDailyStatsItem, type AdminMarketDetail, type AdminMarketStats, type AdminStats, type AdminWalletActivityItem, type AdminWalletActivityResponse, type AnalyticsUserData, type ApiError, type AppNotification, type AppNotificationType, type ApplyReferralCodeResponse, type BalanceResponse, type BetOption, type BroadcastTipRequest, BrowserLocalStorage, Challenges, Chat, type ChatContext, type ChatContextType, type ChatMessage, type ChatMessageReply, type ChatMessageType, type ChatReaction, type ChatReadBy, type ChatState, type ChatStore, type ChatStoreState, type ChessCapturedPieces, type ChessClockTimes, type ClaimReferralRewardsResponse, type Connect4ClockTimes, type CreateChallengeRequest, type CreateChallengeResponse, type CreateLobbyRequest, type CreateTicketData, type CriticalIncident, type CriticalIncidentCategory, type CriticalIncidentImpactType, type CriticalIncidentSeverity, type CriticalIncidentStatus, type CriticalIncidentSummary, type CurrentGame, Daily, type DailyParticipant, type DailyRoom, type DailyToken, type DepositForLobbyResponse, type DepositStatus, type DepositStatusResponse, type DmThread, type DmThreadsStore, type DmThreadsStoreState, type DonateToGameResponse, ESTIMATED_SOL_FEE_LAMPORTS, Escrow, type EscrowSweepPreview, type EscrowSweepRecord, type FaucetResponse, type FeatureFlag, type FriendRequestItem, type FriendsStore, type FriendsStoreState, type FriendshipStatus, type Game, type GameActionsStore, type GameActionsStoreState, type GameHistoryItem, type GameLifecycleState, type GameMetrics, type GamePlayer, type GameStateResponse, type GameStore, type GameStoreState, type GameType, Games, type GenerateHandshakeResponse, type GetTicketsOptions, HttpClient, type IAnalyticsClient, type IHttpClient, type ILogger, type IStorage, type ImageUploadPayload, type InviteFriendRequest, type LeaderboardEntry, type LeaderboardQuery, type LeaderboardRange, type LeaderboardResponse, Leaderboards, type LivePlayer, type LivePlayersPage, Lobbies, type Lobby, type LobbyDepositUpdatedPayload, type LobbyMatchedEvent, type LobbyPlayer, type LobbyStore, type LobbyStoreState, type LogLevel, type LoginResponse, MIN_SOL_TRANSFER_AMOUNT, MIN_TRANSFER_AMOUNT, type MarketBuyResult, type MarketPosition, type MarketSellResult, type MarketState, Markets, type MoneyMinor, NodeStorage, NoopAnalyticsClient, type NotificationEvent, type NotificationsStore, type NotificationsStoreState, type PaginatedCriticalIncidents, type PaginatedFriendRequests, type PaginatedFriends, type PaginatedNotificationsResponse, type PaginatedPlatformFees, type PaginatedReports, type PaginatedSearchUsers, type PaginatedSessions, type PaginatedSupportTickets, type PaginatedTransactionJobs, type PaginatedUsers, type PaymentRequiredChallenge, type PlatformFeeItem, type PrepareDepositResponse, type PrepareTipRequest, type PrepareTipResponse, type PrepareTransferRequest, type PrepareTransferResponse, type PublicUser, type QueueStats, type RedeemResult, type ReferralRewardItem, type ReferralRewardStatus, type ReferralRewardsResponse, type ReferralSummary, type ReferralTreeItem, type ReferralTreeResponse, Referrals, type Report, type ReportCount, type ReportStatus, type ReportUser, Reports, type RetryOptions, SDK, type SDKConfig, SDK_VERSION, SOL_DECIMALS, SOL_MINT, type SdkStore, type SdkUpgradeInfo, type SearchUser, type SendMessageRequest, type SendTipResponse, type SendTransferResponse, type Session, type SessionStats, SharedWorkerTransport, Spectate, type SpectatorMetrics, type SpectatorMetricsByUser, StandaloneWsTransport, type SubmitDepositResponse, type SubmitTransferRequest, type SubmitTransferResponse, Support, type SupportMessage, type SupportMessageSenderRole, type SupportTicket, type SupportTicketCategory, type SupportTicketPriority, type SupportTicketStatus, type SupportTicketUser, type SystemMessageType, TOKEN_KEY, TRANSFER_FEE_MINOR, type TicTacToeClockTimes, Tips, type TransactionJob, type TransactionJobStatus, type TransactionJobType, type TransactionQueueStats, type TransferToken, type TypingUser, type UpdateTicketData, type User, type UserAchievement, type UserActivity, type UserActivityStatus, type UserStats, type UsernameAvailabilityResponse, Users, type ValidAction, Wallet, type WalletActivityCounterpartyType, type WalletActivityDirection, type WalletActivityItem, type WalletActivityKind, type WalletActivityResponse, type WalletActivityStatus, type WalletClaimAction, type WalletMeta, type WalletResponse, type WalletSigner, type WsEvent, WsEventBus, type WsEventName, type WsTransport, type ConnectionState as WsTransportState, createChatStore, createDmThreadsStore, createFriendsStore, createGameActionsStore, createGameStore, createLobbyStore, createLogger, createNotificationsStore, createSdkStore, isRetryableError, logger, selectGameLifecycleState, withRetry };
|
package/dist/index.d.ts
CHANGED
|
@@ -1121,6 +1121,11 @@ declare class Admin {
|
|
|
1121
1121
|
private http;
|
|
1122
1122
|
private logger?;
|
|
1123
1123
|
constructor(http: IHttpClient, logger?: ILogger | undefined);
|
|
1124
|
+
getInternalBots(): Promise<(User & {
|
|
1125
|
+
sol: number;
|
|
1126
|
+
usdc: number;
|
|
1127
|
+
publicKey: string;
|
|
1128
|
+
})[]>;
|
|
1124
1129
|
getUserById(id: string): Promise<User>;
|
|
1125
1130
|
getUserBalance(userId: string): Promise<{
|
|
1126
1131
|
sol: number;
|
|
@@ -1434,12 +1439,55 @@ declare function createGameStore(transport: WsTransport): GameStore;
|
|
|
1434
1439
|
type GameActionsStoreState = {
|
|
1435
1440
|
statesByGameId: Record<string, GameStateResponse>;
|
|
1436
1441
|
};
|
|
1442
|
+
interface ChessClockTimes {
|
|
1443
|
+
whiteTimeMs: number;
|
|
1444
|
+
blackTimeMs: number;
|
|
1445
|
+
}
|
|
1446
|
+
interface ChessCapturedPieces {
|
|
1447
|
+
capturedByWhite: string[];
|
|
1448
|
+
capturedByBlack: string[];
|
|
1449
|
+
}
|
|
1450
|
+
interface TicTacToeClockTimes {
|
|
1451
|
+
xTimeMs: number;
|
|
1452
|
+
oTimeMs: number;
|
|
1453
|
+
}
|
|
1454
|
+
interface Connect4ClockTimes {
|
|
1455
|
+
redTimeMs: number;
|
|
1456
|
+
yellowTimeMs: number;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Common lifecycle fields extracted from any game state.
|
|
1460
|
+
* Used by useGameLifecycle to avoid casting the raw union type.
|
|
1461
|
+
*/
|
|
1462
|
+
interface GameLifecycleState {
|
|
1463
|
+
status: string;
|
|
1464
|
+
winnerId: string | null;
|
|
1465
|
+
betAmount: number;
|
|
1466
|
+
wonAmount: number | null;
|
|
1467
|
+
totalPotMinor: number | undefined;
|
|
1468
|
+
currentPlayerId: string | null;
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Pure selector — extracts lifecycle fields from any game state variant.
|
|
1472
|
+
* Accepts a single GameStateResponse so callers can use it after selecting
|
|
1473
|
+
* the stable store entry via useSdkSelector, avoiding new-object-per-call issues.
|
|
1474
|
+
*/
|
|
1475
|
+
declare function selectGameLifecycleState(gameState: GameStateResponse | null | undefined): GameLifecycleState | null;
|
|
1437
1476
|
type GameActionsStore = {
|
|
1438
1477
|
store: SdkStore<GameActionsStoreState>;
|
|
1439
1478
|
setBaseState: (gameId: string, state: GameStateResponse) => void;
|
|
1440
1479
|
clearState: (gameId: string) => void;
|
|
1441
1480
|
applyWsEvent: (event: WsEvent) => void;
|
|
1442
1481
|
joinGame: (gameId: string) => () => void;
|
|
1482
|
+
getCountdownDigit: (gameId: string, nowMs: number) => number | null;
|
|
1483
|
+
/** Live chess clock — deducts elapsed time since turnStartedAt. */
|
|
1484
|
+
getChessClockTimes: (gameId: string, nowMs: number) => ChessClockTimes | null;
|
|
1485
|
+
/** Captured pieces derived from the current FEN position. */
|
|
1486
|
+
getChessCapturedPieces: (gameId: string) => ChessCapturedPieces | null;
|
|
1487
|
+
/** Live TTT clock — deducts elapsed from the current player's mark (X or O). */
|
|
1488
|
+
getTicTacToeClockTimes: (gameId: string, nowMs: number) => TicTacToeClockTimes | null;
|
|
1489
|
+
/** Live Connect Four clock — deducts elapsed from the current player's color. */
|
|
1490
|
+
getConnect4ClockTimes: (gameId: string, nowMs: number) => Connect4ClockTimes | null;
|
|
1443
1491
|
};
|
|
1444
1492
|
declare function createGameActionsStore(transport: WsTransport): GameActionsStore;
|
|
1445
1493
|
|
|
@@ -1743,7 +1791,7 @@ type LobbyDepositUpdatedPayload = {
|
|
|
1743
1791
|
status: 'confirmed' | 'failed' | 'refunded' | 'refund_failed';
|
|
1744
1792
|
allConfirmed: boolean;
|
|
1745
1793
|
};
|
|
1746
|
-
type
|
|
1794
|
+
type WsEventBase = {
|
|
1747
1795
|
event: 'lobby:created';
|
|
1748
1796
|
payload: Lobby;
|
|
1749
1797
|
} | {
|
|
@@ -1941,6 +1989,9 @@ type WsEvent = {
|
|
|
1941
1989
|
event: 'notification';
|
|
1942
1990
|
payload: unknown;
|
|
1943
1991
|
};
|
|
1992
|
+
type WsEvent = WsEventBase & {
|
|
1993
|
+
_serverTs?: number;
|
|
1994
|
+
};
|
|
1944
1995
|
|
|
1945
1996
|
type LobbyMatchedEvent = {
|
|
1946
1997
|
gameId: string;
|
|
@@ -3006,4 +3057,4 @@ declare class HttpClient implements IHttpClient {
|
|
|
3006
3057
|
private payChallenge;
|
|
3007
3058
|
}
|
|
3008
3059
|
|
|
3009
|
-
export { type AcceptChallengeResponse, type Achievement, Activity, type ActivityFeedItem, type ActivityFeedItemType, type ActivityFeedResponse, type ActivityFeedUser, Admin, type AdminDailyStats, type AdminDailyStatsItem, type AdminFeatureFlag, type AdminGameHistory, type AdminGameHistoryFeeBreakdown, type AdminGameHistoryFeePlayer, type AdminGameHistoryPlayer, type AdminMarketDailyStats, type AdminMarketDailyStatsItem, type AdminMarketDetail, type AdminMarketStats, type AdminStats, type AdminWalletActivityItem, type AdminWalletActivityResponse, type AnalyticsUserData, type ApiError, type AppNotification, type AppNotificationType, type ApplyReferralCodeResponse, type BalanceResponse, type BetOption, type BroadcastTipRequest, BrowserLocalStorage, Challenges, Chat, type ChatContext, type ChatContextType, type ChatMessage, type ChatMessageReply, type ChatMessageType, type ChatReaction, type ChatReadBy, type ChatState, type ChatStore, type ChatStoreState, type ClaimReferralRewardsResponse, type CreateChallengeRequest, type CreateChallengeResponse, type CreateLobbyRequest, type CreateTicketData, type CriticalIncident, type CriticalIncidentCategory, type CriticalIncidentImpactType, type CriticalIncidentSeverity, type CriticalIncidentStatus, type CriticalIncidentSummary, type CurrentGame, Daily, type DailyParticipant, type DailyRoom, type DailyToken, type DepositForLobbyResponse, type DepositStatus, type DepositStatusResponse, type DmThread, type DmThreadsStore, type DmThreadsStoreState, type DonateToGameResponse, ESTIMATED_SOL_FEE_LAMPORTS, Escrow, type EscrowSweepPreview, type EscrowSweepRecord, type FaucetResponse, type FeatureFlag, type FriendRequestItem, type FriendsStore, type FriendsStoreState, type FriendshipStatus, type Game, type GameActionsStore, type GameActionsStoreState, type GameHistoryItem, type GameMetrics, type GamePlayer, type GameStateResponse, type GameStore, type GameStoreState, type GameType, Games, type GenerateHandshakeResponse, type GetTicketsOptions, HttpClient, type IAnalyticsClient, type IHttpClient, type ILogger, type IStorage, type ImageUploadPayload, type InviteFriendRequest, type LeaderboardEntry, type LeaderboardQuery, type LeaderboardRange, type LeaderboardResponse, Leaderboards, type LivePlayer, type LivePlayersPage, Lobbies, type Lobby, type LobbyDepositUpdatedPayload, type LobbyMatchedEvent, type LobbyPlayer, type LobbyStore, type LobbyStoreState, type LogLevel, type LoginResponse, MIN_SOL_TRANSFER_AMOUNT, MIN_TRANSFER_AMOUNT, type MarketBuyResult, type MarketPosition, type MarketSellResult, type MarketState, Markets, type MoneyMinor, NodeStorage, NoopAnalyticsClient, type NotificationEvent, type NotificationsStore, type NotificationsStoreState, type PaginatedCriticalIncidents, type PaginatedFriendRequests, type PaginatedFriends, type PaginatedNotificationsResponse, type PaginatedPlatformFees, type PaginatedReports, type PaginatedSearchUsers, type PaginatedSessions, type PaginatedSupportTickets, type PaginatedTransactionJobs, type PaginatedUsers, type PaymentRequiredChallenge, type PlatformFeeItem, type PrepareDepositResponse, type PrepareTipRequest, type PrepareTipResponse, type PrepareTransferRequest, type PrepareTransferResponse, type PublicUser, type QueueStats, type RedeemResult, type ReferralRewardItem, type ReferralRewardStatus, type ReferralRewardsResponse, type ReferralSummary, type ReferralTreeItem, type ReferralTreeResponse, Referrals, type Report, type ReportCount, type ReportStatus, type ReportUser, Reports, type RetryOptions, SDK, type SDKConfig, SDK_VERSION, SOL_DECIMALS, SOL_MINT, type SdkStore, type SdkUpgradeInfo, type SearchUser, type SendMessageRequest, type SendTipResponse, type SendTransferResponse, type Session, type SessionStats, SharedWorkerTransport, Spectate, type SpectatorMetrics, type SpectatorMetricsByUser, StandaloneWsTransport, type SubmitDepositResponse, type SubmitTransferRequest, type SubmitTransferResponse, Support, type SupportMessage, type SupportMessageSenderRole, type SupportTicket, type SupportTicketCategory, type SupportTicketPriority, type SupportTicketStatus, type SupportTicketUser, type SystemMessageType, TOKEN_KEY, TRANSFER_FEE_MINOR, Tips, type TransactionJob, type TransactionJobStatus, type TransactionJobType, type TransactionQueueStats, type TransferToken, type TypingUser, type UpdateTicketData, type User, type UserAchievement, type UserActivity, type UserActivityStatus, type UserStats, type UsernameAvailabilityResponse, Users, type ValidAction, Wallet, type WalletActivityCounterpartyType, type WalletActivityDirection, type WalletActivityItem, type WalletActivityKind, type WalletActivityResponse, type WalletActivityStatus, type WalletClaimAction, type WalletMeta, type WalletResponse, type WalletSigner, type WsEvent, WsEventBus, type WsEventName, type WsTransport, type ConnectionState as WsTransportState, createChatStore, createDmThreadsStore, createFriendsStore, createGameActionsStore, createGameStore, createLobbyStore, createLogger, createNotificationsStore, createSdkStore, isRetryableError, logger, withRetry };
|
|
3060
|
+
export { type AcceptChallengeResponse, type Achievement, Activity, type ActivityFeedItem, type ActivityFeedItemType, type ActivityFeedResponse, type ActivityFeedUser, Admin, type AdminDailyStats, type AdminDailyStatsItem, type AdminFeatureFlag, type AdminGameHistory, type AdminGameHistoryFeeBreakdown, type AdminGameHistoryFeePlayer, type AdminGameHistoryPlayer, type AdminMarketDailyStats, type AdminMarketDailyStatsItem, type AdminMarketDetail, type AdminMarketStats, type AdminStats, type AdminWalletActivityItem, type AdminWalletActivityResponse, type AnalyticsUserData, type ApiError, type AppNotification, type AppNotificationType, type ApplyReferralCodeResponse, type BalanceResponse, type BetOption, type BroadcastTipRequest, BrowserLocalStorage, Challenges, Chat, type ChatContext, type ChatContextType, type ChatMessage, type ChatMessageReply, type ChatMessageType, type ChatReaction, type ChatReadBy, type ChatState, type ChatStore, type ChatStoreState, type ChessCapturedPieces, type ChessClockTimes, type ClaimReferralRewardsResponse, type Connect4ClockTimes, type CreateChallengeRequest, type CreateChallengeResponse, type CreateLobbyRequest, type CreateTicketData, type CriticalIncident, type CriticalIncidentCategory, type CriticalIncidentImpactType, type CriticalIncidentSeverity, type CriticalIncidentStatus, type CriticalIncidentSummary, type CurrentGame, Daily, type DailyParticipant, type DailyRoom, type DailyToken, type DepositForLobbyResponse, type DepositStatus, type DepositStatusResponse, type DmThread, type DmThreadsStore, type DmThreadsStoreState, type DonateToGameResponse, ESTIMATED_SOL_FEE_LAMPORTS, Escrow, type EscrowSweepPreview, type EscrowSweepRecord, type FaucetResponse, type FeatureFlag, type FriendRequestItem, type FriendsStore, type FriendsStoreState, type FriendshipStatus, type Game, type GameActionsStore, type GameActionsStoreState, type GameHistoryItem, type GameLifecycleState, type GameMetrics, type GamePlayer, type GameStateResponse, type GameStore, type GameStoreState, type GameType, Games, type GenerateHandshakeResponse, type GetTicketsOptions, HttpClient, type IAnalyticsClient, type IHttpClient, type ILogger, type IStorage, type ImageUploadPayload, type InviteFriendRequest, type LeaderboardEntry, type LeaderboardQuery, type LeaderboardRange, type LeaderboardResponse, Leaderboards, type LivePlayer, type LivePlayersPage, Lobbies, type Lobby, type LobbyDepositUpdatedPayload, type LobbyMatchedEvent, type LobbyPlayer, type LobbyStore, type LobbyStoreState, type LogLevel, type LoginResponse, MIN_SOL_TRANSFER_AMOUNT, MIN_TRANSFER_AMOUNT, type MarketBuyResult, type MarketPosition, type MarketSellResult, type MarketState, Markets, type MoneyMinor, NodeStorage, NoopAnalyticsClient, type NotificationEvent, type NotificationsStore, type NotificationsStoreState, type PaginatedCriticalIncidents, type PaginatedFriendRequests, type PaginatedFriends, type PaginatedNotificationsResponse, type PaginatedPlatformFees, type PaginatedReports, type PaginatedSearchUsers, type PaginatedSessions, type PaginatedSupportTickets, type PaginatedTransactionJobs, type PaginatedUsers, type PaymentRequiredChallenge, type PlatformFeeItem, type PrepareDepositResponse, type PrepareTipRequest, type PrepareTipResponse, type PrepareTransferRequest, type PrepareTransferResponse, type PublicUser, type QueueStats, type RedeemResult, type ReferralRewardItem, type ReferralRewardStatus, type ReferralRewardsResponse, type ReferralSummary, type ReferralTreeItem, type ReferralTreeResponse, Referrals, type Report, type ReportCount, type ReportStatus, type ReportUser, Reports, type RetryOptions, SDK, type SDKConfig, SDK_VERSION, SOL_DECIMALS, SOL_MINT, type SdkStore, type SdkUpgradeInfo, type SearchUser, type SendMessageRequest, type SendTipResponse, type SendTransferResponse, type Session, type SessionStats, SharedWorkerTransport, Spectate, type SpectatorMetrics, type SpectatorMetricsByUser, StandaloneWsTransport, type SubmitDepositResponse, type SubmitTransferRequest, type SubmitTransferResponse, Support, type SupportMessage, type SupportMessageSenderRole, type SupportTicket, type SupportTicketCategory, type SupportTicketPriority, type SupportTicketStatus, type SupportTicketUser, type SystemMessageType, TOKEN_KEY, TRANSFER_FEE_MINOR, type TicTacToeClockTimes, Tips, type TransactionJob, type TransactionJobStatus, type TransactionJobType, type TransactionQueueStats, type TransferToken, type TypingUser, type UpdateTicketData, type User, type UserAchievement, type UserActivity, type UserActivityStatus, type UserStats, type UsernameAvailabilityResponse, Users, type ValidAction, Wallet, type WalletActivityCounterpartyType, type WalletActivityDirection, type WalletActivityItem, type WalletActivityKind, type WalletActivityResponse, type WalletActivityStatus, type WalletClaimAction, type WalletMeta, type WalletResponse, type WalletSigner, type WsEvent, WsEventBus, type WsEventName, type WsTransport, type ConnectionState as WsTransportState, createChatStore, createDmThreadsStore, createFriendsStore, createGameActionsStore, createGameStore, createLobbyStore, createLogger, createNotificationsStore, createSdkStore, isRetryableError, logger, selectGameLifecycleState, withRetry };
|
package/dist/index.js
CHANGED
|
@@ -743,6 +743,9 @@ var Admin = class {
|
|
|
743
743
|
this.http = http;
|
|
744
744
|
this.logger = logger2;
|
|
745
745
|
}
|
|
746
|
+
async getInternalBots() {
|
|
747
|
+
return this.http.get("/admin/internal-bots");
|
|
748
|
+
}
|
|
746
749
|
async getUserById(id) {
|
|
747
750
|
return this.http.get(`/admin/users/${id}`);
|
|
748
751
|
}
|
|
@@ -3410,6 +3413,19 @@ function createGameStore(transport) {
|
|
|
3410
3413
|
}
|
|
3411
3414
|
|
|
3412
3415
|
// src/stores/game-actions-store.ts
|
|
3416
|
+
function selectGameLifecycleState(gameState) {
|
|
3417
|
+
const s = gameState;
|
|
3418
|
+
if (!s) return null;
|
|
3419
|
+
const raw = s;
|
|
3420
|
+
return {
|
|
3421
|
+
status: raw.status,
|
|
3422
|
+
winnerId: raw.winnerId ?? null,
|
|
3423
|
+
betAmount: raw.betAmount ?? 0,
|
|
3424
|
+
wonAmount: raw.wonAmount ?? null,
|
|
3425
|
+
totalPotMinor: raw.totalPotMinor,
|
|
3426
|
+
currentPlayerId: raw.currentPlayerId ?? null
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3413
3429
|
var isRpsCompletionPayload = (payload) => payload.gameType === "rock-paper-scissors";
|
|
3414
3430
|
function createGameActionsStore(transport) {
|
|
3415
3431
|
const store = createSdkStore({
|
|
@@ -3431,8 +3447,21 @@ function createGameActionsStore(transport) {
|
|
|
3431
3447
|
});
|
|
3432
3448
|
};
|
|
3433
3449
|
const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
|
|
3450
|
+
const pendingEvents = /* @__PURE__ */ new Map();
|
|
3451
|
+
function enqueue(gameId, event) {
|
|
3452
|
+
const q = pendingEvents.get(gameId) ?? [];
|
|
3453
|
+
q.push(event);
|
|
3454
|
+
pendingEvents.set(gameId, q);
|
|
3455
|
+
}
|
|
3456
|
+
function drainQueue(gameId) {
|
|
3457
|
+
const q = pendingEvents.get(gameId);
|
|
3458
|
+
if (!q?.length) return;
|
|
3459
|
+
pendingEvents.delete(gameId);
|
|
3460
|
+
for (const ev of q) applyWsEvent(ev);
|
|
3461
|
+
}
|
|
3434
3462
|
const setBaseState = (gameId, state) => {
|
|
3435
3463
|
updateState(gameId, state);
|
|
3464
|
+
drainQueue(gameId);
|
|
3436
3465
|
};
|
|
3437
3466
|
const clearState = (gameId) => {
|
|
3438
3467
|
store.updateState((state) => {
|
|
@@ -3442,6 +3471,7 @@ function createGameActionsStore(transport) {
|
|
|
3442
3471
|
const { [gameId]: _, ...rest } = state.statesByGameId;
|
|
3443
3472
|
return { ...state, statesByGameId: rest };
|
|
3444
3473
|
});
|
|
3474
|
+
pendingEvents.delete(gameId);
|
|
3445
3475
|
};
|
|
3446
3476
|
const applyWsEvent = (event) => {
|
|
3447
3477
|
switch (event.event) {
|
|
@@ -3460,7 +3490,10 @@ function createGameActionsStore(transport) {
|
|
|
3460
3490
|
}
|
|
3461
3491
|
case "game:rps:starting": {
|
|
3462
3492
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3463
|
-
if (!current || !isRpsState(current))
|
|
3493
|
+
if (!current || !isRpsState(current)) {
|
|
3494
|
+
enqueue(event.payload.gameId, event);
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3464
3497
|
const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
|
|
3465
3498
|
const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
|
|
3466
3499
|
const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
|
|
@@ -3481,7 +3514,10 @@ function createGameActionsStore(transport) {
|
|
|
3481
3514
|
}
|
|
3482
3515
|
case "game:rps:round:started": {
|
|
3483
3516
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3484
|
-
if (!current || !isRpsState(current))
|
|
3517
|
+
if (!current || !isRpsState(current)) {
|
|
3518
|
+
enqueue(event.payload.gameId, event);
|
|
3519
|
+
return;
|
|
3520
|
+
}
|
|
3485
3521
|
const actions = {};
|
|
3486
3522
|
const baseUsers = /* @__PURE__ */ new Set();
|
|
3487
3523
|
Object.keys(current.roundState.actions).forEach(
|
|
@@ -3515,7 +3551,10 @@ function createGameActionsStore(transport) {
|
|
|
3515
3551
|
}
|
|
3516
3552
|
case "game:rps:action:received": {
|
|
3517
3553
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3518
|
-
if (!current || !isRpsState(current))
|
|
3554
|
+
if (!current || !isRpsState(current)) {
|
|
3555
|
+
enqueue(event.payload.gameId, event);
|
|
3556
|
+
return;
|
|
3557
|
+
}
|
|
3519
3558
|
const updated = {
|
|
3520
3559
|
...current,
|
|
3521
3560
|
roundState: {
|
|
@@ -3534,7 +3573,10 @@ function createGameActionsStore(transport) {
|
|
|
3534
3573
|
}
|
|
3535
3574
|
case "game:rps:timer:cutoff": {
|
|
3536
3575
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3537
|
-
if (!current || !isRpsState(current))
|
|
3576
|
+
if (!current || !isRpsState(current)) {
|
|
3577
|
+
enqueue(event.payload.gameId, event);
|
|
3578
|
+
return;
|
|
3579
|
+
}
|
|
3538
3580
|
const updated = {
|
|
3539
3581
|
...current,
|
|
3540
3582
|
roundState: {
|
|
@@ -3548,7 +3590,10 @@ function createGameActionsStore(transport) {
|
|
|
3548
3590
|
}
|
|
3549
3591
|
case "game:rps:round:reveal": {
|
|
3550
3592
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3551
|
-
if (!current || !isRpsState(current))
|
|
3593
|
+
if (!current || !isRpsState(current)) {
|
|
3594
|
+
enqueue(event.payload.gameId, event);
|
|
3595
|
+
return;
|
|
3596
|
+
}
|
|
3552
3597
|
const actions = {};
|
|
3553
3598
|
const payloadActions = event.payload.actions;
|
|
3554
3599
|
Object.keys(payloadActions || {}).forEach((userId) => {
|
|
@@ -3572,7 +3617,10 @@ function createGameActionsStore(transport) {
|
|
|
3572
3617
|
}
|
|
3573
3618
|
case "game:rps:round:completed": {
|
|
3574
3619
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3575
|
-
if (!current || !isRpsState(current))
|
|
3620
|
+
if (!current || !isRpsState(current)) {
|
|
3621
|
+
enqueue(event.payload.gameId, event);
|
|
3622
|
+
return;
|
|
3623
|
+
}
|
|
3576
3624
|
const roundHistory = [
|
|
3577
3625
|
...current.roundHistory || [],
|
|
3578
3626
|
{
|
|
@@ -3597,7 +3645,10 @@ function createGameActionsStore(transport) {
|
|
|
3597
3645
|
}
|
|
3598
3646
|
case "game:rps:timeout": {
|
|
3599
3647
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3600
|
-
if (!current || !isRpsState(current))
|
|
3648
|
+
if (!current || !isRpsState(current)) {
|
|
3649
|
+
enqueue(event.payload.gameId, event);
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
3601
3652
|
const timedOutUser = event.payload.playerId;
|
|
3602
3653
|
const action = event.payload.action;
|
|
3603
3654
|
const updated = {
|
|
@@ -3622,7 +3673,10 @@ function createGameActionsStore(transport) {
|
|
|
3622
3673
|
const payload = event.payload;
|
|
3623
3674
|
const { gameId } = payload;
|
|
3624
3675
|
const current = store.getState().statesByGameId[gameId];
|
|
3625
|
-
if (!current)
|
|
3676
|
+
if (!current) {
|
|
3677
|
+
enqueue(gameId, event);
|
|
3678
|
+
return;
|
|
3679
|
+
}
|
|
3626
3680
|
const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
|
|
3627
3681
|
...current,
|
|
3628
3682
|
status: "completed",
|
|
@@ -3671,11 +3725,15 @@ function createGameActionsStore(transport) {
|
|
|
3671
3725
|
const current = store.getState().statesByGameId[gameId];
|
|
3672
3726
|
const updated = current ? { ...current, ...incoming } : incoming;
|
|
3673
3727
|
updateState(gameId, updated);
|
|
3728
|
+
drainQueue(gameId);
|
|
3674
3729
|
break;
|
|
3675
3730
|
}
|
|
3676
3731
|
case "game:rematch:requested": {
|
|
3677
3732
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3678
|
-
if (!current)
|
|
3733
|
+
if (!current) {
|
|
3734
|
+
enqueue(event.payload.gameId, event);
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3679
3737
|
const requestedBy = event.payload.requestedBy;
|
|
3680
3738
|
const userId = event.payload.userId;
|
|
3681
3739
|
const requested = new Set(
|
|
@@ -3691,7 +3749,10 @@ function createGameActionsStore(transport) {
|
|
|
3691
3749
|
}
|
|
3692
3750
|
case "game:rematch:cancelled": {
|
|
3693
3751
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3694
|
-
if (!current)
|
|
3752
|
+
if (!current) {
|
|
3753
|
+
enqueue(event.payload.gameId, event);
|
|
3754
|
+
return;
|
|
3755
|
+
}
|
|
3695
3756
|
const requestedBy = event.payload.requestedBy ?? [];
|
|
3696
3757
|
const updated = {
|
|
3697
3758
|
...current,
|
|
@@ -3702,7 +3763,10 @@ function createGameActionsStore(transport) {
|
|
|
3702
3763
|
}
|
|
3703
3764
|
case "game:rematch:started": {
|
|
3704
3765
|
const current = store.getState().statesByGameId[event.payload.gameId];
|
|
3705
|
-
if (!current)
|
|
3766
|
+
if (!current) {
|
|
3767
|
+
enqueue(event.payload.gameId, event);
|
|
3768
|
+
return;
|
|
3769
|
+
}
|
|
3706
3770
|
const updated = {
|
|
3707
3771
|
...current,
|
|
3708
3772
|
rematchRequestedBy: event.payload.playerIds ?? []
|
|
@@ -3713,7 +3777,10 @@ function createGameActionsStore(transport) {
|
|
|
3713
3777
|
case "game:pot:updated": {
|
|
3714
3778
|
const { gameId, totalPotMinor } = event.payload;
|
|
3715
3779
|
const current = store.getState().statesByGameId[gameId];
|
|
3716
|
-
if (!current)
|
|
3780
|
+
if (!current) {
|
|
3781
|
+
enqueue(gameId, event);
|
|
3782
|
+
return;
|
|
3783
|
+
}
|
|
3717
3784
|
const updated = {
|
|
3718
3785
|
...current,
|
|
3719
3786
|
totalPotMinor
|
|
@@ -3726,12 +3793,123 @@ function createGameActionsStore(transport) {
|
|
|
3726
3793
|
}
|
|
3727
3794
|
};
|
|
3728
3795
|
const joinGame = (gameId) => transport.joinRoom(`game:${gameId}`);
|
|
3796
|
+
const getCountdownDigit = (gameId, nowMs) => {
|
|
3797
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3798
|
+
if (!state) return null;
|
|
3799
|
+
if (isRpsState(state)) {
|
|
3800
|
+
if (state.roundState.phase !== "starting") return null;
|
|
3801
|
+
const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
|
|
3802
|
+
if (remaining <= 0) return null;
|
|
3803
|
+
return Math.ceil(remaining / 1e3);
|
|
3804
|
+
}
|
|
3805
|
+
const bufferEndsAt = state.bufferEndsAt;
|
|
3806
|
+
if (bufferEndsAt) {
|
|
3807
|
+
const remaining = new Date(bufferEndsAt).getTime() - nowMs;
|
|
3808
|
+
if (remaining <= 0) return null;
|
|
3809
|
+
return Math.ceil(remaining / 1e3);
|
|
3810
|
+
}
|
|
3811
|
+
return null;
|
|
3812
|
+
};
|
|
3813
|
+
const getChessClockTimes = (gameId, nowMs) => {
|
|
3814
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3815
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3816
|
+
const s = state;
|
|
3817
|
+
let whiteMs = s.whiteTimeMs ?? 0;
|
|
3818
|
+
let blackMs = s.blackTimeMs ?? 0;
|
|
3819
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3820
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3821
|
+
if (!Number.isNaN(startedAt)) {
|
|
3822
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3823
|
+
if (s.currentPlayerId === s.whitePlayerId) {
|
|
3824
|
+
whiteMs = Math.max(0, whiteMs - elapsed);
|
|
3825
|
+
} else if (s.currentPlayerId === s.blackPlayerId) {
|
|
3826
|
+
blackMs = Math.max(0, blackMs - elapsed);
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
|
|
3831
|
+
};
|
|
3832
|
+
const getChessCapturedPieces = (gameId) => {
|
|
3833
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3834
|
+
if (!state || state.gameType !== "chess") return null;
|
|
3835
|
+
const fen = state.fen;
|
|
3836
|
+
if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
|
|
3837
|
+
const placement = fen.split(" ")[0];
|
|
3838
|
+
const white = {};
|
|
3839
|
+
const black = {};
|
|
3840
|
+
for (const char of placement) {
|
|
3841
|
+
if (char === "/" || char >= "1" && char <= "8") continue;
|
|
3842
|
+
const lower = char.toLowerCase();
|
|
3843
|
+
if (char === lower) {
|
|
3844
|
+
black[lower] = (black[lower] ?? 0) + 1;
|
|
3845
|
+
} else {
|
|
3846
|
+
white[lower] = (white[lower] ?? 0) + 1;
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
const INITIAL = {
|
|
3850
|
+
p: 8,
|
|
3851
|
+
r: 2,
|
|
3852
|
+
n: 2,
|
|
3853
|
+
b: 2,
|
|
3854
|
+
q: 1,
|
|
3855
|
+
k: 1
|
|
3856
|
+
};
|
|
3857
|
+
const capturedByWhite = [];
|
|
3858
|
+
const capturedByBlack = [];
|
|
3859
|
+
for (const [type, initial] of Object.entries(INITIAL)) {
|
|
3860
|
+
const missingBlack = initial - (black[type] ?? 0);
|
|
3861
|
+
for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type}`);
|
|
3862
|
+
const missingWhite = initial - (white[type] ?? 0);
|
|
3863
|
+
for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type}`);
|
|
3864
|
+
}
|
|
3865
|
+
return { capturedByWhite, capturedByBlack };
|
|
3866
|
+
};
|
|
3867
|
+
const getTicTacToeClockTimes = (gameId, nowMs) => {
|
|
3868
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3869
|
+
if (!state || state.gameType !== "tic-tac-toe") return null;
|
|
3870
|
+
const s = state;
|
|
3871
|
+
let xMs = s.xTimeMs ?? 0;
|
|
3872
|
+
let oMs = s.oTimeMs ?? 0;
|
|
3873
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3874
|
+
const currentMark = s.playerMarks[s.currentPlayerId];
|
|
3875
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3876
|
+
if (!Number.isNaN(startedAt)) {
|
|
3877
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3878
|
+
if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
|
|
3879
|
+
else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
return { xTimeMs: xMs, oTimeMs: oMs };
|
|
3883
|
+
};
|
|
3884
|
+
const getConnect4ClockTimes = (gameId, nowMs) => {
|
|
3885
|
+
const state = store.getState().statesByGameId[gameId];
|
|
3886
|
+
if (!state || state.gameType !== "connect-four") return null;
|
|
3887
|
+
const s = state;
|
|
3888
|
+
let redMs = s.redTimeMs ?? 0;
|
|
3889
|
+
let yellowMs = s.yellowTimeMs ?? 0;
|
|
3890
|
+
if (s.status === "active" && s.currentPlayerId) {
|
|
3891
|
+
const currentColor = s.playerColors[s.currentPlayerId];
|
|
3892
|
+
const startedAt = Date.parse(s.turnStartedAt);
|
|
3893
|
+
if (!Number.isNaN(startedAt)) {
|
|
3894
|
+
const elapsed = Math.max(0, nowMs - startedAt);
|
|
3895
|
+
if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
|
|
3896
|
+
else if (currentColor === "YELLOW")
|
|
3897
|
+
yellowMs = Math.max(0, yellowMs - elapsed);
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
return { redTimeMs: redMs, yellowTimeMs: yellowMs };
|
|
3901
|
+
};
|
|
3729
3902
|
return {
|
|
3730
3903
|
store,
|
|
3731
3904
|
setBaseState,
|
|
3732
3905
|
clearState,
|
|
3733
3906
|
applyWsEvent,
|
|
3734
|
-
joinGame
|
|
3907
|
+
joinGame,
|
|
3908
|
+
getCountdownDigit,
|
|
3909
|
+
getChessClockTimes,
|
|
3910
|
+
getChessCapturedPieces,
|
|
3911
|
+
getTicTacToeClockTimes,
|
|
3912
|
+
getConnect4ClockTimes
|
|
3735
3913
|
};
|
|
3736
3914
|
}
|
|
3737
3915
|
function isRpsState(state) {
|
|
@@ -4401,12 +4579,14 @@ var WsRouter = class {
|
|
|
4401
4579
|
}
|
|
4402
4580
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4403
4581
|
if (!decoded) return;
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
this.deps.
|
|
4407
|
-
this.deps.
|
|
4408
|
-
this.deps.
|
|
4409
|
-
this.deps.
|
|
4582
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4583
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4584
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4585
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4586
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4587
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4588
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4589
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4410
4590
|
});
|
|
4411
4591
|
}
|
|
4412
4592
|
stop() {
|
|
@@ -4418,12 +4598,14 @@ var WsRouter = class {
|
|
|
4418
4598
|
this.transport.subscribeEvent(eventName, (payload) => {
|
|
4419
4599
|
const decoded = decodeWsEvent(eventName, payload);
|
|
4420
4600
|
if (!decoded) return;
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
this.deps.
|
|
4424
|
-
this.deps.
|
|
4425
|
-
this.deps.
|
|
4426
|
-
this.deps.
|
|
4601
|
+
const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
|
|
4602
|
+
const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
|
|
4603
|
+
this.deps.lobbyStore.applyWsEvent(event);
|
|
4604
|
+
this.deps.gameStore.applyWsEvent(event);
|
|
4605
|
+
this.deps.gameActionsStore.applyWsEvent(event);
|
|
4606
|
+
this.deps.chatStore.applyWsEvent(event);
|
|
4607
|
+
this.deps.dmThreadsStore.applyWsEvent(event);
|
|
4608
|
+
this.deps.notificationsStore.applyWsEvent(event);
|
|
4427
4609
|
});
|
|
4428
4610
|
}
|
|
4429
4611
|
}
|
|
@@ -4826,6 +5008,7 @@ export {
|
|
|
4826
5008
|
export_formatMoneyMinor as formatMoneyMinor,
|
|
4827
5009
|
isRetryableError,
|
|
4828
5010
|
logger,
|
|
5011
|
+
selectGameLifecycleState,
|
|
4829
5012
|
export_toMajor as toMajor,
|
|
4830
5013
|
withRetry
|
|
4831
5014
|
};
|