> ## Documentation Index
> Fetch the complete documentation index at: https://v5.rpgjs.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Structure

> Understand the default RPGJS project structure.

# Structure

The starter separates client boot, server boot, shared client config, and modules.

## `client.ts`

`client.ts` starts the browser client in MMORPG mode:

```ts theme={null}
import { startGame, provideMmorpg } from "@rpgjs/client";
import configClient from "./config/config.client";
import { mergeConfig } from "@signe/di";

startGame(
  mergeConfig(configClient, {
    providers: [provideMmorpg({})],
  }) 
);
```

Use this entry when the client connects to a remote RPGJS server.

By default, MMORPG mode stores a stable session id in `localStorage` and passes it
to `@signe/room` as the connection session id. Refreshes and multiple tabs from
the same browser therefore restore the same player session while each WebSocket
keeps its own connection id. Use `connectionIdScope: "session"` to keep the
session only for one browser tab, or `connectionIdScope: "ephemeral"` to create a
new player session on each page load:

```ts theme={null}
providers: [provideMmorpg({ connectionIdScope: "session" })]
```

If your server uses `engine.auth()`, send the token with the MMORPG connection
query. RPGJS includes this query on the initial lobby connection and on map-room
reconnects:

```ts theme={null}
providers: [
  provideMmorpg({
    query: () => ({
      token: localStorage.getItem("token")
    })
  })
]
```

## `server.ts`

`server.ts` creates the game server and registers your providers:

```ts theme={null}
import { createServer, provideServerModules, LocalStorageSaveStorageStrategy } from "@rpgjs/server";
import { provideMain } from "./modules/main";
import { provideSaveStorage } from "@rpgjs/server";
import { provideTiledMap } from "@rpgjs/tiledmap/server";

export default createServer({
  providers: [
    provideMain(),
    provideSaveStorage(new LocalStorageSaveStorageStrategy({ key: "save" })),
    provideServerModules([]),
    provideTiledMap()
  ]
});
```

This is where you plug modules, maps, database content, save strategies, or server adapters.

## `standalone.ts`

`standalone.ts` runs the client and server together for a standalone RPG:

```ts theme={null}
import { mergeConfig } from "@signe/di";
import { provideRpg, startGame } from "@rpgjs/client";
import startServer from "./server";
import configClient from "./config/config.client";

startGame(
  mergeConfig(configClient, {
    providers: [provideRpg(startServer)],
  })
);
```

Use this entry when you want a single-player RPG running entirely from the client app.

## `config/config.client.ts`

`config.client.ts` contains the common client setup shared by MMORPG and standalone RPG:

```ts theme={null}
import { provideClientGlobalConfig, provideClientModules, Presets } from "@rpgjs/client";
import { provideMain } from "../modules/main";
import { provideTiledMap } from "@rpgjs/tiledmap/client";

export default {
  providers: [
    provideTiledMap({
      basePath: "map",
    }),
    provideClientGlobalConfig(),
    provideMain(),
    provideClientModules([
      {
        spritesheets: [
          {
            id: "hero",
            image: "spritesheets/hero.png",
            ...Presets.RMSpritesheet(3, 4)
          }
        ]
      }
    ])
  ],
};
```

Put here everything the client always needs: map loading, global config, modules, spritesheets, sounds, and resolvers.

### `provideClientGlobalConfig()`

`provideClientGlobalConfig()` is the client-side place for shared global configuration.
It can hold built-in options such as `keyboardControls`, and also any custom object you want
to expose everywhere in the client through dependency injection.

```ts theme={null}
import { provideClientGlobalConfig } from "@rpgjs/client";

export default {
  providers: [
    provideClientGlobalConfig({
      keyboardControls: {
        up: "z",
        down: "s",
        left: "q",
        right: "d",
        action: "enter",
        escape: "escape"
      },
      ui: {
        locale: "fr",
        showDamagePreview: true
      },
      api: {
        baseUrl: "/api"
      }
    })
  ]
};
```

If you omit `keyboardControls`, RPGJS injects the default bindings automatically:

```ts theme={null}
{
  up: "up",
  down: "down",
  left: "left",
  right: "right",
  action: "space",
  escape: "escape"
}
```

The `action` binding also accepts an object when the action key should send a
custom action input. This keeps the key generic in built-in components while
letting the game decide which action and payload to send.

```ts theme={null}
provideClientGlobalConfig({
  keyboardControls: {
    up: "z",
    down: "s",
    left: "q",
    right: "d",
    action: {
      bind: "space",
      action: "projectile:shoot",
      data: (client, sprite) => ({
        target: client.pointer.world(),
        source: "keyboard",
        playerId: sprite.id
      })
    },
    escape: "escape"
  }
})
```

### Custom action inputs

Custom action inputs use the same client-to-server flow whether they come from
the configured action key or from code. The client sends a payload shaped like
`{ action, data }`, and the server receives that payload in the player's
`onInput()` hook.

```ts theme={null}
// Client code
client.processAction("projectile:shoot", {
  target: client.pointer.world(),
  source: "mouse"
})
```

```ts theme={null}
// Server player hook
export const player = {
  onInput(player, input) {
    if (input.action !== "projectile:shoot") return

    const target = input.data?.target
    // Validate all client-provided data before using it.
  }
}
```

Only the default `"action"` input, or `Control.Action`, automatically triggers
nearby event `onAction()` hooks. A custom action such as `"projectile:shoot"`
goes directly to `player.onInput()` and does not trigger event interactions by
itself.

Do not confuse this player hook with map movement input processing. Custom
actions are sent with `client.processAction()` and handled in `player.onInput()`;
movement inputs are queued separately by the map and processed by the movement
loop.

You can retrieve this global config anywhere on the client with `inject(GlobalConfigToken)`:

```ts theme={null}
import { inject } from "@signe/di";
import { GlobalConfigToken } from "@rpgjs/client";
import type { KeyboardActionConfig } from "@rpgjs/client";

const config = inject(GlobalConfigToken) as {
  keyboardControls: {
    up: string;
    down: string;
    left: string;
    right: string;
    action: string | KeyboardActionConfig;
    escape: string;
  };
  ui?: {
    locale?: string;
    showDamagePreview?: boolean;
  };
};

console.log(config.keyboardControls.action);
console.log(config.ui?.locale);
```

This is useful in client services, GUI components, or custom systems that need access to
global input bindings or project-specific client configuration.

## `modules/main`

The main module is usually where you declare your first server hooks and maps:

```ts theme={null}
import { createModule } from "@rpgjs/common";
import server from "./server";

export function provideMain() {
  return createModule("main", [{
    server
  }]);
}
```

This keeps your game logic modular. You can add more modules later for battle, UI, quests, chat, or any custom feature.
