diff --git a/client/index.js b/client/index.js index 141598a..92cb493 100644 --- a/client/index.js +++ b/client/index.js @@ -142,6 +142,15 @@ const AugmentedParser = augmentParserWithThroughput(SocketIoParser); const socket = createClient(window.location.href, { parser: AugmentedParser, }); +let isConnected = false; +socket.on('connect', () => { + room.layers.destroy(); + selfEntity = undefined; + isConnected = true; +}); +socket.on('disconnect', () => { + isConnected = false; +}); // Mouse/touch movement. const pointerMovementHandle = setInterval(() => { do { @@ -172,6 +181,9 @@ const pointerMovementHandle = setInterval(() => { }, 1000 / 15); // Input messages. const inputHandle = setInterval(() => { + if (!isConnected) { + return; + } if (actionState !== actionRegistry.state) { actionState = actionRegistry.state; socket.send(InputPacket.fromState(actionState)); @@ -185,6 +197,9 @@ renderTicker.on('tick', () => { }); let lastTime = performance.now(); const predictionHandle = setInterval(() => { + if (!isConnected) { + return; + } const now = performance.now(); const elapsed = (now - lastTime) / 1000; lastTime = now; @@ -254,6 +269,9 @@ function applyStageLighting() { } // Render. function render() { + if (!isConnected) { + return; + } stage.tick(); if (!mayRender) { return false; @@ -266,6 +284,7 @@ function render() { const renderHandle = setAnimation(render); // UI. const UiComponent = ; ReactDOM.render(UiComponent, stage.ui); diff --git a/client/ui/connection-status.js b/client/ui/connection-status.js new file mode 100644 index 0000000..f2881a6 --- /dev/null +++ b/client/ui/connection-status.js @@ -0,0 +1,78 @@ +// 3rd party. +import classnames from 'classnames'; +import React, {useEffect, useState} from 'react'; +// 2nd party. +import {compose} from '@avocado/core'; +import contempo from 'contempo'; + +const decorate = compose( + contempo(` +.connection-status { + display: none; +} +.connection-status.interrupted { + background-color: rgba(0, 0, 0, .75); + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + position: relative; +} +.connection-status.interrupted .message { + color: white; + font-family: mono; + font-size: 0.7em; + text-align: center; + text-transform: uppercase; +} +.connection-status.interrupted .message .dots { + color: rgb(150, 150, 150); + font-size: 0.5em; +} + `), +); + +const ConnectionStatusComponent = ({socket}) => { + // Start true even if not really connected to prevent initial "interrupted" + // state. + const [isConnected, setIsConnected] = useState(true); + const [dots, setDots] = useState(0); + // Juggle connected state. + useEffect(() => { + const onConnect = () => { + setIsConnected(true); + }; + socket.on('connect', onConnect); + const onDisconnect = () => { + setIsConnected(false); + }; + socket.on('disconnect', onDisconnect); + return () => { + socket.off('connect', onConnect); + socket.off('disconnect', onDisconnect); + }; + }, []); + // Make the dots dance. + useEffect(() => { + if (isConnected) { + return; + } + setTimeout(() => { + setDots((dots + 1) % 39); + }, 50); + }, [dots, isConnected]); + const renderedDots = Array(dots + 1).fill('.').join(''); + return
+
+

:[ Connection interrupted ]:

+

{renderedDots}

+
+
; +} + +export default decorate(ConnectionStatusComponent); diff --git a/client/ui/index.js b/client/ui/index.js index 98c0248..b8c4cdc 100644 --- a/client/ui/index.js +++ b/client/ui/index.js @@ -2,11 +2,13 @@ import React from 'react'; import {hot} from 'react-hot-loader/root'; // 1st party. +import ConnectionStatus from './connection-status'; import WorldTime from './world-time'; -const Ui = ({worldTime}) => { +const Ui = ({socket, worldTime}) => { return + ; };