Merge pull request #11 from mitchemmc/feature/disconnect-detection
v1.3.3
This commit is contained in:
commit
97dfc9f673
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "owlbear-rodeo",
|
"name": "owlbear-rodeo",
|
||||||
"version": "1.3.2",
|
"version": "1.3.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@msgpack/msgpack": "^1.12.1",
|
"@msgpack/msgpack": "^1.12.1",
|
||||||
|
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import Modal from "react-modal";
|
import Modal from "react-modal";
|
||||||
import { useThemeUI, Close } from "theme-ui";
|
import { useThemeUI, Close } from "theme-ui";
|
||||||
|
|
||||||
function Banner({ isOpen, onRequestClose, children }) {
|
function Banner({ isOpen, onRequestClose, children, allowClose }) {
|
||||||
const { theme } = useThemeUI();
|
const { theme } = useThemeUI();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -15,7 +15,7 @@ function Banner({ isOpen, onRequestClose, children }) {
|
|||||||
backgroundColor: theme.colors.highlight,
|
backgroundColor: theme.colors.highlight,
|
||||||
top: "initial",
|
top: "initial",
|
||||||
left: "50%",
|
left: "50%",
|
||||||
right: 0,
|
right: "initial",
|
||||||
// Offset for iOS safe zone
|
// Offset for iOS safe zone
|
||||||
bottom: "env(safe-area-inset-bottom)",
|
bottom: "env(safe-area-inset-bottom)",
|
||||||
border: "none",
|
border: "none",
|
||||||
@ -28,13 +28,19 @@ function Banner({ isOpen, onRequestClose, children }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
{allowClose && (
|
||||||
<Close
|
<Close
|
||||||
m={0}
|
m={0}
|
||||||
sx={{ position: "absolute", top: "4px", right: 0 }}
|
sx={{ position: "absolute", top: "4px", right: 0 }}
|
||||||
onClick={onRequestClose}
|
onClick={onRequestClose}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Banner.defaultProps = {
|
||||||
|
allowClose: true,
|
||||||
|
};
|
||||||
|
|
||||||
export default Banner;
|
export default Banner;
|
||||||
|
8
src/docs/releaseNotes/v1.3.3.md
Normal file
8
src/docs/releaseNotes/v1.3.3.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# v1.3.3
|
||||||
|
|
||||||
|
## Minor Changes
|
||||||
|
|
||||||
|
- Fixed a bug that would cause the game to crash when a player would lose internet connection.
|
||||||
|
- Added an automatic reconnection feature for when internet connection is lost. This should also help when players put the site into the background and try and return to the game later.
|
||||||
|
|
||||||
|
[Reddit]()
|
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState, useContext } from "react";
|
import { useEffect, useState, useContext, useCallback } from "react";
|
||||||
import io from "socket.io-client";
|
import io from "socket.io-client";
|
||||||
|
|
||||||
import { omit } from "../helpers/shared";
|
import { omit } from "../helpers/shared";
|
||||||
@ -19,17 +19,24 @@ function useSession(
|
|||||||
) {
|
) {
|
||||||
const { password, setAuthenticationStatus } = useContext(AuthContext);
|
const { password, setAuthenticationStatus } = useContext(AuthContext);
|
||||||
const [iceServers, setIceServers] = useState([]);
|
const [iceServers, setIceServers] = useState([]);
|
||||||
|
const [connected, setConnected] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
const joinParty = useCallback(async () => {
|
||||||
async function joinParty() {
|
try {
|
||||||
const response = await fetch(process.env.REACT_APP_ICE_SERVERS_URL);
|
const response = await fetch(process.env.REACT_APP_ICE_SERVERS_URL);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setIceServers(data.iceServers);
|
setIceServers(data.iceServers);
|
||||||
socket.emit("join party", partyId, password);
|
socket.emit("join party", partyId, password);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Unable to join party:", e.message);
|
||||||
|
setConnected(false);
|
||||||
}
|
}
|
||||||
joinParty();
|
|
||||||
}, [partyId, password]);
|
}, [partyId, password]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
joinParty();
|
||||||
|
}, [partyId, password, joinParty]);
|
||||||
|
|
||||||
const [peers, setPeers] = useState({});
|
const [peers, setPeers] = useState({});
|
||||||
|
|
||||||
// Signal connected peers of a closure on refresh
|
// Signal connected peers of a closure on refresh
|
||||||
@ -78,6 +85,8 @@ function useSession(
|
|||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
onPeerDisconnected && onPeerDisconnected(peer);
|
onPeerDisconnected && onPeerDisconnected(peer);
|
||||||
|
peer.connection.destroy();
|
||||||
|
setPeers((prevPeers) => omit(prevPeers, [peer.id]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleError(error) {
|
function handleError(error) {
|
||||||
@ -153,9 +162,9 @@ function useSession(
|
|||||||
function handlePartyMemberLeft(id) {
|
function handlePartyMemberLeft(id) {
|
||||||
if (id in peers) {
|
if (id in peers) {
|
||||||
peers[id].connection.destroy();
|
peers[id].connection.destroy();
|
||||||
}
|
|
||||||
setPeers((prevPeers) => omit(prevPeers, [id]));
|
setPeers((prevPeers) => omit(prevPeers, [id]));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleJoinedParty(otherIds) {
|
function handleJoinedParty(otherIds) {
|
||||||
for (let [index, id] of otherIds.entries()) {
|
for (let [index, id] of otherIds.entries()) {
|
||||||
@ -164,6 +173,7 @@ function useSession(
|
|||||||
addPeer(id, true, sync);
|
addPeer(id, true, sync);
|
||||||
}
|
}
|
||||||
setAuthenticationStatus("authenticated");
|
setAuthenticationStatus("authenticated");
|
||||||
|
setConnected(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSignal(data) {
|
function handleSignal(data) {
|
||||||
@ -177,21 +187,36 @@ function useSession(
|
|||||||
setAuthenticationStatus("unauthenticated");
|
setAuthenticationStatus("unauthenticated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSocketDisconnect() {
|
||||||
|
setConnected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSocketReconnect() {
|
||||||
|
setConnected(true);
|
||||||
|
joinParty();
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on("disconnect", handleSocketDisconnect);
|
||||||
|
socket.on("reconnect", handleSocketReconnect);
|
||||||
|
|
||||||
socket.on("party member joined", handlePartyMemberJoined);
|
socket.on("party member joined", handlePartyMemberJoined);
|
||||||
socket.on("party member left", handlePartyMemberLeft);
|
socket.on("party member left", handlePartyMemberLeft);
|
||||||
socket.on("joined party", handleJoinedParty);
|
socket.on("joined party", handleJoinedParty);
|
||||||
socket.on("signal", handleSignal);
|
socket.on("signal", handleSignal);
|
||||||
socket.on("auth error", handleAuthError);
|
socket.on("auth error", handleAuthError);
|
||||||
return () => {
|
return () => {
|
||||||
socket.removeListener("party member joined", handlePartyMemberJoined);
|
socket.off("disconnect", handleSocketDisconnect);
|
||||||
socket.removeListener("party member left", handlePartyMemberLeft);
|
socket.off("reconnect", handleSocketReconnect);
|
||||||
socket.removeListener("joined party", handleJoinedParty);
|
|
||||||
socket.removeListener("signal", handleSignal);
|
|
||||||
socket.removeListener("auth error", handleAuthError);
|
|
||||||
};
|
|
||||||
}, [peers, setAuthenticationStatus, iceServers]);
|
|
||||||
|
|
||||||
return { peers, socket };
|
socket.off("party member joined", handlePartyMemberJoined);
|
||||||
|
socket.off("party member left", handlePartyMemberLeft);
|
||||||
|
socket.off("joined party", handleJoinedParty);
|
||||||
|
socket.off("signal", handleSignal);
|
||||||
|
socket.off("auth error", handleAuthError);
|
||||||
|
};
|
||||||
|
}, [peers, setAuthenticationStatus, iceServers, joinParty]);
|
||||||
|
|
||||||
|
return { peers, socket, connected };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useSession;
|
export default useSession;
|
||||||
|
@ -35,7 +35,7 @@ function Game() {
|
|||||||
);
|
);
|
||||||
const { assetLoadStart, assetLoadFinish } = useContext(MapLoadingContext);
|
const { assetLoadStart, assetLoadFinish } = useContext(MapLoadingContext);
|
||||||
|
|
||||||
const { peers, socket } = useSession(
|
const { peers, socket, connected } = useSession(
|
||||||
gameId,
|
gameId,
|
||||||
handlePeerConnected,
|
handlePeerConnected,
|
||||||
handlePeerDisconnected,
|
handlePeerDisconnected,
|
||||||
@ -421,18 +421,6 @@ function Game() {
|
|||||||
const [peerError, setPeerError] = useState(null);
|
const [peerError, setPeerError] = useState(null);
|
||||||
function handlePeerError({ error, peer }) {
|
function handlePeerError({ error, peer }) {
|
||||||
console.error(error.code);
|
console.error(error.code);
|
||||||
if (
|
|
||||||
error.code === "ERR_ICE_CONNECTION_FAILURE" ||
|
|
||||||
error.code === "ERR_CONNECTION_FAILURE"
|
|
||||||
) {
|
|
||||||
setPeerError(
|
|
||||||
`${
|
|
||||||
peer.id === socket.id
|
|
||||||
? ""
|
|
||||||
: `(${partyNicknames[peer.id] || "Unknown"})`
|
|
||||||
} Connection failure`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (error.code === "ERR_WEBRTC_SUPPORT") {
|
if (error.code === "ERR_WEBRTC_SUPPORT") {
|
||||||
setPeerError("WebRTC not supported");
|
setPeerError("WebRTC not supported");
|
||||||
}
|
}
|
||||||
@ -558,6 +546,17 @@ function Game() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Banner>
|
</Banner>
|
||||||
|
<Banner
|
||||||
|
isOpen={!connected && authenticationStatus === "authenticated"}
|
||||||
|
onRequestClose={() => {}}
|
||||||
|
allowClose={false}
|
||||||
|
>
|
||||||
|
<Box p={1}>
|
||||||
|
<Text as="p" variant="body2">
|
||||||
|
Disconnected. Attempting to reconnect...
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Banner>
|
||||||
<AuthModal isOpen={authenticationStatus === "unauthenticated"} />
|
<AuthModal isOpen={authenticationStatus === "unauthenticated"} />
|
||||||
{authenticationStatus === "unknown" && <LoadingOverlay />}
|
{authenticationStatus === "unknown" && <LoadingOverlay />}
|
||||||
</MapStageProvider>
|
</MapStageProvider>
|
||||||
|
@ -51,7 +51,7 @@ function Home() {
|
|||||||
Join Game
|
Join Game
|
||||||
</Button>
|
</Button>
|
||||||
<Text variant="caption" as="p" sx={{ textAlign: "center" }}>
|
<Text variant="caption" as="p" sx={{ textAlign: "center" }}>
|
||||||
Beta v1.3.2
|
Beta v1.3.3
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
m={2}
|
m={2}
|
||||||
|
@ -11,6 +11,7 @@ const v121 = raw("../docs/releaseNotes/v1.2.1.md");
|
|||||||
const v130 = raw("../docs/releaseNotes/v1.3.0.md");
|
const v130 = raw("../docs/releaseNotes/v1.3.0.md");
|
||||||
const v131 = raw("../docs/releaseNotes/v1.3.1.md");
|
const v131 = raw("../docs/releaseNotes/v1.3.1.md");
|
||||||
const v132 = raw("../docs/releaseNotes/v1.3.2.md");
|
const v132 = raw("../docs/releaseNotes/v1.3.2.md");
|
||||||
|
const v133 = raw("../docs/releaseNotes/v1.3.3.md");
|
||||||
|
|
||||||
function ReleaseNotes() {
|
function ReleaseNotes() {
|
||||||
return (
|
return (
|
||||||
@ -33,6 +34,9 @@ function ReleaseNotes() {
|
|||||||
<Text mb={2} variant="heading" as="h1" sx={{ fontSize: 5 }}>
|
<Text mb={2} variant="heading" as="h1" sx={{ fontSize: 5 }}>
|
||||||
Release Notes
|
Release Notes
|
||||||
</Text>
|
</Text>
|
||||||
|
<div id="v133">
|
||||||
|
<Markdown source={v133} />
|
||||||
|
</div>
|
||||||
<div id="v132">
|
<div id="v132">
|
||||||
<Markdown source={v132} />
|
<Markdown source={v132} />
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user