1.2.4 • Published 6 months ago

@four-leaf-studios/rl-socket-hook v1.2.4

Weekly downloads
-
License
MIT
Repository
-
Last release
6 months ago

@four-leaf-studios/rl-socket-hook

A tiny React wrapper around a Rocket League WebSocket plugin (ws://localhost:49122). It provides:

  • <RLProvider>: React context provider for WebSocket events.
  • useEvent: Subscribe to entire event payloads.
  • useEventSelector: Subscribe to specific slices of event payloads.
  • useRocketLeagueSocket: Low-level hook for raw event data.

Table of Contents

  1. Installation
  2. Quick Start
  3. Core API
  4. Examples
  5. TypeScript Support
  6. Contributing
  7. License

Installation

npm install @four-leaf-studios/rl-socket-hook
# or
yarn add @four-leaf-studios/rl-socket-hook

Quick Start

import {
  RLProvider,
  useEvent,
  useEventSelector,
} from "@four-leaf-studios/rl-socket-hook";

function App() {
  return (
    <RLProvider url="ws://localhost:49122">
      <Scoreboard />
      <Overlay />
    </RLProvider>
  );
}

Core API

RLProvider

Wrap your app to initialize the WebSocket connection and provide context.

<RLProvider url="ws://localhost:49122">{children}</RLProvider>
  • url (string): WebSocket URL. Default: ws://localhost:49122.
  • children: React nodes.

useEvent(eventName)

Subscribe to the full payload of an event.

const gameState = useEvent("game:update_state");
  • Parameters:
    • eventName: key from EventPayloads.
  • Returns: Payload object or undefined.

useEventSelector(eventName, selector, isEqual?)

Subscribe to a specific slice of the payload to minimize re-renders.

const blueScore = useEventSelector(
  "game:update_state",
  (s) => s.game.teams[0].score ?? 0
);
  • Parameters:
    • eventName: string key.
    • selector: (payload) => slice.
    • isEqual?: optional custom equality function.
  • Returns: Selected slice value.

useRocketLeagueSocket

Note: This hook opens its own WebSocket connection. If you are already using <RLProvider>, do not use useRocketLeagueSocket together—it will create a second connection. Instead, use useEvent or useEventSelector within the RLProvider context. Use useRocketLeagueSocket only when you need a standalone socket (e.g., in a custom provider).

import { useRocketLeagueSocket } from "@four-leaf-studios/rl-socket-hook";

function CustomProviderComponent() {
  const allEvents = useRocketLeagueSocket();
  // ...custom handling...
}
  • Returns: An object mapping event names to payloads: { [eventName]: payload }.

Examples

Overlay Component

Full overlay UI with scores and players.

function Overlay() {
  const gameState = useEvent("game:update_state");
  if (!gameState) return null;

  const time = formatTime(gameState.game.time_seconds);
  const blueScore = gameState.game.teams[0].score;
  const orangeScore = gameState.game.teams[1].score;
  // ...
}

Scoreboard

Render only necessary fields:

function Scoreboard() {
  const blueScore = useEventSelector(
    "game:update_state",
    (s) => s.game.teams[0].score ?? 0
  );
  const orangeScore = useEventSelector(
    "game:update_state",
    (s) => s.game.teams[1].score ?? 0
  );
  const time = useEventSelector(
    "game:update_state",
    (s) => s.game.time_seconds ?? 0
  );

  return (
    <div className="flex items-center space-x-4 p-4 bg-neutral-800 text-white">
      <span>🔵 {blueScore}</span>
      <span>⏱️ {formatTime(time)}</span>
      <span>🟠 {orangeScore}</span>
    </div>
  );
}

Raw Event Dump

Debug all incoming events:

function RawDump() {
  const { events } = useRocketLeagueSocket();
  return <pre>{JSON.stringify(events, null, 2)}</pre>;
}
```tsx
function RawDump() {
  const data = useRocketLeagueSocket();
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

WebsocketData Component

A React example showing full usage of useRocketLeagueSocket (connection state, errors, and events rendering).

// src/WebsocketData.tsx
import React from "react";
import useRocketLeagueSocket from "./useRocketLeagueSocket";
import { PayloadStorage } from "./types";

const STATE_LABELS = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"] as const;

export const WebsocketData: React.FC = () => {
  const { events, readyState, error } = useRocketLeagueSocket<PayloadStorage>();

  return (
    <div style={{ fontFamily: "monospace", padding: "1rem" }}>
      <h1>Rocket League Live Events</h1>

      <p>
        <strong>Connection:</strong> {STATE_LABELS[readyState] ?? readyState}
      </p>
      {error && (
        <p style={{ color: "red" }}>
          <strong>Error:</strong> {String(error)}
        </p>
      )}

      {Object.entries(events).map(([event, payload]) => (
        <div
          key={event}
          style={{
            border: "1px solid #ccc",
            marginBottom: "1rem",
            padding: "0.5rem",
            borderRadius: "4px",
            backgroundColor: "#f9f9f9",
          }}
        >
          <h2>{event}</h2>
          <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
            {JSON.stringify(payload, null, 2)}
          </pre>
        </div>
      ))}

      {Object.keys(events).length === 0 && <p>No events received yet.</p>}
    </div>
  );
};

export default WebsocketData;

Send Event Example

Demonstration of sending a custom event once the socket is open:

import React, { useEffect } from "react";
import { useRocketLeagueSocket } from "@four-leaf-studios/rl-socket-hook";

function SendEventExample() {
  const { send, readyState } = useRocketLeagueSocket();

  useEffect(() => {
    if (readyState === WebSocket.OPEN) {
      send("my:custom_event", { foo: "bar" });
    }
  }, [readyState, send]);

  return <div>Sent "my:custom_event" when connected.</div>;
}

TypeScript Support

Built-in Types

All event payloads are typed in the EventPayloads interface. Import as needed:

import type {
  GameUpdateState,
  GoalScoredEvent,
  EventPayloads,
} from "@four-leaf-studios/rl-socket-hook";

Extending EventPayloads

Use declaration merging to override or extend event types:

// src/types/rl-socket-hook.d.ts
import type { GameUpdateState } from "@four-leaf-studios/rl-socket-hook";

declare module "@four-leaf-studios/rl-socket-hook" {
  interface EventPayloads {
    "game:update_state": GameUpdateState;
    "my:custom_event": { foo: string };
  }
}

Contributing

PRs welcome. Please open issues for features or bugs.

License

MIT © Four Leaf Studios

1.2.4

6 months ago

1.2.3

6 months ago

1.2.2

6 months ago

1.2.1

6 months ago

1.2.0

6 months ago

1.1.2

6 months ago

1.1.1

6 months ago

1.1.0

6 months ago

1.0.1

6 months ago

1.0.0

6 months ago