Skip to content

Commit 4c82bad

Browse files
committed
converted monopoly to use @prisel/state
1 parent c0e2528 commit 4c82bad

20 files changed

Lines changed: 547 additions & 583 deletions

packages/common/src/tag.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
export function newTag<TargetT extends object, MetadataT>(
22
name: string,
3-
): [(target: TargetT, value: MetadataT) => void, (target: TargetT) => MetadataT | undefined] {
3+
): [
4+
(target: TargetT, value: MetadataT) => void,
5+
(target: TargetT | undefined | null) => MetadataT | undefined,
6+
] {
47
const registry = new WeakMap<TargetT, MetadataT>();
58
return [
69
(target, value) => {
710
registry.set(target, value);
811
},
9-
registry.get.bind(registry),
12+
(target) => {
13+
if (target == undefined) {
14+
return undefined;
15+
}
16+
return registry.get(target);
17+
},
1018
];
1119
}

packages/monopoly/server.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// run server in production using node
2-
import { Server } from '@prisel/server';
2+
import { RoomType, Server } from '@prisel/server';
33
import { readFileSync } from 'fs';
44
import './src/flags';
5-
import Monopoly from './src/gameConfig';
6-
import RoomConfig from './src/roomConfig';
5+
import { GameState } from './src/gameState';
76

87
const DEFAULT_HOST = '0.0.0.0';
98
const DEFAULT_PORT = '80';
@@ -14,8 +13,11 @@ const host: string = HOST_FILE ? readFileSync(HOST_FILE).toString() : HOST;
1413
const port: number = parseInt(PORT_FILE ? readFileSync(PORT_FILE).toString() : PORT, 10);
1514

1615
Server.create({
17-
gameConfig: Monopoly,
18-
roomConfig: RoomConfig,
16+
onCreateGame:
17+
({ turnOrder }) =>
18+
() =>
19+
GameState({ turnOrder }),
20+
roomType: RoomType.MULTI,
1921
host,
2022
port,
2123
});

packages/monopoly/src/Game.ts

Lines changed: 16 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,41 @@
1-
import { exist, GamePlayer, Id, World } from '@prisel/monopoly-common';
2-
import { assertExist, broadcast, Packet, Player, PlayerId, Room } from '@prisel/server';
3-
import { Emitter, Inspector } from '@prisel/state';
1+
import { GamePlayer, Id, World } from '@prisel/monopoly-common';
2+
import { assertExist, TurnOrder } from '@prisel/server';
3+
import { getGamePlayer } from './stateMachine/utils';
44

55
interface Props {
66
id: string;
7-
players: Map<PlayerId, GamePlayer>;
8-
getGamePlayerByPlayer: (player: Player) => GamePlayer | undefined;
9-
turnOrder: GamePlayer[];
10-
room: Room;
7+
turnOrder: TurnOrder;
118
world: World;
129
}
1310

