Skip to content

Commit 60a5916

Browse files
committed
Fix for error with low-power enchants.
The issue was introduced in v1.2, across all MC versions. When there are sufficiently few bookshelves, the power level can be low enough that no enchant is selected for a slot. The mod had a regression where this wasn't handled properly. Also, updated tests to cover this case.
1 parent 1801011 commit 60a5916

File tree

2 files changed

+81
-49
lines changed

2 files changed

+81
-49
lines changed

java/io/github/d0sboots/enchantmentrevealer/EnchantmentWorker.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,10 @@ static boolean testEnchants(Random rand, int seed, Observation observation,
434434
List<EnchantmentData> list = buildEnchantmentList(rand, seed, observation, i);
435435
tempEnchantmentData[i] = list;
436436
if (list.isEmpty()) {
437-
// Real enchant has something for this slot, but we found nothing.
437+
if (observation.enchants[i] == -1) {
438+
// Nothing at the observed slot, so it matches.
439+
continue;
440+
}
438441
return false;
439442
}
440443
EnchantmentData data = list.get(rand.nextInt(list.size()));
@@ -455,7 +458,7 @@ static boolean testEnchantFast(Random rand, int seed, Observation observation, b
455458
return true; // Always matches
456459
}
457460
if (enchantability <= 0) {
458-
return false; // There's supposed to be an effect, but we can never find one.
461+
return observation.enchants[index] == -1;
459462
}
460463
rand.setSeed(seed + index);
461464

@@ -484,8 +487,7 @@ static boolean testEnchantFast(Random rand, int seed, Observation observation, b
484487
}
485488
tempEnchantmentData[index] = list;
486489
if (list.isEmpty()) {
487-
// Real enchant has something for this slot, but we found nothing.
488-
return false;
490+
return observation.enchants[index] == -1;
489491
}
490492
EnchantmentData data = list.get(rand.nextInt(list.size()));
491493
return target == data.enchantmentobj &&

test/java/io/github/d0sboots/enchantmentrevealer/EnchantmentWorkerTest.java

+75-45
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import net.minecraft.enchantment.Enchantment;
2828
import net.minecraft.enchantment.EnchantmentData;
2929
import net.minecraft.init.Bootstrap;
30-
import net.minecraft.item.Item;
30+
import net.minecraft.init.Items;
3131
import net.minecraft.item.ItemStack;
3232

3333
public class EnchantmentWorkerTest {
@@ -44,6 +44,7 @@ public class EnchantmentWorkerTest {
4444
}
4545
}
4646

47+
// A generic test observation
4748
private static Observation getTestObservation() {
4849
Observation observation = new Observation();
4950
observation.truncatedSeed = 0x2340;
@@ -57,7 +58,26 @@ private static Observation getTestObservation() {
5758
observation.enchantLevels[1] = 2;
5859
observation.enchants[2] = 1; // Fire Protection
5960
observation.enchantLevels[2] = 2;
60-
observation.item = new ItemStack(Item.getByNameOrId("diamond_leggings"));
61+
observation.item = new ItemStack(Items.diamond_leggings);
62+
return observation;
63+
}
64+
65+
// An observation at low power that has two empty slots, which has exposed bugs
66+
// in the past.
67+
private static Observation getWeakObservation() {
68+
Observation observation = new Observation();
69+
observation.truncatedSeed = 0x08e0;
70+
observation.power = 0;
71+
observation.levels[0] = 1;
72+
observation.levels[1] = 3;
73+
observation.levels[2] = 4;
74+
observation.enchants[0] = -1;
75+
observation.enchantLevels[0] = 0;
76+
observation.enchants[1] = -1;
77+
observation.enchantLevels[1] = 0;
78+
observation.enchants[2] = 0x22; // Unbreaking
79+
observation.enchantLevels[2] = 1;
80+
observation.item = new ItemStack(Items.fishing_rod);
6181
return observation;
6282
}
6383

@@ -77,34 +97,12 @@ public void testTestLevels() {
7797
assertTrue(count >= 10000);
7898
}
7999

80-
@Test
81-
public void testTestEnchants() {
82-
Random rand = new Random(0);
83-
Observation observation = getTestObservation();
84-
ItemStack item = observation.item;
85-
List<List<EnchantmentData>> cachedEnchantmentList = EnchantmentWorker.buildEnchantListCache(item);
100+
private static EnchantmentWorker runWorkerLoop(Observation observation, String useSeed, int expectedCandidates) throws InterruptedException {
86101
Enchantment[] targets = new Enchantment[3];
87102
for (int i = 0; i < 3; ++i) {
88103
targets[i] = Enchantment.getEnchantmentByID(observation.enchants[i]);
89104
}
90-
int enchantability = item.getItem().getItemEnchantability(item);
91-
@SuppressWarnings("unchecked") List<EnchantmentData>[] tempData = new List[3];
92-
int count = 0;
93-
for (int i = 0; i < 100000; ++i) {
94-
boolean expected = EnchantmentWorker.testEnchants(rand, i, observation, tempData);
95-
boolean actual = EnchantmentWorker.testEnchantFast(rand, i, observation, false, cachedEnchantmentList, tempData, targets[2], enchantability, 2) && EnchantmentWorker.testEnchantFast(rand, i, observation, false, cachedEnchantmentList, tempData, targets[1], enchantability, 1) && EnchantmentWorker.testEnchantFast(rand, i, observation, false, cachedEnchantmentList, tempData, targets[0], enchantability, 0);
96-
assertEquals(expected, actual);
97-
if (expected) {
98-
count++;
99-
}
100-
}
101-
assertTrue(count >= 100);
102-
}
103-
104-
@Test
105-
public void testFullRun() throws InterruptedException {
106-
Observation observation = getTestObservation();
107-
EnchantmentWorker worker = new EnchantmentWorker(/*useSeed=*/ "always");
105+
EnchantmentWorker worker = new EnchantmentWorker(useSeed);
108106
worker.addObservation(observation);
109107
// Wait for worker to finish
110108
EnchantmentWorker.State state = worker.state;
@@ -113,40 +111,72 @@ public void testFullRun() throws InterruptedException {
113111
state = worker.state;
114112
}
115113
assertEquals("enchantmentrevealer.status.possibles", state.statusMessage);
116-
assertEquals(worker.candidatesLength, state.counts[0][0]);
117-
assertEquals("Found the wrong number of candidates!", 12, state.counts[0][0]);
114+
assertEquals(worker.candidatesLength, state.counts[2][0]);
115+
assertEquals("Found the wrong number of candidates!", expectedCandidates, state.counts[2][0]);
116+
return worker;
117+
}
118+
119+
private static void runFastWorkerTest(Observation observation, int seed, int expectedCandidates)
120+
throws InterruptedException {
121+
EnchantmentWorker worker = runWorkerLoop(observation, "always", expectedCandidates);
118122
int i = 0;
119-
while (i < worker.candidatesLength && worker.candidates[i] != 0x12347) {
123+
while (i < worker.candidatesLength && worker.candidates[i] != seed) {
120124
++i;
121125
}
122126
assertNotEquals("The correct seed was not among the candidates!", worker.candidatesLength, i);
127+
Random rand = new Random(0);
128+
ItemStack item = observation.item;
129+
List<List<EnchantmentData>> cachedEnchantmentList = EnchantmentWorker.buildEnchantListCache(item);
130+
Enchantment[] targets = new Enchantment[3];
131+
for (i = 0; i < 3; ++i) {
132+
targets[i] = Enchantment.getEnchantmentByID(observation.enchants[i]);
133+
}
134+
@SuppressWarnings("unchecked") List<EnchantmentData>[] tempEnchantmentData = new List[3];
135+
int enchantability = item.getItem().getItemEnchantability(item);
136+
for (i = 0; i < worker.candidatesLength; ++i) {
137+
for (int j = 0; j < 3; ++j) {
138+
assertTrue("Failure for " + j + " at i=" + i,
139+
EnchantmentWorker.testEnchantFast(rand, worker.candidates[i], observation, false,
140+
cachedEnchantmentList, tempEnchantmentData, targets[j], enchantability, j));
141+
}
142+
}
123143
}
124144

125-
// This test takes ~1 minute to run.
126145
@Test
127-
public void testFullFullRun() throws InterruptedException {
128-
Observation observation = getTestObservation();
129-
EnchantmentWorker worker = new EnchantmentWorker(/*useSeed=*/ "never");
130-
worker.addObservation(observation);
131-
// Wait for worker to finish
132-
EnchantmentWorker.State state = worker.state;
133-
while (state.enchants == EnchantmentWorker.NO_STRINGS) {
134-
Thread.sleep(50);
135-
state = worker.state;
136-
}
137-
assertEquals("enchantmentrevealer.status.possibles", state.statusMessage);
138-
assertEquals(worker.candidatesLength, state.counts[0][0]);
139-
assertEquals("Found the wrong number of candidates!", 38722, state.counts[0][0]);
146+
public void testFastFullRun() throws InterruptedException {
147+
runFastWorkerTest(getTestObservation(), 0x12347, 12);
148+
}
149+
150+
@Test
151+
public void testFastWeakRun() throws InterruptedException {
152+
runFastWorkerTest(getWeakObservation(), 0x249e08e4, 18202);
153+
}
154+
155+
// These tests take >1 minute to run.
156+
private static void runSlowWorkerTest(Observation observation, int seed, int expectedCandidates)
157+
throws InterruptedException {
158+
EnchantmentWorker worker = runWorkerLoop(observation, "never", expectedCandidates);
140159
int i = 0;
141-
while (i < worker.candidatesLength && worker.candidates[i] != 0x12347) {
160+
while (i < worker.candidatesLength && worker.candidates[i] != seed) {
142161
++i;
143162
}
144163
assertNotEquals("The correct seed was not among the candidates!", worker.candidatesLength, i);
145164
Random rand = new Random(0);
146165
@SuppressWarnings("unchecked") List<EnchantmentData>[] tempEnchantmentData = new List[3];
147166
for (i = 0; i < worker.candidatesLength; ++i) {
148167
assertTrue("Failure at i=" + i,
149-
EnchantmentWorker.testEnchants(rand, worker.candidates[i], observation, tempEnchantmentData));
168+
EnchantmentWorker.testEnchants(
169+
rand, worker.candidates[i], observation, tempEnchantmentData));
150170
}
151171
}
172+
173+
@Test
174+
public void testSlowFullRun() throws InterruptedException {
175+
runSlowWorkerTest(getTestObservation(), 0x12347, 38722);
176+
}
177+
178+
@Test
179+
public void testSlowWeakRun() throws InterruptedException {
180+
runSlowWorkerTest(getWeakObservation(), 0x249e08e4, 75093865);
181+
}
152182
}

0 commit comments

Comments
 (0)