Java Integration

v1.0.6

Panduan lengkap untuk mengintegrasikan NxGate ke plugin Minecraft Java Anda. Versi 1.0.6 membawa fitur hardening: offline grace period, heartbeat, retry otomatis, dan TLS pinning.

Instalasi

Maven

pom.xml
1<repositories>
2 <repository>
3 <id>github</id>
4 <url>https://maven.pkg.github.com/NoxlyDev/NxGate-Library</url>
5 </repository>
6</repositories>
7
8<dependencies>
9 <dependency>
10 <groupId>xyz.noxlydev.nxgate</groupId>
11 <artifactId>nxgate</artifactId>
12 <version>1.0.6</version>
13 </dependency>
14</dependencies>

Gradle (Kotlin DSL)

build.gradle.kts
1repositories {
2 maven {
3 url = uri("https://maven.pkg.github.com/NoxlyDev/NxGate-Library")
4 credentials {
5 username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_USERNAME")
6 password = project.findProperty("gpr.token") ?: System.getenv("GITHUB_TOKEN")
7 }
8 }
9}
10
11dependencies {
12 implementation("xyz.noxlydev.nxgate:nxgate:1.0.6")
13}

Penting: GitHub Token Required

Library NxGate di-host di GitHub Packages. Anda HARUS menambahkan GitHub token dengan scope read:packages di file ~/.m2/settings.xml.

~/.m2/settings.xml
1<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
4 http://maven.apache.org/xsd/settings-1.0.0.xsd">
5 <servers>
6 <server>
7 <id>github</id>
8 <username>YOUR_GITHUB_USERNAME</username>
9 <password>YOUR_GITHUB_TOKEN</password>
10 </server>
11 </servers>
12</settings>

Penggunaan Dasar

Berikut contoh implementasi paling sederhana — cocok untuk MVP atau plugin internal:

1. Konfigurasi config.yml

config.yml
1# NxGate License Configuration
2license-key: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

2. Main.java

Main.java
1import xyz.noxlydev.nxgate.NxGate;
2import org.bukkit.plugin.java.JavaPlugin;
3
4public class Main extends JavaPlugin {
5
6 private static final String USER_ID = "YOUR_USER_ID";
7 private static final String PUBLIC_KEY =
8 "-----BEGIN PUBLIC KEY-----\n" +
9 "<PUBLIC_KEY_RSA_BASE64>\n" +
10 "-----END PUBLIC KEY-----";
11
12 @Override
13 public void onEnable() {
14 saveDefaultConfig();
15 String licenseKey = getConfig().getString("license-key");
16
17 // 2-arg constructor mengaktifkan RSA challenge otomatis
18 NxGate.ValidationType result = new NxGate(USER_ID, PUBLIC_KEY)
19 .setValidationServer("https://api.nxgate.noxlydev.xyz")
20 .verify(licenseKey, "MyPlugin");
21
22 if (result == NxGate.ValidationType.VALID) {
23 getLogger().info("License verified successfully!");
24 } else {
25 getLogger().severe("License verification failed: " + result);
26 getServer().getPluginManager().disablePlugin(this);
27 }
28 }
29}

Catatan

  • Constructor 2 argumen new NxGate(userId, publicKey) otomatis mengaktifkan RSA challenge.
  • Default server URL: https://api.nxgate.noxlydev.xyz — boleh tidak di-set.
  • Method verify(key, scope) return NxGate.ValidationType.
  • Untuk production, gunakan HardenedNxGate (lihat section bawah) — bukan NxGate mentah.

Penggunaan Advanced (Builder Methods)

Class NxGate di v1.0.6 menambah builder method baru untuk retry, timeout, metadata augmentation, dan TLS pinning:

.withRetry(attempts, backoffMs)

Retry exponential backoff (factor 3) saat CONNECTION_ERROR / SERVER_ERROR.

.withTimeout(connectMs, readMs)

HTTP timeout. Default 10s/10s. Lebih kecil = gagal lebih cepat.

.withMetadataAugmentation(true)

Tambahkan machineFp + jarFp ke metadata untuk tamper detection.

.withTlsPinning(List<String>)

Pin SHA-256 sertifikat server. Kebal MITM (Burp/mitmproxy).

