From 887bce81d1fe870f2504de315790e952fdfc44e2 Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Tue, 19 May 2020 22:15:08 +1000 Subject: [PATCH] Refactored peer sync logic to use the data providers --- src/contexts/MapDataContext.js | 28 ++++- src/contexts/TokenDataContext.js | 21 +++- src/modals/SelectMapModal.js | 6 +- src/routes/Game.js | 189 ++++++++++++++----------------- 4 files changed, 131 insertions(+), 113 deletions(-) diff --git a/src/contexts/MapDataContext.js b/src/contexts/MapDataContext.js index bdd6193..433eb9b 100644 --- a/src/contexts/MapDataContext.js +++ b/src/contexts/MapDataContext.js @@ -54,10 +54,7 @@ export function MapDataProvider({ children }) { } async function loadMaps() { - let storedMaps = await database - .table("maps") - .where({ owner: userId }) - .toArray(); + let storedMaps = await database.table("maps").toArray(); const sortedMaps = storedMaps.sort((a, b) => b.created - a.created); const defaultMapsWithIds = await getDefaultMaps(); const allMaps = [...sortedMaps, ...defaultMapsWithIds]; @@ -129,14 +126,37 @@ export function MapDataProvider({ children }) { }); } + async function putMap(map) { + await database.table("maps").put(map); + setMaps((prevMaps) => { + const newMaps = [...prevMaps]; + const i = newMaps.findIndex((m) => m.id === map.id); + if (i > -1) { + newMaps[i] = { ...newMaps[i], ...map }; + } else { + newMaps.unshift(map); + } + return newMaps; + }); + } + + function getMap(mapId) { + return maps.find((map) => map.id === mapId); + } + + const ownedMaps = maps.filter((map) => map.owner === userId); + const value = { maps, + ownedMaps, mapStates, addMap, removeMap, resetMap, updateMap, updateMapState, + putMap, + getMap, }; return ( {children} diff --git a/src/contexts/TokenDataContext.js b/src/contexts/TokenDataContext.js index e28aa0d..096d006 100644 --- a/src/contexts/TokenDataContext.js +++ b/src/contexts/TokenDataContext.js @@ -68,11 +68,21 @@ export function TokenDataProvider({ children }) { } async function putToken(token) { - if (tokens.includes((t) => t.id === token.id)) { - await updateToken(token.id, token); - } else { - await addToken(token); - } + await database.table("tokens").put(token); + setTokens((prevTokens) => { + const newTokens = [...prevTokens]; + const i = newTokens.findIndex((t) => t.id === token.id); + if (i > -1) { + newTokens[i] = { ...newTokens[i], ...token }; + } else { + newTokens.unshift(token); + } + return newTokens; + }); + } + + function getToken(tokenId) { + return tokens.find((token) => token.id === tokenId); } const ownedTokens = tokens.filter((token) => token.owner === userId); @@ -84,6 +94,7 @@ export function TokenDataProvider({ children }) { removeToken, updateToken, putToken, + getToken, }; return ( diff --git a/src/modals/SelectMapModal.js b/src/modals/SelectMapModal.js index 9a84cee..91de48a 100644 --- a/src/modals/SelectMapModal.js +++ b/src/modals/SelectMapModal.js @@ -30,7 +30,7 @@ function SelectMapModal({ }) { const { userId } = useContext(AuthContext); const { - maps, + ownedMaps, mapStates, addMap, removeMap, @@ -44,7 +44,7 @@ function SelectMapModal({ // The map selected in the modal const [selectedMapId, setSelectedMapId] = useState(null); - const selectedMap = maps.find((map) => map.id === selectedMapId); + const selectedMap = ownedMaps.find((map) => map.id === selectedMapId); const selectedMapState = mapStates.find( (state) => state.mapId === selectedMapId ); @@ -189,7 +189,7 @@ function SelectMapModal({ Select or import a map { if ( debouncedMapState && debouncedMapState.mapId && - map && - map.owner === userId && + currentMap && + currentMap.owner === userId && database ) { + // Update the database directly to avoid re-renders database .table("states") .update(debouncedMapState.mapId, debouncedMapState); } - }, [map, debouncedMapState, userId, database]); + }, [currentMap, debouncedMapState, userId, database]); function handleMapChange(newMap, newMapState) { - setMapState(newMapState); - setMap(newMap); + setCurrentMapState(newMapState); + setCurrentMap(newMap); for (let peer of Object.values(peers)) { // Clear the map so the new map state isn't shown on an old map peer.connection.send({ id: "map", data: null }); @@ -110,14 +113,14 @@ function Game() { } function handleMapStateChange(newMapState) { - setMapState(newMapState); + setCurrentMapState(newMapState); for (let peer of Object.values(peers)) { peer.connection.send({ id: "mapState", data: newMapState }); } } function addMapDrawActions(actions, indexKey, actionsKey) { - setMapState((prevMapState) => { + setCurrentMapState((prevMapState) => { const newActions = [ ...prevMapState[actionsKey].slice(0, prevMapState[indexKey] + 1), ...actions, @@ -133,11 +136,11 @@ function Game() { function updateDrawActionIndex(change, indexKey, actionsKey, peerId) { const newIndex = Math.min( - Math.max(mapState[indexKey] + change, -1), - mapState[actionsKey].length - 1 + Math.max(currentMapState[indexKey] + change, -1), + currentMapState[actionsKey].length - 1 ); - setMapState((prevMapState) => ({ + setCurrentMapState((prevMapState) => ({ ...prevMapState, [indexKey]: newIndex, })); @@ -206,10 +209,27 @@ function Game() { * Token state */ + // Get all tokens from a token state and send it to a peer + function sendTokensToPeer(peer, state) { + let sentTokens = {}; + for (let tokenState of Object.values(state.tokens)) { + if ( + tokenState.tokenType === "file" && + !(tokenState.tokenId in sentTokens) + ) { + sentTokens[tokenState.tokenId] = true; + const token = getToken(tokenState.tokenId); + // Omit file from token peer will request file if needed + const { file, ...rest } = token; + peer.connection.send({ id: "token", data: rest }); + } + } + } + async function handleMapTokenStateCreate(tokenState) { // If file type token send the token to the other peers if (tokenState.tokenType === "file") { - const token = await database.table("tokens").get(tokenState.tokenId); + const token = getToken(tokenState.tokenId); const { file, ...rest } = token; for (let peer of Object.values(peers)) { peer.connection.send({ id: "token", data: rest }); @@ -219,10 +239,10 @@ function Game() { } function handleMapTokenStateChange(tokenState) { - if (mapState === null) { + if (currentMapState === null) { return; } - setMapState((prevMapState) => ({ + setCurrentMapState((prevMapState) => ({ ...prevMapState, tokens: { ...prevMapState.tokens, @@ -236,7 +256,7 @@ function Game() { } function handleMapTokenStateRemove(tokenState) { - setMapState((prevMapState) => { + setCurrentMapState((prevMapState) => { const { [tokenState.id]: old, ...rest } = prevMapState.tokens; return { ...prevMapState, tokens: rest }; }); @@ -273,58 +293,37 @@ function Game() { * Peer handlers */ - const { putToken } = useContext(TokenDataContext); - - function sendTokensToPeer(peer, state) { - let sentTokens = {}; - for (let tokenState of Object.values(state.tokens)) { - if ( - tokenState.tokenType === "file" && - !(tokenState.tokenId in sentTokens) - ) { - sentTokens[tokenState.tokenId] = true; - database - .table("tokens") - .get(tokenState.tokenId) - .then((token) => { - const { file, ...rest } = token; - peer.connection.send({ id: "token", data: rest }); - }); - } - } - } + const { putToken, getToken } = useContext(TokenDataContext); + const { putMap, getMap } = useContext(MapDataContext); function handlePeerData({ data, peer }) { if (data.id === "sync") { - if (mapState) { - peer.connection.send({ id: "mapState", data: mapState }); - sendTokensToPeer(peer, mapState); + if (currentMapState) { + peer.connection.send({ id: "mapState", data: currentMapState }); + sendTokensToPeer(peer, currentMapState); } - if (map) { - sendMapDataToPeer(peer, map); + if (currentMap) { + sendMapDataToPeer(peer, currentMap); } } if (data.id === "map") { const newMap = data.data; // If is a file map check cache and request the full file if outdated if (newMap && newMap.type === "file") { - database - .table("maps") - .get(newMap.id) - .then((cachedMap) => { - if (cachedMap && cachedMap.lastModified === newMap.lastModified) { - setMap(cachedMap); - } else { - setMapLoading(true); - peer.connection.send({ id: "mapRequest" }); - } - }); + const cachedMap = getMap(newMap.id); + if (cachedMap && cachedMap.lastModified === newMap.lastModified) { + setCurrentMap(cachedMap); + } else { + setMapLoading(true); + peer.connection.send({ id: "mapRequest", data: newMap.id }); + } } else { - setMap(newMap); + setCurrentMap(newMap); } } // Send full map data including file if (data.id === "mapRequest") { + const map = getMap(data.data); peer.connection.send({ id: "mapResponse", data: map }); } // A new map response with a file attached @@ -332,47 +331,35 @@ function Game() { setMapLoading(false); if (data.data && data.data.type === "file") { const newMap = { ...data.data, file: data.data.file }; - // Store in db - database - .table("maps") - .put(newMap) - .then(() => { - setMap(newMap); - }); + putMap(newMap).then(() => { + setCurrentMap(newMap); + }); } else { - setMap(data.data); + setCurrentMap(data.data); } } if (data.id === "mapState") { - setMapState(data.data); + setCurrentMapState(data.data); } if (data.id === "token") { const newToken = data.data; if (newToken && newToken.type === "file") { - database - .table("tokens") - .get(newToken.id) - .then((cachedToken) => { - if ( - !cachedToken || - cachedToken.lastModified !== newToken.lastModified - ) { - setMapLoading(true); - peer.connection.send({ - id: "tokenRequest", - data: newToken.id, - }); - } + const cachedToken = getToken(newToken.id); + if ( + !cachedToken || + cachedToken.lastModified !== newToken.lastModified + ) { + setMapLoading(true); + peer.connection.send({ + id: "tokenRequest", + data: newToken.id, }); + } } } if (data.id === "tokenRequest") { - database - .table("tokens") - .get(data.data) - .then((token) => { - peer.connection.send({ id: "tokenResponse", data: token }); - }); + const token = getToken(data.data); + peer.connection.send({ id: "tokenResponse", data: token }); } if (data.id === "tokenResponse") { setMapLoading(false); @@ -382,13 +369,13 @@ function Game() { } } if (data.id === "tokenStateEdit") { - setMapState((prevMapState) => ({ + setCurrentMapState((prevMapState) => ({ ...prevMapState, tokens: { ...prevMapState.tokens, ...data.data }, })); } if (data.id === "tokenStateRemove") { - setMapState((prevMapState) => ({ + setCurrentMapState((prevMapState) => ({ ...prevMapState, tokens: omit(prevMapState.tokens, Object.keys(data.data)), })); @@ -403,7 +390,7 @@ function Game() { addMapDrawActions(data.data, "mapDrawActionIndex", "mapDrawActions"); } if (data.id === "mapDrawIndex") { - setMapState((prevMapState) => ({ + setCurrentMapState((prevMapState) => ({ ...prevMapState, mapDrawActionIndex: data.data, })); @@ -412,7 +399,7 @@ function Game() { addMapDrawActions(data.data, "fogDrawActionIndex", "fogDrawActions"); } if (data.id === "mapFogIndex") { - setMapState((prevMapState) => ({ + setCurrentMapState((prevMapState) => ({ ...prevMapState, fogDrawActionIndex: data.data, })); @@ -532,8 +519,8 @@ function Game() { onStreamEnd={handleStreamEnd} />