voeg totale isolatie toe (nog zonder tijdlijn sniksnik)

master
Florens Douwes 4 years ago
parent 18aa4fa369
commit d5ab84f414
  1. 38
      backend/data/timelines.js
  2. 20
      backend/src/Room.ts
  3. 19
      backend/src/Service.ts
  4. 41
      backend/src/User.ts
  5. 48
      backend/src/index.ts
  6. 4
      backend/src/timeline.ts
  7. 29
      frontend/config-overrides.js
  8. 10
      frontend/src/components/Centurion.tsx
  9. 7
      frontend/src/components/Feed.tsx
  10. 181
      frontend/src/components/Lobby.tsx
  11. 10
      frontend/src/components/NextShot.tsx
  12. 43
      frontend/src/components/Player.ts
  13. 11
      frontend/src/components/ShotsTaken.tsx
  14. 1
      frontend/src/css/index.sass
  15. 4
      frontend/src/css/lobby.sass
  16. 41
      frontend/src/lib/Connection.ts
  17. 21
      frontend/src/types/types.ts

@ -2,6 +2,7 @@ module.exports = {
'timelines': [ 'timelines': [
{ {
'name': 'Centurion', 'name': 'Centurion',
'songFile': 'songs/centurion.m4a',
'feed': [ 'feed': [
{ {
@ -2728,6 +2729,43 @@ module.exports = {
] ]
} }
] ]
},
{
'name': 'Totale isolatie',
'songFile': 'songs/totale_isolatie.m4a',
'feed': [
{
'timestamp': 0,
'events': [
{
'type': 'talk',
'text': [
'Ik heb geen tekst',
'want het is veel werk'
]
}
]
},
{
'timestamp': 10,
'events': [
{
'type': 'talk',
'text': [
'Nee echt',
'niks'
]
},
{
'type': 'song',
'text': [
'Blahblah',
'blahblahblah'
]
}
]
}
]
} }
] ]
} }

