From 95a54040d9c83bb777738c8022e28ed894fadd28 Mon Sep 17 00:00:00 2001 From: Drefvelin <54400154+Drefvelin@users.noreply.github.com> Date: Thu, 24 Sep 2026 07:52:51 +0000 Subject: [PATCH] Remember a node's last extraction across restarts. Rare ore cycles were depositing niter and arcane crystals, but the last-cycle line lived only in memory and came back as Extracted: Nothing after a restart. Co-authored-by: Cursor --- pom.xml | 11 ++++ .../tfminecraft/dowsing/utils/Database.java | 4 ++ .../dowsing/utils/LastCycleResult.java | 59 +++++++++++++++++++ .../dowsing/utils/LastCycleResultTest.java | 45 ++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 src/main/java/net/tfminecraft/dowsing/utils/LastCycleResult.java create mode 100644 src/test/java/net/tfminecraft/dowsing/utils/LastCycleResultTest.java diff --git a/pom.xml b/pom.xml index 50c5de3..e8a3e6d 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,12 @@ + + org.junit.jupiter + junit-jupiter + 5.14.0 + test + net.tfminecraft magic @@ -130,6 +136,11 @@ maven-compiler-plugin 3.14.1 + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.4 + diff --git a/src/main/java/net/tfminecraft/dowsing/utils/Database.java b/src/main/java/net/tfminecraft/dowsing/utils/Database.java index 089a0d7..2d96d6c 100644 --- a/src/main/java/net/tfminecraft/dowsing/utils/Database.java +++ b/src/main/java/net/tfminecraft/dowsing/utils/Database.java @@ -140,6 +140,9 @@ public void loadNodes() { } setSlots(currentType, activePMs); Node n = new Node(id, b, loc, g, isActive, level, cycleTime, currentType, timeLeft, inputCounter, efficiency); + if(json.containsKey(LastCycleResult.KEY)) { + n.setLastResult(LastCycleResult.fromJson(json.get(LastCycleResult.KEY))); + } NodeManager.nodes.add(n); } catch (Exception ex) { ex.printStackTrace(); @@ -261,6 +264,7 @@ public void saveNode(Node n) { } defaults.put("active pms", pmArray); defaults.put("efficiency", n.getEfficiency()); + defaults.put(LastCycleResult.KEY, LastCycleResult.toJson(n.getLastResult())); save(file, defaults); } catch (Throwable ex) { ex.printStackTrace(); diff --git a/src/main/java/net/tfminecraft/dowsing/utils/LastCycleResult.java b/src/main/java/net/tfminecraft/dowsing/utils/LastCycleResult.java new file mode 100644 index 0000000..cb0e627 --- /dev/null +++ b/src/main/java/net/tfminecraft/dowsing/utils/LastCycleResult.java @@ -0,0 +1,59 @@ +package net.tfminecraft.dowsing.utils; + +import java.util.HashMap; +import java.util.Map; + +import org.json.simple.JSONObject; + +public final class LastCycleResult { + + public static final String KEY = "last result"; + + private LastCycleResult() {} + + @SuppressWarnings("unchecked") + public static JSONObject toJson(Map result) { + JSONObject object = new JSONObject(); + if(result == null) { + return object; + } + for(Map.Entry entry : result.entrySet()) { + if(entry.getKey() == null || entry.getKey().isBlank() || entry.getValue() == null || entry.getValue() <= 0) { + continue; + } + object.put(entry.getKey(), entry.getValue()); + } + return object; + } + + public static Map fromJson(Object raw) { + Map result = new HashMap<>(); + if(!(raw instanceof JSONObject object)) { + return result; + } + for(Object key : object.keySet()) { + if(key == null) { + continue; + } + int amount = amountOf(object.get(key)); + if(amount > 0) { + result.put(String.valueOf(key), amount); + } + } + return result; + } + + static int amountOf(Object value) { + if(value instanceof Number number) { + return number.intValue(); + } + if(value == null) { + return 0; + } + try { + return (int) Math.round(Double.parseDouble(value.toString())); + } catch (NumberFormatException ex) { + return 0; + } + } +} diff --git a/src/test/java/net/tfminecraft/dowsing/utils/LastCycleResultTest.java b/src/test/java/net/tfminecraft/dowsing/utils/LastCycleResultTest.java new file mode 100644 index 0000000..8f6b52f --- /dev/null +++ b/src/test/java/net/tfminecraft/dowsing/utils/LastCycleResultTest.java @@ -0,0 +1,45 @@ +package net.tfminecraft.dowsing.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.json.simple.JSONObject; +import org.junit.jupiter.api.Test; + +class LastCycleResultTest { + + @Test + void roundTripsExtractedItems() { + Map extracted = new LinkedHashMap<>(); + extracted.put("m.materials.niter", 1); + extracted.put("m.materials.arcane_crystal", 3); + + JSONObject stored = LastCycleResult.toJson(extracted); + Map loaded = LastCycleResult.fromJson(stored); + + assertEquals(extracted, loaded); + } + + @SuppressWarnings("unchecked") + @Test + void readsNumericAmountsWrittenByTheJsonParser() { + JSONObject stored = new JSONObject(); + stored.put("m.materials.niter", 1L); + stored.put("m.materials.arcane_crystal", 3.0d); + + Map loaded = LastCycleResult.fromJson(stored); + + assertEquals(1, loaded.get("m.materials.niter")); + assertEquals(3, loaded.get("m.materials.arcane_crystal")); + } + + @Test + void missingOrEmptyResultsStayEmpty() { + assertTrue(LastCycleResult.fromJson(null).isEmpty()); + assertTrue(LastCycleResult.toJson(Map.of()).isEmpty()); + assertTrue(LastCycleResult.toJson(null).isEmpty()); + } +}