trucoshi 11.1.0 → 12.0.0-rc.1
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 +57 -1
- package/dist/events.d.ts +43 -2
- package/dist/events.js +11 -0
- package/dist/lib/Skins.d.ts +10 -0
- package/dist/lib/Skins.js +10 -0
- package/dist/lib/Treasures.d.ts +3 -0
- package/dist/lib/Treasures.js +7 -0
- package/dist/lib/classes/Deck.js +2 -0
- package/dist/lib/classes/Random.d.ts +2 -0
- package/dist/lib/classes/Random.js +10 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +2 -0
- package/dist/types.d.ts +120 -5
- package/dist/types.js +5 -0
- package/package.json +4 -3
- package/prisma/client/edge.js +105 -6
- package/prisma/client/index-browser.js +100 -1
- package/prisma/client/index.d.ts +14909 -1833
- package/prisma/client/index.js +105 -6
- package/prisma/client/package.json +1 -1
- package/prisma/client/schema.prisma +160 -26
- package/prisma/client/wasm.js +105 -6
package/README.md
CHANGED
|
@@ -26,10 +26,66 @@ Proba la demo actual en [Trucoshi](https://trucoshi.com)
|
|
|
26
26
|
|
|
27
27
|
`yarn build`
|
|
28
28
|
|
|
29
|
+
### Full local dev stack
|
|
30
|
+
|
|
31
|
+
Run the three-repo local stack from this repo with:
|
|
32
|
+
|
|
33
|
+
`yarn dev:all:sudo-docker`
|
|
34
|
+
|
|
35
|
+
Do not prefix the command with `sudo`. The script only uses sudo for Docker commands, and running Yarn itself as root can corrupt local links and generated Prisma files.
|
|
36
|
+
|
|
29
37
|
# Test
|
|
30
38
|
|
|
31
39
|
`yarn test`
|
|
32
40
|
|
|
41
|
+
### Docker e2e
|
|
42
|
+
|
|
43
|
+
Trucoshi e2e tests depend on a running Lightning Accounts e2e server. Start that first in one
|
|
44
|
+
terminal, then run the Trucoshi e2e suite in another:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd /home/fran/Workspace/trucoshi/lightning-accounts
|
|
48
|
+
NODE_ENV=test ./init-e2e.sh
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd /home/fran/Workspace/trucoshi/trucoshi
|
|
53
|
+
NODE_ENV=test ./init-e2e.sh
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use `../lightning-accounts/init-test.sh` when you want to run the Lightning Accounts Jest/e2e
|
|
57
|
+
suite itself. Use `../lightning-accounts/init-e2e.sh` when Trucoshi needs the persistent API,
|
|
58
|
+
regtest bitcoind, and LND containers.
|
|
59
|
+
|
|
60
|
+
### Docker migrations
|
|
61
|
+
|
|
62
|
+
Production and staging containers do not run database seeds. Run migrations explicitly before
|
|
63
|
+
starting a newly built app image.
|
|
64
|
+
|
|
65
|
+
Staging:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
docker compose -f docker-compose.staging.yml --env-file .env build server
|
|
69
|
+
docker compose -f docker-compose.staging.yml --env-file .env up -d postgres redis
|
|
70
|
+
yarn docker:staging:migrate
|
|
71
|
+
docker compose -f docker-compose.staging.yml --env-file .env up -d --build server
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Production uses the same sequence with `docker-compose.prod.yml` and `yarn docker:prod:migrate`.
|
|
75
|
+
|
|
76
|
+
### Admin users
|
|
77
|
+
|
|
78
|
+
User roles live in Lightning Accounts. To promote an existing user by email:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd /home/fran/Workspace/trucoshi/lightning-accounts
|
|
82
|
+
yarn docker:staging:make-admin --email you@example.com
|
|
83
|
+
yarn docker:prod:make-admin --email you@example.com
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The command only updates an existing non-`APPLICATION` user to `ADMIN`; it does not create users or
|
|
87
|
+
run seeds.
|
|
88
|
+
|
|
33
89
|
### Play
|
|
34
90
|
|
|
35
91
|
`yarn cli:play`
|
|
@@ -53,7 +109,7 @@ Proba la demo actual en [Trucoshi](https://trucoshi.com)
|
|
|
53
109
|
|
|
54
110
|
# Donations
|
|
55
111
|
|
|
56
|
-
Donate Bitcoin at [
|
|
112
|
+
Donate Bitcoin at [jfrader.com/tips](https://jfrader.com/tips)
|
|
57
113
|
|
|
58
114
|
# License
|
|
59
115
|
|
package/dist/events.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SocketError } from "./server";
|
|
2
|
-
import { EMatchState, IAccountDetails, IChatMessage, ILobbyOptions, IMatchDetails, IPlayerRanking, IPublicChatRoom, IPublicMatch, IPublicMatchInfo, IPublicMatchStats, IJoinQueueOptions, IQueueMatchFound, IQueueStatus, ITrucoshiStats, IUserData, IWaitingPlayData, IWaitingSayData } from "./types";
|
|
2
|
+
import { EMatchState, IAdminCreateChestRewardCodeInput, IAdminCreateChestRewardCodeResult, IAdminDashboard, IAdminSetNoticeBannerInput, IAdminSetNoticeBannerResult, IAccountDetails, CardSkinId, ICard, IChatMessage, IEquippedDeck, ILobbyOptions, IInventoryCardGroup, IMatchDetails, IPlayerRanking, IPublicChatRoom, IPublicMatch, IPublicMatchInfo, IPublicMatchStats, IJoinQueueOptions, IQueueMatchFound, IQueueStatus, IRewardCodeRedeemResult, IPublicNoticeBanner, ITreasureOpenResult, ITreasureStatus, ITrucoshiStats, IUserData, IWaitingPlayData, IWaitingSayData } from "./types";
|
|
3
3
|
export type IEventCallback<T = {}> = (args: {
|
|
4
4
|
success: boolean;
|
|
5
5
|
error?: SocketError;
|
|
@@ -22,7 +22,8 @@ export declare enum EServerEvent {
|
|
|
22
22
|
UPDATE_CHAT = "UPDAET_CHAT",
|
|
23
23
|
UPDATE_STATS = "UPDATE_STATS",
|
|
24
24
|
QUEUE_UPDATE = "QUEUE_UPDATE",
|
|
25
|
-
QUEUE_MATCH_FOUND = "QUEUE_MATCH_FOUND"
|
|
25
|
+
QUEUE_MATCH_FOUND = "QUEUE_MATCH_FOUND",
|
|
26
|
+
UPDATE_NOTICE_BANNER = "UPDATE_NOTICE_BANNER"
|
|
26
27
|
}
|
|
27
28
|
export interface ServerToClientEvents {
|
|
28
29
|
[EServerEvent.PONG]: (serverTime: number, clientTime: number) => void;
|
|
@@ -30,6 +31,7 @@ export interface ServerToClientEvents {
|
|
|
30
31
|
[EServerEvent.UPDATE_STATS]: (room: ITrucoshiStats) => void;
|
|
31
32
|
[EServerEvent.QUEUE_UPDATE]: (status: IQueueStatus) => void;
|
|
32
33
|
[EServerEvent.QUEUE_MATCH_FOUND]: (match: IQueueMatchFound) => void;
|
|
34
|
+
[EServerEvent.UPDATE_NOTICE_BANNER]: (noticeBanner: IPublicNoticeBanner | null) => void;
|
|
33
35
|
[EServerEvent.NEW_MESSAGE]: (roomId: string, message?: IChatMessage) => void;
|
|
34
36
|
[EServerEvent.UPDATE_ACTIVE_MATCHES]: (activeMatches: IPublicMatchInfo[]) => void;
|
|
35
37
|
[EServerEvent.UPDATE_PUBLIC_MATCHES]: (publicMatches: IPublicMatchInfo[]) => void;
|
|
@@ -60,6 +62,16 @@ export declare enum EClientEvent {
|
|
|
60
62
|
PLAY_AGAIN = "PLAY_AGAIN",
|
|
61
63
|
JOIN_QUEUE = "JOIN_QUEUE",
|
|
62
64
|
LEAVE_QUEUE = "LEAVE_QUEUE",
|
|
65
|
+
FETCH_INVENTORY = "FETCH_INVENTORY",
|
|
66
|
+
SET_DECK_CARD_SKIN = "SET_DECK_CARD_SKIN",
|
|
67
|
+
FETCH_TREASURE_STATUS = "FETCH_TREASURE_STATUS",
|
|
68
|
+
OPEN_TREASURE_CHEST = "OPEN_TREASURE_CHEST",
|
|
69
|
+
DEV_GRANT_TREASURE_CHEST = "DEV_GRANT_TREASURE_CHEST",
|
|
70
|
+
FETCH_NOTICE_BANNER = "FETCH_NOTICE_BANNER",
|
|
71
|
+
ADMIN_FETCH_DASHBOARD = "ADMIN_FETCH_DASHBOARD",
|
|
72
|
+
ADMIN_CREATE_CHEST_REWARD_CODE = "ADMIN_CREATE_CHEST_REWARD_CODE",
|
|
73
|
+
ADMIN_SET_NOTICE_BANNER = "ADMIN_SET_NOTICE_BANNER",
|
|
74
|
+
REDEEM_REWARD_CODE = "REDEEM_REWARD_CODE",
|
|
63
75
|
ADD_BOT = "ADD_BOT",
|
|
64
76
|
FETCH_MATCH = "FETCH_MATCH",
|
|
65
77
|
FETCH_CHAT_ROOM = "FETCH_CHAT_ROOM",
|
|
@@ -111,6 +123,35 @@ export interface ClientToServerEvents {
|
|
|
111
123
|
status?: IQueueStatus;
|
|
112
124
|
}>) => void;
|
|
113
125
|
[EClientEvent.LEAVE_QUEUE]: (callback?: IEventCallback<{}>) => void;
|
|
126
|
+
[EClientEvent.FETCH_INVENTORY]: (callback: IEventCallback<{
|
|
127
|
+
inventory: IInventoryCardGroup[];
|
|
128
|
+
equippedDeck: IEquippedDeck;
|
|
129
|
+
}>) => void;
|
|
130
|
+
[EClientEvent.SET_DECK_CARD_SKIN]: (card: ICard, cardSkinId: CardSkinId | null, callback: IEventCallback<{
|
|
131
|
+
inventory: IInventoryCardGroup[];
|
|
132
|
+
equippedDeck: IEquippedDeck;
|
|
133
|
+
}>) => void;
|
|
134
|
+
[EClientEvent.FETCH_TREASURE_STATUS]: (callback: IEventCallback<{
|
|
135
|
+
treasureStatus: ITreasureStatus;
|
|
136
|
+
}>) => void;
|
|
137
|
+
[EClientEvent.OPEN_TREASURE_CHEST]: (chestId: number, callback: IEventCallback<{
|
|
138
|
+
treasureStatus: ITreasureStatus;
|
|
139
|
+
treasureResult: ITreasureOpenResult;
|
|
140
|
+
inventory: IInventoryCardGroup[];
|
|
141
|
+
equippedDeck: IEquippedDeck;
|
|
142
|
+
}>) => void;
|
|
143
|
+
[EClientEvent.DEV_GRANT_TREASURE_CHEST]: (callback: IEventCallback<{
|
|
144
|
+
treasureStatus: ITreasureStatus;
|
|
145
|
+
}>) => void;
|
|
146
|
+
[EClientEvent.FETCH_NOTICE_BANNER]: (callback: IEventCallback<{
|
|
147
|
+
noticeBanner: IPublicNoticeBanner | null;
|
|
148
|
+
}>) => void;
|
|
149
|
+
[EClientEvent.ADMIN_FETCH_DASHBOARD]: (callback: IEventCallback<{
|
|
150
|
+
dashboard: IAdminDashboard;
|
|
151
|
+
}>) => void;
|
|
152
|
+
[EClientEvent.ADMIN_CREATE_CHEST_REWARD_CODE]: (input: IAdminCreateChestRewardCodeInput, callback: IEventCallback<IAdminCreateChestRewardCodeResult>) => void;
|
|
153
|
+
[EClientEvent.ADMIN_SET_NOTICE_BANNER]: (input: IAdminSetNoticeBannerInput, callback: IEventCallback<IAdminSetNoticeBannerResult>) => void;
|
|
154
|
+
[EClientEvent.REDEEM_REWARD_CODE]: (code: string, callback: IEventCallback<IRewardCodeRedeemResult>) => void;
|
|
114
155
|
[EClientEvent.FETCH_MATCH]: (matchSessionId: string, callback: IEventCallback<{
|
|
115
156
|
match: IPublicMatch | null;
|
|
116
157
|
}>) => void;
|
package/dist/events.js
CHANGED
|
@@ -18,6 +18,7 @@ export var EServerEvent;
|
|
|
18
18
|
EServerEvent["UPDATE_STATS"] = "UPDATE_STATS";
|
|
19
19
|
EServerEvent["QUEUE_UPDATE"] = "QUEUE_UPDATE";
|
|
20
20
|
EServerEvent["QUEUE_MATCH_FOUND"] = "QUEUE_MATCH_FOUND";
|
|
21
|
+
EServerEvent["UPDATE_NOTICE_BANNER"] = "UPDATE_NOTICE_BANNER";
|
|
21
22
|
})(EServerEvent || (EServerEvent = {}));
|
|
22
23
|
export var EClientEvent;
|
|
23
24
|
(function (EClientEvent) {
|
|
@@ -36,6 +37,16 @@ export var EClientEvent;
|
|
|
36
37
|
EClientEvent["PLAY_AGAIN"] = "PLAY_AGAIN";
|
|
37
38
|
EClientEvent["JOIN_QUEUE"] = "JOIN_QUEUE";
|
|
38
39
|
EClientEvent["LEAVE_QUEUE"] = "LEAVE_QUEUE";
|
|
40
|
+
EClientEvent["FETCH_INVENTORY"] = "FETCH_INVENTORY";
|
|
41
|
+
EClientEvent["SET_DECK_CARD_SKIN"] = "SET_DECK_CARD_SKIN";
|
|
42
|
+
EClientEvent["FETCH_TREASURE_STATUS"] = "FETCH_TREASURE_STATUS";
|
|
43
|
+
EClientEvent["OPEN_TREASURE_CHEST"] = "OPEN_TREASURE_CHEST";
|
|
44
|
+
EClientEvent["DEV_GRANT_TREASURE_CHEST"] = "DEV_GRANT_TREASURE_CHEST";
|
|
45
|
+
EClientEvent["FETCH_NOTICE_BANNER"] = "FETCH_NOTICE_BANNER";
|
|
46
|
+
EClientEvent["ADMIN_FETCH_DASHBOARD"] = "ADMIN_FETCH_DASHBOARD";
|
|
47
|
+
EClientEvent["ADMIN_CREATE_CHEST_REWARD_CODE"] = "ADMIN_CREATE_CHEST_REWARD_CODE";
|
|
48
|
+
EClientEvent["ADMIN_SET_NOTICE_BANNER"] = "ADMIN_SET_NOTICE_BANNER";
|
|
49
|
+
EClientEvent["REDEEM_REWARD_CODE"] = "REDEEM_REWARD_CODE";
|
|
39
50
|
EClientEvent["ADD_BOT"] = "ADD_BOT";
|
|
40
51
|
EClientEvent["FETCH_MATCH"] = "FETCH_MATCH";
|
|
41
52
|
EClientEvent["FETCH_CHAT_ROOM"] = "FETCH_CHAT_ROOM";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ICardSkin } from "../types";
|
|
2
|
+
export type ICardSkinRelease = {
|
|
3
|
+
release: string;
|
|
4
|
+
grantOnInventory: boolean;
|
|
5
|
+
grantSource?: string;
|
|
6
|
+
skins: ICardSkin[];
|
|
7
|
+
};
|
|
8
|
+
export declare const SKIN_RELEASES: ICardSkinRelease[];
|
|
9
|
+
export declare const CARD_SKINS: ICardSkin[];
|
|
10
|
+
export declare const INVENTORY_GRANT_RELEASES: ICardSkinRelease[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import skinReleases from "../cosmetics/skins.json";
|
|
2
|
+
const normalizeSkin = (release, skin) => (Object.assign({ enabled: true, unlockable: true, release }, skin));
|
|
3
|
+
export const SKIN_RELEASES = skinReleases.map(({ release, grantOnInventory = false, grantSource, skins }) => ({
|
|
4
|
+
release,
|
|
5
|
+
grantOnInventory,
|
|
6
|
+
grantSource,
|
|
7
|
+
skins: skins.map((skin) => normalizeSkin(release, skin)),
|
|
8
|
+
}));
|
|
9
|
+
export const CARD_SKINS = SKIN_RELEASES.flatMap(({ skins }) => skins);
|
|
10
|
+
export const INVENTORY_GRANT_RELEASES = SKIN_RELEASES.filter(({ grantOnInventory }) => grantOnInventory);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import treasureConfig from "../cosmetics/treasures.json";
|
|
2
|
+
const rarityWeights = treasureConfig.rarityWeights;
|
|
3
|
+
export const TREASURE_CONFIG = {
|
|
4
|
+
eligibleMatchesPerChest: treasureConfig.eligibleMatchesPerChest,
|
|
5
|
+
rarityWeights,
|
|
6
|
+
};
|
|
7
|
+
export const TREASURE_RARITIES = Object.keys(rarityWeights);
|
package/dist/lib/classes/Deck.js
CHANGED
|
@@ -85,10 +85,12 @@ export function shuffleArray(array, getRandom = defaultGetRandom) {
|
|
|
85
85
|
return array;
|
|
86
86
|
}
|
|
87
87
|
export function PlayedCard(player, card, burn) {
|
|
88
|
+
var _a;
|
|
88
89
|
const pc = {
|
|
89
90
|
player,
|
|
90
91
|
card,
|
|
91
92
|
key: card + player.idx,
|
|
93
|
+
cardSkinId: (_a = player.deckSkinByCard) === null || _a === void 0 ? void 0 : _a[card],
|
|
92
94
|
};
|
|
93
95
|
if (burn) {
|
|
94
96
|
pc.card = BURNT_CARD;
|
|
@@ -3,6 +3,8 @@ import { IRandom } from "../../types";
|
|
|
3
3
|
import { AxiosResponse } from "axios";
|
|
4
4
|
export declare const rng: IRng;
|
|
5
5
|
export declare const Random: () => IRandom;
|
|
6
|
+
export type RandomIndexPicker = (max: number) => number;
|
|
7
|
+
export declare const createRandomIndexPicker: (clientSeed: string) => RandomIndexPicker;
|
|
6
8
|
export interface IRng {
|
|
7
9
|
combine(client: string, server: string, bitcoinHash: string, nonce: number): string;
|
|
8
10
|
sha512(string: string): string;
|
|
@@ -42,6 +42,16 @@ export const Random = () => {
|
|
|
42
42
|
};
|
|
43
43
|
return random;
|
|
44
44
|
};
|
|
45
|
+
export const createRandomIndexPicker = (clientSeed) => {
|
|
46
|
+
const random = Random();
|
|
47
|
+
random.clients[0] = clientSeed;
|
|
48
|
+
return (max) => {
|
|
49
|
+
if (!Number.isInteger(max) || max <= 0) {
|
|
50
|
+
throw new Error("Random max must be a positive integer");
|
|
51
|
+
}
|
|
52
|
+
return random.pick(0, max);
|
|
53
|
+
};
|
|
54
|
+
};
|
|
45
55
|
function Rng() {
|
|
46
56
|
return {
|
|
47
57
|
/**
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export * from "./events";
|
|
2
2
|
export { CARDS, CARDS_HUMAN_READABLE, SUITS_HUMAN_READABLE, BURNT_CARD } from "./lib/constants";
|
|
3
|
+
export { CARD_SKINS, INVENTORY_GRANT_RELEASES, SKIN_RELEASES } from "./lib/Skins";
|
|
4
|
+
export { TREASURE_CONFIG, TREASURE_RARITIES } from "./lib/Treasures";
|
|
3
5
|
import { RequestParams, User } from "lightning-accounts";
|
|
4
6
|
import { Match, MatchPlayer, MatchHand, UserStats, MatchBet } from "@trucoshi/prisma";
|
|
5
7
|
import { IHand, IPlayInstance } from "./truco";
|
|
6
8
|
import { CARDS, ITable } from "./lib";
|
|
7
9
|
import { AxiosResponse } from "axios";
|
|
8
|
-
import { ITrucoshi } from "./server";
|
|
10
|
+
import type { ITrucoshi } from "./server";
|
|
9
11
|
import { BotProfile } from "./truco/Bot";
|
|
10
12
|
export declare enum EMatchState {
|
|
11
13
|
UNREADY = "UNREADY",
|
|
@@ -17,13 +19,13 @@ export declare enum EMatchState {
|
|
|
17
19
|
export type IPublicUserStats = Omit<UserStats, "elo">;
|
|
18
20
|
export type IPlayerRanking = Omit<IPublicUserStats, "id" | "satsBet" | "satsWon" | "satsLost"> & Pick<User, "name" | "avatarUrl">;
|
|
19
21
|
export interface IMatchDetails extends Match {
|
|
20
|
-
players: Array<Pick<MatchPlayer, "accountId" | "teamIdx" | "name" | "idx" | "bot">>;
|
|
22
|
+
players: Array<Pick<MatchPlayer, "accountId" | "teamIdx" | "name" | "idx" | "bot" | "deckSkinByCard">>;
|
|
21
23
|
hands: Array<MatchHand>;
|
|
22
24
|
}
|
|
23
25
|
export interface IAccountDetails {
|
|
24
26
|
stats: IPublicUserStats | null;
|
|
25
27
|
matches: Array<Match & {
|
|
26
|
-
players: Pick<MatchPlayer, "accountId" | "idx" | "teamIdx" | "bot" | "name">[];
|
|
28
|
+
players: Pick<MatchPlayer, "accountId" | "idx" | "teamIdx" | "bot" | "name" | "deckSkinByCard">[];
|
|
27
29
|
bet: Pick<MatchBet, "id" | "satsPerPlayer"> | null;
|
|
28
30
|
}>;
|
|
29
31
|
account: User | null;
|
|
@@ -223,6 +225,7 @@ export declare enum GAME_ERROR {
|
|
|
223
225
|
INVALID_IDENTITY = "INVALID_IDENTITY",
|
|
224
226
|
UNEXPECTED_ERROR = "UNEXPECTED_ERROR",
|
|
225
227
|
FORBIDDEN = "FORBIDDEN",
|
|
228
|
+
INVALID_INPUT = "INVALID_INPUT",
|
|
226
229
|
NOT_FOUND = "NOT_FOUND",
|
|
227
230
|
MATCH_ALREADY_STARTED = "MATCH_ALREADY_STARTED",
|
|
228
231
|
LOBBY_IS_FULL = "LOBBY_IS_FULL",
|
|
@@ -236,7 +239,9 @@ export declare enum GAME_ERROR {
|
|
|
236
239
|
NO_FLOR = "NO_FLOR",
|
|
237
240
|
INVALID_SESSION = "INVALID_SESSION",
|
|
238
241
|
PAYMENT_REQUIRED = "PAYMENT_REQUIRED",
|
|
239
|
-
PAYMENT_ERROR = "PAYMENT_ERROR"
|
|
242
|
+
PAYMENT_ERROR = "PAYMENT_ERROR",
|
|
243
|
+
REWARD_CODE_INVALID = "REWARD_CODE_INVALID",
|
|
244
|
+
REWARD_CODE_REDEEMED = "REWARD_CODE_REDEEMED"
|
|
240
245
|
}
|
|
241
246
|
export interface EnvidoState {
|
|
242
247
|
accept: number;
|
|
@@ -268,10 +273,117 @@ export interface IDeck {
|
|
|
268
273
|
shuffle(dealerIdx: number): IDeck;
|
|
269
274
|
}
|
|
270
275
|
export type ICard = keyof typeof CARDS;
|
|
276
|
+
export type CardSkinId = string;
|
|
277
|
+
export type CardSkinRarity = "COMMON" | "RARE" | "EPIC" | "LEGENDARY" | "PROMO";
|
|
278
|
+
export interface ICardSkin {
|
|
279
|
+
id: CardSkinId;
|
|
280
|
+
release: string;
|
|
281
|
+
card: ICard;
|
|
282
|
+
description?: string | null;
|
|
283
|
+
fileName: string;
|
|
284
|
+
assetPath: string;
|
|
285
|
+
rarity: CardSkinRarity;
|
|
286
|
+
enabled: boolean;
|
|
287
|
+
unlockable: boolean;
|
|
288
|
+
}
|
|
289
|
+
export type IEquippedDeck = Partial<Record<ICard, CardSkinId>>;
|
|
290
|
+
export interface IInventoryCardSkin extends ICardSkin {
|
|
291
|
+
unlocked: boolean;
|
|
292
|
+
equipped: boolean;
|
|
293
|
+
}
|
|
294
|
+
export interface IInventoryCardGroup {
|
|
295
|
+
card: ICard;
|
|
296
|
+
skins: IInventoryCardSkin[];
|
|
297
|
+
equippedCardSkinId?: CardSkinId;
|
|
298
|
+
}
|
|
299
|
+
export interface ITreasureConfig {
|
|
300
|
+
eligibleMatchesPerChest: number;
|
|
301
|
+
rarityWeights: Record<CardSkinRarity, number>;
|
|
302
|
+
}
|
|
303
|
+
export interface ITreasureChest {
|
|
304
|
+
id: number;
|
|
305
|
+
sourceMatchId?: number | null;
|
|
306
|
+
earnedAt: string;
|
|
307
|
+
}
|
|
308
|
+
export interface ITreasureStatus {
|
|
309
|
+
progress: number;
|
|
310
|
+
threshold: number;
|
|
311
|
+
unopenedChests: ITreasureChest[];
|
|
312
|
+
}
|
|
313
|
+
export interface ITreasureOpenResult {
|
|
314
|
+
chestId: number;
|
|
315
|
+
rarity: CardSkinRarity | null;
|
|
316
|
+
cardSkin: ICardSkin | null;
|
|
317
|
+
duplicate: boolean;
|
|
318
|
+
granted: boolean;
|
|
319
|
+
}
|
|
320
|
+
export interface IAdminOnlineAccount {
|
|
321
|
+
accountId: number;
|
|
322
|
+
name: string;
|
|
323
|
+
avatarUrl?: string | null;
|
|
324
|
+
role?: User["role"];
|
|
325
|
+
online: boolean;
|
|
326
|
+
}
|
|
327
|
+
export interface IAdminRewardCodeSummary {
|
|
328
|
+
id: number;
|
|
329
|
+
codePreview: string;
|
|
330
|
+
createdByAccountId: number;
|
|
331
|
+
intendedAccountId?: number | null;
|
|
332
|
+
note?: string | null;
|
|
333
|
+
createdAt: string;
|
|
334
|
+
redeemedAt?: string | null;
|
|
335
|
+
redeemedByAccountId?: number | null;
|
|
336
|
+
treasureChestId?: number | null;
|
|
337
|
+
}
|
|
338
|
+
export type NoticeBannerSeverity = "info" | "warning" | "error" | "success";
|
|
339
|
+
export interface IPublicNoticeBanner {
|
|
340
|
+
id: number;
|
|
341
|
+
text: string;
|
|
342
|
+
severity: NoticeBannerSeverity;
|
|
343
|
+
buttonText?: string | null;
|
|
344
|
+
buttonHref?: string | null;
|
|
345
|
+
updatedAt: string;
|
|
346
|
+
}
|
|
347
|
+
export interface IAdminNoticeBanner extends IPublicNoticeBanner {
|
|
348
|
+
active: boolean;
|
|
349
|
+
updatedByAccountId: number;
|
|
350
|
+
createdAt: string;
|
|
351
|
+
}
|
|
352
|
+
export interface IAdminDashboard {
|
|
353
|
+
onlineAccounts: IAdminOnlineAccount[];
|
|
354
|
+
liveGames: IPublicMatchInfo[];
|
|
355
|
+
rewardCodes: IAdminRewardCodeSummary[];
|
|
356
|
+
noticeBanner: IAdminNoticeBanner | null;
|
|
357
|
+
}
|
|
358
|
+
export interface IAdminCreateChestRewardCodeInput {
|
|
359
|
+
intendedAccountId?: number | null;
|
|
360
|
+
note?: string | null;
|
|
361
|
+
}
|
|
362
|
+
export interface IAdminCreateChestRewardCodeResult {
|
|
363
|
+
code: string;
|
|
364
|
+
link: string;
|
|
365
|
+
rewardCode: IAdminRewardCodeSummary;
|
|
366
|
+
}
|
|
367
|
+
export interface IAdminSetNoticeBannerInput {
|
|
368
|
+
active: boolean;
|
|
369
|
+
text?: string | null;
|
|
370
|
+
severity?: NoticeBannerSeverity | null;
|
|
371
|
+
buttonText?: string | null;
|
|
372
|
+
buttonHref?: string | null;
|
|
373
|
+
}
|
|
374
|
+
export interface IAdminSetNoticeBannerResult {
|
|
375
|
+
noticeBanner: IAdminNoticeBanner | null;
|
|
376
|
+
publicNoticeBanner: IPublicNoticeBanner | null;
|
|
377
|
+
}
|
|
378
|
+
export interface IRewardCodeRedeemResult {
|
|
379
|
+
grantedChest: ITreasureChest;
|
|
380
|
+
treasureStatus: ITreasureStatus;
|
|
381
|
+
}
|
|
271
382
|
export interface IPlayedCard {
|
|
272
383
|
key: string;
|
|
273
384
|
player: IPlayer | IPublicPlayer;
|
|
274
385
|
card: ICard;
|
|
386
|
+
cardSkinId?: CardSkinId;
|
|
275
387
|
}
|
|
276
388
|
export interface IHandPoints {
|
|
277
389
|
0: number;
|
|
@@ -296,7 +408,7 @@ export interface IRandom {
|
|
|
296
408
|
bitcoinHeight: number;
|
|
297
409
|
};
|
|
298
410
|
}
|
|
299
|
-
export type IPublicPlayer = Pick<IPlayer, "idx" | "key" | "name" | "bot" | "abandonedTime" | "accountId" | "avatarUrl" | "disabled" | "abandoned" | "ready" | "hand" | "usedHand" | "prevHand" | "teamIdx" | "isTurn" | "turnExpiresAt" | "turnExtensionExpiresAt" | "isEnvidoTurn" | "isOwner" | "hasSaidFlor" | "hasSaidEnvidoPoints" | "hasSaidTruco" | "hasPassed"> & ({
|
|
411
|
+
export type IPublicPlayer = Pick<IPlayer, "idx" | "key" | "name" | "bot" | "abandonedTime" | "accountId" | "avatarUrl" | "disabled" | "abandoned" | "ready" | "hand" | "usedHand" | "prevHand" | "teamIdx" | "isTurn" | "turnExpiresAt" | "turnExtensionExpiresAt" | "isEnvidoTurn" | "isOwner" | "hasSaidFlor" | "hasSaidEnvidoPoints" | "hasSaidTruco" | "hasPassed" | "deckSkinByCard"> & ({
|
|
300
412
|
isMe?: true;
|
|
301
413
|
commands: IPlayer["commands"];
|
|
302
414
|
hasFlor: IPlayer["hasFlor"];
|
|
@@ -361,6 +473,9 @@ export interface IPlayer {
|
|
|
361
473
|
abandoned: boolean;
|
|
362
474
|
ready: boolean;
|
|
363
475
|
opponentProfiles: Record<string, OpponentProfile>;
|
|
476
|
+
deckSkinByCard: IEquippedDeck;
|
|
477
|
+
getCardSkinId(card: ICard): CardSkinId | undefined;
|
|
478
|
+
setDeckSkinByCard(deck: IEquippedDeck): void;
|
|
364
479
|
getRandomCard(): [number, ICard];
|
|
365
480
|
getHighestCard(): [number, ICard];
|
|
366
481
|
getLowestCard(): [number, ICard];
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export * from "./events";
|
|
2
2
|
export { CARDS, CARDS_HUMAN_READABLE, SUITS_HUMAN_READABLE, BURNT_CARD } from "./lib/constants";
|
|
3
|
+
export { CARD_SKINS, INVENTORY_GRANT_RELEASES, SKIN_RELEASES } from "./lib/Skins";
|
|
4
|
+
export { TREASURE_CONFIG, TREASURE_RARITIES } from "./lib/Treasures";
|
|
3
5
|
export var EMatchState;
|
|
4
6
|
(function (EMatchState) {
|
|
5
7
|
EMatchState["UNREADY"] = "UNREADY";
|
|
@@ -63,6 +65,7 @@ export var GAME_ERROR;
|
|
|
63
65
|
GAME_ERROR["INVALID_IDENTITY"] = "INVALID_IDENTITY";
|
|
64
66
|
GAME_ERROR["UNEXPECTED_ERROR"] = "UNEXPECTED_ERROR";
|
|
65
67
|
GAME_ERROR["FORBIDDEN"] = "FORBIDDEN";
|
|
68
|
+
GAME_ERROR["INVALID_INPUT"] = "INVALID_INPUT";
|
|
66
69
|
GAME_ERROR["NOT_FOUND"] = "NOT_FOUND";
|
|
67
70
|
GAME_ERROR["MATCH_ALREADY_STARTED"] = "MATCH_ALREADY_STARTED";
|
|
68
71
|
GAME_ERROR["LOBBY_IS_FULL"] = "LOBBY_IS_FULL";
|
|
@@ -77,6 +80,8 @@ export var GAME_ERROR;
|
|
|
77
80
|
GAME_ERROR["INVALID_SESSION"] = "INVALID_SESSION";
|
|
78
81
|
GAME_ERROR["PAYMENT_REQUIRED"] = "PAYMENT_REQUIRED";
|
|
79
82
|
GAME_ERROR["PAYMENT_ERROR"] = "PAYMENT_ERROR";
|
|
83
|
+
GAME_ERROR["REWARD_CODE_INVALID"] = "REWARD_CODE_INVALID";
|
|
84
|
+
GAME_ERROR["REWARD_CODE_REDEEMED"] = "REWARD_CODE_REDEEMED";
|
|
80
85
|
})(GAME_ERROR || (GAME_ERROR = {}));
|
|
81
86
|
export const DANGEROUS_COMMANDS = [
|
|
82
87
|
ESayCommand.MAZO,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trucoshi",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0-rc.1",
|
|
4
4
|
"description": "Truco Game Server",
|
|
5
5
|
"main": "dist/types.js",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
"dev:all:sudo-docker": "DEV_ALL_DOCKER_SUDO=1 NODE_ENV=development ./init-dev-all.sh",
|
|
30
30
|
"docker:exec": "docker compose exec -i server yarn",
|
|
31
31
|
"docker:prod": "NODE_ENV=production && docker compose -f docker-compose.prod.yml down && docker compose -f docker-compose.prod.yml --env-file .env up --build",
|
|
32
|
+
"docker:prod:migrate": "docker compose -f docker-compose.prod.yml --env-file .env run --rm --no-deps server yarn prisma migrate deploy",
|
|
32
33
|
"docker:staging": "docker compose -f docker-compose.staging.yml down && docker compose -f docker-compose.staging.yml --env-file .env up --build",
|
|
34
|
+
"docker:staging:migrate": "docker compose -f docker-compose.staging.yml --env-file .env run --rm --no-deps server yarn prisma migrate deploy",
|
|
33
35
|
"docker:e2e": "NODE_ENV=test ./init-e2e.sh",
|
|
34
36
|
"test": "dotenv -e .env.test -- ts-mocha --exit ./test/lib/**/*.ts -t 50000",
|
|
35
37
|
"test:e2e": "prisma db push --force-reset && dotenv -e .env.test -- ts-mocha --reporter spec --verbose --exit ./test/**/*.ts -t 60000",
|
|
@@ -81,7 +83,7 @@
|
|
|
81
83
|
"dotenv-cli": "^7.3.0",
|
|
82
84
|
"form-data": "^4.0.5",
|
|
83
85
|
"jsonwebtoken": "^9.0.3",
|
|
84
|
-
"lightning-accounts": "6.
|
|
86
|
+
"lightning-accounts": "6.3.0",
|
|
85
87
|
"lodash.debounce": "^4.0.8",
|
|
86
88
|
"lodash.memoize": "^4.1.2",
|
|
87
89
|
"lodash.partialright": "^4.2.1",
|
|
@@ -95,7 +97,6 @@
|
|
|
95
97
|
"sanitize-html": "^2.17.1",
|
|
96
98
|
"socket.io": "4.8.3",
|
|
97
99
|
"ts-node": "^10.9.1",
|
|
98
|
-
"tsc": "^2.0.4",
|
|
99
100
|
"typescript": "^5.9.3"
|
|
100
101
|
}
|
|
101
102
|
}
|