DevTools · v0.4.0

RagnaWeave

Einfaches Multithreading für Minecraft-Mods & -Plugins. Annotieren statt fummeln — der Build verlagert schwere Arbeit auf Worker-Threads und bringt das Ergebnis sicher zurück auf den Server-Tick.

Download Installation Tutorial
Was es istInstallationTutorial APIDownload

Was es ist

Minecraft-Spiellogik läuft single-threaded auf dem Server-/Main-Thread; die meisten APIs darfst du nicht von Fremd-Threads aufrufen. RagnaWeave nimmt dir die fehleranfällige Handarbeit ab: Du annotierst eine Methode, der Gradle-Build webt sie zur Build-Zeit (Bytecode) um — der Rumpf läuft auf einem Worker-Pool, und RagnaWeave.onMain(...) bringt Ergebnisse sicher auf den nächsten Tick.

@OffThread
public void generateData(Chunk chunk) {
    var heavy = expensiveComputation(chunk);      // läuft auf einem Worker-Thread
    RagnaWeave.onMain(() -> applyToWorld(heavy));  // sicher zurück auf den Server-Thread
}

Der Aufruf generateData(chunk) kehrt sofort zurück — der Tick wird nicht blockiert. Funktioniert auf Fabric (Mods) und Paper/Bukkit (Plugins).

Installation (Gradle)

RagnaWeave liegt in einem eigenen Maven-Repo: https://ragnasoftware.net/maven. Repo registrieren, Plugin anwenden, Plattform wählen — fertig (JDK 21).

settings.gradle.kts

