Server-authoritative economy: wire client to server; entry + rewards on hub
Server: - daily (/api/daily, /api/daily/claim) + shop (/api/shop/buy) + ChargeEntry - GameRoom (via IServiceScopeFactory) deducts ranked entry at match start and applies match rewards at match-over, broadcasting profile + reward over the hub - tested: daily, shop (owned-guard), ranked entry deduction pushed over hub Client: - SignalrService routes profile/coins/plan/daily/shop/match to the server (Bearer); onProfile/onReward hub events; guest/offline fall back to local - session-store syncs profile from hub; game-store serverReward; GameScreen shows live ranked reward from hub (no double submit), submits client-run games - single source of truth in live mode (no economy divergence) Postgres-ready via config (Provider=postgres); EnsureCreated for now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// Verify server deducts the ranked entry at match start (server must be running).
|
||||
import { HubConnectionBuilder, LogLevel } from "@microsoft/signalr";
|
||||
|
||||
const S = "http://localhost:5005";
|
||||
const auth = await (await fetch(`${S}/api/auth/otp/verify`, {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ phone: "0977", code: "1234", name: "Entry" }),
|
||||
})).json();
|
||||
const H = { Authorization: `Bearer ${auth.token}` };
|
||||
|
||||
const before = await (await fetch(`${S}/api/profile`, { headers: H })).json();
|
||||
|
||||
const conn = new HubConnectionBuilder()
|
||||
.withUrl(`${S}/hub/game`, { accessTokenFactory: () => auth.token })
|
||||
.configureLogging(LogLevel.Error).build();
|
||||
let gotProfile = null, gotReward = false;
|
||||
conn.on("profile", (p) => { gotProfile = p; });
|
||||
conn.on("reward", () => { gotReward = true; });
|
||||
await conn.start();
|
||||
await conn.invoke("StartMatchmaking", { name: "Entry", avatar: "a-fox", level: 1, plan: "pro" });
|
||||
await new Promise((r) => setTimeout(r, 4000));
|
||||
|
||||
const after = await (await fetch(`${S}/api/profile`, { headers: H })).json();
|
||||
console.log(JSON.stringify({
|
||||
coinsBefore: before.coins,
|
||||
coinsAfter: after.coins,
|
||||
entryDeducted: before.coins - after.coins,
|
||||
pushedProfileCoins: gotProfile?.coins ?? null,
|
||||
}));
|
||||
await conn.stop();
|
||||
process.exit(0);
|
||||
Reference in New Issue
Block a user