@ -1,9 +1,14 @@
import {Socket} from "socket.io"; import {Socket} from "socket.io";
import User from "./User"; import User from "./User";
import {getIndex, getNextShot, getTimeline, indexForTime} from "./timeline"; import {getIndex, getNextShot, getTimeline, getTimelineNames, indexForTime} from "./timeline";
import {getCurrentTime} from "./util"; import {getCurrentTime} from "./util";
export interface RoomOptions {
seekTime: number
timelineName: string
}
export default class Room { export default class Room {
id: number = 0; id: number = 0;
users: User[] = []; users: User[] = [];
@ -14,6 +19,7 @@ export default class Room {
currentSeconds = 0; currentSeconds = 0;
timelineIndex: number = 0; timelineIndex: number = 0;
seekTime: number = 0;
timelineName: string = 'Centurion'; timelineName: string = 'Centurion';
// For debugging purposes // For debugging purposes
@ -31,6 +37,8 @@ export default class Room {
'running': this.running, 'running': this.running,
'startTime': this.startTime, 'startTime': this.startTime,
'timelineName': this.timelineName, 'timelineName': this.timelineName,
'seekTime': this.seekTime,
'readyToParticipate': user.readyToParticipate || this.leader == user,
} }
} }
@ -67,9 +75,17 @@ export default class Room {
onBeforeDelete() { onBeforeDelete() {
} }
setOptions(options: any) {
this.seekTime = Math.max(0, Math.min(options.seekTime, 250 * 60 * 1000))
if (getTimelineNames().indexOf(options.timelineName) >= 0) {
this.timelineName = options.timelineName;
}
this.sync()
}
start() { start() {
this.running = true; this.running = true;
this.startTime = getCurrentTime() - 1400 * 1000 this.startTime = getCurrentTime() - this.seekTime
this.sync(); this.sync();
} }

@ -1,7 +1,7 @@
import {Socket} from "socket.io"; import {Socket} from "socket.io";
import User from './User' import User from './User'
import Room from './Room' import Room, {RoomOptions} from './Room'
import {getCurrentTime, randomInt} from "./util"; import {getCurrentTime, randomInt} from "./util";
export default class Service { export default class Service {
@ -11,6 +11,7 @@ export default class Service {
onSocketConnect(socket: Socket) { onSocketConnect(socket: Socket) {
let user = new User(socket); let user = new User(socket);
this.socketsToUsers.set(socket.id, user); this.socketsToUsers.set(socket.id, user);
user.sync();
} }
onSocketDisconnect(socket: Socket) { onSocketDisconnect(socket: Socket) {
@ -36,6 +37,14 @@ export default class Service {
}) })
} }
onSetRoomOptions(socket: Socket, options: RoomOptions) {
let user = this.getUser(socket);
if (user.room?.getLeader() == user) {
user.room!!.setOptions(options)
}
}
onRequestStart(socket: Socket) { onRequestStart(socket: Socket) {
let user = this.getUser(socket); let user = this.getUser(socket);
@ -56,9 +65,17 @@ export default class Service {
let room = this.roomIdToRooms.get(roomId)!!; let room = this.roomIdToRooms.get(roomId)!!;
room.join(user); room.join(user);
return true; return true;
} }
onRequestReady(socket: Socket) {
let user = this.getUser(socket);
if (!user.room || user.readyToParticipate) return;
user.readyToParticipate = true;
user.sync();
}
onRequestJoinRandom(socket: Socket) { onRequestJoinRandom(socket: Socket) {
let user = this.getUser(socket); let user = this.getUser(socket);

@ -1,12 +1,14 @@
import {Socket} from "socket.io"; import {Socket} from "socket.io";
import Room from "./Room"; import Room from "./Room";
import {getTimelineNames} from "./timeline";
export default class User { export default class User {
socket: Socket; socket: Socket;
id: string; id: string;
room: Room | null = null; room: Room | null = null;
readyToParticipate: boolean = false;
constructor(socket: Socket) { constructor(socket: Socket) {
this.socket = socket; this.socket = socket;
@ -24,6 +26,7 @@ export default class User {
if (this.room != null) { if (this.room != null) {
this.socket.leave(this.room.id.toString()); this.socket.leave(this.room.id.toString());
this.readyToParticipate = false;
} }
this.room = room; this.room = room;
@ -35,18 +38,36 @@ export default class User {
this.sync(); this.sync();
} }
getConfig() {
return {
'availableTimelines': getTimelineNames()
}
}
sentConfig: any = null;
sentRoom: any = null; sentRoom: any = null;
sentTimelineName: string | null = null; sentTimelineName: string | null = null;
sync() { sync() {
if (!this.shallowEquals(this.sentRoom, this.room?.serialize(this))) { // Config
let config = this.getConfig();
if (!this.syncEquals(this.sentConfig, config)) {
this.sentConfig = config;
this.emit('config', {
'config': this.sentConfig
});
}
// Room
if (!this.syncEquals(this.sentRoom, this.room?.serialize(this))) {
this.sentRoom = this.room?.serialize(this); this.sentRoom = this.room?.serialize(this);
this.emit('room', { this.emit('room', {
'room': this.sentRoom 'room': this.sentRoom
}) })
} }
if (!this.shallowEquals(this.sentTimelineName, this.room?.timelineName)) { // Timeline
if (!this.syncEquals(this.sentTimelineName, this.room?.timelineName)) {
this.sentTimelineName = this.room?.timelineName || null; this.sentTimelineName = this.room?.timelineName || null;
this.emit('timeline', { this.emit('timeline', {
'timeline': this.sentTimelineName == null ? null : this.room!!.serializeTimeline(this) 'timeline': this.sentTimelineName == null ? null : this.room!!.serializeTimeline(this)
@ -58,20 +79,30 @@ export default class User {
this.socket.emit(eventName, obj); this.socket.emit(eventName, obj);
} }
shallowEquals(obj1: any, obj2: any) { syncEquals(obj1: any, obj2: any): boolean {
if (obj1 === undefined && obj2 === undefined)
return true;
if ((obj1 === undefined && obj2 !== undefined) || (obj1 !== undefined && obj2 === undefined))
return false;
if (obj1 === null && obj2 === null) if (obj1 === null && obj2 === null)
return true; return true;
if ((obj1 === null && obj2 !== null) || (obj1 !== null && obj2 === null)) if ((obj1 === null && obj2 !== null) || (obj1 !== null && obj2 === null))
return false; return false;
if (typeof (obj1) != typeof (obj2)) if (typeof (obj1) !== typeof (obj2))
return false; return false;
if (typeof (obj1) === 'string' || typeof (obj1) === 'number' || typeof (obj1) === 'boolean') {
return obj1 === obj2;
}
if (Object.keys(obj1).length !== Object.keys(obj2).length) return false if (Object.keys(obj1).length !== Object.keys(obj2).length) return false
return Object.keys(obj1).every(key => return Object.keys(obj1).every(key =>
obj2.hasOwnProperty(key) && obj1[key] === obj2[key] obj2.hasOwnProperty(key) && this.syncEquals(obj1[key], obj2[key])
); );
} }
} }

@ -34,7 +34,15 @@ io.on('connection', socket => {
service.onTimeSync(socket, requestId, clientTime); service.onTimeSync(socket, requestId, clientTime);
}) })
socket.on('request_start', () => { socket.on('room_options', (options) => {
if (!options) return;
if (!options.timelineName || typeof (options.timelineName) !== 'string') return;
if (!Number.isSafeInteger(options.seekTime)) return;
service.onSetRoomOptions(socket, options);
});
socket.on('request_start', (options) => {
service.onRequestStart(socket); service.onRequestStart(socket);
}); });
@ -44,6 +52,10 @@ io.on('connection', socket => {
service.onRequestJoin(socket, roomId); service.onRequestJoin(socket, roomId);
}); });
socket.on('request_ready', () => {
service.onRequestReady(socket);
})
socket.on('request_join_random', () => { socket.on('request_join_random', () => {
service.onRequestJoinRandom(socket); service.onRequestJoinRandom(socket);
}) })
@ -66,23 +78,23 @@ io.on('connection', socket => {
call.respond(service.hasRoomId(roomId)); call.respond(service.hasRoomId(roomId));
return; return;
} }
//
if (name == 'request_join') { // if (name == 'request_join') {
let roomId = params && params['roomId']; // let roomId = params && params['roomId'];
if (!Number.isSafeInteger(roomId)) { // if (!Number.isSafeInteger(roomId)) {
call.error('Invalid room id'); // call.error('Invalid room id');
return; // return;
} // }
if (!service.hasRoomId(roomId)) { // if (!service.hasRoomId(roomId)) {
call.respond(false); // call.respond(false);
return; // return;
} // }
if (service.onRequestJoin(socket, roomId)) { // if (service.onRequestJoin(socket, roomId)) {
call.respond(true); // call.respond(true);
} else { // } else {
call.respond(false); // call.respond(false);
} // }
} // }
}) })
service.onSocketConnect(socket); service.onSocketConnect(socket);

@ -2,6 +2,10 @@
import timeline from '../data/timelines.js'; import timeline from '../data/timelines.js';
export function getTimelineNames(): string[] {
return timeline.timelines.map((i: any) => i.name)
}
export function getTimeline(name: string) { export function getTimeline(name: string) {
let t = timeline.timelines.find((i: any) => i.name == name); let t = timeline.timelines.find((i: any) => i.name == name);
if (!t) return null; if (!t) return null;

@ -1,4 +1,25 @@
module.exports = function override(config, env) { module.exports = {
// do stuff with the webpack config... webpack: function (config, env) {
return config; return config;
}; },
devServer: function (configFunction) {
// Return the replacement function for create-react-app to use to generate the Webpack
// Development Server config. "configFunction" is the function that would normally have
// been used to generate the Webpack Development server config - you can use it to create
// a starting configuration to then modify instead of having to create a config from scratch.
return function (proxy, allowedHost) {
// Create the default config by calling configFunction with the proxy/allowedHost parameters
const config = configFunction(proxy, allowedHost);
config.proxy = {
"/socket.io": {
target: "http://localhost:3001",
ws: true
}
}
// Return your customised Webpack Development Server config.
return config;
};
}
}

@ -1,8 +1,7 @@
import React, {useRef, useState} from "react"; import React from "react";
import {Row} from "antd"; import {Row} from "antd";
import {NumberParam, useQueryParam} from "use-query-params";
import {roomTime, useRoom, useRoomRunningChanged, useTick} from "../lib/Connection"; import {useRoomRunningAndReadyChanged} from "../lib/Connection";
import NextShot from "./NextShot"; import NextShot from "./NextShot";
import Feed from "./Feed"; import Feed from "./Feed";
import ShotsTaken from "./ShotsTaken"; import ShotsTaken from "./ShotsTaken";
@ -12,7 +11,8 @@ import logo from "../img/via-logo.svg";
import Player from "./Player"; import Player from "./Player";
const Centurion = () => { const Centurion = () => {
let roomRunning = useRoomRunningChanged()?.running || false; const room = useRoomRunningAndReadyChanged();
const showFeed = (room?.running && room.readyToParticipate) || false;
const feedContent = ( const feedContent = (
<React.Fragment> <React.Fragment>
@ -32,7 +32,7 @@ const Centurion = () => {
return ( return (
<> <>
<section className="content"> <section className="content">
{roomRunning ? feedContent : lobbyContent} {showFeed ? feedContent : lobbyContent}
</section> </section>
<footer> <footer>
<img src={logo} className="via-logo" alt="logo"/> <img src={logo} className="via-logo" alt="logo"/>

@ -1,23 +1,22 @@
import React, {useRef} from 'react'; import React from 'react';
import {Col} from "antd" import {Col} from "antd"
import {TimelineItem} from "../types/types"; import {TimelineItem} from "../types/types";
import FeedItem from "./FeedItem" import FeedItem from "./FeedItem"
import connection, {roomTime, useRoom, useRoomRunningChanged, useTimeline} from "../lib/Connection"; import {roomTime, useTimeline} from "../lib/Connection";
import {useUpdateAfterDelay} from "../util/hooks"; import {useUpdateAfterDelay} from "../util/hooks";
import CSSTransition from "react-transition-group/CSSTransition"; import CSSTransition from "react-transition-group/CSSTransition";
import TransitionGroup from "react-transition-group/TransitionGroup"; import TransitionGroup from "react-transition-group/TransitionGroup";
const Feed = (props: any) => { const Feed = (props: any) => {
const roomRunning = useRoomRunningChanged()?.running || false;
const timeline = useTimeline(); const timeline = useTimeline();
useUpdateAfterDelay(500) useUpdateAfterDelay(500)
let liveFeed: TimelineItem[] = []; let liveFeed: TimelineItem[] = [];
if (roomRunning && timeline != null) { if (timeline != null) {
liveFeed = timeline.feed.filter(item => { liveFeed = timeline.feed.filter(item => {
return item.timestamp * 1000 <= roomTime() return item.timestamp * 1000 <= roomTime()
}); });

@ -1,33 +1,40 @@
import React, {MouseEvent, useState} from 'react'; import React, {MouseEvent, useState} from 'react';
import {Input, Button, Card, Col, InputNumber, Row, Space, Divider} from "antd" import {Button, Card, Col, Divider, Form, Input, InputNumber, Row, Select} from "antd"
import { red } from '@ant-design/colors'; import {red} from '@ant-design/colors';
import connection, {useIsConnected, useRoom} from "../lib/Connection"; import connection, {useConfig, useIsConnected, useRoom} from "../lib/Connection";
import "../css/lobby.sass"; import "../css/lobby.sass";
import beer from "../img/beer.png" import beer from "../img/beer.png"
import {RoomOptions} from "../types/types";
const {Option} = Select;
const Lobby = (props: any) => { const Lobby = (props: any) => {
const [seekTime, setSeekTime] = useState(0); // Form/control states.
const [selectedRoomId, setSelectedRoomId] = useState(1); const [selectedRoomId, setSelectedRoomId] = useState(1);
const [seekTime, setSeekTime] = useState(0);
const [timelineName, setTimelineName] = useState(null);
const [joiningLobby, setJoiningLobby] = useState(false); const [joiningLobby, setJoiningLobby] = useState(false);
const [joinLobbyError, setJoinLobbyError] = useState(false); const [joinLobbyError, setJoinLobbyError] = useState(false);
// Room and logic states.
const isConnected = useIsConnected(); const isConnected = useIsConnected();
const room = useRoom(); const room = useRoom();
const config = useConfig();
// @ts-ignore
const connectionType = connection.socket.io.engine.transport.name;
let isLeader = room?.isLeader || false; let isLeader = room?.isLeader || false;
let userCount = room?.userCount || 0; let userCount = room?.userCount || 0;
function handleRequestStartClicked(e: MouseEvent) { function handleRequestStartClicked(e: MouseEvent) {
e.preventDefault(); connection.requestStart(seekTime * 1000);
connection.requestStart();
} }
function handleJoin(e: MouseEvent) { function handleJoin(e: MouseEvent) {
// connection.requestStart(); connection.requestReady();
} }
function applyRoomId(v: number) { function applyRoomId(v: number) {
@ -38,44 +45,79 @@ const Lobby = (props: any) => {
setJoiningLobby(true) setJoiningLobby(true)
} }
function joinRandomLobby() { function handleJoinRandomLobby() {
connection.requestJoinRandom() connection.requestJoinRandom()
setJoinLobbyError(false); setJoinLobbyError(false);
} }
// const {socket} = useSocket("welcome", async (obj: any) => { function handleTimelineNameSet(timelineName: any) {
// if (lobbyQueryId) { setTimelineName(timelineName);
// // lobbyId is already defined, this means we have a queryparam set. connection.setRoomOptions(new RoomOptions(
// await onChangeLobbyInput(lobbyQueryId); seekTime || 0,
// return; timelineName || room?.timelineName || ''))
// }
// console.log("Got welcome", lobbyQueryId);
//
// setLobbyId(obj.room.name);
// setIsLeader(socket.id === obj.room.leaderId);
// setUserCount(obj.room.users?.length || 0);
// });
// const socketRef = useRef<SocketIOClient.Socket>(socket);
async function onChangeRoomInput(i: any) {
// setLobbyId(i);
// const result: any = await emit(connection.socket, 'join_room', i);
// setIsLeader(connection.socket.id === result.room.leaderId);
// setUserCount(result.room.users?.length || 0);
// connection.requestJoin(i);
} }
// useEffect(() => { function handleSetSeekTime(seekTime: number) {
// async function wrapper() { setSeekTime(seekTime);
// const result: any = await emit(connection.current, 'room_info'); connection.setRoomOptions(new RoomOptions(
// setIsLeader(connection.current.id === result.room.leaderId); seekTime * 1000 || 0,
// setUserCount(result.room.users?.length || 0); timelineName || room?.timelineName || ''))
// } }
//
// wrapper();
// }, []);
let leaderConfig = (
<Row justify="center">
<Col>
<Form
layout='horizontal'
labelCol={{span: 8}}
wrapperCol={{span: 24}}
>
<Form.Item label="Starttijd">
<Input
type="number"
suffix="sec"
value={seekTime}
onChange={v => handleSetSeekTime(parseInt(v.target.value) || 0)}/>
</Form.Item>
<Form.Item label="Nummer">
<Select defaultValue={(room && room.timelineName) || ''}
onChange={e => handleTimelineNameSet(e)}>
{config && config.availableTimelines.map((item, i) =>
<Option key={item} value={item}>{item}</Option>
)}
</Select>
</Form.Item>
</Form>
<Button
block
type="primary"
onClick={handleRequestStartClicked}>Start</Button>
</Col>
</Row>
)
let nonLeaderConfig = (
<Row justify="center">
<Col>
<p>
We gaan luisteren naar <b>{room && room.timelineName}</b> en
{room?.running && <span> zijn al gestart!</span>}
{!room?.running && <span> starten op {room?.seekTime} seconden</span>}
</p>
<Button
block
type="primary"
disabled={!room || room.readyToParticipate}
onClick={handleJoin}>{room && room.readyToParticipate ? 'Wachten op het startsein' : 'Kom erbij'}</Button>
</Col>
</Row>
)
// @ts-ignore
return ( return (
<div className="lobby"> <div className="lobby">
<Row> <Row>
@ -106,51 +148,28 @@ const Lobby = (props: any) => {
{isConnected && {isConnected &&
<Row justify="center"> <Row justify="center">
<Col xs={12} sm={10} md={10} xl={6}> <Col xs={24} sm={16} md={12} xl={10}>
<Card> <Card>
<h3>Huidige lobby: <b>{room?.id || 'Geen lobby'}</b></h3> <h3>Huidige lobby: <b>{room?.id || 'Geen lobby'}</b></h3>
<Row> {/*<span>Verbonden met {connectionType}</span>*/}
<Col>
{userCount === 1 ?
<p>Er is één gebruiker aanwezig.</p>
:
<p>Er zijn {userCount} gebruikers aanwezig.</p>
}
<p>{isLeader ? 'Jij bent de baas van deze lobby.' : 'Wachten tot de baas de mix start.'}</p>
</Col> {room &&
</Row> <span>
{userCount === 1 ?
<Row> <p>Er is één gebruiker aanwezig.</p>
<Col> :
{isLeader && <p>Er zijn {userCount} gebruikers aanwezig.</p>
<span>Start de mix op }
<Input </span>
type="number"
min={0}
max={60000}
suffix="sec"
value={seekTime}
onChange={v => setSeekTime(parseInt(v.target.value) || 0)}/>
</span>
}
</Col>
</Row>
{isLeader ?
<Button
block
type="primary"
onClick={handleRequestStartClicked}>Start</Button>
:
<Button
block
type="primary"
disabled={!room}
onClick={handleJoin}>Join</Button>
} }
{room &&
<span>Deel de link met je vrienden om mee te doen!</span>
}
<br/>
<br/>
{room && (isLeader ? leaderConfig : nonLeaderConfig)}
<Divider/> <Divider/>
@ -185,7 +204,7 @@ const Lobby = (props: any) => {
<Col> <Col>
<Button type="primary" <Button type="primary"
onClick={() => { onClick={() => {
joinRandomLobby() handleJoinRandomLobby()
}}>Join een willekeurige lobby</Button> }}>Join een willekeurige lobby</Button>
</Col> </Col>
</Row> </Row>

@ -1,14 +1,10 @@
import React, {useRef, useState} from 'react'; import React from 'react';
import {Col, Progress} from "antd" import {Col, Progress} from "antd"
import {useSocket} from "use-socketio/lib"; import {roomTime, useTimeline} from "../lib/Connection";
import {Tick} from "../types/types";
import connection, {roomTime, useRoom, useTimeline} from "../lib/Connection";
import {useUpdateAfterDelay} from "../util/hooks"; import {useUpdateAfterDelay} from "../util/hooks";
const NextShot = () => { const NextShot = () => {
const room = useRoom()
const timeline = useTimeline() const timeline = useTimeline()
useUpdateAfterDelay(1000) useUpdateAfterDelay(1000)
@ -16,7 +12,7 @@ const NextShot = () => {
let remainingTime = 0; let remainingTime = 0;
let remainingPercentage = 0; let remainingPercentage = 0;
if (room?.running && timeline) { if (timeline) {
const time = roomTime(); const time = roomTime();
const [current, next] = timeline.itemAtTime(time, 'shot'); const [current, next] = timeline.itemAtTime(time, 'shot');

@ -1,20 +1,41 @@
import {roomTime, useRoom, useRoomRunningChanged, useRoomTime, useTick} from "../lib/Connection"; import {roomTime, useRoomRunningAndReadyChanged, useRoomTime, useTimelineSongFileChanged} from "../lib/Connection";
import {useRef} from "react"; import {useRef, useState} from "react";
const Player = () => { const Player = () => {
const roomRunning = useRoomRunningChanged(); const roomRunning = useRoomRunningAndReadyChanged();
const _ = useRoomTime() const _ = useRoomTime()
console.log('PLAYER RENDER', roomTime) const timeline = useTimelineSongFileChanged();
const player = useRef(new Audio("centurion.m4a")); const player = useRef(timeline ? new Audio(timeline.songFile) : null);
if (roomRunning?.running) { const [timesSeeked, setTimesSeeked] = useState(0);
// If our time synchronisation algorithm thing thinks the time is off by more
// than this value, we seek the running player to correct it.
const diffSecondsRequiredToSeekRunningPlayer = 0.20;
// Hard cap we are allowed to seek this player. Some browsers are slow or inaccurate
// and will always be off. To avoid endless skipping of the song this cap stops seeking the
// player.
const maxTimesSeekAllow = 20;
if (player.current && roomRunning?.running && roomRunning.readyToParticipate) {
let targetTime = roomTime() / 1000; let targetTime = roomTime() / 1000;
let diff = player.current.currentTime - targetTime; let diff = player.current.currentTime - targetTime;
console.log('PLAYER DIFF', diff);
if (Math.abs(diff) > 0.1) { console.log('PLAYER DIFF', 'Our time diff with the server: ', diff,
player.current.currentTime = targetTime; 'Time required to seek (seconds): ', diffSecondsRequiredToSeekRunningPlayer);
if (Math.abs(diff) > diffSecondsRequiredToSeekRunningPlayer) {
if (timesSeeked < maxTimesSeekAllow) {
player.current.currentTime = targetTime;
setTimesSeeked(timesSeeked + 1);
console.log('SEEKED', 'The running player time was seeked, times seeked: ' + timesSeeked);
} else {
console.warn('The running player is off, but we\'ve changed the time ' +
'too often, skipping synchronizing the player.');
}
} }
if (player.current.paused) { if (player.current.paused) {
@ -23,7 +44,9 @@ const Player = () => {
}) })
} }
} else { } else {
player.current.pause(); if (player.current) {
player.current.pause();
}
} }
return null; return null;

@ -1,22 +1,17 @@
import React, {useState} from 'react'; import React from 'react';
import {Col, Progress} from "antd" import {Col, Progress} from "antd"
import {useSocket} from "use-socketio/lib"; import {roomTime, useTimeline} from "../lib/Connection";
import {Tick} from "../types/types";
import {roomTime, useRoom, useTimeline} from "../lib/Connection";
import {useUpdateAfterDelay} from "../util/hooks"; import {useUpdateAfterDelay} from "../util/hooks";
const ShotsTaken = () => { const ShotsTaken = () => {
let room = useRoom();
let timeline = useTimeline(); let timeline = useTimeline();
useUpdateAfterDelay(1000); useUpdateAfterDelay(1000);
let taken = 0; let taken = 0;
if (room?.running && timeline) { if (timeline) {
let [current, _] = timeline.eventAtTime(roomTime(), 'shot'); let [current, _] = timeline.eventAtTime(roomTime(), 'shot');
if (current) { if (current) {
taken = current.shotCount!!; taken = current.shotCount!!;

@ -25,6 +25,7 @@ body
.via-logo .via-logo
z-index: -10000
position: fixed position: fixed
right: 0 right: 0
bottom: 0 bottom: 0

@ -2,7 +2,7 @@
.centurion-title .centurion-title
text-align: center text-align: center
font-size: 3.0rem font-size: 2.5rem
min-height: inherit min-height: inherit
.text .text
@ -28,7 +28,7 @@
.hints .hints
margin: 1rem 0 0 0 margin: 1rem 0 0 0
font-size: 2rem font-size: 1.5rem
text-align: center text-align: center
.control .control

@ -2,7 +2,7 @@ import io from "socket.io-client";
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {parse as parseQueryString, stringify as stringifyQueryString} from 'query-string'; import {parse as parseQueryString, stringify as stringifyQueryString} from 'query-string';
import {Room, Tick, Timeline} from "../types/types"; import {Config, Room, RoomOptions, Timeline} from "../types/types";
import {Sub, useSub} from "../util/sub"; import {Sub, useSub} from "../util/sub";
class Connection { class Connection {
@ -10,8 +10,8 @@ class Connection {
socket: SocketIOClient.Socket; socket: SocketIOClient.Socket;
config = new Sub<Config | null>();
room = new Sub<Room | null>(); room = new Sub<Room | null>();
tick = new Sub<Tick | null>();
timeline = new Sub<Timeline | null>(); timeline = new Sub<Timeline | null>();
timeSyncIntervals = [500, 1000, 3000, 5000, 10000, 30000]; timeSyncIntervals = [500, 1000, 3000, 5000, 10000, 30000];
@ -47,13 +47,15 @@ class Connection {
this.onDisconnect(); this.onDisconnect();
}) })
this.socket.on('config', (data: any) => {
this.config.set(data.config);
})
this.socket.on('time_sync', (data: any) => { this.socket.on('time_sync', (data: any) => {
this.timeSyncResponse(data.requestId, data.clientDiff, data.serverTime); this.timeSyncResponse(data.requestId, data.clientDiff, data.serverTime);
}) })
this.socket.on('room', (data: any) => { this.socket.on('room', (data: any) => {
console.log('ROOM', data.room);
if (data.room) { if (data.room) {
this.setQueryLobbyId(data.room.id); this.setQueryLobbyId(data.room.id);
} }
@ -61,10 +63,6 @@ class Connection {
this.room.set(data.room); this.room.set(data.room);
}); });
this.socket.on('tick_event', (data: any) => {
this.tick.set(data.tick);
});
this.socket.on('timeline', (data: any) => { this.socket.on('timeline', (data: any) => {
if (data.timeline) { if (data.timeline) {
this.timeline.set(new Timeline(data.timeline)); this.timeline.set(new Timeline(data.timeline));
@ -122,7 +120,6 @@ class Connection {
} else { } else {
delete query.lobby; delete query.lobby;
} }
console.log('QUERY', query);
let newUrl = window.location.protocol + "//" + window.location.host + let newUrl = window.location.protocol + "//" + window.location.host +
window.location.pathname + (Object.keys(query).length ? ('?' + stringifyQueryString(query)) : ''); window.location.pathname + (Object.keys(query).length ? ('?' + stringifyQueryString(query)) : '');
window.history.pushState({}, '', newUrl); window.history.pushState({}, '', newUrl);
@ -144,8 +141,14 @@ class Connection {
}); });
} }
requestStart() { setRoomOptions(roomOptions: RoomOptions) {
this.socket.emit('request_start'); this.socket.emit('room_options', roomOptions)
}
requestStart(seekTime: number) {
this.socket.emit('request_start', {
seekTime: seekTime
});
} }
async requestJoin(roomId: number): Promise<boolean> { async requestJoin(roomId: number): Promise<boolean> {
@ -159,6 +162,10 @@ class Connection {
}) })
} }
requestReady() {
this.socket.emit('request_ready');
}
requestJoinRandom() { requestJoinRandom() {
this.socket.emit('request_join_random'); this.socket.emit('request_join_random');
} }
@ -279,16 +286,20 @@ export function useRoom(): Room | null {
return useSub(connection.room); return useSub(connection.room);
} }
export function useRoomRunningChanged(): Room | null { export function useConfig(): Config | null {
return useSub(connection.room, (v) => [v && v.running]); return useSub(connection.config);
}
export function useRoomRunningAndReadyChanged(): Room | null {
return useSub(connection.room, (v) => [v && v.running && v.readyToParticipate]);
} }
export function useTimeline(): Timeline | null { export function useTimeline(): Timeline | null {
return useSub(connection.timeline); return useSub(connection.timeline);
} }
export function useTick(): Tick | null { export function useTimelineSongFileChanged(): Timeline | null {
return useSub(connection.tick); return useSub(connection.timeline, (v) => [v && v.songFile]);
} }
export function useRoomTime(): number { export function useRoomTime(): number {

@ -10,20 +10,39 @@ export interface Tick {
} }
} }
export interface Config {
availableTimelines: string[]
}
export interface Room { export interface Room {
id: number, id: number,
userCount: number, userCount: number,
isLeader: boolean, isLeader: boolean,
running: boolean, running: boolean,
startTime: number startTime: number,
seekTime: string,
timelineName: string,
readyToParticipate: boolean
}
export class RoomOptions {
seekTime: number
timelineName: string
constructor(seekTime: number, timelineName: string) {
this.seekTime = seekTime;
this.timelineName = timelineName;
}
} }
export class Timeline { export class Timeline {
name: string name: string
songFile: string
feed: TimelineItem[] feed: TimelineItem[]
constructor(obj: any) { constructor(obj: any) {
this.name = obj.name; this.name = obj.name;
this.songFile = obj.songFile;
this.feed = obj.feed; this.feed = obj.feed;
} }

Loading…
Cancel
Save