From 524a6d9aea55967dd7bc34fc58deddea2e03358d Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Mon, 4 Jan 2021 09:34:25 +1100 Subject: [PATCH] Update network partial updates to have id to prevent invalid changes --- src/helpers/useNetworkedState.js | 53 ++++++++++++++++++++++------ src/network/NetworkedMapAndTokens.js | 5 ++- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/helpers/useNetworkedState.js b/src/helpers/useNetworkedState.js index e005fca..df3e7f7 100644 --- a/src/helpers/useNetworkedState.js +++ b/src/helpers/useNetworkedState.js @@ -3,14 +3,34 @@ import { useEffect, useState, useRef, useCallback } from "react"; import useDebounce from "./useDebounce"; import { diff, applyChanges } from "./diff"; +/** + * @callback setNetworkedState + * @param {any} update The updated state or a state function passed into setState + * @param {boolean} sync Whether to sync the update with the session + * @param {boolean} force Whether to force a full update, usefull when partialUpdates is enabled + */ + +/** + * Helper to sync a react state to a `Session` + * + * @param {any} initialState + * @param {Session} session `Session` instance + * @param {string} eventName Name of the event to send to the session + * @param {number} debounceRate Amount to debounce before sending to the session (ms) + * @param {boolean} partialUpdates Allow sending of partial updates to the session + * @param {string} partialUpdatesKey Key to lookup in the state to identify a partial update + * + * @returns {[any, setNetworkedState]} + */ function useNetworkedState( - defaultState, + initialState, session, eventName, debounceRate = 100, - partialUpdates = true + partialUpdates = true, + partialUpdatesKey = "id" ) { - const [state, _setState] = useState(defaultState); + const [state, _setState] = useState(initialState); // Used to control whether the state needs to be sent to the socket const dirtyRef = useRef(false); @@ -42,7 +62,8 @@ function useNetworkedState( ) { const changes = diff(lastSyncedStateRef.current, debouncedState); if (changes) { - session.socket.emit(`${eventName}_update`, changes); + const update = { id: debouncedState[partialUpdatesKey], changes }; + session.socket.emit(`${eventName}_update`, update); } } else { session.socket.emit(eventName, debouncedState); @@ -51,7 +72,13 @@ function useNetworkedState( forceUpdateRef.current = false; lastSyncedStateRef.current = debouncedState; } - }, [session.socket, eventName, debouncedState, partialUpdates]); + }, [ + session.socket, + eventName, + debouncedState, + partialUpdates, + partialUpdatesKey, + ]); useEffect(() => { function handleSocketEvent(data) { @@ -59,12 +86,16 @@ function useNetworkedState( lastSyncedStateRef.current = data; } - function handleSocketUpdateEvent(changes) { + function handleSocketUpdateEvent(update) { _setState((prevState) => { - let newState = { ...prevState }; - applyChanges(newState, changes); - lastSyncedStateRef.current = newState; - return newState; + if (prevState[partialUpdatesKey] === update.id) { + let newState = { ...prevState }; + applyChanges(newState, update.changes); + lastSyncedStateRef.current = newState; + return newState; + } else { + return prevState; + } }); } @@ -74,7 +105,7 @@ function useNetworkedState( session.socket?.off(eventName, handleSocketEvent); session.socket?.off(`${eventName}_update`, handleSocketUpdateEvent); }; - }, [session.socket, eventName]); + }, [session.socket, eventName, partialUpdatesKey]); return [state, setState]; } diff --git a/src/network/NetworkedMapAndTokens.js b/src/network/NetworkedMapAndTokens.js index 810cfee..a525f5a 100644 --- a/src/network/NetworkedMapAndTokens.js +++ b/src/network/NetworkedMapAndTokens.js @@ -44,7 +44,10 @@ function NetworkedMapAndTokens({ session }) { const [currentMapState, setCurrentMapState] = useNetworkedState( null, session, - "map_state" + "map_state", + 100, + true, + "mapId" ); const [assetManifest, setAssetManifest] = useNetworkedState( [],