Commit 51be8259 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/page/mora' into 'main'

Feat/page/mora

See merge request mycard/Neos!12
parents f2c72f63 11db95f1
Subproject commit d1e33713852a59ad63e7d0ead32f5a0e032f4644 Subproject commit 53a9cf4cf7cdfc55f1bfa7d86d74e61c56a5533f
This diff is collapsed.
...@@ -7,6 +7,9 @@ import { ...@@ -7,6 +7,9 @@ import {
STOC_HS_WATCH_CHANGE, STOC_HS_WATCH_CHANGE,
STOC_JOIN_GAME, STOC_JOIN_GAME,
STOC_TYPE_CHANGE, STOC_TYPE_CHANGE,
STOC_SELECT_HAND,
STOC_SELECT_TP,
STOC_HAND_RESULT,
} from "./protoDecl"; } from "./protoDecl";
import StocChat from "./stoc/stocChat"; import StocChat from "./stoc/stocChat";
import StocJoinGame from "./stoc/stocJoinGame"; import StocJoinGame from "./stoc/stocJoinGame";
...@@ -14,6 +17,8 @@ import StocHsPlayerEnter from "./stoc/stocHsPlayerEnter"; ...@@ -14,6 +17,8 @@ import StocHsPlayerEnter from "./stoc/stocHsPlayerEnter";
import StocHsPlayerChange from "./stoc/stocHsPlayerChange"; import StocHsPlayerChange from "./stoc/stocHsPlayerChange";
import StocHsWatchChange from "./stoc/stocHsWatchChange"; import StocHsWatchChange from "./stoc/stocHsWatchChange";
import StocTypeChange from "./stoc/stocTypeChange"; import StocTypeChange from "./stoc/stocTypeChange";
import StocSelectHand from "./stoc/stocSelectHand";
import StocSelectTp from "./stoc/stocSelectTp";
/* /*
* 将[`ygoProPacket`]对象转换成[`ygopro.YgoStocMsg`]对象 * 将[`ygoProPacket`]对象转换成[`ygopro.YgoStocMsg`]对象
...@@ -55,6 +60,20 @@ export function adaptStoc(packet: ygoProPacket): ygopro.YgoStocMsg { ...@@ -55,6 +60,20 @@ export function adaptStoc(packet: ygoProPacket): ygopro.YgoStocMsg {
break; break;
} }
case STOC_SELECT_HAND: {
pb = new StocSelectHand(packet).upcast();
break;
}
case STOC_SELECT_TP: {
pb = new StocSelectTp(packet).upcast();
break;
}
case STOC_HAND_RESULT: {
// TODO
break;
}
default: { default: {
break; break;
} }
......
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket } from "../packet";
import { CTOS_HAND_RESULT } from "../protoDecl";
/*
* CTOS HandResult
*
* @param res: unsigned char - 玩家的猜拳选择
*
* @usage - 告知服务端当前玩家的猜拳选择
* */
export default class CtosHandResultPacket extends ygoProPacket {
constructor(pb: ygopro.YgoCtosMsg) {
const handResult = pb.ctos_hand_result;
const hand = handResult.hand;
const exData = new Uint8Array(1);
const dataView = new DataView(exData.buffer);
switch (hand) {
case ygopro.HandType.SCISSORS: {
dataView.setUint8(0, 1);
break;
}
case ygopro.HandType.ROCK: {
dataView.setUint8(0, 2);
break;
}
case ygopro.HandType.PAPER: {
dataView.setUint8(0, 3);
break;
}
default: {
console.log("Unknown HandResult type" + hand);
}
}
super(exData.length + 1, CTOS_HAND_RESULT, exData);
}
}
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket } from "../packet";
import { CTOS_TP_RESULT } from "../protoDecl";
/*
* CTOS CTOS_TP_RESULT
*
* @param res: unsigned char - 玩家的先后攻选择
*
* @usage - 告知服务端当前玩家的先后攻选择
*
* */
export default class CtosTpResultPacket extends ygoProPacket {
constructor(pb: ygopro.YgoCtosMsg) {
const tpResult = pb.ctos_tp_result;
const tp = tpResult.tp;
const exData = new Uint8Array(1);
const dataView = new DataView(exData.buffer);
switch (tp) {
case ygopro.CtosTpResult.TpType.FIRST: {
dataView.setUint8(0, 1);
break;
}
case ygopro.CtosTpResult.TpType.SECOND: {
dataView.setUint8(0, 0);
break;
}
default: {
console.log("Unknown HandResult type" + tp);
}
}
super(exData.length + 1, CTOS_TP_RESULT, exData);
}
}
...@@ -7,6 +7,8 @@ export const CTOS_JOIN_GAME = 18; ...@@ -7,6 +7,8 @@ export const CTOS_JOIN_GAME = 18;
export const CTOS_UPDATE_DECK = 2; export const CTOS_UPDATE_DECK = 2;
export const CTOS_HS_READY = 34; export const CTOS_HS_READY = 34;
export const CTOS_HS_START = 37; export const CTOS_HS_START = 37;
export const CTOS_HAND_RESULT = 3;
export const CTOS_TP_RESULT = 4;
export const STOC_JOIN_GAME = 18; export const STOC_JOIN_GAME = 18;
export const STOC_CHAT = 25; export const STOC_CHAT = 25;
...@@ -14,3 +16,6 @@ export const STOC_HS_PLAYER_ENTER = 32; ...@@ -14,3 +16,6 @@ export const STOC_HS_PLAYER_ENTER = 32;
export const STOC_HS_PLAYER_CHANGE = 33; export const STOC_HS_PLAYER_CHANGE = 33;
export const STOC_HS_WATCH_CHANGE = 34; export const STOC_HS_WATCH_CHANGE = 34;
export const STOC_TYPE_CHANGE = 19; export const STOC_TYPE_CHANGE = 19;
export const STOC_SELECT_HAND = 3;
export const STOC_SELECT_TP = 4;
export const STOC_HAND_RESULT = 5;
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket, StocAdapter } from "../packet";
/*
* STOC SelectHand
*
* @usage - 通知客户端/前端提醒用户进行猜拳选择
* */
export default class selectHand implements StocAdapter {
packet: ygoProPacket;
constructor(packet: ygoProPacket) {
this.packet = packet;
}
upcast(): ygopro.YgoStocMsg {
return new ygopro.YgoStocMsg({
stoc_select_hand: new ygopro.StocSelectHand({}),
});
}
}
import { ygopro } from "../../idl/ocgcore";
import { ygoProPacket, StocAdapter } from "../packet";
/*
* STOC SelectTp
*
* @usage - 通知客户端/前端提醒用户进行选先后攻
* */
export default class selectTp implements StocAdapter {
packet: ygoProPacket;
constructor(packet: ygoProPacket) {
this.packet = packet;
}
upcast(): ygopro.YgoStocMsg {
return new ygopro.YgoStocMsg({
stoc_select_tp: new ygopro.StocSelectTp({}),
});
}
}
...@@ -10,6 +10,8 @@ import JoinGameAdapter from "./ocgAdapter/ctos/ctosJoinGame"; ...@@ -10,6 +10,8 @@ import JoinGameAdapter from "./ocgAdapter/ctos/ctosJoinGame";
import UpdateDeckAdapter from "./ocgAdapter/ctos/ctosUpdateDeck"; import UpdateDeckAdapter from "./ocgAdapter/ctos/ctosUpdateDeck";
import HsReadyAdapter from "./ocgAdapter/ctos/ctosHsReady"; import HsReadyAdapter from "./ocgAdapter/ctos/ctosHsReady";
import HsStartAdapter from "./ocgAdapter/ctos/ctosHsStart"; import HsStartAdapter from "./ocgAdapter/ctos/ctosHsStart";
import HandResult from "./ocgAdapter/ctos/ctosHandResult";
import TpResult from "./ocgAdapter/ctos/ctosTpResult";
export function sendUpdateDeck(deck: IDeck) { export function sendUpdateDeck(deck: IDeck) {
const updateDeck = new ygopro.YgoCtosMsg({ const updateDeck = new ygopro.YgoCtosMsg({
...@@ -67,3 +69,41 @@ export function sendJoinGame(ws: WebSocket, version: number, passWd: string) { ...@@ -67,3 +69,41 @@ export function sendJoinGame(ws: WebSocket, version: number, passWd: string) {
ws.send(packet.serialize()); ws.send(packet.serialize());
} }
export function sendHandResult(result: string) {
let hand = ygopro.HandType.UNKNOWN;
if (result === "scissors") {
hand = ygopro.HandType.SCISSORS;
} else if (result === "rock") {
hand = ygopro.HandType.ROCK;
} else if (result === "paper") {
hand = ygopro.HandType.PAPER;
}
const handResult = new ygopro.YgoCtosMsg({
ctos_hand_result: new ygopro.CtosHandResult({
hand,
}),
});
const payload = new HandResult(handResult).serialize();
socketMiddleWare({ cmd: socketCmd.SEND, payload });
}
export function sendTpResult(isFirst: boolean) {
let tp = ygopro.CtosTpResult.TpType.UNKNOWN;
if (isFirst) {
tp = ygopro.CtosTpResult.TpType.FIRST;
} else {
tp = ygopro.CtosTpResult.TpType.SECOND;
}
const tpResult = new ygopro.YgoCtosMsg({
ctos_tp_result: new ygopro.CtosTpResult({
tp,
}),
});
const payload = new TpResult(tpResult).serialize();
socketMiddleWare({ cmd: socketCmd.SEND, payload });
}
.container {
display: flex;
margin: auto;
padding: 0;
}
.item {
text-align: center;
flex: 1;
}
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* */ * */
import React from "react"; import React from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import App from "./ui/App"; import Neos from "./ui/Neos";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { store } from "./store"; import { store } from "./store";
...@@ -33,7 +33,7 @@ root.render( ...@@ -33,7 +33,7 @@ root.render(
<React.StrictMode> <React.StrictMode>
<BrowserRouter> <BrowserRouter>
<Provider store={store}> <Provider store={store}>
<App /> <Neos />
</Provider> </Provider>
</BrowserRouter> </BrowserRouter>
</React.StrictMode> </React.StrictMode>
......
/*
* 猜拳页面的状态更新逻辑
*
* */
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "../store";
export interface moraState {
selectHandAble: boolean;
selectTpAble: boolean;
}
const initialState: moraState = {
selectHandAble: false,
selectTpAble: false,
};
const moraSlice = createSlice({
name: "mora",
initialState,
reducers: {
selectHandAble: (state) => {
state.selectHandAble = true;
},
unSelectHandAble: (state) => {
state.selectHandAble = false;
},
selectTpAble: (state) => {
state.selectTpAble = true;
},
unSelectTpAble: (state) => {
state.selectTpAble = false;
},
},
});
export const {
selectHandAble,
unSelectHandAble,
selectTpAble,
unSelectTpAble,
} = moraSlice.actions;
export const selectHandSelectAble = (state: RootState) =>
state.mora.selectHandAble;
export const selectTpSelectAble = (state: RootState) => state.mora.selectTpAble;
export default moraSlice.reducer;
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { store } from "../../store";
import { selectHandAble } from "../../reducers/moraSlice";
export default function handleSelectHand(_: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
dispatch(selectHandAble());
}
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { store } from "../../store";
import { selectTpAble } from "../../reducers/moraSlice";
export default function handleSelectTp(_: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
dispatch(selectTpAble());
}
...@@ -10,6 +10,8 @@ import handleChat from "./room/chat"; ...@@ -10,6 +10,8 @@ import handleChat from "./room/chat";
import handleHsWatchChange from "./room/hsWatchChange"; import handleHsWatchChange from "./room/hsWatchChange";
import { ygoProPacket } from "../api/ocgcore/ocgAdapter/packet"; import { ygoProPacket } from "../api/ocgcore/ocgAdapter/packet";
import { adaptStoc } from "../api/ocgcore/ocgAdapter/adapter"; import { adaptStoc } from "../api/ocgcore/ocgAdapter/adapter";
import handleSelectHand from "./mora/selectHand";
import handleSelectTp from "./mora/selectTp";
/* /*
* 先将从长连接中读取到的二进制数据通过Adapter转成protobuf结构体, * 先将从长连接中读取到的二进制数据通过Adapter转成protobuf结构体,
...@@ -51,6 +53,22 @@ export default function handleSocketMessage(e: MessageEvent) { ...@@ -51,6 +53,22 @@ export default function handleSocketMessage(e: MessageEvent) {
break; break;
} }
case "stoc_select_hand": {
handleSelectHand(pb);
break;
}
case "stoc_hand_result": {
// TODO
console.log("TODO: handle STOC HandResult.");
break;
}
case "stoc_select_tp": {
handleSelectTp(pb);
break;
}
default: { default: {
break; break;
} }
......
...@@ -5,12 +5,14 @@ import { configureStore } from "@reduxjs/toolkit"; ...@@ -5,12 +5,14 @@ import { configureStore } from "@reduxjs/toolkit";
import joinedReducer from "./reducers/joinSlice"; import joinedReducer from "./reducers/joinSlice";
import chatReducer from "./reducers/chatSlice"; import chatReducer from "./reducers/chatSlice";
import playerReducer from "./reducers/playerSlice"; import playerReducer from "./reducers/playerSlice";
import moraReducer from "./reducers/moraSlice";
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
join: joinedReducer, join: joinedReducer,
chat: chatReducer, chat: chatReducer,
player: playerReducer, player: playerReducer,
mora: moraReducer,
}, },
}); });
......
/*
* 猜拳页面
*
* */
import React from "react";
import { sendHandResult, sendTpResult } from "../api/ocgcore/ocgHelper";
import "../css/Mora.css";
import { useAppSelector } from "../hook";
import {
selectHandSelectAble,
unSelectHandAble,
selectTpSelectAble,
unSelectTpAble,
} from "../reducers/moraSlice";
import { store } from "../store";
// TODO: 应该展示对手卡组信息和聊天信息
export default function Mora() {
const dispatch = store.dispatch;
const selectHandAble = useAppSelector(selectHandSelectAble);
const selectTpAble = useAppSelector(selectTpSelectAble);
const handleSelectScissors = () => {
sendHandResult("scissors");
dispatch(unSelectHandAble());
};
const handleSelectRock = () => {
sendHandResult("rock");
dispatch(unSelectHandAble());
};
const handleSelectPaper = () => {
sendHandResult("paper");
dispatch(unSelectHandAble());
};
const handleSelectFirst = () => {
sendTpResult(true);
dispatch(unSelectTpAble());
};
const handleSelectSecond = () => {
sendTpResult(false);
dispatch(unSelectTpAble());
};
return (
<div className="container">
<div className="item">
<button disabled={!selectHandAble} onClick={handleSelectScissors}>
scissors
</button>
<button disabled={!selectHandAble} onClick={handleSelectRock}>
rock
</button>
<button disabled={!selectHandAble} onClick={handleSelectPaper}>
paper
</button>
</div>
<div className="item">
<button disabled={!selectTpAble} onClick={handleSelectFirst}>
first
</button>
<button disabled={!selectTpAble} onClick={handleSelectSecond}>
second
</button>
</div>
</div>
);
}
...@@ -4,16 +4,17 @@ import WaitRoom from "./WaitRoom"; ...@@ -4,16 +4,17 @@ import WaitRoom from "./WaitRoom";
import ThreeJs from "./ThreeJs"; import ThreeJs from "./ThreeJs";
import BabylonJs from "./BabylonJs"; import BabylonJs from "./BabylonJs";
import { Routes, Route } from "react-router-dom"; import { Routes, Route } from "react-router-dom";
import Mora from "./Mora";
function App() { export default function () {
// FIXME: 这里Mora路由应该由每个房间指定一个路径
return ( return (
<Routes> <Routes>
<Route path="/" element={<JoinRoom />} /> <Route path="/" element={<JoinRoom />} />
<Route path="/:player/:passWd/:ip" element={<WaitRoom />} /> <Route path="/:player/:passWd/:ip" element={<WaitRoom />} />
<Route path="/mora" element={<Mora />} />
<Route path="/three" element={<ThreeJs />} /> <Route path="/three" element={<ThreeJs />} />
<Route path="/babylon" element={<BabylonJs />} /> <Route path="/babylon" element={<BabylonJs />} />
</Routes> </Routes>
); );
} }
export default App;
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* */ * */
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import { fetchDeck } from "../api/Card"; import { fetchDeck } from "../api/Card";
import "../css/WaitRoom.css"; import "../css/WaitRoom.css";
import { useAppSelector } from "../hook"; import { useAppSelector } from "../hook";
...@@ -88,17 +88,13 @@ export default function WaitRoom() { ...@@ -88,17 +88,13 @@ export default function WaitRoom() {
<p> <p>
<button <button
disabled={ disabled={
!( !isHost ||
isHost && player0.state != READY_STATE ||
player0.state != undefined && player1.state != READY_STATE
player0.state === READY_STATE &&
player1.state != undefined &&
player1.state === READY_STATE
)
} }
onClick={handleChoseStart} onClick={handleChoseStart}
> >
start <Link to={{ pathname: `/mora` }}>start</Link>
</button> </button>
</p> </p>
</div> </div>
......
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