Skip to content

Contribute to Moongate

Contributions are welcome: bug reports, fixes, features, tests, documentation, and examples. This guide explains how to prepare a contribution for review.

Search the existing issues and pull requests before starting. Every feature starts with an issue that describes it in detail, as CODE_CONVENTION.md §13.1 requires; the same goes for a change to public APIs, packet behavior or persistence formats. Bug fixes and documentation corrections can go directly into a pull request.

For a bug report, include the Moongate version or commit, operating system, reproduction steps, expected and actual behavior, and relevant logs. Include the client version for network issues. Remove credentials and personal data from anything you share. Feature requests should explain the use case and intended behavior, not just the proposed implementation.

You need Git and the .NET 10 SDK to build the solution. Package verification also requires Bash and access to nuget.org. For website work, use the Node version in website/.nvmrc; Node is not needed for the C# build.

Fork the repository on Codeberg, then create a branch from the current develop:

Terminal window
git clone https://codeberg.org/YOUR-USERNAME/moongate.git
cd moongate
git remote add upstream https://codeberg.org/moongate/moongate.git
git fetch upstream
git switch -c feature/short-description upstream/develop

Use a descriptive branch name, such as fix/packet-length or docs/plugin-guide. For running a server, see the Docker guide, including the required Ultima Online client files and first-start configuration.

Read CODE_CONVENTION.md and use the repository’s .editorconfig. They define C# style, namespaces, type placement, test organization, and public interface documentation.

Keep changes focused on one problem. Follow the surrounding code and avoid unrelated formatting or refactoring. Write documentation, public XML comments, commit messages, and pull request descriptions in English. Discuss breaking changes explicitly and explain their effect on library consumers and plugins.

Use Conventional Commits, for example:

fix(network): reject invalid packet lengths
feat(persistence): add a collection query option
docs(plugins): clarify bundle deployment

Commit messages and pull request descriptions carry no attribution of an AI tool: no Co-authored-by trailer naming a tool and no “Generated by/with” line. A pull request check refuses them. To get the same refusal when you commit, once per clone:

Terminal window
git config core.hooksPath .githooks

For a quick check while you work, run the fast suite. It needs no Docker and no variables, because it leaves out the tests in Integration, Performance and Stress namespaces or with a matching Category trait:

Terminal window
scripts/test.sh
scripts/test.sh fast --filter 'FullyQualifiedName~Serial'

scripts/test.sh all runs the full suite. Its database and Redis tests need isolated PostgreSQL and Redis servers. With Docker running, the tests start them on their own with Testcontainers: one postgres:17-alpine and one redis:7-alpine (with maxmemory-policy noeviction) per test process, removed when the run ends. The first run downloads the images.

To use servers of your own instead, as CI does, set these environment variables through your secret provider; a set variable always wins over the container:

Variable Test dependency
MOONGATE_TEST_POSTGRES_CONNECTION_STRING An administrative Npgsql connection to a disposable PostgreSQL server; the role must be able to create and drop test databases
MOONGATE_TEST_REDIS_CONNECTION_STRING A StackExchange.Redis connection string for a disposable Redis 7+ server configured with maxmemory-policy noeviction

Do not point these variables at a running shard’s databases or Redis instance. Without the variables and without Docker, the integration tests fail; they do not silently skip. Run test projects serially with -m:1, as CI does, because their hosts share the database services.

CI does not run the test suite: run it locally before a pull request and before a release. The PostgreSQL and Redis services of the release checks only serve the package verification.

Run these commands from the repository root to match the solution checks in CI:

Terminal window
dotnet restore Moongate.slnx
dotnet format style Moongate.slnx --diagnostics IDE0022 --severity warn --no-restore --verify-no-changes
dotnet build Moongate.slnx -c Release --no-restore
bash scripts/test.sh all -c Release --no-build

scripts/coverage.sh takes the same arguments, runs the tests with code coverage, and writes a merged report to artifacts/coverage/index.html. CI does not run it, so the Test coverage page of the published documentation says no report was available; run the script locally to see the numbers.

For opt-in concurrent database load tests, see Stress-test PostgreSQL persistence.

Add or update tests for changed behavior. For bug fixes, include a regression test that demonstrates the failure. Follow the test layout in CODE_CONVENTION.md and keep assertions focused on observable behavior. Prose-only corrections do not need new C# tests.

CI runs on pull requests and on pushes to develop and main; changes that touch only docs/, website/ or Markdown files skip it. Pull requests into develop and pushes to develop only restore, check the method body style and build: run the tests locally (bash scripts/test.sh all) before opening the pull request. Pull requests into main, which cut a release, and pushes to main run the release checks: the portable administration client, NuGet packages, runnable README examples, and third-party notices. Neither runs the tests: run bash scripts/test.sh all -c Release locally before a release. The development Docker image (docker.io/tgiachi/moongate:develop) is rebuilt nightly, or on demand from the Actions tab. The administration check uses the same test connections and requires Python 3 with venv support and access to install the dependencies in samples/admin-python/requirements.txt:

Terminal window
bash scripts/verify-admin-protos.sh
bash scripts/verify-packages.sh
./scripts/third-party-notices.sh
git diff --exit-code -- THIRD-PARTY-NOTICES.md

See NuGet package verification for prerequisites and troubleshooting. If a dependency change updates the notices, review and include that update in your commit so the CI comparison is clean.

For documentation or website changes, run:

Terminal window
npm --prefix website ci
npm --prefix website test
npm --prefix website run build

The build checks local links, images, and heading fragments. Edit the original Markdown sources rather than generated website files. See Writing documentation for preview commands and page registration. Library README examples retain their nuget-smoke markers so package verification can compile and run them.

  1. Commit your focused change and push the branch to your fork.
  2. Open a pull request against moongate/moongate:develop.
  3. Explain the problem, resulting behavior, relevant issue, and checks you ran. Call out compatibility changes and any checks you could not run.
  4. Address review feedback and resolve CI failures before merge. Update the documentation when behavior or configuration changes.

Keep discussions respectful and focused on the change. Maintainers review and merge contributions; opening a pull request does not require publishing packages or creating a release.

Maintainers cut releases with releaser-pleaser. On every push to main it keeps one pull request open that carries the next version, worked out from the Conventional Commit messages, the changelog and the version in Directory.Build.props and the Helm chart. Merging that pull request creates the tag and the release, and the tag publishes the archives, the Docker image, the Helm chart, the NuGet packages and the site. Until 1.0 write a breaking change as feat: and describe it in the pull request: a ! would propose 1.0.0.

The project’s license is available in LICENSE. Keep existing copyright and license notices intact; dependency attribution is recorded in THIRD-PARTY-NOTICES.md.

Made with ❤️ by Tom

Buy me a coffee