| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- import { Level, Ranks, SeasonInfo, transUserDataform, transUserExtraDataform, UserRank } from "./NetBase";
- import { NetRoom } from "./NetRoom";
- import { NetTeam } from "./NetTeam";
- import { TTWsClient } from "./TTWsClient";
- import { WsClient } from "./WsClient";
- import { WXWsClient } from "./WXWsClient";
- /**自定义同步游戏变量(只能以r_或p_开头,按帧率同步,重新进入会收到最新的数据)
- * 只有第一个参数有效,可加上第一个参数跟第一个参数同类型,用于接收改变前的数据
- * t_开头为队伍事件
- * r_开头 主机游戏数据 主机权限
- * revt_ 开头房间事件
- * p_ 开头玩家数据 玩家权限和主机ai权限
- * online 玩家在线下线状态
- * 尽量拆分数据类型,不要设置过于复杂的数据类型*/
- type protocol = { extra?: Record<string, any> } & chsdk.OmitIndex<chsdk.EventsMap>
- export interface game_protocol extends protocol {
- t_entry(id: string, location: number, nickName: string): void;//队伍有人进入
- t_exit(id: string, location: number, nickName: string): void;//队伍有人退出
- t_host(id: string, location: number, nickName: string): void;//队长切换
- t_chat(id: string, msg: string, location: number, nickName: string): void;//队伍聊天
- t_online(id: string, online: boolean, location: number, nickName: string): void;//队伍玩家在线情况变化
- t_ready(id: string, online: boolean, location: number, nickName: string): void;//队伍玩家准备情况变化
- t_matchStart(): void;//匹配开始
- t_matchCancel(): void;//匹配取消
- t_matchSuccess(): void;//匹配成功
- t_no_ready(): void;//有玩家未准备
- t_closed(): void;//队伍解散(一般用于被踢出事件)
- //
- r_obj(key: string, data: any, old: any): void;
- r_closed(): void;//房间关闭 关闭后 获取 results 结算信息
- r_host(id: string, location: number, nickName: string): void;//房主切换
- r_chat(id: string, msg: string, location: number, nickName: string): void;//房间聊天
- r_online(id: string, online: boolean, location: number, nickName: string): void;//房间玩家在线情况变化
- r_finish(id: string, rank: number): void//房间某个玩家完成游戏获得名次
- r_exit(id: string, location: number, nickName: string): void;//房间某个玩家离开
- r_state(state: number, old_state?: number): void;//房间游戏状态
- //
- p_obj(key: string, data: any, old: any): void;
- p_state(state: number, old_state?: number): void;//玩家游戏状态
- p_extra(): void;//扩展数据改变
- exit(): void;//玩家离开房间
- finish(rank: number): void;//玩家完成游戏获得名次
- online(online: boolean): void;//玩家断线重连消息
- extra: Record<string, any>;
- }
- /**网络状态*/
- interface ws_event_protocol {
- Connecting(): void;//连接中
- Reconnecting(reconnectCount: number): void;//重新连接中
- Connected(): void;//进入大厅连接成功
- Reconnected(): void;//重连成功
- Error(err: any | string): void;//错误消息
- Closing(): void;//关闭连接中
- Closed(event: string): void;//连接已关闭
- NextSeason(): void;//进入下一个赛季
- }
- interface options {
- /**(可选)query连接token等数据*/query?: string,
- /**(可选) CA 证书字符串,用于 SSL 连接的验证*/ca?: string,
- /**(可选) 指示是否启用重连机制,默认为 `true`*/ reconnection?: boolean,
- /**(可选) 在连接失败后重连的最大次数,默认值为 `10`*/ reconnectCount?: number,
- /**(可选) 每次重连之间的间隔时间,以毫秒为单位,默认值为 `2000`*/ reconnectInterval?: number,
- /**(可选) 心跳断线时间,以毫秒为单位,默认值为 `30000`*/ timeoutInterval?: number,
- /**(可选) 游戏事件同步间隔,默认值为 `66` (0.06秒 1秒钟15次)*/ gameEvtInterval?: number,
- /**(可选) 游戏变量同步间隔,默认值为 `200` (0.2秒 1秒钟5次)*/ gameDataInterval?: number,
- /**(可选) 是否用json序列化否则用messagepck 默认为 `false`*/ json?: boolean
- }
- function isArrayBuffer(it: unknown): it is ArrayBuffer {
- try {
- return it instanceof ArrayBuffer
- } catch (_) {
- return false
- }
- }
- type JsonPrimitive = string | number | boolean | null;
- type Serializable<T> = T extends JsonPrimitive ? T : T extends Date | Function | Symbol | undefined ? never :
- T extends Array<infer U> ? SerializableArray<U> : T extends object ? SerializableObject<T> : never;
- type SerializableArray<T> = Array<Serializable<T>>;
- type SerializableObject<T extends object> = { [K in keyof T]: Serializable<T[K]>; };
- //
- const HEARTBEAT_INTERVAL = 1000; //每秒发送一次 Ping
- const HEARTBEAT_TIMEOUT = 80000; //80 秒的断线超时
- const DIRTYDATACHECK_INTERVAL = 200;//每200毫秒同步一次自定义游戏变量
- const EVTCHECK_INTERVAL = 66;//每66毫秒同步一次自定义游戏事件
- const RECONNECT_COUNT = 10;//默认重连次数
- const RECONNECT_INTERVAL = 2000;//默认重连间隔
- const PING = 'ping';
- const PONG = 'pong';
- //
- const S2C_LOGIN_SUCCESS = 's2c_login_success';
- const S2C_TEAM_SUCCESS = 's2c_team_success';
- const S2C_JOIN_TEAM_FAIL = 's2c_join_team_fail';
- const S2C_TEAM_USER_JOIN = 's2c_team_user_join';
- const S2C_TEAM_USER_EXIT = 's2c_team_user_exit';
- const S2C_TEAM_CHANGE_HOST = 's2c_team_change_host';
- const S2C_USER_RECONNECT = 's2c_user_reconnect';
- const S2C_USER_DISCONNECT = 's2c_user_disconnect';
- const S2C_MATCH = 's2c_match';
- const S2C_MATCH_SUCCESS = 's2c_match_success';
- const S2C_MATCH_CANCEL = 's2c_match_cancel';
- const S2C_ROOM_GAMEDATA_CHANGE = 's2c_room_gameData_change';
- const S2C_USER_GAMEDATA_CHANGE = 's2c_user_gameData_change';
- const S2C_ROOM_CHANGE_HOST = 's2c_room_change_host';
- const S2C_ROOM_CLOSED = 's2c_room_closed';
- const S2C_TALK = 's2c_talk';
- const S2C_EXIT_ROOM = 's2c_exit_room';
- const S2C_FINISH = 's2c_finish';
- const S2C_READY = 's2c_ready';
- const S2C_NOT_READY = 's2c_not_ready';
- const S2C_RECEIVE_REWARD = 's2c_receive_reward';
- const S2C_SEASON = 's2c_season';
- const S2C_SET_USER_DATA_EXTRA = 's2c_set_user_data_extra';
- const S2C_RANK_DATA = 's2c_rank_data';
- //
- const C2S_MESSAGE = 'c2s_message';
- const C2S_SET_TEAM = 'c2s_set_team';
- const C2S_JOIN_TEAM = 'c2s_join_team';
- const C2S_EXIT_TEAM = 'c2s_exit_team';
- const C2S_CLOSE_ROOM = 'c2s_close_room';
- const C2S_ROOM_EXIT = 'c2s_room_exit';
- const C2S_BACKED_RETURN = 'c2s_backed_return';
- const C2S_BACKED = 'c2s_to_backed';
- const C2S_RECEIVE_REWARD = 'c2s_receive_reward';
- const C2S_SEASON = 'c2s_season';
- const C2S_SET_USER_DATA_EXTRA = 'c2s_set_user_data_extra';
- const C2S_RANK_DATA = 'c2s_rank_data';
- //
- const Ver: string = '0.6.2';
- /**客户端*/
- export class ch_net<GD extends game_protocol = game_protocol> {
- /**连接状态事件*/
- public state = chsdk.get_new_event<ws_event_protocol>();
- private _ws: WsClient | TTWsClient | WXWsClient | null = null;
- private _url?: string | null;
- private _ca?: string;
- private reconnection: boolean;
- private reconnectCount: number;
- private reconnectInterval: number;
- private timeoutInterval: number;
- private gameDataInterval: number;
- private gameEvtInterval: number;
- private timeout: any | null = null;
- private season_timeout: any | null = null;
- private json: boolean = false;
- private heartbeatInterval: any | null = null; // 心跳定时器
- private dirtyDataCheckInterval: any | null = null; //变量同步定时器
- private evtCheckInterval: any | null = null; //事件同步定时器
- private _currentReconnectCount: number = 0;
- private _pingTime: number = 0;
- private _ping: number = 0;//ping值
- private _callbacks: { [id: string]: (result: any) => void } = {};
- private _waitSends: Array<string | ArrayBuffer> = [];//| ArrayBufferLike | Blob | ArrayBufferView
- private _new_resolve<T>(id: string): Promise<T> { return new Promise(resolve => this._callbacks[id] = resolve); }
- private _lv: Level = { LowerRank: 0, Rank: 0, Star: 0 };
- private _userKey: string;
- private _userDataExtra: GD['extra'] = null;
- /**玩家扩展数据 */
- public get userDataExtra(): GD['extra'] { return this._userDataExtra; };
- /**获取玩家扩展数据某个字段*/
- public getUserDataExtraField<K extends keyof GD['extra']>(key: K): GD['extra'][K] { return this._userDataExtra?.[key]; }
- /**自己的段位信息
- * @returns {Object}段位信息对象
- */
- public get ownLevel(): Level { return this._lv };
- private _seasonInfo: any;
- /**
- * 赛季信息 包含当前赛季的基本信息以及排名历史。
- * @returns {SeasonInfo} 赛季信息对象
- */
- public get seasonInfo(): SeasonInfo { return this._seasonInfo };
- private _do_resolve<T>(id: string, data: T): boolean {
- const resolveCallback = this._callbacks[id];
- if (!resolveCallback) return false;
- resolveCallback(data);
- delete this._callbacks[id];
- return true;
- }
- private _team: NetTeam<GD> | null;
- private _room: NetRoom<GD> | null;
- public get dataInterval(): number { return this.gameDataInterval * 0.001 };
- public get evtInterval(): number { return this.gameEvtInterval * 0.001 };
- public get isActive(): boolean { return this._ws && this._ws.isActive };
- /**队伍信息*/
- public get team(): NetTeam<GD> {
- return this._team;
- }
- /**是否在队伍中*/
- public get inTeam(): boolean {
- return this._team != null && this._team.own != null;
- }
- /**房间信息*/
- public get room(): NetRoom<GD> {
- return this._room;
- }
- /**是否在房间中*/
- public get inRoom(): boolean {
- return this._room != null && !this._room.cloosed;
- }
- /**
- * 创建一个客户端
- */
- constructor() {
- this._waitSends = [];
- this._callbacks = {};
- }
- /**初始化
- * @param url - 连接的地址 'ws://localhost:3000'
- * @param options - 可选的配置选项对象,支持以下属性:
- * @example
- * 自定义消息
- interface message extends message_protocol {
- useItem(userId: string, otherId: string, itemId: number): void//使用道具
- }
- 自定义数据
- export interface gd extends game_data {
- r_lv(lv: number);//关卡信息
- r_time(time: number);//关卡时间
- }
- export const net = ch.get_new_net<gd, message>();
- const url = chsdk.getUrl('/handle').replace(chsdk.getUrl('/handle').split(':')[0], 'ws');
- const token = chsdk.getToken();
- net.init(url,{query: `token=${token}`});
- */
- public init(url: string, options?: options) {
- this._url = url;
- this._url = `${this._url}${options?.query?.length ? '?' + options.query : ''}`;
- this._ca = options?.ca;
- this.reconnection = options?.reconnection ?? true;
- this.reconnectCount = options?.reconnectCount ?? RECONNECT_COUNT;
- this.reconnectInterval = options?.reconnectInterval ?? RECONNECT_INTERVAL;
- this.timeoutInterval = options?.timeoutInterval ?? HEARTBEAT_TIMEOUT;
- this.gameDataInterval = options?.gameDataInterval ?? DIRTYDATACHECK_INTERVAL;
- this.gameEvtInterval = options?.gameEvtInterval ?? EVTCHECK_INTERVAL;
- this.json = options?.json ?? false;
- this._ws = new WsClient(this._url, this._ca);
- //if (chsdk.get_pf() === chsdk.pf.wx) this._ws = new WXWsClient(this._url, this._ca);
- //if (chsdk.get_pf() === chsdk.pf.tt) this._ws = new TTWsClient(this._url, this._ca);
- this._ws.onConnected = this._onConnected.bind(this);
- this._ws.onError = this._onError.bind(this);
- this._ws.onClosing = this._onClosing.bind(this);
- this._ws.onClosed = this._onClosed.bind(this);
- this._ws.onMessage = this._onMessage.bind(this);
- const isbuffer = typeof ArrayBuffer !== 'undefined';
- console.log('ch_net 初始化 ver:', Ver, isbuffer, this.json, this._url);
- //
- chsdk.sdk_event.on('hide', this.on_hide, this);
- chsdk.sdk_event.on('show', this.on_show, this);
- }
- /**模拟断线*/
- public SimulatedDisconnection(): void {
- this._ws.close();
- }
- /**主动断开连接*/
- public dispose(): void {
- this.state.clearAll();
- this._clear_team();
- this._clear_room();
- this._callbacks = {};
- this._waitSends.length = 0;
- this._ws.close(5000, 'client disconnect');
- chsdk.sdk_event.off('hide', this.on_hide, this);
- chsdk.sdk_event.off('show', this.on_show, this);
- }
- private on_hide(): void {
- if (!this.inRoom) return;
- this.send_data(C2S_BACKED);
- }
- private on_show(): void {
- if (!this.inRoom) return;
- this.send_data(C2S_BACKED_RETURN);
- }
- private encode(msg: any): string | ArrayBuffer {
- return this.json ? JSON.stringify(msg) : msgpack.encode(msg).buffer;
- }
- private decode(medata: any): any {
- return this.json ? JSON.parse(medata) : msgpack.decode(new Uint8Array(medata));
- }
- private send_data(type: string, data?: any): void {
- //console.log(type, data);
- const d = this.encode(data ? { type: type, data: data } : { type: type });
- //console.log(d);
- this._send(d);
- }
- /**开始连接,返回null为成功,否则为错误提示*/
- public async connect(): Promise<string | null> {
- if (!this._ws) {
- chsdk.log.error('not init');
- return 'not init';
- }
- if (!this._ws.connect()) return 'no netconnect';
- this.state.emit(this.state.key.Connecting);
- return this._new_resolve('connect');
- }
- /**主动重新连接*/
- public reconnect(): string | null {
- if (!this._ws) {
- chsdk.log.error('not init');
- return 'not init';
- }
- this._currentReconnectCount = 1;
- this.do_reconnect();
- }
- private do_reconnect(): void {
- this._userKey = null;
- this.state.emit(this.state.key.Reconnecting, this._currentReconnectCount);
- this._ws.connect();
- }
- /**玩家创建队伍,返回null为成功,否则为错误提示*/
- public async creat_team(player_count: number = 4): Promise<string | null> {
- if (!this._ws) return 'No network connection';
- if (this.inTeam) return null;
- this.send_data(C2S_SET_TEAM, player_count);
- return this._new_resolve('creat_team');
- }
- /**玩家进入队伍,返回null为成功,否则为错误提示*/
- public async join_team(password: string): Promise<string | null> {
- if (!this._ws) return 'No network connection';
- if (this.inTeam) return null;
- this.send_data(C2S_JOIN_TEAM, { password: password });
- return this._new_resolve('join_team');
- }
- /**玩家退出队伍,返回null为成功,否则为错误提示*/
- public async exit_team(): Promise<string | null> {
- try {
- if (!this._ws) return 'No network connection';
- if (!this.inTeam) return 'Not in team';
- if (this.inRoom) return 'in game';
- this.send_data(C2S_EXIT_TEAM);
- return this._new_resolve('exit_team');
- } catch (error) {
- return 'Unexpected error occurred';
- }
- }
- /**主机结束游戏关闭房间,成功后收到房间关闭事件*/
- public close_room(): void {
- if (!this._ws) return;
- if (!this.inRoom) return;
- if (!this.room.isHost) return;
- this.send_data(C2S_CLOSE_ROOM);
- }
- /**玩家退出房间 ,返回null为成功,否则为错误提示,退出成功后收到房间关闭事件*/
- public async exit_room(): Promise<string | null> {
- try {
- if (!this._ws) return 'No network connection';
- if (!this.inTeam) return 'Not in team';
- if (!this.inRoom) return 'Not in room';
- this.send_data(C2S_ROOM_EXIT);
- return this._new_resolve('exit_room');
- } catch (error) {
- return 'Unexpected error occurred';
- }
- }
- private _cache_rank: Ranks<GD['extra']> | null = null;
- private _cache_rank_time: number = 0;
- /**获取排行榜行信息 如果是string,就是错误信息,否则为排行榜信息*/
- public async getRankData(): Promise<string | Ranks<GD['extra']>> {
- if (chsdk.date.now() - this._cache_rank_time >= 30000) {
- this._cache_rank = null;
- }
- if (this._cache_rank) return this._cache_rank;
- if (!this._ws) return 'No network connection';
- this.send_data(C2S_RANK_DATA);
- return this._new_resolve('get_rank_data');
- }
- /**强制转化成用护排行类型*/
- public asUserRankType<T extends UserRank<GD['extra']> = UserRank<GD['extra']>>(data: any): T {
- return data as T;
- }
- /**领取赛季奖励 ,返回null为成功,否则为错误提示*/
- public async receive_reward(season: number): Promise<string | null> {
- if (!this._ws) return 'No network connection';
- this.send_data(C2S_RECEIVE_REWARD, season);
- return this._new_resolve('receive_reward');
- }
- /**主动刷新赛季信息,返回null为成功,否则为错误提示*/
- public async update_season(): Promise<string | null> {
- if (!this._ws) return 'No network connection';
- this.send_data(C2S_SEASON);
- return this._new_resolve('update_season');
- }
- /**更新设置玩家整个扩展数据,返回null为成功,否则为错误提示*/
- public async setUserExtra(data: GD['extra'] & SerializableObject<GD['extra']>): Promise<string | null> {
- if (!this._ws) return 'No network connection';
- this._userDataExtra = data;
- this.send_data(C2S_SET_USER_DATA_EXTRA, JSON.stringify(data));
- if (this.inTeam) (this.team.own as any).set_userExtra(this._userDataExtra);
- if (this.inRoom) (this.room.own as any).set_userExtra(this._userDataExtra);
- return this._new_resolve('set_user_extra');
- }
- /**更新设置玩家扩展数据某个字段的值 */
- public async setUserExtraField<K extends keyof GD['extra']>(key: K, value: GD['extra'][K] & SerializableObject<GD['extra'][K]>): Promise<string | null> {
- this._userDataExtra ??= {};
- this._userDataExtra[key] = value;
- return this.setUserExtra(this._userDataExtra);
- }
- //
- private _send(data: string | ArrayBuffer) {
- if (!this._ws) return;
- if (this._ws.isActive) {
- this._ws.send(data);
- } else {
- this._waitSends.push(data);
- }
- }
- //
- private _clear_team(): void {
- this._team?.dispose();
- this._team = null;
- }
- private _clear_room(): void {
- this._room?.dispose();
- this._room = null;
- }
- private _temp_team: any | null;
- private _team_info(data?: any | null) {
- this._clear_team();
- if (!this._userKey) {
- this._temp_team = data;
- } else if (this._temp_team || data) {
- data ??= this._temp_team;
- this._team = new NetTeam<GD>(this._userKey, data, this.send_data.bind(this));
- if (!this._do_resolve('creat_team', null)) this._do_resolve('join_team', null);
- this._temp_team = null;
- }
- }
- private _temp_match_data: any | null;
- private _match_success(data?: any | null): void {
- this._clear_room();
- if (!this._userKey) {
- this._temp_match_data = data;
- } else if (this._temp_match_data || data) {
- data ??= this._temp_match_data;
- const roomData = data.roomData;
- const pData = data.playerData;
- this._room = new NetRoom<GD>(this._userKey, roomData, pData, this.send_data.bind(this));
- if (this._team) (this._team as any).match_success();
- this._temp_match_data = null;
- }
- }
- private _updatePlayerStatus(id: string, online: boolean) {
- if (this.inTeam) (this._team as any).updatePlayerStatus(id, online);
- if (this.inRoom) (this._room as any).updatePlayerStatus(id, online);
- }
- //
- private _onMessage(msg: string | ArrayBuffer): void {
- this.resetHeartbeat();
- try {
- const data = this.decode(msg);
- if (data.type !== PONG) chsdk.log.log('receive', JSON.stringify(data));
- switch (data.type) {
- case PONG:
- this._ping = chsdk.date.now() - this._pingTime;
- chsdk.date.updateServerTime(data.data);
- //chsdk.log.log("pong:", this._ping, data.data, chsdk.date.now());
- break;
- case S2C_LOGIN_SUCCESS:
- this._lv = data.data.level;
- this._userKey = data.data.userKey;
- this._seasonInfo = data.data.seasonInfo;
- this._userDataExtra = transUserExtraDataform(data.data.userDataExtra);
- this._team_info();
- this._match_success();
- //
- if (this._do_resolve('connect', null)) {
- this.state.emit(this.state.key.Connected);
- } else {
- this.state.emit(this.state.key.Reconnected);
- }
- this._callbacks = {};
- this.startHeartbeat();
- while (this._waitSends.length) {
- if (!this._ws) break;
- const data = this._waitSends.shift();
- if (data) this._ws.send(data);
- }
- break;
- case S2C_TEAM_SUCCESS:
- this._team_info(data.data);
- break;
- case S2C_JOIN_TEAM_FAIL:
- this._do_resolve('join_team', data.data);
- break;
- case S2C_TEAM_USER_JOIN:
- const join_user = data.data;
- join_user.status = true;
- (this._team as any).addPlayer({ key: join_user.userKey, data: join_user }, true);
- break;
- case S2C_TEAM_USER_EXIT:
- const esit_id = data.data;
- if (esit_id === this._team.own.Id) {
- (this._team as any).closed();
- this._clear_team();
- this._do_resolve('exit_team', null);
- } else {
- (this._team as any).removePlayer(esit_id);
- }
- break;
- case S2C_TEAM_CHANGE_HOST:
- if (this.inTeam) (this._team as any).changeHost(data.data);
- break;
- case S2C_ROOM_CHANGE_HOST:
- if (this.inRoom) (this._room as any).changeHost(data.data);
- break;
- case S2C_USER_RECONNECT:
- this._updatePlayerStatus(data.data, true);
- break;
- case S2C_USER_DISCONNECT:
- this._updatePlayerStatus(data.data, false);
- break;
- case S2C_MATCH:
- (this._team as any).match_start();
- break;
- case S2C_MATCH_CANCEL:
- (this._team as any).match_cancel();
- break;
- case S2C_NOT_READY:
- (this._team as any).not_ready();
- break;
- case S2C_READY:
- (this._team as any).updatePlayerReady(data.data, true);
- break;
- case S2C_MATCH_SUCCESS:
- this._match_success(data.data);
- break;
- case S2C_ROOM_GAMEDATA_CHANGE:
- (this._room as any).server_change(data.data);
- break;
- case S2C_USER_GAMEDATA_CHANGE:
- const p = this._room.getPlayer(data.data.userKey);
- if (!p) break;
- (p as any).server_change(data.data.data);
- break;
- case S2C_ROOM_CLOSED:
- (this._room as any).closed(data.data);
- const list = this.room.all;
- for (let i = 0; i < list.length; i++) {
- const rp = list[i];
- if (rp.isAI) continue;
- if (rp.isOwn) {
- this._lv.LowerRank = rp.level.LowerRank;
- this._lv.Rank = rp.level.Rank;
- this._lv.Star = rp.level.Star;
- }
- if (this.inTeam) {
- const p = this.team.getPlayer(rp.Id);
- if (!p) continue;
- (p as any).set_level(rp.level, rp.rank, rp.score, rp.totalRank);
- }
- }
- this._do_resolve('exit_room', null);
- break;
- case S2C_TALK:
- if (this.inRoom) {
- (this._room as any).onChat(data.data.userKey, data.data.msg);
- } else if (this.inTeam) {
- (this._team as any).onChat(data.data.userKey, data.data.msg);
- }
- break;
- case S2C_FINISH:
- (this._room as any).finish(data.data.userKey, data.data.rank);
- break;
- case S2C_EXIT_ROOM:
- (this._room as any).exit(data.data);
- break;
- case S2C_RECEIVE_REWARD:
- this._do_resolve('receive_reward', null);
- break;
- case S2C_SEASON:
- this._seasonInfo = data.data;
- this._do_resolve('update_season', null);
- this.state.emit('NextSeason');
- break;
- case S2C_SET_USER_DATA_EXTRA:
- this._do_resolve('set_user_extra', null);
- break;
- case S2C_RANK_DATA:
- this._cache_rank_time = chsdk.date.now();
- const r = data.data;
- const rr = r.ownRank;
- const ownRank: UserRank<GD['extra']> = rr;
- ownRank.UserData = transUserDataform(rr.UserData);
- ownRank.UserDataExtra = transUserExtraDataform(rr.UserDataExtra);
- const elementsRank: UserRank<GD['extra']>[] = [];
- for (let i = 0; i < r.elementsRank.length; i++) {
- const rr = r.elementsRank[i];
- rr.UserData = transUserDataform(rr.UserData);
- rr.UserDataExtra = transUserExtraDataform(rr.UserDataExtra);
- elementsRank.push(rr);
- }
- this._cache_rank = { list: elementsRank, own: ownRank };
- this._do_resolve('get_rank_data', this._cache_rank);
- break;
- case C2S_MESSAGE:
- if (Array.isArray(data.data)) {
- if (this.inRoom) {
- for (let i = 0; i < data.data.length; i++) {
- const d = data.data[i];
- (this._room as any)._onEvt(d.type, d.data);
- }
- }
- } else {
- chsdk.log.log('no def type:', data.data);
- //(this.receive as any)._emit(data.type, data.data);
- }
- break;
- default:
- break;
- }
- } catch (error) {
- chsdk.log.warn(error)
- this.state.emit(this.state.key.Error, error);
- }
- }
- private _onConnected(evt: any): void {
- if (this.timeout) clearTimeout(this.timeout);
- this._currentReconnectCount = 0;
- this.timeout = setTimeout(() => {
- chsdk.log.warn("Closing connection whit no replay logoin!");
- this._ws.close();
- }, 3000);
- }
- private _onError(err: any): void {
- chsdk.log.log('err', err);
- this.state.emit(this.state.key.Error, err);
- }
- private _onClosing(): void {
- chsdk.log.log('closing');
- this.state.emit(this.state.key.Closing);
- }
- private _onClosed(code: number, reason: string): void {
- code ??= 0;
- reason ??= '';
- chsdk.log.log("WebSocket connection closed", code, reason);
- this.stopHeartbeat();
- if (!this.reconnection || (reason && code > 4000)) {
- this.state.emit(this.state.key.Closed, reason);
- this._do_resolve('connect', reason);
- chsdk.log.log("WebSocket connection closed", reason);
- return;
- }
- if (this._currentReconnectCount < this.reconnectCount) {
- this._currentReconnectCount += 1;
- chsdk.log.log(`Reconnecting... Attempt ${this._currentReconnectCount}`);
- setTimeout(() => { this.do_reconnect() }, this.reconnectInterval)
- } else {
- this._clear_team();
- this._clear_room();
- chsdk.log.error("Max reconnect attempts reached. Giving up.");
- this.state.emit(this.state.key.Closed, reason);
- this._do_resolve('connect', reason);
- }
- }
- private startHeartbeat() {
- this.heartbeatInterval = setInterval(() => {
- this.sendHeartbeat();
- }, HEARTBEAT_INTERVAL);
- this.dirtyDataCheckInterval = setInterval(() => {
- this.dirtyDataCheck();
- }, this.gameDataInterval);
- this.evtCheckInterval = setInterval(() => {
- this.evtCheck();
- }, this.gameEvtInterval);
- if (this.season_timeout) clearTimeout(this.season_timeout);
- this.season_timeout = setTimeout(() => {
- this.update_season();
- }, this.seasonInfo.SeasonEndTime - chsdk.date.now() + Math.floor(Math.random() * 2000));
- }
- private resetHeartbeat() {
- if (this.timeout) clearTimeout(this.timeout);
- this.timeout = setTimeout(() => {
- chsdk.log.error("Heartbeat timeout. Closing connection.");
- this._ws.close();
- }, this.timeoutInterval);
- }
- private sendHeartbeat() {
- this._pingTime = chsdk.date.now();
- this.send_data(PING, this._ping);
- }
- private stopHeartbeat() {
- if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
- if (this.timeout) clearTimeout(this.timeout);
- if (this.dirtyDataCheckInterval) clearInterval(this.dirtyDataCheckInterval);
- if (this.evtCheckInterval) clearInterval(this.evtCheckInterval);
- if (this.season_timeout) clearTimeout(this.season_timeout);
- }
- private evtCheck() {
- if (this.inRoom) {
- (this.room as any).doSendChat();
- } else if (this.inTeam) {
- (this.team as any).doSendChat();
- }
- }
- private dirtyDataCheck() {
- if (!this.inRoom) return;
- this.room.doSendDirty();
- }
- /**
- * 分享队伍信息,邀请进队
- * @param extend (可选)分享数据
- * @param title 分享显示标题
- * @param imageUrl 分享显示图片
- */
- public shareNetTeam<T extends { [key: string]: any }>(title?: string, imageUrl?: string, extend?: T) {
- if (!this.inTeam) return;
- chsdk.shareAppMessage(title, '', imageUrl, JSON.stringify({ type: 'NetTeam', pw: this.team.Password, extends: extend }));
- }
- /**
- *获取从好友分享的net数据进入游戏的数据
- */
- public getShareNetTeam<T extends { [key: string]: any }>(): { password: string, extend?: T } {
- const query = chsdk.getQuery();
- if (!query) return null;
- const message = query.message;
- if (!message) return null;
- const data = JSON.parse(message);
- if (!data) return null;
- if (data.type != 'NetTeam') return null;
- query.message = null;
- return { password: data.pw, extend: data.extends as T };
- }
- }
|