Centurion together

master
Wilco Kruijer 5 years ago
parent 3f7987f482
commit c54f9b6273
  1. 2713
      backend/data/timeline.js
  2. 59
      backend/src/Lobby.js
  3. 15
      backend/src/app.js
  4. 25
      backend/src/index.js
  5. 6
      backend/src/state.js
  6. 39
      backend/src/timeline.js
  7. 15
      backend/test/a.js
  8. 7
      backend/test/b.js
  9. 12
      backend/test/state.js
  10. 16
      frontend/src/App.tsx
  11. 23
      frontend/src/components/Feed.tsx
  12. 6
      frontend/src/components/Lobby.tsx
  13. 38
      frontend/src/components/NextShot.tsx
  14. 20
      frontend/src/components/ShotsTaken.tsx
  15. 2
      frontend/src/css/lobby.css
  16. 17
      frontend/src/types/types.ts

File diff suppressed because it is too large Load Diff

@ -1,5 +1,5 @@
const User = require("./User.js"); const User = require("./User.js");
const timeline = require("./timeline.js");
module.exports = class Lobby { module.exports = class Lobby {
/** /**
* @type {User[]} * @type {User[]}
@ -11,23 +11,58 @@ module.exports = class Lobby {
leaderId = undefined; leaderId = undefined;
running = false; running = false;
runningInterval = undefined; startTime = 0;
currentTime = 0; currentSeconds = 0;
timelineIndex = 0;
// For debugging purposes
speedFactor = 1;
constructor(name) { constructor(name) {
this.name = name; this.name = name;
} }
run() { run(ioLobby) {
this.running = true; this.running = true;
this.runningInterval = setInterval(() => { this.startTime = Date.now();
this.currentTime += 1;
}, 1000); const doTick = () => {
} if (this.users.length === 0) {
// this lobby is over.
pause() { return;
this.running = false; }
clearInterval(this.runningInterval);
console.log(this.currentSeconds);
const timestamp = timeline.getIndex(this.timelineIndex);
const nextShot = timeline.getNextShot(this.timelineIndex);
if (!timestamp) {
// We are done.
console.log("Done");
this.running = false;
return;
}
ioLobby.emit('tick_event', {
current: this.currentSeconds,
next: timestamp,
nextShot: nextShot
});
if (this.currentSeconds >= timestamp.timestamp) {
this.timelineIndex += 1;
}
this.currentSeconds += 1;
// We spend some time processing, wait a bit less than 1000ms
const nextTickTime = this.startTime + (1000 * this.currentSeconds / this.speedFactor);
const waitTime = nextTickTime - Date.now();
console.log("waiting", waitTime);
setTimeout(doTick, Math.floor(waitTime / this.speedFactor));
};
doTick();
} }
/** /**

@ -0,0 +1,15 @@
const express = require("express");
const socketIO = require("socket.io");
const state = require("./state.js");
const PORT = 3001;
const app = express();
const server = app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`));
const io = socketIO(server);
app.get('/', (req, res) => res.send('<pre>' + JSON.stringify(state) + '</pre>'));
module.exports = {
app, server, io
};

@ -1,16 +1,7 @@
const express = require("express");
const socketIO = require("socket.io");
const service = require("./service.js"); const service = require("./service.js");
const state = require("./state.js"); const state = require("./state.js");
const PORT = 3001; const { io } = require('./app.js');
const app = express();
const server = app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`));
const io = socketIO(server);
app.get('/', (req, res) => res.send('<pre>' + JSON.stringify(state) + '</pre>'));
io.on('connection', socket => { io.on('connection', socket => {
const socketId = socket.id; const socketId = socket.id;
@ -67,5 +58,19 @@ io.on('connection', socket => {
status: 'ok', status: 'ok',
lobby: lobby lobby: lobby
}); });
});
socket.on('request_start', () => {
console.log('request start', socket.rooms);
const lobby = service.getUserLobby(socketId);
if (!lobby.isLeader(socketId)) {
console.warn("Non leader tried to start.");
return;
}
const ioLobby = io.to(lobby.name + "")
ioLobby.emit('started');
lobby.run(ioLobby);
}) })
}); });

@ -7,7 +7,6 @@ class State {
* @type {Object.<string, Lobby>} * @type {Object.<string, Lobby>}
*/ */
lobbies = {}; lobbies = {};
lobbyCount = 0;
constructor() { constructor() {
} }
@ -19,7 +18,8 @@ class State {
let lobby = undefined; let lobby = undefined;
while (!lobby) { while (!lobby) {
const id = getRandomInt(100, Math.max(1000, this.lobbyCount * 2)); const lobbyCount = Object.keys(this.lobbies).length;
const id = getRandomInt(100, Math.max(1000, lobbyCount * 2));
lobby = this.createLobby(id); lobby = this.createLobby(id);
} }
@ -49,12 +49,10 @@ class State {
return undefined; return undefined;
} }
this.lobbyCount += 1;
return new Lobby(lobbyId); return new Lobby(lobbyId);
} }
removeLobby(lobbyId) { removeLobby(lobbyId) {
this.lobbyCount -= 1;
delete this.lobbies[lobbyId]; delete this.lobbies[lobbyId];
} }
} }

