MCExtension is a high-performance, platform-agnostic library designed for Minecraft servers. It allows developers to load external JAR files as Extensions with isolated classloading and robust dependency resolution.
- Platform Agnostic Core: Decoupled via
HostContext, supporting Spigot, Paper, Folia, and more. - Isolated Classloading: Each extension operates in its own
URLClassLoaderto eliminate dependency conflicts. - Smart Dependency Resolution: Built-in support for extension-on-extension dependencies.
- Async-First Design: Optimized for performance by keeping heavy I/O and initialization off the main server thread.
- License Verification: Native support for license validation.
- Modern Tech Stack: Built with Java 25.
MCExtension is available via GitHub Packages. Add the following to your build.gradle:
repositories {
maven {
name = "MCEngine"
url = uri("https://maven.pkg.github.com/MCEngine/mcextension")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
dependencies {
// Core Manager
implementation 'io.github.mcengine:manager:2026.0.6-4'
// Platform Implementations
implementation 'io.github.mcengine:spigotmc:2026.0.6-4'
implementation 'io.github.mcengine:papermc:2026.0.6-4'
implementation 'io.github.mcengine:foliamc:2026.0.6-4'
}Your extension entry point must implement the IMCExtension interface.
package com.example.yourname.yourextension;
import io.github.mcengine.mcextension.api.IMCExtension;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.Executor;
public class MyAwesomeExtension implements IMCExtension {
// Hardcoded URLs and version info
private static final String LICENSE_SERVER_URL = "https://auth.example.com/verify";
private static final String UPDATE_SERVER_URL = "https://api.example.com/latest-version";
private static final String CURRENT_VERSION = "1.0.0";
@Override
public boolean checkLicense(String token) {
if (token == null || token.isBlank()) {
System.out.println("[MyAwesomeExtension] License token is missing in config.yml!");
return false;
}
System.out.println("[MyAwesomeExtension] Verifying license token...");
try {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
String jsonPayload = "{\"token\":\"" + token + "\"}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(LICENSE_SERVER_URL))
.timeout(Duration.ofSeconds(10))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
if (responseBody != null && responseBody.contains("\"valid\": true")) {
System.out.println("[MyAwesomeExtension] License verified successfully!");
return true;
} else {
System.out.println("[MyAwesomeExtension] Invalid license token.");
return false;
}
} else {
System.out.println("[MyAwesomeExtension] Server responded with error code: " + response.statusCode());
return false;
}
} catch (Exception e) {
System.out.println("[MyAwesomeExtension] Failed to connect to the license server: " + e.getMessage());
return false;
}
}
@Override
public boolean checkUpdate(String url, String token) {
System.out.println("[MyAwesomeExtension] Checking for updates...");
try {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
// Using GET request to fetch the latest version string from your API
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url != null ? url : UPDATE_SERVER_URL))
.timeout(Duration.ofSeconds(10))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
// Assuming the server returns plain text like "1.0.1"
String latestVersion = response.body().trim();
// Compare versions (You can implement a more robust semantic versioning comparison here)
if (!CURRENT_VERSION.equals(latestVersion)) {
System.out.println("[MyAwesomeExtension] A new update is available: v" + latestVersion);
System.out.println("[MyAwesomeExtension] Please download it from our official website.");
return true;
} else {
System.out.println("[MyAwesomeExtension] You are running the latest version.");
return false;
}
} else {
System.out.println("[MyAwesomeExtension] Failed to check for updates. HTTP Code: " + response.statusCode());
return false;
}
} catch (Exception e) {
System.out.println("[MyAwesomeExtension] Could not reach the update server: " + e.getMessage());
return false;
}
}
@Override
public void onLoad(Object plugin, Executor executor) {
System.out.println("[MyAwesomeExtension] Extension is now fully loaded and running.");
// Execute the update check asynchronously to avoid blocking the main server thread
executor.execute(() -> {
checkUpdate(UPDATE_SERVER_URL, null);
});
// Place your regular extension initialization logic here
}
@Override
public void onDisable(Object plugin, Executor executor) {
System.out.println("[MyAwesomeExtension] Extension has been disabled.");
// Place your cleanup logic here
}
}Place this in your extension's src/main/resources folder.
name: "MyExtension"
version: "1.0.0"
# Main classes for different platforms
mains:
spigotmc: "io.github.example.SpigotMain"
papermc: "io.github.example.PaperMain"
# Extension dependencies
extension:
depend: [ "BaseExtensionID" ]To integrate MCExtension into your host plugin, use the provided HostContext or implement your own.
import io.github.mcengine.mcextension.common.MCExtensionManager;
import io.github.mcengine.mcextension.spigotmc.context.SpigotHostContext;
public class MyPlugin extends JavaPlugin {
private MCExtensionManager manager;
@Override
public void onEnable() {
// 1. Initialize Context
SpigotHostContext context = new SpigotHostContext(this);
// 2. Initialize Manager (Iteration -1 for auto-resolve, platform "spigotmc")
this.manager = new MCExtensionManager(-1, "spigotmc");
// 3. Load Extensions Asynchronously
Executor executor = Executors.newCachedThreadPool();
manager.loadAllExtensions(context, executor);
}
}Extensions can enforce licensing by providing a config.yml in their data folder:
license: "XXXX-XXXX-XXXX-XXXX"Distributed under the MIT License. See LICENSE for more information.
- jetsadawijit - GitHub Profile