Main.java (Advanced)
1import xyz.noxlydev.nxgate.NxGate;
2import org.bukkit.plugin.java.JavaPlugin;
3import java.util.List;
4
5public class Main extends JavaPlugin {
6
7 @Override
8 public void onEnable() {
9 saveDefaultConfig();
10 String licenseKey = getConfig().getString("license-key");
11
12 NxGate.ValidationType result = new NxGate(USER_ID, PUBLIC_KEY)
13 .setValidationServer("https://api.nxgate.noxlydev.xyz")
14 .withRetry(3, 1000) // retry 3x, backoff awal 1s (exponential)
15 .withTimeout(8000, 8000) // connect 8s, read 8s
16 .withMetadataAugmentation(true) // kirim machineFp + jarFp ke server
17 // .withTlsPinning(List.of("a1b2c3...")) // optional: pin SHA-256 cert
18 .debug() // print log request/response (dev only)
19 .verify(licenseKey, "MyPlugin");
20
21 switch (result) {
22 case VALID:
23 getLogger().info("License OK");
24 break;
25 case NOT_FOUND:
26 getLogger().severe("License key tidak ditemukan");
27 disablePlugin();
28 break;
29 case NOT_ACTIVE:
30 getLogger().severe("License belum diaktifkan");
31 disablePlugin();
32 break;
33 case EXPIRED:
34 getLogger().severe("License sudah expired");
35 disablePlugin();
36 break;
37 case LICENSE_SCOPE_FAILED:
38 getLogger().severe("Scope tidak cocok dengan dashboard");
39 disablePlugin();
40 break;
41 case IP_LIMIT_EXCEEDED:
42 getLogger().severe("Batas IP terlampaui");
43 disablePlugin();
44 break;
45 case RATE_LIMIT_EXCEEDED:
46 getLogger().warning("Rate limit, coba lagi nanti");
47 disablePlugin();
48 break;
49 case FAILED_CHALLENGE:
50 getLogger().severe("RSA challenge gagal — public key salah?");
51 disablePlugin();
52 break;
53 case CONNECTION_ERROR:
54 getLogger().severe("Tidak dapat terhubung ke server NxGate");
55 disablePlugin();
56 break;
57 case SERVER_ERROR:
58 getLogger().severe("Server NxGate mengembalikan response invalid");
59 disablePlugin();
60 break;
61 }
62 }
63
64 private void disablePlugin() {
65 getServer().getPluginManager().disablePlugin(this);
66 }
67}

HardenedNxGate (Production-Ready) ⭐

Direkomendasikan untuk semua plugin production

HardenedNxGate.Builder menggabungkan retry, timeout, RSA challenge, offline grace cache (HMAC-signed, machine-bound), dan background heartbeat dalam satu API yang siap pakai.

Offline Grace Period

Plugin tetap jalan hingga 24 jam (configurable) saat API NxGate tidak bisa dihubungi.

Background Heartbeat

Re-verify license tiap N jam. License yang dicabut langsung trigger callback.

Tamper-Resistant Cache

Cache HMAC-SHA256 di-bind ke machine + JAR fingerprint. Tidak bisa dipindah/diedit.

Main.java (Hardened)
1import xyz.noxlydev.nxgate.HardenedNxGate;
2import xyz.noxlydev.nxgate.NxGate;
3import org.bukkit.Bukkit;
4import org.bukkit.plugin.java.JavaPlugin;
5import java.util.concurrent.TimeUnit;
6
7public class Main extends JavaPlugin {
8
9 private HardenedNxGate gate;
10
11 @Override
12 public void onEnable() {
13 saveDefaultConfig();
14
15 gate = new HardenedNxGate.Builder()
16 .userId("YOUR_USER_ID")
17 .publicKey(PUBLIC_KEY)
18 .scope("MyPlugin")
19 .serverUrl("https://api.nxgate.noxlydev.xyz")
20 .cacheDir(getDataFolder()) // file .nxgate-cache disimpan di sini
21 .gracePeriod(24, TimeUnit.HOURS) // berapa lama plugin tetap jalan saat offline
22 .heartbeat(6, TimeUnit.HOURS) // re-verify periodik
23 .retry(3, 1000) // 3 attempt, backoff 1s
24 .logger(getLogger())
25 .onRevoked(reason -> {
26 // Dipanggil saat heartbeat mendeteksi license dicabut/expired
27 getLogger().severe("License revoked: " + reason);
28 Bukkit.getScheduler().runTask(this,
29 () -> Bukkit.getPluginManager().disablePlugin(this));
30 })
31 .build();
32
33 boolean ok = gate.verifyBlocking(getConfig().getString("license-key"));
34 if (!ok) {
35 getLogger().severe("License INVALID (" + gate.getLastResult() + ")");
36 Bukkit.getPluginManager().disablePlugin(this);
37 return;
38 }
39
40 // Mulai background heartbeat — re-verify tiap 6 jam (configurable)
41 gate.startHeartbeat(() -> getConfig().getString("license-key"));
42
43 // ... register commands, listeners, dll
44 }
45
46 @Override
47 public void onDisable() {
48 if (gate != null) gate.shutdown(); // hentikan thread heartbeat
49 }
50}

