Write less code. Build better plugins. Support Paper & Folia.
Reduce boilerplate by 50-80% โข Unified Paper/Folia API โข Modern Fluent Design
๐ Documentation โข ๐ Quick Start โข ๐ก Examples โข ๐ค Contributing
// โ Old Way (Bukkit API) - 15+ lines
ItemStack sword = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta meta = sword.getItemMeta();
if (meta != null) {
meta.setDisplayName(ChatColor.RED + "Legendary Sword");
List<String> lore = new ArrayList<>();
lore.add(ChatColor.GRAY + "Forged in dragon fire");
meta.setLore(lore);
meta.addEnchant(Enchantment.SHARPNESS, 5, false);
meta.setUnbreakable(true);
}
sword.setItemMeta(meta);
// โ
New Way (IonAPI) - 6 lines
ItemStack sword = IonItem.builder(Material.DIAMOND_SWORD)
.name("<red>Legendary Sword")
.lore("<gray>Forged in dragon fire")
.enchant(Enchantment.SHARPNESS, 5)
.unbreakable()
.build();60% less code. 100% more readable. Fully type-safe.
| Feature | IonAPI | Bukkit API | Other Libraries |
|---|---|---|---|
| Folia Support | โ Native | โ No | |
| Async ORM | โ Built-in + Caching | โ No | |
| Economy API | โ Vault + Async | โ No | |
| Redis Support | โ Pub/Sub + KV | โ No | โ Rare |
| Hot-Reload Config | โ WatchService | โ Manual | โ Manual |
| Item Builder | โ MiniMessage | โ Varies | |
| GUI System | โ Fluent + Pagination | โ Manual | โ Varies |
| Task Chains | โ Async/Sync | ||
| Testing Framework | โ Mocks included | โ No | โ Rare |
| Learning Curve | ๐ข Low | ๐ก Medium | ๐ก Varies |
| Code Reduction | ๐ข 50-80% | - | ๐ก 30-50% |
|
|
- ๐ฐ Economy System - Vault-compatible with async API (~14 KB)
- ๐ด Redis Integration - Pub/sub messaging + KV storage (~9 KB + Lettuce)
- ๐ฅ Hot-Reload Config - Auto-reload on file changes (built-in)
- ๐ Cross-Server Messaging - Velocity/BungeeCord support (~11 KB)
- ๐ป Packet NPCs - Lightweight, zero-tick NPCs (~24 KB)
- ๐ท๏ธ PlaceholderAPI Bridge - Auto-registration (~7 KB)
- ๐ Dependency Injection - Clean architecture (~6 KB)
- ๐งช Unit Testing - Mock framework (~21 KB)
- ๐ Compatibility Layer - Java 8+ polyfills (~38 KB)
- โฑ๏ธ CooldownManager - Thread-safe player cooldowns
- ๐ฆ RateLimiter - Sliding window rate limiting
- ๐ฌ MessageBuilder - Fluent MiniMessage builder with templates
- ๐ IonScoreboard - Easy scoreboard creation
- ๐ IonBossBar - Boss bar management
- ๐ Metrics - Lightweight performance monitoring
- โก BatchOperation - 10-50x faster bulk database operations
- ๐ ReflectionCache - Cached entity metadata for ORM
Total size (all modules): ~260 KB - Smaller than most images!
๐ก Easy Shading: IonAPI is designed to be easily shadable! Just add the Shadow plugin and dependency - no complex configuration needed.
plugins {
id("com.gradleup.shadow") version "8.3.0"
}
repositories {
mavenCentral()
maven("https://jitpack.io")
}
dependencies {
// IonAPI automatically shades into your plugin!
implementation("com.github.mattbaconz:IonAPI:1.2.0")
}
tasks.shadowJar {
// โ ๏ธ IMPORTANT: Relocate to avoid conflicts!
relocate("com.ionapi", "your.plugin.libs.ionapi")
// Optional: Minimize JAR size
minimize()
}โจ That's it! IonAPI is designed to be easily shadable. Just add the dependency and Shadow plugin handles the rest!
plugins {
id 'com.gradleup.shadow' version '8.3.0'
}
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.mattbaconz:IonAPI:1.2.0'
}
shadowJar {
relocate 'com.ionapi', 'your.plugin.libs.ionapi'
}<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.mattbaconz</groupId>
<artifactId>IonAPI</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<relocations>
<relocation>
<pattern>com.ionapi</pattern>
<shadedPattern>your.plugin.libs.ionapi</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>๐ก Pro Tip: Always relocate IonAPI to avoid conflicts when multiple plugins use different versions! For detailed instructions, module sizes, and dependency graphs, see the Shading Guide.
IonAPI is designed for easy adoption with minimal configuration:
โ What you get:
- ๐ฆ Single JAR - Everything bundled together
- ๐ Conflict-free - Proper relocation prevents issues
- ๐ชถ Lightweight - Only ~300KB when shaded
- โก Fast - No runtime dependencies to load
- ๐ฏ Simple - Just add Shadow plugin and dependency
Example: Complete build.gradle.kts
plugins {
java
id("com.gradleup.shadow") version "8.3.0"
}
group = "com.example"
version = "1.0.0"
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://jitpack.io")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT")
implementation("com.github.mattbaconz:IonAPI:1.2.0")
}
tasks {
shadowJar {
archiveClassifier.set("")
relocate("com.ionapi", "${project.group}.libs.ionapi")
minimize() // Optional: Reduce JAR size
}
build {
dependsOn(shadowJar)
}
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}Build your plugin:
./gradlew shadowJar
# Your plugin JAR with IonAPI included: build/libs/YourPlugin-1.0.0.jarimport com.ionapi.api.IonPlugin;
public class MyPlugin implements IonPlugin {
@Override
public void onEnable() {
getLogger().info("๐ MyPlugin enabled!");
// Register commands
getCommandRegistry().register(new HelloCommand());
// Load config
IonConfig config = getConfigProvider().getConfig();
String message = config.getString("welcome-message");
}
@Override
public void onDisable() {
getScheduler().cancelAll();
getLogger().info("๐ MyPlugin disabled!");
}
@Override
public String getName() {
return "MyPlugin";
}
}public class HelloCommand implements IonCommand {
@Override
public boolean execute(CommandContext ctx) {
String name = ctx.getArg(0, "World");
ctx.reply("<green>Hello, <bold>" + name + "</bold>!");
return true;
}
@Override
public String getName() { return "hello"; }
@Override
public String getDescription() { return "Greets a player"; }
@Override
public String getUsage() { return "/hello [name]"; }
@Override
public String getPermission() { return "myplugin.hello"; }
}plugins {
java
id("com.gradleup.shadow") version "8.3.0"
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT")
implementation("com.github.mattbaconz:IonAPI:1.2.0")
}
tasks.shadowJar {
archiveClassifier.set("")
// โ ๏ธ CRITICAL: Always relocate to avoid conflicts
relocate("com.ionapi", "com.yourname.yourplugin.libs.ionapi")
minimize()
}./gradlew shadowJar
# Copy build/libs/YourPlugin-1.0.0.jar to server/plugins/That's it! ๐
๐ก Why relocate? Prevents conflicts when multiple plugins use IonAPI. See SHADING_GUIDE.md for details.
ItemStack sword = IonItem.builder(Material.DIAMOND_SWORD)
.name("<gradient:red:blue>Legendary Sword")
.lore(
"<gray>Forged in dragon fire",
"",
"<gold>โ Legendary Weapon"
)
.enchant(Enchantment.SHARPNESS, 5)
.enchant(Enchantment.FIRE_ASPECT, 2)
.unbreakable()
.glow()
.build();IonGui.builder()
.title("<gold><bold>โจ Shop Menu")
.rows(3)
.item(10, diamondItem, click -> {
Player player = click.getPlayer();
if (buyItem(player, 100)) {
player.sendMessage("<green>โ Purchased!");
click.close();
} else {
player.sendMessage("<red>โ Not enough money!");
}
})
.fillBorderBuilder(IonItem.of(Material.GRAY_STAINED_GLASS_PANE, " "))
.build()
.open(player);IonScoreboard.create(player)
.title("<gold><bold>โก Server Stats")
.line("<gray>โโโโโโโโโโโโโโ")
.line("")
.line("")
.dynamicLine(1, p -> "<yellow>Players: <white>" + Bukkit.getOnlinePlayers().size())
.dynamicLine(2, p -> "<green>Health: <white>" + (int) p.getHealth() + "โค")
.autoUpdate(20L) // Updates every second
.show();TaskChain.create(plugin)
.async(() -> database.loadPlayerData(uuid))
.syncAt(player, data -> {
player.setLevel(data.level);
player.sendMessage("<green>โ Data loaded!");
})
.delay(2, TimeUnit.SECONDS)
.syncAt(player, () -> player.sendMessage("<gold>Welcome back!"))
.exceptionally(ex -> player.sendMessage("<red>โ Failed to load data!"))
.execute();@Table("players")
@Cacheable(ttl = 60) // Cache for 60 seconds
public class PlayerData {
@PrimaryKey private UUID uuid;
@Column private String name;
@Column private int level;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "guild_id")
private Guild guild;
}
// Simple queries
PlayerData data = db.find(PlayerData.class, playerUuid);
data.setLevel(data.getLevel() + 1);
db.save(data);
// Async support
db.findAsync(PlayerData.class, uuid)
.thenAccept(data -> processData(data));// Check balance
IonEconomy.getBalance(player.getUniqueId()).thenAccept(balance -> {
player.sendMessage("Balance: " + IonEconomy.format(balance));
});
// Fluent transaction API
IonEconomy.transaction(player.getUniqueId())
.withdraw(100)
.reason("Shop purchase")
.commit()
.thenAccept(result -> {
if (result.isSuccess()) {
player.sendMessage("<green>Purchase complete!");
}
});
// Transfer between players
IonEconomy.transfer(sender, receiver, BigDecimal.valueOf(50));IonRedis redis = IonRedisBuilder.create()
.host("localhost")
.port(6379)
.password("secret")
.build();
// Subscribe to channel
redis.subscribe("player-events", message -> {
String data = message.getData();
Bukkit.broadcastMessage("Event: " + data);
});
// Publish message
redis.publish("player-events", "Player joined: " + player.getName());
// Key-value storage with TTL
redis.set("player:" + uuid, playerData, 3600); // Expires in 1 hourHotReloadConfig config = HotReloadConfig.create(this, "config.yml")
.onReload(cfg -> {
welcomeMessage = cfg.getString("welcome-message");
maxPlayers = cfg.getInt("max-players");
getLogger().info("Config reloaded!");
})
.start();
// Edit config.yml while server is running - changes apply instantly!| Feature | Paper | Folia |
|---|---|---|
| โก Scheduler | โ Main thread | โ Region-aware |
| ๐ฎ Commands | โ | โ |
| โ๏ธ Configuration | โ | โ |
| ๐ข Events | โ | โ |
| ๐ ๏ธ Utilities | โ | โ |
| ๐จ Item Builder | โ | โ |
| ๐ฆ GUI System | โ | โ |
| ๐ UI Components | โ | โ |
| ๐ Task Chains | โ | โ Folia-optimized |
IonAPI/
โโโ ๐ฏ ion-api/ Core API interfaces
โโโ โ๏ธ ion-core/ Base implementations
โโโ ๐จ ion-item/ Item Builder
โโโ ๐ฆ ion-gui/ GUI System
โโโ ๐ ion-ui/ Scoreboard & BossBar
โโโ ๐ ion-tasks/ Task Chains
โโโ ๐พ ion-database/ Database ORM + Caching
โโโ ๐ฐ ion-economy/ Economy API + Vault hook
โโโ ๐ด ion-redis/ Redis pub/sub + KV store
โโโ ๐ ion-proxy/ Cross-server messaging
โโโ ๐ป ion-npc/ Packet NPCs
โโโ ๐ท๏ธ ion-placeholder/ PlaceholderAPI bridge
โโโ ๐ ion-inject/ Dependency injection
โโโ ๐งช ion-test/ Testing framework
โโโ ๐ ion-compat/ Compatibility layer
โโโ ๐ฅ๏ธ platforms/ Paper & Folia implementations
We love contributions! Whether it's:
- ๐ Bug reports
- ๐ก Feature requests
- ๐ Documentation improvements
- ๐ง Code contributions
Check out our Contributing Guide to get started!
If IonAPI helps you build better plugins, consider supporting development:
Join our Discord for:
- ๐ฌ Plugin development help
- ๐ Bug reports & support
- ๐ก Feature discussions
- ๐ Community showcase
IonAPI is open source software licensed under the MIT License.
Built with โค๏ธ by mattbaconz
Special thanks to:
- ๐ฎ PaperMC - For the amazing Paper & Folia platforms
- ๐จ Adventure API - For modern text components
- ๐ All contributors - For making IonAPI better
Made with โค๏ธ for the Minecraft plugin development community
๐ Documentation โข ๐ก Examples โข ๐ Report Bug โข ๐ฌ Discord