diff --git a/README.md b/README.md index ad91a9b0..56f63fc0 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Connection made → Direct peer-to-peer, Nostr disconnected If Nostr becomes unavailable, players can fall back to manual connection code exchange. The game continues to function regardless of external infrastructure. -**Implementation:** `src/engine/network/p2p/NostrMatchmaking.ts`, `ConnectionCode.ts` +**Implementation:** `src/hooks/useMultiplayer.ts`, `src/store/multiplayerStore.ts` --- diff --git a/src/components/game/WebGPUGameCanvas.tsx b/src/components/game/WebGPUGameCanvas.tsx index d7b8c878..5ea1aec1 100644 --- a/src/components/game/WebGPUGameCanvas.tsx +++ b/src/components/game/WebGPUGameCanvas.tsx @@ -147,7 +147,6 @@ export function WebGPUGameCanvas() { eventBusRef, isGameInitialized: isWorkerInitialized, placementPreviewRef: refs.placementPreview, - wallPlacementPreviewRef: refs.wallPlacementPreview, overlayManagerRef: refs.overlayManager, lastControlGroupTap, }); @@ -160,7 +159,6 @@ export function WebGPUGameCanvas() { cameraRef: refs.camera, environmentRef: refs.environment, lightPoolRef: refs.lightPool, - fogOfWarRef: refs.fogOfWar, containerRef, map: CURRENT_MAP, }); diff --git a/src/components/game/hooks/useGameInput.ts b/src/components/game/hooks/useGameInput.ts index 275056b5..0b040e05 100644 --- a/src/components/game/hooks/useGameInput.ts +++ b/src/components/game/hooks/useGameInput.ts @@ -18,7 +18,6 @@ import type { IWorldProvider } from '@/engine/ecs/IWorldProvider'; import type { EventBus } from '@/engine/core/EventBus'; import { RTSCamera } from '@/rendering/Camera'; import { BuildingPlacementPreview } from '@/rendering/BuildingPlacementPreview'; -import { WallPlacementPreview } from '@/rendering/WallPlacementPreview'; import { TSLGameOverlayManager } from '@/rendering/tsl'; import { getLocalPlayerId } from '@/store/gameSetupStore'; import { @@ -44,8 +43,6 @@ export interface UseGameInputProps { /** Signal that game is initialized - triggers InputManager dependency update */ isGameInitialized?: boolean; placementPreviewRef: MutableRefObject; - /** @deprecated No longer used - wall placement handled by BuildingInputHandler */ - wallPlacementPreviewRef?: MutableRefObject; overlayManagerRef: MutableRefObject; lastControlGroupTap: MutableRefObject<{ group: number; time: number } | null>; } diff --git a/src/components/game/hooks/usePostProcessing.ts b/src/components/game/hooks/usePostProcessing.ts index 1cd663bb..ba721b56 100644 --- a/src/components/game/hooks/usePostProcessing.ts +++ b/src/components/game/hooks/usePostProcessing.ts @@ -8,7 +8,7 @@ import type { MutableRefObject, RefObject } from 'react'; import { useEffect, useRef } from 'react'; import * as THREE from 'three'; -import { RenderContext, RenderPipeline, TSLFogOfWar } from '@/rendering/tsl'; +import { RenderContext, RenderPipeline } from '@/rendering/tsl'; import { EnvironmentManager } from '@/rendering/EnvironmentManager'; import { LightPool } from '@/rendering/LightPool'; import { RTSCamera } from '@/rendering/Camera'; @@ -24,8 +24,6 @@ export interface UsePostProcessingProps { cameraRef: MutableRefObject; environmentRef: MutableRefObject; lightPoolRef: MutableRefObject; - /** @deprecated Fog of war is now handled via RenderPipeline */ - fogOfWarRef?: MutableRefObject; containerRef: RefObject; map: MapData; } @@ -37,7 +35,6 @@ export function usePostProcessing({ cameraRef, environmentRef, lightPoolRef, - fogOfWarRef: _fogOfWarRef, containerRef, map, }: UsePostProcessingProps): void { diff --git a/src/engine/core/Game.ts b/src/engine/core/Game.ts index 28aea3f4..f531c17e 100644 --- a/src/engine/core/Game.ts +++ b/src/engine/core/Game.ts @@ -24,7 +24,6 @@ import { getDesyncState, useMultiplayerStore, getAdaptiveCommandDelay, - getSlotIdForPeer, getAllRemoteSlotIds, setOnReconnectedCallback, } from '@/store/multiplayerStore'; @@ -180,22 +179,17 @@ export class Game extends GameCore { // Commands use slot IDs (e.g., "player1", "player2"), not peer IDs const validRemoteSlotIds = getAllRemoteSlotIds(); if (!validRemoteSlotIds.includes(command.playerId)) { - // Also check legacy single-peer mode - const legacyPeerId = useMultiplayerStore.getState().remotePeerId; - const legacySlotId = legacyPeerId ? getSlotIdForPeer(legacyPeerId) : null; - if (command.playerId !== legacySlotId) { - console.error( - `[Game] SECURITY: Rejected command with invalid playerId. ` + - `Valid slot IDs: [${validRemoteSlotIds.join(', ')}], Got: ${command.playerId}` - ); - this.eventBus.emit('security:spoofedPlayerId', { - expectedPlayerId: validRemoteSlotIds.join(', '), - spoofedPlayerId: command.playerId, - commandType: command.type, - tick: command.tick, - }); - return; - } + console.error( + `[Game] SECURITY: Rejected command with invalid playerId. ` + + `Valid slot IDs: [${validRemoteSlotIds.join(', ')}], Got: ${command.playerId}` + ); + this.eventBus.emit('security:spoofedPlayerId', { + expectedPlayerId: validRemoteSlotIds.join(', '), + spoofedPlayerId: command.playerId, + commandType: command.type, + tick: command.tick, + }); + return; } // SECURITY: Validate command tick is within acceptable range @@ -611,17 +605,6 @@ export class Game extends GameCore { } } - // Fallback for legacy single-peer mode: try to get slot ID from peer ID mapping - if (remoteSlotIds.length === 0) { - const remotePeerId = useMultiplayerStore.getState().remotePeerId; - if (remotePeerId) { - const slotId = getSlotIdForPeer(remotePeerId); - if (slotId && !players.includes(slotId)) { - players.push(slotId); - } - } - } - return players; } diff --git a/src/hooks/useLobbySync.ts b/src/hooks/useLobbySync.ts index e826a0e0..90d6593e 100644 --- a/src/hooks/useLobbySync.ts +++ b/src/hooks/useLobbySync.ts @@ -114,7 +114,6 @@ export function useLobbySync({ playerName, isPublicLobby }: UseLobbyOptions): Lo setMultiplayer, setConnected, setHost, - setDataChannel, } = useMultiplayerStore(); // Derived state @@ -124,27 +123,24 @@ export function useLobbySync({ playerName, isPublicLobby }: UseLobbyOptions): Lo const isGuestMode = lobbyStatus === 'connected' && !isHost; // When connected as guest, set up multiplayer store + // Note: Data channel is set up via addPeer() in useMultiplayer.ts useEffect(() => { if (lobbyStatus === 'connected' && hostConnection) { setMultiplayer(true); setConnected(true); setHost(false); - setDataChannel(hostConnection); } - }, [lobbyStatus, hostConnection, setMultiplayer, setConnected, setHost, setDataChannel]); + }, [lobbyStatus, hostConnection, setMultiplayer, setConnected, setHost]); // When hosting with connected guests, set up multiplayer + // Note: Data channels are set up via addPeer() in useMultiplayer.ts useEffect(() => { if (isHost && guests.some(g => g.dataChannel)) { setMultiplayer(true); setConnected(true); setHost(true); - const firstConnectedGuest = guests.find(g => g.dataChannel); - if (firstConnectedGuest?.dataChannel) { - setDataChannel(firstConnectedGuest.dataChannel); - } } - }, [isHost, guests, setMultiplayer, setConnected, setHost, setDataChannel]); + }, [isHost, guests, setMultiplayer, setConnected, setHost]); // Send lobby state to guests whenever it changes (host only) useEffect(() => {