Skip to content

Instantly share code, notes, and snippets.

@Warpten
Created July 8, 2012 21:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Warpten/3072829 to your computer and use it in GitHub Desktop.
Save Warpten/3072829 to your computer and use it in GitHub Desktop.
Chess over Internet Draft v0.5
(function () {
var Game = (function() {
var Game = function() {
return new Game.fn.init();
};
Game.fn = Game.prototype = {
constructor: Game,
isVerbose: function() { return this._verbose; },
setVerbose: function(b) { this._verbose = b; },
init: function() {
this.getSocket.setGame(this);
if (!this.getSocket.connect('localhost', 8005))
return Errors.ERR_WEBSOCKET_NOT_IMPLEMENTED;
this.handlers.setGame(this);
// Do not allow clicks until the game has been started.
this._inputControl = false;
this._verbose = true;
return this;
},
/*
* Initiates a new game
*/
newGame: function(startedSide) {
this.getSocket.sendMessage(Opcodes.CMSG_NEW_GAME, {
takenSide: startedSide
});
},
/*
* Handles click event
*/
onClick: function(x, y) {
if (!this._inputControl)
return;
var cellX = Math.floor((x - this.getRenderer().getOffset().left)) / this.getCellSize(),
cellY = Math.floor((y - this.getRenderer().getOffset().top)) / this.getCellSize();
this.getSocket.sendMessage(Opcodes.CMSG_CELL_CLICKED, {
coordinates: { x: cellX, y: cellY }
});
},
/* ********************************************************* */
/* ******************* Socket.IO handlers ****************** */
/* ********************************************************* */
getSocket: {
setGame: function(obj) { this._game = obj; },
getGame: function() { return this._game; },
getHandlerObj: function() { return this._game.handlers; },
getHandler: function(type) {
var handlerObj = getHandlerObj();
if (handlerObj.handlerList[type] && typeof(handlerObj.handlerList[type]) === 'function')
return handlerObj.handlerList[type];
return false;
},
connect: function(hostName, port) {
this._host = hostName;
this._port = port;
if ('MozWebSocket' in window)
this.connection = new MozWebSocket("ws://" + hostName + ':' + port);
else if ('WebSocket' in window)
this.connection = new WebSocket("ws://" + hostName + ':' + port);
else return false;
var self = this;
this.connection.onopen = function(e) {
console.log("Connected to server " + self._host + ":" + self._port);
// Initiate our handshaking here
self.sendMessage(CMSG_HELLO);
};
this.connection.onmessage = function(data) {
if (handler = self.getHandler(data.action))
handler.call(this.getGame(), data.data);
else
console.log("Socket:: Received unknown action " + data.action);
};
this.connection.onerror = function(e) {
console.group();
console.log("Error caught:");
console.log(e);
console.groupEnd();
};
this.connection.onclose = function() {
if (self.getGame().isVerbose())
console.debug("Disconnected from the server");
};
return this;
},
sendMessage: function(type, data) {
this.connection.send(JSON.stringify({
action: type,
data: data
}));
},
},
handlers: {
setGame: function(obj) { this._game = obj; this.setup(); },
getGame: function() { return this._game; },
// In ANY handler, `this` refers to the game object.
setup: function() {
this.handlerList = [];
this.handlerList[Opcodes.SMSG_WELCOME] = function(data) {
if (data === undefined || data.clientUID === undefined)
return; // Log it
this.setPlayerGUID(data.clientUID);
this._inputControl = true;
};
this.handlerList[Opcodes.SMSG_OVERWRITE_UID] = function(data) {
if (data == undefined || data.clientUID === undefined)
return; // Log it
this.setPlayerGUID(data.clientUID);
};
},
},
/* ********************************************************* */
/* ******************* Setters / Getters ******************* */
/* ********************************************************* */
setRenderer: function(renderer) {
this._renderer = renderer;
// Bind click event here
var ctx = this;
$(this.getRenderer().getClickable()).click(function(eventData) {
ctx.onClick(eventData.pageX, eventData.pageY);
});
},
getRenderer: function() { return this._renderer; },
setPlayerGUID: function(uid) { this._playerGUID = uid; },
getPlayerGUID: function() { return this._playerGUID; },
};
return Game;
})();
// Give the init function the Game prototype for later instantiation
Game.fn.init.prototype = Game.fn;
// Expose Game to the global scope
window.Game = Game;
})(window);
var ChessClient = function(wsClient) {
this.ws = wsClient;
this._storage = [];
};
ChessClient.prototype.storeData = function(fieldName, fieldValue) {
this._storage[fieldName] = fieldValue;
};
ChessClient.prototype.getData = function (fieldName) {
if (this._storage[fieldName])
return this._storage[fieldName];
return null;
}
Object.defineProperty(ChessClient, 'getWSClient', {
get: function() { return this.wsClient; }
});
module.exports = ChessClient;
(function (window, undefined) {
var Pieces = {
KING: 1 << 0,
QUEEN: 1 << 1,
KNIGHT: 1 << 2,
BISHOP: 1 << 3,
ROOK: 1 << 4,
PAWN: 1 << 5,
BLACK: 1 << 6,
WHITE: 1 << 7,
};
var Opcodes = {
CMSG_HELLO: 0x0001, // Handshaking initialization
CMSG_NEW_GAME: 0x0002, // Warns the server we are initiating a new game room
CMSG_CELL_CLICKED: 0x0003,
SMSG_WELCOME: 0x0004, // Handshaking confirmation
SMSG_OVERWRITE_UID: 0x0005, // Forces overwriting the client-side UID if it gets changed for some awkward reason
};
var Errors = {
ERR_WEBSOCKET_NOT_IMPLEMENTED: 1, // Your browser does not handle WebSockets. Try upgrading to FireFox 14 or Chrome.
ERR_SERVER_UNREACHABLE: 2, // The server was unreachable.
};
window.Enum = Enum;
window.Opcodes = Opcodes;
window.Errors = Errors;
})(window);
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>EcmaChess v1.0</title>
<style type="text/css">
canvas#diagram {
display: block;
border-radius: 3px;
margin-left: auto;
margin-right: auto;
border: 1px solid black;
box-shadow: -1px -1px 2px rgba(9, 9, 9, 0.2), 1px -1px 2px rgba(9, 9, 9, 0.2), 1px 1px 2px rgba(9, 9, 9, 0.2), -1px 1px 2px rgba(9, 9, 9, 0.2);
background-image: url(./imgs/boardbg.png);
background-size: 100%;
background-position: 0 0;
}
body {
width: 500px;
height: auto;
margin-left: auto;
margin-right: auto;
position: relative;
}
div#wrapper { position: relative; }
div#content { margin-top: 10px; }
div#content select {
border: 1px solid black;
padding: 4px;
border-radius: 3px;
width: 120px;
}
div#content input[type="button"] {
border: 1px solid black;
padding: 4px;
border-radius: 3px;
background: white;
}
div#wrapper canvas { position: absolute; }
canvas#diagram { z-index: 100; }
canvas#highlighter { z-index: 200; }
canvas#piecesHolder { z-index: 300; }
</style>
</head>
<body>
<div id="wrapper">
<canvas id="diagram"></canvas>
<canvas id="highlighter"></canvas>
<canvas id="piecesHolder">Your browser needs to handle canvas for this demo to work.</canvas>
</div>
<div id="content">Play as <select name="startSide">
<option value="64" selected="selected">White</option>
<option value="128">Black</option>
</select> <input type="button" value="Start a new game" id="newGame" /></div>
<!-- Using non-minimized version for development -->
<script src="./js/libs/jquery-1.6.2.js"></script>
<script src="./js/enums.js"></script>
<script src="./js/renderer.js"></script>
<script src="./js/board.js"></script>
<script type="text/javascript">
$(function() {
var diagramNode = document.getElementById("diagram"),
highlightNode = document.getElementById("highlighter"),
piecesHolder = document.getElementById("piecesHolder");
var gameObj = new Game();
gameObj.setRenderer(new Renderer(diagramNode, highlightNode, piecesHolder, document.getElementById("wrapper")));
$("input#newGame").click(function() {
gameObj.newGame(parseInt($("select[name='startSide'] option:selected").val()));
});
});
</script>
</body>
var fs = require('fs'),
ws = require('websocket-server'),
messageFactory = require('./message.js'),
roomFactory = require('./room.js');
var defaultConfigPath = './config.default.json',
customConfigPath = './config.json';
function getConfigFile(path, callback) {
fs.readFile(path, 'utf8', function(error, jsonData) {
if (error) {
console.error("Could not open config file:", error.path);
callback(null);
} else
callback(JSON.parse(jsonData));
});
}
function main(configFile) {
var server = ws.createServer();
server.addListener("connection", function (connection) {
console.log("Client connected!");
connection.addListener("message", function (clientMessage) {
switch (clientMessage.type)
{
case Opcodes.CMSG_HELLO:
server.send(connection, messageFactory.newSMSG(Opcodes.SMSG_WELCOME).toString());
break;
case Opcodes.CMSG_NEW_GAME:
break;
case Opcodes.CMSG_CELL_CLICKED:
default:
break;
}
});
});
server.listen(configFile.port);
console.log("Listening on port " + configFile.port + " ...");
}
process.argv.forEach(function val, index, data) {
if (index === 2)
customConfigPath = val;
});
getConfigFile(defaultConfigPath, function(defaultConfig) {
getConfigFile(customConfigPath, function(localConfig) {
if (localConfig) {
main(localConfig);
} else if (defaultConfig) {
main(defaultConfig);
} else {
console.error("Server cannot start without any configuration file.");
process.exit(1);
}
});
});
var messageFactory = {
newSMSG: function(type, data) {
return new SMSG(type, data);
},
newCMSG: function(string) {
var jsonObj = JSON.parse(string);
return new CMSG(jsonObj.action, jsonObj.data);
}
};
var SMSG = function(type, data) {
this._action = type;
this._data = data;
return this;
};
SMSG.prototype.toString = function() {
var thisJSON = { action: this._action };
if (this._data)
thisJSON.data = this._data;
return JSON.stringify(thisJSON);
};
SMSG.prototype.setAction = function(action) {
this._action = action;
return this;
};
SMSG.prototype.addData = function(fieldName, value) {
this._data.fieldName = value;
return this;
};
var CMSG = function(type, data) {
this._action = type;
this._data = data;
return this;
};
module.exports = messageFactory;
(function (window, undefined) {
var Piece = (function() {
var Piece = function(typeMask, x, y, plrControl) {
return new Piece.fn.init(typeMask);
};
Piece.fn = Piece.prototype = {
constructor: Piece,
init: function() {
return this;
},
};
return Piece;
})();
// Give the init function the Piece prototype for later instantiation
Piece.fn.init.prototype = Piece.fn;
// Expose Piece to the global scope
window.Piece = Piece;
})(window);
(function () {
var Renderer = (function() {
var Renderer = function(diagramLayer, highlighter, piecesLayer, wrapper) {
return new Renderer.fn.init(diagramLayer, highlighter, piecesLayer, wrapper);
};
Renderer.fn = Renderer.prototype = {
constructor: Renderer,
init: function(diagramLayer, highlighter, piecesLayer, wrapper) {
this._diagramLayer = diagramLayer;
this._highlighter = highlighter;
this._piecesLayer = piecesLayer;
this.setCellSize(40);
var setSizing = function(obj, size) {
$(obj).css({
'width': size + 'px',
'height': size + 'px',
});
obj.width = obj.height = size;
};
setSizing(diagramLayer, this.getCellSize() * 8);
setSizing(highlighter, this.getCellSize() * 8);
setSizing(piecesLayer, this.getCellSize() * 8);
setSizing(wrapper, this.getCellSize() * 8);
this._positionOffset = $(diagramLayer).offset();
return this;
},
setCellSize: function(size) { this._cellSize = size; },
getCellSize: function() { return this._cellSize; },
getOffset: function() { return this._positionOffset; },
getClickable: function() { return this._piecesLayer; },
initBoard: function() {
},
};
return Renderer;
})();
// Give the init function the Renderer prototype for later instantiation
Renderer.fn.init.prototype = Renderer.fn;
// Expose Renderer to the global scope
window.Renderer = Renderer;
})(window);
var RoomHandler = {
roomSet: [],
freeRooms: [],
maxRooms: 500,
createRoom: function (roomName, roomCreator, maxViewers, ownerSide) {
if (this.count > this.maxRooms)
return false;
var room = new Room(roomName, roomCreator, maxViewers, ownerSide, this.getNextRoomIndex());
this.roomSet[room.getIndex()] = room;
return room;
},
deleteRoom: function(index) {
if (!this.roomSet[index])
return false;
if (index < this.count)
this.freeRooms.push(index);
delete this.roomSet[index];
},
getNextRoomIndex: function() {
if (this.freeRooms.length != 0) {
this.freeRooms.sort(function (a, b) { return a - b; });
// Remove the smallest element and return it.
return this.freeRoms.shift();
}
return this.count;
}
};
Object.defineProperty(RoomHandler, 'count', {
get: { return this.roomSet.length; }
});
var Room = function(roomName, roomCreator, maxViewers, ownerSide, guid) {
this._roomName = roomName;
this._roomCreator = roomCreator;
this._maxViewers = maxViewers;
this._ownerSide = ownerSide;
this._roomGUID = guid;
this.viewers = [];
};
Room.prototype.addViewer = function(client) {
if (this._maxViewers == this.viewersCount)
return false;
this.viewers.push(client);
return true;
};
Room.prototype.removeViewer = function (client) {
for (var i = 0, l = this.viewers; i < l; ++i) {
var iClient = this.viewers[i];
if (client.getData(0) != iClient.getData(0))
continue;
delete this.viewers[i];
return true;
}
return false;
};
Object.defineProperty(Room, 'viewersCount', {
get: function() { return this.viewers.length; }
});
Object.defineProperty(Room, 'playersCount', {
get: function() { return this.gameStarted ? 2 : 1; }
});
Object.defineProperty(Room, 'clientsCount', {
get: function() { return this.playersCount + this.viewersCount; }
});
module.exports = RoomHandler;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment