Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/main/java/twilightforest/item/MagicMapItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import twilightforest.init.TFBiomes;
import twilightforest.init.TFDataMaps;
import twilightforest.init.TFItems;
import twilightforest.item.mapdata.MapDataManager;
import twilightforest.item.mapdata.TFMagicMapData;
import twilightforest.tags.TFStructureTags;
import twilightforest.util.datamaps.MagicMapBiomeColor;
Expand Down Expand Up @@ -53,7 +54,16 @@ public static ItemStack setupNewMap(ServerLevel level, int worldX, int worldZ, b
@Nullable
public static TFMagicMapData getData(ItemStack stack, Level level) {
MapId mapid = stack.get(DataComponents.MAP_ID);
return mapid == null ? null : TFMagicMapData.getMagicMapData(level, getMapName(mapid.id()));

if (mapid == null) {
return null;
}

if (level instanceof ServerLevel serverLevel) {
return MapDataManager.getServerMagicMapData(serverLevel, mapid);
}

return MapDataManager.getClientMagicMapData(mapid);
}

@Nullable
Expand Down Expand Up @@ -89,7 +99,7 @@ private static TFMagicMapData createMapData(ItemStack stack, ServerLevel level,
ColumnPos pos = getMagicMapCenter(x, z);

TFMagicMapData mapdata = new TFMagicMapData(pos.x(), pos.z(), (byte) scale, trackingPosition, unlimitedTracking, false, dimension);
TFMagicMapData.registerMagicMapData(level, mapdata, getMapName(freeMapId.id())); // call our own register method
MapDataManager.saveServerMagicMapData(level, freeMapId, mapdata); // call our own save method
stack.set(DataComponents.MAP_ID, freeMapId);
return mapdata;
}
Expand Down
22 changes: 16 additions & 6 deletions src/main/java/twilightforest/item/MazeMapItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.jspecify.annotations.Nullable;
import twilightforest.init.TFDataMaps;
import twilightforest.init.TFItems;
import twilightforest.item.mapdata.MapDataManager;
import twilightforest.item.mapdata.TFMazeMapData;
import twilightforest.util.datamaps.OreMapOreColor;

Expand All @@ -53,8 +54,17 @@ public static ItemStack setupNewMap(ServerLevel level, int worldX, int worldZ, b

@Nullable
public static TFMazeMapData getData(ItemStack stack, Level level) {
MapId id = stack.get(DataComponents.MAP_ID);
return id == null ? null : TFMazeMapData.getMazeMapData(level, getMapName(id.id()));
MapId mapId = stack.get(DataComponents.MAP_ID);

if (mapId == null) {
return null;
}

if (level instanceof ServerLevel serverLevel) {
return MapDataManager.getServerMazeMapData(serverLevel, mapId);
}

return MapDataManager.getClientMazeMapData(mapId);
}

@Nullable
Expand All @@ -70,7 +80,7 @@ protected TFMazeMapData getCustomMapData(ItemStack stack, Level level) {
}

private static TFMazeMapData createMapData(ItemStack stack, ServerLevel level, int x, int z, int scale, boolean trackingPosition, boolean unlimitedTracking, ResourceKey<Level> dimension, int y, boolean ore) {
MapId i = level.getFreeMapId();
MapId freeMapId = level.getFreeMapId();

int mapSize = 128 * (1 << scale);
int roundX = Mth.floor((x + 64.0D) / (double) mapSize);
Expand All @@ -81,8 +91,8 @@ private static TFMazeMapData createMapData(ItemStack stack, ServerLevel level, i
TFMazeMapData mapdata = new TFMazeMapData(scaledX, scaledZ, (byte) scale, trackingPosition, unlimitedTracking, false, dimension);
mapdata.calculateMapCenter(level, x, y, z); // call our own map center calculation
mapdata.ore = ore;
TFMazeMapData.registerMazeMapData(level, mapdata, getMapName(i.id())); // call our own register method
stack.set(DataComponents.MAP_ID, i);
MapDataManager.saveServerMazeMapData(level, freeMapId, mapdata); // call our own save method
stack.set(DataComponents.MAP_ID, freeMapId);
return mapdata;
}

Expand Down Expand Up @@ -166,7 +176,7 @@ public void update(Level level, Entity viewer, MapItemSavedData data) {

if (this.mapOres) {
// recolor ores
OreMapOreColor color = state.getBlock().builtInRegistryHolder().getData(TFDataMaps.ORE_MAP_ORE_COLOR);
OreMapOreColor color = state.typeHolder().getData(TFDataMaps.ORE_MAP_ORE_COLOR);
if (color != null) {
multiset.add(color.color(), 1000);
} else if (!state.isAir() && state.is(Tags.Blocks.ORES)) {
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/twilightforest/item/mapdata/MapDataManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package twilightforest.item.mapdata;

import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.saveddata.maps.MapId;
import org.jspecify.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;

/*
* Most of the content in this class is adapted from implementations in ServerLevel and ClientLevel.
*/
public final class MapDataManager {
private static final Map<MapId, TFMagicMapData> MAGIC_MAP_CLIENT_DATA = new HashMap<>();
private static final Map<MapId, TFMazeMapData> MAZE_MAP_CLIENT_DATA = new HashMap<>();

public static @Nullable TFMagicMapData getServerMagicMapData(ServerLevel serverLevel, MapId id) {
return serverLevel.getServer().getDataStorage().get(TFMagicMapData.magicMapType(id));
}

public static @Nullable TFMazeMapData getServerMazeMapData(ServerLevel serverLevel, MapId id) {
return serverLevel.getServer().getDataStorage().get(TFMazeMapData.mazeMapType(id));
}

public static @Nullable TFMagicMapData getClientMagicMapData(MapId id) {
return MAGIC_MAP_CLIENT_DATA.get(id);
}

public static @Nullable TFMazeMapData getClientMazeMapData(MapId id) {
return MAZE_MAP_CLIENT_DATA.get(id);
}

public static void saveServerMagicMapData(ServerLevel serverLevel, MapId id, TFMagicMapData data) {
serverLevel.getServer().getDataStorage().set(TFMagicMapData.magicMapType(id), data);
}

public static void saveServerMazeMapData(ServerLevel serverLevel, MapId id, TFMazeMapData data) {
serverLevel.getServer().getDataStorage().set(TFMazeMapData.mazeMapType(id), data);
}

public static void saveClientMagicMapData(MapId id, TFMagicMapData data) {
MAGIC_MAP_CLIENT_DATA.put(id, data);
}

public static void saveClientMazeMapData(MapId id, TFMazeMapData data) {
MAZE_MAP_CLIENT_DATA.put(id, data);
}
}
181 changes: 93 additions & 88 deletions src/main/java/twilightforest/item/mapdata/TFMagicMapData.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,124 +3,129 @@
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.*;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.saveddata.SavedData;
import net.minecraft.world.level.saveddata.maps.MapDecoration;
import net.minecraft.world.level.saveddata.maps.MapDecorationType;
import net.minecraft.world.level.saveddata.maps.MapId;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import org.jetbrains.annotations.Nullable;
import net.minecraft.world.level.saveddata.SavedDataType;
import net.minecraft.world.level.saveddata.maps.*;
import org.jspecify.annotations.Nullable;
import twilightforest.TwilightForestMod;
import twilightforest.item.MagicMapItem;
import twilightforest.network.MagicMapPacket;
import twilightforest.util.Codecs;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TFMagicMapData extends MapItemSavedData {
private static final Map<String, TFMagicMapData> CLIENT_DATA = new HashMap<>();
public final List<String> conqueredStructures = new ArrayList<>();

public TFMagicMapData(int x, int z, byte scale, boolean trackpos, boolean unlimited, boolean locked, ResourceKey<Level> dim) {
super(x, z, scale, trackpos, unlimited, locked, dim);
// [VanillaCopy] from MapItemSavedData but with our own fields and constructor
public static final Codec<TFMagicMapData> CODEC = RecordCodecBuilder.create(i ->
i.group(
Level.RESOURCE_KEY_CODEC
.fieldOf("dimension")
.forGetter(m -> m.dimension),
Codec.INT
.fieldOf("xCenter")
.forGetter(m -> m.centerX),
Codec.INT
.fieldOf("zCenter")
.forGetter(m -> m.centerZ),
Codec.BYTE
.optionalFieldOf("scale", (byte) 0)
.forGetter(m -> m.scale),
Codec.BYTE_BUFFER
.fieldOf("colors")
.forGetter(m -> ByteBuffer.wrap(m.colors)),
Codec.BOOL
.optionalFieldOf("trackingPosition", true)
.forGetter(m -> m.trackingPosition),
Codec.BOOL
.optionalFieldOf("unlimitedTracking", false)
.forGetter(m -> m.unlimitedTracking),
Codec.BOOL
.optionalFieldOf("locked", false)
.forGetter(m -> m.locked),
MapBanner.CODEC
.listOf()
.optionalFieldOf("banners", List.of())
.forGetter(m -> List.copyOf(m.bannerMarkers.values())),
MapFrame.CODEC
.listOf()
.optionalFieldOf("frames", List.of())
.forGetter(m -> List.copyOf(m.frameMarkers.values())),
DecorationHolder.CODEC
.listOf()
.optionalFieldOf("decorations", List.of())
.forGetter(m -> {
List<DecorationHolder> holders = new ArrayList<>();
m.decorations.forEach((id, decoration) -> {
if (decoration.type().value().showOnItemFrame()) {
holders.add(new DecorationHolder(id, decoration));
}
});
return holders;
}),
Codec.STRING
.listOf()
.optionalFieldOf("conquered_structures", List.of())
.forGetter(m -> List.copyOf(m.conqueredStructures))
).apply(i, TFMagicMapData::new)
);

// [VanillaCopy] from MapItemSavedData but changed to use our Codec and namespace
public static SavedDataType<TFMagicMapData> magicMapType(MapId id) {
return new SavedDataType<>(TwilightForestMod.prefix(id.key()), () -> {
throw new IllegalStateException("Should never create an empty map saved data");
}, CODEC, DataFixTypes.SAVED_DATA_MAP_DATA);
}

public static TFMagicMapData load(CompoundTag nbt, HolderLookup.Provider provider) {
MapItemSavedData data = MapItemSavedData.load(nbt, provider);
final boolean trackingPosition = !nbt.contains("trackingPosition", 1) || nbt.getBoolean("trackingPosition");
final boolean unlimitedTracking = nbt.getBoolean("unlimitedTracking");
final boolean locked = nbt.getBoolean("locked");
TFMagicMapData tfdata = new TFMagicMapData(data.centerX, data.centerZ, data.scale, trackingPosition, unlimitedTracking, locked, data.dimension);

tfdata.colors = data.colors;
tfdata.bannerMarkers.putAll(data.bannerMarkers);
tfdata.frameMarkers.putAll(data.frameMarkers);

for (DecorationHolder decoration : DecorationHolder.CODEC.listOf()
.parse(provider.createSerializationContext(NbtOps.INSTANCE), nbt.get("decorations"))
.resultOrPartial(error -> TwilightForestMod.LOGGER.warn("Failed to parse map decoration: '{}'", error))
.orElse(List.of())) {
MapDecoration mapdecoration1 = decoration.decoration();
MapDecoration mapdecoration = tfdata.decorations.put(decoration.id(), mapdecoration1);
if (!mapdecoration1.equals(mapdecoration)) {
if (mapdecoration != null && mapdecoration.type().value().trackCount()) {
tfdata.trackedDecorationCount--;
}
// [VanillaCopy] from MapItemSavedData
public TFMagicMapData(int centerX, int centerZ, byte scale, boolean trackingPosition, boolean unlimitedTracking, boolean locked, ResourceKey<Level> dimension) {
super(centerX, centerZ, scale, trackingPosition, unlimitedTracking, locked, dimension);
}

if (decoration.decoration().type().value().trackCount()) {
tfdata.trackedDecorationCount++;
}
tfdata.setDecorationsDirty();
}
// [VanillaCopy] from MapItemSavedData but with our own fields
private TFMagicMapData(ResourceKey<Level> dimension, int centerX, int centerZ, byte scale, ByteBuffer colors, boolean trackingPosition, boolean unlimitedTracking, boolean locked, List<MapBanner> banners, List<MapFrame> frames, List<DecorationHolder> decorations, List<String> conqueredStructures) {
this(centerX, centerZ, (byte) Mth.clamp(scale, 0, 4), trackingPosition, unlimitedTracking, locked, dimension);

if (colors.array().length == 16384) {
this.colors = colors.array();
}

if (nbt.contains("conquered_structures", Tag.TAG_LIST)) {
tfdata.conqueredStructures.clear();
ListTag tag = nbt.getList("conquered_structures", Tag.TAG_STRING);
tag.forEach(tag1 -> tfdata.conqueredStructures.add(tag1.getAsString()));
for(MapBanner banner : banners) {
this.bannerMarkers.put(banner.getId(), banner);
this.addDecoration(banner.getDecoration(), null, banner.getId(), banner.pos().getX(), banner.pos().getZ(), 180.0F, banner.name().orElse(null));
}

return tfdata;
}
for(MapFrame frame : frames) {
this.frameMarkers.put(frame.getId(), frame);
this.addDecoration(MapDecorationTypes.FRAME, null, getFrameKey(frame.entityId()), frame.pos().getX(), frame.pos().getZ(), frame.rotation(), null);
}

@Override
public CompoundTag save(CompoundTag tag, HolderLookup.Provider provider) {
tag = super.save(tag, provider);
for (DecorationHolder holder : decorations) {
MapDecoration newDecoration = holder.decoration();
MapDecoration oldDecoration = this.decorations.put(holder.id(), newDecoration);

List<DecorationHolder> holders = new ArrayList<>();
this.decorations.forEach((s, decoration) -> {
if (decoration.type().value().showOnItemFrame()) {
holders.add(new DecorationHolder(s, decoration));
}
});
tag.put("decorations", DecorationHolder.CODEC.listOf().encodeStart(NbtOps.INSTANCE, holders).getOrThrow());
if (!newDecoration.equals(oldDecoration)) {
if (oldDecoration != null && oldDecoration.type().value().trackCount()) {
this.trackedDecorationCount--;
}

if (!this.conqueredStructures.isEmpty()) {
ListTag conqueredTag = new ListTag();
for (String structure : this.conqueredStructures) {
conqueredTag.add(StringTag.valueOf(structure));
if (newDecoration.type().value().trackCount()) {
this.trackedDecorationCount++;
}
}
tag.put("conquered_structures", conqueredTag);
}

return tag;
}

// [VanillaCopy] Adapted from World.getMapData
@Nullable
public static TFMagicMapData getMagicMapData(Level level, String name) {
if (level instanceof ServerLevel serverLevel) return (TFMagicMapData) serverLevel.getServer().overworld().getDataStorage().get(TFMagicMapData.factory(), name);
else return CLIENT_DATA.get(name);
}

// Like the method above, but if we know we're on client
@Nullable
public static TFMagicMapData getClientMagicMapData(String name) {
return CLIENT_DATA.get(name);
}

// [VanillaCopy] Adapted from World.registerMapData
public static void registerMagicMapData(Level level, TFMagicMapData data, String id) {
if (level instanceof ServerLevel serverLevel) serverLevel.getServer().overworld().getDataStorage().set(id, data);
else CLIENT_DATA.put(id, data);
}

public static Factory<MapItemSavedData> factory() {
return new SavedData.Factory<>(() -> {
throw new IllegalStateException("Should never create an empty map saved data");
}, TFMagicMapData::load, DataFixTypes.SAVED_DATA_MAP_DATA);
this.conqueredStructures.addAll(conqueredStructures);
}

@Nullable
Expand Down Expand Up @@ -149,4 +154,4 @@ public record DecorationHolder(String id, MapDecoration decoration) {
Codecs.DECORATION_CODEC.fieldOf("decoration").forGetter(DecorationHolder::decoration)
).apply(instance, DecorationHolder::new));
}
}
}
Loading
Loading