pluginManagement {
    repositories {
        maven("https://ragnasoftware.net/maven")   // RagnaWeave-Plugin
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositories {
        mavenCentral()
        maven("https://ragnasoftware.net/maven")   // RagnaWeave-Runtime (core + Adapter)
        // Paper: maven("https://repo.papermc.io/repository/maven-public/")
    }
}

build.gradle.kts — Paper-Plugin

plugins {
    java
    id("net.ragnasoftware.ragnaweave") version "0.4.0"
}
ragnaweave { platform = "paper" }   // fügt core + paper-Adapter automatisch hinzu

dependencies {
    compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
}

build.gradle.kts — Fabric-Mod (Loom)

plugins {
    id("fabric-loom") version "1.7.4"
    id("net.ragnasoftware.ragnaweave") version "0.4.0"
}
ragnaweave { platform = "fabric" }

build.gradle.kts — NeoForge-Mod (ModDevGradle)

plugins {
    id("net.neoforged.moddev") version "2.0.78"
    id("net.ragnasoftware.ragnaweave") version "0.4.0"
}
neoForge { version = "21.1.93" }
ragnaweave { platform = "neoforge" }

Das Plugin ergänzt die Runtime automatisch und verdrahtet die Bytecode- Transformation in den Build (läuft vor jar/remapJar).

Tutorial

1. RagnaWeave starten

Einmalig beim Start initialisieren, beim Stop herunterfahren.

// Paper-Plugin
public final class MyPlugin extends JavaPlugin {
    @Override public void onEnable()  { RagnaWeavePaper.enable(this); }
    @Override public void onDisable() { RagnaWeavePaper.disable(); }
}

// Fabric-Mod (mit Fabric-API)
ServerLifecycleEvents.SERVER_STARTING.register(RagnaWeaveFabric::enable);
ServerLifecycleEvents.SERVER_STOPPED.register(s -> RagnaWeaveFabric.disable());

2. @OffThread — Arbeit auslagern

Auf void-Methoden. Der Rumpf läuft auf einem Worker; der Aufruf kehrt sofort zurück. MC-APIs nur über onMain(...) anfassen.

@OffThread
public void crunch(int n) {
    long sum = 0;
    for (int i = 0; i < n; i++) sum += i;          // Worker-Thread
    final long total = sum;
    RagnaWeave.onMain(() ->                          // zurück auf den Tick
        getServer().broadcast(Component.text("Fertig: " + total)));
}

Mit Rückgabewert (ab 0.4.0): die Methode als WeaveFuture<T> deklarieren, im Rumpf das Ergebnis liefern. Der Aufrufer bekommt sofort ein Future und macht .thenOnMain(...).

@OffThread
public WeaveFuture<Long> sumChunk(Chunk c) {
    long total = heavyCompute(c);             // Worker-Thread
    return RagnaWeave.completed(total);
}

// Aufruf:
sumChunk(c).thenOnMain(total -> applyToWorld(total));

3. @MainThread — garantiert auf dem Tick

Egal von welchem Thread aufgerufen — der Rumpf läuft auf dem Server-Thread.

@MainThread
public void spawn(Location loc) {
    world.spawnEntity(loc, EntityType.ZOMBIE);   // immer thread-sicher
}

4. Explizite API (ohne Annotationen)

Für Rückgabewerte und Verkettung.

RagnaWeave.supplyAsync(() -> compute())        // Worker
          .thenOnMain(result -> apply(result))  // Server-Thread
          .exceptionallyOnMain(err -> log(err));

5. Parallel-Verarbeitung neu in 0.4.0

Eine ganze Collection über den Worker-Pool verarbeiten — Ergebnisse kommen gesammelt (in Eingabe-Reihenfolge) sicher zurück auf den Tick.

// Map: pro Element ein Ergebnis, Reihenfolge bleibt erhalten
RagnaWeave.parallelMap(chunks, c -> heavyCompute(c))   // alle Worker parallel
          .thenOnMain(results -> results.forEach(this::applyToWorld));

// ForEach: nur Seiteneffekte, ohne Ergebnis
RagnaWeave.parallelForEach(players, p -> precomputeFor(p))
          .thenOnMain(v -> getLogger().info("fertig"));

Wirft ein Element eine Ausnahme, greift exceptionallyOnMain(...).

6. Drosseln, Entprellen, Messen neu in 0.4.0

Drei Annotationen für teure/häufige Tick-Operationen (nur auf void-Methoden):

@Throttle(250)   // läuft höchstens alle 250 ms — Aufrufe dazwischen werden verworfen
public void recalcLighting() { ... }

@Debounce(2000)  // erst 2 s nach dem LETZTEN Aufruf (z. B. „speichern nach Ruhe")
public void saveConfig() { RagnaWeave.onMain(() -> writeToDisk()); }

@Timed           // misst die Dauer, warnt wenn > 50 ms (1 Tick); @Timed(10) für strenger
public void onWorldTick() { ... }

API & Hinweise

@OffThreadvoid-Methode → läuft auf dem Worker-Pool (fire-and-forget).
@MainThreadvoid-Methode → läuft garantiert auf dem Main-/Server-Thread.
@Throttle(ms)void-Methode → höchstens einmal pro ms (leading-edge).
@Debounce(ms)void-Methode → erst ms nach dem letzten Aufruf (trailing-edge).
@Timed(ms?)void-Methode → misst Dauer, warnt über der Schwelle (Default 50 ms).
RagnaWeave.onMain(Runnable)Aufgabe sicher auf dem Tick ausführen.
RagnaWeave.supplyAsync(Callable)Worker-Berechnung mit Ergebnis als WeaveFuture.
RagnaWeave.parallelMap(items, fn)Collection parallel verarbeiten → WeaveFuture<List> (Reihenfolge erhalten).
RagnaWeave.parallelForEach(items, fn)Collection parallel verarbeiten (ohne Ergebnis).
RagnaWeave.completed(value)Fertiges WeaveFuture<T> — für Ergebnis-Rückgabe aus @OffThread-Methoden.
WeaveFuture.thenOnMain(Consumer)Callback auf dem Server-Thread.
WeaveConfigPool-Größe / Fehler-Handler konfigurieren.
Wichtig: @OffThread/@MainThread auf void (fire-and-forget) oder WeaveFuture<T> (mit Ergebnis). Thread-Sicherheit des Rumpfs liegt beim Entwickler: MC-APIs nur via onMain(...).

Download

Empfohlen: über Gradle (siehe Installation). Direkte Artefakte (v0.4.0):

ModulZweckJAR
annotations@OffThread, @MainThread jar · sources
coreEngine (Worker-Pool, WeaveFuture) jar · sources
paperAdapter (BukkitScheduler) jar
fabricAdapter (MinecraftServer) jar
neoforgeAdapter (NeoForge) jar
gradle-pluginBytecode-Transformation jar

Maven-Koordinaten: net.ragnasoftware.ragnaweave:<modul>:0.4.0 · Plugin-Id: net.ragnasoftware.ragnaweave · Repo: https://ragnasoftware.net/maven