Skip to content

Commit

Permalink
first commit. project structure and basic functionality working.
Browse files Browse the repository at this point in the history
  • Loading branch information
endel committed Nov 21, 2015
0 parents commit 6c3814e
Show file tree
Hide file tree
Showing 17 changed files with 464 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out.log
node_modules
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# colyseus

Extensible MMO Game Server for Node.js.

> Colyseus are in early stages of development, so don't expect a mature and
> ready-to-go/ready-to-scale piece of software here
## Features

- Match-making
- binary data transfer

TODO:

- "area of interest" updates/broadcasts
- "delta-encoding"

## Production usage

## How to use

Room

GameObject

- [PM2](https://github.com/Unitech/pm2)

http://pm2.keymetrics.io/docs/usage/specifics/#babeljs

## Options to consider

node --optimize_for_size --max_old_space_size=920 --gc_interval=100 server.js

Some references:

- https://www.cs.cmu.edu/~ashu/papers/cmu-cs-05-112.pdf
- http://dev.louiz.org/projects/batajelo/wiki/Server_architecture
- https://cloud.google.com/solutions/gaming/dedicated-server-gaming-solution/
52 changes: 52 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Tahoma, Geneva, sans-serif; }
</style>
<script type="text/javascript" src="websocket.js"></script>
<script type="text/javascript" src="msgpack.min.js"></script>
</head>
<body>
<strong>Messages</strong><br>

<form id="form">
<input type="text" id="input" value="" />
<input type="submit" value="send" />
</form>

<div id="messages"></div>

<script>
var host = window.document.location.host.replace(/:.*/, '');
var conn = new WebSocketClient('ws://' + host + ':2657');
conn.binaryType = "arraybuffer"

var JOIN_ROOM = 100
var LEAVE_ROOM = 101

conn.onopen = function (event) {
console.log("connected!")
conn.send( msgpack.encode([JOIN_ROOM, "ChatRoom"]) )
}

conn.onmessage = function (event) {
var node = document.createElement("p");
node.innerHTML = JSON.stringify( msgpack.decode(new Uint8Array(event.data)) )
document.getElementById('messages').appendChild(node)
};

conn.onclose = function (event) {
console.log("closing connection...")
}

document.getElementById('form').onsubmit = function(e) {
e.preventDefault()
conn.send( msgpack.encode({message: document.getElementById('input').value}) )
document.getElementById('input').value = null
}

</script>

</body>
</html>
1 change: 1 addition & 0 deletions examples/msgpack.min.js
34 changes: 34 additions & 0 deletions examples/rooms/chat_room.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var Room = require('../../lib/room')

class ChatRoom extends Room {

constructor (options) {
super(options)
console.log("Construct ChatRoom")
}

onJoin (client) {
console.log(client.id, "connected into ChatRoom")
}

onLeave (client) {
console.log(client.id, "disconnected from ChatRoom")
}

onMessage (client, data) {
console.log(client.id, "send a message: ", data)
}

update () {
console.log(`ChatRoom ~> Update: ${ this.state.clients }`)
}

dispose () {
console.log("Dispose ChatRoom")
}

}

ChatRoom.updateInterval = 200

module.exports = ChatRoom
16 changes: 16 additions & 0 deletions examples/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var colyseus = require('../index')
, ChatRoom = require('./rooms/chat_room')
, http = require('http')
, express = require('express')
, port = process.env.PORT || 2657
, app = express();

var server = http.createServer(app)
, gameServer = new colyseus.Server({server: server})

gameServer.room(ChatRoom)

app.use(express.static(__dirname));
server.listen(port);

console.log(`Listening on http://localhost:${ port }`)
1 change: 1 addition & 0 deletions examples/websocket.js
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
Server: require('./lib/server'),
Room: require('./lib/room')
}
8 changes: 8 additions & 0 deletions lib/game_object.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class GameObject {

constructor () {
}

}

module.exports = GameObject
84 changes: 84 additions & 0 deletions lib/match_maker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
var utils = require('./utils')

class MatchMaker {

constructor () {
this.handlers = {}
this.availableRooms = {}
this.activeMatches = []
}

addHandler (name, handler) {
this.handlers[ name ] = handler
this.availableRooms[ name ] = []
}

hasHandler (roomName) {
return this.handlers[ roomName ]
}

hasAvailableRoom (roomName) {
return (this.availableRooms[ roomName ] &&
this.availableRooms[ roomName ].length > 0)
}

joinOrCreate (client, roomName, options) {
var room = null

if (this.hasAvailableRoom(roomName)) {
room = this.requestJoin(client, roomName, options)
console.log("Join", roomName, "?", room)
}

if (!room) {
console.log("Let's create", roomName)
room = this.create(client, roomName, options)
}

return room
}

requestJoin (client, roomName, options) {
for (var i=0; i<this.availableRooms[ roomName ].length; i++) {
let availableRoom = this.availableRooms[ roomName ][i]
if (availableRoom.requestJoin(options)) {
return availableRoom
}
}
return false
}

create (client, roomName, options) {
var room = null
, handler = this.handlers[ roomName ]

if (handler && handler.isValidOptions(options)) {
room = new handler(options)
room.once('dispose', this.disposeRoom.bind(this, room))
this.availableRooms[ roomName ].push(room)
}

return room
}

disposeRoom(room) {
var roomName = room.constructor.name;

// remove from available rooms
if (this.hasAvailableRoom(roomName)) {
let index = this.availableRooms[roomName].indexOf(room);
if (index !== -1) {
utils.splice(this.availableRooms[roomName], index)
}
}

// remove from active matches
let index = this.activeMatches.indexOf(room)
if (index !== -1) {
utils.splice(this.activeMatches, index)
}
}

}

module.exports = MatchMaker
15 changes: 15 additions & 0 deletions lib/protocol.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Use some conventions from http status codes
// https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

// 1xx Informational
module.exports.JOIN_ROOM = 100
module.exports.LEAVE_ROOM = 101

// 2xx Success

// 3xx Redirection

// 4xx Client Error
module.exports.BAD_REQUEST = 400

// 5xx Server Error
54 changes: 54 additions & 0 deletions lib/room.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
var RoomState = require('./room_state')
, EventEmitter = require('events')

class Room extends EventEmitter {

constructor (options) {
super()

this.state = new RoomState()
this.options = options

if (this.constructor.updateInterval && this.update) {
this.updateInterval = setInterval(this.update.bind(this), this.constructor.updateInterval)
}
}

requestJoin (options) { return (this.options == options) }
onMessage (client, data) { }

// onJoin (client, options) { }
// onLeave (client) { }
// update () { }
// dispose () { }

_onJoin (client, options) {
this.state.clients += 1
if (this.onJoin) this.onJoin(client, options)
}

_onLeave (client) {
this.state.clients -= 1
if (this.onLeave) this.onLeave(client)

// custom cleanup method & clear intervals
if (this.state.clients == 0) {
if (this.dispose) this.dispose();
clearInterval(this.updateInterval)
this.emit('dispose')
}
}

// helper methods
broadcast (data) {
this.clients.forEach(function(client) {
client.send(data)
})
}

}

Room.updateInterval = 1000
Room.isValidOptions = function(options) { return true; }

module.exports = Room
15 changes: 15 additions & 0 deletions lib/room_state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Room state, with list of GameObjects

class RoomState {

constructor () {
this.clients = 0
this.gameObjects = []
}

// TODO: sync data / gameobjects
sync () { }

}

module.exports = RoomState
Loading

0 comments on commit 6c3814e

Please sign in to comment.