Merge pull request #11 from mitchemmc/feature/disconnect-detection

v1.3.3
This commit is contained in:
Mitchell McCaffrey 2020-06-16 22:14:37 +10:00 committed by GitHub
commit 97dfc9f673
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 35 deletions

View File

@ -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",

View File

@ -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}
<Close {allowClose && (
m={0} <Close
sx={{ position: "absolute", top: "4px", right: 0 }} m={0}
onClick={onRequestClose} sx={{ position: "absolute", top: "4px", right: 0 }}
/> onClick={onRequestClose}
/>
)}
</Modal> </Modal>
); );
} }
Banner.defaultProps = {
allowClose: true,
};
export default Banner; export default Banner;

View 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]()

View File

@ -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,8 +162,8 @@ 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) {
@ -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;

View File

@ -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>

View File

@ -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}

View File

@ -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>