Skip to content

Commit 32e991b

Browse files
committed
Updated LootSearch
1 parent 1c4fc33 commit 32e991b

6 files changed

Lines changed: 374 additions & 52 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,10 +445,11 @@ Reports number of waypoints changed.
445445

446446
### LootSearch
447447
- Using [my fork](https://github.com/cev-api/SeedMapper-CevAPI) of [SeedMapper](https://github.com/xpple/SeedMapper/) you can export loot tables for selected/all structures and then parse that information using the same UI as ChestSearch.
448+
- Also compatible with LootProbe's exports.
448449
- Sorted by closest chest.
449450
- You can search for specific loot and set a waypoint towards it.
450-
- Supports enchantments.
451-
- Will not function without a valid SeedMapper loot export JSON for the server you're in.
451+
- Supports enchantments.d
452+
- Will not function without a valid SeedMapper or LootProbe loot export JSON for the server you're in.
452453

453454
![Loot](https://i.imgur.com/pe7bbAB.png)
454455

src/main/java/net/wurstclient/clickgui/screens/WaypointsScreen.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,12 +267,12 @@ protected void init()
267267
minecraft.setScreen(this);
268268
}).bounds(x + 205, rowY, 55, 20).build());
269269

270-
Button copyBtn = addRenderableWidget(
271-
Button.builder(Component.literal("Copy"), b -> {
272-
String s = w.getPos().getX() + ", " + w.getPos().getY()
273-
+ ", " + w.getPos().getZ();
274-
minecraft.keyboardHandler.setClipboard(s);
275-
}).bounds(x + 265, rowY, 35, 20).build());
270+
Button copyBtn = addRenderableWidget(
271+
Button.builder(Component.literal("Copy"), b -> {
272+
String s = w.getPos().getX() + " " + w.getPos().getY()
273+
+ " " + w.getPos().getZ();
274+
minecraft.keyboardHandler.setClipboard(s);
275+
}).bounds(x + 265, rowY, 35, 20).build());
276276

277277
RowWidgets rw = new RowWidgets();
278278
rw.w = w;

src/main/java/net/wurstclient/hacks/LootSearchHack.java

Lines changed: 87 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,45 @@
77
*/
88
package net.wurstclient.hacks;
99

10-
import net.wurstclient.hack.Hack;
11-
import net.wurstclient.WurstClient;
10+
import java.io.File;
11+
import java.nio.file.Path;
12+
import net.minecraft.network.chat.Component;
13+
import net.wurstclient.Category;
14+
import net.wurstclient.WurstClient;
15+
import net.wurstclient.hack.Hack;
1216
import net.wurstclient.lootsearch.LootChestManager;
1317
import net.wurstclient.lootsearch.LootSearchUtil;
14-
15-
import java.io.File;
18+
import net.wurstclient.settings.FileSetting;
19+
import net.wurstclient.settings.TextFieldSetting;
1620

1721
public final class LootSearchHack extends Hack
1822
{
19-
public LootSearchHack()
20-
{
21-
super("LootSearch");
22-
// No category -> Navigator-only
23-
}
23+
private final FileSetting lootJsonPicker =
24+
new FileSetting("Loot JSON", "", "lootprobe", folder -> {
25+
try
26+
{
27+
java.nio.file.Files.createDirectories(folder);
28+
Path placeholder = folder.resolve("lootprobe-placeholder.json");
29+
if(!java.nio.file.Files.exists(placeholder))
30+
java.nio.file.Files.writeString(placeholder, "{}\n");
31+
}catch(java.io.IOException e)
32+
{
33+
throw new RuntimeException(e);
34+
}
35+
});
36+
37+
private final TextFieldSetting literalJsonPath = new TextFieldSetting(
38+
"JSON Path",
39+
"Literal path to a loot export JSON file. If set, this path is used instead of auto-detection.",
40+
"");
41+
42+
public LootSearchHack()
43+
{
44+
super("LootSearch");
45+
setCategory(Category.ITEMS);
46+
addSetting(lootJsonPicker);
47+
addSetting(literalJsonPath);
48+
}
2449

2550
@Override
2651
protected void onEnable()
@@ -36,26 +61,15 @@ protected void onEnable()
3661
}catch(Throwable ignored)
3762
{}
3863

