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
+
;
};