Skip to content

Commit 911b425

Browse files
committed
Fixed Datapack Waypoint Exporting, Added Random Color Scheme
1 parent a0feb44 commit 911b425

6 files changed

Lines changed: 179 additions & 52 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Change the datapack structure color scheme (applies immediately):
5656
- ```/sm:datapack colorscheme 1``` - current scheme
5757
- ```/sm:datapack colorscheme 2``` - secondary scheme
5858
- ```/sm:datapack colorscheme 3``` - third scheme
59+
- ```/sm:datapack colorscheme random``` - generate and persist a vibrant, high-contrast palette until you run this command again
5960

6061
Change the datapack structure icon style (applies immediately):
6162
- ```/sm:datapack iconstyle 1``` - small flat colored squares (default)

src/main/java/dev/xpple/seedmapper/command/commands/DatapackImportCommand.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public static void register(com.mojang.brigadier.CommandDispatcher<FabricClientC
4242
.then(literal("read")
4343
.executes(DatapackImportCommand::read))
4444
.then(literal("colorscheme")
45+
.then(literal("random")
46+
.executes(DatapackImportCommand::randomizeColorScheme))
4547
.then(argument("scheme", IntegerArgumentType.integer(1, 3))
4648
.executes(DatapackImportCommand::setColorScheme)))
4749
.then(literal("iconstyle")
@@ -124,6 +126,25 @@ private static int setColorScheme(CommandContext<FabricClientCommandSource> cont
124126
return Command.SINGLE_SUCCESS;
125127
}
126128

129+
private static int randomizeColorScheme(CommandContext<FabricClientCommandSource> context) {
130+
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
131+
java.util.List<Integer> palette = DatapackStructureManager.generateRandomColorPalette();
132+
Configs.DatapackRandomColors = palette;
133+
Configs.DatapackColorScheme = DatapackStructureManager.COLOR_SCHEME_RANDOM;
134+
Configs.save();
135+
DatapackStructureManager.clearColorSchemeCache();
136+
source.sendFeedback(Component.translatable("seedMap.datapackImport.colorschemeRandomized"));
137+
try {
138+
int generatorFlags = source.getGeneratorFlags();
139+
SeedMapScreen.reopenIfOpen(generatorFlags);
140+
SeedMapMinimapManager.refreshIfOpenWithGeneratorFlags(generatorFlags);
141+
} catch (CommandSyntaxException ignored) {
142+
SeedMapScreen.reopenIfOpen(0);
143+
SeedMapMinimapManager.refreshIfOpenWithGeneratorFlags(0);
144+
}
145+
return Command.SINGLE_SUCCESS;
146+
}
147+
127148
private static int setIconStyle(CommandContext<FabricClientCommandSource> context) {
128149
CustomClientCommandSource source = CustomClientCommandSource.of(context.getSource());
129150
int style = IntegerArgumentType.getInteger(context, "style");

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import dev.xpple.seedmapper.seedmap.MapFeature;
1212
import dev.xpple.seedmapper.seedmap.SeedMapMinimapManager;
1313
import dev.xpple.seedmapper.seedmap.SeedMapScreen;
14+
import dev.xpple.seedmapper.datapack.DatapackStructureManager;
1415
import dev.xpple.seedmapper.util.BaritoneIntegration;
1516
import dev.xpple.seedmapper.util.ComponentUtils;
1617
import dev.xpple.seedmapper.util.SeedIdentifier;
@@ -24,8 +25,10 @@
2425

2526
import java.net.SocketAddress;
2627
import java.time.Duration;
28+
import java.util.ArrayList;
2729
import java.util.EnumSet;
2830
import java.util.HashMap;
31+
import java.util.List;
2932
import java.util.Map;
3033
import java.util.Objects;
3134
import java.util.function.Supplier;
@@ -97,8 +100,11 @@ private static Component displaySavedSeeds() {
97100
@Config(setter = @Config.Setter("setDatapackColorScheme"))
98101
public static int DatapackColorScheme = 1;
99102

103+
@Config
104+
public static List<Integer> DatapackRandomColors = new ArrayList<>();
105+
100106
private static void setDatapackColorScheme(int scheme) {
101-
DatapackColorScheme = Math.clamp(scheme, 1, 3);
107+
DatapackColorScheme = Math.clamp(scheme, 1, DatapackStructureManager.COLOR_SCHEME_RANDOM);
102108
}
103109

104110
@Config(setter = @Config.Setter("setDatapackIconStyle"))

src/main/java/dev/xpple/seedmapper/datapack/DatapackStructureManager.java

Lines changed: 80 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import java.util.Locale;
7676
import java.util.Map;
7777
import java.util.Optional;
78+
import java.util.Random;
7879
import java.util.Set;
7980
import java.util.concurrent.CompletableFuture;
8081
import java.util.function.Consumer;
@@ -92,6 +93,12 @@ public final class DatapackStructureManager {
9293
private static volatile String lastImportedUrl = null;
9394
private static volatile Path lastImportedCachePath = null;
9495

96+
public static final int COLOR_SCHEME_RANDOM = 4;
97+
private static final int RANDOM_COLOR_PALETTE_SIZE = 128;
98+
private static final double RANDOM_COLOR_HUE_INCREMENT = 0.618033988749895;
99+
private static final double RANDOM_COLOR_SATURATION_MIN = 0.80;
100+
private static final double RANDOM_COLOR_VALUE_MIN = 0.75;
101+
95102
private DatapackStructureManager() {}
96103

97104
public static List<CustomStructureSet> get(WorldIdentifier identifier) {
@@ -693,6 +700,7 @@ public record RandomSpreadCandidate(ChunkPos chunkPos, WorldgenRandom random) {}
693700

694701
public record StructureSetEntry(String id, Holder<Structure> structure, int weight, boolean custom) {
695702
private static final int EMPTY_COLOR = 0xFF_FFFFFF;
703+
private static final int RANDOM_SCHEME = DatapackStructureManager.COLOR_SCHEME_RANDOM;
696704
private static final double GOLDEN_RATIO = 0.618033988749895;
697705
private static final double MIN_COLOR_DISTANCE = 0.35;
698706
private static final int MAX_COLOR_TRIES = 64;
@@ -725,6 +733,26 @@ private static int colorForScheme(String idString, int scheme) {
725733
}
726734
}
727735

736+
private static int colorForRandomPalette(String idString) {
737+
synchronized (SCHEME_COLOR_CACHE) {
738+
Map<String, Integer> cache = SCHEME_COLOR_CACHE.computeIfAbsent(RANDOM_SCHEME, _ -> new HashMap<>());
739+
Integer cached = cache.get(idString);
740+
if (cached != null) {
741+
return cached;
742+
}
743+
java.util.List<Integer> palette = DatapackStructureManager.getRandomColorPalette();
744+
int color;
745+
if (palette.isEmpty()) {
746+
color = colorFromId(idString);
747+
} else {
748+
int index = Math.floorMod(idString.hashCode(), palette.size());
749+
color = palette.get(index);
750+
}
751+
cache.put(idString, color);
752+
return color;
753+
}
754+
}
755+
728756
private static int pickDistinctColor(int scheme, double hue, double saturation, double value) {
729757
java.util.List<Integer> used = SCHEME_COLORS.computeIfAbsent(scheme, _ -> new java.util.ArrayList<>());
730758
int tries = 0;
@@ -786,36 +814,6 @@ private static boolean isDistinct(int candidate, java.util.List<Integer> used) {
786814
return true;
787815
}
788816

789-
private static int hsvToRgb(double hue, double saturation, double value) {
790-
double h = (hue % 1.0 + 1.0) % 1.0;
791-
double s = Math.clamp(saturation, 0.0, 1.0);
792-
double v = Math.clamp(value, 0.0, 1.0);
793-
double c = v * s;
794-
double x = c * (1.0 - Math.abs((h * 6.0) % 2.0 - 1.0));
795-
double m = v - c;
796-
double r1;
797-
double g1;
798-
double b1;
799-
double h6 = h * 6.0;
800-
if (h6 < 1.0) {
801-
r1 = c; g1 = x; b1 = 0.0;
802-
} else if (h6 < 2.0) {
803-
r1 = x; g1 = c; b1 = 0.0;
804-
} else if (h6 < 3.0) {
805-
r1 = 0.0; g1 = c; b1 = x;
806-
} else if (h6 < 4.0) {
807-
r1 = 0.0; g1 = x; b1 = c;
808-
} else if (h6 < 5.0) {
809-
r1 = x; g1 = 0.0; b1 = c;
810-
} else {
811-
r1 = c; g1 = 0.0; b1 = x;
812-
}
813-
int r = (int) Math.round((r1 + m) * 255.0);
814-
int g = (int) Math.round((g1 + m) * 255.0);
815-
int b = (int) Math.round((b1 + m) * 255.0);
816-
return 0xFF000000 | (r << 16) | (g << 8) | b;
817-
}
818-
819817
private static String prettyName(String id) {
820818
if (id == null) {
821819
return "";
@@ -839,6 +837,7 @@ public int tint() {
839837
return switch (Configs.DatapackColorScheme) {
840838
case 2 -> colorForScheme(this.id, 2);
841839
case 3 -> colorForScheme(this.id, 3);
840+
case RANDOM_SCHEME -> colorForRandomPalette(this.id);
842841
default -> colorForScheme(this.id, 1);
843842
};
844843
}
@@ -851,6 +850,57 @@ public static void clearColorSchemeCache() {
851850
}
852851
}
853852

853+
public static java.util.List<Integer> getRandomColorPalette() {
854+
java.util.List<Integer> palette = Configs.DatapackRandomColors;
855+
if (palette == null || palette.isEmpty()) {
856+
return java.util.List.of();
857+
}
858+
return java.util.List.copyOf(palette);
859+
}
860+
861+
public static java.util.List<Integer> generateRandomColorPalette() {
862+
Random random = new Random();
863+
java.util.List<Integer> palette = new ArrayList<>(RANDOM_COLOR_PALETTE_SIZE);
864+
double hue = random.nextDouble();
865+
for (int i = 0; i < RANDOM_COLOR_PALETTE_SIZE; i++) {
866+
hue = (hue + RANDOM_COLOR_HUE_INCREMENT) % 1.0;
867+
double saturation = RANDOM_COLOR_SATURATION_MIN + random.nextDouble() * (1.0 - RANDOM_COLOR_SATURATION_MIN);
868+
double value = RANDOM_COLOR_VALUE_MIN + random.nextDouble() * (1.0 - RANDOM_COLOR_VALUE_MIN);
869+
palette.add(hsvToRgb(hue, saturation, value));
870+
}
871+
return palette;
872+
}
873+
874+
private static int hsvToRgb(double hue, double saturation, double value) {
875+
double h = (hue % 1.0 + 1.0) % 1.0;
876+
double s = Math.clamp(saturation, 0.0, 1.0);
877+
double v = Math.clamp(value, 0.0, 1.0);
878+
double c = v * s;
879+
double x = c * (1.0 - Math.abs((h * 6.0) % 2.0 - 1.0));
880+
double m = v - c;
881+
double r1;
882+
double g1;
883+
double b1;
884+
double h6 = h * 6.0;
885+
if (h6 < 1.0) {
886+
r1 = c; g1 = x; b1 = 0.0;
887+
} else if (h6 < 2.0) {
888+
r1 = x; g1 = c; b1 = 0.0;
889+
} else if (h6 < 3.0) {
890+
r1 = 0.0; g1 = c; b1 = x;
891+
} else if (h6 < 4.0) {
892+
r1 = 0.0; g1 = x; b1 = c;
893+
} else if (h6 < 5.0) {
894+
r1 = x; g1 = 0.0; b1 = c;
895+
} else {
896+
r1 = c; g1 = 0.0; b1 = x;
897+
}
898+
int r = (int) Math.round((r1 + m) * 255.0);
899+
int g = (int) Math.round((g1 + m) * 255.0);
900+
int b = (int) Math.round((b1 + m) * 255.0);
901+
return 0xFF000000 | (r << 16) | (g << 8) | b;
902+
}
903+
854904
private static PackResources createPathPack(String id, Path path, PackSource source) {
855905
PackLocationInfo info = new PackLocationInfo(id, Component.literal(id), source, Optional.empty());
856906
return new PathPackResources(info, path);

src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -980,12 +980,19 @@ private void exportVisibleStructures() {
980980
for (ExportEntry exportEntry : exportEntries) {
981981
BlockPos pos = exportEntry.pos();
982982
JsonObject jsonEntry = new JsonObject();
983-
jsonEntry.addProperty("feature", exportEntry.feature().getName());
983+
MapFeature feature = exportEntry.feature();
984+
String featureName = feature != null ? feature.getName() : exportEntry.label();
985+
jsonEntry.addProperty("feature", featureName);
986+
jsonEntry.addProperty("label", exportEntry.label());
984987
jsonEntry.addProperty("number", exportEntry.number());
985988
jsonEntry.addProperty("x", pos.getX());
986989
jsonEntry.addProperty("y", pos.getY());
987990
jsonEntry.addProperty("z", pos.getZ());
988991
jsonEntry.addProperty("biome", exportEntry.biome());
992+
jsonEntry.addProperty("structureId", exportEntry.structureId());
993+
if (exportEntry.datapackEntry() != null) {
994+
jsonEntry.addProperty("datapackId", exportEntry.datapackEntry().id());
995+
}
989996
if (dimensionKey != null) {
990997
jsonEntry.addProperty("dimension", dimensionKey.identifier().toString());
991998
}
@@ -1516,7 +1523,7 @@ private void exportVisibleStructuresToXaero() {
15161523
continue;
15171524
}
15181525
occupiedCoords.add(coordKey);
1519-
String name = "%s %d".formatted(exportEntry.feature().getName(), exportEntry.number());
1526+
String name = "%s %d".formatted(exportEntry.label(), exportEntry.number());
15201527
String waypointLine = "waypoint:%s:%s:%d:%d:%d:%d:%s:%d:%s:%s:%d:%d:%s".formatted(
15211528
encodeXaeroName(name),
15221529
buildXaeroInitials(name),
@@ -1556,25 +1563,44 @@ private void exportVisibleStructuresToXaero() {
15561563
}
15571564

15581565
private List<ExportEntry> collectVisibleExportEntries() {
1559-
List<FeatureWidget> visibleWidgets = this.featureWidgets.stream()
1560-
.filter(widget -> Configs.ToggledFeatures.contains(widget.feature))
1561-
.filter(FeatureWidget::withinBounds)
1562-
.sorted(Comparator
1563-
.comparing((FeatureWidget widget) -> widget.feature.getName())
1564-
.thenComparing(widget -> widget.featureLocation.getX())
1565-
.thenComparing(widget -> widget.featureLocation.getZ()))
1566-
.toList();
1567-
if (visibleWidgets.isEmpty()) {
1566+
List<ExportCandidate> candidates = new ArrayList<>(this.featureWidgets.size() + this.customStructureWidgets.size());
1567+
for (FeatureWidget widget : this.featureWidgets) {
1568+
if (!Configs.ToggledFeatures.contains(widget.feature())) {
1569+
continue;
1570+
}
1571+
if (!widget.withinBounds()) {
1572+
continue;
1573+
}
1574+
MapFeature feature = widget.feature();
1575+
candidates.add(new ExportCandidate(feature.getName(), feature.getName(), widget.featureLocation, feature, null, feature.getStructureId()));
1576+
}
1577+
for (CustomStructureWidget widget : this.customStructureWidgets) {
1578+
if (!widget.withinBounds()) {
1579+
continue;
1580+
}
1581+
DatapackStructureManager.StructureSetEntry entry = widget.entry();
1582+
String tooltip = entry.tooltip().getString();
1583+
String label = tooltip.isBlank() ? entry.id() : tooltip;
1584+
String key = "datapack:" + entry.id();
1585+
candidates.add(new ExportCandidate(key, label, widget.featureLocation(), null, entry, -1));
1586+
}
1587+
if (candidates.isEmpty()) {
15681588
return List.of();
15691589
}
1570-
Object2IntMap<MapFeature> featureCounts = new Object2IntOpenHashMap<>();
1590+
candidates.sort(Comparator
1591+
.comparing(ExportCandidate::key)
1592+
.thenComparing(candidate -> candidate.pos().getX())
1593+
.thenComparing(candidate -> candidate.pos().getZ())
1594+
);
1595+
Object2IntMap<String> featureCounts = new Object2IntOpenHashMap<>();
15711596
featureCounts.defaultReturnValue(0);
1572-
List<ExportEntry> exportEntries = new ArrayList<>(visibleWidgets.size());
1573-
for (FeatureWidget widget : visibleWidgets) {
1574-
int nextIndex = featureCounts.getInt(widget.feature) + 1;
1575-
featureCounts.put(widget.feature, nextIndex);
1576-
BlockPos pos = widget.featureLocation;
1577-
exportEntries.add(new ExportEntry(widget.feature, nextIndex, pos, this.getBiomeName(pos)));
1597+
List<ExportEntry> exportEntries = new ArrayList<>(candidates.size());
1598+
for (ExportCandidate candidate : candidates) {
1599+
String key = candidate.key();
1600+
int nextIndex = featureCounts.getInt(key) + 1;
1601+
featureCounts.put(key, nextIndex);
1602+
BlockPos pos = candidate.pos();
1603+
exportEntries.add(new ExportEntry(candidate.feature(), candidate.datapackEntry(), key, candidate.label(), nextIndex, pos, this.getBiomeName(pos), candidate.structureId()));
15781604
}
15791605
return exportEntries;
15801606
}
@@ -1639,7 +1665,29 @@ private static String buildXaeroInitials(String name) {
16391665
return initials.length() == 0 ? "WP" : initials.toString();
16401666
}
16411667

1642-
private record ExportEntry(MapFeature feature, int number, BlockPos pos, String biome) {}
1668+
private record ExportEntry(
1669+
@Nullable MapFeature feature,
1670+
@Nullable DatapackStructureManager.StructureSetEntry datapackEntry,
1671+
String key,
1672+
String label,
1673+
int number,
1674+
BlockPos pos,
1675+
String biome,
1676+
int structureId
1677+
) {
1678+
public boolean isDatapack() {
1679+
return this.datapackEntry != null;
1680+
}
1681+
}
1682+
1683+
private record ExportCandidate(
1684+
String key,
1685+
String label,
1686+
BlockPos pos,
1687+
@Nullable MapFeature feature,
1688+
@Nullable DatapackStructureManager.StructureSetEntry datapackEntry,
1689+
int structureId
1690+
) {}
16431691

16441692
private Path resolveXaeroWorldFolder(String worldIdentifier) {
16451693
Path minimapDir = this.minecraft.gameDirectory.toPath().resolve("xaero").resolve("minimap");
@@ -3838,8 +3886,8 @@ private void openLootTableScreen() {
38383886
return;
38393887
}
38403888
List<LootExportHelper.Target> targets = exportEntries.stream()
3841-
.filter(entry -> LocateCommand.LOOT_SUPPORTED_STRUCTURES.contains(entry.feature().getStructureId()))
3842-
.map(entry -> new LootExportHelper.Target(entry.feature().getStructureId(), entry.pos()))
3889+
.filter(entry -> LocateCommand.LOOT_SUPPORTED_STRUCTURES.contains(entry.structureId()))
3890+
.map(entry -> new LootExportHelper.Target(entry.structureId(), entry.pos()))
38433891
.toList();
38443892
if (targets.isEmpty()) {
38453893
player.displayClientMessage(Component.literal("No lootable structures in view."), false);

src/main/resources/assets/seedmapper/lang/en_us.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
"seedMap.datapackImport.read.none": "No datapack URL is available.",
140140
"seedMap.datapackImport.read.current": "Datapack URL: %s",
141141
"seedMap.datapackImport.colorschemeSet": "Datapack color scheme set to %d.",
142+
"seedMap.datapackImport.colorschemeRandomized": "Random datapack color palette generated and saved.",
142143
"seedMap.datapackImport.iconStyleSet": "Datapack icon style set to %d.",
143144
"config.showDatapackStructures.comment": "Render imported datapack structures on the SeedMap."
144145
}

0 commit comments

Comments
 (0)