@lobstar/preact
Version:
A collection of Preact hooks to use Lobstar library for network and lobby management for multiplayer web games
174 lines (173 loc) • 6.47 kB
JavaScript
import { useRef, useEffect, useCallback, useState } from "preact/hooks";
import { GameSessionManager, SESSION_STATES, } from "@lobstar/core";
const createSessionId = () => {
return Math.random().toString(36).substring(2, 9);
};
/**
* Core hook that initializes and provides access to the GameSessionManager
*/
export function useGameSession(options) {
const sessionRef = useRef(null);
const [sessionId, setSessionId] = useState(createSessionId());
const debug = options?.debug || false;
// Only track session state here, leave player tracking to usePlayers
const [sessionState, setSessionState] = useState(SESSION_STATES.DISCONNECTED);
const log = useCallback((message) => {
if (debug) {
console.log(`[useGameSession:${sessionId}] ${message}`);
}
}, [debug, sessionId]);
// Function to reset the session completely
const resetSession = useCallback(() => {
log("Resetting session - forcing new session manager on next use");
if (sessionRef.current) {
log("Calling leave() on current session before reset");
sessionRef.current.leave();
sessionRef.current = null;
}
// Reset session state
setSessionState(SESSION_STATES.DISCONNECTED);
// Create a new session instance
setSessionId(createSessionId());
}, [log]);
useEffect(() => {
// Initialize the session manager
log("Initializing new GameSessionManager");
sessionRef.current = new GameSessionManager(options);
// Setup event listeners for debugging
const unsubScribeState = sessionRef.current.on("stateChange", ({ state, previousState }) => {
log(`State changed: ${previousState} -> ${state}`);
setSessionState(state);
});
const unsubPlayersUpdate = sessionRef.current.on("playersUpdate", ({ players: updatedPlayers }) => {
log(`Players updated: ${Object.keys(updatedPlayers).length} players`);
log(`Players: ${JSON.stringify(Object.values(updatedPlayers).map((p) => ({ id: p.id, name: p.name, ready: p.isReady })))}`);
});
const unsubKicked = sessionRef.current.on("kicked", () => {
log("Player was kicked from session - forcing session reset");
resetSession();
});
// Clean up on unmount
return () => {
log("Component unmounting, cleaning up session");
// Unsubscribe from all events
unsubScribeState();
unsubPlayersUpdate();
unsubKicked();
if (sessionRef.current) {
log("Calling leave() on session manager");
sessionRef.current.leave();
sessionRef.current = null;
}
};
}, [log, sessionId, resetSession]);
const host = useCallback(async (playerName, lobbyId) => {
if (!sessionRef.current) {
log("Cannot host: sessionRef is null");
return "";
}
log(`Hosting game as "${playerName}" ${lobbyId ? `with lobby ID ${lobbyId}` : "with generated ID"}`);
try {
return await sessionRef.current.host(playerName, lobbyId);
}
catch (error) {
resetSession();
throw error;
}
}, [log, resetSession]);
const join = useCallback(async (lobbyId, playerName) => {
if (!sessionRef.current) {
log("Cannot join: sessionRef is null");
return;
}
log(`Joining lobby ${lobbyId} as "${playerName}"`);
try {
await sessionRef.current.join(lobbyId, playerName);
}
catch (error) {
resetSession();
throw error;
}
}, [log, resetSession]);
const leave = useCallback(() => {
if (!sessionRef.current) {
log("Cannot leave: sessionRef is null");
return;
}
log("Leaving session");
sessionRef.current.leave();
// Force recreation of the session manager on next use
resetSession();
}, [log, resetSession]);
const setReady = useCallback((isReady) => {
if (!sessionRef.current) {
log(`Cannot set ready to ${isReady}: sessionRef is null`);
return;
}
log(`Setting ready state to ${isReady}`);
sessionRef.current.setReady(isReady);
}, [log]);
const startGame = useCallback(() => {
if (!sessionRef.current) {
log("Cannot start game: sessionRef is null");
return;
}
log("Starting game");
sessionRef.current.startGame();
}, [log]);
const endGame = useCallback(() => {
if (!sessionRef.current) {
log("Cannot end game: sessionRef is null");
return;
}
log("Ending game");
sessionRef.current.endGame();
}, [log]);
const kickPlayer = useCallback((playerId) => {
if (!sessionRef.current) {
log(`Cannot kick player ${playerId}: sessionRef is null`);
return;
}
log(`Kicking player ${playerId}`);
sessionRef.current.kickPlayer(playerId);
}, [log]);
const sendMessage = useCallback((peerId, data) => {
if (!sessionRef.current) {
log(`Cannot send message to ${peerId}: sessionRef is null`);
return;
}
log(`Sending message to ${peerId}`);
sessionRef.current.sendMessage(peerId, data);
}, [log]);
const sendMessageToHost = useCallback((data) => {
if (!sessionRef.current) {
log("Cannot send message to host: sessionRef is null");
return;
}
log("Sending message to host");
sessionRef.current.sendMessageToHost(data);
}, [log]);
const broadcastMessage = useCallback((data, excludeSelf) => {
if (!sessionRef.current) {
log("Cannot broadcast message: sessionRef is null");
return;
}
log(`Broadcasting message ${excludeSelf ? "(excluding self)" : ""}`);
sessionRef.current.broadcastMessage(data, excludeSelf);
}, [log]);
return {
session: sessionRef.current,
host,
join,
leave,
setReady,
startGame,
endGame,
kickPlayer,
sendMessage,
sendMessageToHost,
broadcastMessage,
// Only expose session state here
sessionState,
};
}