diff --git a/backend/data/timelines.js b/backend/data/timelines.js index 801091b..6fcc77a 100644 --- a/backend/data/timelines.js +++ b/backend/data/timelines.js @@ -2,6 +2,7 @@ module.exports = { 'timelines': [ { 'name': 'Centurion', + 'songFile': 'songs/centurion.m4a', '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' + ] + } + ] + } + ] } ] } diff --git a/backend/src/Room.ts b/backend/src/Room.ts index d9aa685..3f04f4b 100644 --- a/backend/src/Room.ts +++ b/backend/src/Room.ts @@ -1,9 +1,14 @@ import {Socket} from "socket.io"; import User from "./User"; -import {getIndex, getNextShot, getTimeline, indexForTime} from "./timeline"; +import {getIndex, getNextShot, getTimeline, getTimelineNames, indexForTime} from "./timeline"; import {getCurrentTime} from "./util"; +export interface RoomOptions { + seekTime: number + timelineName: string +} + export default class Room { id: number = 0; users: User[] = []; @@ -14,6 +19,7 @@ export default class Room { currentSeconds = 0; timelineIndex: number = 0; + seekTime: number = 0; timelineName: string = 'Centurion'; // For debugging purposes @@ -31,6 +37,8 @@ export default class Room { 'running': this.running, 'startTime': this.startTime, 'timelineName': this.timelineName, + 'seekTime': this.seekTime, + 'readyToParticipate': user.readyToParticipate || this.leader == user, } } @@ -67,9 +75,17 @@ export default class Room { 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() { this.running = true; - this.startTime = getCurrentTime() - 1400 * 1000 + this.startTime = getCurrentTime() - this.seekTime this.sync(); } diff --git a/backend/src/Service.ts b/backend/src/Service.ts index b3e3c41..a31789d 100644 --- a/backend/src/Service.ts +++ b/backend/src/Service.ts @@ -1,7 +1,7 @@ import {Socket} from "socket.io"; import User from './User' -import Room from './Room' +import Room, {RoomOptions} from './Room' import {getCurrentTime, randomInt} from "./util"; export default class Service { @@ -11,6 +11,7 @@ export default class Service { onSocketConnect(socket: Socket) { let user = new User(socket); this.socketsToUsers.set(socket.id, user); + user.sync(); } 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) { let user = this.getUser(socket); @@ -56,9 +65,17 @@ export default class Service { let room = this.roomIdToRooms.get(roomId)!!; room.join(user); + return true; } + onRequestReady(socket: Socket) { + let user = this.getUser(socket); + if (!user.room || user.readyToParticipate) return; + user.readyToParticipate = true; + user.sync(); + } + onRequestJoinRandom(socket: Socket) { let user = this.getUser(socket); diff --git a/backend/src/User.ts b/backend/src/User.ts index 290ddc6..b40145e 100644 --- a/backend/src/User.ts +++ b/backend/src/User.ts @@ -1,12 +1,14 @@ import {Socket} from "socket.io"; import Room from "./Room"; +import {getTimelineNames} from "./timeline"; export default class User { socket: Socket; id: string; room: Room | null = null; + readyToParticipate: boolean = false; constructor(socket: Socket) { this.socket = socket; @@ -24,6 +26,7 @@ export default class User { if (this.room != null) { this.socket.leave(this.room.id.toString()); + this.readyToParticipate = false; } this.room = room; @@ -35,18 +38,36 @@ export default class User { this.sync(); } + getConfig() { + return { + 'availableTimelines': getTimelineNames() + } + } + + sentConfig: any = null; sentRoom: any = null; sentTimelineName: string | null = null; 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.emit('room', { '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.emit('timeline', { 'timeline': this.sentTimelineName == null ? null : this.room!!.serializeTimeline(this) @@ -58,20 +79,30 @@ export default class User { 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) return true; if ((obj1 === null && obj2 !== null) || (obj1 !== null && obj2 === null)) return false; - if (typeof (obj1) != typeof (obj2)) + if (typeof (obj1) !== typeof (obj2)) 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 return Object.keys(obj1).every(key => - obj2.hasOwnProperty(key) && obj1[key] === obj2[key] + obj2.hasOwnProperty(key) && this.syncEquals(obj1[key], obj2[key]) ); } } diff --git a/backend/src/index.ts b/backend/src/index.ts index a6cb73f..e0ec8ba 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -34,7 +34,15 @@ io.on('connection', socket => { 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); }); @@ -44,6 +52,10 @@ io.on('connection', socket => { service.onRequestJoin(socket, roomId); }); + socket.on('request_ready', () => { + service.onRequestReady(socket); + }) + socket.on('request_join_random', () => { service.onRequestJoinRandom(socket); }) @@ -66,23 +78,23 @@ io.on('connection', socket => { call.respond(service.hasRoomId(roomId)); return; } - - if (name == 'request_join') { - let roomId = params && params['roomId']; - if (!Number.isSafeInteger(roomId)) { - call.error('Invalid room id'); - return; - } - if (!service.hasRoomId(roomId)) { - call.respond(false); - return; - } - if (service.onRequestJoin(socket, roomId)) { - call.respond(true); - } else { - call.respond(false); - } - } + // + // if (name == 'request_join') { + // let roomId = params && params['roomId']; + // if (!Number.isSafeInteger(roomId)) { + // call.error('Invalid room id'); + // return; + // } + // if (!service.hasRoomId(roomId)) { + // call.respond(false); + // return; + // } + // if (service.onRequestJoin(socket, roomId)) { + // call.respond(true); + // } else { + // call.respond(false); + // } + // } }) service.onSocketConnect(socket); diff --git a/backend/src/timeline.ts b/backend/src/timeline.ts index 8c75510..9afa7d3 100644 --- a/backend/src/timeline.ts +++ b/backend/src/timeline.ts @@ -2,6 +2,10 @@ import timeline from '../data/timelines.js'; +export function getTimelineNames(): string[] { + return timeline.timelines.map((i: any) => i.name) +} + export function getTimeline(name: string) { let t = timeline.timelines.find((i: any) => i.name == name); if (!t) return null; diff --git a/frontend/config-overrides.js b/frontend/config-overrides.js index e9b672a..4236e05 100644 --- a/frontend/config-overrides.js +++ b/frontend/config-overrides.js @@ -1,4 +1,25 @@ -module.exports = function override(config, env) { - // do stuff with the webpack config... - return config; -}; \ No newline at end of file +module.exports = { + webpack: function (config, env) { + 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; + }; + } +} diff --git a/frontend/src/components/Centurion.tsx b/frontend/src/components/Centurion.tsx index 8460ce9..0d9f301 100644 --- a/frontend/src/components/Centurion.tsx +++ b/frontend/src/components/Centurion.tsx @@ -1,8 +1,7 @@ -import React, {useRef, useState} from "react"; +import React from "react"; 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 Feed from "./Feed"; import ShotsTaken from "./ShotsTaken"; @@ -12,7 +11,8 @@ import logo from "../img/via-logo.svg"; import Player from "./Player"; const Centurion = () => { - let roomRunning = useRoomRunningChanged()?.running || false; + const room = useRoomRunningAndReadyChanged(); + const showFeed = (room?.running && room.readyToParticipate) || false; const feedContent = ( @@ -32,7 +32,7 @@ const Centurion = () => { return ( <>
- {roomRunning ? feedContent : lobbyContent} + {showFeed ? feedContent : lobbyContent}