Contribute to Moongate
Contributions are welcome: bug reports, fixes, features, tests, documentation, and examples. This guide explains how to prepare a contribution for review.
Before you start
Section titled “Before you start”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.
Set up your checkout
Section titled “Set up your checkout”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:
git clone https://codeberg.org/YOUR-USERNAME/moongate.gitcd moongategit remote add upstream https://codeberg.org/moongate/moongate.gitgit fetch upstreamgit switch -c feature/short-description upstream/developUse 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.
Follow the project conventions
Section titled “Follow the project conventions”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 lengthsfeat(persistence): add a collection query optiondocs(plugins): clarify bundle deploymentCommit 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:
git config core.hooksPath .githooksVerify your changes
Section titled “Verify your changes”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:
scripts/test.shscripts/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:
dotnet restore Moongate.slnxdotnet format style Moongate.slnx --diagnostics IDE0022 --severity warn --no-restore --verify-no-changesdotnet build Moongate.slnx -c Release --no-restorebash scripts/test.sh all -c Release --no-buildscripts/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:
bash scripts/verify-admin-protos.shbash scripts/verify-packages.sh./scripts/third-party-notices.shgit diff --exit-code -- THIRD-PARTY-NOTICES.mdSee 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:
npm --prefix website cinpm --prefix website testnpm --prefix website run buildThe 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.
Submit a pull request
Section titled “Submit a pull request”- Commit your focused change and push the branch to your fork.
- Open a pull request against
moongate/moongate:develop. - Explain the problem, resulting behavior, relevant issue, and checks you ran. Call out compatibility changes and any checks you could not run.
- 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