Builder Options

MethodDefaultDeskripsi
.userId(String)User ID dari dashboard NxGate. Wajib.
.publicKey(String)Public RSA key (PEM) untuk verify challenge.
.scope(String)nullScope untuk membatasi license per produk.
.serverUrl(String)api.nxgate.noxlydev.xyzURL API NxGate.
.cacheDir(File).Folder penyimpanan file .nxgate-cache.
.gracePeriod(n, unit)24 HOURSBerapa lama cache valid saat offline.
.heartbeat(n, unit)6 HOURSInterval re-verify periodik.
.retry(attempts, ms)3, 1000Retry + initial backoff (exponential).
.logger(Logger)java.util.loggingLogger plugin (biasanya getLogger()).
.onRevoked(Consumer)nullCallback saat heartbeat deteksi license dicabut.
.tlsPins(List)nullSHA-256 pin sertifikat server (optional).

Wajib panggil shutdown() di onDisable

Heartbeat berjalan di daemon thread. Tanpa gate.shutdown(), thread akan tetap hidup saat plugin di-reload dan menyebabkan thread leak.

Fingerprints Utility

Class Fingerprints baru di v1.0.6 menyediakan hash stabil untuk identifikasi mesin & integritas JAR. Otomatis dipakai oleh HardenedNxGate, tapi bisa juga dipakai langsung:

Fingerprints.java
1import xyz.noxlydev.nxgate.Fingerprints;
2
3// Hash 32-char dari OS + hostname + MAC address
4String machineFp = Fingerprints.machine();
5
6// Hash SHA-256 dari JAR yang me-load class ini (deteksi tampering)
7String jarFp = Fingerprints.callerJar();
8
9getLogger().info("Machine: " + machineFp);
10getLogger().info("Plugin JAR: " + jarFp);
11
12// Hasil sudah di-cache setelah panggilan pertama, aman dipanggil dari thread mana pun.

Fingerprints.machine()

SHA-256 (32 hex char) dari kombinasi os.name + os.arch + hostname + first non-loopback MAC. Stabil per-mesin.

Fingerprints.callerJar()

SHA-256 (32 hex char) dari isi JAR yang me-load class. Berubah jika JAR dimodifikasi/re-packaged.

Shade & Relocate (Wajib)

Selalu shade + relocate

Library xyz.noxlydev.nxgate dan dependency org.json harus di-shade & relocate untuk menghindari konflik dengan plugin lain yang pakai library/versi berbeda.

Maven (maven-shade-plugin)

pom.xml
1<build>
2 <plugins>
3 <plugin>
4 <groupId>org.apache.maven.plugins</groupId>
5 <artifactId>maven-shade-plugin</artifactId>
6 <version>3.5.1</version>
7 <executions>
8 <execution>
9 <phase>package</phase>
10 <goals><goal>shade</goal></goals>
11 <configuration>
12 <relocations>
13 <relocation>
14 <pattern>xyz.noxlydev.nxgate</pattern>
15 <shadedPattern>your.plugin.package.libs.nxgate</shadedPattern>
16 </relocation>
17 <relocation>
18 <pattern>org.json</pattern>
19 <shadedPattern>your.plugin.package.libs.json</shadedPattern>
20 </relocation>
21 </relocations>
22 </configuration>
23 </execution>
24 </executions>
25 </plugin>
26 </plugins>
27</build>

Gradle (Shadow plugin)

build.gradle.kts
1plugins {
2 id("com.gradleup.shadow") version "8.3.5"
3}
4
5tasks.shadowJar {
6 relocate("xyz.noxlydev.nxgate", "your.plugin.package.libs.nxgate")
7 relocate("org.json", "your.plugin.package.libs.json")
8}

Migrasi dari 1.0.4 ←' 1.0.6

100% backwards compatible — kode lama tetap jalan tanpa perubahan. Yang perlu dilakukan:

  1. Ganti versi di pom.xml / build.gradle.kts dari 1.0.4 ke 1.0.6.
  2. (Opsional) Tambahkan .withRetry(3, 1000) dan .withTimeout(8000, 8000) untuk ketahanan.
  3. (Direkomendasikan) Migrasi ke HardenedNxGate.Builder untuk dukungan offline + heartbeat.
  4. Tambah relocate org.json di shade config.

Langkah Selanjutnya