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.
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).
RagnaWeave liegt in einem eigenen Maven-Repo: https://ragnasoftware.net/maven.
Repo registrieren, Plugin anwenden, Plattform wählen — fertig (JDK 21).
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/")
}
}
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")
}
plugins {
id("fabric-loom") version "1.7.4"
id("net.ragnasoftware.ragnaweave") version "0.4.0"
}
ragnaweave { platform = "fabric" }
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).
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());
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));
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
}
Für Rückgabewerte und Verkettung.
RagnaWeave.supplyAsync(() -> compute()) // Worker
.thenOnMain(result -> apply(result)) // Server-Thread
.exceptionallyOnMain(err -> log(err));
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(...).
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() { ... }
@OffThread | void-Methode → läuft auf dem Worker-Pool (fire-and-forget). |
@MainThread | void-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. |
WeaveConfig | Pool-Größe / Fehler-Handler konfigurieren. |
@OffThread/@MainThread
auf void (fire-and-forget) oder WeaveFuture<T> (mit Ergebnis).
Thread-Sicherheit des Rumpfs liegt beim Entwickler: MC-APIs nur via onMain(...).Empfohlen: über Gradle (siehe Installation). Direkte Artefakte (v0.4.0):
| Modul | Zweck | JAR |
|---|---|---|
| annotations | @OffThread, @MainThread |
jar · sources |
| core | Engine (Worker-Pool, WeaveFuture) | jar · sources |
| paper | Adapter (BukkitScheduler) | jar |
| fabric | Adapter (MinecraftServer) | jar |
| neoforge | Adapter (NeoForge) | jar |
| gradle-plugin | Bytecode-Transformation | jar |
Maven-Koordinaten: net.ragnasoftware.ragnaweave:<modul>:0.4.0
· Plugin-Id: net.ragnasoftware.ragnaweave · Repo: https://ragnasoftware.net/maven