OPX//77
Guides

Contributing

Last updated on

How to change opx_infinity, opx_lib or this site — branches, checks before pushing, code conventions, text, and the documentation to update in the same change.

The rules reviewers apply. In the framework, CONTRIBUTING.md (workflow and house style), docs/MANUAL.md (the design record, formerly the README) and ui/README.md are the authority when they disagree with this page. The framework's README.md is now only its front page.

Branches and pull requests

Nothing is pushed to main directly: branch, push, open a pull request. main is what the test server must be able to run.

BranchFor
feat/<thing>a new capability
fix/<thing>a defect
audit/<thing>a sweep across the codebase

Before you push

From the opx_infinity folder, with opx_lib checked out next to it:

lua tests/run.lua                                    # must be green
luac -p $(find . -name '*.lua' -not -name 'open77.lua')
npm run typecheck && npm run build                   # only if ui/ changed
CheckCatches
tests/run.luaboots the real manifest on a stub platform; also fails on a file missing from the manifest, a global the sandbox removes, a client-only global in server code, or a module hanging its internals on OPX
luac -psyntax errors. open77.lua is the manifest DSL, not Lua, so it is excluded.
npm run buildwrites web/index.html, the shipped page. Commit it with the ui/ change.
CI (.github/workflows/check.yml)the above, plus tools/check-sandbox-globals.py (no metatables or require in shared code)

The budget meter

The client kills a coroutine resume that runs past its instruction budget (about 10,000 VM instructions), silently, mid-operation. Desktop Lua has no such limit, so a green suite does not prove a handler fits. Run the opt-in meter on any client change:

OPX_BUDGET_METER=5000 lua tests/run.lua

At the end it lists every client call site whose worst single resume cost more than the figure, dearest first: each thread resume, net event, page handler, event handler, key mapping and scheduler job. The stub host's own work is not charged. A site on the list is a resume the platform may kill: split it (see the instruction budget). To pin a fix, tests/run.lua has callCost(fn, ...) and resumeCost(env, control, start, rounds), which count exactly and let a check fail at the old cost.

Code conventions

RuleDetail
Lua 5.4, not CfxLuaNo vector literals, no backtick hashes, no ?., no +=.
IndentationTabs in .lua; two spaces elsewhere (.editorconfig). LF line endings, UTF-8.
QuotesSingle quotes in Lua.
One file per manifest lineNever glob scripts. Every .lua file must be listed in open77.lua.
Modules talk through contractsNever reach into another module's files or tables. Nothing new goes on OPX without a reason (the test suite keeps a list).
Read settings lateM.Settings inside a phase, never at file scope.
Answer, don't raisePrefer a refusal with a named code. Most functions answer a Result ({ ok = true, value } / { ok = false, error, detail }); error is stable, detail is for logs only.
Server decidesRe-check every network payload; see Server authority.
Repeating workOPX.Scheduler.Every, not a hand-written loop.
NativesLook every native up with the Open77 devkit; add its manifest permission in the same change.
CommentsExplain why, not what. -- @author <name> on a file header and on a public function.

Text

  • Player-facing text comes from a catalogue: locale(key, params). Logs and error codes stay in English.
  • modules/<id>/locales.lua holds English and French with the same keys and the same {placeholders}.
  • Never rename a key: it may be a code on the wire.

See Text and locales.

The WebUI

Read ui/README.md in the framework first; it is binding. Hard-code no colour — use the theme tokens. open77_pause owns Escape.

Commit messages

Say what changed and why it was wrong before. If a change fixes something that failed silently, describe what the player saw: that is what someone will search for later.

Deploying to the test server

  • Archive from the commit, not the working tree, then restart and read the journal in the same change. See Install.
  • A change a live server must confirm (a native the stub host only imitates) gets a section in docs/IN_GAME_CHECKLIST.md: steps, expected result, log lines. Tick its boxes in a PR that edits the file, with the builds used.
  • A limit of the Open77 platform is not worked around in silence: it goes in docs/PLATFORM_REQUESTS.md with the evidence, the current fallback, the exact ask and what to change here once it lands.
  • Never DROP DATABASE; empty tables instead, after a mysqldump.

