project init
This commit is contained in:
commit
215d09c776
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.idea
|
28
README.md
Normal file
28
README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Welcome to Colyseus!
|
||||
|
||||
This project has been created using [⚔️ `create-colyseus-app`](https://github.com/colyseus/create-colyseus-app/) - an npm init template for kick starting a Colyseus project in TypeScript.
|
||||
|
||||
[Documentation](http://docs.colyseus.io/)
|
||||
|
||||
## :crossed_swords: Usage
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
- `index.ts`: main entry point, register an empty room handler and attach [`@colyseus/monitor`](https://github.com/colyseus/colyseus-monitor)
|
||||
- `src/rooms/MyRoom.ts`: an empty room handler for you to implement your logic
|
||||
- `src/rooms/schema/MyRoomState.ts`: an empty schema used on your room's state.
|
||||
- `loadtest/example.ts`: scriptable client for the loadtest tool (see `npm run loadtest`)
|
||||
- `package.json`:
|
||||
- `scripts`:
|
||||
- `npm start`: runs `ts-node-dev index.ts`
|
||||
- `npm run loadtest`: runs the [`@colyseus/loadtest`](https://github.com/colyseus/colyseus-loadtest/) tool for testing the connection, using the `loadtest/example.ts` script.
|
||||
- `tsconfig.json`: TypeScript configuration file
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
25
loadtest/example.ts
Normal file
25
loadtest/example.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Room, Client } from "colyseus.js";
|
||||
|
||||
export function requestJoinOptions (this: Client, i: number) {
|
||||
return { requestNumber: i };
|
||||
}
|
||||
|
||||
export function onJoin(this: Room) {
|
||||
console.log(this.sessionId, "joined.");
|
||||
|
||||
this.onMessage("*", (type, message) => {
|
||||
console.log(this.sessionId, "received:", type, message);
|
||||
});
|
||||
}
|
||||
|
||||
export function onLeave(this: Room) {
|
||||
console.log(this.sessionId, "left.");
|
||||
}
|
||||
|
||||
export function onError(this: Room, err: any) {
|
||||
console.log(this.sessionId, "!! ERROR !!", err.message);
|
||||
}
|
||||
|
||||
export function onStateChange(this: Room, state: any) {
|
||||
console.log(this.sessionId, "new state:", state);
|
||||
}
|
2568
package-lock.json
generated
Normal file
2568
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
package.json
Normal file
35
package.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
"description": "npm init template for bootstrapping an empty Colyseus project",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"start": "ts-node-dev src/index.ts",
|
||||
"loadtest": "colyseus-loadtest loadtest/example.ts --room my_room --numClients 3",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "UNLICENSED",
|
||||
"bugs": {
|
||||
"url": "https://github.com/colyseus/create-colyseus/issues"
|
||||
},
|
||||
"homepage": "https://github.com/colyseus/create-colyseus#readme",
|
||||
"devDependencies": {
|
||||
"@colyseus/loadtest": "^0.14.0",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.1",
|
||||
"ts-node": "^8.1.0",
|
||||
"ts-node-dev": "^1.0.0-pre.63",
|
||||
"typescript": "^3.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@colyseus/command": "^0.1.6",
|
||||
"@colyseus/monitor": "^0.12.2",
|
||||
"@colyseus/social": "^0.10.9",
|
||||
"colyseus": "^0.14.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.16.4",
|
||||
"express-jwt": "^5.3.1"
|
||||
}
|
||||
}
|
38
src/index.ts
Normal file
38
src/index.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import http from "http";
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import { Server } from "colyseus";
|
||||
import { monitor } from "@colyseus/monitor";
|
||||
// import socialRoutes from "@colyseus/social/express"
|
||||
|
||||
import { GeneralRoom } from "./rooms/GeneralRoom";
|
||||
import {MongooseDriver} from "colyseus/lib/matchmaker/drivers/MongooseDriver";
|
||||
|
||||
const port = Number(process.env.PORT || 2567);
|
||||
const app = express()
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json())
|
||||
|
||||
const server = http.createServer(app);
|
||||
const gameServer = new Server({
|
||||
server,
|
||||
driver: new MongooseDriver(),
|
||||
});
|
||||
|
||||
// register your room handlers
|
||||
gameServer.define('general_room', GeneralRoom);
|
||||
|
||||
/**
|
||||
* Register @colyseus/social routes
|
||||
*
|
||||
* - uncomment if you want to use default authentication (https://docs.colyseus.io/server/authentication/)
|
||||
* - also uncomment the import statement
|
||||
*/
|
||||
// app.use("/", socialRoutes);
|
||||
|
||||
// register colyseus monitor AFTER registering your room handlers
|
||||
app.use("/colyseus", monitor());
|
||||
|
||||
gameServer.listen(port);
|
||||
console.log(`Listening on ws://localhost:${ port }`)
|
66
src/rooms/GeneralRoom.ts
Normal file
66
src/rooms/GeneralRoom.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Room, Client } from "colyseus";
|
||||
import { CardGameState } from "./schema/CardGameState";
|
||||
import { OnJoinCommand } from "./commands/OnJoinCommand";
|
||||
import { PlayReadyCommand} from "./commands/PlayReadyCommand";
|
||||
import { Dispatcher } from "@colyseus/command";
|
||||
import {DiscardCommand} from "./commands/DiscardCommand";
|
||||
import {NextSubCommand} from "./commands/NextSubCommand";
|
||||
import {SelectPetCommand} from "./commands/SelectPetCommand";
|
||||
|
||||
export class GeneralRoom extends Room {
|
||||
dispatcher = new Dispatcher(this);
|
||||
maxClients = 4;
|
||||
clientMap = new Map();
|
||||
|
||||
getClient(sessionId: string) {
|
||||
return this.clientMap.get(sessionId);
|
||||
}
|
||||
|
||||
onCreate (options: any) {
|
||||
this.setState(new CardGameState());
|
||||
this.state.gameSate = 0;
|
||||
this.onMessage("play_ready", (client, message) => {
|
||||
console.log('play_ready from ', client.sessionId, message);
|
||||
this.dispatcher.dispatch(new PlayReadyCommand(), {client});
|
||||
});
|
||||
|
||||
this.onMessage("discard_card", (client, message) => {
|
||||
console.log('discard_card from ', client.sessionId, message);
|
||||
this.dispatcher.dispatch(new DiscardCommand(), {client, cards: message.cards});
|
||||
});
|
||||
|
||||
this.onMessage("give_up_take", (client, message) => {
|
||||
console.log('give_up_take from ', client.sessionId, message);
|
||||
this.dispatcher.dispatch(new NextSubCommand(), {});
|
||||
});
|
||||
|
||||
this.onMessage("select_pet", (client, message) => {
|
||||
console.log('select_pet from ', client.sessionId, message);
|
||||
this.dispatcher.dispatch(new SelectPetCommand(), {client, cardId: message.cardId});
|
||||
});
|
||||
|
||||
this.onMessage("*", (client, type, message) => {
|
||||
//
|
||||
// Triggers when any other type of message is sent,
|
||||
// excluding "action", which has its own specific handler defined above.
|
||||
//
|
||||
console.log(client.sessionId, "sent", type, message);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
onJoin (client: Client, options: any) {
|
||||
this.dispatcher.dispatch(new OnJoinCommand(), {
|
||||
client: client
|
||||
});
|
||||
this.clientMap.set(client.sessionId, client);
|
||||
}
|
||||
|
||||
onLeave (client: Client, consented: boolean) {
|
||||
}
|
||||
|
||||
onDispose() {
|
||||
this.dispatcher.stop();
|
||||
}
|
||||
|
||||
}
|
9
src/rooms/commands/AsyncSequence.ts
Normal file
9
src/rooms/commands/AsyncSequence.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import {Wait} from "./Wait";
|
||||
|
||||
export class AsyncSequence extends Command {
|
||||
execute() {
|
||||
return [new Wait().setPayload(1), new Wait().setPayload(2), new Wait().setPayload(3)];
|
||||
}
|
||||
}
|
13
src/rooms/commands/ChildAsyncCommand.ts
Normal file
13
src/rooms/commands/ChildAsyncCommand.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
|
||||
export class ChildAsyncCommand extends Command<CardGameState, { i: number }> {
|
||||
async execute({ i }: {i: number}) {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
this.state.i += i;
|
||||
resolve();
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
}
|
8
src/rooms/commands/ChildCommand.ts
Normal file
8
src/rooms/commands/ChildCommand.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
|
||||
export class ChildCommand extends Command<CardGameState, { i: number }> {
|
||||
execute({ i }: {i: number}) {
|
||||
this.state.i += i;
|
||||
}
|
||||
}
|
32
src/rooms/commands/DeepAsync.ts
Normal file
32
src/rooms/commands/DeepAsync.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
|
||||
export class DeepAsync extends Command<CardGameState> {
|
||||
async execute() {
|
||||
this.state.i = 0;
|
||||
return [new DeepOneAsync(), new DeepOneAsync()];
|
||||
}
|
||||
}
|
||||
|
||||
export class DeepOneAsync extends Command<CardGameState> {
|
||||
async execute() {
|
||||
await this.delay(100);
|
||||
this.state.i += 1;
|
||||
return [new DeepTwoAsync()];
|
||||
}
|
||||
}
|
||||
|
||||
export class DeepTwoAsync extends Command<CardGameState> {
|
||||
async execute() {
|
||||
await this.delay(100);
|
||||
this.state.i += 10;
|
||||
return [new DeepThreeAsync()];
|
||||
}
|
||||
}
|
||||
|
||||
export class DeepThreeAsync extends Command<CardGameState> {
|
||||
async execute() {
|
||||
await this.delay(100);
|
||||
this.state.i += 100;
|
||||
}
|
||||
}
|
29
src/rooms/commands/DeepSync.ts
Normal file
29
src/rooms/commands/DeepSync.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
|
||||
export class DeepSync extends Command<CardGameState> {
|
||||
execute() {
|
||||
this.state.i = 0;
|
||||
return [new DeepOneSync(), new DeepOneSync()];
|
||||
}
|
||||
}
|
||||
|
||||
export class DeepOneSync extends Command<CardGameState> {
|
||||
execute() {
|
||||
this.state.i += 1;
|
||||
return [new DeepTwoSync()];
|
||||
}
|
||||
}
|
||||
|
||||
export class DeepTwoSync extends Command<CardGameState> {
|
||||
execute() {
|
||||
this.state.i += 10;
|
||||
return [new DeepThreeSync()];
|
||||
}
|
||||
}
|
||||
|
||||
export class DeepThreeSync extends Command<CardGameState> {
|
||||
execute() {
|
||||
this.state.i += 100;
|
||||
}
|
||||
}
|
46
src/rooms/commands/DiscardCommand.ts
Normal file
46
src/rooms/commands/DiscardCommand.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import gameUtil from "../../utils/game.util";
|
||||
import {Client} from "colyseus";
|
||||
import {NextTurnCommand} from "./NextTurnCommand";
|
||||
import {NextSubCommand} from "./NextSubCommand";
|
||||
|
||||
/**
|
||||
* 出牌
|
||||
*/
|
||||
export class DiscardCommand extends Command<CardGameState, { client: Client, cards: [string] }> {
|
||||
validate({ client, cards } = this.payload) {
|
||||
const player = this.state.players.get(client.sessionId);
|
||||
return player !== undefined && gameUtil.checkCardsExists(player.cards, cards);
|
||||
}
|
||||
|
||||
execute({ client, cards } = this.payload) {
|
||||
this.state.cards.clear();
|
||||
for (let id of cards) {
|
||||
this.state.cards.set(id, this.state.players.get(client.sessionId).cards.get(id));
|
||||
this.state.players.get(client.sessionId).cards.delete(id);
|
||||
this.state.players.get(client.sessionId).cardSet.delete(id);
|
||||
}
|
||||
if (this.state.currentTurn == client.sessionId) {
|
||||
/**
|
||||
* 这说明是当前轮正常出牌,
|
||||
* 如果出一张牌的话, 进入胡牌轮
|
||||
* 否则直接进入选随从轮
|
||||
*/
|
||||
|
||||
if (cards.length === 1) {
|
||||
return [new NextSubCommand()];
|
||||
} else {
|
||||
this.state.gameState = 4;
|
||||
// return [new NextTurnCommand()];
|
||||
}
|
||||
|
||||
} else {
|
||||
/**
|
||||
* 出牌id和当前轮id不一直, 说明是是吃牌轮, 直接轮到下一位
|
||||
*/
|
||||
return [new NextTurnCommand()];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
24
src/rooms/commands/DrawCommand.ts
Normal file
24
src/rooms/commands/DrawCommand.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
|
||||
export class DrawCommand extends Command<CardGameState, {}> {
|
||||
execute() {
|
||||
let sessionId = this.state.currentTurn;
|
||||
let curPlayer = this.state.players.get(sessionId);
|
||||
let curClient;
|
||||
for (let client of this.room.clients) {
|
||||
if (client.sessionId === sessionId) {
|
||||
curClient = client;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let cards = [];
|
||||
for (let i = 0; i < 2; i ++) {
|
||||
let card = this.state.cardQueue.pop();
|
||||
cards.push(card);
|
||||
curPlayer.cards.set(card.id, card);
|
||||
curPlayer.cardSet.add(card.id);
|
||||
}
|
||||
curClient.send('draw_card', cards);
|
||||
}
|
||||
}
|
12
src/rooms/commands/EnqueueAsyncCommand.ts
Normal file
12
src/rooms/commands/EnqueueAsyncCommand.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import {ChildAsyncCommand} from "./ChildAsyncCommand";
|
||||
|
||||
export class EnqueueAsyncCommand extends Command<CardGameState, { count: number }> {
|
||||
async execute({ count }: {count: number}) {
|
||||
this.state.i = 0;
|
||||
|
||||
return [...Array(count)].map(_ =>
|
||||
new ChildAsyncCommand().setPayload({ i: count }));
|
||||
}
|
||||
}
|
13
src/rooms/commands/EnqueueCommand.ts
Normal file
13
src/rooms/commands/EnqueueCommand.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import {Card} from "../schema/Card";
|
||||
import {ChildCommand} from "./ChildCommand";
|
||||
|
||||
export class EnqueueCommand extends Command<CardGameState, { count: number }> {
|
||||
execute({ count }: {count: number}) {
|
||||
this.state.i = 0;
|
||||
|
||||
return [...Array(count)].map(_ =>
|
||||
new ChildCommand().setPayload({ i: count }));
|
||||
}
|
||||
}
|
20
src/rooms/commands/NextSubCommand.ts
Normal file
20
src/rooms/commands/NextSubCommand.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import {NextTurnCommand} from "./NextTurnCommand";
|
||||
|
||||
export class NextSubCommand extends Command<CardGameState, {}> {
|
||||
|
||||
execute() {
|
||||
this.state.gameState = 3;
|
||||
const sessionIds = [...this.state.players.keys()];
|
||||
let nextSubTurn = this.state.subTurn ?
|
||||
sessionIds[(sessionIds.indexOf(this.state.subTurn) + 1) % sessionIds.length]
|
||||
: sessionIds[(sessionIds.indexOf(this.state.currentTurn) + 1) % sessionIds.length];
|
||||
if (nextSubTurn !== this.state.currentTurn) {
|
||||
this.state.subTurn = nextSubTurn;
|
||||
} else {
|
||||
return [new NextTurnCommand()];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
17
src/rooms/commands/NextTurnCommand.ts
Normal file
17
src/rooms/commands/NextTurnCommand.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import {DrawCommand} from "./DrawCommand";
|
||||
|
||||
export class NextTurnCommand extends Command<CardGameState, {}> {
|
||||
|
||||
execute() {
|
||||
this.state.gameState = 2;
|
||||
this.state.subTurn = '';
|
||||
const sessionIds = [...this.state.players.keys()];
|
||||
this.state.currentTurn = (this.state.currentTurn)
|
||||
? sessionIds[(sessionIds.indexOf(this.state.currentTurn) + 1) % sessionIds.length]
|
||||
: sessionIds[0];
|
||||
return [new DrawCommand()]
|
||||
}
|
||||
|
||||
}
|
19
src/rooms/commands/OnJoinCommand.ts
Normal file
19
src/rooms/commands/OnJoinCommand.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Command} from "@colyseus/command";
|
||||
import {CardGameState} from "../schema/CardGameState";
|
||||
import {Player} from "../schema/Player";
|
||||
import {Client} from "colyseus";
|
||||
|
||||
export class OnJoinCommand extends Command<CardGameState, {
|
||||
client: Client
|
||||
}> {
|
||||
|
||||
execute({client}: { client: Client }) {
|
||||
this.state.players.set(client.sessionId, new Player());
|
||||
if (this.state.players.size >= this.room.maxClients) {
|
||||
this.room.lock();
|
||||
this.state.gameState = 1;
|
||||
}
|
||||
this.room.broadcast("player_join", `${client.sessionId}`, {except: client});
|
||||
}
|
||||
|
||||
}
|
51
src/rooms/commands/PlayReadyCommand.ts
Normal file
51
src/rooms/commands/PlayReadyCommand.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import {Client} from "colyseus";
|
||||
import gameUtil from "../../utils/game.util";
|
||||
import {GeneralRoom} from "../GeneralRoom";
|
||||
|
||||
export class PlayReadyCommand extends Command<CardGameState, {
|
||||
client: Client
|
||||
}> {
|
||||
|
||||
execute({ client } : {client: Client}) {
|
||||
this.state.players.get(client.sessionId).state = 1;
|
||||
this.room.broadcast("player_ready", {player: client.sessionId}, {except: client});
|
||||
let readyCount = 0;
|
||||
for (let [sessionId, player] of this.state.players) {
|
||||
if (player.state === 1) {
|
||||
readyCount ++;
|
||||
}
|
||||
}
|
||||
// 如果所有人的状态都为已准备状态, 则开始发牌
|
||||
if (readyCount >= this.room.maxClients) {
|
||||
this.state.cardQueue = gameUtil.initCardQue();
|
||||
for (let client of this.room.clients) {
|
||||
let player = this.state.players.get(client.sessionId);
|
||||
let cards = [];
|
||||
for (let i = 0; i < 6; i ++) {
|
||||
let card = this.state.cardQueue.pop();
|
||||
cards.push(card);
|
||||
player.cards.set(card.id, card);
|
||||
player.cardSet.add(card.id);
|
||||
}
|
||||
client.send('draw_card', cards);
|
||||
}
|
||||
let curClient = this.room.clients[0];
|
||||
this.state.currentTurn = curClient.sessionId;
|
||||
this.state.subTurn = '';
|
||||
let cards = [];
|
||||
let curPlayer = this.state.players.get(curClient.sessionId);
|
||||
for (let i = 0; i < 2; i ++) {
|
||||
let card = this.state.cardQueue.pop();
|
||||
cards.push(card);
|
||||
curPlayer.cards.set(card.id, card);
|
||||
curPlayer.cardSet.add(card.id);
|
||||
}
|
||||
curClient.send('draw_card', cards);
|
||||
this.state.gameState = 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
29
src/rooms/commands/SelectPetCommand.ts
Normal file
29
src/rooms/commands/SelectPetCommand.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
import {Client} from "colyseus";
|
||||
import {NextTurnCommand} from "./NextTurnCommand";
|
||||
|
||||
export class SelectPetCommand extends Command<CardGameState, {client: Client, cardId: string}> {
|
||||
execute({client, cardId}: {client: Client, cardId: string}) {
|
||||
let sessionId = this.state.currentTurn;
|
||||
let player = this.state.players.get(sessionId);
|
||||
let ap = 0;
|
||||
let moreAp = 0;
|
||||
let count = 0;
|
||||
for (let card of this.state.cards.values()) {
|
||||
ap += card.number;
|
||||
count ++;
|
||||
if (count > 2) {
|
||||
moreAp += 10;
|
||||
}
|
||||
}
|
||||
for (let [key, pet] of player.pets) {
|
||||
if (pet.ap == 0 ) {
|
||||
pet.ap = ap + moreAp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [new NextTurnCommand()];
|
||||
|
||||
}
|
||||
}
|
12
src/rooms/commands/ValidationCommand.ts
Normal file
12
src/rooms/commands/ValidationCommand.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
import { CardGameState } from "../schema/CardGameState";
|
||||
|
||||
export class ValidationCommand extends Command<CardGameState, number> {
|
||||
validate(n = this.payload) {
|
||||
return n === 1;
|
||||
}
|
||||
|
||||
execute() {
|
||||
throw new Error("This should never execute!")
|
||||
}
|
||||
}
|
7
src/rooms/commands/Wait.ts
Normal file
7
src/rooms/commands/Wait.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Command } from "@colyseus/command";
|
||||
|
||||
export class Wait extends Command<any, number> {
|
||||
async execute(number: number) {
|
||||
await this.delay(100);
|
||||
}
|
||||
}
|
38
src/rooms/schema/Card.ts
Normal file
38
src/rooms/schema/Card.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import {Schema, type, filter} from "@colyseus/schema";
|
||||
import { Client } from "colyseus";
|
||||
|
||||
export class Card extends Schema {
|
||||
|
||||
constructor(number: number, type: string, id: string) {
|
||||
super();
|
||||
this.number = number;
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.played = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 点数
|
||||
*/
|
||||
// @filter(function(
|
||||
// this: Card, // the instance of the class `@filter` has been defined (instance of `Card`)
|
||||
// client: Client, // the Room's `client` instance which this data is going to be filtered to
|
||||
// value: Card['number'], // the value of the field to be filtered. (value of `number` field)
|
||||
// root: Schema // the root state Schema instance
|
||||
// ) {
|
||||
// return !this.played || this.owner === client.sessionId;
|
||||
// })
|
||||
@type("number")
|
||||
number: number;
|
||||
@type("string")
|
||||
owner: string;
|
||||
@type("string")
|
||||
id: string;
|
||||
@type("boolean")
|
||||
played: boolean = false;
|
||||
/**
|
||||
* 种类
|
||||
*/
|
||||
@type("string")
|
||||
type: string;
|
||||
}
|
43
src/rooms/schema/CardGameState.ts
Normal file
43
src/rooms/schema/CardGameState.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { Schema, MapSchema, type } from "@colyseus/schema";
|
||||
import {Player} from "./Player";
|
||||
import {Card} from "./Card";
|
||||
|
||||
|
||||
export class CardGameState extends Schema {
|
||||
|
||||
@type({ map: Player })
|
||||
players = new MapSchema<Player>();
|
||||
/**
|
||||
* 游戏装备
|
||||
* 0: 等待玩家加入
|
||||
* 1: 等待玩家准备
|
||||
* 2: 游戏进行中-出牌轮
|
||||
* 3: 游戏进行中-吃碰轮
|
||||
* 4: 游戏中吃牌后的选随从轮
|
||||
* 9: 游戏结束
|
||||
*/
|
||||
@type("number")
|
||||
gameState: number = 0;
|
||||
|
||||
@type("string")
|
||||
currentTurn: string;
|
||||
/**
|
||||
* 用于吃牌时的计轮, 只有在gameState==3的时候才需要判断
|
||||
*/
|
||||
@type("string")
|
||||
subTurn: string;
|
||||
|
||||
@type("number")
|
||||
i: number;
|
||||
/**
|
||||
* 当局游戏卡组队列
|
||||
*/
|
||||
cardQueue: Card[] = [];
|
||||
|
||||
/**
|
||||
* 上轮玩家出的牌
|
||||
*/
|
||||
@type({map: Card})
|
||||
cards = new MapSchema<Card>() ;
|
||||
|
||||
}
|
22
src/rooms/schema/Pet.ts
Normal file
22
src/rooms/schema/Pet.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {Schema, type} from "@colyseus/schema";
|
||||
|
||||
export class Pet extends Schema {
|
||||
/**
|
||||
* 当前点数
|
||||
*/
|
||||
@type("number")
|
||||
ap: number;
|
||||
|
||||
/**
|
||||
* 种类
|
||||
*/
|
||||
@type("string")
|
||||
type: string;
|
||||
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ap = 0;
|
||||
this.type = '';
|
||||
}
|
||||
}
|
49
src/rooms/schema/Player.ts
Normal file
49
src/rooms/schema/Player.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {MapSchema, SetSchema, Schema, type} from "@colyseus/schema";
|
||||
import {Card} from "./Card";
|
||||
import {Pet} from "./Pet";
|
||||
|
||||
export class Player extends Schema {
|
||||
/**
|
||||
* 手牌
|
||||
*/
|
||||
cards = new MapSchema<Card>();
|
||||
|
||||
@type({ set: "string" })
|
||||
cardSet = new SetSchema<string>();
|
||||
/**
|
||||
* 当前hp
|
||||
*/
|
||||
@type("number")
|
||||
hp: number;
|
||||
/**
|
||||
* 当前点数
|
||||
*/
|
||||
@type("number")
|
||||
ap: number;
|
||||
/**
|
||||
* 状态
|
||||
* 0: 正常状态
|
||||
* 1: 已准备好
|
||||
* 2: 死亡
|
||||
* 9: 掉线
|
||||
*/
|
||||
@type("number")
|
||||
state: number;
|
||||
/**
|
||||
* 随从
|
||||
*/
|
||||
@type({ map: Pet })
|
||||
pets = new MapSchema<Pet>();
|
||||
|
||||
|
||||
//TODO: set hp, ap from cfg
|
||||
constructor() {
|
||||
super();
|
||||
this.state = 0;
|
||||
this.hp = 200;
|
||||
this.ap = 30;
|
||||
for (let i = 0; i < 6; i++) {
|
||||
this.pets.set(i+'', new Pet());
|
||||
}
|
||||
}
|
||||
}
|
77
src/utils/array.util.ts
Normal file
77
src/utils/array.util.ts
Normal file
@ -0,0 +1,77 @@
|
||||
let arrUtil = {
|
||||
/**
|
||||
* 随机排序
|
||||
* @param arr
|
||||
*/
|
||||
randomSort<T>(arr: Array<T>) {
|
||||
for (let j, x, i = arr.length; i; j = (Math.random() * i) | 0, x = arr[--i], arr[i] = arr[j], arr[j] = x) {
|
||||
;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 检查数组中是否含有另外一个object
|
||||
* @param arr 目标数组
|
||||
* @param obj 与数组同类型的obj | 同类型的数组 | 指定child字段的值 | 指定child字段的数组
|
||||
* @param child 比较字段
|
||||
*/
|
||||
contains<T>(arr: Array<T>, obj: any, child: string): boolean {
|
||||
let result = false;
|
||||
if (child) {
|
||||
const isArr = Array.isArray(obj);
|
||||
if (isArr) {
|
||||
if (obj[0].hasOwnProperty(child)) {
|
||||
let set0 = new Set();
|
||||
for (let s of obj) {
|
||||
set0.add(s[child]);
|
||||
}
|
||||
let set1 = new Set(arr.filter (x => set0.has(x)));
|
||||
return set0.size === set1.size;
|
||||
} else {
|
||||
let set0 = new Set(obj);
|
||||
let set1 = new Set(arr.filter (x => set0.has(x)));
|
||||
return set1.size === set0.size;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (obj.hasOwnProperty(child)) {
|
||||
for (let sub of arr) {
|
||||
if (sub.hasOwnProperty(child)) {
|
||||
// @ts-ignore
|
||||
if (sub[child] === obj[child]) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let sub of arr) {
|
||||
if (sub.hasOwnProperty(child)) {
|
||||
// @ts-ignore
|
||||
if (sub[child] === obj) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// 不指定 比较字段 的话, 只处理2种情况
|
||||
// 1: obj 为数组
|
||||
// 2: obj 不是数组
|
||||
if (Array.isArray(obj)) {
|
||||
let set0 = new Set(obj);
|
||||
let set1 = new Set(arr.filter (x => set0.has(x)));
|
||||
return set1.size === set0.size;
|
||||
} else {
|
||||
let idx = arr.indexOf(obj);
|
||||
return !!(~idx);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default arrUtil;
|
41
src/utils/game.util.ts
Normal file
41
src/utils/game.util.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import {Card} from "../rooms/schema/Card";
|
||||
|
||||
import arrUtil from "./array.util";
|
||||
|
||||
let gameUtil = {
|
||||
// TODO: 根据配表生成牌组
|
||||
initCardQue() {
|
||||
let cards: Array<Card> = [];
|
||||
let nums: Array<number> = [];
|
||||
let types : Array<number> = [];
|
||||
for (let i = 0; i < 12; i ++) {
|
||||
for (let j = 0; j < 16; j ++) {
|
||||
nums.push(i);
|
||||
}
|
||||
}
|
||||
arrUtil.randomSort(nums);
|
||||
for (let i = 0; i < 8; i ++) {
|
||||
for (let j = 0; j < 24; j ++) {
|
||||
types.push(i);
|
||||
}
|
||||
}
|
||||
arrUtil.randomSort(types);
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
cards.push(new Card(nums[i] + 1, types[i] + '', i + ''));
|
||||
}
|
||||
arrUtil.randomSort(cards);
|
||||
return cards;
|
||||
},
|
||||
|
||||
checkCardsExists(cardMap: Map<string, Card>, cardIds: Array<string>) {
|
||||
let result = true;
|
||||
for (let id of cardIds) {
|
||||
if (!cardMap.has(id)) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
export default gameUtil;
|
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"strictNullChecks": false,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user