Change Doudizhu message process from websocket to restful api

This commit is contained in:
songyih 2020-02-06 22:51:26 -08:00
parent 9de0847a1e
commit 19fafcb832
3 changed files with 53 additions and 126 deletions

View File

@ -9,53 +9,12 @@ const port = process.env.PORT || 10080;
app.use(cors()); app.use(cors());
app.use(express.json()); app.use(express.json());
const server = app.listen(port, () => { app.listen(port, () => {
console.log(`Server is running on port: ${port}`); console.log(`Server is running on port: ${port}`);
}); });
const socket = require("socket.io");
const io = socket(server);
let testDoudizhuData = null, testLeducHoldemData = null; let testDoudizhuData = null, testLeducHoldemData = null;
io.on("connection", socket => {
console.log("successfully connected to rlcard showdown frontend");
socket.emit("getMessage", "successfully connected to rlcard showdown node server");
socket.on("getMessage", message => {
let res = null;
if(message){
switch(message.type){
case(0):
res = {
type: 0,
message: {
playerInfo: testDoudizhuData.playerInfo,
initHands: testDoudizhuData.initHands
}
};
socket.emit("getMessage", res);
break;
case(1):
console.log(message);
if(message.message.turn >= testDoudizhuData.moveHistory.length){
// todo: process end of game
}else{
res = {
type: 1,
message: {
turn: message.message.turn,
playerIdx: testDoudizhuData.moveHistory[message.message.turn].playerIdx,
move: testDoudizhuData.moveHistory[message.message.turn].move
}
};
}
socket.emit("getMessage", res);
break;
}
}
})
});
router.get('/replay/leduc_holdem/:id', (req, res)=>{ router.get('/replay/leduc_holdem/:id', (req, res)=>{
res.json(testLeducHoldemData); res.json(testLeducHoldemData);
}); });

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import axios from 'axios';
import '../assets/gameview.scss'; import '../assets/gameview.scss';
import { DoudizhuGameBoard } from '../components/GameBoard'; import { DoudizhuGameBoard } from '../components/GameBoard';
import webSocket from "socket.io-client"; import { removeCards, doubleRaf, deepCopy } from "../utils";
import { removeCards, doubleRaf, deepCopy, debounce } from "../utils";
import { Layout } from 'element-react'; import { Layout } from 'element-react';
import Slider from '@material-ui/core/Slider'; import Slider from '@material-ui/core/Slider';
@ -19,6 +19,8 @@ class DoudizhuGameView extends React.Component {
this.initConsiderationTime = 2000; this.initConsiderationTime = 2000;
this.considerationTimeDeduction = 100; this.considerationTimeDeduction = 100;
this.gameStateTimeout = null; this.gameStateTimeout = null;
this.apiUrl = window.g.apiUrl;
this.moveHistory = [];
this.initGameState = { this.initGameState = {
gameStatus: "ready", // "ready", "playing", "paused", "over" gameStatus: "ready", // "ready", "playing", "paused", "over"
@ -32,7 +34,6 @@ class DoudizhuGameView extends React.Component {
}; };
this.state = { this.state = {
ws: null,
gameInfo: this.initGameState, gameInfo: this.initGameState,
gameStateLoop: null, gameStateLoop: null,
gameSpeed: 0 gameSpeed: 0
@ -42,11 +43,6 @@ class DoudizhuGameView extends React.Component {
gameStateTimer() { gameStateTimer() {
this.gameStateTimeout = setTimeout(()=>{ this.gameStateTimeout = setTimeout(()=>{
let currentConsiderationTime = this.state.gameInfo.considerationTime; let currentConsiderationTime = this.state.gameInfo.considerationTime;
// for test use
// console.log(currentConsiderationTime);
// if(currentConsiderationTime === 1000){
// debugger;
// }
if(currentConsiderationTime > 0) { if(currentConsiderationTime > 0) {
currentConsiderationTime -= this.considerationTimeDeduction * Math.pow(2, this.state.gameSpeed); currentConsiderationTime -= this.considerationTimeDeduction * Math.pow(2, this.state.gameSpeed);
currentConsiderationTime = currentConsiderationTime < 0 ? 0 : currentConsiderationTime; currentConsiderationTime = currentConsiderationTime < 0 ? 0 : currentConsiderationTime;
@ -55,83 +51,55 @@ class DoudizhuGameView extends React.Component {
this.setState({gameInfo: gameInfo}); this.setState({gameInfo: gameInfo});
this.gameStateTimer(); this.gameStateTimer();
}else{ }else{
const turn = this.state.gameInfo.turn; let res = this.moveHistory[this.state.gameInfo.turn];
const gameStateReq = { if(res.playerIdx === this.state.gameInfo.currentPlayer){
type: 1, let gameInfo = deepCopy(this.state.gameInfo);
message: {turn: turn} gameInfo.latestAction[res.playerIdx] = res.move === "P" ? "P" : res.move.split(" ");
}; gameInfo.turn++;
let gameInfo = deepCopy(this.state.gameInfo); gameInfo.currentPlayer = (gameInfo.currentPlayer+1)%3;
this.setState({gameInfo: gameInfo}); // take away played cards from player's hands
this.state.ws.emit("getMessage", gameStateReq); const remainedCards = removeCards(gameInfo.latestAction[res.playerIdx], gameInfo.hands[res.playerIdx]);
if(remainedCards !== false){
gameInfo.hands[res.playerIdx] = remainedCards;
}else{
console.log("Cannot find cards in move from player's hand");
}
gameInfo.considerationTime = this.initConsiderationTime;
this.setState({gameInfo: gameInfo});
}else{
console.log("Mismatched current player index");
}
} }
}, 100); }, 100);
} }
startReplay() { startReplay() {
if(this.state.ws !== null){ // for test use
const replayReq = {type: 0}; const replayId = 0;
this.state.ws.emit("getMessage", replayReq);
// init game state
let initGameState = deepCopy(this.initGameState);
// set game status to playing
initGameState.gameStatus = "playing";
this.setState({gameInfo: initGameState});
if(this.gameStateTimeout){ axios.get(`${this.apiUrl}/replay/doudizhu/${replayId}`)
window.clearTimeout(this.gameStateTimeout); .then(res => {
this.gameStateTimeout = null; res = res.data;
} // init replay info
// loop to update game state this.moveHistory = res.moveHistory;
this.gameStateTimer(); let gameInfo = deepCopy(this.initGameState);
}else{ gameInfo.gameStatus = "playing";
console.log("websocket not connected"); gameInfo.playerInfo = res.playerInfo;
} gameInfo.hands = res.initHands.map(element => {
}; return element.split(" ");
});
// the first player should be landlord
gameInfo.currentPlayer = res.playerInfo.find(element=>{return element.role === "landlord"}).index;
this.setState({gameInfo: gameInfo}, ()=>{
if(this.gameStateTimeout){
window.clearTimeout(this.gameStateTimeout);
this.gameStateTimeout = null;
}
// loop to update game state
this.gameStateTimer();
});
});
connectWebSocket() {
let ws = webSocket("http://localhost:10080");
ws.on("getMessage", message => {
if(message){
switch(message.type){
case 0:
// init replay info
let gameInfo = deepCopy(this.state.gameInfo);
gameInfo.playerInfo = message.message.playerInfo;
gameInfo.hands = message.message.initHands.map(element => {
return element.split(" ");
});
// the first player should be landlord
gameInfo.currentPlayer = message.message.playerInfo.find(element=>{return element.role === "landlord"}).index;
this.setState({gameInfo: gameInfo});
break;
case 1:
// getting player actions
let res = message.message;
if(res.turn === this.state.gameInfo.turn && res.playerIdx === this.state.gameInfo.currentPlayer){
let gameInfo = deepCopy(this.state.gameInfo);
gameInfo.latestAction[res.playerIdx] = res.move === "P" ? "P" : res.move.split(" ");
gameInfo.turn++;
gameInfo.currentPlayer = (gameInfo.currentPlayer+1)%3;
// take away played cards from player's hands
const remainedCards = removeCards(gameInfo.latestAction[res.playerIdx], gameInfo.hands[res.playerIdx]);
if(remainedCards !== false){
gameInfo.hands[res.playerIdx] = remainedCards;
}else{
console.log("Cannot find cards in move from player's hand");
}
gameInfo.considerationTime = this.initConsiderationTime;
this.setState({gameInfo: gameInfo});
}else{
console.log("Mismatched game turn or current player index", message);
}
break;
default:
console.log("Wrong message type ", message);
break;
}
}
});
this.setState({ws: ws});
}; };
runNewTurn(prevTurn){ runNewTurn(prevTurn){
@ -175,7 +143,6 @@ class DoudizhuGameView extends React.Component {
} }
changeGameSpeed(newVal){ changeGameSpeed(newVal){
console.log('wdnmd');
this.setState({gameSpeed: newVal}); this.setState({gameSpeed: newVal});
} }
@ -188,11 +155,10 @@ class DoudizhuGameView extends React.Component {
case "paused": case "paused":
return <Button variant={"contained"} startIcon={<PlayArrowRoundedIcon />} color="primary" onClick={()=>{this.resumeReplay()}}>Resume</Button>; return <Button variant={"contained"} startIcon={<PlayArrowRoundedIcon />} color="primary" onClick={()=>{this.resumeReplay()}}>Resume</Button>;
case "over": case "over":
return <Button variant={"contained"} startIcon={<ReplayRoundedIcon />} color="primary" onClick={()=>{this.startReplay()}}>Resume</Button>; return <Button variant={"contained"} startIcon={<ReplayRoundedIcon />} color="primary" onClick={()=>{this.startReplay()}}>Restart</Button>;
default: default:
alert(`undefined game status: ${status}`); alert(`undefined game status: ${status}`);
} }
return ;
} }
render(){ render(){
@ -247,14 +213,14 @@ class DoudizhuGameView extends React.Component {
<div className="game-controller"> <div className="game-controller">
<Layout.Row> <Layout.Row>
<Layout.Col span="24"> <Layout.Col span="24">
<Button variant={"contained"} color="primary" onClick={()=>{this.connectWebSocket()}}>Connect</Button> {/*<Button variant={"contained"} color="primary" onClick={()=>{this.connectWebSocket()}}>Connect</Button>*/}
{ this.gameStatusButton(this.state.gameInfo.gameStatus) } { this.gameStatusButton(this.state.gameInfo.gameStatus) }
</Layout.Col> </Layout.Col>
</Layout.Row> </Layout.Row>
<Layout.Row style={{height: "31px"}}> <Layout.Row style={{height: "31px"}}>
<Layout.Col span="8" style={{height: "100%"}}> <Layout.Col span="8" style={{height: "100%"}}>
<div style={{display: "table", height: "100%"}}> <div style={{display: "table", height: "100%"}}>
<span style={{display: "table-cell", verticalAlign: "middle"}}>Consideration Time</span> <span style={{display: "table-cell", verticalAlign: "middle"}}>Game Speed</span>
</div> </div>
</Layout.Col> </Layout.Col>
<Layout.Col span="16"> <Layout.Col span="16">

View File

@ -2,7 +2,7 @@ import React from 'react';
import axios from 'axios'; import axios from 'axios';
import '../assets/gameview.scss'; import '../assets/gameview.scss';
import {LeducHoldemGameBoard} from '../components/GameBoard'; import {LeducHoldemGameBoard} from '../components/GameBoard';
import {doubleRaf, deepCopy} from "../utils"; import {deepCopy} from "../utils";
import { Button, Layout } from 'element-react'; import { Button, Layout } from 'element-react';
import Slider from '@material-ui/core/Slider'; import Slider from '@material-ui/core/Slider';
@ -10,12 +10,14 @@ import Slider from '@material-ui/core/Slider';
class LeducHoldemGameView extends React.Component { class LeducHoldemGameView extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const mainViewerId = 0; // Id of the player at the bottom of screen const mainViewerId = 0; // Id of the player at the bottom of screen
this.initConsiderationTime = 2000; this.initConsiderationTime = 2000;
this.considerationTimeDeduction = 100; this.considerationTimeDeduction = 100;
this.gameStateTimeout = null; this.gameStateTimeout = null;
this.apiUrl = window.g.apiUrl; this.apiUrl = window.g.apiUrl;
this.moveHistory = []; this.moveHistory = [];
this.initGameState = { this.initGameState = {
playerInfo: [], playerInfo: [],
hands: [], hands: [],