-
Notifications
You must be signed in to change notification settings - Fork 2
Implementation Document
This document is outdated
- Socket.io websocket implementation in nodeJS
Not decided.
- an array of size n x m
- Each element in the array represent a tile
- Each road has reference to the next roads, each property has reference to the connected properties. Each road tile has reference to the property tie to this road. (roads cannot be properties)
- Tiles can be accessed using the index or coordinates, each tile also stores its own index and coordinates
- Starting position
- Properties has base price
- Current player
- Game state (started, finished)
- Player has location in the board (x, y)
- Player has direction that he is traveling
- Player has last path travelled, denoted in an array of coordinates [(x1, y1), (x2, y2), (x3, y3)]
- Player has cash value
- Player has index, player can be accessed using index, player also stores its own index
- Property has rent, initial rent is 0
- Property has ownership, denoted by player’s index
Client and server communicate through messages. We follow the same format for messages. Each message contains two parts: type and data
type: String
data: Object
For example:
socket.emit('ACTION_TYPE', { response: 'yes'});
Client login
type: LOGIN
data: {username: 'zebra'}
// server will send back an clientId
Client reconnect
type: RECONNECT
data: {userId: '123'}
Client request join room
type: JOIN
data: {
roomId: ‘unique id’
}
Client leaving room
type: LEAVE
data: {}
Client ready
type: READY
data: {}
Host start
type: START
data: {}
Host kick another client
type: KICK
data: { userId: '12345' }
// userId is another user's id
Client create room
type: CREATE_ROOM
data: {roomName: 'desert'}
// server send back room id
Client chat
type: CHAT
data: {message: 'Hello'}
// publicly visible in the current room
Server sends message back to client upon client request
Client login
type: LOGIN_ACCEPT
data: {}
Client request join room
type: JOIN_ACCEPT
data: {}
type: JOIN_ERROR
data: {
errorType: 'ROOM_NOT_FOUND' or ...
}
Send room information to client
// server will send this immediately to client when they join.
// Also when there is any update
type: ROOM_STATE
data: {...}
Client leaving room
type: LEAVE_ACCEPT
data: {}
Client ready
type: READY_ACCEPT
data: {}
Host start
// send to all client
type: GAME_START
data: {}
Client disconnect
// To be decided
Host kick another client
type: KICK_ACCEPT
data: {}
type KICK_ERROR
data: {
errorType: 'USER_NOT_FOUND', 'NO_PERMISSION' ...
}
Client create room
type: CREATE_ROOM_ACCEPT
data: {roomId: '12345'}
type: CREAT_ROOM_ERROR
data: { errorType: 'TOO_MANY_ROOMS'}
Client chat
type: CHAT
data: {message: 'Hello', messageId: 1, username: "name", extra: {} }
// id increases when we have new message.
// publicly visible in the current room
Client roll dice
type: DICE
data: {}
Passive response
type: RESPONSE
data: { response: 'yes', actionId: 1 }
Game state update
type: GAME_STATE
data: {...}
The state of the game is composed of two parts. The static part and the dynamic part.
The static game states can be initialized at the beginning of the game. We don't need to recalculate them throughout the game. Some static states include the connectivity among tiles, neighborhoods.
The dynamic game states are updated throughout the game and whenever they are updated, server will push new game states to client. Server will always push the current game state to clients, instead of just the diff for the last update. This is to make sure that even if a client disconnect for a while, it can still receive the whole game states that it needs. Some dynamic states include players' cash value, properties' rents and ownership.
Server stores all the game state. When it needs to update a client, it extracts out a portion that a client needs. For example, if we have a client that only displays the current leaderboard based on player's cash, we can extract out only the cash value for each player and ignore other game states.
{
currentPlayerLastMove: [[x,y], [x, y], [x,y]],
currentPlayer: id
players: [
{
cash: 1000,
position: [x, y],
id: 1,
direction: 'UP', 'DOWN', 'LEFT', 'RIGHT',
status: 'LOST', 'JAIL'...
currentActionID: [1, 2]
}
],
properties: {
name{
id1: {
rent: 1000,
position: [x, y],
onwer: undefined or playerId
},
id2: {
}
}
}
}
// TODO
{
// TODO
[0,1] {
baseRent: 122
id: id123
neighborhood: name
}
}
currentActionArray = [buyHouseActionHandlers, buyStockActionHander]
var playerActions = [] playerActions[0].currentAction
client: socket.emit("yes", {answer: "yes"});
serer: gameState.client1.currentAction = buyStockActionHandler; socket.emit('do you want to buy');
var buyHouseActionHandlers ={
onYes: function() {
},
onNo: function() {
}
}
var buyStockActionHander() {
onYes: function() {
}
}
socket.on("yes", function(){
gameState.client1.currentAction.onYest();
});
# Data Validation
Server side can perform data validation through `server/objects`. All the types in state will
have a coresponding schema. A schema looks like below:
const roomSchema = { id: Types.isString, name: Types.isString, users: Types.isObjectOf(isUserState), };
A room object that matches this schema could be:
const room = { id: 'room12', name: 'nice room', users: {} };
The basic types are defined in `server/objects/baseTypes`.
To create a complex type(such as Room), we need to create a function that takes in value, and path, and return
an array of validation errors. All the current types return an array of errors, so we can build on top of them.
`value` is the value that we are validating, path is the relative path that leads to this value. For example:
If we need to validate the room inside following state:
{ global: { rooms: [{ id: 'room12', name: 'nice room', users: {} }] } }
The `value` is
{ id: 'room12', name: 'nice room', users: {} }
and the `path` is `".global.rooms[0]"`.
If there is no error, the validation function should return an empty array.
# Hosting:
## Naming convention
* File/Folder name: camelCase
* Function name: camelCase
* Variable name: camelCase
* Class name: CamelCase
* Constants: SNAKE_CASE
# Random things:
**Update a rent:** find the property tie to the road, traverse to left, and to right of the linked list, update the rent on all connected properties owned by the same owner before th update.