Skip to content
Snippets Groups Projects
Commit 61701c54 authored by Oliver's avatar Oliver
Browse files

Initial template

parents
Branches
No related tags found
No related merge requests found
# Node build artifacts
node_modules
npm-debug.log
# Local development
*.env
*.dev
.DS_Store
# Docker
Dockerfile
docker-compose.yml
web: npm start
app.js 0 → 100644
'use strict';
//Set up express
const express = require('express');
const app = express();
//Setup socket.io
const server = require('http').Server(app);
const io = require('socket.io')(server);
//Setup static page handling
app.set('view engine', 'ejs');
app.use('/static', express.static('public'));
//Handle client interface on /
app.get('/', (req, res) => {
res.render('client');
});
//Start the server
function startServer() {
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
}
//Chat message
function handleChat(message) {
console.log('Handling chat: ' + message);
io.emit('chat',message);
}
//Handle new connection
io.on('connection', socket => {
console.log('New connection');
//Handle on chat message received
socket.on('chat', message => {
handleChat(message);
});
//Handle disconnection
socket.on('disconnect', () => {
console.log('Dropped connection');
});
});
//Start server
if (module === require.main) {
startServer();
}
module.exports = server;
app.yaml 0 → 100644
# Use NodeJS on flexible app engine
runtime: nodejs
env: flex
# Only use one instance
manual_scaling:
instances: 1
# Use low resources during development and testing
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10
This diff is collapsed.
{
"name": "ecsexample",
"version": "1.0.0",
"description": "ECS Example Game",
"main": "app.js",
"engines": {
"node": "16.x"
},
"scripts": {
"start": "node app.js",
"gdeploy": "gcloud app deploy --version test",
"hdeploy": "git push heroku master"
},
"author": "Oli Bills",
"license": "ECS",
"dependencies": {
"ejs": "^3.1.6",
"express": "^4.17.1",
"socket.io": "^4.3.1"
}
}
public/background.jpg

113 KiB

