From ad236d6e6ad15c3926c6b84679eb673414cfe62f Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Sun, 19 Apr 2020 15:15:48 +1000 Subject: [PATCH] Added network sync to drawing --- src/components/Map.js | 40 ++++++----------------- src/routes/Game.js | 75 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 30 deletions(-) diff --git a/src/components/Map.js b/src/components/Map.js index b958102..ebb3bd5 100644 --- a/src/components/Map.js +++ b/src/components/Map.js @@ -22,6 +22,11 @@ function Map({ onMapTokenChange, onMapTokenRemove, onMapChange, + onMapDraw, + onMapDrawUndo, + onMapDrawRedo, + drawActions, + drawActionIndex, }) { function handleProxyDragEnd(isOnMap, token) { if (isOnMap && onMapTokenChange) { @@ -40,30 +45,15 @@ function Map({ const [selectedTool, setSelectedTool] = useState("pan"); const [drawnShapes, setDrawnShapes] = useState([]); - const [drawActions, setDrawActions] = useState([]); - const [drawActionIndex, setDrawActionIndex] = useState(-1); function handleShapeAdd(shape) { - setDrawActions((prevActions) => { - const newActions = [ - ...prevActions.slice(0, drawActionIndex + 1), - { type: "add", shape }, - ]; - setDrawActionIndex(newActions.length - 1); - return newActions; - }); + onMapDraw({ type: "add", shape }); } function handleShapeRemove(shapeId) { - setDrawActions((prevActions) => { - const newActions = [ - ...prevActions.slice(0, drawActionIndex + 1), - { type: "remove", shapeId }, - ]; - setDrawActionIndex(newActions.length - 1); - return newActions; - }); + onMapDraw({ type: "remove", shapeId }); } + // Replay the draw actions and convert them to shapes for the map drawing useEffect(() => { let shapesById = {}; for (let i = 0; i <= drawActionIndex; i++) { @@ -78,16 +68,6 @@ function Map({ setDrawnShapes(Object.values(shapesById)); }, [drawActions, drawActionIndex]); - function handleDrawActionUndo() { - setDrawActionIndex((prevIndex) => Math.max(prevIndex - 1, -1)); - } - - function handleDrawActionRedo() { - setDrawActionIndex((prevIndex) => - Math.min(prevIndex + 1, drawActions.length - 1) - ); - } - const disabledTools = []; if (!mapData) { disabledTools.push("pan"); @@ -290,8 +270,8 @@ function Map({ onToolChange={setSelectedTool} selectedTool={selectedTool} disabledTools={disabledTools} - onUndo={handleDrawActionUndo} - onRedo={handleDrawActionRedo} + onUndo={onMapDrawUndo} + onRedo={onMapDrawRedo} undoDisabled={drawActionIndex < 0} redoDisabled={drawActionIndex === drawActions.length - 1} /> diff --git a/src/routes/Game.js b/src/routes/Game.js index 2830635..ccd55b4 100644 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -36,6 +36,10 @@ function Game() { handlePeerError ); + /** + * Map state + */ + const [mapSource, setMapSource] = useState(null); const mapDataRef = useRef(null); @@ -74,6 +78,52 @@ function Game() { } } + const [mapDrawActions, setMapDrawActions] = useState([]); + // An index into the draw actions array to which only actions before the + // index will be performed (used in undo and redo) + const [mapDrawActionIndex, setMapDrawActionIndex] = useState(-1); + function addNewMapDrawActions(actions) { + setMapDrawActions((prevActions) => { + const newActions = [ + ...prevActions.slice(0, mapDrawActionIndex + 1), + ...actions, + ]; + const newIndex = newActions.length - 1; + setMapDrawActionIndex(newIndex); + return newActions; + }); + } + + function handleMapDraw(action) { + addNewMapDrawActions([action]); + for (let peer of Object.values(peers)) { + peer.connection.send({ id: "mapDraw", data: [action] }); + } + } + + function handleMapDrawUndo() { + const newIndex = Math.max(mapDrawActionIndex - 1, -1); + setMapDrawActionIndex(newIndex); + for (let peer of Object.values(peers)) { + peer.connection.send({ id: "mapDrawIndex", data: newIndex }); + } + } + + function handleMapDrawRedo() { + const newIndex = Math.min( + mapDrawActionIndex + 1, + mapDrawActions.length - 1 + ); + setMapDrawActionIndex(newIndex); + for (let peer of Object.values(peers)) { + peer.connection.send({ id: "mapDrawIndex", data: newIndex }); + } + } + + /** + * Party state + */ + const { nickname, setNickname } = useNickname(); const [partyNicknames, setPartyNicknames] = useState({}); @@ -94,6 +144,10 @@ function Game() { } } + /** + * Peer handlers + */ + function handlePeerData({ data, peer }) { if (data.id === "sync") { if (mapSource) { @@ -102,6 +156,12 @@ function Game() { if (mapTokens) { peer.connection.send({ id: "tokenEdit", data: mapTokens }); } + if (mapDrawActions) { + peer.connection.send({ id: "mapDraw", data: mapDrawActions }); + } + if (mapDrawActionIndex != mapDrawActions.length - 1) { + peer.connection.send({ id: "mapDrawIndex", data: mapDrawActionIndex }); + } } if (data.id === "map") { const blob = new Blob([data.data.file]); @@ -125,6 +185,12 @@ function Game() { ...data.data, })); } + if (data.id === "mapDraw") { + addNewMapDrawActions(data.data); + } + if (data.id === "mapDrawIndex") { + setMapDrawActionIndex(data.data); + } } function handlePeerDisconnected(peer) { @@ -169,6 +235,10 @@ function Game() { } } + /** + * Stream handler + */ + function handleStreamStart(localStream) { setStream(localStream); const tracks = localStream.getTracks(); @@ -238,6 +308,11 @@ function Game() { onMapTokenChange={handleMapTokenChange} onMapTokenRemove={handleMapTokenRemove} onMapChange={handleMapChange} + onMapDraw={handleMapDraw} + onMapDrawUndo={handleMapDrawUndo} + onMapDrawRedo={handleMapDrawRedo} + drawActions={mapDrawActions} + drawActionIndex={mapDrawActionIndex} />