-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmatchService.ts
More file actions
114 lines (100 loc) · 2.75 KB
/
matchService.ts
File metadata and controls
114 lines (100 loc) · 2.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import type { EndReason } from '../game/types'
export interface MatchPrepareOptions {
roomCode: string
creatorWallet: string
opponentWallet: string
stakeSol: number
startTimeMs: number
}
export interface MatchFinalizeOptions {
roomCode: string
creatorWallet: string
winnerWallet: string
reason: EndReason
}
function normalizeRoomCode(roomCode: string) {
return roomCode.trim().toUpperCase()
}
function mapReason(reason: EndReason) {
if (reason === 'disconnect') return 'disconnect'
if (reason === 'timeout') return 'timeout'
return 'hp'
}
async function parseResponse<T>(response: Response): Promise<T> {
const rawBody = await response.text()
const contentType = response.headers.get('content-type') ?? ''
let payload: { error?: string } | null = null
if (rawBody && contentType.includes('application/json')) {
try {
payload = JSON.parse(rawBody) as { error?: string }
} catch {
payload = null
}
}
if (!response.ok) {
const plainText = rawBody
.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ')
.trim()
throw new Error(
payload?.error ??
(plainText ||
`Match service request failed with status ${response.status}.`)
)
}
if (payload) {
return payload as T
}
if (!rawBody) {
throw new Error('Match service returned an empty response.')
}
try {
return JSON.parse(rawBody) as T
} catch {
throw new Error('Match service returned a non-JSON success payload.')
}
}
export async function prepareMatchState(options: MatchPrepareOptions) {
const response = await fetch('/api/match/prepare', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
roomCode: normalizeRoomCode(options.roomCode),
creatorWallet: options.creatorWallet,
opponentWallet: options.opponentWallet,
stakeSol: options.stakeSol,
startTimeMs: options.startTimeMs,
}),
})
return parseResponse<{
roomCode: string
matchStateAddress: string
initializeSignature: string | null
armSignature: string | null
stage: number | null
}>(response)
}
export async function finalizeMatch(options: MatchFinalizeOptions) {
const response = await fetch('/api/match/finalize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
roomCode: normalizeRoomCode(options.roomCode),
creatorWallet: options.creatorWallet,
winnerWallet: options.winnerWallet,
reason: mapReason(options.reason),
}),
})
return parseResponse<{
roomCode: string
matchStateAddress: string
escrowAddress: string
finishSignature: string | null
settleSignature: string | null
alreadySettled: boolean
}>(response)
}