Bundle Layout

Structure bundle directories and manifest fields the way Selene expects them.

Minimal server-tested bundle

This is the smallest practical layout for a bundle you want to load through the server runtime:

bundles/
  example.bundle/
    bundle.json
    server/
      init.lua

Example manifest:

{
  "name": "example.bundle",
  "description": "Minimal server bundle",
  "entrypoints": [
    "server/init.lua"
  ]
}

Runtime folder conventions

Selene consistently distinguishes three script roots:

  • common/: shared across runtimes
  • server/: server-only
  • client/: client-only

Those roots affect more than execution:

  • Entrypoint filters decide which files may run in each runtime
  • Script hot reload watches common plus the active runtime root
  • Messages are loaded from client/i18n, common/i18n, and server/i18n
  • Bundle data and assets are watched by path under bundle-owned runtime directories

Lua module naming

Selene maps Lua files to module names like this:

  • init.lua becomes <bundle-name>
  • common/foo.lua becomes <bundle-name>.common.foo
  • server/world/spawn.lua becomes <bundle-name>.server.world.spawn

That naming matters when you use require(...) and when Selene reloads Lua state.

Dependencies and load order

Declare bundle dependencies explicitly in bundle.json:

{
  "name": "example.bundle",
  "dependencies": [
    "base.bundle",
    "ui.bundle"
  ]
}

Selene resolves dependencies before loading dependent bundles. If a required bundle is missing, the runtime aborts loading rather than partially enabling the bundle set.

Preload layout

Use preloads when you want a stable Lua module name that is available before entrypoints run:

{
  "name": "example.bundle",
  "preloads": {
    "example.shared": "common/shared.lua",
    "example.legacy": {
      "file": "common/legacy.lua",
      "encoding": "UTF-8"
    }
  }
}

This is useful when your bundle wants to expose modules under names that do not map directly to the default file-to-module convention.

Server and client wiring

Server-side wiring is name-based:

bundles_path=../../bundles
bundles=example.bundle

Client-side wiring is path-based:

java -jar selene-client.jar \
  --bundle example.bundle /absolute/path/to/example.bundle

That difference is easy to miss. The same bundle directory can be valid for both runtimes, but the server and client do not discover it the same way.