Skip to main content
The ROBLOX module is a client-server system that integrates SkyTeam features into member airline games on ROBLOX.

Overview

The module provides a TypeScript-based framework that compiles to Lua using roblox-ts, enabling airline games to connect with the SkyTeam API and display real-time information to players. Package: @skyteam/models
Location: apps/models
Language: TypeScript (compiled to Lua)

Technology Stack

  • Language: TypeScript with roblox-ts compiler
  • UI Framework: Roact (React for ROBLOX)
  • Build Tool: roblox-ts v3.0
  • Project Manager: Rojo for syncing to ROBLOX Studio
  • Type Definitions: @rbxts/types, @rbxts/compiler-types

Project Structure

apps/models/
├── src/
│   ├── loader/
│   │   └── main.server.ts        # Server-side loader script
│   └── module/
│       ├── index.ts               # Main SkyTeamModule class
│       ├── client/
│       │   ├── index.client.tsx   # Client runtime
│       │   └── ErrorMessage.tsx   # Error UI component
│       └── shared/
│           └── CommunicationTypes.d.ts  # Event type definitions
├── default.project.json           # Rojo configuration
└── package.json

Architecture

SkyTeamModule Class

The main module class (src/module/index.ts:29) initializes the SkyTeam system:
apps/models/src/module/index.ts
export interface Settings {
  /**
   * The token for the module.
   */
  TOKEN: string;
  /**
   * The flags for the module.
   */
  Flags: string[];
}

/**
 * @class SkyTeamModule
 * @author h1ddenscript
 * @description This module is used to setup the SkyTeam system within a SkyTeam Alliance Members' game.
 */
export default class {
  private Settings: Settings;
  public API: string = "http://localhost:4000";
  public RemoteEvent: RemoteEvent = new Instance("RemoteEvent");
  public Slocked: boolean = false;
  public InitalizationError: string | undefined;

  constructor(Settings: Settings) {
    script.Parent = ReplicatedStorage;
    this.Settings = Settings;
  }

  /**
   * @method Initialize
   * @description Hot method is used to initialize the module.
   */
  public Initialize(): void {
    this.RemoteEvent.Name = ".SKYTEAM_CLIENT_RUNTIME";
    this.RemoteEvent.Parent = ReplicatedStorage;

    // Clone client script to existing players
    for (const Player of Players.GetPlayers()) {
      const ClientScript = script.WaitForChild("client").Clone();
      ClientScript.Parent = Player.WaitForChild("PlayerGui");
    }

    // Handle new players joining
    Players.PlayerAdded.Connect((Player) => {
      const ClientScript = script.WaitForChild("client").Clone();
      ClientScript.Parent = Player.WaitForChild("PlayerGui");

      if (this.InitalizationError) {
        this.PublicError("SERVER_INIT_ERROR", {
          Body: this.InitalizationError,
        });
      }
    });

    // Test API connectivity
    this.TestServices().catch((err) => {
      warn(err);
      this.InitalizationError = err;
      this.PublicError("SERVER_INIT_ERROR", {
        Body: `SkyTeam module failed to initialize. Please contact a developer. Error: ${err}`,
      });
    });
  }

  private async TestServices(): Promise<any> {
    return new Promise(async (resolve, reject) => {
      const [success, result] = pcall(() => {
        HttpService.GetAsync("https://example.com/");
      });

      if (!success) {
        reject(result);
        return;
      }

      resolve(result);
    });
  }

  private async PublicError<T extends EventTypeKeys>(
    WhichEvent: T,
    args: EventTypes[T],
  ) {
    this.RemoteEvent.FireAllClients(WhichEvent, args);
  }
}

Server-Side Loader

The loader script (src/loader/main.server.ts:1) instantiates and initializes the module:
apps/models/src/loader/main.server.ts
import SkyTeamModule from "module";

const SkyTeam = new SkyTeamModule({
  TOKEN: "YAY",
  Flags: ["flag1", "flag2"],
}).Initialize();

Client Runtime

The client script (src/module/client/index.client.tsx:1) handles client-side events:
apps/models/src/module/client/index.client.tsx
import Roact from "@rbxts/roact";
import { Players, ReplicatedStorage } from "@rbxts/services";
import ErrorMessage from "./ErrorMessage";
import type { EventTypeKeys, EventTypes } from "../shared/CommunicationTypes";

const ClientCommunication = ReplicatedStorage.WaitForChild(
  ".SKYTEAM_CLIENT_RUNTIME",
) as RemoteEvent;
const Player = Players.LocalPlayer;

wait(0.5);

/**
 * This is a really hacky way to mount it w/o serverside replication
 * I'm just scared of weird scripts ppl might use that could break it
 * if we just left it in playergui
 */
if (script.Parent?.IsA("PlayerGui")) {
  script.Clone().Parent = Player.WaitForChild("PlayerScripts");
} else {
  script.Parent = undefined;

  ClientCommunication.OnClientEvent.Connect(
    (eventType: EventTypeKeys, rawargs: unknown) => {
      switch (eventType) {
        case "SERVER_INIT_ERROR": {
          const args = rawargs as EventTypes["SERVER_INIT_ERROR"];

          Roact.mount(
            <ErrorMessage
              BackgroundColor={Color3.fromRGB(255, 149, 0)}
              ElementColor={Color3.fromRGB(71, 41, 0)}
              Title="Service Initialization Error"
              Body={args.Body}
            />,
            Player.WaitForChild("PlayerGui"),
          );
          break;
        }
      }
    },
  );
}

Event Communication

