Skip to content

Commit e652ac6

Browse files
committed
Datapack Support
1 parent 1ec7391 commit e652ac6

File tree

9 files changed

+2172
-2
lines changed

9 files changed

+2172
-2
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,40 @@ You can also override per dimension:
3131

3232
Per-dimension values take priority when set; use 0 to fall back to the global border.
3333

34+
### Datapack Structures Import
35+
Import a datapack URL so its custom structures appear on the SeedMap. Datapack structures render as solid colored squares (one color per structure) and show up in the datapack toggle list so you can enable/disable them like vanilla features. You can also right‑click a datapack structure and mark it complete/incomplete (a green tick is drawn on the icon).
36+
37+
Datapack structures load differently to vanilla: vanilla features are built-in, while datapack structures are parsed from the provided pack and then cached. They load in the background and appear progressively as tiles are processed.
38+
39+
Version mismatch handling: if the server version doesn't match the client, SeedMapper avoids loading registries that frequently fail across versions (notably enchantments and enchantment providers). This lets the worldgen data load even when other datapack content is incompatible.
40+
41+
Caching scope: datapack structures are cached for the current game session. Switching dimensions or servers keeps the cached structures available, but restarting the game will reload them.
42+
43+
Import a datapack:
44+
- ```/sm:datapack import <url>```
45+
46+
Save the last imported URL for the current server (by IP):
47+
- ```/sm:datapack save```
48+
49+
Load the saved URL for the current server:
50+
- ```/sm:datapack load```
51+
52+
Read the current datapack URL (last imported or saved for this server):
53+
- ```/sm:datapack read```
54+
55+
Change the datapack structure color scheme (applies immediately):
56+
- ```/sm:datapack colorscheme 1``` - current scheme
57+
- ```/sm:datapack colorscheme 2``` - secondary scheme
58+
- ```/sm:datapack colorscheme 3``` - third scheme
59+
60+
Enable or disable auto-loading on join:
61+
- ```/sm:datapack autoload true```
62+
- ```/sm:datapack autoload false```
63+
64+
Autoload uses the saved URL for the server you are joining. Save is keyed by the server address, so each server can keep its own datapack URL.
65+
66+
![Datapack](https://i.imgur.com/8coATaJ.png)
67+
3468
### Double Click Recenter
3569
Double clicking anywhere on the map will recenter the map to the players location.
3670

src/main/java/dev/xpple/seedmapper/SeedMapper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import dev.xpple.seedmapper.command.commands.SeedMapCommand;
2020
import dev.xpple.seedmapper.command.commands.SourceCommand;
2121
import dev.xpple.seedmapper.command.commands.StopTaskCommand;
22+
import dev.xpple.seedmapper.command.commands.DatapackImportCommand;
2223
import dev.xpple.seedmapper.config.Configs;
2324
import dev.xpple.seedmapper.config.DurationAdapter;
2425
import dev.xpple.seedmapper.config.MapFeatureAdapter;
@@ -114,6 +115,7 @@ private static void registerCommands(CommandDispatcher<FabricClientCommandSource
114115
DiscordCommand.register(dispatcher);
115116
SampleCommand.register(dispatcher);
116117
ExportLootCommand.register(dispatcher);
118+
DatapackImportCommand.register(dispatcher);
117119
// ESP config command
118120
dev.xpple.seedmapper.command.commands.EspConfigCommand.register(dispatcher);
119121
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package dev.xpple.seedmapper.command.commands;
2+
3+
import com.github.cubiomes.Cubiomes;
4+
import com.mojang.brigadier.Command;
5+
import com.mojang.brigadier.StringReader;
6+
import com.mojang.brigadier.arguments.BoolArgumentType;
7+
import com.mojang.brigadier.arguments.IntegerArgumentType;
8+
import com.mojang.brigadier.arguments.StringArgumentType;
9+
import com.mojang.brigadier.context.CommandContext;
10+
import com.mojang.brigadier.exceptions.CommandSyntaxException;
11+
import dev.xpple.seedmapper.command.CustomClientCommandSource;
12+
import dev.xpple.seedmapper.config.Configs;
13+
import dev.xpple.seedmapper.datapack.DatapackStructureManager;
14+
import dev.xpple.seedmapper.util.SeedIdentifier;
15+
import dev.xpple.seedmapper.util.WorldIdentifier;
16+
import dev.xpple.seedmapper.command.arguments.DimensionArgument;
17+
import dev.xpple.seedmapper.seedmap.SeedMapScreen;
18+
import dev.xpple.seedmapper.seedmap.SeedMapMinimapManager;
19+
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
20+
import net.minecraft.client.Minecraft;
21+
import net.minecraft.network.chat.Component;
22+
import net.minecraft.server.permissions.PermissionSet;
23+
24+
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
25+
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
26+
27+
public class DatapackImportCommand {
28+
private static final String COMMAND = "sm:datapack";
29+
30+
public static void register(com.mojang.brigadier.CommandDispatcher<FabricClientCommandSource> dispatcher) {
31+
dispatcher.register(literal(COMMAND)
32+
.then(literal("import")
33+
.then(argument("url", StringArgumentType.greedyString())
34+
.executes(DatapackImportCommand::execute)))
35+
.then(literal("autoload")
36+
.then(argument("enabled", BoolArgumentType.bool())
37+
.executes(DatapackImportCommand::toggleAutoload)))
38+
.then(literal("save")
39+
.executes(DatapackImportCommand::save))
40+
.then(literal("load")
41+
.executes(DatapackImportCommand::load))
42+
.then(literal("read")
43+
.executes(DatapackImportCommand::read))
44+
.then(literal("colorscheme")
45+
.then(argument("scheme", IntegerArgumentType.integer(1, 3))
46+
.executes(DatapackImportCommand::setColorScheme))));
47+
}
48+
49+
@SuppressWarnings("unused")
50+
private static int execute(CommandContext<FabricClientCommandSource> context) {
51+
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
52+
String url = StringArgumentType.getString(context, "url").trim();
53+
return importUrl(source, url);
54+
}
55+
56+
private static int toggleAutoload(CommandContext<FabricClientCommandSource> context) {
57+
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
58+
boolean enabled = BoolArgumentType.getBool(context, "enabled");
59+
Configs.DatapackAutoload = enabled;
60+
Configs.save();
61+
source.sendFeedback(Component.translatable("seedMap.datapackImport.autoloadSet", enabled));
62+
return Command.SINGLE_SUCCESS;
63+
}
64+
65+
private static int save(CommandContext<FabricClientCommandSource> context) {
66+
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
67+
String url = DatapackStructureManager.getLastImportedUrl();
68+
if (url == null || url.isBlank()) {
69+
source.sendError(Component.translatable("seedMap.datapackImport.save.noImported"));
70+
return Command.SINGLE_SUCCESS;
71+
}
72+
if (!Configs.saveDatapackUrlForCurrentServer(url)) {
73+
source.sendError(Component.translatable("seedMap.datapackImport.save.noServer"));
74+
return Command.SINGLE_SUCCESS;
75+
}
76+
source.sendFeedback(Component.translatable("seedMap.datapackImport.save.success"));
77+
return Command.SINGLE_SUCCESS;
78+
}
79+
80+
private static int load(CommandContext<FabricClientCommandSource> context) {
81+
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
82+
String url = Configs.getSavedDatapackUrlForCurrentServer();
83+
if (url == null || url.isBlank()) {
84+
source.sendError(Component.translatable("seedMap.datapackImport.load.none"));
85+
return Command.SINGLE_SUCCESS;
86+
}
87+
return importUrl(source, url);
88+
}
89+
90+
private static int read(CommandContext<FabricClientCommandSource> context) {
91+
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
92+
String url = DatapackStructureManager.getLastImportedUrl();
93+
if (url == null || url.isBlank()) {
94+
url = Configs.getSavedDatapackUrlForCurrentServer();
95+
}
96+
if (url == null || url.isBlank()) {
97+
source.sendError(Component.translatable("seedMap.datapackImport.read.none"));
98+
return Command.SINGLE_SUCCESS;
99+
}
100+
source.sendFeedback(Component.translatable("seedMap.datapackImport.read.current", url));
101+
return Command.SINGLE_SUCCESS;
102+
}
103+
104+
private static int setColorScheme(CommandContext<FabricClientCommandSource> context) {
105+
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
106+
int scheme = IntegerArgumentType.getInteger(context, "scheme");
107+
Configs.DatapackColorScheme = scheme;
108+
Configs.save();
109+
DatapackStructureManager.clearColorSchemeCache();
110+
source.sendFeedback(Component.translatable("seedMap.datapackImport.colorschemeSet", scheme));
111+
try {
112+
int generatorFlags = source.getGeneratorFlags();
113+
SeedMapScreen.reopenIfOpen(generatorFlags);
114+
SeedMapMinimapManager.refreshIfOpenWithGeneratorFlags(generatorFlags);
115+
} catch (CommandSyntaxException ignored) {
116+
SeedMapScreen.reopenIfOpen(0);
117+
SeedMapMinimapManager.refreshIfOpenWithGeneratorFlags(0);
118+
}
119+
return Command.SINGLE_SUCCESS;
120+
}
121+
122+
public static int importUrl(CustomClientCommandSource source, String url) {
123+
if (url.isBlank()) {
124+
source.sendError(Component.translatable("seedMap.datapackImport.status.invalidUrl"));
125+
return Command.SINGLE_SUCCESS;
126+
}
127+
128+
SeedIdentifier seed = Configs.Seed;
129+
if (seed == null) {
130+
source.sendError(Component.translatable("seedMap.datapackImport.noSeed"));
131+
return Command.SINGLE_SUCCESS;
132+
}
133+
134+
int dimension;
135+
try {
136+
String dimensionPath = source.getWorld().dimension().identifier().getPath();
137+
dimension = DimensionArgument.dimension().parse(new StringReader(dimensionPath));
138+
} catch (CommandSyntaxException e) {
139+
source.sendError(Component.translatable("seedMap.datapackImport.dimensionError"));
140+
return Command.SINGLE_SUCCESS;
141+
}
142+
143+
WorldIdentifier identifier = new WorldIdentifier(seed.seed(), dimension, seed.version(), seed.generatorFlags());
144+
source.sendFeedback(Component.translatable("seedMap.datapackImport.started"));
145+
DatapackStructureManager.importDatapack(identifier, url, source::sendFeedback, source::sendError);
146+
return Command.SINGLE_SUCCESS;
147+
}
148+
149+
public static void importUrlForCurrentServer(String url) {
150+
Minecraft minecraft = Minecraft.getInstance();
151+
if (minecraft == null || minecraft.player == null || minecraft.level == null) {
152+
return;
153+
}
154+
CustomClientCommandSource source = new CustomClientCommandSource(
155+
minecraft.getConnection(),
156+
minecraft,
157+
minecraft.player,
158+
minecraft.player.position(),
159+
minecraft.player.getRotationVector(),
160+
minecraft.level,
161+
PermissionSet.NO_PERMISSIONS,
162+
new java.util.HashMap<>()
163+
);
164+
importUrl(source, url);
165+
}
166+
}

src/main/java/dev/xpple/seedmapper/config/Configs.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ private static Component displaySavedSeeds() {
7171
@Config
7272
public static boolean AutoApplySeedCrackerSeed = true;
7373

74+
@Config
75+
public static boolean DatapackAutoload = false;
76+
77+
@Config
78+
public static Map<String, String> DatapackSavedUrls = new HashMap<>();
79+
80+
@Config(setter = @Config.Setter("setDatapackColorScheme"))
81+
public static int DatapackColorScheme = 1;
82+
83+
private static void setDatapackColorScheme(int scheme) {
84+
DatapackColorScheme = Math.clamp(scheme, 1, 3);
85+
}
86+
7487
public static String getCurrentServerKey() {
7588
Minecraft minecraft = Minecraft.getInstance();
7689
if (minecraft == null || minecraft.getConnection() == null || minecraft.getConnection().getConnection() == null) {
@@ -80,6 +93,35 @@ public static String getCurrentServerKey() {
8093
return remoteAddress != null ? remoteAddress.toString() : null;
8194
}
8295

96+
public static String getSavedDatapackUrlForCurrentServer() {
97+
String key = getCurrentServerKey();
98+
if (key == null) {
99+
return null;
100+
}
101+
String url = DatapackSavedUrls.get(key);
102+
return (url == null || url.isBlank()) ? null : url;
103+
}
104+
105+
public static boolean saveDatapackUrlForCurrentServer(String url) {
106+
String key = getCurrentServerKey();
107+
if (key == null || url == null || url.isBlank()) {
108+
return false;
109+
}
110+
String trimmed = url.trim();
111+
DatapackSavedUrls.put(key, trimmed);
112+
save();
113+
return true;
114+
}
115+
116+
public static void removeDatapackUrlForCurrentServer() {
117+
String key = getCurrentServerKey();
118+
if (key == null) {
119+
return;
120+
}
121+
DatapackSavedUrls.remove(key);
122+
save();
123+
}
124+
83125
public static void applySeedForCurrentServer(long seed, boolean storeAsSavedSeed) {
84126
SeedIdentifier identifier = new SeedIdentifier(seed);
85127
String key = getCurrentServerKey();
@@ -261,6 +303,13 @@ private static Component getPlayerDirectionArrowComment() {
261303
return Component.translatable("config.showPlayerDirectionArrow.comment");
262304
}
263305

306+
@Config(comment = "getShowDatapackStructuresComment")
307+
public static boolean ShowDatapackStructures = true;
308+
309+
private static Component getShowDatapackStructuresComment() {
310+
return Component.translatable("config.showDatapackStructures.comment");
311+
}
312+
264313
@Config(comment = "getManualWaypointCompassOverlayComment")
265314
public static boolean ManualWaypointCompassOverlay = false;
266315

@@ -330,6 +379,45 @@ public static void setSeedMapCompletedStructures(String worldIdentifier, java.ut
330379
SeedMapCompletedStructures.put(worldIdentifier, String.join(",", entries));
331380
}
332381

382+
@Config
383+
public static Map<String, String> DatapackStructureDisabled = new HashMap<>();
384+
385+
public static java.util.Set<String> getDatapackStructureDisabled(String worldIdentifier) {
386+
if (worldIdentifier == null || worldIdentifier.isBlank()) {
387+
return new java.util.HashSet<>();
388+
}
389+
String raw = DatapackStructureDisabled.get(worldIdentifier);
390+
if (raw == null || raw.isBlank()) {
391+
return new java.util.HashSet<>();
392+
}
393+
java.util.Set<String> entries = new java.util.HashSet<>();
394+
for (String part : raw.split(",")) {
395+
if (!part.isBlank()) {
396+
entries.add(part.trim());
397+
}
398+
}
399+
return entries;
400+
}
401+
402+
public static void setDatapackStructureDisabled(String worldIdentifier, java.util.Set<String> entries) {
403+
if (worldIdentifier == null || worldIdentifier.isBlank()) {
404+
return;
405+
}
406+
if (entries == null || entries.isEmpty()) {
407+
DatapackStructureDisabled.remove(worldIdentifier);
408+
return;
409+
}
410+
DatapackStructureDisabled.put(worldIdentifier, String.join(",", entries));
411+
}
412+
413+
public static boolean isDatapackStructureEnabled(String worldIdentifier, String structureId) {
414+
if (structureId == null || structureId.isBlank()) {
415+
return true;
416+
}
417+
java.util.Set<String> disabled = getDatapackStructureDisabled(worldIdentifier);
418+
return !disabled.contains(structureId);
419+
}
420+
333421
public static void applyWaypointCompassOverlaySetting() {
334422
try {
335423
dev.xpple.simplewaypoints.config.Configs.waypointMarkerRenderLimit = 0;

0 commit comments

Comments
 (0)