1411
export default class Game {
1512
public id: string = '';
16-
// map of Id<GamePlayer>, GamePlayer
17-
public players: Map<string, GamePlayer> = new Map();
18-
public turnOrder: GamePlayer[] = [];
19-
private _room?: Room;
20-
public get room() {
21-
return assertExist(this._room);
22-
}
23-
24-
public inspector?: Inspector;
25-
public playerLeftEmitter?: Emitter<GamePlayer>;
26-
13+
public turnOrder?: TurnOrder;
2714
private _world?: World;
2815
public get world() {
2916
return assertExist(this._world);
3017
}
3118

32-
private getGamePlayerByPlayer: Props['getGamePlayerByPlayer'] = () => undefined;
33-
3419
public init(props: Props) {
3520
this.id = props.id;
36-
this.players = props.players;
3721
this.turnOrder = props.turnOrder;
38-
this._room = props.room;
3922
this._world = props.world;
40-
this.getGamePlayerByPlayer = props.getGamePlayerByPlayer;
4123
return this;
4224
}
4325

44-
public giveTurnToNext(): void {
45-
this.turnOrder = [...this.turnOrder.slice(1), this.turnOrder[0]];
46-
}
47-
48-
public getNextPlayer(): GamePlayer {
49-
return [...this.turnOrder.slice(1), this.turnOrder[0]][0];
50-
}
51-
52-
public isCurrentPlayer(player: GamePlayer) {
53-
const currentPlayerId = this.turnOrder[0].id;
54-
const playerId = player.getGamePlayerInfo().id;
55-
return exist(currentPlayerId) && currentPlayerId === playerId;
56-
}
57-
58-
public getCurrentPlayer(): GamePlayer {
59-
return this.turnOrder[0];
60-
}
61-
62-
public getGamePlayer(player: Player): GamePlayer | undefined {
63-
return this.getGamePlayerByPlayer(player);
64-
}
65-
26+
/**
27+
* Returns the {@link GamePlayer} by searching using the id. If player
28+
* already leave the room, this will return undefined.
29+
* @param id Id of {@link GamePlayer}.
30+
* @returns
31+
*/
6632
public getGamePlayerById(id: Id<GamePlayer>): GamePlayer | undefined {
67-
return this.players.get(id);
68-
}
69-
70-
public broadcast(packetBuilder: ((player: GamePlayer) => Packet | undefined) | Packet) {
71-
broadcast(this.room.getPlayers(), (player) => {
72-
const gamePlayer = this.getGamePlayer(player);
73-
if (gamePlayer) {
74-
return typeof packetBuilder === 'function'
75-
? packetBuilder(gamePlayer)
76-
: packetBuilder;
33+
for (const player of this.turnOrder?.getAllPlayers() || []) {
34+
const gamePlayer = getGamePlayer(player);
35+
if (gamePlayer?.id === id) {
36+
return gamePlayer;
7737
}
78-
});
38+
}
7939
}
8040
}
8141

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { ChanceInput, ChanceInputArgs } from '@prisel/monopoly-common';
2+
import { TurnOrder } from '@prisel/server';
23
import { StateConfig, StateFunc } from '@prisel/state';
34

45
export type ChanceHandler<T extends keyof ChanceInputArgs> = StateFunc<{
56
input: ChanceInput<T>;
67
setNextState: (nextState: StateConfig<any>) => void;
8+
turnOrder: TurnOrder;
79
}>;
Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { Action, Anim, animationMap, ChanceInput } from '@prisel/monopoly-common';
22
import { monopolypb } from '@prisel/protos';
3-
import { Packet } from '@prisel/server';
3+
import { broadcast, Packet, TurnOrder } from '@prisel/server';
44
import { endState, newState, StateConfig } from '@prisel/state';
5-
import { AnimatingAllPlayers, getCurrentPlayer, getGame } from '../stateMachine/utils';
5+
import { getGamePlayer, PlayingAnimation } from '../stateMachine/utils';
66

77
export function* collectibleHandler(props: {
88
input: ChanceInput<'collectible'>;
99
setNextState: (nextState: StateConfig<any>) => void;
10+
turnOrder: TurnOrder;
1011
}) {
11-
const currentPlayer = getCurrentPlayer();
12-
const game = getGame();
13-
const { input, setNextState } = props;
12+
const { turnOrder, input } = props;
13+
const currentPlayer = getGamePlayer(turnOrder.getCurrentPlayer());
14+
if (!currentPlayer) {
15+
return endState();
16+
}
1417
const inputArgs = input.inputArgs;
1518
currentPlayer.addCollectible(input);
16-
game.broadcast(
19+
broadcast(
20+
turnOrder.getAllPlayers(),
1721
Packet.forAction(Action.ANNOUNCE_CHANCE)
1822
.setPayload(monopolypb.AnnounceRecievedChancePayload, {
1923
player: currentPlayer.id,
@@ -30,15 +34,15 @@ export function* collectibleHandler(props: {
3034
.build(),
3135
);
3236

33-
yield newState(
34-
AnimatingAllPlayers,
35-
Anim.create('player_emotion', monopolypb.PlayerEmotionExtra)
37+
yield newState(PlayingAnimation, {
38+
animation: Anim.create('player_emotion', monopolypb.PlayerEmotionExtra)
3639
.setExtra({
3740
player: currentPlayer.getGamePlayerInfo(),
3841
emotion: monopolypb.PlayerEmotionExtra_EmotionType.CHEER,
3942
})
4043
.setLength(animationMap.player_emotion),
41-
);
44+
turnOrder,
45+
});
4246

4347
return endState();
4448
}

packages/monopoly/src/chanceHandlers/MoneyExchangeHandler.ts

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@ import {
33
Anim,
44
animationMap,
55
ChanceInput,
6-
GamePlayer,
76
MoneyExchangeDirection,
87
} from '@prisel/monopoly-common';
98
import { monopolypb } from '@prisel/protos';
10-
import { Packet } from '@prisel/server';
9+
import { broadcast, Packet, TurnOrder } from '@prisel/server';
1110
import { endState, newState, StateConfig } from '@prisel/state';
1211
import { log } from '../log';
13-
import { AnimatingAllPlayers, getCurrentPlayer, getGame } from '../stateMachine/utils';
12+
import { getGamePlayer, PlayingAnimation } from '../stateMachine/utils';
1413

1514
export function* moneyExchangeHandler(props: {
1615
input: ChanceInput<'money_exchange'>;
1716
setNextState: (nextState: StateConfig<any>) => void;
17+
turnOrder: TurnOrder;
1818
}) {
19-
const game = getGame();
20-
const currentPlayer = getCurrentPlayer();
21-
const { input, setNextState } = props;
19+
const { input, turnOrder } = props;
20+
const currentPlayer = getGamePlayer(turnOrder.getCurrentPlayer());
21+
if (!currentPlayer) {
22+
return endState();
23+
}
2224
const inputArgs = input.inputArgs;
2325
const exchanges: {
2426
[playerId: string]: number;
@@ -39,22 +41,28 @@ export function* moneyExchangeHandler(props: {
3941
case MoneyExchangeDirection.FROM_ALL_OTHER_PLAYERS:
4042
// TODO: take account of lost players if we allow them to stay in
4143
// game
42-
const totalGainedMoney = inputArgs.amount * Array.from(game.players.keys()).length;
44+
const totalGainedMoney = inputArgs.amount * turnOrder.size;
4345
currentPlayer.gainMoney(totalGainedMoney);
4446
exchanges[currentPlayer.id] = totalGainedMoney;
45-
game.players.forEach((gamePlayer) => {
46-
gamePlayer.gainMoney(-inputArgs.amount);
47-
exchanges[gamePlayer.id] = -inputArgs.amount;
47+
turnOrder.getAllPlayers().forEach((player) => {
48+
const gamePlayer = getGamePlayer(player);
49+
if (gamePlayer) {
50+
gamePlayer.gainMoney(-inputArgs.amount);
51+
exchanges[gamePlayer.id] = -inputArgs.amount;
52+
}
4853
});
4954
playerEmotion = monopolypb.PlayerEmotionExtra_EmotionType.CHEER;
5055
break;
5156
case MoneyExchangeDirection.TO_ALL_OTHER_PLAYERS:
52-
const totalLostMoney = inputArgs.amount * Array.from(game.players.keys()).length;
57+
const totalLostMoney = inputArgs.amount * turnOrder.size;
5358
currentPlayer.gainMoney(-totalLostMoney);
5459
exchanges[currentPlayer.id] = -totalLostMoney;
55-
game.players.forEach((gamePlayer) => {
56-
gamePlayer.gainMoney(inputArgs.amount);
57-
exchanges[gamePlayer.id] = inputArgs.amount;
60+
turnOrder.getAllPlayers().forEach((player) => {
61+
const gamePlayer = getGamePlayer(player);
62+
if (gamePlayer) {
63+
gamePlayer.gainMoney(inputArgs.amount);
64+
exchanges[gamePlayer.id] = inputArgs.amount;
65+
}
5866
});
5967
playerEmotion = monopolypb.PlayerEmotionExtra_EmotionType.ANGRY;
6068
break;
@@ -63,8 +71,12 @@ export function* moneyExchangeHandler(props: {
6371
log.error('CashExchangeHandler cannot handle cash_exchange Chance with no direction');
6472
return endState();
6573
}
66-
game.broadcast((player: GamePlayer) =>
67-
Packet.forAction(Action.ANNOUNCE_CHANCE)
74+
broadcast(turnOrder.getAllPlayers(), (player) => {
75+
const gamePlayer = getGamePlayer(player);
76+
if (!gamePlayer) {
77+
return;
78+
}
79+
return Packet.forAction(Action.ANNOUNCE_CHANCE)
6880
.setPayload(monopolypb.AnnounceRecievedChancePayload, {
6981
player: currentPlayer.id,
7082
chance: {
@@ -73,23 +85,23 @@ export function* moneyExchangeHandler(props: {
7385
oneofKind: 'moneyExchange',
7486
moneyExchange: {
7587
exchanges,
76-
myCurrentMoney: player.money,
88+
myCurrentMoney: gamePlayer.money,
7789
},
7890
},
7991
},
8092
})
81-
.build(),
82-
);
93+
.build();
94+
});
8395

84-
yield newState(
85-
AnimatingAllPlayers,
86-
Anim.create('player_emotion', monopolypb.PlayerEmotionExtra)
96+
yield newState(PlayingAnimation, {
97+
animation: Anim.create('player_emotion', monopolypb.PlayerEmotionExtra)
8798
.setExtra({
8899
player: currentPlayer.getGamePlayerInfo(),
89100
emotion: playerEmotion,
90101
})
91102
.setLength(animationMap.player_emotion),
92-
);
103+
turnOrder,
104+
});
93105

94106
return endState();
95107
}

packages/monopoly/src/chanceHandlers/MoveStepsHandler.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { exist, Tile } from '@prisel/monopoly-common';
22
import { endState, newState } from '@prisel/state';
33
import { MovingState } from '../stateMachine/Moving';
4-
import { getCurrentPlayer } from '../stateMachine/utils';
4+
import { getGamePlayer } from '../stateMachine/utils';
55
import { ChanceHandler } from './ChanceHandler';
66

77
export const moveStepsHandler: ChanceHandler<'move_steps'> = (props) => {
8-
const { input, setNextState } = props;
9-
const currentPlayer = getCurrentPlayer();
8+
const { input, setNextState, turnOrder } = props;
9+
const currentPlayer = getGamePlayer(turnOrder.getCurrentPlayer());
10+
if (!currentPlayer) {
11+
return endState();
12+
}
1013
const inputArgs = input.inputArgs;
1114
const currentTile = currentPlayer.pathTile?.get();
1215
if (!exist(currentTile)) {
@@ -19,7 +22,7 @@ export const moveStepsHandler: ChanceHandler<'move_steps'> = (props) => {
1922
if (inputArgs.steps < 0) {
2023
path = currentTile.genPathReverse(-inputArgs.steps);
2124
}
22-
setNextState(newState(MovingState, { type: 'usingTiles', tiles: path }));
25+
setNextState(newState(MovingState, { type: 'usingTiles', tiles: path, turnOrder }));
2326

2427
return endState();
2528
};

packages/monopoly/src/chanceHandlers/MoveToTileHandler.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ import { exist, Tile } from '@prisel/monopoly-common';
22
import { endState, newState } from '@prisel/state';
33
import assert from 'assert';
44
import { MovingState } from '../stateMachine/Moving';
5-
import { getCurrentPlayer, getGame } from '../stateMachine/utils';
5+
import { getGame, getGamePlayer } from '../stateMachine/utils';
66
import { getRand } from '../utils';
77
import { ChanceHandler } from './ChanceHandler';
88

99
const MAX_PATH_LENGTH = 1000;
1010

1111
export const moveToTileHandler: ChanceHandler<'move_to_tile'> = (props) => {
12-
const currentPlayer = getCurrentPlayer();
12+
const { input, setNextState, turnOrder } = props;
13+
const currentPlayer = getGamePlayer(turnOrder.getCurrentPlayer());
14+
if (!currentPlayer) {
15+
return endState();
16+
}
1317
const game = getGame();
1418
const currentTile = currentPlayer.pathTile?.get();
15-
const { input, setNextState } = props;
1619
if (!exist(currentTile)) {
1720
return endState();
1821
}
@@ -24,7 +27,9 @@ export const moveToTileHandler: ChanceHandler<'move_to_tile'> = (props) => {
2427
);
2528

2629
if (input.inputArgs.isTeleport) {
27-
setNextState(newState(MovingState, { type: 'usingTeleport', target: targetTile }));
30+
setNextState(
31+
newState(MovingState, { type: 'usingTeleport', target: targetTile, turnOrder }),
32+
);
2833
return endState();
2934
}
3035

@@ -36,7 +41,7 @@ export const moveToTileHandler: ChanceHandler<'move_to_tile'> = (props) => {
3641
});
3742

3843
if (path.length !== 0) {
39-
setNextState(newState(MovingState, { type: 'usingTiles', tiles: path }));
44+
setNextState(newState(MovingState, { type: 'usingTiles', tiles: path, turnOrder }));
4045
return endState();
4146
}
4247
};

0 commit comments

Comments
 (0)