Moved peer connections to an optional pull model
This commit is contained in:
parent
b40f78042f
commit
6a4c6b30ec
@ -440,6 +440,7 @@ function Map({
|
||||
return (
|
||||
<MapInteraction
|
||||
map={map}
|
||||
mapState={mapState}
|
||||
controls={
|
||||
<>
|
||||
{mapControls}
|
||||
|
@ -21,6 +21,7 @@ import KeyboardContext from "../../contexts/KeyboardContext";
|
||||
|
||||
function MapInteraction({
|
||||
map,
|
||||
mapState,
|
||||
children,
|
||||
controls,
|
||||
selectedToolId,
|
||||
@ -32,12 +33,16 @@ function MapInteraction({
|
||||
// Map loaded taking in to account different resolutions
|
||||
const [mapLoaded, setMapLoaded] = useState(false);
|
||||
useEffect(() => {
|
||||
if (map === null) {
|
||||
if (
|
||||
!map ||
|
||||
(map.type === "file" && !map.file && !map.resolutions) ||
|
||||
mapState.mapId !== map.id
|
||||
) {
|
||||
setMapLoaded(false);
|
||||
} else if (mapImageSourceStatus === "loaded") {
|
||||
setMapLoaded(true);
|
||||
}
|
||||
}, [mapImageSourceStatus, map]);
|
||||
}, [mapImageSourceStatus, map, mapState]);
|
||||
|
||||
const [stageWidth, setStageWidth] = useState(1);
|
||||
const [stageHeight, setStageHeight] = useState(1);
|
||||
|
@ -2,12 +2,14 @@ import React, { useState, useEffect, useContext } from "react";
|
||||
|
||||
import useNetworkedState from "../helpers/useNetworkedState";
|
||||
import DatabaseContext from "./DatabaseContext";
|
||||
import AuthContext from "./AuthContext";
|
||||
|
||||
import { getRandomMonster } from "../helpers/monsters";
|
||||
|
||||
const PlayerContext = React.createContext();
|
||||
|
||||
export function PlayerProvider({ session, children }) {
|
||||
const { userId } = useContext(AuthContext);
|
||||
const { database, databaseStatus } = useContext(DatabaseContext);
|
||||
|
||||
const [playerState, setPlayerState] = useNetworkedState(
|
||||
@ -16,6 +18,8 @@ export function PlayerProvider({ session, children }) {
|
||||
timer: null,
|
||||
dice: { share: false, rolls: [] },
|
||||
pointer: {},
|
||||
sessionId: null,
|
||||
userId,
|
||||
},
|
||||
session,
|
||||
"player_state"
|
||||
@ -56,9 +60,16 @@ export function PlayerProvider({ session, children }) {
|
||||
}, [playerState, database, databaseStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleConnected() {
|
||||
setPlayerState((prevState) => ({
|
||||
...prevState,
|
||||
userId,
|
||||
}));
|
||||
}, [userId]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleSocketConnect() {
|
||||
// Set the player state to trigger a sync
|
||||
setPlayerState(playerState);
|
||||
setPlayerState({ ...playerState, sessionId: session.id });
|
||||
}
|
||||
|
||||
function handleSocketPartyState(partyState) {
|
||||
@ -70,14 +81,20 @@ export function PlayerProvider({ session, children }) {
|
||||
}
|
||||
}
|
||||
|
||||
session.on("connected", handleSocketConnect);
|
||||
|
||||
if (session.socket) {
|
||||
session.on("connected", handleConnected);
|
||||
session.socket.on("connect", handleSocketConnect);
|
||||
session.socket.on("reconnect", handleSocketConnect);
|
||||
session.socket.on("party_state", handleSocketPartyState);
|
||||
}
|
||||
|
||||
return () => {
|
||||
session.off("connected", handleSocketConnect);
|
||||
|
||||
if (session.socket) {
|
||||
session.off("connected", handleConnected);
|
||||
session.socket.off("connect", handleSocketConnect);
|
||||
session.socket.off("reconnect", handleSocketConnect);
|
||||
session.socket.off("party_state", handleSocketPartyState);
|
||||
}
|
||||
};
|
||||
|
@ -11,7 +11,28 @@ function useDataSource(data, defaultSources, unknownSource) {
|
||||
}
|
||||
let url = unknownSource;
|
||||
if (data.type === "file") {
|
||||
url = URL.createObjectURL(new Blob([data.file]));
|
||||
if (data.resolutions) {
|
||||
// Check is a resolution is specified
|
||||
if (data.quality && data.resolutions[data.quality]) {
|
||||
url = URL.createObjectURL(
|
||||
new Blob([data.resolutions[data.quality].file])
|
||||
);
|
||||
}
|
||||
// If no file available fallback to the highest resolution
|
||||
else if (!data.file) {
|
||||
const resolutionArray = Object.keys(data.resolutions);
|
||||
url = URL.createObjectURL(
|
||||
new Blob([
|
||||
data.resolutions[resolutionArray[resolutionArray.length - 1]]
|
||||
.file,
|
||||
])
|
||||
);
|
||||
} else {
|
||||
url = URL.createObjectURL(new Blob([data.file]));
|
||||
}
|
||||
} else {
|
||||
url = URL.createObjectURL(new Blob([data.file]));
|
||||
}
|
||||
} else if (data.type === "default") {
|
||||
url = defaultSources[data.key];
|
||||
}
|
||||
@ -19,7 +40,10 @@ function useDataSource(data, defaultSources, unknownSource) {
|
||||
|
||||
return () => {
|
||||
if (data.type === "file" && url) {
|
||||
URL.revokeObjectURL(url);
|
||||
// Remove file url after 5 seconds as we still may be using it while the next image loads
|
||||
setTimeout(() => {
|
||||
URL.revokeObjectURL(url);
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
}, [data, defaultSources, unknownSource]);
|
||||
|
@ -3,49 +3,10 @@ import useImage from "use-image";
|
||||
|
||||
import useDataSource from "./useDataSource";
|
||||
|
||||
import { isEmpty } from "./shared";
|
||||
|
||||
import { mapSources as defaultMapSources } from "../maps";
|
||||
|
||||
function useMapImage(map) {
|
||||
const [mapSourceMap, setMapSourceMap] = useState({});
|
||||
// Update source map data when either the map or map quality changes
|
||||
useEffect(() => {
|
||||
function updateMapSource() {
|
||||
if (map && map.type === "file" && map.resolutions) {
|
||||
// If quality is set and the quality is available
|
||||
if (map.quality !== "original" && map.resolutions[map.quality]) {
|
||||
setMapSourceMap({
|
||||
...map.resolutions[map.quality],
|
||||
id: map.id,
|
||||
quality: map.quality,
|
||||
});
|
||||
} else if (!map.file) {
|
||||
// If no file fallback to the highest resolution
|
||||
const resolutionArray = Object.keys(map.resolutions);
|
||||
setMapSourceMap({
|
||||
...map.resolutions[resolutionArray[resolutionArray.length - 1]],
|
||||
id: map.id,
|
||||
});
|
||||
} else {
|
||||
setMapSourceMap(map);
|
||||
}
|
||||
} else {
|
||||
setMapSourceMap(map);
|
||||
}
|
||||
}
|
||||
if (map && map.id !== mapSourceMap.id) {
|
||||
updateMapSource();
|
||||
} else if (map && map.type === "file") {
|
||||
if (map.file && map.quality !== mapSourceMap.quality) {
|
||||
updateMapSource();
|
||||
}
|
||||
} else if (!map && !isEmpty(mapSourceMap)) {
|
||||
setMapSourceMap({});
|
||||
}
|
||||
}, [map, mapSourceMap]);
|
||||
|
||||
const mapSource = useDataSource(mapSourceMap, defaultMapSources);
|
||||
const mapSource = useDataSource(map, defaultMapSources);
|
||||
const [mapSourceImage, mapSourceImageStatus] = useImage(mapSource);
|
||||
|
||||
// Create a map source that only updates when the image is fully loaded
|
||||
|
@ -18,6 +18,21 @@ function useNetworkedState(defaultState, session, eventName) {
|
||||
}
|
||||
}, [session.socket, dirty, eventName, state]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleSocketEvent(data) {
|
||||
_setState(data);
|
||||
}
|
||||
|
||||
if (session.socket) {
|
||||
session.socket.on(eventName, handleSocketEvent);
|
||||
}
|
||||
return () => {
|
||||
if (session.socket) {
|
||||
session.socket.off(eventName, handleSocketEvent);
|
||||
}
|
||||
};
|
||||
}, [session.socket]);
|
||||
|
||||
return [state, setState];
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import React, { useState, useContext, useEffect, useCallback } from "react";
|
||||
import React, { useState, useContext, useEffect } from "react";
|
||||
|
||||
import TokenDataContext from "../contexts/TokenDataContext";
|
||||
import MapDataContext from "../contexts/MapDataContext";
|
||||
import MapLoadingContext from "../contexts/MapLoadingContext";
|
||||
import AuthContext from "../contexts/AuthContext";
|
||||
import DatabaseContext from "../contexts/DatabaseContext";
|
||||
import PlayerContext from "../contexts/PlayerContext";
|
||||
|
||||
import { omit } from "../helpers/shared";
|
||||
import useDebounce from "../helpers/useDebounce";
|
||||
@ -26,6 +27,7 @@ import Tokens from "../components/token/Tokens";
|
||||
*/
|
||||
function NetworkedMapAndTokens({ session }) {
|
||||
const { userId } = useContext(AuthContext);
|
||||
const { partyState } = useContext(PlayerContext);
|
||||
const {
|
||||
assetLoadStart,
|
||||
assetLoadFinish,
|
||||
@ -44,6 +46,97 @@ function NetworkedMapAndTokens({ session }) {
|
||||
session,
|
||||
"map_state"
|
||||
);
|
||||
const [assetManifest, setAssetManifest] = useNetworkedState(
|
||||
[],
|
||||
session,
|
||||
"manifest"
|
||||
);
|
||||
|
||||
function loadAssetManifestFromMap(map, mapState) {
|
||||
const assets = [];
|
||||
if (map.type === "file") {
|
||||
const { id, lastModified, owner } = map;
|
||||
assets.push({ type: "map", id, lastModified, owner });
|
||||
}
|
||||
let processedTokens = new Set();
|
||||
for (let tokenState of Object.values(mapState.tokens)) {
|
||||
const token = getToken(tokenState.tokenId);
|
||||
if (
|
||||
token &&
|
||||
token.type === "file" &&
|
||||
!processedTokens.has(tokenState.tokenId)
|
||||
) {
|
||||
processedTokens.add(tokenState.tokenId);
|
||||
// Omit file from token peer will request file if needed
|
||||
const { id, lastModified, owner } = token;
|
||||
assets.push({ type: "token", id, lastModified, owner });
|
||||
}
|
||||
}
|
||||
setAssetManifest(assets);
|
||||
}
|
||||
|
||||
function compareAssets(a, b) {
|
||||
return a.type === b.type && a.id === b.id;
|
||||
}
|
||||
|
||||
// Return true if an asset is out of date
|
||||
function assetNeedsUpdate(oldAsset, newAsset) {
|
||||
return (
|
||||
compareAssets(oldAsset, newAsset) &&
|
||||
oldAsset.lastModified > newAsset.lastModified
|
||||
);
|
||||
}
|
||||
|
||||
function addAssetIfNeeded(asset) {
|
||||
// Asset needs updating
|
||||
const exists = assetManifest.some((oldAsset) =>
|
||||
compareAssets(oldAsset, asset)
|
||||
);
|
||||
const needsUpdate = assetManifest.some((oldAsset) =>
|
||||
assetNeedsUpdate(oldAsset, asset)
|
||||
);
|
||||
if (!exists || needsUpdate) {
|
||||
setAssetManifest((prevAssets) => [
|
||||
...prevAssets.filter((prevAsset) => compareAssets(prevAsset, asset)),
|
||||
asset,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!assetManifest) {
|
||||
return;
|
||||
}
|
||||
|
||||
async function requestAssetsIfNeeded() {
|
||||
for (let asset of assetManifest) {
|
||||
if (asset.owner === userId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const owner = Object.values(partyState).find(
|
||||
(player) => player.userId === asset.owner
|
||||
);
|
||||
if (!owner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (asset.type === "map") {
|
||||
const cachedMap = await getMapFromDB(asset.id);
|
||||
if (cachedMap && cachedMap.lastModified >= asset.lastModified) {
|
||||
// Update last used for cache invalidation
|
||||
const lastUsed = Date.now();
|
||||
await updateMap(cachedMap.id, { lastUsed });
|
||||
setCurrentMap({ ...cachedMap, lastUsed });
|
||||
} else {
|
||||
session.sendTo(owner.sessionId, "mapRequest", asset.id, "map");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestAssetsIfNeeded();
|
||||
}, [assetManifest, partyState, session]);
|
||||
|
||||
/**
|
||||
* Map state
|
||||
@ -68,28 +161,19 @@ function NetworkedMapAndTokens({ session }) {
|
||||
function handleMapChange(newMap, newMapState) {
|
||||
setCurrentMapState(newMapState);
|
||||
setCurrentMap(newMap);
|
||||
session.send("map", null, "map");
|
||||
|
||||
if (newMap && newMap.type === "file") {
|
||||
const { file, resolutions, ...rest } = newMap;
|
||||
session.socket.emit("map", rest);
|
||||
} else {
|
||||
session.socket.emit("map", newMap);
|
||||
}
|
||||
|
||||
if (!newMap || !newMapState) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.send("map", getMapDataToSend(newMap), "map");
|
||||
const tokensToSend = getMapTokensToSend(newMapState);
|
||||
for (let token of tokensToSend) {
|
||||
session.send("token", token, "token");
|
||||
}
|
||||
}
|
||||
|
||||
function getMapDataToSend(mapData) {
|
||||
// Omit file from map change, receiver will request the file if
|
||||
// they have an outdated version
|
||||
if (mapData.type === "file") {
|
||||
const { file, resolutions, ...rest } = mapData;
|
||||
return rest;
|
||||
} else {
|
||||
return mapData;
|
||||
}
|
||||
loadAssetManifestFromMap(newMap, newMapState);
|
||||
}
|
||||
|
||||
function handleMapStateChange(newMapState) {
|
||||
@ -169,35 +253,12 @@ function NetworkedMapAndTokens({ session }) {
|
||||
* Token state
|
||||
*/
|
||||
|
||||
// Get all tokens from a token state
|
||||
const getMapTokensToSend = useCallback(
|
||||
(state) => {
|
||||
let sentTokens = {};
|
||||
const tokens = [];
|
||||
for (let tokenState of Object.values(state.tokens)) {
|
||||
const token = getToken(tokenState.tokenId);
|
||||
if (
|
||||
token &&
|
||||
token.type === "file" &&
|
||||
!(tokenState.tokenId in sentTokens)
|
||||
) {
|
||||
sentTokens[tokenState.tokenId] = true;
|
||||
// Omit file from token peer will request file if needed
|
||||
const { file, ...rest } = token;
|
||||
tokens.push(rest);
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
},
|
||||
[getToken]
|
||||
);
|
||||
|
||||
async function handleMapTokenStateCreate(tokenState) {
|
||||
// If file type token send the token to the other peers
|
||||
const token = getToken(tokenState.tokenId);
|
||||
if (token && token.type === "file") {
|
||||
const { file, ...rest } = token;
|
||||
session.send("token", rest);
|
||||
const { id, lastModified, owner } = token;
|
||||
addAssetIfNeeded({ type: "token", id, lastModified, owner });
|
||||
}
|
||||
handleMapTokenStateChange({ [tokenState.id]: tokenState });
|
||||
}
|
||||
@ -224,112 +285,76 @@ function NetworkedMapAndTokens({ session }) {
|
||||
|
||||
useEffect(() => {
|
||||
async function handlePeerData({ id, data, reply }) {
|
||||
if (id === "sync") {
|
||||
if (currentMapState) {
|
||||
const tokensToSend = getMapTokensToSend(currentMapState);
|
||||
for (let token of tokensToSend) {
|
||||
reply("token", token, "token");
|
||||
}
|
||||
}
|
||||
if (currentMap) {
|
||||
reply("map", getMapDataToSend(currentMap), "map");
|
||||
}
|
||||
}
|
||||
if (id === "map") {
|
||||
const newMap = data;
|
||||
if (newMap && newMap.type === "file") {
|
||||
const cachedMap = await getMapFromDB(newMap.id);
|
||||
if (cachedMap && cachedMap.lastModified >= newMap.lastModified) {
|
||||
// Update last used for cache invalidation
|
||||
const lastUsed = Date.now();
|
||||
await updateMap(cachedMap.id, { lastUsed });
|
||||
setCurrentMap({ ...cachedMap, lastUsed });
|
||||
} else {
|
||||
// Save map data but remove last modified so if there is an error
|
||||
// during the map request the cache is invalid. Also add last used
|
||||
// for cache invalidation
|
||||
await putMap({ ...newMap, lastModified: 0, lastUsed: Date.now() });
|
||||
reply("mapRequest", newMap.id, "map");
|
||||
}
|
||||
} else {
|
||||
setCurrentMap(newMap);
|
||||
}
|
||||
}
|
||||
if (id === "mapRequest") {
|
||||
const map = await getMapFromDB(data);
|
||||
|
||||
function replyWithPreview(preview) {
|
||||
function replyWithMap(preview, resolution) {
|
||||
let response = {
|
||||
...map,
|
||||
resolutions: undefined,
|
||||
file: undefined,
|
||||
// Remove last modified so if there is an error
|
||||
// during the map request the cache is invalid
|
||||
lastModified: 0,
|
||||
// Add last used for cache invalidation
|
||||
lastUsed: Date.now(),
|
||||
};
|
||||
// Send preview if available
|
||||
if (map.resolutions[preview]) {
|
||||
reply(
|
||||
"mapResponse",
|
||||
{
|
||||
id: map.id,
|
||||
resolutions: { [preview]: map.resolutions[preview] },
|
||||
},
|
||||
"map"
|
||||
);
|
||||
response.resolutions = { [preview]: map.resolutions[preview] };
|
||||
reply("mapResponse", response, "map");
|
||||
}
|
||||
}
|
||||
|
||||
function replyWithFile(resolution) {
|
||||
let file;
|
||||
// If the resolution exists send that
|
||||
// Send full map at the desired resolution if available
|
||||
if (map.resolutions[resolution]) {
|
||||
file = map.resolutions[resolution].file;
|
||||
response.file = map.resolutions[resolution].file;
|
||||
} else if (map.file) {
|
||||
// The resolution might not exist for other users so send the file instead
|
||||
file = map.file;
|
||||
response.file = map.file;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
reply(
|
||||
"mapResponse",
|
||||
{
|
||||
id: map.id,
|
||||
file,
|
||||
// Add last modified back to file to set cache as valid
|
||||
lastModified: map.lastModified,
|
||||
},
|
||||
"map"
|
||||
);
|
||||
// Add last modified back to file to set cache as valid
|
||||
response.lastModified = map.lastModified;
|
||||
reply("mapResponse", response, "map");
|
||||
}
|
||||
|
||||
switch (map.quality) {
|
||||
case "low":
|
||||
replyWithFile("low");
|
||||
replyWithMap(undefined, "low");
|
||||
break;
|
||||
case "medium":
|
||||
replyWithPreview("low");
|
||||
replyWithFile("medium");
|
||||
replyWithMap("low", "medium");
|
||||
break;
|
||||
case "high":
|
||||
replyWithPreview("medium");
|
||||
replyWithFile("high");
|
||||
replyWithMap("medium", "high");
|
||||
break;
|
||||
case "ultra":
|
||||
replyWithPreview("medium");
|
||||
replyWithFile("ultra");
|
||||
replyWithMap("medium", "ultra");
|
||||
break;
|
||||
case "original":
|
||||
if (map.resolutions) {
|
||||
if (map.resolutions.medium) {
|
||||
replyWithPreview("medium");
|
||||
replyWithMap("medium");
|
||||
} else if (map.resolutions.low) {
|
||||
replyWithPreview("low");
|
||||
replyWithMap("low");
|
||||
} else {
|
||||
replyWithMap();
|
||||
}
|
||||
} else {
|
||||
replyWithMap();
|
||||
}
|
||||
replyWithFile();
|
||||
break;
|
||||
default:
|
||||
replyWithFile();
|
||||
replyWithMap();
|
||||
}
|
||||
}
|
||||
|
||||
if (id === "mapResponse") {
|
||||
const { id, ...update } = data;
|
||||
await updateMap(id, update);
|
||||
const updatedMap = await getMapFromDB(data.id);
|
||||
setCurrentMap(updatedMap);
|
||||
const newMap = data;
|
||||
await putMap(newMap);
|
||||
setCurrentMap(newMap);
|
||||
}
|
||||
|
||||
if (id === "token") {
|
||||
const newToken = data;
|
||||
if (newToken && newToken.type === "file") {
|
||||
@ -369,21 +394,25 @@ function NetworkedMapAndTokens({ session }) {
|
||||
assetProgressUpdate({ id, total, count });
|
||||
}
|
||||
|
||||
function handleSocketMapState(mapState) {
|
||||
setCurrentMapState(mapState, false);
|
||||
function handleSocketMap(map) {
|
||||
if (map) {
|
||||
setCurrentMap(map);
|
||||
} else {
|
||||
setCurrentMap(null);
|
||||
}
|
||||
}
|
||||
|
||||
session.on("data", handlePeerData);
|
||||
session.on("dataProgress", handlePeerDataProgress);
|
||||
if (session.socket) {
|
||||
session.socket.on("map_state", handleSocketMapState);
|
||||
session.socket.on("map", handleSocketMap);
|
||||
}
|
||||
|
||||
return () => {
|
||||
session.off("data", handlePeerData);
|
||||
session.off("dataProgress", handlePeerDataProgress);
|
||||
if (session.socket) {
|
||||
session.socket.off("map_state", handleSocketMapState);
|
||||
session.socket.off("map", handleSocketMap);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -111,6 +111,25 @@ class Session extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to a single peer
|
||||
*
|
||||
* @param {string} sessionId - the socket id of the player to send to
|
||||
* @param {string} eventId - the id of the event to send
|
||||
* @param {object} data
|
||||
* @param {string} channel
|
||||
*/
|
||||
sendTo(sessionId, eventId, data, channel) {
|
||||
if (!(sessionId in this.peers)) {
|
||||
this._addPeer(sessionId, true);
|
||||
this.peers[sessionId].connection.once("connect", () => {
|
||||
this.peers[sessionId].connection.send({ id: eventId, data }, channel);
|
||||
});
|
||||
} else {
|
||||
this.peers[sessionId].connection.send({ id: eventId, data }, channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a party
|
||||
*
|
||||
@ -132,7 +151,7 @@ class Session extends EventEmitter {
|
||||
this.socket.emit("join_game", gameId, password);
|
||||
}
|
||||
|
||||
_addPeer(id, initiator, sync) {
|
||||
_addPeer(id, initiator) {
|
||||
try {
|
||||
const connection = new Connection({
|
||||
initiator,
|
||||
@ -143,7 +162,7 @@ class Session extends EventEmitter {
|
||||
connection.createDataChannel("map", { iceServers: this._iceServers });
|
||||
connection.createDataChannel("token", { iceServers: this._iceServers });
|
||||
}
|
||||
const peer = { id, connection, initiator, sync };
|
||||
const peer = { id, connection, initiator };
|
||||
|
||||
function sendPeer(id, data, channel) {
|
||||
peer.connection.send({ id, data }, channel);
|
||||
@ -155,9 +174,6 @@ class Session extends EventEmitter {
|
||||
|
||||
function handleConnect() {
|
||||
this.emit("connect", { peer, reply: sendPeer });
|
||||
if (peer.sync) {
|
||||
peer.connection.send({ id: "sync" });
|
||||
}
|
||||
}
|
||||
|
||||
function handleDataComplete(data) {
|
||||
@ -220,22 +236,17 @@ class Session extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
_handleJoinedGame(otherIds) {
|
||||
for (let i = 0; i < otherIds.length; i++) {
|
||||
const id = otherIds[i];
|
||||
// Send a sync request to the first member of the party
|
||||
const sync = i === 0;
|
||||
this._addPeer(id, true, sync);
|
||||
}
|
||||
_handleJoinedGame() {
|
||||
this.emit("authenticationSuccess");
|
||||
this.emit("connected");
|
||||
}
|
||||
|
||||
_handlePlayerJoined(id) {
|
||||
this._addPeer(id, false, false);
|
||||
this.emit("playerJoined", id);
|
||||
}
|
||||
|
||||
_handlePlayerLeft(id) {
|
||||
this.emit("playerLeft", id);
|
||||
if (id in this.peers) {
|
||||
this.peers[id].connection.destroy();
|
||||
delete this.peers[id];
|
||||
@ -244,9 +255,10 @@ class Session extends EventEmitter {
|
||||
|
||||
_handleSignal(data) {
|
||||
const { from, signal } = data;
|
||||
if (from in this.peers) {
|
||||
this.peers[from].connection.signal(signal);
|
||||
if (!(from in this.peers)) {
|
||||
this._addPeer(from, false);
|
||||
}
|
||||
this.peers[from].connection.signal(signal);
|
||||
}
|
||||
|
||||
_handleAuthError() {
|
||||
|
Loading…
Reference in New Issue
Block a user