trucoshi 11.0.1 → 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 +127 -7
- package/dist/types.js +5 -0
- package/package.json +6 -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;
|
|
@@ -45,15 +47,16 @@ export interface ILobbyOptions {
|
|
|
45
47
|
satsPerPlayer: number;
|
|
46
48
|
}
|
|
47
49
|
export interface IJoinQueueOptions {
|
|
48
|
-
maxPlayers: 2 | 4 | 6;
|
|
50
|
+
maxPlayers: 0 | 2 | 4 | 6;
|
|
49
51
|
allowBots: boolean;
|
|
50
52
|
}
|
|
51
53
|
export interface IQueueStatus {
|
|
52
54
|
requestId: string;
|
|
53
|
-
maxPlayers: 2 | 4 | 6;
|
|
55
|
+
maxPlayers: 0 | 2 | 4 | 6;
|
|
54
56
|
queuedPlayers: number;
|
|
55
57
|
requiredPlayers: number;
|
|
56
58
|
position: number;
|
|
59
|
+
queuedAt: number;
|
|
57
60
|
botFallbackAt?: number;
|
|
58
61
|
}
|
|
59
62
|
export interface IQueueMatchFound {
|
|
@@ -101,6 +104,8 @@ export interface IMatchPreviousHand {
|
|
|
101
104
|
export interface IPublicMatch {
|
|
102
105
|
id?: number;
|
|
103
106
|
options: ILobbyOptions;
|
|
107
|
+
createdFromQueue: boolean;
|
|
108
|
+
queueOptions?: IJoinQueueOptions;
|
|
104
109
|
busy: boolean;
|
|
105
110
|
state: EMatchState;
|
|
106
111
|
handState: EHandState | null;
|
|
@@ -129,6 +134,8 @@ export interface IPublicMatchInfo {
|
|
|
129
134
|
options: ILobbyOptions;
|
|
130
135
|
state: EMatchState;
|
|
131
136
|
winnerTeamIdx: 0 | 1 | undefined;
|
|
137
|
+
createdFromQueue: boolean;
|
|
138
|
+
queueOptions?: IJoinQueueOptions;
|
|
132
139
|
}
|
|
133
140
|
export type IPublicChatRoom = Pick<IChatRoom, "id" | "messages">;
|
|
134
141
|
export interface IChatMessage {
|
|
@@ -218,6 +225,7 @@ export declare enum GAME_ERROR {
|
|
|
218
225
|
INVALID_IDENTITY = "INVALID_IDENTITY",
|
|
219
226
|
UNEXPECTED_ERROR = "UNEXPECTED_ERROR",
|
|
220
227
|
FORBIDDEN = "FORBIDDEN",
|
|
228
|
+
INVALID_INPUT = "INVALID_INPUT",
|
|
221
229
|
NOT_FOUND = "NOT_FOUND",
|
|
222
230
|
MATCH_ALREADY_STARTED = "MATCH_ALREADY_STARTED",
|
|
223
231
|
LOBBY_IS_FULL = "LOBBY_IS_FULL",
|
|
@@ -231,7 +239,9 @@ export declare enum GAME_ERROR {
|
|
|
231
239
|
NO_FLOR = "NO_FLOR",
|
|
232
240
|
INVALID_SESSION = "INVALID_SESSION",
|
|
233
241
|
PAYMENT_REQUIRED = "PAYMENT_REQUIRED",
|
|
234
|
-
PAYMENT_ERROR = "PAYMENT_ERROR"
|
|
242
|
+
PAYMENT_ERROR = "PAYMENT_ERROR",
|
|
243
|
+
REWARD_CODE_INVALID = "REWARD_CODE_INVALID",
|
|
244
|
+
REWARD_CODE_REDEEMED = "REWARD_CODE_REDEEMED"
|
|
235
245
|
}
|
|
236
246
|
export interface EnvidoState {
|
|
237
247
|
accept: number;
|
|
@@ -263,10 +273,117 @@ export interface IDeck {
|
|
|
263
273
|
shuffle(dealerIdx: number): IDeck;
|
|
264
274
|
}
|
|
265
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
|
+
}
|
|
266
382
|
export interface IPlayedCard {
|
|
267
383
|
key: string;
|
|
268
384
|
player: IPlayer | IPublicPlayer;
|
|
269
385
|
card: ICard;
|
|
386
|
+
cardSkinId?: CardSkinId;
|
|
270
387
|
}
|
|
271
388
|
export interface IHandPoints {
|
|
272
389
|
0: number;
|
|
@@ -291,7 +408,7 @@ export interface IRandom {
|
|
|
291
408
|
bitcoinHeight: number;
|
|
292
409
|
};
|
|
293
410
|
}
|
|
294
|
-
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"> & ({
|
|
295
412
|
isMe?: true;
|
|
296
413
|
commands: IPlayer["commands"];
|
|
297
414
|
hasFlor: IPlayer["hasFlor"];
|
|
@@ -356,6 +473,9 @@ export interface IPlayer {
|
|
|
356
473
|
abandoned: boolean;
|
|
357
474
|
ready: boolean;
|
|
358
475
|
opponentProfiles: Record<string, OpponentProfile>;
|
|
476
|
+
deckSkinByCard: IEquippedDeck;
|
|
477
|
+
getCardSkinId(card: ICard): CardSkinId | undefined;
|
|
478
|
+
setDeckSkinByCard(deck: IEquippedDeck): void;
|
|
359
479
|
getRandomCard(): [number, ICard];
|
|
360
480
|
getHighestCard(): [number, ICard];
|
|
361
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",
|
|
@@ -25,9 +25,13 @@
|
|
|
25
25
|
"start:prod": "node bin/trucoshi-server",
|
|
26
26
|
"start:prod:migrate": "prisma migrate deploy && yarn start:prod",
|
|
27
27
|
"docker": "NODE_ENV=development ./init-dev.sh",
|
|
28
|
+
"dev:all": "NODE_ENV=development ./init-dev-all.sh",
|
|
29
|
+
"dev:all:sudo-docker": "DEV_ALL_DOCKER_SUDO=1 NODE_ENV=development ./init-dev-all.sh",
|
|
28
30
|
"docker:exec": "docker compose exec -i server yarn",
|
|
29
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",
|
|
30
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",
|
|
31
35
|
"docker:e2e": "NODE_ENV=test ./init-e2e.sh",
|
|
32
36
|
"test": "dotenv -e .env.test -- ts-mocha --exit ./test/lib/**/*.ts -t 50000",
|
|
33
37
|
"test:e2e": "prisma db push --force-reset && dotenv -e .env.test -- ts-mocha --reporter spec --verbose --exit ./test/**/*.ts -t 60000",
|
|
@@ -79,7 +83,7 @@
|
|
|
79
83
|
"dotenv-cli": "^7.3.0",
|
|
80
84
|
"form-data": "^4.0.5",
|
|
81
85
|
"jsonwebtoken": "^9.0.3",
|
|
82
|
-
"lightning-accounts": "6.
|
|
86
|
+
"lightning-accounts": "6.3.0",
|
|
83
87
|
"lodash.debounce": "^4.0.8",
|
|
84
88
|
"lodash.memoize": "^4.1.2",
|
|
85
89
|
"lodash.partialright": "^4.2.1",
|
|
@@ -93,7 +97,6 @@
|
|
|
93
97
|
"sanitize-html": "^2.17.1",
|
|
94
98
|
"socket.io": "4.8.3",
|
|
95
99
|
"ts-node": "^10.9.1",
|
|
96
|
-
"tsc": "^2.0.4",
|
|
97
100
|
"typescript": "^5.9.3"
|
|
98
101
|
}
|
|
99
102
|
}
|