var socket = null;
//Prepare game
var app = new Vue({
el: '#game',
data: {
error: null,
chatmessage: '',
me: { name: '', state: 0, score: 0 },
state: { state: false },
players: {},
},
mounted: function() {
connect();
},
methods: {
admin(command) {
socket.emit('admin',command)
},
action() {
socket.emit('action','advance');
},
join() {
socket.emit('join');
},
chat() {
socket.emit('chat',this.chatmessage);
this.chatmessage = '';
},
announce(message) {
const messages = document.getElementById('messages');
var item = document.createElement('li');
item.textContent = message;
messages.prepend(item);
},
update(data) {
this.me = data.me;
this.state = data.state;
this.players = data.players;
},
fail(message) {
this.error = message;
setTimeout(clearError, 3000);
},
capitalise(text) {
return text.charAt(0).toUpperCase() + text.slice(1);
}
}
});
function clearError() {
app.error = null;
}
function connect() {
//Prepare web socket
socket = io();
socket.on('connect', function() {
app.state.state = 0;
});
socket.on('connect_error', function(message) {
alert('Unable to connect: ' + message);
});
socket.on('disconnect', function() {
alert('Disconnected');
app.state = { state: -1 };
});
socket.on('fail', function(message) {
app.fail(message);
});
socket.on('state', function(data) {
app.update(data);
});
socket.on('chat', function(message) {
app.announce(message);
});
}
body {
background-image: url('/static/background.jpg');
background-size: cover;
background-repeat: no-repeat;
background-color: #acc5d0;
}
.title {
font-weight: bold;
font-size: 32px;
text-align: center;
}
.player {
background-color: white;
border: 1px solid black;
padding: 2px;
border-radius: 3px;
margin-bottom: 3px;
}
.player.me {
color: blue;
}
.admin {
background-color: black;
color: white;
text-align: center;
}
.container {
background-color: rgba(255,255,255,0.8);
padding-bottom: 10px;
padding-top: 10px;
height: 100vh;
}
#messages {
list-style-type: none;
margin: 0;
padding: 0;
overflow: auto;
max-height: 80vh;
}
#messages li {
padding: 5px 10px;
}
#messages li:nth-child(odd) {
background-color: rgba(0,0,0,0.2);
}
<div>
<input type="text" @keyup.enter="chat()" v-model="chatmessage" class="form-control" placeholder="Chat">
<ul id="chat">
<li v-for="message in messages">{{message}}</li>
</ul>
</div>
<!doctype html>
<html lang="en">
<%- include('header'); -%>
<h2 class="title">ECSterminate: <span style="color: red">Red</span> Light, <span style="color: green">Green</span> Light</h2>
<div id="game">
<div class="row">
<div class="col-md-9">
<!-- Error Display -->
<div v-if="error" class="alert alert-danger">
<strong>Error: </strong> {{error}}
</div>
<!-- Waiting for connection -->
<div v-if="state.state === false">
<%- include('loading') -%>
</div>
<!-- Waiting for player to join -->
<div v-else-if="state.state == 0">
<%- include('join') -%>
</div>
<!-- Playing the game -->
<div v-else-if="state.state == 1">
<%- include('play') -%>
</div>
<!-- Playing the game -->
<div v-else-if="state.state == 2">
<%- include('scores') -%>
</div>
<div class="admin" v-if="me.name == 1">
<p><strong>Debug information:</strong> My state: {{me.state}} | Game state: {{state.state}}</p>
</div>
</div>
<div class="col-md-3">
<input type="text" @keyup.enter="chat()" v-model="chatmessage" class="form-control" placeholder="Chat" v-if="me.state > 0">
<ul id="messages">
</ul>
</div>
</div>
</div>
<%- include('footer'); -%>
</html>
</div>
</main>
<!-- Bootstrap Javascript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<!-- SocketIO -->
<script src="https://cdn.socket.io/4.3.2/socket.io.min.js" integrity="sha384-KAZ4DtjNhLChOB/hxXuKqhMLYvx3b5MlT55xPEiNmREKRzeEm+RVPlTnAn0ajQNs" crossorigin="anonymous"></script>
<!-- VueJS -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- Application Javascript -->
<script src="/static/game.js"></script>
</body>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<!-- Local CSS -->
<link href="/static/main.css" rel="stylesheet">
<title>ECSTerminate</title>
</head>
<body>
<main>
<div class="container">
<div v-if="me.state == 0">
<p><button class="btn btn-lg btn-primary" @click="join">Join Game</button></p>
</div>
<div v-else>
<p>Waiting for other players...</p>
<div class="row">
<div v-for="player of players" class="col-sm-2 text-center">
<div class="player">
<strong>Player {{player.name}}</strong>
</div>
</div>
<div v-if="me.name == 1">
<button class="btn btn-lg btn-success" @click="admin('start')">Start Game</button>
</div>
</div>
</div>
<p>Connecting...</p>
<div class="row">
<div class="col-sm-4 text-center">
<p><strong>Time Remaining:</strong></p>
<p>{{state.countdown}}</p>
</div>
<div class="col-sm-4 text-center">
<div class="player me" v-if="me.state == 0">
<strong>Player {{me.name}}</strong>
<p>{{me.score}} / 100</p>
<p><button class="btn btn-sm btn-primary" @click="action('advance')">Step</button></p>
</div>
<div v-else-if="me.state == 1">
<h2>You have survived!</h2>
</div>
<div v-else-if="me.state == -1">
<h2>You have been eliminated!</h2>
</div>
</div>
<div class="col-sm-4 text-center">
<p><strong>The light is:</strong></p>
<p v-bind:style="{color: state.light}">{{capitalise(state.light)}}</p>
<p v-if="me.name == 1"><button class="btn btn-sm btn-primary" @click="admin('light')">Toggle</button></p>
</div>
</div>
<div class="row">
<div v-for="player of players" class="col-md-2 text-center" v-if="player.name != me.name && player.state > -1">
<div class="player">
<strong>Player {{player.name}}</strong>
<p>{{player.score}}</p>
</div>
</div>
</div>
<h2 class="text-center">The Survivors</h2>
<div class="row">
<div v-for="player of players" class="col-md-2 text-center" v-if="player.state > -1">
<div class="player">
<strong>Player {{player.name}}</strong>
<p>{{player.score}}</p>
</div>
</div>
</div>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment