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

View File

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

View File

@ -9,10 +9,6 @@ import { getRandomMonster } from "../helpers/monsters";
export const PlayerStateContext = 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 }) {
const { userId } = useContext(AuthContext);
@ -23,7 +19,6 @@ export function PlayerProvider({ session, children }) {
nickname: "",
timer: null,
dice: { share: false, rolls: [] },
pointer: { position: { x: 0, y: 0 }, visible: false },
sessionId: null,
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 (
<PlayerStateContext.Provider value={playerState}>
<PlayerUpdaterContext.Provider value={setPlayerState}>
<PlayerStateWithoutPointerContext.Provider
value={playerStateWithoutPointer}
>
{children}
</PlayerStateWithoutPointerContext.Provider>
{children}
</PlayerUpdaterContext.Provider>
</PlayerStateContext.Provider>
);

View File

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