Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/magefree/mage/llms.txt

Use this file to discover all available pages before exploring further.

XMage is an open-source project that welcomes contributions of every kind — new card implementations, bug fixes, game-rule corrections, AI improvements, and UI enhancements. All changes flow through GitHub pull requests against the master branch. The workflow below covers everything from forking the repository to getting a PR merged, including how to write and run tests and what CI checks must pass.
The XMage wiki has dedicated pages that supplement this guide: Setting up your Development Environment, Development Testing Tools, Development Workflow, and Development HOWTO Guides. These pages contain additional detail on server configuration, cheat commands, and card-implementation patterns.

Contribution Flow

1
Fork the repository and clone your fork
2
On GitHub, click Fork on magefree/mage to create your own copy. Then clone it locally:
3
git clone https://github.com/<your-username>/mage.git
cd mage
4
Add the upstream remote so you can keep your fork in sync:
5
git remote add upstream https://github.com/magefree/mage.git
6
Create a feature or fix branch
7
Always work on a dedicated branch — never commit directly to master. Use a short, descriptive name that indicates what the branch contains:
8
# For a new card implementation
git checkout -b feat/lightning-bolt

# For a bug fix
git checkout -b fix/lifelink-interaction

# For a game-rule correction
git checkout -b fix/commander-damage-tracking
9
Build the project and confirm a clean baseline
10
Before making any changes, verify the project builds cleanly and all tests pass on your machine:
11
mvn install -DskipTests   # fast build — confirms compilation
mvn test                  # runs the full Mage.Tests suite
12
If any tests fail before you make a change, note them — they are pre-existing failures unrelated to your work.
13
Implement your change
14
Depending on the type of contribution, the relevant modules differ:
15
Change typePrimary module(s)New card implementationMage.Sets/src/mage/cards/<letter>/New expansion setMage.Sets/src/mage/sets/Game-rule bug fixMage/src/main/java/mage/Server-side fixMage.Server/src/main/java/mage/server/Client UI fixMage.Client/src/main/java/mage/client/New game modeMage.Server.Plugins/Mage.Game.<Name>/New AI behaviorMage.Server.Plugins/Mage.Player.AI*/
16
For card implementations, the Utils/gen-card.pl script scaffolds the boilerplate Java class from a template:
17
cd Utils
perl gen-card.pl
# Enter the card name when prompted
18
The generated file lands in the correct Mage.Sets/src/mage/cards/<letter>/ directory, ready for you to fill in abilities and effects.
19
Write or update tests in Mage.Tests/
20
Every non-trivial change should be accompanied by a JUnit test. Tests live in:
21
Mage.Tests/src/test/java/org/mage/test/
22
Organized by category:
23
cards/abilities/   — keyword and activated ability tests
cards/continuous/  — layer and continuous-effect tests
cards/damage/      — damage prevention and assignment tests
combat/            — attack and block tests
commander/         — commander-format rule tests
game/              — general game-scenario tests
player/            — player-action tests
24
A minimal test class looks like this:
25
package org.mage.test.cards.abilities;

import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;

public class LightningBoltTest extends CardTestPlayerBase {

    @Test
    public void testDealsDamageToPlayer() {
        addCard(Zone.HAND, playerA, "Lightning Bolt");
        addCard(Zone.BATTLEFIELD, playerA, "Mountain");

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);

        setStopAt(1, PhaseStep.END_TURN);
        execute();

        assertLife(playerB, 17);
    }
}
26
Run the full test suite
27
Before pushing, confirm your changes do not break existing tests:
28
mvn test
29
To run only the tests in a specific class during development:
30
mvn test -pl Mage.Tests -Dtest=LightningBoltTest
31
To run a single test method:
32
mvn test -pl Mage.Tests -Dtest=LightningBoltTest#testDealsDamageToPlayer
33
Commit with a descriptive message
34
Use a clear, imperative commit message that summarizes what the commit does and — for card implementations — includes the card name:
35
git add Mage.Sets/src/mage/cards/l/LightningBolt.java \
        Mage.Tests/src/test/java/org/mage/test/cards/abilities/LightningBoltTest.java
git commit -m "Implement Lightning Bolt (deals 3 damage to any target)"
36
For bug fixes, reference the GitHub issue number:
37
git commit -m "Fix lifelink not applying to combat damage from tokens (#9876)"
38
Push your branch and open a pull request
39
Push the branch to your fork:
40
git push origin feat/lightning-bolt
41
Then open a pull request on GitHub:
42
  • Go to https://github.com/magefree/mage/pulls.
  • Click New pull requestcompare across forks.
  • Select your fork and branch as the head, and magefree/mage master as the base.
  • Write a description explaining what the PR does and, if it fixes a bug, how to reproduce the original issue.
  • 43
    CI checks must pass
    44
    XMage uses GitHub Actions (.github/workflows/maven.yml). On every push and pull request targeting master, the workflow:
    45
  • Checks out the code.
  • Sets up JDK 17 (Temurin distribution).
  • Runs mvn test -B with a custom log4j.properties for CI-friendly output.
  • 46
    # From .github/workflows/maven.yml
    - name: Set up JDK 17
      uses: actions/setup-java@v5
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: maven
    - name: Build with Maven
      run: mvn test -B -Dxmage.dataCollectors.printGameLogs=false \
             -Dlog4j.configuration=file:${GITHUB_WORKSPACE}/.travis/log4j.properties
    
    47
    PRs are not merged until the Actions build shows a green checkmark. Fix any failures before requesting a review.

    Keeping Your Fork in Sync

    Before starting new work, sync your local master with upstream to avoid merge conflicts:
    git fetch upstream
    git checkout master
    git merge upstream/master
    git push origin master
    
    Then create your branch from the updated master.

    Bug Reports

    Report bugs via GitHub Issues. A useful bug report includes:
    • XMage version (shown in the title bar of the client)
    • Operating system and Java version (java -version)
    • Steps to reproduce — ideally a minimal deck and sequence of plays
    • Expected behavior vs. actual behavior
    • Screenshots or log output if relevant
    The project also maintains a FAQ label for common problems and their solutions.

    Feature Requests

    Feature requests are also filed as GitHub Issues. When requesting a new feature:
    • Describe the desired behavior and the use case it serves.
    • If it relates to a specific card or game rule, cite the card name and ruling.
    • Check existing issues first to avoid duplicates.

    Code Style

    XMage follows the Java conventions already established in the codebase:
    • Indentation: 4 spaces (no tabs).
    • Braces: on the same line as control statements (K&R style), always present even for single-statement blocks.
    • Naming: camelCase for methods and variables; PascalCase for class names; UPPER_SNAKE_CASE for constants.
    • Card classes: one public class per file, class name matches the card name with all non-alphanumeric characters removed (e.g., "Swords to Plowshares"SwordsToPlowshares.java).
    • No wildcard imports: prefer explicit import statements.
    The Maven build uses maven-compiler-plugin 3.8.1 with useIncrementalCompilation=false. There is no enforced checkstyle plugin in the root POM, but reviewers will flag style deviations in the PR.
    Start a local server with -Dxmage.testMode=true (or the -testMode=true argument) to quickly verify card interactions without needing to authenticate. Test mode enables cheat commands in the client — you can add any card to your hand, set life totals, skip to a specific phase, and more. This is the fastest way to confirm that a new card ability works correctly before writing a formal JUnit test. See the Development Testing Tools wiki page for the full list of available commands.

    Community Channels

    Build docs developers (and LLMs) love