feat: disconnected
This commit is contained in:
parent
216e528c13
commit
eb599c60c0
|
@ -1,5 +1,12 @@
|
|||
import {encode} from '@/packets/index.js';
|
||||
|
||||
let connected = false;
|
||||
let socket;
|
||||
|
||||
function onMessage(event) {
|
||||
postMessage(event.data);
|
||||
}
|
||||
|
||||
onmessage = async (event) => {
|
||||
if (!connected) {
|
||||
const url = new URL(`wss://${event.data.host}/ws`)
|
||||
|
@ -8,12 +15,21 @@ onmessage = async (event) => {
|
|||
}
|
||||
socket = new WebSocket(url.href);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
await new Promise((resolve) => {
|
||||
socket.onopen = resolve;
|
||||
const {promise, resolve, reject} = Promise.withResolvers();
|
||||
socket.addEventListener('open', resolve);
|
||||
socket.addEventListener('error', reject);
|
||||
await promise;
|
||||
socket.removeEventListener('open', resolve);
|
||||
socket.removeEventListener('error', reject);
|
||||
socket.addEventListener('message', onMessage);
|
||||
socket.addEventListener('close', () => {
|
||||
postMessage(encode({type: 'ConnectionAborted'}));
|
||||
close();
|
||||
});
|
||||
socket.addEventListener('error', () => {
|
||||
postMessage(encode({type: 'ConnectionAborted'}));
|
||||
close();
|
||||
});
|
||||
socket.onmessage = (event) => {
|
||||
postMessage(event.data);
|
||||
};
|
||||
connected = true;
|
||||
return;
|
||||
}
|
||||
|
|
3
app/packets/connection-aborted.js
Normal file
3
app/packets/connection-aborted.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Packet from '@/net/packet.js';
|
||||
|
||||
export default class ConnectionAborted extends Packet {}
|
27
app/react-components/disconnected.jsx
Normal file
27
app/react-components/disconnected.jsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import {useEffect, useState} from 'react';
|
||||
|
||||
import styles from './disconnected.module.css';
|
||||
|
||||
export default function Disconnected() {
|
||||
const [dots, setDots] = useState(3);
|
||||
const [delta, setDelta] = useState(1);
|
||||
useEffect(() => {
|
||||
const handle = setTimeout(() => {
|
||||
const updated = dots + delta;
|
||||
setDots(updated);
|
||||
if (updated < 1 || updated > 5) {
|
||||
setDelta(-delta);
|
||||
}
|
||||
}, 100);
|
||||
return () => {
|
||||
clearTimeout(handle);
|
||||
};
|
||||
}, [dots, delta]);
|
||||
const rendered = Array(dots).fill('.').join('');
|
||||
return (
|
||||
<div className={styles.disconnected}>
|
||||
<p>There's a problem with the connection.</p>
|
||||
<p>{rendered}Reconnection attempt in progress{rendered}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
14
app/react-components/disconnected.module.css
Normal file
14
app/react-components/disconnected.module.css
Normal file
|
@ -0,0 +1,14 @@
|
|||
.disconnected {
|
||||
align-items: center;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
p {
|
||||
color: #cccccc;
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import addKeyListener from '@/add-key-listener.js';
|
|||
import {ACTION_MAP, RESOLUTION} from '@/constants.js';
|
||||
import ClientContext from '@/context/client.js';
|
||||
|
||||
import Disconnected from './disconnected.jsx';
|
||||
import Dom from './dom.jsx';
|
||||
import HotBar from './hotbar.jsx';
|
||||
import Pixi from './pixi.jsx';
|
||||
|
@ -16,7 +17,7 @@ const KEY_MAP = {
|
|||
keyUp: 0,
|
||||
};
|
||||
|
||||
export default function Ui() {
|
||||
export default function Ui({disconnected}) {
|
||||
// Key input.
|
||||
const client = useContext(ClientContext);
|
||||
useEffect(() => {
|
||||
|
@ -43,6 +44,9 @@ export default function Ui() {
|
|||
<Pixi />
|
||||
<Dom>
|
||||
<HotBar active={0} slots={Array(10).fill(0).map(() => {})} />
|
||||
{disconnected && (
|
||||
<Disconnected />
|
||||
)}
|
||||
</Dom>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,66 @@
|
|||
import {useEffect, useState} from 'react';
|
||||
import {useParams} from 'react-router-dom';
|
||||
|
||||
import ClientContext from '@/context/client.js';
|
||||
import LocalClient from '@/net/client/local.js';
|
||||
import RemoteClient from '@/net/client/remote.js';
|
||||
import {decode, encode} from '@/packets/index.js';
|
||||
import Ui from '@/react-components/ui.jsx';
|
||||
|
||||
import styles from './play.module.css';
|
||||
|
||||
export default function Index() {
|
||||
const [client, setClient] = useState();
|
||||
const [disconnected, setDisconnected] = useState();
|
||||
const params = useParams();
|
||||
const [type, url] = params['*'].split('/');
|
||||
useEffect(() => {
|
||||
let Client;
|
||||
switch (type) {
|
||||
case 'local':
|
||||
Client = LocalClient;
|
||||
break;
|
||||
case 'remote':
|
||||
Client = RemoteClient;
|
||||
break;
|
||||
}
|
||||
class SilphiusClient extends Client {
|
||||
accept(packed) {
|
||||
super.accept(decode(packed));
|
||||
}
|
||||
transmit(packet) {
|
||||
super.transmit(encode(packet));
|
||||
}
|
||||
}
|
||||
const client = new SilphiusClient();
|
||||
async function connect() {
|
||||
await client.connect(url);
|
||||
setClient(client);
|
||||
}
|
||||
connect();
|
||||
return () => {
|
||||
client.disconnect();
|
||||
};
|
||||
}, [type, url]);
|
||||
useEffect(() => {
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
function onConnectionAborted() {
|
||||
setDisconnected(true);
|
||||
}
|
||||
client.addPacketListener('ConnectionAborted', onConnectionAborted);
|
||||
return () => {
|
||||
client.removePacketListener('ConnectionAborted', onConnectionAborted);
|
||||
};
|
||||
}, [client]);
|
||||
return (
|
||||
<Ui />
|
||||
<div className={styles.play}>
|
||||
{client && (
|
||||
<ClientContext.Provider value={client}>
|
||||
<Ui disconnected={disconnected} />
|
||||
</ClientContext.Provider>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import {useEffect, useState} from 'react';
|
||||
import {Outlet, useParams} from 'react-router-dom';
|
||||
|
||||
import ClientContext from '@/context/client.js';
|
||||
import LocalClient from '@/net/client/local.js';
|
||||
import RemoteClient from '@/net/client/remote.js';
|
||||
import {decode, encode} from '@/packets/index.js';
|
||||
|
||||
import styles from './play.module.css';
|
||||
|
||||
export default function Index() {
|
||||
const [client, setClient] = useState();
|
||||
const params = useParams();
|
||||
const [type, url] = params['*'].split('/');
|
||||
useEffect(() => {
|
||||
let Client;
|
||||
switch (type) {
|
||||
case 'local':
|
||||
Client = LocalClient;
|
||||
break;
|
||||
case 'remote':
|
||||
Client = RemoteClient;
|
||||
break;
|
||||
}
|
||||
class SilphiusClient extends Client {
|
||||
accept(packed) {
|
||||
super.accept(decode(packed));
|
||||
}
|
||||
transmit(packet) {
|
||||
super.transmit(encode(packet));
|
||||
}
|
||||
}
|
||||
const client = new SilphiusClient();
|
||||
async function connect() {
|
||||
await client.connect(url);
|
||||
setClient(client);
|
||||
}
|
||||
connect();
|
||||
return () => {
|
||||
client.disconnect();
|
||||
};
|
||||
}, [type, url]);
|
||||
return (
|
||||
<div className={styles.play}>
|
||||
{client && (
|
||||
<ClientContext.Provider value={client}>
|
||||
<Outlet />
|
||||
</ClientContext.Provider>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user