Spaces:
Sleeping
Sleeping
File size: 3,621 Bytes
055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 ea5f18a 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 ea5f18a 7dd98ed 055e3d8 7fb6a22 93f151d 28bfc7d 93f151d 28bfc7d 93f151d 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 7fb6a22 055e3d8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | /**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useRef, useCallback } from "react";
import { MatchMode } from "../types";
import { getMultiplayerSessionId } from "./useProgression";
import { Client, Room } from "@colyseus/sdk";
import { ChatMessage, MultiplayerMatchContext } from "./multiplayer/types";
import { setupRoomListeners } from "./multiplayer/listeners";
export function useMultiplayer() {
const [chatMessages, setChatMessages] = useState<ChatMessage[]>([]);
const roomRef = useRef<Room | null>(null);
const myPilotIdRef = useRef<string>("");
const disconnectMultiplayer = useCallback(() => {
if (roomRef.current) {
try {
roomRef.current.leave();
} catch (err) {
console.warn("Error leaving room:", err);
}
roomRef.current = null;
}
}, []);
const connectMultiplayer = useCallback(
(
engine: MultiplayerMatchContext,
_renderer3D: any,
mapId: string,
mode: MatchMode,
nickname: string,
skin: string,
onLocalPlayerHit: (tgtId: string, isGround: boolean) => void,
onMatchRejected: (reason: string) => void
) => {
disconnectMultiplayer();
const sessionId = getMultiplayerSessionId();
engine.isMultiplayer = true;
engine.isHost = false;
// Discard locally-spawned bots — the server is authoritative and will
// deliver the real roster via Colyseus state sync (onAdd).
engine.pilots = engine.pilots.filter(p => p.id === "player");
const protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
const client = new Client(`${protocol}${window.location.host}`);
console.log(`[Multiplayer] Connecting to Colyseus at ${window.location.host}...`);
client.joinOrCreate("air_combat", {
token: sessionId,
nickname: nickname || "Maverick",
aircraftId: engine.pilots.find(p => p.id === "player")?.specs.id || "falcon-mk2",
skin: skin || "default",
mapId,
mode
})
.then((room) => {
roomRef.current = room;
myPilotIdRef.current = room.sessionId;
console.log(`[Multiplayer] Joined room: ${room.roomId}`);
// Register all schema sync & message listeners from separate module
setupRoomListeners(room, engine, setChatMessages, onLocalPlayerHit);
})
.catch((err) => {
const reason: string = err?.message || String(err);
console.error("[Multiplayer] Join failed:", reason, err);
fetch("/api/client-error", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
context: "connectMultiplayer.catch",
message: reason,
stack: err?.stack || ""
})
}).catch(() => {});
onMatchRejected(reason);
});
// Bind local player shoots callback to nothing on server auth
engine.onProjectileSpawn = () => {};
engine.onGroundTargetDamage = () => {};
engine.onLocalPlayerKill = () => {};
engine.onPlayerDamage = () => {};
},
[disconnectMultiplayer]
);
const sendChat = useCallback((text: string, nickname: string) => {
if (roomRef.current) {
roomRef.current.send("chat", text);
} else {
setChatMessages((prev) => [
...prev.slice(-49),
{ sender: nickname || "Cadet", text, ts: Date.now() }
]);
}
}, []);
return {
chatMessages,
setChatMessages,
connectMultiplayer,
disconnectMultiplayer,
sendChat,
roomRef,
myPilotIdRef
};
}
|