From 7b77dba9f772f395b26718afc818f87ee7c70ea8 Mon Sep 17 00:00:00 2001 From: Leo Date: Sat, 10 Oct 2026 02:01:03 +0200 Subject: [PATCH] fix(network): move connect in init, openning all for chans --- docs/docs/network/network-client-api.mdx | 27 +--- docs/docs/network/network-client.mdx | 3 - example/pong-network/apps/client/src/main.ts | 5 +- modules/network/README.md | 11 +- .../src/client/client.network.library.ts | 126 +++++++----------- modules/network/src/client/index.ts | 2 +- .../src/client/network-client-context.type.ts | 7 +- modules/network/src/shared/channels.ts | 23 +--- .../client/client.network.library.spec.ts | 90 ++++--------- 9 files changed, 91 insertions(+), 203 deletions(-) diff --git a/docs/docs/network/network-client-api.mdx b/docs/docs/network/network-client-api.mdx index b8b9d7d..eaff3a7 100644 --- a/docs/docs/network/network-client-api.mdx +++ b/docs/docs/network/network-client-api.mdx @@ -14,35 +14,20 @@ description: "API reference for the client networking package" Reliable channels need `SERVER_TCP_PORT` and unreliable ones `SERVER_UDP_PORT`. Using a channel whose port is not configured throws. +The client connects during `__init`, so its channels are open once the game runs. +TCP connects first, then UDP with the TCP session token, so both share one client +id. `__init` rejects when a transport fails, or when its channels are not open +within 10 seconds. + ```ts import { Channel } from "@nanoforge-dev/network/client"; -await ctx.network.connect({ channels: [Channel.ReliableOrdered, Channel.UnreliableOrdered] }); - ctx.network.sendData({ type: "join" }); // Channel.ReliableOrdered ctx.network.sendData({ x: 1, y: 2 }, { channel: Channel.UnreliableOrdered }); const messages = ctx.network.getReceivedPackets().map((packet) => packet.json()); // every channel ``` -## connect(options?) - -Connect to the server and resolve once the channels are open. Only the -transports the channels need are opened: TCP for reliable channels, UDP for -unreliable ones. TCP connects first, then UDP with the TCP session token, so both -share one client id. Calling it again for channels that are already open resolves -right away. - -Option `channels`: `Channel[]`, the channels to open and wait for. Every -configured channel by default. - -Option `timeout`: `number`, milliseconds before giving up. `10000` by default. - -Rejects when a channel's port is not configured, when a transport fails to -connect, or when the channels are not open before the timeout. - -Returns: `Promise` - ## sendData(data, options?) Send a payload to the server. @@ -140,7 +125,7 @@ Returns: `boolean` ## clientId Client id assigned by the server, shared by TCP and UDP. `undefined` until -`connect` has been welcomed by the server. It is only kept in memory: a page +the server has welcomed the client. It is only kept in memory: a page reload gets a new id. Type: `string | undefined` diff --git a/docs/docs/network/network-client.mdx b/docs/docs/network/network-client.mdx index 4899152..38926f8 100644 --- a/docs/docs/network/network-client.mdx +++ b/docs/docs/network/network-client.mdx @@ -23,9 +23,6 @@ four [channels](packet-framing), picked with the `channel` option: ```javascript import { Channel } from "@nanoforge-dev/network/client"; -// Open the channels the game uses and wait until they are ready -await network.connect({ channels: [Channel.ReliableOrdered, Channel.UnreliableOrdered] }); - // Send a value as JSON, on Channel.ReliableOrdered network.sendData({ hello: "world" }); diff --git a/example/pong-network/apps/client/src/main.ts b/example/pong-network/apps/client/src/main.ts index 2b4ef18..3f71c25 100644 --- a/example/pong-network/apps/client/src/main.ts +++ b/example/pong-network/apps/client/src/main.ts @@ -3,7 +3,7 @@ import { NanoforgeFactory } from "@nanoforge-dev/core"; import { EcsLibrary } from "@nanoforge-dev/ecs/client"; import { Circle, Graphics2DLibrary, Layer, Rect } from "@nanoforge-dev/graphics-2d"; import { InputEnum, InputLibrary } from "@nanoforge-dev/input"; -import { Channel, NetworkClientLibrary } from "@nanoforge-dev/network/client"; +import { NetworkClientLibrary } from "@nanoforge-dev/network/client"; import { AssignId, Play } from "../../../shared/protocol"; import { @@ -95,9 +95,6 @@ export const main = async (options: ClientRunOptions): Promise => { registry.addSystem(controlPlayer); registry.addSystem(draw); - await network.connect({ - channels: [Channel.ReliableOrdered, Channel.UnreliableOrdered, Channel.UnreliableUnordered], - }); network.packets.send(Play); await app.run(); diff --git a/modules/network/README.md b/modules/network/README.md index 6c9a46e..358bb16 100644 --- a/modules/network/README.md +++ b/modules/network/README.md @@ -164,9 +164,6 @@ import { Channel, NetworkClientLibrary } from "@nanoforge-dev/network/client"; app.use(new NetworkClientLibrary()); -// Once, at game start: open the channels the game uses and wait for them. -await ctx.network.connect({ channels: [Channel.ReliableOrdered, Channel.UnreliableOrdered] }); - // Every frame: ctx.network.sendData({ type: "join" }); // Channel.ReliableOrdered ctx.network.sendData({ x: 1, y: 2 }, { channel: Channel.UnreliableOrdered }); @@ -189,9 +186,9 @@ ctx.network.sendToAll(snapshot, { channel: Channel.UnreliableOrdered }); ctx.network.sendToClient(clientId, { type: "event" }); // Channel.ReliableOrdered ``` -`connect` only opens the transports its channels need, and rejects when a -transport fails or the channels are not open within `timeout` milliseconds -(10 seconds by default). The server starts listening during `__init`. +The client connects during `__init` and its channels are open before the game +starts: `__init` rejects when a transport fails or the channels are not open +within 10 seconds. The server starts listening during `__init`. ### Typed packets @@ -239,7 +236,7 @@ ctx.network.clients.onDisconnect((info) => console.log(`${info.id} left`)); const info = ctx.network.getClientInfo(clientId); ``` -On the client, `ctx.network.clientId` holds the id once `connect` has been welcomed. A page reload +On the client, `ctx.network.clientId` holds the id once the server has welcomed the client. A page reload starts a new session with a new id. Using a channel whose port wasn't configured on that side throws. diff --git a/modules/network/src/client/client.network.library.ts b/modules/network/src/client/client.network.library.ts index ed9e33c..71aea66 100644 --- a/modules/network/src/client/client.network.library.ts +++ b/modules/network/src/client/client.network.library.ts @@ -7,7 +7,6 @@ import { CONNECT_TIMEOUT_MS, type Channel, type ChannelOptions, - type ConnectOptions, DEFAULT_CHANNEL, isReliableChannel, } from "../shared/channels"; @@ -34,7 +33,7 @@ import type { import { TCPClient } from "./tcp.client.network"; import { UDPClient } from "./udp.client.network"; -/** Delay between two checks of the channels while `connect` waits. */ +/** Delay between two checks of the channels while `__init` waits for them. */ const CONNECT_POLL_MS = 50; /** @@ -42,10 +41,10 @@ const CONNECT_POLL_MS = 50; * * @remarks * Reads network configuration from the environment via `ClientConfigNetwork` - * during `__init`. Call `connect` to open the connections: TCP (WebSocket) - * carries the reliable channels and UDP (WebRTC data channels) the unreliable - * ones. TCP connects first; once welcomed, UDP presents the session token so - * both transports share one client id. + * and opens the connections during `__init`, so the channels are open before + * the game runs: TCP (WebSocket) carries the reliable channels and UDP (WebRTC + * data channels) the unreliable ones. TCP connects first; once welcomed, UDP + * presents the session token so both transports share one client id. * * Configuration (via environment variables): * - `SERVER_ADDRESS` — hostname or IP of the server (required). @@ -56,7 +55,6 @@ const CONNECT_POLL_MS = 50; * * @example * ```ts - * await ctx.network.connect({ channels: [Channel.ReliableOrdered, Channel.UnreliableOrdered] }); * ctx.network.sendData({ type: "join" }); // Channel.ReliableOrdered * ctx.network.sendData({ x: 1, y: 2 }, { channel: Channel.UnreliableOrdered }); * const messages = ctx.network.getReceivedPackets().map((packet) => packet.json()); // every channel @@ -68,8 +66,6 @@ export class NetworkClientLibrary extends Library implements EditorAwareLibrary private _tcp?: TCPClient; private _udp?: UDPClient; - /** Pending or settled connection of each transport, so it is only opened once. */ - private readonly _connecting = new Map>(); private readonly _session: ClientSession = {}; private readonly _dispatcher = new PacketDispatcher(); /** Typed packets drained from the transports, dispatched by the next `__run`. */ @@ -125,60 +121,8 @@ export class NetworkClientLibrary extends Library implements EditorAwareLibrary this._session, ); } - } - /** - * Connect to the server and wait until `channels` are open. - * - * @remarks - * Only opens the transports the channels need: TCP for reliable channels, - * UDP for unreliable ones. Calling it again for already open channels - * resolves right away. - * - * @param options - `channels` to wait for (every configured channel by - * default) and a `timeout` in milliseconds (10 seconds by default). - * @throws When a channel's port was not configured, a transport fails to - * connect, or the channels are not open before the timeout. - */ - public async connect({ - channels = this.configuredChannels(), - timeout = CONNECT_TIMEOUT_MS, - }: ConnectOptions = {}): Promise { - if (!this._tcp && !this._udp) { - throw new Error("NetworkClientLibrary: connect() was called before __init"); - } - const deadline = Date.now() + timeout; - const reliable = channels.find(isReliableChannel); - const unreliable = channels.find((channel) => !isReliableChannel(channel)); - - // TCP first, so UDP joins its session with the token from the TCP welcome. - const transports: [string, TCPClient | UDPClient][] = []; - if (reliable !== undefined) transports.push(["TCP", this.requireTcp(reliable)]); - if (unreliable !== undefined) transports.push(["UDP", this.requireUdp(unreliable)]); - - const failures: unknown[] = []; - for (const [name, transport] of transports) { - try { - await this.connectTransport(name, transport); - } catch (error) { - failures.push(error); - } - } - if (failures.length > 0) { - throw new Error("NetworkClientLibrary: could not connect to the server", { - cause: failures.length === 1 ? failures[0] : failures, - }); - } - - while (!channels.every((channel) => this.isConnected({ channel }))) { - if (Date.now() >= deadline) { - const pending = channels.filter((channel) => !this.isConnected({ channel })); - throw new Error( - `NetworkClientLibrary: channels ${pending.join(", ")} not open after ${timeout}ms`, - ); - } - await new Promise((resolve) => setTimeout(resolve, CONNECT_POLL_MS)); - } + await this.openChannels(); } /** @@ -259,7 +203,6 @@ export class NetworkClientLibrary extends Library implements EditorAwareLibrary // eslint-disable-next-line @typescript-eslint/no-this-alias const library = this; return { - connect: (options) => library.connect(options), sendData: (data, options) => library.sendData(data, options), getReceivedPackets: (options) => library.getReceivedPackets(options), isConnected: (options) => library.isConnected(options), @@ -272,6 +215,50 @@ export class NetworkClientLibrary extends Library implements EditorAwareLibrary }; } + /** + * Connect every configured transport and wait until its channels are open. + * + * @remarks + * Channels cannot be opened once the game runs, and connection errors are + * best caught at start, so this runs during `__init`. + * + * @throws When a transport fails to connect, or the channels are not open + * within `CONNECT_TIMEOUT_MS`. + */ + private async openChannels(): Promise { + const deadline = Date.now() + CONNECT_TIMEOUT_MS; + const channels = this.configuredChannels(); + + // TCP first, so UDP joins its session with the token from the TCP welcome. + const transports: [string, TCPClient | UDPClient][] = []; + if (this._tcp) transports.push(["TCP", this._tcp]); + if (this._udp) transports.push(["UDP", this._udp]); + + const failures: unknown[] = []; + for (const [name, transport] of transports) { + try { + await transport.connect(); + } catch (error) { + failures.push(new Error(`${name} connection failed`, { cause: error })); + } + } + if (failures.length > 0) { + throw new Error("NetworkClientLibrary: could not connect to the server", { + cause: failures.length === 1 ? failures[0] : failures, + }); + } + + while (!channels.every((channel) => this.isConnected({ channel }))) { + if (Date.now() >= deadline) { + const pending = channels.filter((channel) => !this.isConnected({ channel })); + throw new Error( + `NetworkClientLibrary: channels ${pending.join(", ")} not open after ${CONNECT_TIMEOUT_MS}ms`, + ); + } + await new Promise((resolve) => setTimeout(resolve, CONNECT_POLL_MS)); + } + } + private configuredChannels(): Channel[] { return CHANNELS.filter((channel) => isReliableChannel(channel) ? this._tcp !== undefined : this._udp !== undefined, @@ -301,17 +288,4 @@ export class NetworkClientLibrary extends Library implements EditorAwareLibrary if (!this._udp) throw new Error(`Channel ${channel} needs SERVER_UDP_PORT to be set`); return this._udp; } - - private connectTransport(name: string, transport: TCPClient | UDPClient): Promise { - let connecting = this._connecting.get(transport); - if (!connecting) { - connecting = transport.connect().catch((error: unknown) => { - // Forget the failure so a later `connect` can retry. - this._connecting.delete(transport); - throw new Error(`${name} connection failed`, { cause: error }); - }); - this._connecting.set(transport, connecting); - } - return connecting; - } } diff --git a/modules/network/src/client/index.ts b/modules/network/src/client/index.ts index e4f7f13..9ad6f53 100644 --- a/modules/network/src/client/index.ts +++ b/modules/network/src/client/index.ts @@ -1,7 +1,7 @@ import "./context-augmentation"; export type { NetworkConfig } from "../shared/config.network"; -export { Channel, type ChannelOptions, type ConnectOptions } from "../shared/channels"; +export { Channel, type ChannelOptions } from "../shared/channels"; export { NetworkData, type NetworkPayload } from "../shared/network-data"; export { type PacketArgs, diff --git a/modules/network/src/client/network-client-context.type.ts b/modules/network/src/client/network-client-context.type.ts index 9d210d3..68b660e 100644 --- a/modules/network/src/client/network-client-context.type.ts +++ b/modules/network/src/client/network-client-context.type.ts @@ -1,4 +1,4 @@ -import type { Channel, ChannelOptions, ConnectOptions } from "../shared/channels"; +import type { Channel, ChannelOptions } from "../shared/channels"; import type { NetworkData, NetworkPayload } from "../shared/network-data"; import type { PacketArgs, PacketDefinition, PacketListener } from "../shared/packet"; @@ -59,11 +59,6 @@ export interface NetworkClientContextApi { readonly packets: ClientPacketsApi; /** Client id assigned by the server, shared by TCP and UDP. `undefined` until welcomed. */ readonly clientId: string | undefined; - /** - * Connect to the server and resolve once `channels` (every configured - * channel by default) are open. Rejects on failure or after `timeout` ms. - */ - connect(options?: ConnectOptions): Promise; /** * Send bytes, a string or a JSON value to the server, on * `Channel.ReliableOrdered` unless another `channel` is given. diff --git a/modules/network/src/shared/channels.ts b/modules/network/src/shared/channels.ts index fe8dffd..df91d08 100644 --- a/modules/network/src/shared/channels.ts +++ b/modules/network/src/shared/channels.ts @@ -52,30 +52,9 @@ export type ChannelOptions = { channel?: Channel; }; -/** How long `connect` waits for its channels by default. */ +/** How long the client waits for its channels to open during `__init`. */ export const CONNECT_TIMEOUT_MS = 10_000; -/** - * Options of the client's `connect`. - * - * @example - * ```ts - * await ctx.network.connect({ - * channels: [Channel.ReliableOrdered, Channel.UnreliableUnordered], - * }); - * ``` - */ -export type ConnectOptions = { - /** Channels to open and wait for, every configured channel by default. */ - channels?: Channel[]; - /** - * Give up after this many milliseconds. - * - * @default 10000 - */ - timeout?: number; -}; - /** * Tell whether a channel is carried by the TCP WebSocket. * diff --git a/modules/network/test/client/client.network.library.spec.ts b/modules/network/test/client/client.network.library.spec.ts index a7f26d1..5aa9b1d 100644 --- a/modules/network/test/client/client.network.library.spec.ts +++ b/modules/network/test/client/client.network.library.spec.ts @@ -83,6 +83,7 @@ describe("NetworkClientLibrary", () => { }); afterEach(() => { + vi.useRealTimers(); vi.unstubAllGlobals(); vi.restoreAllMocks(); }); @@ -108,7 +109,6 @@ describe("NetworkClientLibrary", () => { }); const lib = new NetworkClientLibrary(); await lib.__init(ctx); - await lib.connect(); expect(() => lib.sendData(new Uint8Array([1]))).not.toThrow(); expect(() => lib.sendData(new Uint8Array([1]), { channel: Channel.UnreliableOrdered }), @@ -123,7 +123,6 @@ describe("NetworkClientLibrary", () => { }); const lib = new NetworkClientLibrary(); await lib.__init(ctx); - await lib.connect(); expect(RTCPeerConnection).toHaveBeenCalledWith({ iceServers: [{ urls: "stun:stun.example.com:3478" }], }); @@ -136,7 +135,6 @@ describe("NetworkClientLibrary", () => { }); const lib = new NetworkClientLibrary(); await lib.__init(ctx); - await lib.connect(); expect(() => lib.sendData(new Uint8Array([1]), { channel: Channel.UnreliableUnordered }), ).not.toThrow(); @@ -153,7 +151,6 @@ describe("NetworkClientLibrary", () => { }); const lib = new NetworkClientLibrary(); await lib.__init(ctx); - await lib.connect(); expect(() => lib.sendData(new Uint8Array([1]))).not.toThrow(); expect(() => lib.sendData(new Uint8Array([1]), { channel: Channel.UnreliableUnordered }), @@ -168,7 +165,6 @@ describe("NetworkClientLibrary", () => { }); const lib = new NetworkClientLibrary(); await lib.__init(ctx); - await lib.connect(); expect(vi.mocked(WebSocket).mock.calls.map(([url]) => url)).toStrictEqual([ "ws://127.0.0.1:8080", @@ -185,9 +181,8 @@ describe("NetworkClientLibrary", () => { SERVER_ADDRESS: "127.0.0.1", }); const lib = new NetworkClientLibrary(); - await lib.__init(ctx); - await expect(lib.connect()).rejects.toThrow( + await expect(lib.__init(ctx)).rejects.toThrow( "NetworkClientLibrary: could not connect to the server", ); expect(vi.mocked(WebSocket).mock.calls[1]?.[0]).toBe("ws://127.0.0.1:8081"); @@ -201,69 +196,52 @@ describe("NetworkClientLibrary", () => { }); }); - describe("connect", () => { - it("should not open any connection during __init", async () => { - await new NetworkClientLibrary().__init(makeInitContext(both)); - expect(WebSocket).not.toHaveBeenCalled(); + describe("connection", () => { + it("should open only the configured transports during __init", async () => { + await new NetworkClientLibrary().__init(makeInitContext(udpOnly)); + + expect(vi.mocked(WebSocket).mock.calls.map(([url]) => url)).toStrictEqual([ + "ws://127.0.0.1:8081", + ]); }); - it("should only open the transports the channels need", async () => { + it("should open all four channels by default when both ports are configured", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(both)); - await lib.connect({ channels: [Channel.UnreliableUnordered] }); - expect(vi.mocked(WebSocket).mock.calls.map(([url]) => url)).toStrictEqual([ - "ws://127.0.0.1:8081", + expect(Object.values(Channel).map((channel) => lib.isConnected({ channel }))).toStrictEqual([ + true, + true, + true, + true, ]); }); it("should wait until every channel is open", async () => { dataChannelState = "connecting"; const lib = new NetworkClientLibrary(); - await lib.__init(makeInitContext(udpOnly)); - let connected = false; - const connecting = lib - .connect({ channels: [Channel.UnreliableOrdered] }) - .then(() => (connected = true)); + let initialized = false; + const init = lib.__init(makeInitContext(udpOnly)).then(() => (initialized = true)); await new Promise((resolve) => setTimeout(resolve, 100)); - expect(connected).toBe(false); + expect(initialized).toBe(false); dataChannel("unreliable-ordered").readyState = "open"; - await connecting; - expect(connected).toBe(true); + dataChannel("unreliable-unordered").readyState = "open"; + await init; + expect(initialized).toBe(true); }); it("should reject when the channels are not open in time", async () => { dataChannelState = "connecting"; - const lib = new NetworkClientLibrary(); - await lib.__init(makeInitContext(udpOnly)); + vi.useFakeTimers(); - await expect(lib.connect({ timeout: 100 })).rejects.toThrow( - "channels unreliable-ordered, unreliable-unordered not open after 100ms", + const init = new NetworkClientLibrary().__init(makeInitContext(udpOnly)); + const rejection = expect(init).rejects.toThrow( + "channels unreliable-ordered, unreliable-unordered not open after 10000ms", ); - }); - - it("should not reconnect a transport that is already open", async () => { - const lib = new NetworkClientLibrary(); - await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); - await lib.connect({ channels: [Channel.ReliableUnordered] }); - - expect(WebSocket).toHaveBeenCalledTimes(1); - }); - - it("should reject a channel whose port is not configured", async () => { - const lib = new NetworkClientLibrary(); - await lib.__init(makeInitContext(tcpOnly)); - - await expect(lib.connect({ channels: [Channel.UnreliableOrdered] })).rejects.toThrow( - "Channel unreliable-ordered needs SERVER_UDP_PORT to be set", - ); - }); - - it("should reject when called before __init", async () => { - await expect(new NetworkClientLibrary().connect()).rejects.toThrow("before __init"); + await vi.advanceTimersByTimeAsync(10_000); + await rejection; }); }); @@ -271,7 +249,6 @@ describe("NetworkClientLibrary", () => { it("should send on ReliableOrdered when no channel is given", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); lib.sendData(new Uint8Array([1])); expect(tcpSocket().send).toHaveBeenCalledWith(new Uint8Array([0, 0, 1])); @@ -280,7 +257,6 @@ describe("NetworkClientLibrary", () => { it("should send each reliable channel over the same TCP socket", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); lib.sendData(new Uint8Array([1]), { channel: Channel.ReliableOrdered }); lib.sendData(new Uint8Array([2]), { channel: Channel.ReliableUnordered }); @@ -294,7 +270,6 @@ describe("NetworkClientLibrary", () => { it("should send strings as UTF-8 and other values as JSON", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(both)); - await lib.connect(); lib.sendData({ type: "play" }); lib.sendData("hi", { channel: Channel.UnreliableUnordered }); @@ -310,7 +285,6 @@ describe("NetworkClientLibrary", () => { it("should open one data channel per unreliable channel", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(udpOnly)); - await lib.connect(); const peerConnection = vi.mocked(RTCPeerConnection).mock.instances[0] as any; expect( @@ -321,7 +295,6 @@ describe("NetworkClientLibrary", () => { it("should send unreliable channels on their own data channel", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(udpOnly)); - await lib.connect(); lib.sendData(new Uint8Array([3]), { channel: Channel.UnreliableUnordered }); expect(dataChannel("unreliable-unordered").send).toHaveBeenCalledWith(new Uint8Array([0, 3])); @@ -331,7 +304,6 @@ describe("NetworkClientLibrary", () => { it("should read one channel, or every channel with reliable ones first", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(both)); - await lib.connect(); dataChannel("unreliable-unordered").onmessage({ data: new Uint8Array([0, 9]).buffer }); tcpSocket().onmessage({ data: new Uint8Array([1, 0, 2]).buffer }); @@ -350,7 +322,6 @@ describe("NetworkClientLibrary", () => { it("should return packets that read back as JSON", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); tcpSocket().onmessage({ data: new Uint8Array([0, 0, ...utf8('{"type":"move"}')]).buffer }); @@ -362,7 +333,6 @@ describe("NetworkClientLibrary", () => { it("should report connected only once every configured channel is", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(both)); - await lib.connect(); tcpSocket().readyState = 1; dataChannel("unreliable-ordered").readyState = "connecting"; @@ -393,7 +363,6 @@ describe("NetworkClientLibrary", () => { it("should dispatch typed packets in __run, reliable ones first", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(both)); - await lib.connect(); const calls: unknown[] = []; lib.packets.on(Move, (move, info) => calls.push([move, info])); lib.packets.on(Play, (play, info) => calls.push([play, info])); @@ -417,7 +386,6 @@ describe("NetworkClientLibrary", () => { it("should keep typed packets out of getReceivedPackets until __run", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); const listener = vi.fn(); lib.packets.on(Play, listener); @@ -430,7 +398,6 @@ describe("NetworkClientLibrary", () => { it("should drop sendData packets left unread for a whole tick", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); tcpSocket().onmessage({ data: new Uint8Array([0, 0, 1]).buffer }); await lib.__run(ctx); @@ -445,7 +412,6 @@ describe("NetworkClientLibrary", () => { it("should not lose packets drained by a listener", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); const listener = vi.fn(() => { receive(encodeTypedPacket(Play, undefined), Channel.ReliableOrdered); lib.getReceivedPackets(); @@ -461,7 +427,6 @@ describe("NetworkClientLibrary", () => { it("should send on the definition's channel unless another is given", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(both)); - await lib.connect(); lib.packets.send(Move, { x: 1 }); lib.packets.send(Play, undefined, { channel: Channel.ReliableUnordered }); @@ -482,7 +447,6 @@ describe("NetworkClientLibrary", () => { it("delegates to the library", async () => { const lib = new NetworkClientLibrary(); await lib.__init(makeInitContext(tcpOnly)); - await lib.connect(); const api = lib.expose(); api.sendData(new Uint8Array([5]), { channel: Channel.ReliableUnordered });