39-
File dir = LootSearchUtil.getSeedmapperLootDir();
40-
if(dir == null || !dir.exists() || !dir.isDirectory())
41-
{
42-
if(WurstClient.MC != null && WurstClient.MC.player != null)
43-
WurstClient.MC.player.displayClientMessage(
44-
net.minecraft.network.chat.Component.literal(
45-
"SeedMapper loot folder not found. Run SeedMapper and export loot first."),
46-
false);
47-
setEnabled(false);
48-
return;
49-
}
50-
51-
File f = LootSearchUtil.findFileForServer(serverIp);
64+
File f = resolveLootFile(serverIp);
5265
if(f == null)
5366
{
67+
File dir = LootSearchUtil.getSeedmapperLootDir();
5468
if(WurstClient.MC != null && WurstClient.MC.player != null)
5569
{
5670
WurstClient.MC.player
5771
.displayClientMessage(
58-
net.minecraft.network.chat.Component.literal(
72+
Component.literal(
5973
"No loot export found for this server."),
6074
false);
6175
sendLootSearchDebug(serverIp, dir);
@@ -75,19 +89,59 @@ protected void onEnable()
7589
setEnabled(false);
7690
}
7791

92+
private File resolveLootFile(String serverIp)
93+
{
94+
// Highest priority: literal path entered by user.
95+
File explicit = resolveLiteralJsonPath();
96+
if(explicit != null)
97+
return explicit;
98+
99+
// Then use selected file from wurst/lootprobe.
100+
File selected = LootSearchUtil
101+
.normalizeJsonFile(lootJsonPicker.getSelectedFile().toFile());
102+
if(selected != null && !selected.getName()
103+
.equalsIgnoreCase("lootprobe-placeholder.json"))
104+
return selected;
105+
106+
// Fallback to existing server-based SeedMapper detection.
107+
return LootSearchUtil.findFileForServer(serverIp);
108+
}
109+
110+
private File resolveLiteralJsonPath()
111+
{
112+
String raw = literalJsonPath.getValue();
113+
if(raw == null || raw.isBlank())
114+
return null;
115+
116+
File file = new File(raw.trim());
117+
if(!file.isAbsolute() && WurstClient.MC != null
118+
&& WurstClient.MC.gameDirectory != null)
119+
{
120+
file = new File(WurstClient.MC.gameDirectory, raw.trim());
121+
}
122+
123+
File normalized = LootSearchUtil.normalizeJsonFile(file);
124+
if(normalized == null)
125+
{
126+
if(WurstClient.MC != null && WurstClient.MC.player != null)
127+
WurstClient.MC.player.displayClientMessage(Component.literal(
128+
"LootSearch: JSON Path does not point to an existing .json file."),
129+
false);
130+
}
131+
return normalized;
132+
}
133+
78134
private void sendLootSearchDebug(String serverIp, File dir)
79135
{
80136
if(WurstClient.MC == null || WurstClient.MC.player == null)
81137
return;
82138

83139
String ip = serverIp == null ? "<null>" : serverIp;
84140
String dirPath = dir == null ? "<null>" : dir.getAbsolutePath();
85-
WurstClient.MC.player
86-
.displayClientMessage(net.minecraft.network.chat.Component
87-
.literal("LootSearch debug: serverIp=" + ip), false);
88-
WurstClient.MC.player
89-
.displayClientMessage(net.minecraft.network.chat.Component
90-
.literal("LootSearch debug: lootDir=" + dirPath), false);
141+
WurstClient.MC.player.displayClientMessage(
142+
Component.literal("LootSearch debug: serverIp=" + ip), false);
143+
WurstClient.MC.player.displayClientMessage(
144+
Component.literal("LootSearch debug: lootDir=" + dirPath), false);
91145

92146
if(dir == null || !dir.exists() || !dir.isDirectory())
93147
return;
@@ -96,10 +150,8 @@ private void sendLootSearchDebug(String serverIp, File dir)
96150
dir.listFiles((d, name) -> name.toLowerCase().endsWith(".json"));
97151
if(files == null || files.length == 0)
98152
{
99-
WurstClient.MC.player.displayClientMessage(
100-
net.minecraft.network.chat.Component.literal(
101-
"LootSearch debug: no .json files in lootDir"),
102-
false);
153+
WurstClient.MC.player.displayClientMessage(Component
154+
.literal("LootSearch debug: no .json files in lootDir"), false);
103155
return;
104156
}
105157

@@ -113,8 +165,7 @@ private void sendLootSearchDebug(String serverIp, File dir)
113165
if(files.length > 10)
114166
names.append(" ... (").append(files.length).append(" total)");
115167

116-
WurstClient.MC.player
117-
.displayClientMessage(net.minecraft.network.chat.Component
118-
.literal("LootSearch debug: files=" + names), false);
168+
WurstClient.MC.player.displayClientMessage(
169+
Component.literal("LootSearch debug: files=" + names), false);
119170
}
120171
}

src/main/java/net/wurstclient/lootsearch/LootChestManager.java

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@
99

1010
import net.wurstclient.chestsearch.ChestEntry;
1111
import net.wurstclient.chestsearch.ChestManager;
12+
import net.wurstclient.WurstClient;
1213

1314
import java.io.File;
1415
import java.util.ArrayList;
1516
import java.util.List;
17+
import java.util.Locale;
1618

1719
public class LootChestManager extends ChestManager
1820
{
1921
private final List<ChestEntry> entries = new ArrayList<>();
22+
private final boolean mixedDimensionFile;
2023

2124
public LootChestManager(File lootFile, String serverIp)
2225
{
2326
super();
27+
mixedDimensionFile = LootSearchUtil.isMixedDimensionFile(lootFile);
2428
if(lootFile != null && lootFile.exists())
2529
{
2630
entries.addAll(LootSearchUtil.parseLootFile(lootFile, serverIp));
@@ -32,12 +36,16 @@ public java.util.List<ChestEntry> search(String q)
3236
{
3337
if(q == null || q.isBlank())
3438
return new ArrayList<>(entries);
35-
String qq = q.toLowerCase();
39+
String qq = q.toLowerCase(Locale.ROOT);
3640
List<ChestEntry> out = new ArrayList<>();
3741
for(ChestEntry e : entries)
3842
{
3943
boolean matched = false;
40-
if(e.dimension != null && e.dimension.toLowerCase().contains(qq))
44+
if(e.serverIp != null
45+
&& e.serverIp.toLowerCase(Locale.ROOT).contains(qq))
46+
matched = true;
47+
if(e.dimension != null
48+
&& e.dimension.toLowerCase(Locale.ROOT).contains(qq))
4149
matched = true;
4250
if(!matched && e.items != null)
4351
{
@@ -46,7 +54,53 @@ public java.util.List<ChestEntry> search(String q)
4654
if(it == null)
4755
continue;
4856
if(it.itemId != null
49-
&& it.itemId.toLowerCase().contains(qq))
57+
&& it.itemId.toLowerCase(Locale.ROOT).contains(qq))
58+
{
59+
matched = true;
60+
break;
61+
}
62+
if(it.displayName != null
63+
&& it.displayName.toLowerCase(Locale.ROOT).contains(qq))
64+
{
65+
matched = true;
66+
break;
67+
}
68+
if(it.nbt != null && it.nbt.toString()
69+
.toLowerCase(Locale.ROOT).contains(qq))
70+
{
71+
matched = true;
72+
break;
73+
}
74+
if(it.enchantments != null)
75+
{
76+
for(String en : it.enchantments)
77+
{
78+
if(en != null
79+
&& en.toLowerCase(Locale.ROOT).contains(qq))
80+
{
81+
matched = true;
82+
break;
83+
}
84+
}
85+
if(matched)
86+
break;
87+
}
88+
if(it.potionEffects != null)
89+
{
90+
for(String pe : it.potionEffects)
91+
{
92+
if(pe != null
93+
&& pe.toLowerCase(Locale.ROOT).contains(qq))
94+
{
95+
matched = true;
96+
break;
97+
}
98+
}
99+
if(matched)
100+
break;
101+
}
102+
if(it.primaryPotion != null && it.primaryPotion
103+
.toLowerCase(Locale.ROOT).contains(qq))
50104
{
51105
matched = true;
52106
break;
@@ -56,13 +110,13 @@ public java.util.List<ChestEntry> search(String q)
56110
if(matched)
57111
out.add(e);
58112
}
59-
return out;
113+
return filterForCurrentDimension(out);
60114
}
61115

62116
@Override
63117
public java.util.List<ChestEntry> all()
64118
{
65-
return new ArrayList<>(entries);
119+
return filterForCurrentDimension(new ArrayList<>(entries));
66120
}
67121

68122
@Override
@@ -71,4 +125,54 @@ public void removeChest(String serverIp, String dimension, int x, int y,
71125
{
72126
entries.removeIf(e -> e.x == x && e.y == y && e.z == z);
73127
}
128+
129+
private List<ChestEntry> filterForCurrentDimension(List<ChestEntry> input)
130+
{
131+
if(!mixedDimensionFile || input == null || input.isEmpty())
132+
return input;
133+
134+
String playerDim = getPlayerDimensionKey();
135+
if(playerDim.isEmpty())
136+
return input;
137+
138+
List<ChestEntry> filtered = new ArrayList<>(input.size());
139+
for(ChestEntry entry : input)
140+
{
141+
if(entry == null)
142+
continue;
143+
String entryDim = canonicalDimension(entry.dimension);
144+
if(entryDim.isEmpty() || entryDim.equals("mixed")
145+
|| entryDim.equals(playerDim))
146+
{
147+
filtered.add(entry);
148+
}
149+
}
150+
return filtered;
151+
}
152+
153+
private static String getPlayerDimensionKey()
154+
{
155+
try
156+
{
157+
if(WurstClient.MC == null || WurstClient.MC.level == null)
158+
return "";
159+
String full =
160+
WurstClient.MC.level.dimension().identifier().toString();
161+
return canonicalDimension(full);
162+
}catch(Throwable ignored)
163+
{
164+
return "";
165+
}
166+
}
167+
168+
private static String canonicalDimension(String dimension)
169+
{
170+
if(dimension == null || dimension.isBlank())
171+
return "";
172+
String lower = dimension.trim().toLowerCase(Locale.ROOT);
173+
int colon = lower.indexOf(':');
174+
if(colon >= 0 && colon < lower.length() - 1)
175+
return lower.substring(colon + 1);
176+
return lower;
177+
}
74178
}

0 commit comments

Comments
 (0)