diff --git a/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/player-store.ts b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/player-store.ts new file mode 100644 index 0000000..fa92dc8 --- /dev/null +++ b/cocoscreator_projects/YouleNexus/assets/framework/platform/stores/player-store.ts @@ -0,0 +1,38 @@ +import { signal, type ReadonlyReactive } from '../../core/reactive.ts'; +import type { PlayerState } from './types.ts'; +import type { ReadonlyPlayerStore } from '../readonly.ts'; +import type { LoginResponseData } from '../../core/types/login.ts'; + +const INITIAL: PlayerState = { + playerid: 0, nickname: '', avatar: '', sex: 0, + bean: 0, roomcard: 0, score: 0, charm: 0, + taskstate: 0, advanced: 0, + bankpower: 0, bank: 0, bankpwd: 0, + ip: '', sign: null, tel: null, + invitecode: null, initCard: 0, initBean: 0, +}; + +/** 玩家自身状态(= 旧 C_Player)。 */ +export class PlayerStore implements ReadonlyPlayerStore { + private s = signal({ ...INITIAL }); + get state(): ReadonlyReactive { return this.s; } + + /** 从 login 响应 A 组填充。字段名对齐 protocol/04(直接取服务器下发值)。 */ + applyLogin(raw: LoginResponseData): void { + const r = raw as any; + this.s.value = { + ...this.s.value, + playerid: r.playerid ?? 0, + nickname: r.nickname ?? '', avatar: r.avatar ?? '', sex: r.sex ?? 0, + bean: r.bean ?? 0, roomcard: r.roomcard ?? 0, score: r.score ?? 0, charm: r.charm ?? 0, + taskstate: r.taskstate ?? 0, advanced: r.advanced ?? 0, + bankpower: r.bankpower ?? 0, bank: r.bank ?? 0, bankpwd: r.bankpwd ?? 0, + ip: r.ip ?? '', sign: r.sign ?? null, tel: r.tel ?? null, + invitecode: r.invitecode ?? null, initCard: r.initCard ?? 0, initBean: r.initBean ?? 0, + ...(r.openid != null ? { openid: String(r.openid) } : {}), + ...(r.unionid != null ? { unionid: r.unionid } : {}), + ...(r.province != null ? { province: String(r.province) } : {}), + ...(r.city != null ? { city: String(r.city) } : {}), + }; + } +} diff --git a/cocoscreator_projects/framework-tests/platform/player-store.test.ts b/cocoscreator_projects/framework-tests/platform/player-store.test.ts new file mode 100644 index 0000000..be7368a --- /dev/null +++ b/cocoscreator_projects/framework-tests/platform/player-store.test.ts @@ -0,0 +1,36 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { PlayerStore } from '../../YouleNexus/assets/framework/platform/stores/player-store.ts'; + +const RAW = { state: 0, playerid: 430511, nickname: '测试号', avatar: 'http://a', sex: 0, + bean: 0, roomcard: 3, score: 0, taskstate: 1, advanced: 0, bankpower: 1, bank: 0, bankpwd: 0, + ip: '127.0.0.1', sign: null, tel: null, invitecode: null, initCard: '3', initBean: '0', + openid: 'o1', unionid: 'u1', province: 'p', city: 'c' }; + +test('applyLogin 填充 PlayerState 并通知订阅者', () => { + const store = new PlayerStore(); + let notified = 0; + store.state.subscribe(() => { notified++; }); + store.applyLogin(RAW as any); + const v = store.state.value; + assert.equal(v.playerid, 430511); + assert.equal(v.nickname, '测试号'); + assert.equal(v.roomcard, 3); + assert.equal(v.taskstate, 1); + assert.equal(v.openid, 'o1'); + assert.equal(v.initCard, '3'); + assert.equal(notified, 1); +}); + +test('charm 等缺省字段补默认 0', () => { + const store = new PlayerStore(); + store.applyLogin(RAW as any); + assert.equal(store.state.value.charm, 0); +}); + +test('未下发的可选字段(openid 等)缺省时不写入', () => { + const store = new PlayerStore(); + store.applyLogin({ state: 0, playerid: 1 } as any); + assert.equal(store.state.value.openid, undefined); + assert.equal(store.state.value.playerid, 1); +});