Contributing
MineGun is an open project — contributions of all kinds are welcome, from new weapon implementations to bug fixes and documentation improvements. This page covers everything you need to get set up and submit good work.
Local Setup
Clone the repo, import as a Gradle project in IntelliJ, and verify the build before making any changes.
git clone https://github.com/AbyssalNetwork/minegun.git
cd minegun
./gradlew :lib:build # compile + test the library
./gradlew :demo:installDist # build the runnable demo
ℹ
IntelliJ tip: After cloning, open the repo root as a Gradle project and let IntelliJ sync. Set the run configuration's "Use classpath of module" to demo.main — otherwise the Run button will miss Minestom and lib on the classpath. Prefer ./gradlew :demo:run for quick iteration.
Code Style
Static Interface Pattern
The core of MineGun is built on Java interfaces with static methods. This is intentional — it avoids instantiation overhead and lets weapon classes compose behaviour by implementing multiple interfaces. When adding new functionality to the library, follow the same pattern: put logic in a static method on an interface, not in a class with instance state.
public interface MySystem {
static void register() {
MinecraftServer.getGlobalEventHandler().addListener(...);
}
static void doThing(Player player) { ... }
}
public class MySystem {
private final GlobalEventHandler geh;
public MySystem(GlobalEventHandler geh) { this.geh = geh; }
}
Naming
- Interface names are PascalCase nouns —
RaycastWeapons, HealthManagement
- Static methods are camelCase verbs —
register(), givePlayer(), damage()
- Class names in the logger follow the existing lowercase-first convention —
minegunLogger — keep this consistent
- Weapon classes live in
org.vardinsdev.minegun.Weapons with a capital W — match this exactly
Logging
Use minegunLogger for all console output — never System.out.println directly outside the logger itself. Pick the right level:
| Method | Use for |
minegunLogger.info() | Registration events, lifecycle steps |
minegunLogger.success() | Successful player actions (giving weapons, kills) |
minegunLogger.warn() | Non-fatal unexpected states |
minegunLogger.error() | Failures, invalid usage (e.g. console running player-only commands) |
Formatting
- 4-space indentation, no tabs
- Opening braces on the same line
- One blank line between methods
- Keep lambda bodies inline where they fit on one line; extract to a named method if they grow beyond ~5 lines
Adding a New Weapon
All weapons follow the same structure. Create a new interface in org.vardinsdev.minegun.Weapons that extends RaycastWeapons and HealthManagement, then implement two static methods: givePlayer and register.
public interface Shotgun extends RaycastWeapons, HealthManagement {
static void givePlayer(Player player) {
ItemStack item = ItemStack.builder(Material.WOODEN_SWORD)
.set(DataComponents.CUSTOM_NAME,
Component.text("Shotgun", NamedTextColor.YELLOW))
.build();
player.setItemInHand(PlayerHand.MAIN, item);
minegunLogger.success(player.getUsername() + " has been given a Shotgun!");
}
static void register(InstanceContainer instanceContainer) {
MinecraftServer.getGlobalEventHandler()
.addListener(PlayerUseItemEvent.class, event -> {
if (event.getPlayer().getItemInMainHand().material()
!= Material.WOODEN_SWORD) return;
Player hit = RaycastWeapons.shoot(
event.getPlayer(), 500L,
instanceContainer, Particle.SMOKE, false);
if (hit != null) {
hit.damage(DamageType.ARROW, 12f);
hit.heal();
HealthManagement.damage(hit, 40);
if (HealthManagement.getHealth(hit) <= 0) {
HealthManagement.setKilledBy(hit, event.getPlayer());
}
}
});
}
}
Once the class exists, register it in your server setup alongside the other weapons:
Rifle.register(instanceContainer);
RocketLauncher.register(instanceContainer);
Shotgun.register(instanceContainer); // your new weapon
And add it to giveCommand.java in the demo so it's accessible in-game:
case "shotgun" -> Shotgun.givePlayer(player);
⚠
Choose a unique material. Each weapon uses a distinct Material as its item type — the PlayerUseItemEvent listener filters by material, so two weapons sharing the same material will both fire on every right-click. Pick something not already used by Rifle (WOODEN_HOE) or RocketLauncher (WOODEN_AXE).
Pull Request Guidelines
- One concern per PR. A new weapon, a bug fix, and a refactor should be three separate PRs — not one. This makes review faster and keeps the git history readable.
- Branch off
master. Name your branch descriptively: feat/shotgun, fix/rocket-damage, docs/health-api.
- Test with the demo first. Run
./gradlew :demo:installDist and join the server before opening a PR. If the demo breaks, the PR isn't ready.
- Keep
:lib and :demo changes separate. If your change touches both, commit the lib change first, then the demo change that uses it. This makes the dependency direction clear in the history.
- No new dependencies in
:lib without discussion. The library's only dependency is Minestom — keep it that way unless there's a strong reason. Open an issue first.
- Update the wiki. If you add a weapon, a new API method, or change a method signature, update the corresponding wiki page before the PR is merged.
Reporting Issues
Open an issue on GitHub Issues. Include:
- A short description of what went wrong and what you expected
- The Minestom version you're using (
net.minestom:minestom:X)
- Java version (
java --version)
- The relevant stack trace or console output if applicable
- A minimal reproduction — ideally a modified
Main.java that shows the problem
✓
Check the demo first. If the bug also reproduces in the unmodified demo server, include that in the report — it makes it much easier to isolate.