Commit 1c2804f4 authored by Chunchi Che's avatar Chunchi Che Committed by WANG HE

add comment

parent 029a83e5
......@@ -6,6 +6,13 @@ export interface IDeck {
side?: number[];
}
/*
* 返回卡组资源。
*
* @param deck- 卡组名称
* @returns 卡组数据
*
* */
export async function fetchDeck(deck: string): Promise<IDeck> {
const res = await axios.get<IDeck>("http://localhost:3030/deck/" + deck);
......
import { StocAdapter, ygoProPacket } from "./packet";
import { ygoProPacket } from "./packet";
import { ygopro } from "../idl/ocgcore";
import {
STOC_CHAT,
......@@ -15,6 +15,13 @@ import StocHsPlayerChange from "./stoc/stocHsPlayerChange";
import StocHsWatchChange from "./stoc/stocHsWatchChange";
import StocTypeChange from "./stoc/stocTypeChange";
/*
* 将[`ygoProPacket`]对象转换成[`ygopro.YgoStocMsg`]对象
*
* @param packet - The ygoProPacket object
* @returns The ygopro.YgoStocMsg object
*
* */
export function adaptStoc(packet: ygoProPacket): ygopro.YgoStocMsg {
let pb = new ygopro.YgoStocMsg({});
switch (packet.proto) {
......
......@@ -2,6 +2,11 @@ import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket } from "../packet";
import { CTOS_HS_READY } from "../protoDecl";
/*
* CTOS HsReady
*
* @usage - 告诉ygopro服务端当前玩家准备完毕
* */
export default class CtosHsReady extends ygoProPacket {
constructor(_: ygopro.YgoCtosMsg) {
super(1, CTOS_HS_READY, new Uint8Array(0));
......
......@@ -2,6 +2,11 @@ import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket } from "../packet";
import { CTOS_HS_START } from "../protoDecl";
/*
* CTOS HsStart
*
* @usage - 开始游戏对局
* */
export default class CtosHsStartPacket extends ygoProPacket {
constructor(_: ygopro.YgoCtosMsg) {
super(1, CTOS_HS_START, new Uint8Array(0));
......
......@@ -3,6 +3,16 @@ import { ygoProPacket } from "../packet";
import { CTOS_JOIN_GAME } from "../protoDecl";
import { strEncodeUTF16 } from "../util";
/*
* CTOS JoinGame
*
* @param version: unsigned short - 版本号
* @param align: unsigned short - 对齐填充
* @param gameid: unsigned int - 永远是0
* @param passWd: [unsigned short; 20] - 房间密码
*
* @usage - 加入房间
* */
export default class CtosJoinGamePacket extends ygoProPacket {
constructor(pb: ygopro.YgoCtosMsg) {
const joinGame = pb.ctos_join_game;
......
......@@ -3,6 +3,13 @@ import { ygoProPacket } from "../packet";
import { CTOS_PLAYER_INFO } from "../protoDecl";
import { strEncodeUTF16 } from "../util";
/*
* CTOS PlayerInfo
*
* @param player: [unsigned short; 20] - 玩家昵称
*
* @usage - 告诉ygopro服务端当前玩家的昵称
* */
export default class CtosPlayerInfoPacket extends ygoProPacket {
constructor(pb: ygopro.YgoCtosMsg) {
const player = pb.ctos_player_info.name;
......
......@@ -4,6 +4,18 @@ import { CTOS_UPDATE_DECK } from "../protoDecl";
const BYTES_PER_U32 = 4;
/*
* CTOS UpdateDeck
*
* @param main: unsigned int - 主卡组数目
* @param extra: unsigned int - 额外卡组数目
* @param side: unsigned int - 副卡组数目
* @param mainCards: [unsigned int; main] - 主卡组数据
* @param extraCards: [unsigned int; extra] - 额外卡组数据
* @param side: [unsigned int; side] - 副卡组数据
*
* @usage - 更新对局的卡组信息
* */
export default class CtosUpdateDeck extends ygoProPacket {
constructor(pb: ygopro.YgoCtosMsg) {
const updateDeck = pb.ctos_update_deck;
......
/*
* Adapter模块的抽象层。
*
* */
import { ygopro } from "../idl/ocgcore";
const littleEndian: boolean = true;
const PACKET_MIN_LEN = 3;
// Ref: https://www.icode9.com/content-1-1341344.html
export class ygoProPacket {
packetLen: number;
proto: number;
exData: Uint8Array;
packetLen: number; // 数据包长度
proto: number; // ygopro协议标识
exData: Uint8Array; // 数据包内容
constructor(packetLen: number, proto: number, exData: Uint8Array) {
this.packetLen = packetLen;
......@@ -14,6 +19,11 @@ export class ygoProPacket {
this.exData = exData;
}
/*
* 将[`ygoProPacket`]对象序列化,
* 返回的二进制数数组可通过长连接发送到ygopro服务端。
*
* */
serialize(): Uint8Array {
const array = new Uint8Array(this.packetLen + 2);
const dataView = new DataView(array.buffer);
......@@ -25,6 +35,11 @@ export class ygoProPacket {
return array;
}
/*
* 将二进制数据反序列化成[`ygoProPacket`]对象,
* 返回值可用于业务逻辑处理。
*
* */
static deserialize(array: ArrayBuffer): ygoProPacket {
try {
if (array.byteLength < PACKET_MIN_LEN) {
......
/*
* Ygopro的协议标识声明。
*
* */
export const CTOS_PLAYER_INFO = 16;
export const CTOS_JOIN_GAME = 18;
export const CTOS_UPDATE_DECK = 2;
......
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket, StocAdapter } from "../packet";
/*
* STOC Chat
*
* @param player: unsigned short - 玩家编号
* @param message: [unsigned short] - 聊天消息文本
*
* @usage - 更新聊天消息
* */
export default class chatAdapter implements StocAdapter {
packet: ygoProPacket;
......
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket, StocAdapter } from "../packet";
/*
* STOC HsPlayerChange
*
* @param todo
*
* @usage - 更新玩家状态
* */
export default class hsPlayerChangeAdapter implements StocAdapter {
packet: ygoProPacket;
......
......@@ -4,6 +4,14 @@ import { UTF16_BUFFER_MAX_LEN } from "../util";
const UINT8_PER_UINT16 = 2;
/*
* STOC HsPlayerEnter
*
* @param name: [unsigned short; 20] - 玩家昵称
* @param pos: unsigned chat - 玩家进入房间的位置
*
* @usage - 有新玩家进入房间,更新状态
* */
export default class hsPlayerEnterAdapter implements StocAdapter {
packet: ygoProPacket;
......
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket, StocAdapter } from "../packet";
/*
* STOC HsWatchChange
*
* @param count: unsigned short - 观观者数量
*
* @usage - 更新观战者数量
* */
export default class hsWatchChangeAdapter implements StocAdapter {
packet: ygoProPacket;
......
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket, StocAdapter } from "../packet";
/*
* STOC JoinGame
*
* @usage - 告知客户端/前端已成功加入房间
* */
export default class joinGameAdapter implements StocAdapter {
packet: ygoProPacket;
......
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket, StocAdapter } from "../packet";
/*
* STOC TypeChange
*
* @param todo
*
* @usage - 更新玩家状态
* */
export default class typeChangeAdapter implements StocAdapter {
packet: ygoProPacket;
......
/*
* 一些基础函数。
*
* */
export const UTF16_BUFFER_MAX_LEN = 20;
const FILLING_TOKEN: number = 0xcccc;
/*
* 将`string`类型字符串转成`utf-16`编码的二进制数组。
*
* @param str - The `string` type string
* @returns The `utf-16` `Uint8Array`
*
* */
export function strEncodeUTF16(str: string) {
let buf = new ArrayBuffer(UTF16_BUFFER_MAX_LEN * 2);
let bufView = new Uint16Array(buf);
......
/*
* 一些发ygopro协议数据包的辅助函数,用于简化业务代码。
*
* */
import { ygopro } from "./idl/ocgcore";
import socketMiddleWare, { socketCmd } from "../../middleware/socket";
import { IDeck } from "../Card";
......@@ -16,7 +20,7 @@ export function sendUpdateDeck(deck: IDeck) {
}),
});
// 如果要实现UI层和Adapter层解耦,这里应该不感知具体Adapter类型
// FIXME: 如果要实现UI层和Adapter层解耦,这里应该不感知具体Adapter类型
const payload = new UpdateDeckAdapter(updateDeck).serialize();
socketMiddleWare({ cmd: socketCmd.SEND, payload });
......@@ -46,7 +50,7 @@ export function sendPlayerInfo(ws: WebSocket, player: string) {
name: player,
}),
});
const packet = new PlayerInfoAdapter(playerInfo); // todo: 需要收敛在一个层次里
const packet = new PlayerInfoAdapter(playerInfo);
ws.send(packet.serialize());
}
......
/*
* Socket中间件
*
* 所有长连接/Websocket相关的逻辑都应该收敛在这里。
*
* */
import handleSocketOpen from "../service/onSocketOpen";
import handleSocketMessage from "../service/onSocketMessage";
export enum socketCmd {
// 建立长连接
CONNECT,
// 断开长连接
DISCONNECT,
// 通过长连接发送数据
SEND,
}
export interface socketAction {
cmd: socketCmd;
// 创建长连接需要业务方传入的数据
initInfo?: {
ip: string;
player: string;
passWd: string;
};
// 通过长连接发送的数据
payload?: Uint8Array;
}
let ws: WebSocket | null = null;
// FIXME: 应该有个返回值,告诉业务方本次请求的结果。比如建立长连接失败。
export default function (action: socketAction) {
switch (action.cmd) {
case socketCmd.CONNECT: {
......
/*
* Chat状态更新逻辑
*
* */
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "../store";
......
/*
* 加入房间状态更新逻辑
*
* */
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store";
......
/*
* 进入房间的玩家状态更新逻辑
*
* */
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "../store";
......
/*
* 长连接消息事件订阅处理逻辑
*
* */
import handleHsPlayerChange from "./room/hsPlayerChange";
import handleTypeChange from "./room/typeChange";
import handleHsPlayerEnter from "./room/hsPlayerEnter";
......@@ -7,6 +11,11 @@ import handleHsWatchChange from "./room/hsWatchChange";
import { ygoProPacket } from "../api/ocgcore/ocgAdapter/packet";
import { adaptStoc } from "../api/ocgcore/ocgAdapter/adapter";
/*
* 先将从长连接中读取到的二进制数据通过Adapter转成protobuf结构体,
* 然后再分发到各个处理函数中去处理。
*
* */
export default function handleSocketMessage(e: MessageEvent) {
const packet = ygoProPacket.deserialize(e.data);
const pb = adaptStoc(packet);
......
/*
* 长连接建立事件订阅处理逻辑
*
* */
import { sendJoinGame, sendPlayerInfo } from "../api/ocgcore/ocgHelper";
/*
* 长连接建立后,需要马上发送PlayerInfo和JoinGame两个数据包,
* 否则ygopro服务端超过2s后会自动断连。
*
* */
export default function handleSocketOpen(
ws: WebSocket | null,
ip: string,
......
/*
* 全局状态存储模块
* */
import { configureStore } from "@reduxjs/toolkit";
import joinedReducer from "./reducers/joinSlice";
import chatReducer from "./reducers/chatSlice";
......
// 测试用的Babylon.js demo页面
import React, { useEffect, useRef } from "react";
import * as BABYLON from "@babylonjs/core";
......
/*
* 加入房间页面
*
* player: 玩家昵称;
* addr: IP地址;
* passWd: 房间密码。
*
* */
import React, { useState, ChangeEvent } from "react";
import { Link } from "react-router-dom";
import "../css/JoinRoom.css";
......
/*
* 等待房间页面
*
* */
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { fetchDeck } from "../api/Card";
......@@ -32,6 +36,7 @@ export default function WaitRoom() {
useEffect(() => {
if (ip && player && player.length != 0 && passWd && passWd.length != 0) {
// 页面第一次渲染时,通过socket中间件向ygopro服务端请求建立长连接
socketMiddleWare({
cmd: socketCmd.CONNECT,
initInfo: {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment