Moved to socket for game state networking

This commit is contained in:
Mitchell McCaffrey 2020-12-05 17:16:06 +11:00
parent 2847118ac1
commit f2cb8b69db
4 changed files with 71 additions and 115 deletions

View File

@ -0,0 +1,24 @@
import { useEffect, useState, useRef } from "react";
function useNetworkedState(defaultState, session, eventName) {
const [state, _setState] = useState(defaultState);
// Used to control whether the state needs to be sent to the socket
const dirtyRef = useRef(false);
// Update dirty at the same time as state
function setState(update, sync = true) {
dirtyRef.current = sync;
_setState(update);
}
useEffect(() => {
if (dirtyRef.current) {
session.socket.emit(eventName, state);
dirtyRef.current = false;
}
}, [state, eventName]);
return [state, setState];
}
export default useNetworkedState;

View File

@ -8,6 +8,7 @@ import DatabaseContext from "../contexts/DatabaseContext";
import { omit } from "../helpers/shared";
import useDebounce from "../helpers/useDebounce";
import useNetworkedState from "../helpers/useNetworkedState";
// Load session for auto complete
// eslint-disable-next-line no-unused-vars
import Session from "./Session";
@ -38,7 +39,11 @@ function NetworkedMapAndTokens({ session }) {
);
const [currentMap, setCurrentMap] = useState(null);
const [currentMapState, setCurrentMapState] = useState(null);
const [currentMapState, setCurrentMapState] = useNetworkedState(
null,
session,
"map_state"
);
/**
* Map state
@ -69,7 +74,6 @@ function NetworkedMapAndTokens({ session }) {
return;
}
session.send("mapState", newMapState);
session.send("map", getMapDataToSend(newMap), "map");
const tokensToSend = getMapTokensToSend(newMapState);
for (let token of tokensToSend) {
@ -90,7 +94,6 @@ function NetworkedMapAndTokens({ session }) {
function handleMapStateChange(newMapState) {
setCurrentMapState(newMapState);
session.send("mapState", newMapState);
}
function addMapDrawActions(actions, indexKey, actionsKey) {
@ -123,48 +126,26 @@ function NetworkedMapAndTokens({ session }) {
function handleMapDraw(action) {
addMapDrawActions([action], "mapDrawActionIndex", "mapDrawActions");
session.send("mapDraw", [action]);
}
function handleMapDrawUndo() {
const index = updateDrawActionIndex(
-1,
"mapDrawActionIndex",
"mapDrawActions"
);
session.send("mapDrawIndex", index);
updateDrawActionIndex(-1, "mapDrawActionIndex", "mapDrawActions");
}
function handleMapDrawRedo() {
const index = updateDrawActionIndex(
1,
"mapDrawActionIndex",
"mapDrawActions"
);
session.send("mapDrawIndex", index);
updateDrawActionIndex(1, "mapDrawActionIndex", "mapDrawActions");
}
function handleFogDraw(action) {
addMapDrawActions([action], "fogDrawActionIndex", "fogDrawActions");
session.send("mapFog", [action]);
}
function handleFogDrawUndo() {
const index = updateDrawActionIndex(
-1,
"fogDrawActionIndex",
"fogDrawActions"
);
session.send("mapFogIndex", index);
updateDrawActionIndex(-1, "fogDrawActionIndex", "fogDrawActions");
}
function handleFogDrawRedo() {
const index = updateDrawActionIndex(
1,
"fogDrawActionIndex",
"fogDrawActions"
);
session.send("mapFogIndex", index);
updateDrawActionIndex(1, "fogDrawActionIndex", "fogDrawActions");
}
function handleNoteChange(note) {
@ -175,7 +156,6 @@ function NetworkedMapAndTokens({ session }) {
[note.id]: note,
},
}));
session.send("mapNoteChange", note);
}
function handleNoteRemove(noteId) {
@ -183,7 +163,6 @@ function NetworkedMapAndTokens({ session }) {
...prevMapState,
notes: omit(prevMapState.notes, [noteId]),
}));
session.send("mapNoteRemove", noteId);
}
/**
@ -234,7 +213,6 @@ function NetworkedMapAndTokens({ session }) {
...change,
},
}));
session.send("tokenStateEdit", change);
}
function handleMapTokenStateRemove(tokenState) {
@ -242,14 +220,12 @@ function NetworkedMapAndTokens({ session }) {
const { [tokenState.id]: old, ...rest } = prevMapState.tokens;
return { ...prevMapState, tokens: rest };
});
session.send("tokenStateRemove", { [tokenState.id]: tokenState });
}
useEffect(() => {
async function handlePeerData({ id, data, reply }) {
if (id === "sync") {
if (currentMapState) {
reply("mapState", currentMapState);
const tokensToSend = getMapTokensToSend(currentMapState);
for (let token of tokensToSend) {
reply("token", token, "token");
@ -354,9 +330,6 @@ function NetworkedMapAndTokens({ session }) {
const updatedMap = await getMapFromDB(data.id);
setCurrentMap(updatedMap);
}
if (id === "mapState") {
setCurrentMapState(data);
}
if (id === "token") {
const newToken = data;
if (newToken && newToken.type === "file") {
@ -384,51 +357,6 @@ function NetworkedMapAndTokens({ session }) {
putToken(newToken);
}
}
if (id === "tokenStateEdit" && currentMapState) {
setCurrentMapState((prevMapState) => ({
...prevMapState,
tokens: { ...prevMapState.tokens, ...data },
}));
}
if (id === "tokenStateRemove" && currentMapState) {
setCurrentMapState((prevMapState) => ({
...prevMapState,
tokens: omit(prevMapState.tokens, Object.keys(data)),
}));
}
if (id === "mapDraw" && currentMapState) {
addMapDrawActions(data, "mapDrawActionIndex", "mapDrawActions");
}
if (id === "mapDrawIndex" && currentMapState) {
setCurrentMapState((prevMapState) => ({
...prevMapState,
mapDrawActionIndex: data,
}));
}
if (id === "mapFog" && currentMapState) {
addMapDrawActions(data, "fogDrawActionIndex", "fogDrawActions");
}
if (id === "mapFogIndex" && currentMapState) {
setCurrentMapState((prevMapState) => ({
...prevMapState,
fogDrawActionIndex: data,
}));
}
if (id === "mapNoteChange" && currentMapState) {
setCurrentMapState((prevMapState) => ({
...prevMapState,
notes: {
...prevMapState.notes,
[data.id]: data,
},
}));
}
if (id === "mapNoteRemove" && currentMapState) {
setCurrentMapState((prevMapState) => ({
...prevMapState,
notes: omit(prevMapState.notes, [data]),
}));
}
}
function handlePeerDataProgress({ id, total, count }) {
@ -441,12 +369,22 @@ function NetworkedMapAndTokens({ session }) {
assetProgressUpdate({ id, total, count });
}
function handleSocketMapState(mapState) {
setCurrentMapState(mapState, false);
}
session.on("data", handlePeerData);
session.on("dataProgress", handlePeerDataProgress);
if (session.socket) {
session.socket.on("map_state", handleSocketMapState);
}
return () => {
session.off("data", handlePeerData);
session.off("dataProgress", handlePeerDataProgress);
if (session.socket) {
session.socket.off("map_state", handleSocketMapState);
}
};
});

View File

@ -59,7 +59,7 @@ class Session extends EventEmitter {
_iceServers;
// Store party id and password for reconnect
_partyId;
_gameId;
_password;
constructor() {
@ -83,17 +83,11 @@ class Session extends EventEmitter {
withCredentials: true,
});
this.socket.on(
"party member joined",
this._handlePartyMemberJoined.bind(this)
);
this.socket.on(
"party member left",
this._handlePartyMemberLeft.bind(this)
);
this.socket.on("joined party", this._handleJoinedParty.bind(this));
this.socket.on("player_joined", this._handlePlayerJoined.bind(this));
this.socket.on("player_left", this._handlePlayerLeft.bind(this));
this.socket.on("joined_game", this._handleJoinedGame.bind(this));
this.socket.on("signal", this._handleSignal.bind(this));
this.socket.on("auth error", this._handleAuthError.bind(this));
this.socket.on("auth_error", this._handleAuthError.bind(this));
this.socket.on("disconnect", this._handleSocketDisconnect.bind(this));
this.socket.io.on("reconnect", this._handleSocketReconnect.bind(this));
@ -120,22 +114,22 @@ class Session extends EventEmitter {
/**
* Join a party
*
* @param {string} partyId - the id of the party to join
* @param {string} gameId - the id of the party to join
* @param {string} password - the password of the party
*/
async joinParty(partyId, password) {
if (typeof partyId !== "string" || typeof password !== "string") {
async joinGame(gameId, password) {
if (typeof gameId !== "string" || typeof password !== "string") {
console.error(
"Unable to join party: invalid party ID or password",
partyId,
"Unable to join game: invalid game ID or password",
gameId,
password
);
return;
}
this._partyId = partyId;
this._gameId = gameId;
this._password = password;
this.socket.emit("join party", partyId, password);
this.socket.emit("join_game", gameId, password);
}
_addPeer(id, initiator, sync) {
@ -226,18 +220,7 @@ class Session extends EventEmitter {
}
}
_handlePartyMemberJoined(id) {
this._addPeer(id, false, false);
}
_handlePartyMemberLeft(id) {
if (id in this.peers) {
this.peers[id].connection.destroy();
delete this.peers[id];
}
}
_handleJoinedParty(otherIds) {
_handleJoinedGame(otherIds) {
for (let i = 0; i < otherIds.length; i++) {
const id = otherIds[i];
// Send a sync request to the first member of the party
@ -248,6 +231,17 @@ class Session extends EventEmitter {
this.emit("connected");
}
_handlePlayerJoined(id) {
this._addPeer(id, false, false);
}
_handlePlayerLeft(id) {
if (id in this.peers) {
this.peers[id].connection.destroy();
delete this.peers[id];
}
}
_handleSignal(data) {
const { from, signal } = data;
if (from in this.peers) {
@ -273,8 +267,8 @@ class Session extends EventEmitter {
}
_handleSocketReconnect() {
if (this._partyId) {
this.joinParty(this._partyId, this._password);
if (this._gameId) {
this.joinGame(this._gameId, this._password);
}
}
}

View File

@ -93,7 +93,7 @@ function Game() {
// Join game
useEffect(() => {
if (session.state === "online" && databaseStatus !== "loading") {
session.joinParty(gameId, password);
session.joinGame(gameId, password);
}
}, [gameId, password, databaseStatus, session, offline]);