Type-safe event system (src/module/shared/CommunicationTypes.d.ts:1):
apps/models/src/module/shared/CommunicationTypes.d.ts
// Define interfaces for each event type's arguments
export interface ServerInitErrorArgs {
  Body: string;
}

export interface UnknownArgs {
  // Add any properties for UNKNOWN event type if needed
}

// Map event types to their respective argument types
export interface EventTypes {
  SERVER_INIT_ERROR: ServerInitErrorArgs;
  UNKNOWN: UnknownArgs;
}

// Keys
export type EventTypeKeys = keyof EventTypes;

Usage in Games

Basic Setup

  1. Install Module: Place compiled module in ReplicatedStorage
  2. Configure Token: Set airline API token in loader script
  3. Initialize: Call .Initialize() on server start
local SkyTeamModule = require(game.ReplicatedStorage.SkyTeamModule)

local SkyTeam = SkyTeamModule.new({
  TOKEN = "your_airline_token_here",
  Flags = {"premium", "miles_enabled"}
})

SkyTeam:Initialize()

Event Handling

The module communicates via RemoteEvent:
  • Server → Client: Initialization errors, status updates
  • Client → Server: User actions, data requests
  • Type Safety: TypeScript ensures correct event data types

UI Components

Error Display

The module includes a Roact-based error UI that displays initialization failures:
<ErrorMessage
  BackgroundColor={Color3.fromRGB(255, 149, 0)}
  ElementColor={Color3.fromRGB(71, 41, 0)}
  Title="Service Initialization Error"
  Body="Failed to connect to SkyTeam API"
/>

Future Components

Planned UI components:
  • Miles Display: Show player’s SkyTeam miles
  • Flight Info: Current flight details and status
  • Rewards Shop: Browse and purchase miles products
  • Alliance Info: View connected airlines

Development

Scripts

# Build to Lua
pnpm build

# Watch mode (compile on save)
pnpm dev:watch

# Rojo server (sync to Studio)
pnpm dev:serve

# Combined dev (watch + serve)
pnpm dev

Build Process

  1. TypeScript Compilation: roblox-ts compiles .ts files to .lua
  2. Rojo Sync: Rojo syncs compiled files to ROBLOX Studio
  3. Type Checking: TypeScript validates types before compilation

Dependencies

apps/models/package.json
{
  "dependencies": {
    "@rbxts/ripple": "^0.9.3",
    "@rbxts/roact": "^3.0.1",
    "@rbxts/services": "^1.5.5"
  },
  "devDependencies": {
    "@rbxts/compiler-types": "3.0.0-types.0",
    "@rbxts/types": "^1.0.843",
    "roblox-ts": "^3.0.0",
    "typescript": "^5.8.2"
  }
}

API Integration

The module connects to the SkyTeam API (src/module/index.ts:31):
public API: string = "http://localhost:4000";
In production, this points to the live API endpoint for:
  • Fetching user data and miles
  • Recording flight activity
  • Validating purchases
  • Syncing airline information

Service Testing

The module validates HTTP connectivity on initialization (src/module/index.ts:75):
private async TestServices(): Promise<any> {
  return new Promise(async (resolve, reject) => {
    const [success, result] = pcall(() => {
      HttpService.GetAsync("https://example.com/");
    });

    if (!success) {
      reject(result);
      return;
    }

    resolve(result);
  });
}
This ensures:
  • HTTP requests are enabled in game settings
  • Network connectivity is available
  • API endpoints are reachable

Error Handling

Initialization Errors

If initialization fails:
  1. Error is logged to server console
  2. Error message stored in InitalizationError
  3. All clients receive error via RemoteEvent
  4. Client displays error UI to players
  5. New players joining also see the error

Graceful Degradation

When API is unavailable:
  • Game continues to function normally
  • SkyTeam features are disabled
  • Clear error message shown to players
  • Automatic retry on next server restart

Security Considerations

Token Protection

  • Airline tokens should be stored server-side only
  • Never expose tokens to client scripts
  • Rotate tokens regularly
  • Use environment-specific tokens (dev/prod)

Client Validation

  • All client requests validated on server
  • API calls only from server scripts
  • Rate limiting on API endpoints
  • Input sanitization for user data

Rojo Configuration

The module uses Rojo for project management (default.project.json):
{
  "name": "SkyTeamModule",
  "tree": {
    "$className": "DataModel",
    "ReplicatedStorage": {
      "$className": "ReplicatedStorage",
      "SkyTeamModule": {
        "$path": "out"
      }
    }
  }
}

Future Features

  • Real-time Flight Tracking: Sync active flights to API
  • Miles Earning: Award miles for completed flights
  • In-Game Shop: Purchase upgrades with miles
  • Alliance Notifications: Broadcast alliance-wide messages
  • Player Statistics: Track and display flight history
  • Achievements: Unlock rewards for milestones

TypeScript Benefits

Type Safety

// Compile-time error if wrong event type
this.PublicError("INVALID_EVENT", { data: "test" }); // ❌ Error

// Correct usage with type checking
this.PublicError("SERVER_INIT_ERROR", { Body: "error" }); // ✅ Valid

Autocomplete & IntelliSense

  • Full IDE support in VS Code
  • Autocomplete for ROBLOX APIs
  • Inline documentation from type definitions
  • Refactoring support (rename, extract, etc.)

Modern JavaScript Features

  • Async/await for asynchronous operations
  • Arrow functions and destructuring
  • Template literals
  • Optional chaining and nullish coalescing
All compiled down to Luau-compatible code.

Build docs developers (and LLMs) love