@ -0,0 +1,39 @@
const timeline = require('../data/timeline.js');
/**
*
* @param i
* @returns {*}
*/
function getIndex(i) {
if (i >= timeline.length) {
return;
}
return timeline[i];
}
/**
* @param {number} i - the index.
* @returns {{count: number, timestamp: number}|undefined}
*/
function getNextShot(i) {
for (; i < timeline.length; i++) {
const time = getIndex(i);
for (let event of time.events) {
if (event.type === 'shot') {
return {
timestamp: time.timestamp,
count: event.shotCount
}
}
}
}
return undefined;
}
module.exports = {
getIndex, getNextShot
};

@ -1,15 +0,0 @@
const express = require('express');
const state = require('./state.js');
const b = require('./b.js');
const PORT = 3002;
const app = express();
const server = app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`));
app.get('/', (req, res) => {
let i = state.a;
b.test(i += 1);
return res.send('<pre>' + JSON.stringify(state) + '</pre>')
});

@ -1,7 +0,0 @@
const state = require('./state.js');
module.exports = {
test: (i) => {
state.setA(i);
}
};

@ -1,12 +0,0 @@
class State {
a = 3;
constructor() {
}
setA(i) {
this.a = i;
}
}
module.exports = new State();

@ -10,22 +10,20 @@ import Lobby from "./components/Lobby";
const App = () => { const App = () => {
const [connected, setConnected] = useState(false); const [started, setStarted] = useState(false);
const [lobbyId, setLobbyId] = useState(null);
useEffect(() => { useEffect(() => {
socket.on('welcome', async (obj: any) => { socket.on('started', async () => {
setLobbyId(obj.lobby.name); setStarted(true);
setConnected(true); console.log('ffin started')
}); });
return () => { return () => {
socket.off("SENDING_NEW_TIME"); socket.off("started");
socket.off("welcome");
} }
}); });
const gameContent = ( const feedContent = (
<Row> <Row>
<NextShot/> <NextShot/>
<Feed/> <Feed/>
@ -37,7 +35,7 @@ const App = () => {
<Lobby/> <Lobby/>
); );
const content = lobbyContent; const content = started ? feedContent : lobbyContent;
return ( return (
<> <>

@ -1,6 +1,7 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {Col, Row} from "antd" import {Col, Row} from "antd"
import socket from "../util/socket"; import socket from "../util/socket";
import {Tick} from "../types/types";
const Feed = () => { const Feed = () => {
@ -10,20 +11,20 @@ const Feed = () => {
}]); }]);
useEffect(() => { useEffect(() => {
socket.on("SENDING_NEW_TIME", (data: string) => { socket.on("tick_event", (tick: Tick) => {
console.log(data);
// console.log("Shot:", tick.nextShot?.timestamp, tick.nextShot?.count)
setCount(c => c + 1); // setCount(c => c + 1);
const newItems = feedItems; // const newItems = feedItems;
newItems.push({ // newItems.push({
title: "Ha1", body: "Doei", key: count // title: "Ha1", body: "Doei", key: count
}); // });
//
setFeedItems(newItems); // setFeedItems(newItems);
}); });
return () => { return () => {
socket.off("SENDING_NEW_TIME"); socket.off("tick_event");
} }
}); });

@ -42,7 +42,7 @@ const Lobby = () => {
<div className="lobby"> <div className="lobby">
<Row> <Row>
<Col span={24}> <Col span={24}>
<h1>Centurion! <img src={beer1} className="beer" alt="beer"/></h1> <h1 id="centurion-title">Centurion! <img src={beer1} className="beer" alt="beer"/></h1>
</Col> </Col>
</Row> </Row>
<Row> <Row>
@ -55,7 +55,7 @@ const Lobby = () => {
<Row> <Row>
<Col className="control" span={8} offset={8}> <Col className="control" span={8} offset={8}>
<Card title={`Lobby setup (${lobbyId}):`} actions={ isLeader ? [ <Card title={`Lobby setup (${lobbyId}):`} actions={ isLeader ? [
<span>Start</span>, <span onClick={async () => {await emit('request_start')}}>Start</span>,
]: []}> ]: []}>
<span> <span>
Huidige lobby <InputNumber size="small" min={100} max={100000} value={lobbyId} Huidige lobby <InputNumber size="small" min={100} max={100000} value={lobbyId}
@ -63,7 +63,7 @@ const Lobby = () => {
</span> </span>
<p>Er zijn {userCount} gebruiker(s) aanwezig.</p> <p>Er zijn {userCount} gebruiker(s) aanwezig.</p>
<p> {isLeader ? 'Jij bent de baas.' : 'Wachtend op de baas...'}</p> <p> {isLeader ? 'Jij bent de baas.' : 'Wachten op de baas...'}</p>
</Card> </Card>
</Col> </Col>

@ -1,26 +1,52 @@
import React, {useEffect} from 'react'; import React, {useEffect, useRef, useState} from 'react';
import {Col, Progress} from "antd" import {Col, Progress} from "antd"
import socket from "../util/socket"; import socket from "../util/socket";
import {Tick} from "../types/types";
const NextShot = () => { const NextShot = () => {
const [remaining, setRemaining] = useState(60);
const [remainingPercentage, setRemainingPercentage] = useState(100);
const fullTime = useRef(0);
useEffect(() => { useEffect(() => {
socket.on("SENDING_NEW_TIME", (data: string) => { socket.on("tick_event", (tick: Tick) => {
if (!tick.nextShot) {
setRemaining(0);
return;
}
if (fullTime.current === 0) {
fullTime.current = tick.nextShot.timestamp - tick.current;
}
const shotEvent = tick.next.events.find(e => e.type === 'shot');
if (shotEvent && tick.current === tick.next.timestamp) {
fullTime.current = 0;
}
const timeRemaining = tick.nextShot.timestamp - tick.current;
setRemaining(timeRemaining);
// Fix divide by zero (.. || 1)
setRemainingPercentage(Math.ceil(timeRemaining / (fullTime.current || 1) * 100));
}); });
return () => { return () => {
socket.off("SENDING_NEW_TIME"); socket.off("tick_event");
} }
}); }, []);
return ( return (
<Col className="sider" span={4}> <Col className="sider" span={4}>
<h1>Tijd tot volgende shot:</h1> <h1>Tijd tot volgende shot:</h1>
<Progress type="circle" <Progress type="circle"
percent={75} percent={remainingPercentage}
format={_ => remaining + ' sec.'}
strokeColor={"#304ba3"} strokeColor={"#304ba3"}
strokeWidth={10}/> strokeWidth={10}
status="normal"/>
</Col> </Col>
); );

@ -1,24 +1,34 @@
import React, {useEffect} from 'react'; import React, {useEffect, useRef, useState} from 'react';
import {Col, Progress} from "antd" import {Col, Progress} from "antd"
import socket from "../util/socket"; import socket from "../util/socket";
import {Tick} from "../types/types";
const ShotsTaken = () => { const ShotsTaken = () => {
const [remaining, setRemaining] = useState(100);
useEffect(() => { useEffect(() => {
socket.on("SENDING_NEW_TIME", (data: string) => { socket.on("tick_event", (tick: Tick) => {
if (!tick.nextShot) {
setRemaining(0);
return;
}
setRemaining(tick.nextShot.count - 1);
}); });
return () => { return () => {
socket.off("SENDING_NEW_TIME"); socket.off("tick_event");
} }
}); }, []);
return ( return (
<Col className="sider" span={4}> <Col className="sider" span={4}>
<h1>Shots genomen:</h1> <h1>Shots genomen:</h1>
<Progress type="circle" <Progress type="circle"
percent={25} percent={100 - remaining}
format={_ => remaining + ' / 100'}
status="normal"
strokeColor={"#304ba3"} strokeColor={"#304ba3"}
strokeWidth={10}/> strokeWidth={10}/>
</Col> </Col>

@ -1,4 +1,4 @@
h1 { #centurion-title {
font-size: 3.5rem; font-size: 3.5rem;
text-align: center; text-align: center;
min-height: inherit; min-height: inherit;

@ -0,0 +1,17 @@
export interface Tick {
current: number,
next: {
timestamp: number,
events: TimestampEvent[]
},
nextShot?: {
timestamp: number,
count: number
}
}
export interface TimestampEvent {
type: 'talk' | 'shot' | 'song' | 'time',
text: string[],
shotCount?: number
}
Loading…
Cancel
Save