Moved pointer out of player state and into separate socket event

This commit is contained in:
Mitchell McCaffrey 2021-01-01 12:37:54 +11:00
parent 501fc4377c
commit b13b46b17d
4 changed files with 43 additions and 70 deletions

View File

@ -18,8 +18,6 @@ import MapStageContext, {
import AuthContext from "../../contexts/AuthContext"; import AuthContext from "../../contexts/AuthContext";
import SettingsContext from "../../contexts/SettingsContext"; import SettingsContext from "../../contexts/SettingsContext";
import KeyboardContext from "../../contexts/KeyboardContext"; import KeyboardContext from "../../contexts/KeyboardContext";
import { PlayerUpdaterContext } from "../../contexts/PlayerContext";
import PartyContext from "../../contexts/PartyContext";
function MapInteraction({ function MapInteraction({
map, map,
@ -180,8 +178,6 @@ function MapInteraction({
const auth = useContext(AuthContext); const auth = useContext(AuthContext);
const settings = useContext(SettingsContext); const settings = useContext(SettingsContext);
const player = useContext(PlayerUpdaterContext);
const party = useContext(PartyContext);
const mapInteraction = { const mapInteraction = {
stageScale, stageScale,
@ -223,19 +219,15 @@ function MapInteraction({
/> />
{/* Forward auth context to konva elements */} {/* Forward auth context to konva elements */}
<AuthContext.Provider value={auth}> <AuthContext.Provider value={auth}>
<PlayerUpdaterContext.Provider value={player}> <SettingsContext.Provider value={settings}>
<PartyContext.Provider value={party}> <KeyboardContext.Provider value={keyboardValue}>
<SettingsContext.Provider value={settings}> <MapInteractionProvider value={mapInteraction}>
<KeyboardContext.Provider value={keyboardValue}> <MapStageProvider value={mapStageRef}>
<MapInteractionProvider value={mapInteraction}> {mapLoaded && children}
<MapStageProvider value={mapStageRef}> </MapStageProvider>
{mapLoaded && children} </MapInteractionProvider>
</MapStageProvider> </KeyboardContext.Provider>
</MapInteractionProvider> </SettingsContext.Provider>
</KeyboardContext.Provider>
</SettingsContext.Provider>
</PartyContext.Provider>
</PlayerUpdaterContext.Provider>
</AuthContext.Provider> </AuthContext.Provider>
</Layer> </Layer>
</Stage> </Stage>

View File

@ -17,12 +17,12 @@ import useSetting from "../../helpers/useSetting";
import PartyContext from "../../contexts/PartyContext"; import PartyContext from "../../contexts/PartyContext";
import { import {
PlayerUpdaterContext, PlayerUpdaterContext,
PlayerStateWithoutPointerContext, PlayerStateContext,
} from "../../contexts/PlayerContext"; } from "../../contexts/PlayerContext";
function Party({ gameId, stream, partyStreams, onStreamStart, onStreamEnd }) { function Party({ gameId, stream, partyStreams, onStreamStart, onStreamEnd }) {
const setPlayerState = useContext(PlayerUpdaterContext); const setPlayerState = useContext(PlayerUpdaterContext);
const playerState = useContext(PlayerStateWithoutPointerContext); const playerState = useContext(PlayerStateContext);
const partyState = useContext(PartyContext); const partyState = useContext(PartyContext);
const [fullScreen] = useSetting("map.fullScreen"); const [fullScreen] = useSetting("map.fullScreen");

View File

@ -9,10 +9,6 @@ import { getRandomMonster } from "../helpers/monsters";
export const PlayerStateContext = React.createContext(); export const PlayerStateContext = React.createContext();
export const PlayerUpdaterContext = React.createContext(() => {}); export const PlayerUpdaterContext = React.createContext(() => {});
/**
* Store the player state without the pointer data to prevent unnecessary updates
*/
export const PlayerStateWithoutPointerContext = React.createContext();
export function PlayerProvider({ session, children }) { export function PlayerProvider({ session, children }) {
const { userId } = useContext(AuthContext); const { userId } = useContext(AuthContext);
@ -23,7 +19,6 @@ export function PlayerProvider({ session, children }) {
nickname: "", nickname: "",
timer: null, timer: null,
dice: { share: false, rolls: [] }, dice: { share: false, rolls: [] },
pointer: { position: { x: 0, y: 0 }, visible: false },
sessionId: null, sessionId: null,
userId, userId,
}, },
@ -94,27 +89,10 @@ export function PlayerProvider({ session, children }) {
}; };
}); });
const [playerStateWithoutPointer, setPlayerStateWithoutPointer] = useState(
playerState
);
useEffect(() => {
const { pointer, ...state } = playerState;
if (
!playerStateWithoutPointer ||
!compare(playerStateWithoutPointer, state)
) {
setPlayerStateWithoutPointer(state);
}
}, [playerState, playerStateWithoutPointer]);
return ( return (
<PlayerStateContext.Provider value={playerState}> <PlayerStateContext.Provider value={playerState}>
<PlayerUpdaterContext.Provider value={setPlayerState}> <PlayerUpdaterContext.Provider value={setPlayerState}>
<PlayerStateWithoutPointerContext.Provider {children}
value={playerStateWithoutPointer}
>
{children}
</PlayerStateWithoutPointerContext.Provider>
</PlayerUpdaterContext.Provider> </PlayerUpdaterContext.Provider>
</PlayerStateContext.Provider> </PlayerStateContext.Provider>
); );

View File

@ -2,24 +2,23 @@ import React, { useState, useContext, useEffect, useRef } from "react";
import { Group } from "react-konva"; import { Group } from "react-konva";
import AuthContext from "../contexts/AuthContext"; import AuthContext from "../contexts/AuthContext";
import PartyContext from "../contexts/PartyContext";
import { PlayerUpdaterContext } from "../contexts/PlayerContext";
import MapPointer from "../components/map/MapPointer"; import MapPointer from "../components/map/MapPointer";
import { isEmpty } from "../helpers/shared"; import { isEmpty } from "../helpers/shared";
import { lerp, compare } from "../helpers/vector2"; import { lerp, compare } from "../helpers/vector2";
// Send pointer updates every 33ms // Send pointer updates every 50ms (20fps)
const sendTickRate = 100; const sendTickRate = 50;
let t = 0; function NetworkedMapPointer({ session, active, gridSize }) {
function NetworkedMapPointer({ active, gridSize }) {
const { userId } = useContext(AuthContext); const { userId } = useContext(AuthContext);
const setPlayerState = useContext(PlayerUpdaterContext);
const partyState = useContext(PartyContext);
const [localPointerState, setLocalPointerState] = useState({}); const [localPointerState, setLocalPointerState] = useState({});
const sessionRef = useRef(session);
useEffect(() => {
sessionRef.current = session;
}, [session]);
useEffect(() => { useEffect(() => {
if (userId && !(userId in localPointerState)) { if (userId && !(userId in localPointerState)) {
setLocalPointerState({ setLocalPointerState({
@ -44,14 +43,15 @@ function NetworkedMapPointer({ active, gridSize }) {
if (counter > sendTickRate) { if (counter > sendTickRate) {
counter -= sendTickRate; counter -= sendTickRate;
if (ownPointerUpdateRef.current) { if (
const { position, visible } = ownPointerUpdateRef.current; ownPointerUpdateRef.current &&
console.log("send time", performance.now() - t); sessionRef.current &&
t = performance.now(); sessionRef.current.socket
setPlayerState((prev) => ({ ) {
...prev, sessionRef.current.socket.emit(
pointer: { position, visible }, "player_pointer",
})); ownPointerUpdateRef.current
);
ownPointerUpdateRef.current = null; ownPointerUpdateRef.current = null;
} }
} }
@ -86,13 +86,9 @@ function NetworkedMapPointer({ active, gridSize }) {
const interpolationsRef = useRef({}); const interpolationsRef = useRef({});
useEffect(() => { useEffect(() => {
// TODO: Handle player disconnect while pointer visible // TODO: Handle player disconnect while pointer visible
const interpolations = interpolationsRef.current; function handleSocketPlayerPointer(pointer) {
for (let player of Object.values(partyState)) { const interpolations = interpolationsRef.current;
const id = player.userId; const id = pointer.id;
const pointer = player.pointer;
if (!id) {
continue;
}
if (!(id in interpolations)) { if (!(id in interpolations)) {
interpolations[id] = { interpolations[id] = {
id, id,
@ -103,8 +99,6 @@ function NetworkedMapPointer({ active, gridSize }) {
!compare(interpolations[id].to.position, pointer.position, 0.0001) || !compare(interpolations[id].to.position, pointer.position, 0.0001) ||
interpolations[id].to.visible !== pointer.visible interpolations[id].to.visible !== pointer.visible
) { ) {
console.log("receive time", performance.now() - t, pointer.position);
t = performance.now();
const from = interpolations[id].to; const from = interpolations[id].to;
interpolations[id] = { interpolations[id] = {
id, id,
@ -119,14 +113,23 @@ function NetworkedMapPointer({ active, gridSize }) {
}; };
} }
} }
}, [partyState]);
if (session.socket) {
session.socket.on("player_pointer", handleSocketPlayerPointer);
}
return () => {
session.socket.off("player_pointer", handleSocketPlayerPointer);
};
}, [session]);
// Animate to the peer pointer positions // Animate to the peer pointer positions
useEffect(() => { useEffect(() => {
let request = requestAnimationFrame(animate); let request = requestAnimationFrame(animate);
function animate(time) { function animate() {
request = requestAnimationFrame(animate); request = requestAnimationFrame(animate);
const time = performance.now();
let interpolatedPointerState = {}; let interpolatedPointerState = {};
for (let interp of Object.values(interpolationsRef.current)) { for (let interp of Object.values(interpolationsRef.current)) {
if (!interp.from || !interp.to) { if (!interp.from || !interp.to) {