Contributing - rebaserHEAD/NexusForever GitHub Wiki

explanation, committed verified 2026-10-14 game_rework f9bb26ef stable a4e75b18

How a change gets into upstream NexusForever, and what makes one look like it belongs there. Upstream writes down only part of its standard: the rest lives in an editor settings file and in the habits of the code, so this page explains where each part comes from and which wins when they disagree. The rules themselves are on three reference pages: Contributing code style, Contributing code patterns and Contributing pull requests.

How upstream works

One maintainer, decisions on Discord

One person merges everything. Design questions are settled on the project Discord, and pull request threads report the outcome in a line or two ("after discussion"). So if a change needs a decision, it needs one there before it needs a pull request.

The maintainer finishes pull requests

More than half of the pull requests merged since 2024 carry commits from the maintainer on the contributor's own branch: style fixes, a missing migration, removed tests, renames to line up with the expedition branch. Small style misses get fixed for you. What holds a pull request up is behaviour: something it breaks, logic the client wouldn't use, code sitting in the wrong layer.

That's also why a pull request's Allow edits by maintainers box should stay ticked.

A codebase mid-move

game_rework is being reworked in three directions at once:

  • Static singletons are moving to dependency injection.
  • The world server is splitting into services behind a message broker, each with its own database (Server architecture).
  • Gameplay is being rebuilt on the expedition branch, to be merged back (Upstream branches).

So most patterns exist in two generations side by side, and copying the nearest example can copy the old one. The reference pages say which is which.

flowchart TD
  D[Discord: the decision] --> P[Pull request]
  P --> R[Review: behaviour, fidelity, layering]
  R --> M[Maintainer fixes style on the branch]
  M --> S[Squash into game_rework]

Where the rules come from

Four sources

Source What it covers Weight
Upstream's contributing pages Line endings, British English, braces, naming, summaries, enums, attributes, database naming and placement, bug reports Written rule. Wins over any drift in the code
Source/NexusForever.sln.DotSettings this., var, field prefixes, member order, attribute placement Applied as you type in ReSharper and Rider; other editors ignore it
The code itself Everything else: namespaces, usings, logging, message models, registration, save masks, the dependency injection region Habit. Strong where it's near universal, weak where it's split
Review history Naming against the client, layering, scope, which branch What reviewers actually push back on

There's no .editorconfig, no CONTRIBUTING file and no CI in the repository, so nothing checks any of this for you.

When the sources disagree

  1. A written rule beats the code's drift. Plenty of public methods have no summary; yours still need one.
  2. When the code is split between an old and a new pattern, follow the new one. An injected ILogger<T> over NLog's static logger, a manager registered for injection over a Singleton<T>.
  3. When the split is by project, match the project you're in. The world-side projects name private fields in plain lowerCamel; the broker services and their database projects prefix them with _. No file mixes the two.
  4. When nothing settles it, match the file you're editing.

What the fork does on top

Same surface, newer language

Code we add follows upstream's formatting closely enough that a reader can't tell where ours starts. Inside that, we use current C# where it makes the code clearer or safer: collection expressions, property patterns and is null, a record struct in place of a repeated tuple, frozen and immutable collections for indexes built once at start-up, and Stopwatch.GetElapsedTime for timings. Upstream already uses some of these, such as collection expressions, target-typed new and switch expressions; others it hasn't picked up yet.

We don't restyle code we only touch. We also leave out anything that would put work on other contributors: nullable reference types stay off, because switching them on means thousands of new warnings and annotations that everyone else has to keep.

Checked in game

A change reaches stable only after it's been seen working in game, against a prediction from the game tables (Verifying a feature). Upstream has no tests and no CI, so this is the safety net we hold ourselves to.

Branch rules

Upstream doesn't mind rebases or force-pushes on an open pull request, since it squashes most of them anyway. We do neither once a pull request is open, and put review fixes on as new commits (Taking a change upstream).

Where the code lives

Piece Path
Editor style settings Source/NexusForever.sln.DotSettings
Line-ending rule .gitattributes
Package versions Source/Directory.Packages.props
Dependency injection helpers Source/NexusForever.Shared/ServiceCollectionExtensions.cs
Upstream's written rules Contributing, code style, database style, bug submission