put it up on github
This commit is contained in:
commit
8160fed639
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/node_modules/
|
194
client/index.css
Normal file
194
client/index.css
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
body {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
margin: 0;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.wrapper {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
background: -moz-radial-gradient(center, ellipse cover, #ffffff 0%, #e5e5e5 100%);
|
||||||
|
background: -webkit-radial-gradient(center, ellipse cover, #ffffff 0%,#e5e5e5 100%);
|
||||||
|
background: radial-gradient(ellipse at center, #ffffff 0%,#e5e5e5 100%);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
.screen {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.dialog {
|
||||||
|
position: absolute;
|
||||||
|
width: 260px;
|
||||||
|
padding: 20px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 10px;
|
||||||
|
top: 20%;
|
||||||
|
}
|
||||||
|
h1, h2, h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
input[type="text"] {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 125%;
|
||||||
|
padding: 2px;
|
||||||
|
box-shadow: inset 1px 1px 10px #ddd;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.dialog input {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background: #ffffff;
|
||||||
|
background: -moz-linear-gradient(top, #ffffff 0%, #e5e5e5 100%);
|
||||||
|
background: -webkit-linear-gradient(top, #ffffff 0%,#e5e5e5 100%);
|
||||||
|
background: linear-gradient(to bottom, #ffffff 0%,#e5e5e5 100%);
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background: -moz-linear-gradient(top, #ffffff 0%, #e8e8e8 100%);
|
||||||
|
background: -webkit-linear-gradient(top, #ffffff 0%,#e8e8e8 100%);
|
||||||
|
background: linear-gradient(to bottom, #ffffff 0%,#e8e8e8 100%);
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
.boxlayout {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.box {
|
||||||
|
text-align: left;
|
||||||
|
width: 25%;
|
||||||
|
display: inline-block;
|
||||||
|
height: 400px;
|
||||||
|
margin: 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
min-width: 280px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
box-shadow: 4px 4px 10px #ddd;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.stat {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#waitlist {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
#waitlist .red, #waitlist .green {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
#waitlist .red {
|
||||||
|
background-color: #ffd5d5;
|
||||||
|
border: 1px solid #ff5c5c;
|
||||||
|
}
|
||||||
|
#waitlist .green {
|
||||||
|
background-color: #daffda;
|
||||||
|
border: 1px solid #00f700;
|
||||||
|
}
|
||||||
|
.waitlistInstance {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 5px;
|
||||||
|
max-height: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.waitlistInstance:nth-child(odd) {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
.joinBtn {
|
||||||
|
float: right;
|
||||||
|
color: #03A9F4;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0px 10px;
|
||||||
|
}
|
||||||
|
.joinBtn:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
height: 40px;
|
||||||
|
line-height: 2.5;
|
||||||
|
padding: 5px 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
button#leave {
|
||||||
|
float: right;
|
||||||
|
line-height: 2;
|
||||||
|
}
|
||||||
|
.sidebar {
|
||||||
|
float: left;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 51px;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
#g_s_stat {
|
||||||
|
color: #119286;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 200px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
#game_canvas {
|
||||||
|
margin-left: 300px;
|
||||||
|
}
|
||||||
|
.endresult {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.result {
|
||||||
|
width: 530px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
button#lobby {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
.bigstat {
|
||||||
|
font-size: 200%;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.message.t_event {
|
||||||
|
color: #b5b5b5;
|
||||||
|
}
|
||||||
|
.message .sender {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.message.me .sender {
|
||||||
|
color: blue !important;
|
||||||
|
}
|
||||||
|
.chatbox {
|
||||||
|
margin-top: 50px;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
#messages {
|
||||||
|
height: 250px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: pre-line;
|
||||||
|
max-width: 254px;
|
||||||
|
}
|
72
client/index.html
Normal file
72
client/index.html
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="about" content="A Diamond* WebGame game">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script>
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<script type="text/javascript" src="./index.js"></script>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Open+Sans">
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="./index.css">
|
||||||
|
<title>HTML5 Connect Four</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="screen" id="start">
|
||||||
|
<div class="dialog">
|
||||||
|
<h1>Connect Four</h1>
|
||||||
|
<p id="warning_message"></p>
|
||||||
|
<input type="text" id="player_name" placeholder="Your name here">
|
||||||
|
<button id="sock_player_init">Join server</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="screen boxlayout" style="display: none;" id="selection">
|
||||||
|
<div class="box">
|
||||||
|
<h1>Statistics</h1>
|
||||||
|
<span class="stat">Players in game: <span id="stats_players"></span></span>
|
||||||
|
<span class="stat">Total games since server started: <span id="stats_games"></span></span>
|
||||||
|
<span class="stat">You've played in <span id="stats_clientgames"></span> matches this session.</span>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<h1>Players waiting</h1>
|
||||||
|
<div id="waitlist">
|
||||||
|
<p><i class="fa fa-spinner fa-spin fa-fw"></i> Loading..</p>
|
||||||
|
</div>
|
||||||
|
<button id="waitlist_quit" style="display: none;">Cancel</button>
|
||||||
|
<div class="idbuttons">
|
||||||
|
<button id="waitlist_join">Join Wait List</button>
|
||||||
|
<button id="waitlist_join_random">Join Random Game</button>
|
||||||
|
<button id="waitlist_join_refresh">Refresh</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<h1>How to play</h1>
|
||||||
|
<p>This is a HTML5 remake of <a href="https://en.wikipedia.org/wiki/Connect_Four" target="_blank">Connect Four</a></p>
|
||||||
|
<ul>
|
||||||
|
<li>Connect 4 tiles of your color to win.</li>
|
||||||
|
<li>Matches are detected horizontally, vertically and diagonally.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="screen game gamelayout" style="display: none;" id="game">
|
||||||
|
<div class="header">You're playing against <span id="opponent_name">null</span> <button id="leave">Leave game</button></div>
|
||||||
|
<canvas id="game_canvas" width="640" height="640"></canvas>
|
||||||
|
<div class="sidebar">
|
||||||
|
<span class="stat" id="g_s_stat">Your turn.</span>
|
||||||
|
<div class="chatbox">
|
||||||
|
<div class="letterbox" id="messages"></div>
|
||||||
|
<input type="text" id="message_send" placeholder="Send message..">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/mustache" id="waitlistInstance">
|
||||||
|
<div class="waitlistInstance">
|
||||||
|
<span class="name">{{name}}</span>
|
||||||
|
<a href="#" class="joinBtn" onclick="joinWaiting('{{gameId}}')">Join</a>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
621
client/index.js
Normal file
621
client/index.js
Normal file
@ -0,0 +1,621 @@
|
|||||||
|
(function ($) {
|
||||||
|
let io = window.io.connect()
|
||||||
|
let Connect4 = {
|
||||||
|
DOM: {},
|
||||||
|
playerName: '',
|
||||||
|
playerID: '',
|
||||||
|
verified: null,
|
||||||
|
locked: false,
|
||||||
|
waitlist: [],
|
||||||
|
played: 0,
|
||||||
|
renderTick: false,
|
||||||
|
Game: {
|
||||||
|
gameId: null,
|
||||||
|
myTurn: false,
|
||||||
|
myColor: '',
|
||||||
|
opponentID: '',
|
||||||
|
opponentName: '',
|
||||||
|
places: [[],[],[],[],[],[],[],[],[]]
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
blue: '#102aed',
|
||||||
|
red: '#ed1010'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.requestAnimFrame = (function() {
|
||||||
|
return window.requestAnimationFrame ||
|
||||||
|
window.webkitRequestAnimationFrame ||
|
||||||
|
window.mozRequestAnimationFrame ||
|
||||||
|
function (callback) {
|
||||||
|
window.setTimeout(callback, 1000 / 60)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
function mustacheTempl (tmlTag, data) {
|
||||||
|
let html = ''
|
||||||
|
const tag = document.querySelector('#' + tmlTag)
|
||||||
|
|
||||||
|
if (!tag) return ''
|
||||||
|
html = tag.innerHTML
|
||||||
|
html = window.Mustache.to_html(html, data)
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointerOnCanvas (e) {
|
||||||
|
let x
|
||||||
|
let y
|
||||||
|
|
||||||
|
if (e.changedTouches) {
|
||||||
|
let touch = e.changedTouches[0]
|
||||||
|
if (touch) {
|
||||||
|
e.pageX = touch.pageX
|
||||||
|
e.pageY = touch.pageY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.pageX || e.pageY) {
|
||||||
|
x = e.pageX
|
||||||
|
y = e.pageY
|
||||||
|
} else {
|
||||||
|
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft
|
||||||
|
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop
|
||||||
|
}
|
||||||
|
|
||||||
|
x -= Connect4.DOM.canvas.offsetLeft
|
||||||
|
y -= Connect4.DOM.canvas.offsetTop
|
||||||
|
|
||||||
|
return {x: x, y: y}
|
||||||
|
}
|
||||||
|
|
||||||
|
let GameDrawer = {
|
||||||
|
drawMyBoard: true,
|
||||||
|
boardStaticState: null,
|
||||||
|
|
||||||
|
mX: 0,
|
||||||
|
mY: 0,
|
||||||
|
|
||||||
|
padding: 32,
|
||||||
|
|
||||||
|
gridX: 0,
|
||||||
|
gridY: 0,
|
||||||
|
|
||||||
|
gridSize: 64,
|
||||||
|
mouseOn: false,
|
||||||
|
|
||||||
|
bw: 576,
|
||||||
|
bh: 576,
|
||||||
|
|
||||||
|
startGame: () => {
|
||||||
|
Connect4.Game.places = [[],[],[],[],[],[],[],[],[]]
|
||||||
|
Connect4.ctx.clearRect(0, 0, Connect4.canvasW, Connect4.canvasH)
|
||||||
|
Connect4.Game.myTurn = true
|
||||||
|
|
||||||
|
let p = GameDrawer.padding
|
||||||
|
|
||||||
|
Connect4.ctx.beginPath()
|
||||||
|
for (let x = 0; x <= GameDrawer.bw; x += GameDrawer.gridSize) {
|
||||||
|
Connect4.ctx.moveTo(0.5 + x + p, p)
|
||||||
|
Connect4.ctx.lineTo(0.5 + x + p, GameDrawer.bh + p)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let x = 0; x <= GameDrawer.bh; x += GameDrawer.gridSize) {
|
||||||
|
Connect4.ctx.moveTo(p, 0.5 + x + p)
|
||||||
|
Connect4.ctx.lineTo(GameDrawer.bw + p, 0.5 + x + p)
|
||||||
|
}
|
||||||
|
Connect4.ctx.closePath()
|
||||||
|
|
||||||
|
Connect4.ctx.lineWidth = 1
|
||||||
|
Connect4.ctx.strokeStyle = "black"
|
||||||
|
Connect4.ctx.stroke()
|
||||||
|
|
||||||
|
GameDrawer.boardStaticState = new Image()
|
||||||
|
GameDrawer.boardStaticState.src = Connect4.DOM.canvas.toDataURL()
|
||||||
|
GameDrawer.boardStaticState.onload = () => {
|
||||||
|
Connect4.renderTick = true
|
||||||
|
GameDrawer.gameLoop()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
possible: (column) => {
|
||||||
|
let inTable = Connect4.Game.places[column]
|
||||||
|
if (inTable.length === 9) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
click: () => {
|
||||||
|
if (!Connect4.Game.gameId) return
|
||||||
|
|
||||||
|
if (Connect4.Game.myTurn && GameDrawer.mouseOn) {
|
||||||
|
let column = GameDrawer.gridX - 1
|
||||||
|
if (GameDrawer.possible(column)) {
|
||||||
|
io.emit('place_at', {column: column, gameId: Connect4.Game.gameId})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updater: () => {
|
||||||
|
for (let i in Connect4.Game.places) {
|
||||||
|
let col = Connect4.Game.places[i]
|
||||||
|
for (let p in col) {
|
||||||
|
let piece = col[p]
|
||||||
|
if (piece.dy < piece.y) {
|
||||||
|
piece.dy += 0.5
|
||||||
|
} else {
|
||||||
|
piece.dy = piece.y
|
||||||
|
}
|
||||||
|
Connect4.ctx.fillStyle = Connect4.color[piece.color]
|
||||||
|
Connect4.ctx.fillRect((parseInt(i) * GameDrawer.gridSize) + GameDrawer.padding,
|
||||||
|
(piece.dy * GameDrawer.gridSize) + GameDrawer.padding, GameDrawer.gridSize, GameDrawer.gridSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Connect4.renderTick || !Connect4.Game.gameId) return
|
||||||
|
if (Connect4.Game.myTurn) {
|
||||||
|
if (GameDrawer.mouseOn) {
|
||||||
|
let pos = (GameDrawer.gridX * GameDrawer.gridSize)
|
||||||
|
|
||||||
|
Connect4.ctx.beginPath()
|
||||||
|
Connect4.ctx.moveTo(pos - 8, (GameDrawer.padding / 2) - 4)
|
||||||
|
Connect4.ctx.lineTo(pos, (GameDrawer.padding / 2) + 4)
|
||||||
|
Connect4.ctx.lineTo(pos + 8, (GameDrawer.padding / 2) - 4)
|
||||||
|
Connect4.ctx.closePath()
|
||||||
|
|
||||||
|
Connect4.ctx.lineWidth = 10
|
||||||
|
Connect4.ctx.strokeStyle = Connect4.color[Connect4.Game.myColor]
|
||||||
|
Connect4.ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
gameLoop: () => {
|
||||||
|
Connect4.ctx.clearRect(0, 0, Connect4.canvasW, Connect4.canvasH)
|
||||||
|
if (!Connect4.renderTick) return
|
||||||
|
|
||||||
|
GameDrawer.updater()
|
||||||
|
|
||||||
|
Connect4.ctx.drawImage(GameDrawer.boardStaticState, 0, 0)
|
||||||
|
requestAnimFrame(GameDrawer.gameLoop)
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize: () => {
|
||||||
|
Connect4.DOM.canvas.addEventListener('mousemove', (e) => {
|
||||||
|
let p = pointerOnCanvas(e)
|
||||||
|
|
||||||
|
if (p.x > GameDrawer.padding && p.y > GameDrawer.padding) {
|
||||||
|
let gridX = Math.floor((p.x + GameDrawer.padding) / GameDrawer.gridSize)
|
||||||
|
let gridY = Math.floor((p.y + GameDrawer.padding) / GameDrawer.gridSize)
|
||||||
|
|
||||||
|
if (gridX <= 9 && gridY <= 9) {
|
||||||
|
GameDrawer.mouseOn = true
|
||||||
|
|
||||||
|
GameDrawer.gridX = gridX
|
||||||
|
GameDrawer.gridY = gridY
|
||||||
|
} else {
|
||||||
|
GameDrawer.mouseOn = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GameDrawer.mouseOn = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Connect4.DOM.canvas.addEventListener('mouseleave', (e) => {
|
||||||
|
GameDrawer.mouseOn = false
|
||||||
|
})
|
||||||
|
|
||||||
|
Connect4.DOM.canvas.addEventListener('click', (e) => {
|
||||||
|
GameDrawer.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (GameDrawer.placingShips && e.keyCode === 82) {
|
||||||
|
if (GameDrawer.shipOrientation === 0) {
|
||||||
|
GameDrawer.shipOrientation = 1
|
||||||
|
} else {
|
||||||
|
GameDrawer.shipOrientation = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStored (variable) {
|
||||||
|
let result = null
|
||||||
|
if (!window.localStorage) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.localStorage.game_store) {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(window.localStorage.game_store)
|
||||||
|
if (obj[variable] != null) {
|
||||||
|
result = obj[variable]
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
result = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeVar (variable, value) {
|
||||||
|
if (!window.localStorage) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.localStorage.game_store) {
|
||||||
|
try {
|
||||||
|
let obj = JSON.parse(window.localStorage.game_store)
|
||||||
|
obj[variable] = value
|
||||||
|
window.localStorage.game_store = JSON.stringify(obj)
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let obj = {}
|
||||||
|
obj[variable] = value
|
||||||
|
window.localStorage.game_store = JSON.stringify(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playerNameValidation (name) {
|
||||||
|
if (/^([A-Z0-9_\-@]{3,20})$/i.test(name)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function logWarning (msg) {
|
||||||
|
Connect4.DOM.joinWarn.innerHTML = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
function logStatus (msg) {
|
||||||
|
Connect4.DOM.statusCurrent.innerHTML = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinGame (game) {
|
||||||
|
Connect4.played += 1
|
||||||
|
|
||||||
|
alert('Game has started!')
|
||||||
|
//io.emit('leave_game', {gameId: Connect4.Game.gameId})
|
||||||
|
Connect4.Game.gameId = game.gameId
|
||||||
|
Connect4.Game.opponentID = game.opponentId
|
||||||
|
Connect4.Game.opponentName = game.opponentName
|
||||||
|
Connect4.Game.myColor = game.color
|
||||||
|
Connect4.DOM.opponentName.innerHTML = game.opponentName
|
||||||
|
|
||||||
|
Connect4.DOM.chatbox.innerHTML = ''
|
||||||
|
|
||||||
|
io.emit('game_poll', {gameId: Connect4.Game.gameId})
|
||||||
|
|
||||||
|
Connect4.DOM.gameScreen.style.display = 'block'
|
||||||
|
Connect4.DOM.selectionScreen.style.display = 'none'
|
||||||
|
Connect4.DOM.waitlistBtns.style.display = 'block'
|
||||||
|
Connect4.DOM.waitlistQuit.style.display = 'none'
|
||||||
|
GameDrawer.startGame()
|
||||||
|
addChatMessage('event', null, 'Game started!')
|
||||||
|
}
|
||||||
|
|
||||||
|
function attemptJoin (name) {
|
||||||
|
if (Connect4.locked) return
|
||||||
|
if (!io.connected) {
|
||||||
|
return logWarning('Disconnected from server socket.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playerNameValidation(name) == false) {
|
||||||
|
return logWarning('Username not allowed.')
|
||||||
|
}
|
||||||
|
|
||||||
|
logWarning('Attempting to join..')
|
||||||
|
Connect4.locked = true
|
||||||
|
io.emit('session_create', {name: name})
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinSuccess (data) {
|
||||||
|
Connect4.playerName = data.name
|
||||||
|
Connect4.playerID = data.uid
|
||||||
|
Connect4.DOM.selectionScreen.style.display = 'block'
|
||||||
|
|
||||||
|
storeVar('name', data.name)
|
||||||
|
io.emit('poll_games')
|
||||||
|
Connect4.locked = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinResponse (data) {
|
||||||
|
if (data.success !== true) {
|
||||||
|
Connect4.locked = false
|
||||||
|
return logWarning(data.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
Connect4.DOM.startScreen.style.display = 'none'
|
||||||
|
|
||||||
|
joinSuccess(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomInt(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructWaitList() {
|
||||||
|
let finalML = ''
|
||||||
|
for (let i in Connect4.waitlist) {
|
||||||
|
let game = Connect4.waitlist[i]
|
||||||
|
finalML += mustacheTempl('waitlistInstance', game)
|
||||||
|
}
|
||||||
|
waitlist.innerHTML = finalML
|
||||||
|
}
|
||||||
|
|
||||||
|
window.joinWaiting = (gameId) => {
|
||||||
|
if (Connect4.Game.gameId) return
|
||||||
|
io.emit('game_attempt_join', {gameId: gameId})
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameEnds (reason, winner) {
|
||||||
|
if (reason === 1) {
|
||||||
|
if (winner === true) {
|
||||||
|
alert('You won!')
|
||||||
|
logStatus('You won!')
|
||||||
|
} else {
|
||||||
|
alert('You lost.')
|
||||||
|
logStatus('You lost.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason === 0 && winner === true) {
|
||||||
|
alert('Your opponent left the game.')
|
||||||
|
Connect4.DOM.gameScreen.style.display = 'none'
|
||||||
|
Connect4.DOM.selectionScreen.style.display = 'block'
|
||||||
|
Connect4.renderTick = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reason === 2) {
|
||||||
|
alert('You tied!')
|
||||||
|
logStatus('It\'s a tie!.')
|
||||||
|
}
|
||||||
|
|
||||||
|
Connect4.locked = false
|
||||||
|
Connect4.Game.gameId = null
|
||||||
|
Connect4.Game.myTurn = false
|
||||||
|
io.emit('poll_games')
|
||||||
|
|
||||||
|
//Connect4.DOM.gameScreen.style.display = 'none'
|
||||||
|
//Connect4.DOM.selectionScreen.style.display = 'block'
|
||||||
|
|
||||||
|
Connect4.DOM.waitlistBtns.style.display = 'block'
|
||||||
|
Connect4.DOM.waitlistQuit.style.display = 'none'
|
||||||
|
addChatMessage('event', null, 'Disconnected')
|
||||||
|
}
|
||||||
|
|
||||||
|
function forceRelogin () {
|
||||||
|
logWarning('Please log in again.')
|
||||||
|
Connect4.DOM.gameScreen.style.display = 'none'
|
||||||
|
Connect4.DOM.selectionScreen.style.display = 'none'
|
||||||
|
Connect4.DOM.startScreen.style.display = 'block'
|
||||||
|
Connect4.DOM.resultScreen.style.display = 'none'
|
||||||
|
|
||||||
|
Connect4.locked = false
|
||||||
|
Connect4.playerName = ''
|
||||||
|
Connect4.Game.gameId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
function addChatMessage (type, senderName, message) {
|
||||||
|
let msgElem = '<div class="message t_' + type + '">'
|
||||||
|
if (senderName) {
|
||||||
|
msgElem += '<span class="sender">' + senderName + '</span> '
|
||||||
|
}
|
||||||
|
msgElem += '<span class="line">' + escapeHtml(message) + '</span>'
|
||||||
|
|
||||||
|
Connect4.DOM.chatbox.innerHTML += msgElem
|
||||||
|
Connect4.DOM.chatbox.scrollTop = Connect4.DOM.chatbox.scrollHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
const startScreen = Connect4.DOM.startScreen = $.querySelector('#start')
|
||||||
|
const selectionScreen = Connect4.DOM.selectionScreen = $.querySelector('#selection')
|
||||||
|
const gameScreen = Connect4.DOM.gameScreen = $.querySelector('#game')
|
||||||
|
|
||||||
|
const warning = Connect4.DOM.joinWarn = startScreen.querySelector('#warning_message')
|
||||||
|
const playerName = startScreen.querySelector('#player_name')
|
||||||
|
const startButton = startScreen.querySelector('#sock_player_init')
|
||||||
|
|
||||||
|
const waitlist = Connect4.DOM.waitlist = selectionScreen.querySelector('#waitlist')
|
||||||
|
const random = selectionScreen.querySelector('#waitlist_join_random')
|
||||||
|
const newGame = selectionScreen.querySelector('#waitlist_join')
|
||||||
|
const refresh = selectionScreen.querySelector('#waitlist_join_refresh')
|
||||||
|
|
||||||
|
const waitlistQuit = Connect4.DOM.waitlistQuit = selectionScreen.querySelector('#waitlist_quit')
|
||||||
|
const waitlistBtns = Connect4.DOM.waitlistBtns = selectionScreen.querySelector('.idbuttons')
|
||||||
|
|
||||||
|
const stat_ingame = selectionScreen.querySelector('#stats_players')
|
||||||
|
const stat_total = selectionScreen.querySelector('#stats_games')
|
||||||
|
const stat_client = selectionScreen.querySelector('#stats_clientgames')
|
||||||
|
|
||||||
|
const leaveBtn = gameScreen.querySelector('#leave')
|
||||||
|
const opponentName = Connect4.DOM.opponentName = gameScreen.querySelector('#opponent_name')
|
||||||
|
|
||||||
|
let canvas = Connect4.DOM.canvas = gameScreen.querySelector('#game_canvas')
|
||||||
|
let ctx = Connect4.ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
Connect4.canvasW = canvas.width
|
||||||
|
Connect4.canvasH = canvas.height
|
||||||
|
|
||||||
|
Connect4.DOM.statusCurrent = gameScreen.querySelector('#g_s_stat')
|
||||||
|
|
||||||
|
const chatbox = Connect4.DOM.chatbox = gameScreen.querySelector('#messages')
|
||||||
|
const chatfield = Connect4.DOM.chatfield = gameScreen.querySelector('#message_send')
|
||||||
|
|
||||||
|
GameDrawer.initialize()
|
||||||
|
|
||||||
|
let uname = getStored('name')
|
||||||
|
if (uname) {
|
||||||
|
playerName.value = uname
|
||||||
|
}
|
||||||
|
|
||||||
|
playerName.addEventListener('keydown', (e) => {
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
attemptJoin(playerName.value)
|
||||||
|
}
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
chatfield.addEventListener('keydown', (e) => {
|
||||||
|
if (e.keyCode === 13 && Connect4.Game.gameId) {
|
||||||
|
if (chatfield.value != '') {
|
||||||
|
io.emit('chat_send', {message: chatfield.value, gameId: Connect4.Game.gameId})
|
||||||
|
addChatMessage('chat me', Connect4.playerName, chatfield.value)
|
||||||
|
chatfield.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
startButton.addEventListener('click', (e) => {
|
||||||
|
attemptJoin(playerName.value)
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
newGame.addEventListener('click', (e) => {
|
||||||
|
if (Connect4.locked) return
|
||||||
|
if (Connect4.Game.gameId) return
|
||||||
|
io.emit('new_game')
|
||||||
|
Connect4.locked = true
|
||||||
|
})
|
||||||
|
|
||||||
|
refresh.addEventListener('click', (e) => {
|
||||||
|
if (Connect4.locked) return
|
||||||
|
io.emit('poll_games')
|
||||||
|
})
|
||||||
|
|
||||||
|
waitlistQuit.addEventListener('click', (e) => {
|
||||||
|
io.emit('leave_game', {gameId: Connect4.Game.gameId})
|
||||||
|
})
|
||||||
|
|
||||||
|
leaveBtn.addEventListener('click', (e) => {
|
||||||
|
if (Connect4.Game.gameId) {
|
||||||
|
io.emit('leave_game', {gameId: Connect4.Game.gameId})
|
||||||
|
}
|
||||||
|
Connect4.DOM.gameScreen.style.display = 'none'
|
||||||
|
Connect4.DOM.selectionScreen.style.display = 'block'
|
||||||
|
Connect4.renderTick = false
|
||||||
|
})
|
||||||
|
|
||||||
|
random.addEventListener('click', (e) => {
|
||||||
|
Connect4.joinRandomWhenDone = true
|
||||||
|
io.emit('poll_games')
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('chat', (data) => {
|
||||||
|
addChatMessage('chat', data.name, data.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('infmessage', (message) => {
|
||||||
|
logStatus(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('game_start', (data) => {
|
||||||
|
leaveBtn.innerHTML = 'Leave game'
|
||||||
|
joinGame(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('left_success', () => {
|
||||||
|
gameEnds(0, null)
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('turn', (val) => {
|
||||||
|
if (val === true) {
|
||||||
|
Connect4.Game.myTurn = true
|
||||||
|
logStatus('Your turn.')
|
||||||
|
} else {
|
||||||
|
Connect4.Game.myTurn = false
|
||||||
|
logStatus('Your opponent\'s turn.')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('place', (data) => {
|
||||||
|
let col = Connect4.Game.places[data.column]
|
||||||
|
col.push({y: 8 - col.length, color: data.color, dy: 0})
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('game_error', (data) => {
|
||||||
|
alert(data.message)
|
||||||
|
gameEnds(0, null)
|
||||||
|
io.emit('poll_games')
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('force_relog', () => {
|
||||||
|
forceRelogin()
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('game_end', (data) => {
|
||||||
|
gameEnds(data.result, data.win)
|
||||||
|
leaveBtn.innerHTML = 'Back to lobby'
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('game_new_done', (data) => {
|
||||||
|
Connect4.locked = true
|
||||||
|
Connect4.DOM.waitlist.innerHTML = '<div class="green">Waiting for an opponent..</div>'
|
||||||
|
Connect4.DOM.waitlistBtns.style.display = 'none'
|
||||||
|
Connect4.DOM.waitlistQuit.style.display = 'block'
|
||||||
|
Connect4.Game.gameId = data.gameId
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('current_stats', (data) => {
|
||||||
|
dataOpponentDestroyed.innerHTML = data.opponentShipsLeft
|
||||||
|
dataMineDestroyed.innerHTML = data.myShipsLeft
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('login_status', joinResponse)
|
||||||
|
io.on('poll_games_res', (data) => {
|
||||||
|
Connect4.DOM.waitlistQuit.style.display = 'none'
|
||||||
|
|
||||||
|
let list = data.list
|
||||||
|
|
||||||
|
if (data.sessions != null) {
|
||||||
|
stat_ingame.innerHTML = data.sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.totalGames != null) {
|
||||||
|
stat_total.innerHTML = data.totalGames
|
||||||
|
}
|
||||||
|
|
||||||
|
stat_client.innerHTML = Connect4.played
|
||||||
|
|
||||||
|
if (!list.length) {
|
||||||
|
waitlist.innerHTML = '<div class="red">No people currently waiting, press <b>Join Wait List</b> to enter.</div>'
|
||||||
|
Connect4.waitlist = []
|
||||||
|
|
||||||
|
if(Connect4.joinRandomWhenDone) {
|
||||||
|
delete Connect4.joinRandomWhenDone
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Connect4.waitlist = list
|
||||||
|
|
||||||
|
if (Connect4.joinRandomWhenDone && Connect4.waitlist.length) {
|
||||||
|
delete Connect4.joinRandomWhenDone
|
||||||
|
|
||||||
|
let rand = getRandomInt(1, Connect4.waitlist.length)
|
||||||
|
|
||||||
|
io.emit('game_attempt_join', {gameId: Connect4.waitlist[rand - 1].gameId})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructWaitList()
|
||||||
|
})
|
||||||
|
|
||||||
|
io.on('disconnect', () => {
|
||||||
|
gameEnds(0, null)
|
||||||
|
forceRelogin()
|
||||||
|
logWarning('Server disconnected')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})(document)
|
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "battleship.js",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Battleship game in the browser",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"battleship",
|
||||||
|
"game",
|
||||||
|
"html5"
|
||||||
|
],
|
||||||
|
"author": "Evert",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.15.2",
|
||||||
|
"socketio": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
454
server.js
Normal file
454
server.js
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
const express = require('express')
|
||||||
|
const socketio = require('socket.io')
|
||||||
|
const http = require('http')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
let app = express()
|
||||||
|
let server = http.createServer(app)
|
||||||
|
let io = socketio(server)
|
||||||
|
|
||||||
|
app.enable('trust proxy')
|
||||||
|
app.disable('x-powered-by')
|
||||||
|
|
||||||
|
app.use('/', express.static(path.join(__dirname, '/client/')))
|
||||||
|
|
||||||
|
function playerNameValidation (name) {
|
||||||
|
if (/^([A-Z0-9_\-@]{3,20})$/i.test(name)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let clients = {}
|
||||||
|
let games = {}
|
||||||
|
|
||||||
|
let totalGames = 0
|
||||||
|
|
||||||
|
// Generate a random int betweem two ints
|
||||||
|
function getRandomInt(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate random string of characters
|
||||||
|
function nuid(len) {
|
||||||
|
let buf = [],
|
||||||
|
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
||||||
|
charlen = chars.length
|
||||||
|
|
||||||
|
for (let i = 0; i < len; ++i) {
|
||||||
|
buf.push(chars[getRandomInt(0, charlen - 1)])
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function clientsBySocketID (id) {
|
||||||
|
let result = null
|
||||||
|
|
||||||
|
for (let uid in clients) {
|
||||||
|
let client = clients[uid]
|
||||||
|
if (client.sockID === id) {
|
||||||
|
result = uid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function determineOpponent (myIndex) {
|
||||||
|
let opponent = 'red'
|
||||||
|
|
||||||
|
if (myIndex === 'red') {
|
||||||
|
opponent = 'blue'
|
||||||
|
}
|
||||||
|
|
||||||
|
return opponent
|
||||||
|
}
|
||||||
|
|
||||||
|
function killGamesClientIsIn (uid) {
|
||||||
|
for (let gameId in games) {
|
||||||
|
let game = games[gameId]
|
||||||
|
if (game.blue && game.blue === uid) {
|
||||||
|
if (!game.isWaiting && game.red) {
|
||||||
|
clients[game.red].socket.emit('game_end', {win: true, result: 0})
|
||||||
|
}
|
||||||
|
} else if (game.red && game.red === uid) {
|
||||||
|
if (clients[game.blue]) {
|
||||||
|
clients[game.blue].socket.emit('game_end', {win: true, result: 0})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete games[gameId]
|
||||||
|
console.log(gameId + ' was ended abruptly on ' + uid + '\'s demand.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNewGame (uid) {
|
||||||
|
let client = clients[uid]
|
||||||
|
let gameId = nuid(16)
|
||||||
|
|
||||||
|
client.socket.emit('game_new_done', {gameId: gameId})
|
||||||
|
|
||||||
|
console.log(client.name + ' has started a new game. ID: ' + gameId)
|
||||||
|
|
||||||
|
games[gameId] = {
|
||||||
|
blue: uid,
|
||||||
|
red: null,
|
||||||
|
isWaiting: true,
|
||||||
|
turn: 1,
|
||||||
|
places: [[],[],[],[],[],[],[],[],[]],
|
||||||
|
created: new Date(),
|
||||||
|
started: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinGame (uid, gameId) {
|
||||||
|
let me = clients[uid]
|
||||||
|
|
||||||
|
if (!games[gameId]) {
|
||||||
|
return me.socket.emit('game_error', {message: 'That game has ended!'})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (games[gameId].red != null) {
|
||||||
|
return me.socket.emit('game_error', {message: 'That game has already started!'})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clients[games[gameId].blue]) {
|
||||||
|
return me.socket.emit('game_error', {message: 'That game has ended!'})
|
||||||
|
}
|
||||||
|
|
||||||
|
let game = games[gameId]
|
||||||
|
|
||||||
|
game.red = uid
|
||||||
|
|
||||||
|
game.isWaiting = false
|
||||||
|
game.started = new Date()
|
||||||
|
|
||||||
|
let opponent = clients[game.blue]
|
||||||
|
|
||||||
|
if (!opponent) {
|
||||||
|
return me.socket.emit('game_error', {message: 'Your opponent abruptly dissappeared, what?'})
|
||||||
|
}
|
||||||
|
|
||||||
|
opponent.socket.emit('game_start', {gameId: gameId, opponentId: uid, opponentName: me.name, color: 'blue'})
|
||||||
|
me.socket.emit('game_start', {gameId: gameId, opponentId: opponent.uid, opponentName: opponent.name, color: 'red'})
|
||||||
|
|
||||||
|
game.turn = 'blue'
|
||||||
|
clients[game.blue].socket.emit('turn', true)
|
||||||
|
clients[uid].socket.emit('turn', false)
|
||||||
|
|
||||||
|
totalGames += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function endGame (gameId, victoryId, loserId, status) {
|
||||||
|
if (clients[victoryId]) {
|
||||||
|
clients[victoryId].socket.emit('game_end', {win: true, result: status})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clients[loserId]) {
|
||||||
|
clients[loserId].socket.emit('game_end', {win: false, result: status})
|
||||||
|
}
|
||||||
|
|
||||||
|
delete games[gameId]
|
||||||
|
console.log(gameId + ' ended with ' + victoryId + '\'s victory.')
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitingGamesList (uid) {
|
||||||
|
let result = []
|
||||||
|
let cap = 0
|
||||||
|
|
||||||
|
let gamesInSession = 0
|
||||||
|
for (let i in games) {
|
||||||
|
let game = games[i]
|
||||||
|
if (!game.isWaiting) {
|
||||||
|
gamesInSession += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let gameId in games) {
|
||||||
|
if (cap >= 20) break
|
||||||
|
|
||||||
|
let game = games[gameId]
|
||||||
|
|
||||||
|
if (game.isWaiting) {
|
||||||
|
let userName = clients[game.blue].name
|
||||||
|
if (uid && game.blue === uid) continue
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
gameId: gameId,
|
||||||
|
name: userName,
|
||||||
|
started: game.started
|
||||||
|
})
|
||||||
|
|
||||||
|
cap += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sessions: gamesInSession,
|
||||||
|
totalGames: totalGames,
|
||||||
|
list: result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function determinePlayerById (gameId, uid) {
|
||||||
|
let game = games[gameId]
|
||||||
|
|
||||||
|
if (!game) return null
|
||||||
|
|
||||||
|
if (game.blue && game.blue === uid) {
|
||||||
|
return 'blue'
|
||||||
|
} else if (game.red && game.red === uid) {
|
||||||
|
return 'red'
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPiece (game, col, index) {
|
||||||
|
if (col > 8 || index > 8) return
|
||||||
|
col = game.places[col]
|
||||||
|
|
||||||
|
if (!col) return
|
||||||
|
|
||||||
|
if (!col.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let match = null
|
||||||
|
for (let i in col) {
|
||||||
|
if (col[i].y === index) {
|
||||||
|
match = col[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectWin (color, game) {
|
||||||
|
let win = false
|
||||||
|
for (let c in game.places) {
|
||||||
|
let col = game.places[c]
|
||||||
|
for (let p in col) {
|
||||||
|
let piece = col[p]
|
||||||
|
let matches = 0
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
let pAt = getPiece(game, parseInt(c) + i, piece.y)
|
||||||
|
if (pAt && pAt.color === color) {
|
||||||
|
console.log(pAt)
|
||||||
|
matches += 1
|
||||||
|
} else {
|
||||||
|
matches = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches >= 4) {
|
||||||
|
win = true
|
||||||
|
console.log('horizontal win')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
matches = 0
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
let pAt = getPiece(game, parseInt(c), piece.y + i)
|
||||||
|
if (pAt && pAt.color === color) {
|
||||||
|
matches += 1
|
||||||
|
} else {
|
||||||
|
matches = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches >= 4) {
|
||||||
|
console.log('vertical win')
|
||||||
|
win = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
matches = 0
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
let pAt = getPiece(game, parseInt(c) + i, piece.y - i)
|
||||||
|
if (pAt && pAt.color === color) {
|
||||||
|
matches += 1
|
||||||
|
} else {
|
||||||
|
matches = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches >= 4) {
|
||||||
|
console.log('diagonal right win')
|
||||||
|
win = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
matches = 0
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
let pAt = getPiece(game, parseInt(c) - i, piece.y - i)
|
||||||
|
if (pAt && pAt.color === color) {
|
||||||
|
matches += 1
|
||||||
|
} else {
|
||||||
|
matches = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches >= 4) {
|
||||||
|
console.log('diagonal left win')
|
||||||
|
win = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return win
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectTie (game) {
|
||||||
|
let tie = true
|
||||||
|
for (let c in game.places) {
|
||||||
|
let col = game.places[c]
|
||||||
|
if (col.length !== 9) {
|
||||||
|
tie = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tie
|
||||||
|
}
|
||||||
|
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
socket.on('session_create', (data) => {
|
||||||
|
if (!data.name) {
|
||||||
|
return socket.emit('login_status', {success: false, message: 'Invalid name.'})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playerNameValidation(data.name)) {
|
||||||
|
return socket.emit('login_status', {success: false, message: 'Invalid name.'})
|
||||||
|
}
|
||||||
|
|
||||||
|
let playerUid = nuid(32)
|
||||||
|
|
||||||
|
socket.emit('login_status', {success: true, uid: playerUid, name: data.name})
|
||||||
|
clients[playerUid] = {
|
||||||
|
socket: socket,
|
||||||
|
name: data.name,
|
||||||
|
sockID: socket.conn.id
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('New player: "' + data.name + '" with uid ' + playerUid)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('poll_games', () => {
|
||||||
|
let client = clientsBySocketID(socket.conn.id)
|
||||||
|
socket.emit('poll_games_res', waitingGamesList(client))
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('game_attempt_join', (data) => {
|
||||||
|
let client = clientsBySocketID(socket.conn.id)
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
||||||
|
socket.emit('force_relog')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.gameId) return
|
||||||
|
|
||||||
|
joinGame(client, data.gameId)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('leave_game', (data) => {
|
||||||
|
let client = clientsBySocketID(socket.conn.id)
|
||||||
|
|
||||||
|
if (!client) return
|
||||||
|
killGamesClientIsIn(client)
|
||||||
|
|
||||||
|
socket.emit('left_success')
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('new_game', () => {
|
||||||
|
let client = clientsBySocketID(socket.conn.id)
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
||||||
|
socket.emit('force_relog')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewGame(client)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('chat_send', (data) => {
|
||||||
|
let client = clientsBySocketID(socket.conn.id)
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
||||||
|
socket.emit('force_relog')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let game = games[data.gameId]
|
||||||
|
let playerInGame = determinePlayerById(data.gameId, client)
|
||||||
|
|
||||||
|
if (!playerInGame) {
|
||||||
|
socket.emit('game_error', {message: 'unexpected error. code: 763'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let opponent = determineOpponent(playerInGame)
|
||||||
|
let opponentObj = game[opponent]
|
||||||
|
let me = game[playerInGame]
|
||||||
|
|
||||||
|
clients[opponentObj].socket.emit('chat', {name: clients[me].name, message: data.message})
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('place_at', (data) => {
|
||||||
|
let client = clientsBySocketID(socket.conn.id)
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
socket.emit('game_error', {message: 'You are not logged in properly!'})
|
||||||
|
socket.emit('force_relog')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let game = games[data.gameId]
|
||||||
|
let playerInGame = determinePlayerById(data.gameId, client)
|
||||||
|
|
||||||
|
if (!playerInGame) {
|
||||||
|
socket.emit('game_error', {message: 'unexpected error. code: 763'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.column == null || data.column > 9) {
|
||||||
|
socket.emit('game_error', {message: 'Unexpected column'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let opponent = determineOpponent(playerInGame)
|
||||||
|
opponent = game[opponent]
|
||||||
|
|
||||||
|
let me = game[playerInGame]
|
||||||
|
|
||||||
|
clients[me].socket.emit('place', {column: data.column, color: playerInGame})
|
||||||
|
clients[opponent].socket.emit('place', {column: data.column, color: playerInGame})
|
||||||
|
|
||||||
|
game.places[data.column].push({color: playerInGame, y: 8 - game.places[data.column].length})
|
||||||
|
console.log(game.places[data.column])
|
||||||
|
|
||||||
|
if (detectWin(playerInGame, game)) {
|
||||||
|
endGame(data.gameId, me, opponent, 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detectTie(game)) {
|
||||||
|
endGame(data.gameId, me, opponent, 2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clients[me].socket.emit('turn', false)
|
||||||
|
clients[opponent].socket.emit('turn', true)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
let client = clientsBySocketID(socket.conn.id)
|
||||||
|
if (!client) return
|
||||||
|
|
||||||
|
killGamesClientIsIn(client)
|
||||||
|
|
||||||
|
console.log('Player uid ' + client + ' left.')
|
||||||
|
|
||||||
|
delete clients[client]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(8245)
|
Loading…
Reference in New Issue
Block a user