Updating this documentation

A change that adds, removes or changes a command, event, contract function, config key or page channel updates this site in the same change. Pages are MDX files under content/docs/**.mdx; each folder's meta.json gives its sidebar title and page order.

Adding a module

  1. Create content/docs/opx_infinity/<id>/ from an existing module folder (weather is the model):
    • index.mdx — what it does, the summary table, Commands, how it works, Configuration, Refusal codes.
    • server.mdx / client.mdx — the contract, one ## per member.
    • events.mdx — network and local events, hooks, page channels.
    • meta.json — { "title": "<id>", "pages": ["index", "server", "client", "events"] }. Leave out pages and sections the module does not have.
  2. Add "<id>" to pages in content/docs/opx_infinity/meta.json, in alphabetical order.
  3. Run the checks (below) until nothing is missing.

Page format

Every function, export or event is one heading with a one-line description, the signature snippet, a typed list and a tiny example:

## SetWeather [#server-weather-setweather]

Crosses to a weather and reschedules the next roll.

```lua
weather.SetWeather(name, transitionSeconds, reason)
```

**Parameters**
- name: `string`
- transitionSeconds?: `number`
  - 0..300. Default: the row's `TRANSITION_SECONDS`.
- reason?: `string`

**Returns**
- `Result` — errors `no_presets`, `unknown_preset`, `invalid_transition`

```lua title="Example"
local set = weather.SetWeather('rain', 30, 'storm event')
```
  • Optional parameters end with ?; types in backticks. Omit Parameters or Returns when there are none.
  • Commands, config keys and refusal codes are tables on the module's index.mdx, anchor in the first cell.
  • {, } and < outside code are read as JSX: wrap them in backticks.

Anchors

Every published name needs an anchor: [#id] at the end of a heading, a self-closing <a id … /> anywhere else (a table cell).

## SetTime [#server-weather-settime]

| <a id="config-weather-enabled" />`enabled` | `true` | … |

The anchor is the name in lower case with every run of other characters replaced by -. Never define one twice on a page, and never change one: links and old-URL redirects depend on it.

NameAnchor
server contract member character.AddMoneyserver-character-addmoney
command opx.admin.player.gotoopx-admin-player-goto
event opx:net:chat:sayopx-net-chat-say
page channel menu:choosepage-menu-choose
config key SELLER_CUT_PERCENT of dealershipconfig-dealership-seller-cut-percent
function OPX.Scheduler.Everyopx-scheduler-every
opx_lib function Lib.Rpc.Calllib-rpc-call
server export AddMoney (in content/docs/creators/)export-server-addmoney

Checks

npm install
node scripts/check-content.mjs     # MDX compiles, front matter, links and anchors
npm run check:api                  # every published name has an anchor
npm run types:check
npm run dev                        # local preview with hot reload
npm run build                      # static export to out/
  • node scripts/check-content.mjs [file-or-dir ...] checks only the paths given.
  • npm run check:api runs scripts/check-api-coverage.sh: it boots the framework on its stub host and fails on any published name without an anchor. It needs Lua 5.4 and checkouts of opx_infinity and opx_lib (next to this repository, or ./scripts/check-api-coverage.sh /path/to/opx_infinity).

Writing style

  • Front matter on every page: title and a one-sentence description (shown under the title, so do not repeat it).
  • Snippets first, little prose. Short, direct sentences; tables for anything a reader looks up.
  • British spelling.
  • Never document something you have not read in the code. If the code and the framework manual (docs/MANUAL.md) disagree, the code wins; say so to the maintainers.
  • Callouts are rare: <Callout type="warn"> for a surprising or irreversible effect, type="error" for lost money or data.

Licence

The framework is MIT licensed. A contribution is accepted under the same terms.

On this page