From e604aae16165c22962cc1cedd03bbe5af2b9869c Mon Sep 17 00:00:00 2001 From: RealDarkStudios <94769968+RealDarkStudios@users.noreply.github.com> Date: Sat, 19 Jul 2025 15:13:49 -0500 Subject: [PATCH 1/5] feat/storage-rework (compat 0.14.16.X) - Start Rework - Reworked Storages to be available to any addon with little work. Storages store many BlockPos so that they can be referenced later - Added interfaces for Storages (IStorage, IServerStorage (no sync), ISyncServerStorage, and IClientStorage) - Added base PMWStorage class - Completely rewrote PMWStorages - Added StorageInstance to hold storage IDs, maps, and storage factories - Added S2CStoragePacket for syncing storage data - Added PMWUtils#isCornerAdjacent, #testAround, #storageCornerAdjacent, and #isRadarCornerAdjacent - Moved PMWNetworking#RADARS_ID to RadarStorage.ID - Deprecated PMWSavedData, replaced by PMWStorageSavedData - Moved ExampleOverlay to the example mod id - ID Overlay now requires you to crouch and renders all overlays - Changed NearbyRadarsCommand to StoragesCommand and added new features. This change remaps /nearbyradars to /storages pmweatherapi:radars Took 1 minute --- gradle.properties | 2 +- .../nullved/pmweatherapi/PMWeatherAPI.java | 8 +- .../client/data/IClientStorage.java | 66 +++++ .../client/radar/RadarClientStorage.java | 73 +---- .../pmweatherapi/client/render/IDOverlay.java | 24 +- .../pmweatherapi/config/PMWClientConfig.java | 2 +- .../pmweatherapi/data/PMWSavedData.java | 2 + .../data/PMWStorageSavedData.java | 66 +++++ .../pmweatherapi/data/PMWStorages.java | 58 ++-- .../nullved/pmweatherapi/event/PMWEvents.java | 39 ++- .../pmweatherapi/example/ExampleOverlay.java | 2 +- .../mixin/BlockBehaviourMixin.java | 10 +- .../mixin/PacketNBTFromClientMixin.java | 2 +- .../pmweatherapi/network/PMWNetworking.java | 23 +- .../pmweatherapi/network/S2CRadarsPacket.java | 61 ++-- .../network/S2CStoragePacket.java | 85 ++++++ .../pmweatherapi/radar/NearbyRadars.java | 14 +- .../radar/RadarServerStorage.java | 121 ++------ .../pmweatherapi/radar/RadarStorage.java | 239 ++-------------- .../pmweatherapi/storage/IServerStorage.java | 124 ++++++++ .../pmweatherapi/storage/IStorage.java | 51 ++++ .../storage/ISyncServerStorage.java | 27 ++ .../pmweatherapi/storage/PMWStorage.java | 270 ++++++++++++++++++ .../pmweatherapi/storage/StorageInstance.java | 85 ++++++ .../nullved/pmweatherapi/util/PMWUtils.java | 68 +++++ 25 files changed, 1031 insertions(+), 491 deletions(-) create mode 100644 src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java create mode 100644 src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/IStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java diff --git a/gradle.properties b/gradle.properties index 99e15c3..54e58b9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ parchment_mappings_version=2024.11.17 mod_id=pmweatherapi mod_name=PMWeatherAPI mod_license=GNU GPL 3.0 -mod_version=0.14.16.2 +mod_version=0.14.16.3-dev+14 mod_group_id=net.nullved mod_authors=NullVed mod_description=An API for interfacing with ProtoManly's Weather Mod diff --git a/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java b/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java index 290e612..ebff77b 100644 --- a/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java +++ b/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java @@ -15,8 +15,10 @@ import net.nullved.pmweatherapi.client.render.IDOverlay; import net.nullved.pmweatherapi.client.render.RadarOverlays; import net.nullved.pmweatherapi.config.PMWClientConfig; +import net.nullved.pmweatherapi.data.PMWStorages; import net.nullved.pmweatherapi.network.PMWNetworking; -import net.nullved.pmweatherapi.radar.RadarMode; +import net.nullved.pmweatherapi.radar.RadarServerStorage; +import net.nullved.pmweatherapi.radar.RadarStorage; import org.slf4j.Logger; @Mod(PMWeatherAPI.MODID) @@ -40,6 +42,8 @@ public PMWeatherAPI(IEventBus modEventBus, ModContainer modContainer) { } private void commonSetup(FMLCommonSetupEvent event) { + PMWStorages.registerStorage(RadarStorage.ID, RadarServerStorage::new); + // if (!ModList.get().isLoaded("pmweather")) { // throw new RuntimeException("ProtoManly's Weather not detected!"); // } @@ -51,7 +55,7 @@ private void registerPayloads(RegisterPayloadHandlersEvent event) { private void clientSetup(FMLClientSetupEvent event) { RadarOverlays.registerOverlay(() -> IDOverlay.INSTANCE); - //RadarOverlays.registerOverlay(() -> ExampleOverlay.INSTANCE); +// RadarOverlays.registerOverlay(() -> ExampleOverlay.INSTANCE); } public static ResourceLocation rl(String path) { diff --git a/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java new file mode 100644 index 0000000..38eb0a0 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java @@ -0,0 +1,66 @@ +package net.nullved.pmweatherapi.client.data; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.IntArrayTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; +import net.nullved.pmweatherapi.network.S2CStoragePacket; +import net.nullved.pmweatherapi.storage.IStorage; + +import java.util.stream.Collectors; + +/** + * The interface defining the client-side implementation of a Storage such as {@link RadarClientStorage} + *

+ * Every time the client changes dimension, a new {@link IClientStorage} is created for each storage. + * @since 0.14.16.3 + */ +public interface IClientStorage extends IStorage { + ClientLevel getLevel(); + + default void syncAll(CompoundTag tag) { + clean(); + syncAdd(tag); + } + + /** + * Syncs data from a {@link S2CStoragePacket} with operation {@code add} into this storage's memory + * @since 0.14.16.3 + */ + default void syncAdd(CompoundTag tag) { + if (tag.contains("list") && tag.getBoolean("list")) { + // list format + ListTag list = tag.getList("data", ListTag.TAG_INT_ARRAY); + add(list.stream().map(t -> { + if (t instanceof IntArrayTag iat) { + return new BlockPos(iat.get(0).getAsInt(), iat.get(1).getAsInt(), iat.get(2).getAsInt()); + } else return null; + }).collect(Collectors.toSet())); + } else { + // not list format + add(NbtUtils.readBlockPos(tag, "data").orElseThrow()); + } + } + + /** + * Syncs data from a {@link S2CStoragePacket} with operation {@code remove} into this storage's memory + * @since 0.14.16.3 + */ + default void syncRemove(CompoundTag tag) { + if (tag.contains("list") && tag.getBoolean("list")) { + // list format + ListTag list = tag.getList("data", ListTag.TAG_INT_ARRAY); + remove(list.stream().map(t -> { + if (t instanceof IntArrayTag iat) { + return new BlockPos(iat.get(0).getAsInt(), iat.get(1).getAsInt(), iat.get(2).getAsInt()); + } else return null; + }).collect(Collectors.toSet())); + } else { + // not list format + remove(NbtUtils.readBlockPos(tag, "data").orElseThrow()); + } + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java index b06f7d8..9afa4f6 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java @@ -1,84 +1,39 @@ package net.nullved.pmweatherapi.client.radar; +import dev.protomanly.pmweather.block.RadarBlock; import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.IntArrayTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtUtils; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; +import net.nullved.pmweatherapi.client.data.IClientStorage; import net.nullved.pmweatherapi.client.data.PMWClientStorages; -import net.nullved.pmweatherapi.network.S2CRadarsPacket; import net.nullved.pmweatherapi.radar.RadarStorage; -import java.util.stream.Collectors; - /** - * A Radar Storage for the client side. - * Every time the client changes dimension, a new {@code RadarClientStorage} will be created - *
- * You should not create a {@code RadarClientStorage} directly, instead, use {@link PMWClientStorages#getRadars()} - * @since 0.14.15.3 + * A {@link IClientStorage} implementation for {@link RadarBlock}s + *

+ * You should not create a {@link RadarClientStorage}, instead, use {@link PMWClientStorages#getRadars()} + * @since 0.14.16.3 */ -public class RadarClientStorage extends RadarStorage { +public class RadarClientStorage extends RadarStorage implements IClientStorage { /** * DO NOT CALL THIS CONSTRUCTOR!!! *
* Get a radar storage from {@link PMWClientStorages#getRadars()} * @param dimension The dimension to create this storage for - * @since 0.14.15.3 + * @since 0.14.16.3 */ public RadarClientStorage(ResourceKey dimension) { super(dimension); } /** - * Gets the level associated with this {@code RadarClientStorage} - * @return The {@link Minecraft} instance {@link Level} - * @since 0.14.15.3 + * Gets the level associated with this {@link RadarClientStorage} + * @return The {@link Minecraft} {@link ClientLevel} + * @since 0.14.16.3 */ @Override - public Level getLevel() { + public ClientLevel getLevel() { return Minecraft.getInstance().level; } - - /** - * Syncs data from a {@link S2CRadarsPacket} with operation {@code add} into this storage's memory - * @since 0.14.15.3 - */ - public void syncAdd(CompoundTag tag) { - if (tag.contains("list") && tag.getBoolean("list")) { - // list format - ListTag list = tag.getList("data", ListTag.TAG_INT_ARRAY); - addRadars(list.stream().map(t -> { - if (t instanceof IntArrayTag iat) { - return new BlockPos(iat.get(0).getAsInt(), iat.get(1).getAsInt(), iat.get(2).getAsInt()); - } else return null; - }).collect(Collectors.toSet())); - } else { - // not list format - addRadar(NbtUtils.readBlockPos(tag, "data").orElseThrow()); - } - } - - /** - * Syncs data from a {@link S2CRadarsPacket} with operation {@code remove} into this storage's memory - * @since 0.14.15.3 - */ - public void syncRemove(CompoundTag tag) { - if (tag.contains("list") && tag.getBoolean("list")) { - // list format - ListTag list = tag.getList("data", ListTag.TAG_INT_ARRAY); - removeRadars(list.stream().map(t -> { - if (t instanceof IntArrayTag iat) { - return new BlockPos(iat.get(0).getAsInt(), iat.get(1).getAsInt(), iat.get(2).getAsInt()); - } else return null; - }).collect(Collectors.toSet())); - } else { - // not list format - removeRadar(NbtUtils.readBlockPos(tag, "data").orElseThrow()); - } - } -} +} \ No newline at end of file diff --git a/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java b/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java index 9a0203b..0a7a0ff 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java +++ b/src/main/java/net/nullved/pmweatherapi/client/render/IDOverlay.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; +import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; @@ -10,6 +11,8 @@ import net.nullved.pmweatherapi.config.PMWClientConfig; import net.nullved.pmweatherapi.radar.RadarMode; +import java.util.function.Supplier; + /** * The overlay for {@link RadarMode} IDs. *
@@ -23,16 +26,35 @@ public class IDOverlay implements IRadarOverlay { @Override public void render(boolean canRender, RenderData renderData, BufferBuilder bufferBuilder, Object... args) { if (!PMWClientConfig.showRadarModeId) return; + if (!Minecraft.getInstance().player.isCrouching()) return; + RadarMode mode = getRadarMode(renderData); PoseStack poseStack = renderData.poseStack(); PMWClientConfig.RadarModeIDSide side = PMWClientConfig.radarModeIDSide; + poseStack.pushPose(); + poseStack.translate(side.x, 1.055f, side.z); - poseStack.mulPose(Axis.ZP.rotationDegrees(side.rotation)); + poseStack.mulPose(Axis.YN.rotationDegrees(side.rotation)); poseStack.scale(0.01f, 0.01f, 0.01f); renderText(Component.literal(mode.getId().toString()), renderData, poseStack); + float lineHeight = 8.0f; + float offset = lineHeight; + for (Supplier overlay: RadarOverlays.getOverlays()) { + poseStack.pushPose(); + poseStack.translate(0, 0, offset); + poseStack.scale(0.6f, 0.6f, 0.6f); + + renderText(Component.literal(overlay.get().getID().toString()).withColor(0x888888), renderData, poseStack); + + poseStack.popPose(); + offset += lineHeight * 0.6f; + } + + poseStack.popPose(); + // While I could reset the pose, it is not strictly necessary // poseStack.scale(100f, 100f, 100f); // poseStack.mulPose(Axis.ZP.rotationDegrees(-side.rotation)); diff --git a/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java b/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java index a778d7b..4436858 100644 --- a/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java +++ b/src/main/java/net/nullved/pmweatherapi/config/PMWClientConfig.java @@ -43,7 +43,7 @@ public enum RadarModeIDSide { SOUTH(180, 2, 2), WEST(-90, -1, 2),; - public int rotation, x, z; + public final int rotation, x, z; RadarModeIDSide(int rotation, int x, int z) { this.rotation = rotation; diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java index 2c1daa3..7e39608 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java @@ -8,7 +8,9 @@ /** * The {@link SavedData} for PMWeatherAPI * @since 0.14.15.3 + * @deprecated Since 0.14.16.3 | Using new Storages system */ +@Deprecated(since = "0.14.16.3", forRemoval = true) public class PMWSavedData extends SavedData { private CompoundTag tag; private RadarStorage radarStorage; diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java b/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java new file mode 100644 index 0000000..301da98 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java @@ -0,0 +1,66 @@ +package net.nullved.pmweatherapi.data; + +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.saveddata.SavedData; +import net.nullved.pmweatherapi.storage.IStorage; + +/** + * A {@link SavedData} instance for {@link IStorage}s + * + * @since 0.14.16.3 + */ +public class PMWStorageSavedData extends SavedData { + private final CompoundTag tag; + private IStorage storage; + + /** + * Gets the factory for loading the {@link SavedData} + * @return A {@link SavedData.Factory} for {@link PMWStorageSavedData} + * @since 0.14.16.3 + */ + public static SavedData.Factory factory() { + return new SavedData.Factory<>(PMWStorageSavedData::new, PMWStorageSavedData::load, null); + } + + public PMWStorageSavedData() { + this.tag = new CompoundTag(); + } + + public PMWStorageSavedData(CompoundTag tag) { + this.tag = tag; + } + + private static PMWStorageSavedData load(CompoundTag compoundTag, HolderLookup.Provider registries) { + return new PMWStorageSavedData(compoundTag); + } + + @Override + public CompoundTag save(CompoundTag compoundTag, HolderLookup.Provider provider) { + return storage.save(compoundTag); + } + + /** + * Sets the {@link IStorage} associated with this {@link Level} or dimension + * @param storage The {@link IStorage} + * @since 0.14.16.3 + */ + public void setStorage(IStorage storage) { + this.storage = storage; + } + + /** + * Gets the {@link SavedData} from the {@link Level} + * @return A data {@link CompoundTag} + * @since 0.14.16.3 + */ + public CompoundTag getTag() { + return tag; + } + + @Override + public boolean isDirty() { + return true; + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java index 33817b6..7eff7f2 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java @@ -1,39 +1,65 @@ package net.nullved.pmweatherapi.data; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; -import net.nullved.pmweatherapi.radar.RadarMode; +import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.radar.RadarServerStorage; -import net.nullved.pmweatherapi.radar.RadarStorage; -import net.nullved.pmweatherapi.util.StringProperty; +import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.storage.StorageInstance; +import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.function.Function; /** * A class holding maps of dimensions to different storages * @since 0.14.15.3 */ public class PMWStorages { - public static final Map, RadarServerStorage> RADARS = new HashMap<>(); + public static final ResourceLocation RADARS = PMWeatherAPI.rl("radars"); + + public static final Map> STORAGE_INSTANCES = new HashMap<>(); + /** - * Gets a {@link RadarServerStorage} for the given dimension - * @param dim The dimension to get the radar storage for + * Gets the map of {@link RadarServerStorage}s * @return The associated dimension's {@link RadarServerStorage} * @since 0.14.15.3 */ - public static RadarServerStorage getRadar(ResourceKey dim) { - return RADARS.get(dim); + public static StorageInstance radars() { + return (StorageInstance) get(RADARS); } - /** - * Gets a {@link RadarServerStorage} for the given dimension - * @param dim The dimension to get the radar storage for - * @return The associated dimension's {@link RadarServerStorage} - * @since 0.14.15.3 - */ - public static RadarServerStorage getRadar(Level dim) { - return getRadar(dim.dimension()); + public static StorageInstance get(ResourceLocation location) { + return STORAGE_INSTANCES.get(location); + } + + public static void set(ResourceLocation location, StorageInstance instance) { + STORAGE_INSTANCES.put(location, instance); + } + + public static Optional> get(ResourceLocation location, Class clazz) { + return STORAGE_INSTANCES.get(location).cast(clazz); + } + + public static Collection> getAll() { + return STORAGE_INSTANCES.values(); + } + + public static void generateForDimension(ServerLevel dimension) { + STORAGE_INSTANCES.forEach((rl, si) -> si.load(dimension)); + } + + public static void removeForDimension(ResourceKey dimension) { + STORAGE_INSTANCES.forEach((rl, si) -> si.remove(dimension)); + } + + public static void registerStorage(ResourceLocation id, Function creator) { + StorageInstance instance = new StorageInstance<>(id, creator); + STORAGE_INSTANCES.put(id, instance); } } diff --git a/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java b/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java index 3d51687..32972ec 100644 --- a/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java +++ b/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java @@ -13,9 +13,11 @@ import net.neoforged.neoforge.event.level.ChunkWatchEvent; import net.neoforged.neoforge.event.level.LevelEvent; import net.nullved.pmweatherapi.PMWeatherAPI; -import net.nullved.pmweatherapi.command.NearbyRadarsCommand; +import net.nullved.pmweatherapi.command.StoragesCommand; import net.nullved.pmweatherapi.data.PMWStorages; import net.nullved.pmweatherapi.radar.RadarServerStorage; +import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.storage.ISyncServerStorage; import java.util.*; @@ -23,12 +25,26 @@ public class PMWEvents { @SubscribeEvent public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) { - PMWStorages.getRadar(event.getEntity().level()).syncAllToPlayer(event.getEntity()); + ResourceKey dimension = event.getEntity().level().dimension(); + + PMWStorages.getAll().forEach(si -> { + IServerStorage storage = si.get(dimension); + if (storage != null) { + if (storage instanceof ISyncServerStorage isss) isss.syncAllToPlayer(event.getEntity()); + } + }); + + PMWeatherAPI.LOGGER.info("Synced all sync-storages to joined player {}", event.getEntity().getDisplayName().getString()); } +// +// @SubscribeEvent +// public static void onPlayerChangeDimension(PlayerEvent.PlayerChangedDimensionEvent event) { +// PMWStorages.getStoragesForDimension(event.getTo()).forEach(iss -> iss.syncAllToPlayer(event.getEntity())); +// } @SubscribeEvent - public static void onChunkLoadEvent(ChunkWatchEvent.Sent event) { - RadarServerStorage radarStorage = PMWStorages.getRadar(event.getLevel()); + public static void onChunkSentEvent(ChunkWatchEvent.Sent event) { + RadarServerStorage radarStorage = PMWStorages.radars().getOrCreate(event.getLevel()); if (radarStorage.shouldRecalculate(event.getChunk().getPos())) { List radars = new ArrayList<>(); LevelAccessor level = event.getLevel(); @@ -38,26 +54,22 @@ public static void onChunkLoadEvent(ChunkWatchEvent.Sent event) { if (level.getBlockState(pos).getBlock() instanceof RadarBlock) radars.add(pos); } - radarStorage.addRadars(radars); - radarStorage.syncAdd(radars); + radarStorage.addAndSync(radars); } } @SubscribeEvent public static void onRegisterCommandsEvent(RegisterCommandsEvent event) { PMWeatherAPI.LOGGER.info("Registering PMWeatherAPI Commands"); - NearbyRadarsCommand.register(event.getDispatcher()); + StoragesCommand.register(event.getDispatcher()); } @SubscribeEvent public static void onLevelLoadEvent(LevelEvent.Load event) { LevelAccessor level = event.getLevel(); if (!level.isClientSide() && level instanceof ServerLevel slevel) { - ResourceKey dimension = slevel.dimension(); - RadarServerStorage radars = new RadarServerStorage(slevel); - radars.read(); - PMWStorages.RADARS.put(dimension, radars); - PMWeatherAPI.LOGGER.info("Loaded radars for dimension {}", slevel.dimension().location()); + PMWStorages.generateForDimension(slevel); + PMWeatherAPI.LOGGER.info("Loaded storages for dimension {}", slevel.dimension().location()); } } @@ -65,7 +77,8 @@ public static void onLevelLoadEvent(LevelEvent.Load event) { public static void onLevelUnloadEvent(LevelEvent.Unload event) { LevelAccessor level = event.getLevel(); if (!level.isClientSide() && level instanceof ServerLevel slevel) { - PMWStorages.RADARS.remove(slevel.dimension()); + PMWStorages.removeForDimension(slevel.dimension()); + PMWeatherAPI.LOGGER.info("Unloaded storages for dimension {}", slevel.dimension().location()); } } } diff --git a/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java b/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java index 36a70e4..29f0f68 100644 --- a/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java +++ b/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java @@ -41,7 +41,7 @@ public void render(boolean canRender, RenderData renderData, BufferBuilder buffe @Override public String getModID() { - return PMWeatherAPI.MODID + "_test"; + return "example"; } private static void renderMarker(BufferBuilder bufferBuilder, Vec3 relative) { diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java index dec4c09..d062fc6 100644 --- a/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java +++ b/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java @@ -17,18 +17,16 @@ public class BlockBehaviourMixin { @Inject(method = "onPlace", at = @At("HEAD")) private static void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston, CallbackInfo ci) { if (state.getBlock() instanceof RadarBlock) { - RadarServerStorage radarStorage = PMWStorages.getRadar(level); - radarStorage.addRadar(pos); - radarStorage.syncAdd(pos); + RadarServerStorage radarStorage = PMWStorages.radars().get(level.dimension()); + radarStorage.addAndSync(pos); } } @Inject(method = "onRemove", at = @At("HEAD")) private static void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston, CallbackInfo ci) { if (state.getBlock() instanceof RadarBlock) { - RadarServerStorage radarStorage = PMWStorages.getRadar(level); - radarStorage.removeRadar(pos); - radarStorage.syncRemove(pos); + RadarServerStorage radarStorage = PMWStorages.radars().get(level.dimension()); + radarStorage.removeAndSync(pos); } } } diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java index ba62f7b..442e4b4 100644 --- a/src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java +++ b/src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java @@ -18,6 +18,6 @@ public class PacketNBTFromClientMixin { @Inject(method = "handle", at = @At(value = "INVOKE", target = "Ldev/protomanly/pmweather/block/entity/RadarBlockEntity;playerRequestsSync(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/core/BlockPos;)V")) private void onHandle(Player player, CallbackInfo ci) { - PMWStorages.getRadar(player.level()).addRadar(NbtUtils.readBlockPos(this.compoundTag, "blockPos").get()); + PMWStorages.radars().get(player.level().dimension()).add(NbtUtils.readBlockPos(this.compoundTag, "blockPos").get()); } } diff --git a/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java b/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java index 74c5670..ee586d1 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java +++ b/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java @@ -14,14 +14,13 @@ import net.nullved.pmweatherapi.PMWeatherAPI; import java.util.function.BiConsumer; +import java.util.function.Function; /** * The base networking class for PMWeatherAPI * @since 0.14.15.3 */ public class PMWNetworking { - public static final ResourceLocation RADARS_ID = PMWeatherAPI.rl("radars"); - /** * Registers all of PMWeatherAPI's packets. Should not be called * @param args The arguments to pass @@ -55,27 +54,27 @@ public static void registerServerboundPacket(Cus * @param A packet extending {@link CustomPacketPayload} * @since 0.14.15.3 */ - public static void registerClientboundPacket(CustomPacketPayload.Type type, StreamCodec codec, BiConsumer handler, Object... args) { + public static void registerClientboundPacket(CustomPacketPayload.Type type, StreamCodec codec, BiConsumer handler, Object... args) { PayloadRegistrar registrar = (PayloadRegistrar) args[0]; IPayloadHandler clientHandler = (pkt, ctx) -> ctx.enqueueWork(() -> handler.accept(pkt, ctx.player())); registrar.playToClient(type, codec, clientHandler); } /** - * Sends all radars to all clients + * Sends all clients a {@link S2CStoragePacket}. The type of packet is dependent on the caller of this method * @param tag The tag to send, generated by the {@link net.nullved.pmweatherapi.radar.RadarServerStorage} - * @since 0.14.15.3 + * @since 0.14.16.3 */ - public static void serverSendRadarsToAll(CompoundTag tag) { - PacketDistributor.sendToAllPlayers(new S2CRadarsPacket(tag)); + public static void serverSendStorageToAll(CompoundTag tag, Function> pkt) { + PacketDistributor.sendToAllPlayers(pkt.apply(tag)); } /** - * Sends all radars to a specific player - * @param tag The tag to send, generated by the {@link net.nullved.pmweatherapi.radar.RadarServerStorage} - * @since 0.14.15.3 + * Sends a {@link S2CStoragePacket} to a specific player. The type of packet is dependent on the caller of this method + * @param tag The tag to send with the packet + * @since 0.14.16.3 */ - public static void serverSendRadarsToPlayer(CompoundTag tag, Player player) { - PacketDistributor.sendToPlayer((ServerPlayer) player, new S2CRadarsPacket(tag)); + public static void serverSendStorageToPlayer(CompoundTag tag, Function> pkt, Player player) { + PacketDistributor.sendToPlayer((ServerPlayer) player, pkt.apply(tag)); } } diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java index d0a4d34..4274422 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java @@ -1,70 +1,41 @@ package net.nullved.pmweatherapi.network; import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.world.entity.player.Player; import net.nullved.pmweatherapi.PMWeatherAPI; -import net.nullved.pmweatherapi.client.radar.RadarClientStorage; import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; /** - * A packet for sending radar information from Server -> Client (S2C) - * @param tag The {@link CompoundTag} to send - * @since 0.14.15.3 + * The packet that syncs radars from the server to the client, using the Storages system + * @since 0.14.16.3 */ -public record S2CRadarsPacket(CompoundTag tag) implements CustomPacketPayload { - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(PMWeatherAPI.rl("s2c_radars")); +public class S2CRadarsPacket extends S2CStoragePacket { + public static final CustomPacketPayload.Type TYPE = new Type<>(PMWeatherAPI.rl("s2c_radars")); public static final StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.COMPOUND_TAG, S2CRadarsPacket::tag, S2CRadarsPacket::new); /** - * Creates a new {@link S2CRadarsPacket} from a {@link RegistryFriendlyByteBuf} - * @param buf The {@link RegistryFriendlyByteBuf} to read a {@link CompoundTag} from - * @since 0.14.15.3 - */ - public S2CRadarsPacket(RegistryFriendlyByteBuf buf) { - this(buf.readNbt()); - } - - /** - * Writes the data into the given {@link FriendlyByteBuf} - * @param buf The {@link FriendlyByteBuf} to write into - * @since 0.14.15.3 + * Creates a new {@link S2CRadarsPacket} + * @param tag The {@link CompoundTag} to send with the packet + * @since 0.14.16.3 */ - public void write(FriendlyByteBuf buf) { - buf.writeNbt(tag); + public S2CRadarsPacket(CompoundTag tag) { + super(tag); } /** - * Handles the packet on the CLIENT SIDE - * @param player The player the packet was sent to - * @since 0.14.15.3 + * Gets the {@link RadarClientStorage} that is receiving data + * @return The {@link RadarClientStorage} + * @since 0.14.16.3 */ - public void handle(Player player) { - try { - String operation = tag.getString("operation"); - RadarClientStorage radarStorage = PMWClientStorages.getRadars(); - - if (operation.equals("add")) { - radarStorage.syncAdd(tag); - } else if (operation.equals("remove")) { - radarStorage.syncRemove(tag); - } else { - PMWeatherAPI.LOGGER.error("Unknown S2CRadarsPacket operation: {}", operation); - } - } catch (Exception e) { - PMWeatherAPI.LOGGER.error("An error occurred when trying to write packet", e); - } + @Override + public RadarClientStorage getStorage() { + return PMWClientStorages.getRadars(); } - /** - * Gets the packet type - * @return The packet type - * @since 0.14.15.3 - */ @Override public Type type() { return TYPE; diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java new file mode 100644 index 0000000..548e8d3 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java @@ -0,0 +1,85 @@ +package net.nullved.pmweatherapi.network; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.world.entity.player.Player; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.storage.IStorage; + +/** + * A base packet for the Storages system that syncs data from the Server -> Client (S2C) + * @param The {@link IClientStorage} that will be synced to + * @since 0.14.16.3 + */ +public abstract class S2CStoragePacket implements CustomPacketPayload { + public CompoundTag tag; + + /** + * Gets the {@link IClientStorage} that will be synced to. + * @return The storage to be synced. + * @since 0.14.16.3 + */ + public abstract T getStorage(); + + public S2CStoragePacket(CompoundTag tag) { + this.tag = tag; + } + + /** + * Creates a new {@link S2CStoragePacket} from a {@link RegistryFriendlyByteBuf} + * @param buf The {@link RegistryFriendlyByteBuf} to read a {@link CompoundTag} from + * @since 0.14.16.3 + */ + public S2CStoragePacket(RegistryFriendlyByteBuf buf) { + this(buf.readNbt()); + } + + /** + * Gets the {@link CompoundTag} send with the packet + * @return The packet data + * @since 0.14.16.3 + */ + public CompoundTag tag() { + return tag; + } + + /** + * Writes the data into the given {@link FriendlyByteBuf} + * @param buf The {@link FriendlyByteBuf} to write into + * @since 0.14.16.3 + */ + public void write(FriendlyByteBuf buf) { + buf.writeNbt(tag); + } + + /** + * Handles the packet on the CLIENT SIDE + * @param player The player the packet was sent to + * @since 0.14.16.3 + */ + public void handle(Player player) { + try { + String operation = tag.getString("operation"); + T storage = getStorage(); + + if (operation.equals("overwrite")) { + storage.syncAll(tag); + } else if (operation.equals("add")) { + storage.syncAdd(tag); + } else if (operation.equals("remove")) { + storage.syncRemove(tag); + } else { + PMWeatherAPI.LOGGER.error("Unknown S2CRadarsPacket operation: {}", operation); + } + } catch (Exception e) { + PMWeatherAPI.LOGGER.error("An error occurred when trying to write packet", e); + } + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java b/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java index 91886c1..783166f 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java @@ -50,7 +50,7 @@ public static NearbyRadars client() { * @since 0.14.15.3 */ public static NearbyRadars get(ResourceKey dim) { - return DIMENSION_MAP.computeIfAbsent(dim, d -> new NearbyRadars(PMWStorages.getRadar(d))); + return DIMENSION_MAP.computeIfAbsent(dim, d -> new NearbyRadars(PMWStorages.radars().get(d))); } /** @@ -73,8 +73,8 @@ public static NearbyRadars get(Level level) { public Set radarsNearBlock(BlockPos pos, double radius) { Set radarList = new HashSet<>(); - for (BlockPos radar: storage.getAllRadars()) { - if (radar.distToCenterSqr(pos.getX(), pos.getY(), pos.getZ()) <= radius * radius) radarList.add(radar); + for (BlockPos radar: storage.getAll()) { + if (Math.abs(radar.distToCenterSqr(pos.getX(), pos.getY(), pos.getZ())) <= radius * radius) radarList.add(radar); } radarList.remove(pos); @@ -92,8 +92,8 @@ public Set radarsNearBlock(BlockPos pos, double radius) { public Set radarsNearChunk(ChunkPos pos, double radius) { Set radarList = new HashSet<>(); - for (BlockPos radar: storage.getAllRadars()) { - if (radar.distToCenterSqr(pos.getMiddleBlockX(), radar.getY(), pos.getMiddleBlockZ()) <= radius * radius) radarList.add(radar); + for (BlockPos radar: storage.getAll()) { + if (Math.abs(radar.distToCenterSqr(pos.getMiddleBlockX(), radar.getY(), pos.getMiddleBlockZ())) <= radius * radius) radarList.add(radar); } return radarList; @@ -109,8 +109,8 @@ public Set radarsNearChunk(ChunkPos pos, double radius) { public Set radarsNearPlayer(Player player, double radius) { Set radarList = new HashSet<>(); - for (BlockPos radar: storage.getAllRadars()) { - if (radar.distToCenterSqr(player.getX(), player.getY(), player.getZ()) <= radius * radius) radarList.add(radar); + for (BlockPos radar: storage.getAll()) { + if (Math.abs(radar.distToCenterSqr(player.getX(), player.getY(), player.getZ())) <= radius * radius) radarList.add(radar); } return radarList; diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java index 58d486a..8242161 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java @@ -1,131 +1,44 @@ package net.nullved.pmweatherapi.radar; -import net.minecraft.core.BlockPos; +import dev.protomanly.pmweather.block.RadarBlock; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.player.Player; -import net.nullved.pmweatherapi.network.PMWNetworking; +import net.nullved.pmweatherapi.client.data.IClientStorage; import net.nullved.pmweatherapi.data.PMWStorages; - -import java.util.Collection; +import net.nullved.pmweatherapi.network.S2CRadarsPacket; +import net.nullved.pmweatherapi.network.S2CStoragePacket; +import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.storage.ISyncServerStorage; /** - * A Radar Storage for the server side. - * There is a {@code RadarServerStorage} for each dimension of a save - *
- * You should not create a {@code RadarServerStorage}, instead, use {@link PMWStorages#getRadar}. - * @since 0.14.15.3 + * A {@link IServerStorage} implementation for {@link RadarBlock}s + *

+ * You should not create a {@link RadarServerStorage}, instead, use {@link PMWStorages#radars()} + * + * @since 0.14.16.3 */ -public class RadarServerStorage extends RadarStorage { +public class RadarServerStorage extends RadarStorage implements ISyncServerStorage { private final ServerLevel level; /** * DO NOT CALL THIS CONSTRUCTOR!!! *
- * Get a radar storage from {@link PMWStorages#getRadar} + * Get a radar storage from {@link PMWStorages#radars()} * @param level The level to create this storage for - * @since 0.14.15.3 + * @since 0.14.16.3 */ public RadarServerStorage(ServerLevel level) { super(level.dimension()); this.level = level; } - /** - * Gets the level associated with this {@code RadarServerStorage} - * @return A {@link ServerLevel} - * @since 0.14.15.3 - */ @Override public ServerLevel getLevel() { return level; } - /** - * Syncs all radars to the given player - * @param player The {@link Player} to sync all radars to - * @since 0.14.15.3 - */ - public void syncAllToPlayer(Player player) { - CompoundTag tag = new CompoundTag(); - tag.putString("operation", "add"); - tag.putBoolean("list", true); - - ListTag list = new ListTag(); - for (BlockPos pos: getAllRadars()) { - list.add(NbtUtils.writeBlockPos(pos)); - } - - tag.put("data", list); - - PMWNetworking.serverSendRadarsToPlayer(tag, player); - } - - /** - * Syncs a new radar to all clients - * @param pos The {@link BlockPos} of the new radar - * @since 0.14.15.3 - */ - public void syncAdd(BlockPos pos) { - CompoundTag tag = new CompoundTag(); - tag.putString("operation", "add"); - tag.put("data", NbtUtils.writeBlockPos(pos)); - - PMWNetworking.serverSendRadarsToAll(tag); - } - - /** - * Syncs multiple new radars to all clients - * @param posList A {@link Collection} of {@link BlockPos} to sync - * @since 0.14.15.3 - */ - public void syncAdd(Collection posList) { - CompoundTag tag = new CompoundTag(); - tag.putString("operation", "add"); - tag.putBoolean("list", true); - - ListTag list = new ListTag(); - for (BlockPos pos: posList) { - list.add(NbtUtils.writeBlockPos(pos)); - } - - tag.put("data", list); - - PMWNetworking.serverSendRadarsToAll(tag); - } - - /** - * Syncs a radar removal to all clients - * @param pos The {@link BlockPos} of the radar to remove - * @since 0.14.15.3 - */ - public void syncRemove(BlockPos pos) { - CompoundTag tag = new CompoundTag(); - tag.putString("operation", "remove"); - tag.put("data", NbtUtils.writeBlockPos(pos)); - - PMWNetworking.serverSendRadarsToAll(tag); - } - - /** - * Syncs multiple radar removals to all clients - * @param posList A {@link Collection} of {@link BlockPos} to sync - * @since 0.14.15.3 - */ - public void syncRemove(Collection posList) { - CompoundTag tag = new CompoundTag(); - tag.putString("operation", "remove"); - tag.putBoolean("list", true); - - ListTag list = new ListTag(); - for (BlockPos pos: posList) { - list.add(NbtUtils.writeBlockPos(pos)); - } - - tag.put("data", list); - - PMWNetworking.serverSendRadarsToAll(tag); + @Override + public S2CStoragePacket packet(CompoundTag tag) { + return new S2CRadarsPacket(tag); } } diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java index 8a439e6..60e39f4 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java @@ -1,240 +1,35 @@ package net.nullved.pmweatherapi.radar; -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtUtils; +import dev.protomanly.pmweather.block.RadarBlock; import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.PMWeatherAPI; -import net.nullved.pmweatherapi.data.PMWSavedData; - -import java.util.*; -import java.util.stream.Collectors; +import net.nullved.pmweatherapi.storage.IStorage; +import net.nullved.pmweatherapi.storage.PMWStorage; /** - * Saves all the radars to a file to be saved and loaded from + * A {@link IStorage} implementation for {@link RadarBlock}s + * + * @since 0.14.16.3 */ -public abstract class RadarStorage { - private Map> radars = new HashMap<>(); - private Map checkTimes = new HashMap<>(); - private ResourceKey dimension; - - public abstract Level getLevel(); +public abstract class RadarStorage extends PMWStorage { + public static final ResourceLocation ID = PMWeatherAPI.rl("radars"); public RadarStorage(ResourceKey dimension) { - this.dimension = dimension; - } - - public Set getAllRadars() { - return radars.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); - } - - public Set getRadarsInChunk(ChunkPos pos) { - return radars.getOrDefault(pos, Set.of()); - } - - public boolean shouldRecalculate(ChunkPos pos) { - if (!checkTimes.containsKey(pos)) { - checkTimes.put(pos, System.currentTimeMillis()); - return true; - } - - return checkTimes.get(pos) - System.currentTimeMillis() > 30000L; - } - - public void addRadar(BlockPos pos) { - ChunkPos chunkPos = new ChunkPos(pos); - Set set = radars.computeIfAbsent(chunkPos, c -> new HashSet<>()); - set.add(pos); - radars.put(chunkPos, set); + super(dimension); } - public void addRadars(Collection pos) { - pos.forEach(this::addRadar); - } - - public void removeRadar(BlockPos pos) { - ChunkPos chunkPos = new ChunkPos(pos); - Set set = radars.computeIfAbsent(chunkPos, c -> new HashSet<>()); - set.remove(pos); - radars.put(chunkPos, set); - } - - public void removeRadars(Collection pos) { - pos.forEach(this::removeRadar); - } - - public CompoundTag save(CompoundTag tag) { - PMWeatherAPI.LOGGER.info("Saving radars to level..."); - tag.putInt("version", 1); - tag.putLong("saveTime", System.currentTimeMillis()); - - for (Map.Entry> entry : radars.entrySet()) { - ListTag blockList = new ListTag(); - entry.getValue().forEach(blockPos -> blockList.add(NbtUtils.writeBlockPos(blockPos))); - tag.put(String.valueOf(entry.getKey().toLong()), blockList); - } + public abstract Level getLevel(); - PMWeatherAPI.LOGGER.info("Saved radars to level"); - return tag; + @Override + public ResourceLocation getId() { + return ID; } - public void read() { - PMWSavedData savedData = ((ServerLevel) this.getLevel()).getDataStorage().computeIfAbsent(PMWSavedData.factory(), "pmweatherapi_radars"); - savedData.setRadarStorage(this); - PMWeatherAPI.LOGGER.info("Reading radars from level..."); - CompoundTag data = savedData.getTag(); - Set chunks = data.getAllKeys(); - chunks.removeAll(Set.of("version", "saveTime")); - - for (String chunk: chunks) { - Set radars = new HashSet<>(); - - ListTag blockList = (ListTag) data.get(chunk); - for (int i = 0; i < blockList.size(); i++) { - int[] bp = blockList.getIntArray(i); - radars.add(new BlockPos(bp[0], bp[1], bp[2])); - } - - this.radars.put(new ChunkPos(Long.parseLong(chunk)), radars); - } + @Override + public int version() { + return 1; } - -// private static final String DATA_FOLDER = "data/pmweatherapi"; -// private static final String DATA_FILE = "radars.json"; -// private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); -// -// /** -// * Reads all radars from a save or server -// *
-// * For each dimension ({@link ResourceKey}), there is a {@link Map} mapping a {@link ChunkPos} to all the saved radar's {@link BlockPos}'s -// * @return All saved radars across all dimensions -// */ -// public static Map, Map>> readAllRadars() { -// Radars.clear(); -// Map, Map>> dimensionToRadar = new HashMap<>(); -// -// try { -// Path worldDir = getWorldSaveDirectory(); -// Path modDir = worldDir.resolve(DATA_FOLDER); -// Path file = modDir.resolve(DATA_FILE); -// -// if (!file.toFile().exists()) { -// Radars.clear(); -// PMWeatherAPI.LOGGER.warn("No radars file for this save!"); -// return dimensionToRadar; -// } -// -// JsonObject root; -// try (FileReader fr = new FileReader(file.toFile())) { -// root = JsonParser.parseReader(fr).getAsJsonObject(); -// } catch (IOException e) { -// PMWeatherAPI.LOGGER.warn("Failed to read radars.json! Error:", e); -// return dimensionToRadar; -// } -// -// int version = root.has("version") ? root.get("version").getAsInt() : 1; -// if (version > 1) { -// PMWeatherAPI.LOGGER.info("Radars file has newer version {} than supported (1), attemping load regardless", version); -// } -// -// root.entrySet() -// .stream() -// .filter(e -> !e.getKey().equals("version") && !e.getKey().equals("saveTime")) -// .forEach(e -> { -// ResourceKey dim = ResourceKey.create(Registries.DIMENSION, ResourceLocation.parse(e.getKey())); -// Map> radarMap = readRadarsForDimension(new HashMap<>(), e.getValue()); -// dimensionToRadar.put(dim, radarMap); -// }); -// PMWeatherAPI.LOGGER.info("Loaded saved radar data!"); -// } catch (Exception e) { -// PMWeatherAPI.LOGGER.warn("Unable to load saved radar data! Error:", e); -// } -// -// return dimensionToRadar; -// } -// -// /** -// * Saves all radars to data/pmweatherapi/radars.json -// */ -// public static void saveAllRadars() { -// try { -// Path worldDir = getWorldSaveDirectory(); -// Path modDir = worldDir.resolve(DATA_FOLDER); -// Files.createDirectories(modDir); -// Path file = modDir.resolve(DATA_FILE); -// -// JsonObject root = new JsonObject(); -// root.addProperty("version", 1); -// root.addProperty("saveTime", System.currentTimeMillis()); -// -// for (Map.Entry, Radars> radars: Radars.getAllDimensions().entrySet()) { -// root.add(radars.getKey().location().toString(), saveRadarsForDimension(radars.getKey(), radars.getValue())); -// } -// -// Path tmp = modDir.resolve(DATA_FILE + ".tmp"); -// -// try (FileWriter writer = new FileWriter(tmp.toFile())) { -// GSON.toJson(root, writer); -// writer.flush(); -// } -// -// Files.move(tmp, file, StandardCopyOption.REPLACE_EXISTING); -// PMWeatherAPI.LOGGER.info("Saved radars for all dimensions to {}/{}", DATA_FOLDER, DATA_FILE); -// } catch (IOException e) { -// PMWeatherAPI.LOGGER.warn("Failed to save radars.json! Error:", e); -// } -// } -// -// protected static JsonObject saveRadarsForDimension(ResourceKey dimension, Radars radars) { -// JsonObject root = new JsonObject(); -// Map> radarMap = radars.getRadarMap(); -// -// for (Map.Entry> entry: radarMap.entrySet()) { -// if (entry.getValue().isEmpty()) continue; -// JsonArray array = new JsonArray(); -// -// for (BlockPos pos: entry.getValue()) { -// JsonObject obj = new JsonObject(); -// obj.addProperty("x", pos.getX()); -// obj.addProperty("y", pos.getY()); -// obj.addProperty("z", pos.getZ()); -// array.add(obj); -// } -// -// root.add(String.valueOf(entry.getKey().toLong()), array); -// } -// -// return root; -// } -// -// protected static Map> readRadarsForDimension(Map> radarMap, JsonElement json) { -// if (json.isJsonObject()) { -// JsonObject root = json.getAsJsonObject(); -// -// for (Map.Entry entry: root.entrySet()) { -// JsonArray array = entry.getValue().getAsJsonArray(); -// if (array.isEmpty()) continue; -// -// ChunkPos chunkPos = new ChunkPos(Long.parseLong(entry.getKey())); -// Set radars = new HashSet<>(); -// -// for (JsonElement e: array) { -// JsonObject obj = e.getAsJsonObject(); -// int x = obj.get("x").getAsInt(); -// int y = obj.get("y").getAsInt(); -// int z = obj.get("z").getAsInt(); -// radars.add(new BlockPos(x, y, z)); -// } -// -// radarMap.put(chunkPos, radars); -// } -// } -// -// return radarMap; -// } } diff --git a/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java new file mode 100644 index 0000000..e373b36 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java @@ -0,0 +1,124 @@ +package net.nullved.pmweatherapi.storage; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.network.PMWNetworking; +import net.nullved.pmweatherapi.network.S2CStoragePacket; + +import java.util.Collection; + +/** + * The Server Storage interface. + * There is a {@link IServerStorage} for each dimension of a save + *
+ * You should only create a {@link IServerStorage} for a level once, instead, use {@link PMWStorages} if built-in or a custom storage handler. + * @since 0.14.16.3 + */ +public interface IServerStorage extends IStorage { + /** + * Gets the level associated with this {@link IServerStorage} + * @return A {@link ServerLevel} + * @since 0.14.16.3 + */ + ServerLevel getLevel(); + + /** + * Generates a {@link S2CStoragePacket} to be sent to the client + * @param tag The {@link CompoundTag} to be sent + * @return A {@link S2CStoragePacket} instance + * @since 0.14.16.3 + */ + S2CStoragePacket packet(CompoundTag tag); + + /** + * Syncs all {@link BlockPos} from the storage to the given player + * @param player The {@link Player} to sync all data to + * @since 0.14.16.3 + */ + default void syncAllToPlayer(Player player) { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "overwrite"); + tag.putBoolean("list", true); + + ListTag list = new ListTag(); + for (BlockPos pos: getAll()) { + list.add(NbtUtils.writeBlockPos(pos)); + } + + tag.put("data", list); + + PMWNetworking.serverSendStorageToPlayer(tag, this::packet, player); + } + + /** + * Syncs new {@link BlockPos} to all clients + * @param pos The new {@link BlockPos} + * @since 0.14.16.3 + */ + default void syncAdd(BlockPos pos) { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "add"); + tag.put("data", NbtUtils.writeBlockPos(pos)); + + PMWNetworking.serverSendStorageToAll(tag, this::packet); + } + + /** + * Syncs multiple new {@link BlockPos} to all clients + * @param posList A {@link Collection} of {@link BlockPos} to sync + * @since 0.14.16.3 + */ + default void syncAdd(Collection posList) { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "add"); + tag.putBoolean("list", true); + + ListTag list = new ListTag(); + for (BlockPos pos: posList) { + list.add(NbtUtils.writeBlockPos(pos)); + } + + tag.put("data", list); + + PMWNetworking.serverSendStorageToAll(tag, this::packet); + } + + /** + * Syncs a {@link BlockPos} removal to all clients + * @param pos The {@link BlockPos} of the radar to remove + * @since 0.14.16.3 + */ + default void syncRemove(BlockPos pos) { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "remove"); + tag.put("data", NbtUtils.writeBlockPos(pos)); + + PMWNetworking.serverSendStorageToAll(tag, this::packet); + } + + /** + * Syncs multiple {@link BlockPos} removals to all clients + * @param posList A {@link Collection} of {@link BlockPos} to sync + * @since 0.14.16.3 + */ + default void syncRemove(Collection posList) { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "remove"); + tag.putBoolean("list", true); + + ListTag list = new ListTag(); + for (BlockPos pos: posList) { + list.add(NbtUtils.writeBlockPos(pos)); + } + + tag.put("data", list); + + PMWNetworking.serverSendStorageToAll(tag, this::packet); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java new file mode 100644 index 0000000..0156cc4 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java @@ -0,0 +1,51 @@ +package net.nullved.pmweatherapi.storage; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.radar.RadarStorage; + +import java.util.Collection; +import java.util.Set; + +/** + * The interface defining a Storage such as {@link RadarStorage} + *

+ * On the server-side, there is a {@link IServerStorage} for each dimension of a world. + * On the client-side, there is one {@link IClientStorage} for each player on a world. + *

+ * To add or remove {@link BlockPos}, use {@link #add} and {@link #remove}. + * To get all {@link BlockPos} use {@link #getAll()} or {@link #getInChunk(ChunkPos)} + *

+ * Optionally, Storages can be numerically versioned, however, you must write you own version mismatch handler + *

+ * For method definitions, see {@link PMWStorage} + * + * @see PMWStorage + * @since 0.14.16.3 + */ +public interface IStorage { + Level getLevel(); + ResourceLocation getId(); + int version(); + + void clean(); + + Set getAll(); + Set getAllWithinRange(BlockPos base, double radius); + Set getInChunk(ChunkPos pos); + Set getInAdjacentChunks(ChunkPos pos); + + boolean shouldRecalculate(ChunkPos pos); + + void add(BlockPos pos); + void add(Collection pos); + void remove(BlockPos pos); + void remove(Collection pos); + + CompoundTag save(CompoundTag tag); + void read(); +} diff --git a/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java new file mode 100644 index 0000000..ac3f453 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java @@ -0,0 +1,27 @@ +package net.nullved.pmweatherapi.storage; + +import net.minecraft.core.BlockPos; + +import java.util.Collection; + +public interface ISyncServerStorage extends IServerStorage { + default void addAndSync(BlockPos pos) { + this.add(pos); + this.syncAdd(pos); + } + + default void addAndSync(Collection pos) { + this.add(pos); + this.syncAdd(pos); + } + + default void removeAndSync(BlockPos pos) { + this.remove(pos); + this.syncRemove(pos); + } + + default void removeAndSync(Collection pos) { + this.remove(pos); + this.syncRemove(pos); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java new file mode 100644 index 0000000..40f212a --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java @@ -0,0 +1,270 @@ +package net.nullved.pmweatherapi.storage; + +import dev.protomanly.pmweather.block.RadarBlock; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.neoforged.neoforge.event.level.ChunkWatchEvent; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; +import net.nullved.pmweatherapi.data.PMWStorageSavedData; +import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.event.PMWEvents; +import net.nullved.pmweatherapi.radar.RadarServerStorage; +import net.nullved.pmweatherapi.radar.RadarStorage; + +import java.util.*; +import java.util.stream.Collectors; + + +/** + * The basic {@link IStorage} implementation that should cover most, if not all, use-cases. + *

+ * Using this class automatically adds your storage to {@link PMWStorages}, + * which allows for easy retrieval using {@link PMWStorages#get(ResourceLocation)} + *

+ * A "Storage" saves and maintains a list of {@link BlockPos} to the {@link Level} that can be reloaded on world load. + * It does this by separating each {@link BlockPos} by chunk (more specifically, by {@link ChunkPos}) + *

+ * Any {@link BlockPos} can be saved, regardless of the type of {@link Block}. + * The use case for {@link RadarStorage} specifically is to store the positions of {@link RadarBlock}s in the world + *

+ * {@link PMWStorage} does not handle syncing radars from the server to the client, instead, + * implement {@link IServerStorage} on a Server Storage and {@link IClientStorage} on a Client Storage + *

+ * For a full implementation example, see {@link RadarStorage}, {@link RadarServerStorage}, and {@link RadarClientStorage} + * + * @see IStorage + * @see IServerStorage + * @see IClientStorage + * @since 0.14.16.3 + */ +public abstract class PMWStorage implements IStorage { + /** + * A {@link Set} of {@link BlockPos} split up by {@link ChunkPos} + * @since 0.14.16.3 + */ + private Map> positions = new HashMap<>(); + /** + * The times each {@link ChunkPos} was last checked + * @since 0.14.16.3 + */ + private Map checkTimes = new HashMap<>(); + /** + * The dimension to store {@link BlockPos} for + * @since 0.14.16.3 + */ + private ResourceKey dimension; + + @Override + public void clean() { + positions.clear(); + checkTimes.clear(); + } + + /** + * Gets the level associated with this {@link IStorage}. + * For the client side, it returns the {@link ClientLevel}. + * For the server side, it returns a {@link ServerLevel}. + * + * @return A {@link Level} instance + * @since 0.14.16.3 + */ + public abstract Level getLevel(); + + /** + * The {@link ResourceLocation} ID of this {@link IStorage}. + * Used primarily for saving to the file at {@code data/_.dat}. + * + * @return A {@link ResourceLocation} + * @since 0.14.16.3 + */ + public abstract ResourceLocation getId(); + + /** + * The version of this {@link IStorage}. + * To disable version data from being saved, return {@code -1} + * + * @return The version of the saved data + * @since 0.14.16.3 + */ + public abstract int version(); + + /** + * The base constructor + * + * @param dimension The dimension of the {@link IStorage} + * @since 0.14.16.3 + */ + public PMWStorage(ResourceKey dimension) { + this.dimension = dimension; + } + + /** + * Gets a {@link Set} of every {@link BlockPos} saved in this {@link IStorage}, regardless of {@link ChunkPos} + * + * @return Every saved {@link BlockPos} + * @since 0.14.16.3 + */ + public Set getAll() { + return positions.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + } + + @Override + public Set getAllWithinRange(BlockPos base, double radius) { + int chunks = (int) Math.ceil(radius / 16.0F) + 1; + ChunkPos cpos = new ChunkPos(base); + + HashSet set = new HashSet<>(); + for (int x = -chunks; x <= chunks; x++) { + for (int z = -chunks; z <= chunks; z++) { + for (BlockPos candidate: getInChunk(new ChunkPos(cpos.x + x, cpos.z + z))) { + if (Math.abs(base.distToCenterSqr(candidate.getX(), candidate.getY(), candidate.getZ())) <= radius * radius) set.add(candidate); + } + } + } + + return set; + } + + /** + * Gets the {@link Set} of {@link BlockPos} for this {@link ChunkPos} + * + * @param pos The {@link ChunkPos} to search + * @return A {@link Set} of the {@link BlockPos} in this chunk + * @since 0.14.16.3 + */ + public Set getInChunk(ChunkPos pos) { + return positions.getOrDefault(pos, Set.of()); + } + + @Override + public Set getInAdjacentChunks(ChunkPos pos) { + Set set = new HashSet<>(); + for (int x = -1; x <= 1; x++) { + for (int z = -1; z <= 1; z++) { + set.addAll(getInChunk(new ChunkPos(pos.x + x, pos.z + z))); + } + } + return set; + } + + /** + * Determines if the data for the given {@link ChunkPos} is older than 30 seconds or does not exist. + * Intended to be used while listening to a {@link ChunkWatchEvent.Sent} event (See {@link PMWEvents}) + * + * @param pos The {@link ChunkPos} to check + * @return Whether the data should be recalculated or not + * @since 0.14.16.3 + */ + public boolean shouldRecalculate(ChunkPos pos) { + if (!checkTimes.containsKey(pos)) { + checkTimes.put(pos, System.currentTimeMillis()); + return true; + } + + return checkTimes.get(pos) - System.currentTimeMillis() > 30000L; + } + + /** + * Adds a single {@link BlockPos} to the {@link IStorage} + * + * @param pos The new {@link BlockPos} + * @since 0.14.16.3 + */ + public void add(BlockPos pos) { + ChunkPos chunkPos = new ChunkPos(pos); + Set set = positions.computeIfAbsent(chunkPos, c -> new HashSet<>()); + set.add(pos); + positions.put(chunkPos, set); + } + + /** + * Adds multiple new {@link BlockPos} to the {@link IStorage} + * + * @param pos A {@link Collection} of new {@link BlockPos} + * @since 0.14.16.3 + */ + public void add(Collection pos) { + pos.forEach(this::add); + } + + /** + * Removes a single {@link BlockPos} from the {@link IStorage} + * + * @param pos The {@link BlockPos} to remove + * @since 0.14.16.3 + */ + public void remove(BlockPos pos) { + ChunkPos chunkPos = new ChunkPos(pos); + Set set = positions.computeIfAbsent(chunkPos, c -> new HashSet<>()); + set.remove(pos); + positions.put(chunkPos, set); + } + + /** + * Removes multiple {@link BlockPos} from the {@link IStorage} + * + * @param pos A {@link Collection} of {@link BlockPos} to remove + * @since 0.14.16.3 + */ + public void remove(Collection pos) { + pos.forEach(this::remove); + } + + /** + * Saves the data of this {@link IStorage} to a {@link CompoundTag} + * + * @param tag The pre-existing {@link CompoundTag} + * @return A {@link CompoundTag} with storage data + * @since 0.14.16.3 + */ + public CompoundTag save(CompoundTag tag) { + PMWeatherAPI.LOGGER.info("Saving storage {} to level...", getId()); + if (version() != -1) tag.putInt("version", version()); + tag.putLong("saveTime", System.currentTimeMillis()); + + for (Map.Entry> entry : positions.entrySet()) { + ListTag blockList = new ListTag(); + entry.getValue().forEach(blockPos -> blockList.add(NbtUtils.writeBlockPos(blockPos))); + tag.put(String.valueOf(entry.getKey().toLong()), blockList); + } + + PMWeatherAPI.LOGGER.info("Saved storage {} to level", getId()); + return tag; + } + + /** + * Reads the saved data from the {@link Level} and initializes this {@link IStorage} with the data + * @since 0.14.16.3 + */ + public void read() { + PMWStorageSavedData savedData = ((ServerLevel) this.getLevel()).getDataStorage().computeIfAbsent(PMWStorageSavedData.factory(), getId().toString().replace(":", "_")); + savedData.setStorage(this); + PMWeatherAPI.LOGGER.info("Reading storage {} from level...", getId()); + CompoundTag data = savedData.getTag(); + Set chunks = data.getAllKeys(); + chunks.removeAll(Set.of("version", "saveTime")); + + for (String chunk : chunks) { + Set radars = new HashSet<>(); + + ListTag blockList = (ListTag) data.get(chunk); + for (int i = 0; i < blockList.size(); i++) { + int[] bp = blockList.getIntArray(i); + radars.add(new BlockPos(bp[0], bp[1], bp[2])); + } + + this.positions.put(new ChunkPos(Long.parseLong(chunk)), radars); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java new file mode 100644 index 0000000..1f3d200 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java @@ -0,0 +1,85 @@ +package net.nullved.pmweatherapi.storage; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.nullved.pmweatherapi.PMWeatherAPI; + +import java.util.*; +import java.util.function.Function; + +public class StorageInstance { + private final ResourceLocation id; + private final HashMap, S> map = new HashMap<>(); + private final Function creator; + + public StorageInstance(ResourceLocation id, Function creator) { + this.id = id; + this.creator = creator; + } + + public ResourceLocation id() { + return id; + } + + public HashMap, S> getBackingMap() { + return map; + } + + public void clear() { + this.map.clear(); + } + + public Set> keySet() { + return map.keySet(); + } + + public Collection values() { + return map.values(); + } + + public Set, S>> entrySet() { + return map.entrySet(); + } + + public S get(ResourceKey dimension) { + return map.get(dimension); + } + + public S getOrCreate(ServerLevel level) { + return map.computeIfAbsent(level.dimension(), dim -> creator.apply(level)); + } + + public Optional> cast(Class clazz) { + if (map.isEmpty()) return Optional.of(new StorageInstance<>(id(), sl -> (O) creator.apply(sl))); + + S val = map.values().stream().findFirst().orElseThrow(); + if (clazz.isAssignableFrom(val.getClass())) { + // almost unevitable to + @SuppressWarnings("unchecked") + StorageInstance casted = new StorageInstance<>(id(), sl -> (O) creator.apply(sl)); + HashMap, O> backingMap = casted.getBackingMap(); + + try { + + for (Map.Entry, S> entry : this.map.entrySet()) { + backingMap.put(entry.getKey(), clazz.cast(entry.getValue())); + } + + return Optional.of(casted); + } catch (ClassCastException e) { + PMWeatherAPI.LOGGER.error("Could not cast {} to {}", val.getClass().getSimpleName(), clazz.getSimpleName()); + return Optional.empty(); + } + } else return Optional.empty(); + } + + public void load(ServerLevel level) { + map.put(level.dimension(), creator.apply(level)); + } + + public void remove(ResourceKey dimension) { + map.remove(dimension); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java b/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java index a287e83..2f35d03 100644 --- a/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java +++ b/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java @@ -2,10 +2,49 @@ import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.radar.NearbyRadars; +import net.nullved.pmweatherapi.storage.IStorage; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; public class PMWUtils { + public static boolean isCornerAdjacent(BlockPos a, BlockPos b) { + int dx = Math.abs(a.getX() - b.getX()); + int dy = Math.abs(a.getY() - b.getY()); + int dz = Math.abs(a.getZ() - b.getZ()); + + return (dx <= 1 && dy <= 1 && dz <= 1) && (dx + dy + dz > 0); + } + + public static Set testAround(BlockPos pos, Function test) { + HashSet set = new HashSet<>(); + + for (int x = -1; x <= 1; x++) { + for (int y = -1; y <= 1; y++) { + for (int z = -1; z <= 1; z++) { + if (x == 0 && y == 0 && z == 0) continue; + if (test.apply(pos.offset(x, y, z))) set.add(pos.offset(x, y, z)); + } + } + } + + return set; + } + + public static Set storageCornerAdjacent(IStorage storage, BlockPos pos) { + HashSet set = new HashSet<>(); + + for (BlockPos bp: storage.getInAdjacentChunks(new ChunkPos(pos))) { + if (isCornerAdjacent(bp, pos)) set.add(bp); + } + + return set; + } + /** * Determines if a radar is next to the given {@link BlockPos} * @param dim The dimension to search in @@ -27,4 +66,33 @@ public static boolean isRadarAdjacent(ResourceKey dim, BlockPos pos) { public static boolean isRadarAdjacent(Level level, BlockPos pos) { return isRadarAdjacent(level.dimension(), pos); } + + /** + * Determines if a radar is within 1 block, including diagonally, to the given {@link BlockPos} + * @param dim The dimension to search in + * @param pos The {@link BlockPos} to look by + * @return {@code true} if there is a radar adjacent to this block, {@code false} otherwise + * @since 0.14.16.3 + */ + public static boolean isRadarCornerAdjacent(ResourceKey dim, BlockPos pos) { + Set nearby = NearbyRadars.get(dim).radarsNearBlock(pos, 3); + + boolean adj = false; + for (BlockPos nearbyPos : nearby) { + adj |= isCornerAdjacent(nearbyPos, pos); + } + + return adj; + } + + /** + * Determines if a radar is within 1 block, including diagonally, to the given {@link BlockPos}. + * @param level The {@link Level} to search in + * @param pos The {@link BlockPos} to look by + * @return {@code true} if there is a radar adjacent to this block, {@code false} otherwise + * @since 0.14.16.3 + */ + public static boolean isRadarCornerAdjacent(Level level, BlockPos pos) { + return isRadarCornerAdjacent(level.dimension(), pos); + } } From 2cd6794b31d1d19091613482089c3e425861168c Mon Sep 17 00:00:00 2001 From: RealDarkStudios <94769968+RealDarkStudios@users.noreply.github.com> Date: Sat, 19 Jul 2025 15:15:13 -0500 Subject: [PATCH 2/5] feat/storage-rework (compat 0.14.16.X) - Push command updates - Changed NearbyRadarsCommand to StoragesCommand and added new features. This change remaps /nearbyradars to /storages pmweatherapi:radars Took 1 minute --- .../command/NearbyRadarsCommand.java | 61 ------------ .../pmweatherapi/command/StoragesCommand.java | 97 +++++++++++++++++++ 2 files changed, 97 insertions(+), 61 deletions(-) delete mode 100644 src/main/java/net/nullved/pmweatherapi/command/NearbyRadarsCommand.java create mode 100644 src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java diff --git a/src/main/java/net/nullved/pmweatherapi/command/NearbyRadarsCommand.java b/src/main/java/net/nullved/pmweatherapi/command/NearbyRadarsCommand.java deleted file mode 100644 index aba36a8..0000000 --- a/src/main/java/net/nullved/pmweatherapi/command/NearbyRadarsCommand.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.nullved.pmweatherapi.command; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.context.CommandContext; -import net.minecraft.ChatFormatting; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.player.Player; -import net.nullved.pmweatherapi.PMWeatherAPI; -import net.nullved.pmweatherapi.radar.NearbyRadars; -import net.nullved.pmweatherapi.util.PMWUtils; - -import java.util.Set; - -public class NearbyRadarsCommand { - public static void register(CommandDispatcher dispatcher) { - dispatcher.register( - Commands.literal("nearbyradars") - .then(Commands.argument("radius", IntegerArgumentType.integer(1, 2048)) - .executes(NearbyRadarsCommand::nearbyRadars)) - .executes(NearbyRadarsCommand::nearbyRadars) - ); - } - - private static int nearbyRadars(CommandContext ctx) { - PMWeatherAPI.LOGGER.info("Checking for nearby radars..."); - - if (!ctx.getSource().isPlayer()) { - ctx.getSource().sendSystemMessage(Component.translatable("commands.pmweatherapi.non_player")); - return Command.SINGLE_SUCCESS; - } - - Player plr = ctx.getSource().getPlayer(); - int radius; - try { - radius = ctx.getArgument("radius", Integer.class); - } catch (Exception e) { - radius = 512; - } - - long startTimeMillis = System.currentTimeMillis(); - Set blocks = NearbyRadars.get(plr.level()).radarsNearBlock(plr.blockPosition(), radius); - long elapsedTime = System.currentTimeMillis() - startTimeMillis; - - StringBuilder sb = new StringBuilder("Found ").append(blocks.size()).append(" radars in ").append(elapsedTime / 1000.0F).append("s"); - for (BlockPos blockPos : blocks) { - sb.append("\nPos: ").append(blockPos.toShortString()); - } - - if (PMWUtils.isRadarAdjacent(plr.level(), plr.blockPosition())) { - sb.append("\nYou are next to a radar!"); - } - - plr.sendSystemMessage(Component.literal(sb.toString()).withColor(ChatFormatting.GOLD.getColor())); - return Command.SINGLE_SUCCESS; - } -} diff --git a/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java b/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java new file mode 100644 index 0000000..3d9b3cc --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java @@ -0,0 +1,97 @@ +package net.nullved.pmweatherapi.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.ResourceLocationArgument; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.ChunkPos; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.util.PMWUtils; + +import java.util.Set; +import java.util.function.BiFunction; + +public class StoragesCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register( + Commands.literal("storages") + .then(Commands.argument("storage", ResourceLocationArgument.id()) + .suggests((ctx, builder) -> { + PMWStorages.getAll().forEach(si -> builder.suggest(si.id().toString())); + return builder.buildFuture(); + }) + .then(Commands.literal("all") + .then(Commands.argument("radius", IntegerArgumentType.integer(0, 2048)) + .executes(StoragesCommand::storageAll)) + .executes(StoragesCommand::storageAll)) + .then(Commands.literal("adjacentChunks") + .executes(StoragesCommand::storageAdjacentChunks)) +// .then(Commands.argument("radius", IntegerArgumentType.integer(1, 2048)) +// .executes(StoragesCommand::exec)) + .executes(StoragesCommand::storageAll) + ) + ); + } + + private static int storageAll(CommandContext ctx) { + BiFunction> func; + try { + final int radius = ctx.getArgument("radius", Integer.class); + func = (srv, plr) -> srv.getAllWithinRange(plr.blockPosition(), radius); + } catch (Exception e) { + func = (srv, plr) -> srv.getAll(); + } + + return exec(ctx, func); + } + + private static int storageAdjacentChunks(CommandContext ctx) { + return exec(ctx, (srv, plr) -> srv.getInAdjacentChunks(new ChunkPos(plr.blockPosition()))); + } + + private static int exec(CommandContext ctx, BiFunction> blocksFunction) { + PMWeatherAPI.LOGGER.info("Checking for nearby storages..."); + + Player plr = ctx.getSource().getPlayer(); + ResourceLocation storage = ResourceLocationArgument.getId(ctx, "storage"); + if (!ctx.getSource().isPlayer()) { + ctx.getSource().sendSystemMessage(Component.translatable("commands.pmweatherapi.non_player")); + return Command.SINGLE_SUCCESS; + } + + long startTimeMillis = System.currentTimeMillis(); + IServerStorage srv = PMWStorages.get(storage).get(plr.level().dimension()); + + Set blocks = Set.of(); + if (srv != null) { + blocks = blocksFunction.apply(srv, plr); + } + long elapsedTime = System.currentTimeMillis() - startTimeMillis; + + StringBuilder sb = new StringBuilder("Found ").append(blocks.size()).append(" block positions in ").append(elapsedTime / 1000.0F).append("s"); + for (BlockPos blockPos : blocks) { + sb.append("\nPos: ").append(blockPos.toShortString()); + + if (PMWUtils.isRadarCornerAdjacent(plr.level(), blockPos)) { + sb.append(" (RA)"); + } + } + + if (PMWUtils.isRadarCornerAdjacent(plr.level(), plr.blockPosition())) { + sb.append("\nYou are next to a radar!"); + } + + plr.sendSystemMessage(Component.literal(sb.toString()).withColor(ChatFormatting.GOLD.getColor())); + return Command.SINGLE_SUCCESS; + } +} From b6a7d25005c3e71e3da08cbaac76ab45b174c5f2 Mon Sep 17 00:00:00 2001 From: RealDarkStudios <94769968+RealDarkStudios@users.noreply.github.com> Date: Tue, 22 Jul 2025 08:55:30 -0500 Subject: [PATCH 3/5] v0.15.1.1+storage-rework - Begin Storage Rework Took 1 minute --- README.md | 14 +++++++- gradle.properties | 2 +- .../client/data/IClientStorage.java | 6 ++-- .../client/radar/RadarClientStorage.java | 6 ++-- .../pmweatherapi/data/PMWSavedData.java | 4 +-- .../data/PMWStorageSavedData.java | 8 ++--- .../pmweatherapi/data/PMWStorages.java | 3 ++ .../mixin/RadarRendererMixin.java | 2 +- .../pmweatherapi/network/PMWNetworking.java | 4 +-- .../pmweatherapi/network/S2CRadarsPacket.java | 6 ++-- .../network/S2CStoragePacket.java | 12 +++---- .../radar/RadarServerStorage.java | 4 +-- .../pmweatherapi/radar/RadarStorage.java | 2 +- .../pmweatherapi/storage/IServerStorage.java | 16 ++++----- .../pmweatherapi/storage/IStorage.java | 2 +- .../pmweatherapi/storage/PMWStorage.java | 34 +++++++++---------- .../pmweatherapi/storage/StorageInstance.java | 5 ++- .../nullved/pmweatherapi/util/ColorMap.java | 4 +-- .../nullved/pmweatherapi/util/PMWUtils.java | 22 ++++++++++-- 19 files changed, 96 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 61988c6..ccd55e1 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ In your `build.gradle`, add the following: ```groovy repositories { + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + } maven { name = "DU Maven" url = "https://maven.digitalunderworlds.com/snapshots/" @@ -38,8 +42,16 @@ repositories { } dependencies { - implementation "net.nullved:pmweatherapi:" + implementation "maven.modrinth:protomanlys-weather:${pmweather_ver}-alpha" + implementation "net.nullved:pmweatherapi:${pmweatherapi_ver}" } ``` +In your `gradle.properties`, add: + +```properties +pmweather_ver=0.15.1 +pmweatherapi_ver=0.15.1.0 +``` + For usage examples and more details, check out the [wiki](https://github.com/RealDarkStudios/PMWeatherAPI/wiki/). diff --git a/gradle.properties b/gradle.properties index 2c38b49..1fa0b42 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ parchment_mappings_version=2024.11.17 mod_id=pmweatherapi mod_name=PMWeatherAPI mod_license=GNU GPL 3.0 -mod_version=0.15.1.0 +mod_version=0.15.1.1+storage-rework mod_group_id=net.nullved mod_authors=NullVed mod_description=An API for interfacing with ProtoManly's Weather Mod diff --git a/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java index 38eb0a0..8182902 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java @@ -16,7 +16,7 @@ * The interface defining the client-side implementation of a Storage such as {@link RadarClientStorage} *

* Every time the client changes dimension, a new {@link IClientStorage} is created for each storage. - * @since 0.14.16.3 + * @since 0.15.1.1 */ public interface IClientStorage extends IStorage { ClientLevel getLevel(); @@ -28,7 +28,7 @@ default void syncAll(CompoundTag tag) { /** * Syncs data from a {@link S2CStoragePacket} with operation {@code add} into this storage's memory - * @since 0.14.16.3 + * @since 0.15.1.1 */ default void syncAdd(CompoundTag tag) { if (tag.contains("list") && tag.getBoolean("list")) { @@ -47,7 +47,7 @@ default void syncAdd(CompoundTag tag) { /** * Syncs data from a {@link S2CStoragePacket} with operation {@code remove} into this storage's memory - * @since 0.14.16.3 + * @since 0.15.1.1 */ default void syncRemove(CompoundTag tag) { if (tag.contains("list") && tag.getBoolean("list")) { diff --git a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java index 9afa4f6..d34dc4f 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java @@ -13,7 +13,7 @@ * A {@link IClientStorage} implementation for {@link RadarBlock}s *

* You should not create a {@link RadarClientStorage}, instead, use {@link PMWClientStorages#getRadars()} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public class RadarClientStorage extends RadarStorage implements IClientStorage { /** @@ -21,7 +21,7 @@ public class RadarClientStorage extends RadarStorage implements IClientStorage { *
* Get a radar storage from {@link PMWClientStorages#getRadars()} * @param dimension The dimension to create this storage for - * @since 0.14.16.3 + * @since 0.15.1.1 */ public RadarClientStorage(ResourceKey dimension) { super(dimension); @@ -30,7 +30,7 @@ public RadarClientStorage(ResourceKey dimension) { /** * Gets the level associated with this {@link RadarClientStorage} * @return The {@link Minecraft} {@link ClientLevel} - * @since 0.14.16.3 + * @since 0.15.1.1 */ @Override public ClientLevel getLevel() { diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java index 7e39608..d921393 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java @@ -8,9 +8,9 @@ /** * The {@link SavedData} for PMWeatherAPI * @since 0.14.15.3 - * @deprecated Since 0.14.16.3 | Using new Storages system + * @deprecated Since 0.15.1.1 | Using new Storages system */ -@Deprecated(since = "0.14.16.3", forRemoval = true) +@Deprecated(since = "0.15.1.1", forRemoval = true) public class PMWSavedData extends SavedData { private CompoundTag tag; private RadarStorage radarStorage; diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java b/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java index 301da98..ded4681 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java @@ -9,7 +9,7 @@ /** * A {@link SavedData} instance for {@link IStorage}s * - * @since 0.14.16.3 + * @since 0.15.1.1 */ public class PMWStorageSavedData extends SavedData { private final CompoundTag tag; @@ -18,7 +18,7 @@ public class PMWStorageSavedData extends SavedData { /** * Gets the factory for loading the {@link SavedData} * @return A {@link SavedData.Factory} for {@link PMWStorageSavedData} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public static SavedData.Factory factory() { return new SavedData.Factory<>(PMWStorageSavedData::new, PMWStorageSavedData::load, null); @@ -44,7 +44,7 @@ public CompoundTag save(CompoundTag compoundTag, HolderLookup.Provider provider) /** * Sets the {@link IStorage} associated with this {@link Level} or dimension * @param storage The {@link IStorage} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void setStorage(IStorage storage) { this.storage = storage; @@ -53,7 +53,7 @@ public void setStorage(IStorage storage) { /** * Gets the {@link SavedData} from the {@link Level} * @return A data {@link CompoundTag} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public CompoundTag getTag() { return tag; diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java index 7eff7f2..5a9fda0 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java @@ -35,6 +35,9 @@ public static StorageInstance radars() { } public static StorageInstance get(ResourceLocation location) { + if (!STORAGE_INSTANCES.containsKey(location)) { + PMWeatherAPI.LOGGER.error("No storage instance found for location {}", location); + } return STORAGE_INSTANCES.get(location); } diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java index 28d4ff3..a1747c9 100644 --- a/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java +++ b/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java @@ -433,7 +433,7 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS } float rdbz = dbz * 60.0F; - Color startColor = radarBlockEntity.terrainMap.getOrDefault(id, Color.BLACK); + Color startColor = radarBlockEntity.terrainMap.getOrDefault(longID, Color.BLACK); if (radarBlockEntity.init && update) { Holder biome = radarBlockEntity.getNearestBiome(new BlockPos((int) worldPos.x, (int) worldPos.y, (int) worldPos.z)); String rn = biome.getRegisteredName().toLowerCase(); diff --git a/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java b/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java index ee586d1..04718e2 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java +++ b/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java @@ -63,7 +63,7 @@ public static void registerClientboundPacket(Cus /** * Sends all clients a {@link S2CStoragePacket}. The type of packet is dependent on the caller of this method * @param tag The tag to send, generated by the {@link net.nullved.pmweatherapi.radar.RadarServerStorage} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public static void serverSendStorageToAll(CompoundTag tag, Function> pkt) { PacketDistributor.sendToAllPlayers(pkt.apply(tag)); @@ -72,7 +72,7 @@ public static void serverSendStorageToAll(CompoundTag tag, Function> pkt, Player player) { PacketDistributor.sendToPlayer((ServerPlayer) player, pkt.apply(tag)); diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java index 4274422..76b920e 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java @@ -11,7 +11,7 @@ /** * The packet that syncs radars from the server to the client, using the Storages system - * @since 0.14.16.3 + * @since 0.15.1.1 */ public class S2CRadarsPacket extends S2CStoragePacket { public static final CustomPacketPayload.Type TYPE = new Type<>(PMWeatherAPI.rl("s2c_radars")); @@ -20,7 +20,7 @@ public class S2CRadarsPacket extends S2CStoragePacket { /** * Creates a new {@link S2CRadarsPacket} * @param tag The {@link CompoundTag} to send with the packet - * @since 0.14.16.3 + * @since 0.15.1.1 */ public S2CRadarsPacket(CompoundTag tag) { super(tag); @@ -29,7 +29,7 @@ public S2CRadarsPacket(CompoundTag tag) { /** * Gets the {@link RadarClientStorage} that is receiving data * @return The {@link RadarClientStorage} - * @since 0.14.16.3 + * @since 0.15.1.1 */ @Override public RadarClientStorage getStorage() { diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java index 548e8d3..35c54e1 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java @@ -16,7 +16,7 @@ /** * A base packet for the Storages system that syncs data from the Server -> Client (S2C) * @param The {@link IClientStorage} that will be synced to - * @since 0.14.16.3 + * @since 0.15.1.1 */ public abstract class S2CStoragePacket implements CustomPacketPayload { public CompoundTag tag; @@ -24,7 +24,7 @@ public abstract class S2CStoragePacket implements Cust /** * Gets the {@link IClientStorage} that will be synced to. * @return The storage to be synced. - * @since 0.14.16.3 + * @since 0.15.1.1 */ public abstract T getStorage(); @@ -35,7 +35,7 @@ public S2CStoragePacket(CompoundTag tag) { /** * Creates a new {@link S2CStoragePacket} from a {@link RegistryFriendlyByteBuf} * @param buf The {@link RegistryFriendlyByteBuf} to read a {@link CompoundTag} from - * @since 0.14.16.3 + * @since 0.15.1.1 */ public S2CStoragePacket(RegistryFriendlyByteBuf buf) { this(buf.readNbt()); @@ -44,7 +44,7 @@ public S2CStoragePacket(RegistryFriendlyByteBuf buf) { /** * Gets the {@link CompoundTag} send with the packet * @return The packet data - * @since 0.14.16.3 + * @since 0.15.1.1 */ public CompoundTag tag() { return tag; @@ -53,7 +53,7 @@ public CompoundTag tag() { /** * Writes the data into the given {@link FriendlyByteBuf} * @param buf The {@link FriendlyByteBuf} to write into - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void write(FriendlyByteBuf buf) { buf.writeNbt(tag); @@ -62,7 +62,7 @@ public void write(FriendlyByteBuf buf) { /** * Handles the packet on the CLIENT SIDE * @param player The player the packet was sent to - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void handle(Player player) { try { diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java index 8242161..78cd4ca 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java @@ -15,7 +15,7 @@ *

* You should not create a {@link RadarServerStorage}, instead, use {@link PMWStorages#radars()} * - * @since 0.14.16.3 + * @since 0.15.1.1 */ public class RadarServerStorage extends RadarStorage implements ISyncServerStorage { private final ServerLevel level; @@ -25,7 +25,7 @@ public class RadarServerStorage extends RadarStorage implements ISyncServerStora *
* Get a radar storage from {@link PMWStorages#radars()} * @param level The level to create this storage for - * @since 0.14.16.3 + * @since 0.15.1.1 */ public RadarServerStorage(ServerLevel level) { super(level.dimension()); diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java index 60e39f4..8766abf 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java @@ -12,7 +12,7 @@ /** * A {@link IStorage} implementation for {@link RadarBlock}s * - * @since 0.14.16.3 + * @since 0.15.1.1 */ public abstract class RadarStorage extends PMWStorage { public static final ResourceLocation ID = PMWeatherAPI.rl("radars"); diff --git a/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java index e373b36..4b80827 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java @@ -18,13 +18,13 @@ * There is a {@link IServerStorage} for each dimension of a save *
* You should only create a {@link IServerStorage} for a level once, instead, use {@link PMWStorages} if built-in or a custom storage handler. - * @since 0.14.16.3 + * @since 0.15.1.1 */ public interface IServerStorage extends IStorage { /** * Gets the level associated with this {@link IServerStorage} * @return A {@link ServerLevel} - * @since 0.14.16.3 + * @since 0.15.1.1 */ ServerLevel getLevel(); @@ -32,14 +32,14 @@ public interface IServerStorage extends IStorage { * Generates a {@link S2CStoragePacket} to be sent to the client * @param tag The {@link CompoundTag} to be sent * @return A {@link S2CStoragePacket} instance - * @since 0.14.16.3 + * @since 0.15.1.1 */ S2CStoragePacket packet(CompoundTag tag); /** * Syncs all {@link BlockPos} from the storage to the given player * @param player The {@link Player} to sync all data to - * @since 0.14.16.3 + * @since 0.15.1.1 */ default void syncAllToPlayer(Player player) { CompoundTag tag = new CompoundTag(); @@ -59,7 +59,7 @@ default void syncAllToPlayer(Player player) { /** * Syncs new {@link BlockPos} to all clients * @param pos The new {@link BlockPos} - * @since 0.14.16.3 + * @since 0.15.1.1 */ default void syncAdd(BlockPos pos) { CompoundTag tag = new CompoundTag(); @@ -72,7 +72,7 @@ default void syncAdd(BlockPos pos) { /** * Syncs multiple new {@link BlockPos} to all clients * @param posList A {@link Collection} of {@link BlockPos} to sync - * @since 0.14.16.3 + * @since 0.15.1.1 */ default void syncAdd(Collection posList) { CompoundTag tag = new CompoundTag(); @@ -92,7 +92,7 @@ default void syncAdd(Collection posList) { /** * Syncs a {@link BlockPos} removal to all clients * @param pos The {@link BlockPos} of the radar to remove - * @since 0.14.16.3 + * @since 0.15.1.1 */ default void syncRemove(BlockPos pos) { CompoundTag tag = new CompoundTag(); @@ -105,7 +105,7 @@ default void syncRemove(BlockPos pos) { /** * Syncs multiple {@link BlockPos} removals to all clients * @param posList A {@link Collection} of {@link BlockPos} to sync - * @since 0.14.16.3 + * @since 0.15.1.1 */ default void syncRemove(Collection posList) { CompoundTag tag = new CompoundTag(); diff --git a/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java index 0156cc4..3e3fbf4 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java @@ -25,7 +25,7 @@ * For method definitions, see {@link PMWStorage} * * @see PMWStorage - * @since 0.14.16.3 + * @since 0.15.1.1 */ public interface IStorage { Level getLevel(); diff --git a/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java index 40f212a..ad26dcd 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java @@ -46,22 +46,22 @@ * @see IStorage * @see IServerStorage * @see IClientStorage - * @since 0.14.16.3 + * @since 0.15.1.1 */ public abstract class PMWStorage implements IStorage { /** * A {@link Set} of {@link BlockPos} split up by {@link ChunkPos} - * @since 0.14.16.3 + * @since 0.15.1.1 */ private Map> positions = new HashMap<>(); /** * The times each {@link ChunkPos} was last checked - * @since 0.14.16.3 + * @since 0.15.1.1 */ private Map checkTimes = new HashMap<>(); /** * The dimension to store {@link BlockPos} for - * @since 0.14.16.3 + * @since 0.15.1.1 */ private ResourceKey dimension; @@ -77,7 +77,7 @@ public void clean() { * For the server side, it returns a {@link ServerLevel}. * * @return A {@link Level} instance - * @since 0.14.16.3 + * @since 0.15.1.1 */ public abstract Level getLevel(); @@ -86,7 +86,7 @@ public void clean() { * Used primarily for saving to the file at {@code data/_.dat}. * * @return A {@link ResourceLocation} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public abstract ResourceLocation getId(); @@ -95,7 +95,7 @@ public void clean() { * To disable version data from being saved, return {@code -1} * * @return The version of the saved data - * @since 0.14.16.3 + * @since 0.15.1.1 */ public abstract int version(); @@ -103,7 +103,7 @@ public void clean() { * The base constructor * * @param dimension The dimension of the {@link IStorage} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public PMWStorage(ResourceKey dimension) { this.dimension = dimension; @@ -113,7 +113,7 @@ public PMWStorage(ResourceKey dimension) { * Gets a {@link Set} of every {@link BlockPos} saved in this {@link IStorage}, regardless of {@link ChunkPos} * * @return Every saved {@link BlockPos} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public Set getAll() { return positions.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); @@ -141,7 +141,7 @@ public Set getAllWithinRange(BlockPos base, double radius) { * * @param pos The {@link ChunkPos} to search * @return A {@link Set} of the {@link BlockPos} in this chunk - * @since 0.14.16.3 + * @since 0.15.1.1 */ public Set getInChunk(ChunkPos pos) { return positions.getOrDefault(pos, Set.of()); @@ -164,7 +164,7 @@ public Set getInAdjacentChunks(ChunkPos pos) { * * @param pos The {@link ChunkPos} to check * @return Whether the data should be recalculated or not - * @since 0.14.16.3 + * @since 0.15.1.1 */ public boolean shouldRecalculate(ChunkPos pos) { if (!checkTimes.containsKey(pos)) { @@ -179,7 +179,7 @@ public boolean shouldRecalculate(ChunkPos pos) { * Adds a single {@link BlockPos} to the {@link IStorage} * * @param pos The new {@link BlockPos} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void add(BlockPos pos) { ChunkPos chunkPos = new ChunkPos(pos); @@ -192,7 +192,7 @@ public void add(BlockPos pos) { * Adds multiple new {@link BlockPos} to the {@link IStorage} * * @param pos A {@link Collection} of new {@link BlockPos} - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void add(Collection pos) { pos.forEach(this::add); @@ -202,7 +202,7 @@ public void add(Collection pos) { * Removes a single {@link BlockPos} from the {@link IStorage} * * @param pos The {@link BlockPos} to remove - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void remove(BlockPos pos) { ChunkPos chunkPos = new ChunkPos(pos); @@ -215,7 +215,7 @@ public void remove(BlockPos pos) { * Removes multiple {@link BlockPos} from the {@link IStorage} * * @param pos A {@link Collection} of {@link BlockPos} to remove - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void remove(Collection pos) { pos.forEach(this::remove); @@ -226,7 +226,7 @@ public void remove(Collection pos) { * * @param tag The pre-existing {@link CompoundTag} * @return A {@link CompoundTag} with storage data - * @since 0.14.16.3 + * @since 0.15.1.1 */ public CompoundTag save(CompoundTag tag) { PMWeatherAPI.LOGGER.info("Saving storage {} to level...", getId()); @@ -245,7 +245,7 @@ public CompoundTag save(CompoundTag tag) { /** * Reads the saved data from the {@link Level} and initializes this {@link IStorage} with the data - * @since 0.14.16.3 + * @since 0.15.1.1 */ public void read() { PMWStorageSavedData savedData = ((ServerLevel) this.getLevel()).getDataStorage().computeIfAbsent(PMWStorageSavedData.factory(), getId().toString().replace(":", "_")); diff --git a/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java index 1f3d200..349ce0b 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java @@ -76,10 +76,13 @@ public Optional> cast(Class cla } public void load(ServerLevel level) { - map.put(level.dimension(), creator.apply(level)); + S storage = creator.apply(level); + storage.read(); + map.put(level.dimension(), storage); } public void remove(ResourceKey dimension) { + S storage = map.get(dimension); map.remove(dimension); } } diff --git a/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java b/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java index 4b9bc58..47c0df4 100644 --- a/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java +++ b/src/main/java/net/nullved/pmweatherapi/util/ColorMap.java @@ -107,8 +107,8 @@ public Color getWithBiome(float val, Holder biome, Vec3 worldPos) { if (rn.contains("ocean") || rn.contains("river")) startColor = new Color(biome.value().getWaterColor()); else if (rn.contains("beach") || rn.contains("desert")) { if (rn.contains("badlands")) startColor = new Color(214, 111, 42); - else startColor = new Color(biome.value().getGrassColor(worldPos.x, worldPos.z)); - } else startColor = new Color(227, 198, 150); + else startColor = new Color(227, 198, 150); + } else startColor = new Color(biome.value().getGrassColor(worldPos.x, worldPos.z)); if (val < firstThreshold) { return lerp(Math.clamp(val / (firstThreshold - min), 0.0F, 1.0F), startColor, segments.firstEntry().getValue().to); diff --git a/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java b/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java index 2f35d03..cb6fae7 100644 --- a/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java +++ b/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java @@ -12,6 +12,12 @@ import java.util.function.Function; public class PMWUtils { + /** + * Checks if two {@link BlockPos} are corner-adjacent + * @param a A {@link BlockPos} + * @param b Another {@link BlockPos} + * @return {@code true} if they are corner-adjacent + */ public static boolean isCornerAdjacent(BlockPos a, BlockPos b) { int dx = Math.abs(a.getX() - b.getX()); int dy = Math.abs(a.getY() - b.getY()); @@ -20,6 +26,12 @@ public static boolean isCornerAdjacent(BlockPos a, BlockPos b) { return (dx <= 1 && dy <= 1 && dz <= 1) && (dx + dy + dz > 0); } + /** + * Performs a {@link Function} for each {@link BlockPos} around a {@link BlockPos} + * @param pos The base {@link BlockPos} + * @param test The test + * @return A {@link Set} of {@link BlockPos} that passed the test + */ public static Set testAround(BlockPos pos, Function test) { HashSet set = new HashSet<>(); @@ -35,6 +47,12 @@ public static Set testAround(BlockPos pos, Function return set; } + /** + * Gets all {@link BlockPos} in a {@link IStorage} that are corner-adjacent to the base {@link BlockPos} + * @param storage The {@link IStorage} to check in + * @param pos The base {@link BlockPos} + * @return A {@link Set} of {@link BlockPos} that are in the {@link IStorage} around the base {@link BlockPos} + */ public static Set storageCornerAdjacent(IStorage storage, BlockPos pos) { HashSet set = new HashSet<>(); @@ -72,7 +90,7 @@ public static boolean isRadarAdjacent(Level level, BlockPos pos) { * @param dim The dimension to search in * @param pos The {@link BlockPos} to look by * @return {@code true} if there is a radar adjacent to this block, {@code false} otherwise - * @since 0.14.16.3 + * @since 0.15.1.1 */ public static boolean isRadarCornerAdjacent(ResourceKey dim, BlockPos pos) { Set nearby = NearbyRadars.get(dim).radarsNearBlock(pos, 3); @@ -90,7 +108,7 @@ public static boolean isRadarCornerAdjacent(ResourceKey dim, BlockPos pos * @param level The {@link Level} to search in * @param pos The {@link BlockPos} to look by * @return {@code true} if there is a radar adjacent to this block, {@code false} otherwise - * @since 0.14.16.3 + * @since 0.15.1.1 */ public static boolean isRadarCornerAdjacent(Level level, BlockPos pos) { return isRadarCornerAdjacent(level.dimension(), pos); From 9eec1517a997c10575045bb85946c2ecf85441c4 Mon Sep 17 00:00:00 2001 From: RealDarkStudios <94769968+RealDarkStudios@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:00:59 -0500 Subject: [PATCH 4/5] v0.15.3.3-storages-1 - Client Storages - Add Client Storages Took 1 hour 5 minutes --- gradle.properties | 2 +- .../nullved/pmweatherapi/PMWeatherAPI.java | 6 +- .../client/data/PMWClientStorages.java | 81 +++++++++++-------- .../client/radar/RadarClientStorage.java | 10 +-- .../client/storage/ClientStorageInstance.java | 57 +++++++++++++ .../pmweatherapi/data/PMWStorages.java | 13 +-- .../nullved/pmweatherapi/event/PMWEvents.java | 12 +-- .../pmweatherapi/network/S2CRadarsPacket.java | 2 +- .../pmweatherapi/radar/NearbyRadars.java | 2 +- .../pmweatherapi/storage/StorageInstance.java | 21 +++-- 10 files changed, 143 insertions(+), 63 deletions(-) create mode 100644 src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java diff --git a/gradle.properties b/gradle.properties index d7cd7bd..6c1e0b2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ parchment_mappings_version=2024.11.17 mod_id=pmweatherapi mod_name=PMWeatherAPI mod_license=GNU GPL 3.0 -mod_version=0.15.3.2 +mod_version=0.15.3.3-storages-1 mod_group_id=net.nullved mod_authors=NullVed mod_description=An API for interfacing with ProtoManly's Weather Mod diff --git a/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java b/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java index ebff77b..be62439 100644 --- a/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java +++ b/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java @@ -12,6 +12,8 @@ import net.neoforged.neoforge.client.gui.ConfigurationScreen; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; import net.nullved.pmweatherapi.client.render.IDOverlay; import net.nullved.pmweatherapi.client.render.RadarOverlays; import net.nullved.pmweatherapi.config.PMWClientConfig; @@ -42,7 +44,7 @@ public PMWeatherAPI(IEventBus modEventBus, ModContainer modContainer) { } private void commonSetup(FMLCommonSetupEvent event) { - PMWStorages.registerStorage(RadarStorage.ID, RadarServerStorage::new); + PMWStorages.registerStorage(RadarStorage.ID, RadarServerStorage.class, RadarServerStorage::new); // if (!ModList.get().isLoaded("pmweather")) { // throw new RuntimeException("ProtoManly's Weather not detected!"); @@ -54,6 +56,8 @@ private void registerPayloads(RegisterPayloadHandlersEvent event) { } private void clientSetup(FMLClientSetupEvent event) { + PMWClientStorages.registerStorage(RadarStorage.ID, RadarClientStorage.class, RadarClientStorage::new); + RadarOverlays.registerOverlay(() -> IDOverlay.INSTANCE); // RadarOverlays.registerOverlay(() -> ExampleOverlay.INSTANCE); } diff --git a/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java b/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java index 28f3378..820eaf9 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java +++ b/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java @@ -1,17 +1,23 @@ package net.nullved.pmweatherapi.client.data; import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; +import net.minecraft.resources.ResourceLocation; import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.client.radar.RadarClientStorage; +import net.nullved.pmweatherapi.client.storage.ClientStorageInstance; import net.nullved.pmweatherapi.radar.RadarMode; +import net.nullved.pmweatherapi.radar.RadarStorage; import java.awt.*; +import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.function.Function; /** * A class holding the specific storage instances for the client @@ -25,44 +31,55 @@ public class PMWClientStorages { */ public static Map>> RADAR_MODE_COLORS = new HashMap<>(); - private static Level lastLevel; - private static RadarClientStorage radar; + public static final Map> STORAGE_INSTANCES = new HashMap<>(); - /** - * Resets this client's internal radar storage - * @since 0.14.15.3 - */ - public static void resetRadars() { - radar = null; + private static ClientLevel lastLevel; + + public static ClientStorageInstance radars() { + return get(RadarStorage.ID, RadarClientStorage.class).orElseThrow(); } - /** - * Gets the radar storage for the dimension this client is currently in - * @return A {@link RadarClientStorage} - * @since 0.14.15.3 - */ - public static RadarClientStorage getRadars() { - try { - Level level = Minecraft.getInstance().level; - if (radar == null || level != lastLevel) { - init(level); - } - } catch (Exception e) { - PMWeatherAPI.LOGGER.error(e.getMessage(), e); + public static ClientStorageInstance get(ResourceLocation location) { + if (!STORAGE_INSTANCES.containsKey(location)) { + PMWeatherAPI.LOGGER.error("No storage instance found for location {}", location); } - return radar; + ClientLevel curLevel = Minecraft.getInstance().level; + if (curLevel != null && curLevel != lastLevel) { + loadNewLevel(curLevel); + } + + ClientStorageInstance csi = STORAGE_INSTANCES.get(location); + if (csi.get() == null) { + csi.load(curLevel); + } + + return csi; } - /** - * Initializes this client's storages to be the storages for the given level - * @param level The {@link Level} to initialize storages for - * @since 0.14.15.3 - */ - public static void init(Level level) { + public static void set(ResourceLocation location, ClientStorageInstance instance) { + STORAGE_INSTANCES.put(location, instance); + } + + public static Optional> get(ResourceLocation location, Class clazz) { + return get(location).cast(clazz); + } + + public static Collection> getAll() { + return STORAGE_INSTANCES.values(); + } + + public static void resetAll() { + STORAGE_INSTANCES.forEach((location, instance) -> instance.clear()); + } + + public static void loadNewLevel(ClientLevel level) { lastLevel = level; - if (level != null) { - radar = new RadarClientStorage(level.dimension()); - } + STORAGE_INSTANCES.forEach((location, instance) -> instance.load(level)); + } + + public static void registerStorage(ResourceLocation id, Class clazz, Function creator) { + ClientStorageInstance instance = new ClientStorageInstance<>(id, clazz, creator); + STORAGE_INSTANCES.put(id, instance); } } diff --git a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java index d34dc4f..1d44ab5 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java @@ -12,19 +12,19 @@ /** * A {@link IClientStorage} implementation for {@link RadarBlock}s *

- * You should not create a {@link RadarClientStorage}, instead, use {@link PMWClientStorages#getRadars()} + * You should not create a {@link RadarClientStorage}, instead, use {@link PMWClientStorages#radars()} * @since 0.15.1.1 */ public class RadarClientStorage extends RadarStorage implements IClientStorage { /** * DO NOT CALL THIS CONSTRUCTOR!!! *
- * Get a radar storage from {@link PMWClientStorages#getRadars()} - * @param dimension The dimension to create this storage for + * Get a radar storage from {@link PMWClientStorages#radars()} + * @param clientLevel The {@link ClientLevel} to create this storage for * @since 0.15.1.1 */ - public RadarClientStorage(ResourceKey dimension) { - super(dimension); + public RadarClientStorage(ClientLevel clientLevel) { + super(clientLevel.dimension()); } /** diff --git a/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java b/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java new file mode 100644 index 0000000..7320e38 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java @@ -0,0 +1,57 @@ +package net.nullved.pmweatherapi.client.storage; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.IClientStorage; + +import java.util.Optional; +import java.util.function.Function; + +public class ClientStorageInstance { + private final ResourceLocation id; + private final Class clazz; + private final Function creator; + private C storage; + + public ClientStorageInstance(ResourceLocation id, Class clazz, Function creator) { + this.id = id; + this.clazz = clazz; + this.creator = creator; + } + + public ResourceLocation id() { + return id; + } + + public C get() { + return storage; + } + + public void set(C storage) { + this.storage = storage; + } + + public Optional> cast(Class oclazz) { + if (oclazz.isAssignableFrom(clazz)) { + @SuppressWarnings("unchecked") + ClientStorageInstance casted = new ClientStorageInstance<>(id(), oclazz, cl -> (O) creator.apply(cl)); + + try { + casted.set((O) storage); + return Optional.of(casted); + } catch (ClassCastException e) { + PMWeatherAPI.LOGGER.error("Could not cast {} to {}", clazz.getSimpleName(), oclazz.getSimpleName()); + return Optional.empty(); + } + } else return Optional.empty(); + } + + public void load(ClientLevel level) { + storage = creator.apply(level); + } + + public void clear() { + storage = null; + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java index 5a9fda0..9fae1ff 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java @@ -6,6 +6,7 @@ import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.radar.RadarServerStorage; +import net.nullved.pmweatherapi.radar.RadarStorage; import net.nullved.pmweatherapi.storage.IServerStorage; import net.nullved.pmweatherapi.storage.StorageInstance; @@ -20,8 +21,6 @@ * @since 0.14.15.3 */ public class PMWStorages { - public static final ResourceLocation RADARS = PMWeatherAPI.rl("radars"); - public static final Map> STORAGE_INSTANCES = new HashMap<>(); @@ -31,7 +30,7 @@ public class PMWStorages { * @since 0.14.15.3 */ public static StorageInstance radars() { - return (StorageInstance) get(RADARS); + return get(RadarStorage.ID, RadarServerStorage.class).orElseThrow(); } public static StorageInstance get(ResourceLocation location) { @@ -53,6 +52,10 @@ public static Collection> getAll() { return STORAGE_INSTANCES.values(); } + public static Collection getForDimension(ResourceKey dimension) { + return getAll().stream().map(si -> si.get(dimension)).toList(); + } + public static void generateForDimension(ServerLevel dimension) { STORAGE_INSTANCES.forEach((rl, si) -> si.load(dimension)); } @@ -61,8 +64,8 @@ public static void removeForDimension(ResourceKey dimension) { STORAGE_INSTANCES.forEach((rl, si) -> si.remove(dimension)); } - public static void registerStorage(ResourceLocation id, Function creator) { - StorageInstance instance = new StorageInstance<>(id, creator); + public static void registerStorage(ResourceLocation id, Class clazz, Function creator) { + StorageInstance instance = new StorageInstance<>(id, clazz, creator); STORAGE_INSTANCES.put(id, instance); } } diff --git a/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java b/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java index 32972ec..f6b02ac 100644 --- a/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java +++ b/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java @@ -36,11 +36,13 @@ public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) { PMWeatherAPI.LOGGER.info("Synced all sync-storages to joined player {}", event.getEntity().getDisplayName().getString()); } -// -// @SubscribeEvent -// public static void onPlayerChangeDimension(PlayerEvent.PlayerChangedDimensionEvent event) { -// PMWStorages.getStoragesForDimension(event.getTo()).forEach(iss -> iss.syncAllToPlayer(event.getEntity())); -// } + + @SubscribeEvent + public static void onPlayerChangeDimension(PlayerEvent.PlayerChangedDimensionEvent event) { + PMWStorages.getForDimension(event.getTo()).forEach(iss -> { + if (iss != null) iss.syncAllToPlayer(event.getEntity()); + }); + } @SubscribeEvent public static void onChunkSentEvent(ChunkWatchEvent.Sent event) { diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java index 76b920e..9df9895 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java @@ -33,7 +33,7 @@ public S2CRadarsPacket(CompoundTag tag) { */ @Override public RadarClientStorage getStorage() { - return PMWClientStorages.getRadars(); + return PMWClientStorages.radars().get(); } @Override diff --git a/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java b/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java index 783166f..aa3c394 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java @@ -40,7 +40,7 @@ private NearbyRadars(RadarStorage storage) { */ @OnlyIn(Dist.CLIENT) public static NearbyRadars client() { - return new NearbyRadars(PMWClientStorages.getRadars()); + return new NearbyRadars(PMWClientStorages.radars().get()); } /** diff --git a/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java index 349ce0b..c2b1bbf 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java @@ -11,11 +11,13 @@ public class StorageInstance { private final ResourceLocation id; - private final HashMap, S> map = new HashMap<>(); + private final Class clazz; private final Function creator; + private final HashMap, S> map = new HashMap<>(); - public StorageInstance(ResourceLocation id, Function creator) { + public StorageInstance(ResourceLocation id, Class clazz, Function creator) { this.id = id; + this.clazz = clazz; this.creator = creator; } @@ -51,25 +53,20 @@ public S getOrCreate(ServerLevel level) { return map.computeIfAbsent(level.dimension(), dim -> creator.apply(level)); } - public Optional> cast(Class clazz) { - if (map.isEmpty()) return Optional.of(new StorageInstance<>(id(), sl -> (O) creator.apply(sl))); - - S val = map.values().stream().findFirst().orElseThrow(); - if (clazz.isAssignableFrom(val.getClass())) { - // almost unevitable to + public Optional> cast(Class oclazz) { + if (oclazz.isAssignableFrom(clazz)) { @SuppressWarnings("unchecked") - StorageInstance casted = new StorageInstance<>(id(), sl -> (O) creator.apply(sl)); + StorageInstance casted = new StorageInstance<>(id(), oclazz, sl -> (O) creator.apply(sl)); HashMap, O> backingMap = casted.getBackingMap(); try { - for (Map.Entry, S> entry : this.map.entrySet()) { - backingMap.put(entry.getKey(), clazz.cast(entry.getValue())); + backingMap.put(entry.getKey(), oclazz.cast(entry.getValue())); } return Optional.of(casted); } catch (ClassCastException e) { - PMWeatherAPI.LOGGER.error("Could not cast {} to {}", val.getClass().getSimpleName(), clazz.getSimpleName()); + PMWeatherAPI.LOGGER.error("Could not cast {} to {}", clazz.getSimpleName(), oclazz.getSimpleName()); return Optional.empty(); } } else return Optional.empty(); From e76aaea880ce82f9f35d0aa72a91e28a8d95fd69 Mon Sep 17 00:00:00 2001 From: RealDarkStudios <94769968+realdarkstudios@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:28:57 -0400 Subject: [PATCH 5/5] v0.15.3.3-rc1 - Storages + Radar Overlay Textures - Storages should be complete or near complete - Storages allow you to save data in a format of your choice so that the data can be read even when chunks are not loaded. Useful for things such as showing radar positions in unloaded chunks (Radar Networks coming soon!) - Added storages for Metars (Weather Stations) and WSR-88Ds - Added #renderTexture methods to Radar Overlays - Added RadarMode#disableBaseRendering to disable the defualt pixel renderer (Needed by Hezi for Radar Overhaul) - And a bunch more stuff that I don't to write right now --- gradle.properties | 4 +- .../nullved/pmweatherapi/PMWeatherAPI.java | 33 ++- .../client/data/IClientStorage.java | 93 +++++--- .../client/data/PMWClientStorages.java | 106 +++++++-- .../client/event/PMWClientEvents.java | 30 +++ .../client/metar/MetarClientStorage.java | 41 ++++ .../client/radar/RadarClientStorage.java | 13 +- .../client/radar/WSRClientStorage.java | 41 ++++ .../client/render/IRadarOverlay.java | 99 +++++++- .../client/storage/ClientStorageInstance.java | 20 +- .../pmweatherapi/command/StoragesCommand.java | 107 +++++++-- .../pmweatherapi/data/PMWSavedData.java | 6 +- .../data/PMWStorageSavedData.java | 12 +- .../pmweatherapi/data/PMWStorages.java | 110 +++++++-- .../nullved/pmweatherapi/event/PMWEvents.java | 89 +++++-- .../pmweatherapi/example/ExampleOverlay.java | 21 +- .../metar/MetarServerStorage.java | 40 ++++ .../pmweatherapi/metar/MetarStorage.java | 43 ++++ .../pmweatherapi/metar/MetarStorageData.java | 80 +++++++ .../mixin/BlockBehaviourMixin.java | 46 +++- .../mixin/PacketNBTFromClientMixin.java | 23 -- .../pmweatherapi/mixin/RadarBlockMixin.java | 3 + .../mixin/RadarRendererMixin.java | 24 +- .../pmweatherapi/mixin/WSR88DCoreMixin.java | 20 ++ .../pmweatherapi/network/PMWNetworking.java | 14 +- .../pmweatherapi/network/S2CMetarPacket.java | 44 ++++ ...CRadarsPacket.java => S2CRadarPacket.java} | 16 +- .../network/S2CStoragePacket.java | 26 +-- .../pmweatherapi/network/S2CWSRPacket.java | 44 ++++ .../pmweatherapi/radar/NearbyRadars.java | 15 +- .../nullved/pmweatherapi/radar/RadarMode.java | 21 +- .../{ => storage}/RadarServerStorage.java | 21 +- .../radar/{ => storage}/RadarStorage.java | 20 +- .../radar/storage/RadarStorageData.java | 50 ++++ .../radar/storage/WSRServerStorage.java | 48 ++++ .../radar/storage/WSRStorage.java | 41 ++++ .../radar/storage/WSRStorageData.java | 50 ++++ .../pmweatherapi/storage/IServerStorage.java | 102 +++++--- .../pmweatherapi/storage/IStorage.java | 24 +- .../storage/ISyncServerStorage.java | 62 ++++- .../pmweatherapi/storage/PMWStorage.java | 218 ++++++++++++------ .../pmweatherapi/storage/StorageInstance.java | 33 ++- .../storage/data/BlockPosData.java | 29 +++ .../storage/data/IStorageData.java | 20 ++ .../storage/data/StorageData.java | 93 ++++++++ .../storage/data/StorageDataManager.java | 42 ++++ .../nullved/pmweatherapi/util/PMWUtils.java | 14 +- src/main/resources/pmweatherapi.mixins.json | 4 +- 48 files changed, 1801 insertions(+), 354 deletions(-) create mode 100644 src/main/java/net/nullved/pmweatherapi/client/event/PMWClientEvents.java create mode 100644 src/main/java/net/nullved/pmweatherapi/client/metar/MetarClientStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/client/radar/WSRClientStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/metar/MetarServerStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/metar/MetarStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/metar/MetarStorageData.java delete mode 100644 src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java create mode 100644 src/main/java/net/nullved/pmweatherapi/mixin/WSR88DCoreMixin.java create mode 100644 src/main/java/net/nullved/pmweatherapi/network/S2CMetarPacket.java rename src/main/java/net/nullved/pmweatherapi/network/{S2CRadarsPacket.java => S2CRadarPacket.java} (69%) create mode 100644 src/main/java/net/nullved/pmweatherapi/network/S2CWSRPacket.java rename src/main/java/net/nullved/pmweatherapi/radar/{ => storage}/RadarServerStorage.java (63%) rename src/main/java/net/nullved/pmweatherapi/radar/{ => storage}/RadarStorage.java (57%) create mode 100644 src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorageData.java create mode 100644 src/main/java/net/nullved/pmweatherapi/radar/storage/WSRServerStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorage.java create mode 100644 src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorageData.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/data/BlockPosData.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/data/IStorageData.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/data/StorageData.java create mode 100644 src/main/java/net/nullved/pmweatherapi/storage/data/StorageDataManager.java diff --git a/gradle.properties b/gradle.properties index 6c1e0b2..85f9805 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,9 +17,9 @@ parchment_mappings_version=2024.11.17 mod_id=pmweatherapi mod_name=PMWeatherAPI mod_license=GNU GPL 3.0 -mod_version=0.15.3.3-storages-1 +mod_version=0.15.3.3-rc1 mod_group_id=net.nullved -mod_authors=NullVed +mod_authors=nullved mod_description=An API for interfacing with ProtoManly's Weather Mod # Dependencies diff --git a/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java b/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java index be62439..acd7c1d 100644 --- a/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java +++ b/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java @@ -13,16 +13,27 @@ import net.neoforged.neoforge.client.gui.IConfigScreenFactory; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.client.metar.MetarClientStorage; import net.nullved.pmweatherapi.client.radar.RadarClientStorage; +import net.nullved.pmweatherapi.client.radar.WSRClientStorage; import net.nullved.pmweatherapi.client.render.IDOverlay; import net.nullved.pmweatherapi.client.render.RadarOverlays; import net.nullved.pmweatherapi.config.PMWClientConfig; import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.example.ExampleOverlay; +import net.nullved.pmweatherapi.metar.MetarServerStorage; +import net.nullved.pmweatherapi.metar.MetarStorage; +import net.nullved.pmweatherapi.metar.MetarStorageData; import net.nullved.pmweatherapi.network.PMWNetworking; -import net.nullved.pmweatherapi.radar.RadarServerStorage; -import net.nullved.pmweatherapi.radar.RadarStorage; +import net.nullved.pmweatherapi.radar.RadarMode; +import net.nullved.pmweatherapi.radar.storage.*; +import net.nullved.pmweatherapi.storage.data.BlockPosData; +import net.nullved.pmweatherapi.storage.data.StorageDataManager; +import org.checkerframework.checker.units.qual.C; import org.slf4j.Logger; +import java.awt.*; + @Mod(PMWeatherAPI.MODID) public class PMWeatherAPI { public static final String MODID = "pmweatherapi"; @@ -44,11 +55,14 @@ public PMWeatherAPI(IEventBus modEventBus, ModContainer modContainer) { } private void commonSetup(FMLCommonSetupEvent event) { - PMWStorages.registerStorage(RadarStorage.ID, RadarServerStorage.class, RadarServerStorage::new); + StorageDataManager.register(BlockPosData.ID, BlockPosData::deserializeFromNBT); + StorageDataManager.register(RadarStorageData.ID, RadarStorageData::deserializeFromNBT); + StorageDataManager.register(MetarStorageData.ID, MetarStorageData::deserializeFromNBT); + StorageDataManager.register(WSRStorageData.ID, WSRStorageData::deserializeFromNBT); -// if (!ModList.get().isLoaded("pmweather")) { -// throw new RuntimeException("ProtoManly's Weather not detected!"); -// } + PMWStorages.registerStorage(RadarStorage.ID, RadarServerStorage.class, RadarServerStorage::new); + PMWStorages.registerStorage(MetarStorage.ID, MetarServerStorage.class, MetarServerStorage::new); + PMWStorages.registerStorage(WSRStorage.ID, WSRServerStorage.class, WSRServerStorage::new); } private void registerPayloads(RegisterPayloadHandlersEvent event) { @@ -56,12 +70,15 @@ private void registerPayloads(RegisterPayloadHandlersEvent event) { } private void clientSetup(FMLClientSetupEvent event) { + //RadarMode.removeBaseRendering(true); + PMWClientStorages.registerStorage(RadarStorage.ID, RadarClientStorage.class, RadarClientStorage::new); + PMWClientStorages.registerStorage(MetarStorage.ID, MetarClientStorage.class, MetarClientStorage::new); + PMWClientStorages.registerStorage(WSRStorage.ID, WSRClientStorage.class, WSRClientStorage::new); RadarOverlays.registerOverlay(() -> IDOverlay.INSTANCE); -// RadarOverlays.registerOverlay(() -> ExampleOverlay.INSTANCE); + //RadarOverlays.registerOverlay(() -> ExampleOverlay.INSTANCE); } - public static ResourceLocation rl(String path) { return ResourceLocation.fromNamespaceAndPath(MODID, path); } diff --git a/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java index 8182902..0adad50 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java @@ -2,25 +2,31 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.IntArrayTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.*; +import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.client.radar.RadarClientStorage; import net.nullved.pmweatherapi.network.S2CStoragePacket; import net.nullved.pmweatherapi.storage.IStorage; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; +import java.util.Objects; import java.util.stream.Collectors; /** * The interface defining the client-side implementation of a Storage such as {@link RadarClientStorage} *

* Every time the client changes dimension, a new {@link IClientStorage} is created for each storage. - * @since 0.15.1.1 + * @since 0.15.3.3 */ -public interface IClientStorage extends IStorage { +public interface IClientStorage extends IStorage { ClientLevel getLevel(); + /** + * Syncs all data from a {@link S2CStoragePacket} with operation {@code overwrite} into this storage's memory + * @param tag The {@link CompoundTag} of the data + * @since 0.15.3.3 + */ default void syncAll(CompoundTag tag) { clean(); syncAdd(tag); @@ -28,39 +34,74 @@ default void syncAll(CompoundTag tag) { /** * Syncs data from a {@link S2CStoragePacket} with operation {@code add} into this storage's memory - * @since 0.15.1.1 + * @param tag The {@link CompoundTag} of the data + * @since 0.15.3.3 */ default void syncAdd(CompoundTag tag) { + int version = tag.getInt("version"); if (tag.contains("list") && tag.getBoolean("list")) { // list format - ListTag list = tag.getList("data", ListTag.TAG_INT_ARRAY); - add(list.stream().map(t -> { - if (t instanceof IntArrayTag iat) { - return new BlockPos(iat.get(0).getAsInt(), iat.get(1).getAsInt(), iat.get(2).getAsInt()); - } else return null; - }).collect(Collectors.toSet())); + ListTag list = tag.getList("data", ListTag.TAG_COMPOUND); + add(list.stream().map((t -> { + try { + return (D) StorageData.deserializeFromNBT((CompoundTag) t, version); + } catch (ClassCastException e) { + PMWeatherAPI.LOGGER.info("Invalid data entry in NBT: {}", e.getMessage()); + return null; + } + })).filter(Objects::nonNull).collect(Collectors.toSet())); } else { - // not list format - add(NbtUtils.readBlockPos(tag, "data").orElseThrow()); + try { + add((D) StorageData.deserializeFromNBT(tag.getCompound("data"), version)); + } catch (ClassCastException e) { + PMWeatherAPI.LOGGER.info("Invalid data entry in NBT: {}", e.getMessage()); + } } } /** * Syncs data from a {@link S2CStoragePacket} with operation {@code remove} into this storage's memory - * @since 0.15.1.1 + * @param tag The {@link CompoundTag} of the data + * @since 0.15.3.3 */ default void syncRemove(CompoundTag tag) { - if (tag.contains("list") && tag.getBoolean("list")) { - // list format - ListTag list = tag.getList("data", ListTag.TAG_INT_ARRAY); - remove(list.stream().map(t -> { - if (t instanceof IntArrayTag iat) { - return new BlockPos(iat.get(0).getAsInt(), iat.get(1).getAsInt(), iat.get(2).getAsInt()); - } else return null; - }).collect(Collectors.toSet())); + int version = tag.getInt("version"); + if (!tag.contains("format")) { + // data + if (tag.contains("list") && tag.getBoolean("list")) { + // list format + ListTag list = tag.getList("data", ListTag.TAG_COMPOUND); + removeByData(list.stream().map((t -> { + try { + return (D) StorageData.deserializeFromNBT((CompoundTag) t, version); + } catch (ClassCastException e) { + PMWeatherAPI.LOGGER.info("Invalid data entry in NBT: {}", e.getMessage()); + return null; + } + })).filter(Objects::nonNull).collect(Collectors.toSet())); + } else { + // not list format + try { + remove((D) StorageData.deserializeFromNBT(tag.getCompound("data"), version)); + } catch (ClassCastException e) { + PMWeatherAPI.LOGGER.info("Invalid data entry in NBT: {}", e.getMessage()); + } + } + } else if (tag.getString("format").equals("blockpos")) { + if (tag.contains("list") && tag.getBoolean("list")) { + // list format + ListTag list = tag.getList("data", ListTag.TAG_INT_ARRAY); + removeByPos(list.stream().map(t -> { + if (t instanceof IntArrayTag iat) { + return new BlockPos(iat.get(0).getAsInt(), iat.get(1).getAsInt(), iat.get(2).getAsInt()); + } else return null; + }).collect(Collectors.toSet())); + } else { + // not list format + remove(NbtUtils.readBlockPos(tag, "data").orElseThrow()); + } } else { - // not list format - remove(NbtUtils.readBlockPos(tag, "data").orElseThrow()); + PMWeatherAPI.LOGGER.info("Invalid data format for packet: '{}'!", tag.getString("format")); } } } diff --git a/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java b/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java index 820eaf9..f2ad8dd 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java +++ b/src/main/java/net/nullved/pmweatherapi/client/data/PMWClientStorages.java @@ -7,10 +7,19 @@ import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.OnlyIn; import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.metar.MetarClientStorage; import net.nullved.pmweatherapi.client.radar.RadarClientStorage; +import net.nullved.pmweatherapi.client.radar.WSRClientStorage; import net.nullved.pmweatherapi.client.storage.ClientStorageInstance; +import net.nullved.pmweatherapi.metar.MetarStorage; +import net.nullved.pmweatherapi.metar.MetarStorageData; import net.nullved.pmweatherapi.radar.RadarMode; -import net.nullved.pmweatherapi.radar.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.WSRStorage; +import net.nullved.pmweatherapi.radar.storage.WSRStorageData; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; import java.awt.*; import java.util.Collection; @@ -31,25 +40,54 @@ public class PMWClientStorages { */ public static Map>> RADAR_MODE_COLORS = new HashMap<>(); - public static final Map> STORAGE_INSTANCES = new HashMap<>(); + public static final Map> STORAGE_INSTANCES = new HashMap<>(); private static ClientLevel lastLevel; - public static ClientStorageInstance radars() { + /** + * Gets the {@link ClientStorageInstance} of the {@link RadarClientStorage} + * @return The {@link ClientStorageInstance} + * @since 0.14.15.3 + */ + public static ClientStorageInstance radars() { return get(RadarStorage.ID, RadarClientStorage.class).orElseThrow(); } - public static ClientStorageInstance get(ResourceLocation location) { + /** + * Gets the {@link ClientStorageInstance} of the {@link MetarClientStorage} + * @return The {@link ClientStorageInstance} + * @since 0.15.3.3 + */ + public static ClientStorageInstance metars() { + return get(MetarStorage.ID, MetarClientStorage.class).orElseThrow(); + } + + /** + * Gets the {@link ClientStorageInstance} of the {@link WSRClientStorage} + * @return The {@link ClientStorageInstance} + * @since 0.15.3.3 + */ + public static ClientStorageInstance wsrs() { + return get(WSRStorage.ID, WSRClientStorage.class).orElseThrow(); + } + + /** + * Get a {@link ClientStorageInstance} for a given {@link ResourceLocation} ID + * @param location The ID of the storage + * @return A {@link ClientStorageInstance} + * @since 0.15.3.3 + */ + public static ClientStorageInstance get(ResourceLocation location) { if (!STORAGE_INSTANCES.containsKey(location)) { PMWeatherAPI.LOGGER.error("No storage instance found for location {}", location); } ClientLevel curLevel = Minecraft.getInstance().level; if (curLevel != null && curLevel != lastLevel) { - loadNewLevel(curLevel); + loadDimension(curLevel); } - ClientStorageInstance csi = STORAGE_INSTANCES.get(location); + ClientStorageInstance csi = STORAGE_INSTANCES.get(location); if (csi.get() == null) { csi.load(curLevel); } @@ -57,29 +95,67 @@ public static ClientStorageInstance get(ResourceLocation location) { return csi; } - public static void set(ResourceLocation location, ClientStorageInstance instance) { + /** + * Overwrite a {@link ClientStorageInstance} + * @param location The ID {@link ResourceLocation} + * @param instance The new {@link ClientStorageInstance} + * @since 0.15.3.3 + */ + public static void set(ResourceLocation location, ClientStorageInstance instance) { STORAGE_INSTANCES.put(location, instance); } - public static Optional> get(ResourceLocation location, Class clazz) { + /** + * Casts the {@link ClientStorageInstance} to the specified {@link IClientStorage} class after retrieval + * @param location The ID {@link ResourceLocation} + * @param clazz The {@link Class} of an {@link IClientStorage} to cast to + * @return The casted {@link ClientStorageInstance} + * @param The {@link IStorageData} of the {@link IClientStorage} + * @param The {@link IClientStorage} + * @since 0.15.3.3 + */ + public static > Optional> get(ResourceLocation location, Class clazz) { return get(location).cast(clazz); } - public static Collection> getAll() { + /** + * Gets all {@link ClientStorageInstance}s + * @return A {@link Collection} of all {@link ClientStorageInstance}s + * @since 0.15.3.3 + */ + public static Collection> getAll() { return STORAGE_INSTANCES.values(); } + /** + * Resets all data for all {@link ClientStorageInstance}s + * @since 0.15.3.3 + */ public static void resetAll() { - STORAGE_INSTANCES.forEach((location, instance) -> instance.clear()); + getAll().forEach(ClientStorageInstance::clear); } - public static void loadNewLevel(ClientLevel level) { - lastLevel = level; - STORAGE_INSTANCES.forEach((location, instance) -> instance.load(level)); + /** + * Loads a new {@link ClientLevel} for all {@link ClientStorageInstance}s + * @param clientLevel The new {@link ClientLevel} to load + * @since 0.15.3.3 + */ + public static void loadDimension(ClientLevel clientLevel) { + lastLevel = clientLevel; + STORAGE_INSTANCES.forEach((location, instance) -> instance.load(clientLevel)); } - public static void registerStorage(ResourceLocation id, Class clazz, Function creator) { - ClientStorageInstance instance = new ClientStorageInstance<>(id, clazz, creator); + /** + * Register a new {@link IClientStorage} + * @param id The {@link ResourceLocation} to save this {@link IClientStorage} as + * @param clazz The {@link Class} of the {@link IClientStorage} + * @param creator A function creating another {@link IClientStorage} for the given {@link ClientLevel} + * @param The {@link IStorageData} of the {@link IClientStorage} + * @param The {@link IClientStorage} + * @since 0.15.3.3 + */ + public static > void registerStorage(ResourceLocation id, Class clazz, Function creator) { + ClientStorageInstance instance = new ClientStorageInstance<>(id, clazz, creator); STORAGE_INSTANCES.put(id, instance); } } diff --git a/src/main/java/net/nullved/pmweatherapi/client/event/PMWClientEvents.java b/src/main/java/net/nullved/pmweatherapi/client/event/PMWClientEvents.java new file mode 100644 index 0000000..20bb949 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/client/event/PMWClientEvents.java @@ -0,0 +1,30 @@ +package net.nullved.pmweatherapi.client.event; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.level.LevelAccessor; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.level.LevelEvent; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; + +@EventBusSubscriber(modid = PMWeatherAPI.MODID, value = Dist.CLIENT) +public class PMWClientEvents { + @SubscribeEvent + public static void onLevelLoadEvent(LevelEvent.Load event) { + LevelAccessor level = event.getLevel(); + if (level.isClientSide() && level instanceof ClientLevel clevel) { + PMWeatherAPI.LOGGER.info("Loaded client storages for dimension {}", clevel.dimension().location()); + PMWClientStorages.loadDimension(clevel); + } + } + + @SubscribeEvent + public static void onLevelUnloadEvent(LevelEvent.Unload event) { + LevelAccessor level = event.getLevel(); + if (level.isClientSide() && level instanceof ClientLevel clevel) { + PMWeatherAPI.LOGGER.info("Unloaded client storages for dimension {}", clevel.dimension().location()); + } + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/client/metar/MetarClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/metar/MetarClientStorage.java new file mode 100644 index 0000000..c856022 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/client/metar/MetarClientStorage.java @@ -0,0 +1,41 @@ +package net.nullved.pmweatherapi.client.metar; + +import dev.protomanly.pmweather.block.MetarBlock; +import dev.protomanly.pmweather.block.RadarBlock; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.metar.MetarStorage; +import net.nullved.pmweatherapi.metar.MetarStorageData; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; + +/** + * A {@link IClientStorage} implementation for {@link MetarBlock}s + *

+ * You should not create a {@link MetarClientStorage}, instead, use {@link PMWClientStorages#metars()} + * @since 0.15.3.3 + */ +public class MetarClientStorage extends MetarStorage implements IClientStorage { + /** + * DO NOT CALL THIS CONSTRUCTOR!!! + *
+ * Get a radar storage from {@link PMWClientStorages#metars()} + * @param clientLevel The {@link ClientLevel} to create this storage for + * @since 0.15.3.3 + */ + public MetarClientStorage(ClientLevel clientLevel) { + super(clientLevel.dimension()); + } + + /** + * Gets the level associated with this {@link MetarClientStorage} + * @return The {@link Minecraft} {@link ClientLevel} + * @since 0.15.3.3 + */ + @Override + public ClientLevel getLevel() { + return Minecraft.getInstance().level; + } +} \ No newline at end of file diff --git a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java index 1d44ab5..9136a23 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/client/radar/RadarClientStorage.java @@ -3,25 +3,24 @@ import dev.protomanly.pmweather.block.RadarBlock; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.client.data.IClientStorage; import net.nullved.pmweatherapi.client.data.PMWClientStorages; -import net.nullved.pmweatherapi.radar.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; /** * A {@link IClientStorage} implementation for {@link RadarBlock}s *

* You should not create a {@link RadarClientStorage}, instead, use {@link PMWClientStorages#radars()} - * @since 0.15.1.1 + * @since 0.15.3.3 */ -public class RadarClientStorage extends RadarStorage implements IClientStorage { +public class RadarClientStorage extends RadarStorage implements IClientStorage { /** * DO NOT CALL THIS CONSTRUCTOR!!! *
* Get a radar storage from {@link PMWClientStorages#radars()} * @param clientLevel The {@link ClientLevel} to create this storage for - * @since 0.15.1.1 + * @since 0.15.3.3 */ public RadarClientStorage(ClientLevel clientLevel) { super(clientLevel.dimension()); @@ -30,7 +29,7 @@ public RadarClientStorage(ClientLevel clientLevel) { /** * Gets the level associated with this {@link RadarClientStorage} * @return The {@link Minecraft} {@link ClientLevel} - * @since 0.15.1.1 + * @since 0.15.3.3 */ @Override public ClientLevel getLevel() { diff --git a/src/main/java/net/nullved/pmweatherapi/client/radar/WSRClientStorage.java b/src/main/java/net/nullved/pmweatherapi/client/radar/WSRClientStorage.java new file mode 100644 index 0000000..c0a6107 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/client/radar/WSRClientStorage.java @@ -0,0 +1,41 @@ +package net.nullved.pmweatherapi.client.radar; + +import dev.protomanly.pmweather.block.RadarBlock; +import dev.protomanly.pmweather.multiblock.wsr88d.WSR88DCore; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; +import net.nullved.pmweatherapi.radar.storage.WSRStorage; +import net.nullved.pmweatherapi.radar.storage.WSRStorageData; + +/** + * A {@link IClientStorage} implementation for {@link WSR88DCore}s + *

+ * You should not create a {@link WSRClientStorage}, instead, use {@link PMWClientStorages#wsrs()} + * @since 0.15.3.3 + */ +public class WSRClientStorage extends WSRStorage implements IClientStorage { + /** + * DO NOT CALL THIS CONSTRUCTOR!!! + *
+ * Get a radar storage from {@link PMWClientStorages#wsrs()} + * @param clientLevel The {@link ClientLevel} to create this storage for + * @since 0.15.3.3 + */ + public WSRClientStorage(ClientLevel clientLevel) { + super(clientLevel.dimension()); + } + + /** + * Gets the level associated with this {@link WSRClientStorage} + * @return The {@link Minecraft} {@link ClientLevel} + * @since 0.15.3.3 + */ + @Override + public ClientLevel getLevel() { + return Minecraft.getInstance().level; + } +} \ No newline at end of file diff --git a/src/main/java/net/nullved/pmweatherapi/client/render/IRadarOverlay.java b/src/main/java/net/nullved/pmweatherapi/client/render/IRadarOverlay.java index ec2415b..812234b 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/render/IRadarOverlay.java +++ b/src/main/java/net/nullved/pmweatherapi/client/render/IRadarOverlay.java @@ -2,16 +2,21 @@ import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.entity.BlockEntity; import net.nullved.pmweatherapi.data.PMWExtras; import net.nullved.pmweatherapi.radar.RadarMode; +import org.joml.Matrix3f; +import org.joml.Matrix4f; /** * An interface defining a radar overlay @@ -29,17 +34,107 @@ public interface IRadarOverlay { */ void render(boolean canRender, RenderData renderData, BufferBuilder bufferBuilder, Object... args); + /** + * Get the {@link RadarMode} of the radar + * @param renderData The {@link RenderData} + * @return The radar's {@link RadarMode} + * @since 0.14.16.2 + */ default RadarMode getRadarMode(RenderData renderData) { return renderData.blockEntity().getBlockState().getValue(PMWExtras.RADAR_MODE); } + /** + * Render a texture at the given {@link ResourceLocation} + * @param texture The {@link ResourceLocation} of the texture + * @param renderData The {@link RenderData} + * @param poseStack The {@link PoseStack} to render with + * @param color The color + * @since 0.15.3.3 + */ + default void renderTexture(ResourceLocation texture, RenderData renderData, PoseStack poseStack, int color) { + PoseStack.Pose pose = poseStack.last(); + VertexConsumer consumer = renderData.multiBufferSource().getBuffer(RenderType.entityCutout(texture)); + + consumer.addVertex(pose, -0.5f, -0.5f, 0.0f) + .setColor(color) + .setUv(0.0F, 0.0F) + .setOverlay(renderData.combinedOverlayIn()) + .setLight(0xF000F0) + .setNormal(pose, 0.0f, 0.0f, 1.0f); + + consumer.addVertex(pose, 0.5f, -0.5f, 0.0f) + .setColor(color) + .setUv(1.0F, 0.0F) + .setOverlay(renderData.combinedOverlayIn()) + .setLight(0xF000F0) + .setNormal(pose, 0.0f, 0.0f, 1.0f); + + consumer.addVertex(pose, 0.5f, 0.5f, 0.0f) + .setColor(color) + .setUv(1.0F, 1.0F) + .setOverlay(renderData.combinedOverlayIn()) + .setLight(0xF000F0) + .setNormal(pose, 0.0f, 0.0f, 1.0f); + + consumer.addVertex(pose, -0.5f, 0.5f, 0.0f) + .setColor(color) + .setUv(0.0F, 1.0F) + .setOverlay(renderData.combinedOverlayIn()) + .setLight(0xF000F0) + .setNormal(pose, 0.0f, 0.0f, 1.0f); + } + + /** + * Render a texture at the given {@link ResourceLocation} + * @param texture The {@link ResourceLocation} of the texture + * @param renderData The {@link RenderData} + * @param poseStack The {@link PoseStack} to render with + * @since 0.15.3.3 + */ + default void renderTexture(ResourceLocation texture, RenderData renderData, PoseStack poseStack) { + renderTexture(texture, renderData, poseStack, 0xFFFFFFFF); + } + + /** + * Render a texture at the given {@link ResourceLocation} + * @param texture The {@link ResourceLocation} of the texture + * @param renderData The {@link RenderData} + * @param color The color + * @since 0.15.3.3 + */ + default void renderTexture(ResourceLocation texture, RenderData renderData, int color) { + renderTexture(texture, renderData, renderData.poseStack(), color); + } + + /** + * Render a texture at the given {@link ResourceLocation} + * @param texture The {@link ResourceLocation} of the texture + * @param renderData The {@link RenderData} + * @since 0.15.3.3 + */ + default void renderTexture(ResourceLocation texture, RenderData renderData) { + renderTexture(texture, renderData, renderData.poseStack(), 0xFFFFFFFF); + } + + /** + * Render the text given in the given {@link Component} + * @param component The {@link Component} to render + * @param renderData The {@link RenderData} + * @param poseStack The {@link PoseStack} to render with + * @since 0.14.16.2 + */ default void renderText(Component component, RenderData renderData, PoseStack poseStack) { Font font = Minecraft.getInstance().font; - poseStack.mulPose(Axis.XN.rotationDegrees(-90)); font.drawInBatch(component, 0, 0, 0xFFFFFF, false, poseStack.last().pose(), renderData.multiBufferSource(), Font.DisplayMode.NORMAL, 0xFFFFFF, 0xFFFFFF); - poseStack.mulPose(Axis.XN.rotationDegrees(90)); } + /** + * Render the text given in the given {@link Component} + * @param component The {@link Component} to render + * @param renderData The {@link RenderData} + * @since 0.14.16.2 + */ default void renderText(Component component, RenderData renderData) { this.renderText(component, renderData, renderData.poseStack()); } diff --git a/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java b/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java index 7320e38..16c9513 100644 --- a/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java +++ b/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java @@ -4,11 +4,25 @@ import net.minecraft.resources.ResourceLocation; import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.storage.StorageInstance; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.Optional; import java.util.function.Function; -public class ClientStorageInstance { +/** + * A client version of {@link StorageInstance}s, but only holds one {@link IClientStorage} + *

+ * You should not create {@link ClientStorageInstance}s yourself. + * Instead, get them from {@link PMWClientStorages#get} + * + * @param The {@link IStorageData} of the {@link IClientStorage} + * @param The {@link IClientStorage} + * @since 0.15.3.3 + */ +public class ClientStorageInstance> { private final ResourceLocation id; private final Class clazz; private final Function creator; @@ -32,10 +46,10 @@ public void set(C storage) { this.storage = storage; } - public Optional> cast(Class oclazz) { + public > Optional> cast(Class oclazz) { if (oclazz.isAssignableFrom(clazz)) { @SuppressWarnings("unchecked") - ClientStorageInstance casted = new ClientStorageInstance<>(id(), oclazz, cl -> (O) creator.apply(cl)); + ClientStorageInstance casted = new ClientStorageInstance<>(id(), oclazz, cl -> (O) creator.apply(cl)); try { casted.set((O) storage); diff --git a/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java b/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java index 3d9b3cc..b37d257 100644 --- a/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java +++ b/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java @@ -8,14 +8,17 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.ResourceLocationArgument; -import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.ChunkPos; import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; import net.nullved.pmweatherapi.data.PMWStorages; import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.storage.IStorage; +import net.nullved.pmweatherapi.storage.data.StorageData; import net.nullved.pmweatherapi.util.PMWUtils; import java.util.Set; @@ -30,36 +33,94 @@ public static void register(CommandDispatcher dispatcher) { PMWStorages.getAll().forEach(si -> builder.suggest(si.id().toString())); return builder.buildFuture(); }) + .then(Commands.literal("client") + .then(Commands.literal("all") + .then(Commands.argument("radius", IntegerArgumentType.integer(1, 2048)) + .executes(StoragesCommand::clientAll)) + .executes(StoragesCommand::clientAll)) + .then(Commands.literal("adjacentChunks") + .executes(StoragesCommand::clientAdjacentChunks)) + .executes(StoragesCommand::clientAll)) .then(Commands.literal("all") - .then(Commands.argument("radius", IntegerArgumentType.integer(0, 2048)) - .executes(StoragesCommand::storageAll)) - .executes(StoragesCommand::storageAll)) + .then(Commands.argument("radius", IntegerArgumentType.integer(1, 2048)) + .executes(StoragesCommand::serverAll)) + .executes(StoragesCommand::serverAll)) .then(Commands.literal("adjacentChunks") - .executes(StoragesCommand::storageAdjacentChunks)) -// .then(Commands.argument("radius", IntegerArgumentType.integer(1, 2048)) -// .executes(StoragesCommand::exec)) - .executes(StoragesCommand::storageAll) + .executes(StoragesCommand::serverAdjacentChunks)) + .executes(StoragesCommand::serverAll) ) ); } - private static int storageAll(CommandContext ctx) { - BiFunction> func; + private static int clientAll(CommandContext ctx) { + BiFunction, Player, Set> func; try { final int radius = ctx.getArgument("radius", Integer.class); - func = (srv, plr) -> srv.getAllWithinRange(plr.blockPosition(), radius); + func = (stg, plr) -> stg.getAllWithinRange(plr.blockPosition(), radius); } catch (Exception e) { - func = (srv, plr) -> srv.getAll(); + func = (stg, plr) -> stg.getAll(); + } + + return execClient(ctx, func); + } + + private static int serverAll(CommandContext ctx) { + BiFunction, Player, Set> func; + try { + final int radius = ctx.getArgument("radius", Integer.class); + func = (stg, plr) -> stg.getAllWithinRange(plr.blockPosition(), radius); + } catch (Exception e) { + func = (stg, plr) -> stg.getAll(); } return exec(ctx, func); } - private static int storageAdjacentChunks(CommandContext ctx) { - return exec(ctx, (srv, plr) -> srv.getInAdjacentChunks(new ChunkPos(plr.blockPosition()))); + private static int clientAdjacentChunks(CommandContext ctx) { + return execClient(ctx, (stg, plr) -> stg.getInAdjacentChunks(new ChunkPos(plr.blockPosition()))); + } + + private static int serverAdjacentChunks(CommandContext ctx) { + return exec(ctx, (stg, plr) -> stg.getInAdjacentChunks(new ChunkPos(plr.blockPosition()))); + } + + private static int exec(CommandContext ctx, BiFunction, Player, Set> blocksFunction) { + PMWeatherAPI.LOGGER.info("Checking for nearby storages..."); + + Player plr = ctx.getSource().getPlayer(); + ResourceLocation storage = ResourceLocationArgument.getId(ctx, "storage"); + if (!ctx.getSource().isPlayer()) { + ctx.getSource().sendSystemMessage(Component.translatable("commands.pmweatherapi.non_player")); + return Command.SINGLE_SUCCESS; + } + + long startTimeMillis = System.currentTimeMillis(); + IServerStorage stg = (IServerStorage) PMWStorages.get(storage).get(plr.level().dimension()); + + Set blocks = Set.of(); + if (stg != null) { + blocks = blocksFunction.apply(stg, plr); + } + long elapsedTime = System.currentTimeMillis() - startTimeMillis; + + StringBuilder sb = new StringBuilder("Found ").append(blocks.size()).append(" server block positions in ").append(elapsedTime / 1000.0F).append("s"); + for (D data : blocks) { + sb.append("\nPos: ").append(data.getPos().toShortString()); + + if (PMWUtils.isRadarCornerAdjacent(plr.level(), data.getPos())) { + sb.append(" (RA)"); + } + } + + if (PMWUtils.isRadarCornerAdjacent(plr.level(), plr.blockPosition())) { + sb.append("\nYou are next to a radar!"); + } + + plr.sendSystemMessage(Component.literal(sb.toString()).withColor(ChatFormatting.GOLD.getColor())); + return Command.SINGLE_SUCCESS; } - private static int exec(CommandContext ctx, BiFunction> blocksFunction) { + private static int execClient(CommandContext ctx, BiFunction, Player, Set> blocksFunction) { PMWeatherAPI.LOGGER.info("Checking for nearby storages..."); Player plr = ctx.getSource().getPlayer(); @@ -70,19 +131,19 @@ private static int exec(CommandContext ctx, BiFunction stg = (IClientStorage) PMWClientStorages.get(storage).get(); - Set blocks = Set.of(); - if (srv != null) { - blocks = blocksFunction.apply(srv, plr); + Set blocks = Set.of(); + if (stg != null) { + blocks = blocksFunction.apply(stg, plr); } long elapsedTime = System.currentTimeMillis() - startTimeMillis; - StringBuilder sb = new StringBuilder("Found ").append(blocks.size()).append(" block positions in ").append(elapsedTime / 1000.0F).append("s"); - for (BlockPos blockPos : blocks) { - sb.append("\nPos: ").append(blockPos.toShortString()); + StringBuilder sb = new StringBuilder("Found ").append(blocks.size()).append(" client block positions in ").append(elapsedTime / 1000.0F).append("s"); + for (D data : blocks) { + sb.append("\nPos: ").append(data.getPos().toShortString()); - if (PMWUtils.isRadarCornerAdjacent(plr.level(), blockPos)) { + if (PMWUtils.isRadarCornerAdjacent(plr.level(), data.getPos())) { sb.append(" (RA)"); } } diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java index d921393..d9ff6fe 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java @@ -3,14 +3,14 @@ import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.saveddata.SavedData; -import net.nullved.pmweatherapi.radar.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; /** * The {@link SavedData} for PMWeatherAPI * @since 0.14.15.3 - * @deprecated Since 0.15.1.1 | Using new Storages system + * @deprecated Since 0.15.3.3 | For removal in 0.16.0.0 | Using new Storages system */ -@Deprecated(since = "0.15.1.1", forRemoval = true) +@Deprecated(since = "0.15.3.3", forRemoval = true) public class PMWSavedData extends SavedData { private CompoundTag tag; private RadarStorage radarStorage; diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java b/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java index ded4681..fc63946 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorageSavedData.java @@ -9,16 +9,16 @@ /** * A {@link SavedData} instance for {@link IStorage}s * - * @since 0.15.1.1 + * @since 0.15.3.3 */ public class PMWStorageSavedData extends SavedData { private final CompoundTag tag; - private IStorage storage; + private IStorage storage; /** * Gets the factory for loading the {@link SavedData} * @return A {@link SavedData.Factory} for {@link PMWStorageSavedData} - * @since 0.15.1.1 + * @since 0.15.3.3 */ public static SavedData.Factory factory() { return new SavedData.Factory<>(PMWStorageSavedData::new, PMWStorageSavedData::load, null); @@ -44,16 +44,16 @@ public CompoundTag save(CompoundTag compoundTag, HolderLookup.Provider provider) /** * Sets the {@link IStorage} associated with this {@link Level} or dimension * @param storage The {@link IStorage} - * @since 0.15.1.1 + * @since 0.15.3.3 */ - public void setStorage(IStorage storage) { + public void setStorage(IStorage storage) { this.storage = storage; } /** * Gets the {@link SavedData} from the {@link Level} * @return A data {@link CompoundTag} - * @since 0.15.1.1 + * @since 0.15.3.3 */ public CompoundTag getTag() { return tag; diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java index 9fae1ff..402a04c 100644 --- a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java +++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java @@ -5,10 +5,14 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.PMWeatherAPI; -import net.nullved.pmweatherapi.radar.RadarServerStorage; -import net.nullved.pmweatherapi.radar.RadarStorage; +import net.nullved.pmweatherapi.metar.MetarServerStorage; +import net.nullved.pmweatherapi.metar.MetarStorage; +import net.nullved.pmweatherapi.metar.MetarStorageData; +import net.nullved.pmweatherapi.radar.storage.*; import net.nullved.pmweatherapi.storage.IServerStorage; import net.nullved.pmweatherapi.storage.StorageInstance; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.Collection; import java.util.HashMap; @@ -21,51 +25,127 @@ * @since 0.14.15.3 */ public class PMWStorages { - public static final Map> STORAGE_INSTANCES = new HashMap<>(); - + public static final Map> STORAGE_INSTANCES = new HashMap<>(); /** * Gets the map of {@link RadarServerStorage}s * @return The associated dimension's {@link RadarServerStorage} * @since 0.14.15.3 */ - public static StorageInstance radars() { + public static StorageInstance radars() { return get(RadarStorage.ID, RadarServerStorage.class).orElseThrow(); } - public static StorageInstance get(ResourceLocation location) { + /** + * Gets the map of {@link MetarServerStorage}s + * @return The associated dimension's {@link MetarServerStorage} + * @since 0.15.3.3 + */ + public static StorageInstance metars() { + return get(MetarStorage.ID, MetarServerStorage.class).orElseThrow(); + } + + /** + * Gets the map of {@link WSRServerStorage}s + * @return The associated dimension's {@link WSRServerStorage} + * @since 0.15.3.3 + */ + public static StorageInstance wsrs() { + return get(WSRStorage.ID, WSRServerStorage.class).orElseThrow(); + } + + /** + * Get a {@link StorageInstance} for a given {@link ResourceLocation} ID + * @param location The ID of the storage + * @return A {@link StorageInstance} + * @since 0.15.3.3 + */ + public static StorageInstance get(ResourceLocation location) { if (!STORAGE_INSTANCES.containsKey(location)) { PMWeatherAPI.LOGGER.error("No storage instance found for location {}", location); } return STORAGE_INSTANCES.get(location); } - public static void set(ResourceLocation location, StorageInstance instance) { + /** + * Overwrite a {@link StorageInstance} + * @param location The ID {@link ResourceLocation} + * @param instance The new {@link StorageInstance} + * @since 0.15.3.3 + */ + public static void set(ResourceLocation location, StorageInstance instance) { STORAGE_INSTANCES.put(location, instance); } - public static Optional> get(ResourceLocation location, Class clazz) { + /** + * Casts the {@link StorageInstance} to the specified {@link IServerStorage} class after retrieval + * @param location The ID {@link ResourceLocation} + * @param clazz The {@link Class} of an {@link IServerStorage} to cast to + * @return The casted {@link StorageInstance} + * @param The {@link IStorageData} of the {@link IServerStorage} + * @param The {@link IServerStorage} + * @since 0.15.3.3 + */ + public static > Optional> get(ResourceLocation location, Class clazz) { return STORAGE_INSTANCES.get(location).cast(clazz); } - public static Collection> getAll() { + /** + * Resets all data for all {@link StorageInstance}s + * @since 0.15.3.3 + */ + public static void resetAll() { + getAll().forEach(StorageInstance::clear); + } + + /** + * Gets all {@link StorageInstance}s + * @return A {@link Collection} of all {@link StorageInstance}s + * @since 0.15.3.3 + */ + public static Collection> getAll() { return STORAGE_INSTANCES.values(); } - public static Collection getForDimension(ResourceKey dimension) { + /** + * Gets all {@link IServerStorage}s for a given dimension + * @param dimension The {@link ResourceKey} of the dimension to get + * @return A {@link Collection} of {@link IServerStorage} for the given dimension + * @since 0.15.3.3 + */ + public static Collection> getForDimension(ResourceKey dimension) { return getAll().stream().map(si -> si.get(dimension)).toList(); } - public static void generateForDimension(ServerLevel dimension) { - STORAGE_INSTANCES.forEach((rl, si) -> si.load(dimension)); + /** + * Loads a new {@link ServerLevel} for all {@link StorageInstance}s + * @param serverLevel The new {@link ServerLevel} to load + * @since 0.15.3.3 + */ + public static void loadDimension(ServerLevel serverLevel) { + STORAGE_INSTANCES.forEach((rl, si) -> si.load(serverLevel)); } - public static void removeForDimension(ResourceKey dimension) { + /** + * Removes a dimension from all {@link StorageInstance}s + * @param dimension The {@link ResourceKey} of the dimension to remove + * @since 0.15.3.3 + */ + public static void removeDimension(ResourceKey dimension) { STORAGE_INSTANCES.forEach((rl, si) -> si.remove(dimension)); } - public static void registerStorage(ResourceLocation id, Class clazz, Function creator) { - StorageInstance instance = new StorageInstance<>(id, clazz, creator); + /** + * Register a new {@link IServerStorage} + * @param id The {@link ResourceLocation} to save this {@link IServerStorage} as + * @param clazz The {@link Class} of the {@link IServerStorage} + * @param creator A function creating another {@link IServerStorage} for the given {@link ServerLevel} + * @param The {@link IStorageData} of the {@link IServerStorage} + * @param The {@link IServerStorage} + * @since 0.15.3.3 + */ + public static > void registerStorage(ResourceLocation id, Class clazz, Function creator) { + StorageInstance instance = new StorageInstance<>(id, clazz, creator); STORAGE_INSTANCES.put(id, instance); } } diff --git a/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java b/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java index f6b02ac..40a2891 100644 --- a/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java +++ b/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java @@ -1,36 +1,47 @@ package net.nullved.pmweatherapi.event; -import dev.protomanly.pmweather.block.RadarBlock; +import dev.protomanly.pmweather.event.GameBusEvents; +import dev.protomanly.pmweather.weather.Sounding; +import dev.protomanly.pmweather.weather.ThermodynamicEngine; +import dev.protomanly.pmweather.weather.WeatherHandler; +import dev.protomanly.pmweather.weather.WindEngine; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.phys.Vec3; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.event.RegisterCommandsEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; -import net.neoforged.neoforge.event.level.ChunkWatchEvent; import net.neoforged.neoforge.event.level.LevelEvent; +import net.neoforged.neoforge.event.tick.ServerTickEvent; import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.command.StoragesCommand; import net.nullved.pmweatherapi.data.PMWStorages; -import net.nullved.pmweatherapi.radar.RadarServerStorage; +import net.nullved.pmweatherapi.metar.MetarStorageData; import net.nullved.pmweatherapi.storage.IServerStorage; import net.nullved.pmweatherapi.storage.ISyncServerStorage; -import java.util.*; +import java.util.ArrayList; +import java.util.List; @EventBusSubscriber(bus = EventBusSubscriber.Bus.GAME, modid = PMWeatherAPI.MODID) public class PMWEvents { + private static int ticks = 0; + @SubscribeEvent public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) { ResourceKey dimension = event.getEntity().level().dimension(); PMWStorages.getAll().forEach(si -> { - IServerStorage storage = si.get(dimension); + IServerStorage storage = si.get(dimension); if (storage != null) { - if (storage instanceof ISyncServerStorage isss) isss.syncAllToPlayer(event.getEntity()); + if (storage instanceof ISyncServerStorage isss) { + PMWeatherAPI.LOGGER.debug("Syncing stoage {}", isss.getId().toString()); + isss.syncAllToPlayer(event.getEntity()); + } } }); @@ -40,23 +51,61 @@ public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) { @SubscribeEvent public static void onPlayerChangeDimension(PlayerEvent.PlayerChangedDimensionEvent event) { PMWStorages.getForDimension(event.getTo()).forEach(iss -> { - if (iss != null) iss.syncAllToPlayer(event.getEntity()); + if (iss instanceof ISyncServerStorage isss) isss.syncAllToPlayer(event.getEntity()); }); } +// @SubscribeEvent +// public static void onChunkSentEvent(ChunkWatchEvent.Sent event) { +// RadarServerStorage radarStorage = PMWStorages.radars().getOrCreate(event.getLevel()); +// if (radarStorage.shouldRecalculate(event.getChunk().getPos())) { +// List radars = new ArrayList<>(); +// LevelAccessor level = event.getLevel(); +// Set blockEntities = event.getChunk().getBlockEntitiesPos(); +// +// for (BlockPos pos : blockEntities) { +// if (level.getBlockState(pos).getBlock() instanceof RadarBlock) radars.add(new RadarStorageData(pos)); +// } +// +// radarStorage.addAndSync(radars); +// } +// } + @SubscribeEvent - public static void onChunkSentEvent(ChunkWatchEvent.Sent event) { - RadarServerStorage radarStorage = PMWStorages.radars().getOrCreate(event.getLevel()); - if (radarStorage.shouldRecalculate(event.getChunk().getPos())) { - List radars = new ArrayList<>(); - LevelAccessor level = event.getLevel(); - Set blockEntities = event.getChunk().getBlockEntitiesPos(); - - for (BlockPos pos : blockEntities) { - if (level.getBlockState(pos).getBlock() instanceof RadarBlock) radars.add(pos); - } + public static void onTick(ServerTickEvent.Pre event) { + ticks += 1; + if (ticks % 1200 == 0) { + ticks = 0; + + PMWeatherAPI.LOGGER.info("Saving metar data!"); + PMWStorages.metars().entrySet().forEach(entry -> { + WeatherHandler weatherHandler = GameBusEvents.MANAGERS.get(entry.getKey()); + + List updated = new ArrayList<>(); + entry.getValue().getAll().forEach(msd -> { + BlockPos pos = msd.getPos(); + Level level = weatherHandler.getWorld(); + + Vec3 wind = WindEngine.getWind(pos, level); + int windAngle = Math.floorMod((int)Math.toDegrees(Math.atan2(wind.x, -wind.z)), 360); + double windspeed = wind.length(); + ThermodynamicEngine.AtmosphericDataPoint sfc = ThermodynamicEngine.samplePoint(weatherHandler, pos.getCenter(), level, null, 0); + float temp = sfc.temperature(); + float dew = sfc.dewpoint(); + float riskV = 0.0F; + for(int i = 0; i < 24000; i += 200) { + Sounding sounding = new Sounding(weatherHandler, pos.getCenter(), level, 250, 16000, i); + float r = sounding.getRisk(i); + if (r > riskV) { + riskV = r; + } + } - radarStorage.addAndSync(radars); + MetarStorageData newMsd = new MetarStorageData(pos, temp, dew, (float) windAngle, (float) windspeed, riskV); + updated.add(newMsd); + }); + entry.getValue().addAndSync(updated); + }); } } @@ -70,7 +119,7 @@ public static void onRegisterCommandsEvent(RegisterCommandsEvent event) { public static void onLevelLoadEvent(LevelEvent.Load event) { LevelAccessor level = event.getLevel(); if (!level.isClientSide() && level instanceof ServerLevel slevel) { - PMWStorages.generateForDimension(slevel); + PMWStorages.loadDimension(slevel); PMWeatherAPI.LOGGER.info("Loaded storages for dimension {}", slevel.dimension().location()); } } @@ -79,7 +128,7 @@ public static void onLevelLoadEvent(LevelEvent.Load event) { public static void onLevelUnloadEvent(LevelEvent.Unload event) { LevelAccessor level = event.getLevel(); if (!level.isClientSide() && level instanceof ServerLevel slevel) { - PMWStorages.removeForDimension(slevel.dimension()); + PMWStorages.removeDimension(slevel.dimension()); PMWeatherAPI.LOGGER.info("Unloaded storages for dimension {}", slevel.dimension().location()); } } diff --git a/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java b/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java index 29f0f68..ac65ca5 100644 --- a/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java +++ b/src/main/java/net/nullved/pmweatherapi/example/ExampleOverlay.java @@ -1,7 +1,10 @@ package net.nullved.pmweatherapi.example; import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; import dev.protomanly.pmweather.block.RadarBlock; +import dev.protomanly.pmweather.block.entity.RadarBlockEntity; import dev.protomanly.pmweather.config.ClientConfig; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity; @@ -14,6 +17,7 @@ import net.nullved.pmweatherapi.radar.NearbyRadars; import net.nullved.pmweatherapi.radar.RadarMode; import net.nullved.pmweatherapi.storm.NearbyStorms; +import net.nullved.pmweatherapi.util.ColorMaps; import org.joml.Vector3f; /** @@ -30,12 +34,13 @@ public void render(boolean canRender, RenderData renderData, BufferBuilder buffe BlockEntity blockEntity = renderData.blockEntity(); BlockPos pos = blockEntity.getBlockPos(); RadarMode mode = getRadarMode(renderData); + if (mode == RadarMode.REFLECTIVITY) { NearbyRadars.client().forRadarNearBlock(pos, 2048, - p -> renderMarker(bufferBuilder, p.offset(-pos.getX(), -pos.getY(), -pos.getZ()).getCenter())); + p -> renderMarker(bufferBuilder, renderData, p.offset(-pos.getX(), -pos.getY(), -pos.getZ()).getCenter(), 0xFF880000)); } else if (mode == RadarMode.VELOCITY) { NearbyStorms.client().forStormNearBlock(pos, 2048, - s -> renderMarker(bufferBuilder, s.position.add(-pos.getX(), -pos.getY(), -pos.getZ()))); + s -> renderMarker(bufferBuilder, renderData, s.position.add(-pos.getX(), -pos.getY(), -pos.getZ()), 0xFF008800)); } } @@ -44,14 +49,22 @@ public String getModID() { return "example"; } - private static void renderMarker(BufferBuilder bufferBuilder, Vec3 relative) { + private void renderMarker(BufferBuilder bufferBuilder, RenderData renderData, Vec3 relative, int color) { float resolution = ClientConfig.radarResolution; Vector3f radarPos = relative.add(0.5, 0.5, 0.5).toVector3f().mul(3 / (2 * resolution)).div(2048, 0, 2048).div(1.0F / resolution, 0.0F, 1.0F / resolution); Vector3f topLeft = (new Vector3f(-1.0F, 0.0F, -1.0F)).mul(0.015F).add(radarPos.x, 0.005F, radarPos.z); Vector3f bottomLeft = (new Vector3f(-1.0F, 0.0F, 1.0F)).mul(0.015F).add(radarPos.x, 0.005F, radarPos.z); Vector3f bottomRight = (new Vector3f(1.0F, 0.0F, 1.0F)).mul(0.015F).add(radarPos.x, 0.005F, radarPos.z); Vector3f topRight = (new Vector3f(1.0F, 0.0F, -1.0F)).mul(0.015F).add(radarPos.x, 0.005F, radarPos.z); - int color = 0xFFAAAAAA; + + PoseStack pose = renderData.poseStack(); + pose.pushPose(); + pose.translate(0.5F, 1.05F, 0.5F); + pose.translate(radarPos.x, 0.005F, radarPos.z); + pose.scale(0.05F, 0.05F, 0.05F); + pose.mulPose(Axis.XP.rotationDegrees(180)); + + pose.popPose(); bufferBuilder.addVertex(topLeft).setColor(color).addVertex(bottomLeft).setColor(color).addVertex(bottomRight).setColor(color).addVertex(topRight).setColor(color); } } diff --git a/src/main/java/net/nullved/pmweatherapi/metar/MetarServerStorage.java b/src/main/java/net/nullved/pmweatherapi/metar/MetarServerStorage.java new file mode 100644 index 0000000..82d5274 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/metar/MetarServerStorage.java @@ -0,0 +1,40 @@ +package net.nullved.pmweatherapi.metar; + +import dev.protomanly.pmweather.block.MetarBlock; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.metar.MetarClientStorage; +import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.network.S2CMetarPacket; +import net.nullved.pmweatherapi.network.S2CStoragePacket; +import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.storage.ISyncServerStorage; + +/** + * {@link IServerStorage} for {@link MetarBlock}s + *

+ * You should not create a {@link MetarServerStorage}, instead, use {@link PMWStorages#metars()} + * + * @since 0.15.3.3 + * @see MetarStorage + * @see MetarClientStorage + */ +public class MetarServerStorage extends MetarStorage implements ISyncServerStorage { + private final ServerLevel level; + + public MetarServerStorage(ServerLevel level) { + super(level.dimension()); + this.level = level; + } + + @Override + public ServerLevel getLevel() { + return level; + } + + @Override + public S2CStoragePacket> packet(CompoundTag tag) { + return new S2CMetarPacket(tag); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/metar/MetarStorage.java b/src/main/java/net/nullved/pmweatherapi/metar/MetarStorage.java new file mode 100644 index 0000000..7f9f3c8 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/metar/MetarStorage.java @@ -0,0 +1,43 @@ +package net.nullved.pmweatherapi.metar; + +import dev.protomanly.pmweather.block.MetarBlock; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.metar.MetarClientStorage; +import net.nullved.pmweatherapi.storage.PMWStorage; + +/** + * {@link PMWStorage} for {@link MetarBlock}s + * + * @since 0.15.3.3 + * @see PMWStorage + * @see MetarServerStorage + * @see MetarClientStorage + */ +public abstract class MetarStorage extends PMWStorage { + public static final int VERSION = 1; + public static final ResourceLocation ID = PMWeatherAPI.rl("metars"); + + public MetarStorage(ResourceKey dimension) { + super(dimension); + } + + public abstract Level getLevel(); + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public ResourceLocation getExpectedDataType() { + return MetarStorageData.ID; + } + + @Override + public int version() { + return VERSION; + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/metar/MetarStorageData.java b/src/main/java/net/nullved/pmweatherapi/metar/MetarStorageData.java new file mode 100644 index 0000000..9a31d03 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/metar/MetarStorageData.java @@ -0,0 +1,80 @@ +package net.nullved.pmweatherapi.metar; + +import dev.protomanly.pmweather.block.MetarBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.storage.data.StorageData; + +/** + * {@link StorageData} for {@link MetarBlock}s. + * Includes position, temp, dewpoint, wind, and risk data + * + * @since 0.15.3.3 + * @see StorageData + */ +public class MetarStorageData extends StorageData { + public static final ResourceLocation ID = PMWeatherAPI.rl("metar"); + private float temp, dew, windAngle, windspeed, risk; + + public MetarStorageData(BlockPos pos, float temp, float dew, float windAngle, float windspeed, float risk) { + super(pos); + this.temp = temp; + this.dew = dew; + this.windAngle = windAngle; + this.windspeed = windspeed; + this.risk = risk; + } + + public float getTemp() { + return temp; + } + + public float getDew() { + return dew; + } + + public float getWindAngle() { + return windAngle; + } + + public float getWindspeed() { + return windspeed; + } + + public float getRisk() { + return risk; + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public CompoundTag serializeToNBT() { + CompoundTag tag = super.serializeToNBT(); + tag.putFloat("temp", temp); + tag.putFloat("dew", dew); + tag.putFloat("wind_angle", windAngle); + tag.putFloat("windspeed", windspeed); + tag.putFloat("risk", risk); + return tag; + } + + public static MetarStorageData deserializeFromNBT(CompoundTag tag, int version) { + BlockPos bp = deserializeBlockPos(tag); + + if (bp == null) { + throw new IllegalArgumentException("Could not read BlockPos in MetarStorageData!"); + } + + float temp = tag.getFloat("temp"); + float dew = tag.getFloat("dew"); + float windAngle = tag.getFloat("wind_angle"); + float windspeed = tag.getFloat("windspeed"); + float risk = tag.getFloat("risk"); + return new MetarStorageData(bp, temp, dew, windAngle, windspeed, risk); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java index d062fc6..558edc5 100644 --- a/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java +++ b/src/main/java/net/nullved/pmweatherapi/mixin/BlockBehaviourMixin.java @@ -1,12 +1,27 @@ package net.nullved.pmweatherapi.mixin; +import dev.protomanly.pmweather.block.MetarBlock; import dev.protomanly.pmweather.block.RadarBlock; +import dev.protomanly.pmweather.block.entity.RadarBlockEntity; +import dev.protomanly.pmweather.event.GameBusEvents; +import dev.protomanly.pmweather.multiblock.wsr88d.WSR88DCore; +import dev.protomanly.pmweather.weather.Sounding; +import dev.protomanly.pmweather.weather.ThermodynamicEngine; +import dev.protomanly.pmweather.weather.WeatherHandler; +import dev.protomanly.pmweather.weather.WindEngine; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import net.nullved.pmweatherapi.data.PMWExtras; import net.nullved.pmweatherapi.data.PMWStorages; -import net.nullved.pmweatherapi.radar.RadarServerStorage; +import net.nullved.pmweatherapi.metar.MetarServerStorage; +import net.nullved.pmweatherapi.metar.MetarStorageData; +import net.nullved.pmweatherapi.radar.RadarMode; +import net.nullved.pmweatherapi.radar.storage.RadarServerStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; +import net.nullved.pmweatherapi.radar.storage.WSRStorageData; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -18,7 +33,29 @@ public class BlockBehaviourMixin { private static void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston, CallbackInfo ci) { if (state.getBlock() instanceof RadarBlock) { RadarServerStorage radarStorage = PMWStorages.radars().get(level.dimension()); - radarStorage.addAndSync(pos); + radarStorage.addAndSync(new RadarStorageData(pos, state.getValue(PMWExtras.RADAR_MODE))); + } else if (state.getBlock() instanceof MetarBlock) { + // Get Metar data + WeatherHandler weatherHandler = GameBusEvents.MANAGERS.get(level.dimension()); + Vec3 wind = WindEngine.getWind(pos, level); + int windAngle = Math.floorMod((int)Math.toDegrees(Math.atan2(wind.x, -wind.z)), 360); + double windspeed = wind.length(); + ThermodynamicEngine.AtmosphericDataPoint sfc = ThermodynamicEngine.samplePoint(weatherHandler, pos.getCenter(), level, null, 0); + float temp = sfc.temperature(); + float dew = sfc.dewpoint(); + float riskV = 0.0F; + for(int i = 0; i < 24000; i += 200) { + Sounding sounding = new Sounding(weatherHandler, pos.getCenter(), level, 250, 16000, i); + float r = sounding.getRisk(i); + if (r > riskV) { + riskV = r; + } + } + + MetarServerStorage metarStorage = PMWStorages.metars().get(level.dimension()); + metarStorage.addAndSync(new MetarStorageData(pos, temp, dew, (float) windAngle, (float) windspeed, riskV)); + } else if (state.getBlock() instanceof WSR88DCore wsr) { + PMWStorages.wsrs().get(level.dimension()).addAndSync(new WSRStorageData(pos, wsr.isComplete(state))); } } @@ -27,6 +64,11 @@ private static void onRemove(BlockState state, Level level, BlockPos pos, BlockS if (state.getBlock() instanceof RadarBlock) { RadarServerStorage radarStorage = PMWStorages.radars().get(level.dimension()); radarStorage.removeAndSync(pos); + } else if (state.getBlock() instanceof MetarBlock) { + MetarServerStorage metarStorage = PMWStorages.metars().get(level.dimension()); + metarStorage.removeAndSync(pos); + }else if (state.getBlock() instanceof WSR88DCore wsr) { + PMWStorages.wsrs().get(level.dimension()).removeAndSync(pos); } } } diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java deleted file mode 100644 index 442e4b4..0000000 --- a/src/main/java/net/nullved/pmweatherapi/mixin/PacketNBTFromClientMixin.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.nullved.pmweatherapi.mixin; - -import dev.protomanly.pmweather.networking.PacketNBTFromClient; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.world.entity.player.Player; -import net.nullved.pmweatherapi.data.PMWStorages; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(PacketNBTFromClient.class) -public class PacketNBTFromClientMixin { - @Shadow @Final private CompoundTag compoundTag; - - @Inject(method = "handle", at = @At(value = "INVOKE", target = "Ldev/protomanly/pmweather/block/entity/RadarBlockEntity;playerRequestsSync(Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/core/BlockPos;)V")) - private void onHandle(Player player, CallbackInfo ci) { - PMWStorages.radars().get(player.level().dimension()).add(NbtUtils.readBlockPos(this.compoundTag, "blockPos").get()); - } -} diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/RadarBlockMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/RadarBlockMixin.java index 873547d..49108eb 100644 --- a/src/main/java/net/nullved/pmweatherapi/mixin/RadarBlockMixin.java +++ b/src/main/java/net/nullved/pmweatherapi/mixin/RadarBlockMixin.java @@ -14,7 +14,9 @@ import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.phys.BlockHitResult; import net.nullved.pmweatherapi.data.PMWExtras; +import net.nullved.pmweatherapi.data.PMWStorages; import net.nullved.pmweatherapi.radar.RadarMode; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -49,6 +51,7 @@ private InteractionResult useWithoutItem(BlockState state, Level level, BlockPos if (!level.isClientSide()) { RadarMode currentMode = state.getValue(PMWExtras.RADAR_MODE); RadarMode newMode = currentMode.cycle(); + PMWStorages.radars().get(level.dimension()).addAndSync(new RadarStorageData(pos, newMode)); level.setBlockAndUpdate(pos, state.setValue(PMWExtras.RADAR_MODE, newMode)); } diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java index 67acf73..b1943f1 100644 --- a/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java +++ b/src/main/java/net/nullved/pmweatherapi/mixin/RadarRendererMixin.java @@ -568,17 +568,19 @@ private void render(BlockEntity blockEntity, float partialTicks, PoseStack poseS color = dbg; } - r = (float)color.getRed() / 255.0F; - g = (float)color.getGreen() / 255.0F; - b = (float)color.getBlue() / 255.0F; - a = (float)color.getAlpha() / 255.0F * 0.75F + 0.25F; - - Vector3f topLeft = (new Vector3f(-1.0F, 0.0F, -1.0F)).mul(size / 4.0F).add(pixelPos); - Vector3f bottomLeft = (new Vector3f(-1.0F, 0.0F, 1.0F)).mul(size / 4.0F).add(pixelPos); - Vector3f bottomRight = (new Vector3f(1.0F, 0.0F, 1.0F)).mul(size / 4.0F).add(pixelPos); - Vector3f topRight = (new Vector3f(1.0F, 0.0F, -1.0F)).mul(size / 4.0F).add(pixelPos); - - bufferBuilder.addVertex(topLeft).setColor(r, g, b, a).addVertex(bottomLeft).setColor(r, g, b, a).addVertex(bottomRight).setColor(r, g, b, a).addVertex(topRight).setColor(r, g, b, a); + if (!RadarMode.isBaseRenderingDisabled()) { + r = (float) color.getRed() / 255.0F; + g = (float) color.getGreen() / 255.0F; + b = (float) color.getBlue() / 255.0F; + a = (float) color.getAlpha() / 255.0F * 0.75F + 0.25F; + + Vector3f topLeft = (new Vector3f(-1.0F, 0.0F, -1.0F)).mul(size / 4.0F).add(pixelPos); + Vector3f bottomLeft = (new Vector3f(-1.0F, 0.0F, 1.0F)).mul(size / 4.0F).add(pixelPos); + Vector3f bottomRight = (new Vector3f(1.0F, 0.0F, 1.0F)).mul(size / 4.0F).add(pixelPos); + Vector3f topRight = (new Vector3f(1.0F, 0.0F, -1.0F)).mul(size / 4.0F).add(pixelPos); + + bufferBuilder.addVertex(topLeft).setColor(r, g, b, a).addVertex(bottomLeft).setColor(r, g, b, a).addVertex(bottomRight).setColor(r, g, b, a).addVertex(topRight).setColor(r, g, b, a); + } } } diff --git a/src/main/java/net/nullved/pmweatherapi/mixin/WSR88DCoreMixin.java b/src/main/java/net/nullved/pmweatherapi/mixin/WSR88DCoreMixin.java new file mode 100644 index 0000000..f26f8d0 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/mixin/WSR88DCoreMixin.java @@ -0,0 +1,20 @@ +package net.nullved.pmweatherapi.mixin; + +import dev.protomanly.pmweather.multiblock.wsr88d.WSR88DCore; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.radar.storage.WSRStorageData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(WSR88DCore.class) +public class WSR88DCoreMixin { + @Inject(method = "completionChanged", at = @At("HEAD")) + public void completionChanged(boolean completed, Level level, BlockState blockState, BlockPos pos, CallbackInfo ci) { + PMWStorages.wsrs().get(level.dimension()).addAndSync(new WSRStorageData(pos, completed)); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java b/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java index 04718e2..867461b 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java +++ b/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java @@ -1,17 +1,15 @@ package net.nullved.pmweatherapi.network; import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.neoforged.neoforge.network.PacketDistributor; import net.neoforged.neoforge.network.handling.IPayloadHandler; import net.neoforged.neoforge.network.registration.PayloadRegistrar; -import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.radar.storage.RadarServerStorage; import java.util.function.BiConsumer; import java.util.function.Function; @@ -27,7 +25,9 @@ public class PMWNetworking { * @since 0.14.15.3 */ public static void register(Object... args) { - registerClientboundPacket(S2CRadarsPacket.TYPE, S2CRadarsPacket.STREAM_CODEC, S2CRadarsPacket::handle, args); + registerClientboundPacket(S2CRadarPacket.TYPE, S2CRadarPacket.STREAM_CODEC, S2CRadarPacket::handle, args); + registerClientboundPacket(S2CMetarPacket.TYPE, S2CMetarPacket.STREAM_CODEC, S2CMetarPacket::handle, args); + registerClientboundPacket(S2CWSRPacket.TYPE, S2CWSRPacket.STREAM_CODEC, S2CWSRPacket::handle, args); } /** @@ -62,8 +62,8 @@ public static void registerClientboundPacket(Cus /** * Sends all clients a {@link S2CStoragePacket}. The type of packet is dependent on the caller of this method - * @param tag The tag to send, generated by the {@link net.nullved.pmweatherapi.radar.RadarServerStorage} - * @since 0.15.1.1 + * @param tag The tag to send, generated by the {@link RadarServerStorage} + * @since 0.15.3.3 */ public static void serverSendStorageToAll(CompoundTag tag, Function> pkt) { PacketDistributor.sendToAllPlayers(pkt.apply(tag)); @@ -72,7 +72,7 @@ public static void serverSendStorageToAll(CompoundTag tag, Function> pkt, Player player) { PacketDistributor.sendToPlayer((ServerPlayer) player, pkt.apply(tag)); diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CMetarPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CMetarPacket.java new file mode 100644 index 0000000..00a4d73 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CMetarPacket.java @@ -0,0 +1,44 @@ +package net.nullved.pmweatherapi.network; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.client.metar.MetarClientStorage; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; + +/** + * The packet that syncs metars from the server to the client, using the Storages system + * @since 0.15.3.3 + */ +public class S2CMetarPacket extends S2CStoragePacket { + public static final Type TYPE = new Type<>(PMWeatherAPI.rl("s2c_metar")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.COMPOUND_TAG, S2CMetarPacket::tag, S2CMetarPacket::new); + + /** + * Creates a new {@link S2CMetarPacket} + * @param tag The {@link CompoundTag} to send with the packet + * @since 0.15.3.3 + */ + public S2CMetarPacket(CompoundTag tag) { + super(tag); + } + + /** + * Gets the {@link MetarClientStorage} that is receiving data + * @return The {@link MetarClientStorage} + * @since 0.15.3.3 + */ + @Override + public MetarClientStorage getStorage() { + return PMWClientStorages.metars().get(); + } + + @Override + public Type type() { + return TYPE; + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarPacket.java similarity index 69% rename from src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java rename to src/main/java/net/nullved/pmweatherapi/network/S2CRadarPacket.java index 9df9895..e65ddb6 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarPacket.java @@ -11,25 +11,25 @@ /** * The packet that syncs radars from the server to the client, using the Storages system - * @since 0.15.1.1 + * @since 0.15.3.3 */ -public class S2CRadarsPacket extends S2CStoragePacket { - public static final CustomPacketPayload.Type TYPE = new Type<>(PMWeatherAPI.rl("s2c_radars")); - public static final StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.COMPOUND_TAG, S2CRadarsPacket::tag, S2CRadarsPacket::new); +public class S2CRadarPacket extends S2CStoragePacket { + public static final CustomPacketPayload.Type TYPE = new Type<>(PMWeatherAPI.rl("s2c_radar")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.COMPOUND_TAG, S2CRadarPacket::tag, S2CRadarPacket::new); /** - * Creates a new {@link S2CRadarsPacket} + * Creates a new {@link S2CRadarPacket} * @param tag The {@link CompoundTag} to send with the packet - * @since 0.15.1.1 + * @since 0.15.3.3 */ - public S2CRadarsPacket(CompoundTag tag) { + public S2CRadarPacket(CompoundTag tag) { super(tag); } /** * Gets the {@link RadarClientStorage} that is receiving data * @return The {@link RadarClientStorage} - * @since 0.15.1.1 + * @since 0.15.3.3 */ @Override public RadarClientStorage getStorage() { diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java index 35c54e1..2ddc217 100644 --- a/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java @@ -3,30 +3,25 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.world.entity.player.Player; import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.client.data.IClientStorage; -import net.nullved.pmweatherapi.client.radar.RadarClientStorage; -import net.nullved.pmweatherapi.client.data.PMWClientStorages; -import net.nullved.pmweatherapi.storage.IStorage; /** * A base packet for the Storages system that syncs data from the Server -> Client (S2C) - * @param The {@link IClientStorage} that will be synced to - * @since 0.15.1.1 + * @param The {@link IClientStorage} that will be synced to + * @since 0.15.3.3 */ -public abstract class S2CStoragePacket implements CustomPacketPayload { +public abstract class S2CStoragePacket> implements CustomPacketPayload { public CompoundTag tag; /** * Gets the {@link IClientStorage} that will be synced to. * @return The storage to be synced. - * @since 0.15.1.1 + * @since 0.15.3.3 */ - public abstract T getStorage(); + public abstract C getStorage(); public S2CStoragePacket(CompoundTag tag) { this.tag = tag; @@ -35,7 +30,7 @@ public S2CStoragePacket(CompoundTag tag) { /** * Creates a new {@link S2CStoragePacket} from a {@link RegistryFriendlyByteBuf} * @param buf The {@link RegistryFriendlyByteBuf} to read a {@link CompoundTag} from - * @since 0.15.1.1 + * @since 0.15.3.3 */ public S2CStoragePacket(RegistryFriendlyByteBuf buf) { this(buf.readNbt()); @@ -44,7 +39,7 @@ public S2CStoragePacket(RegistryFriendlyByteBuf buf) { /** * Gets the {@link CompoundTag} send with the packet * @return The packet data - * @since 0.15.1.1 + * @since 0.15.3.3 */ public CompoundTag tag() { return tag; @@ -53,21 +48,22 @@ public CompoundTag tag() { /** * Writes the data into the given {@link FriendlyByteBuf} * @param buf The {@link FriendlyByteBuf} to write into - * @since 0.15.1.1 + * @since 0.15.3.3 */ public void write(FriendlyByteBuf buf) { + tag.putInt("version", getStorage().version()); buf.writeNbt(tag); } /** * Handles the packet on the CLIENT SIDE * @param player The player the packet was sent to - * @since 0.15.1.1 + * @since 0.15.3.3 */ public void handle(Player player) { try { String operation = tag.getString("operation"); - T storage = getStorage(); + C storage = getStorage(); if (operation.equals("overwrite")) { storage.syncAll(tag); diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CWSRPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CWSRPacket.java new file mode 100644 index 0000000..24591fd --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/network/S2CWSRPacket.java @@ -0,0 +1,44 @@ +package net.nullved.pmweatherapi.network; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; +import net.nullved.pmweatherapi.client.metar.MetarClientStorage; +import net.nullved.pmweatherapi.client.radar.WSRClientStorage; + +/** + * The packet that syncs wsrs from the server to the client, using the Storages system + * @since 0.15.3.3 + */ +public class S2CWSRPacket extends S2CStoragePacket { + public static final Type TYPE = new Type<>(PMWeatherAPI.rl("s2c_wsr")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.COMPOUND_TAG, S2CWSRPacket::tag, S2CWSRPacket::new); + + /** + * Creates a new {@link S2CWSRPacket} + * @param tag The {@link CompoundTag} to send with the packet + * @since 0.15.3.3 + */ + public S2CWSRPacket(CompoundTag tag) { + super(tag); + } + + /** + * Gets the {@link WSRClientStorage} that is receiving data + * @return The {@link WSRClientStorage} + * @since 0.15.3.3 + */ + @Override + public WSRClientStorage getStorage() { + return PMWClientStorages.wsrs().get(); + } + + @Override + public Type type() { + return TYPE; + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java b/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java index aa3c394..16aacad 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/NearbyRadars.java @@ -10,6 +10,9 @@ import net.neoforged.api.distmarker.OnlyIn; import net.nullved.pmweatherapi.client.data.PMWClientStorages; import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; +import net.nullved.pmweatherapi.storage.data.BlockPosData; import java.util.HashMap; import java.util.HashSet; @@ -73,8 +76,8 @@ public static NearbyRadars get(Level level) { public Set radarsNearBlock(BlockPos pos, double radius) { Set radarList = new HashSet<>(); - for (BlockPos radar: storage.getAll()) { - if (Math.abs(radar.distToCenterSqr(pos.getX(), pos.getY(), pos.getZ())) <= radius * radius) radarList.add(radar); + for (RadarStorageData radar: storage.getAll()) { + if (Math.abs(radar.getPos().distToCenterSqr(pos.getX(), pos.getY(), pos.getZ())) <= radius * radius) radarList.add(radar.getPos()); } radarList.remove(pos); @@ -92,8 +95,8 @@ public Set radarsNearBlock(BlockPos pos, double radius) { public Set radarsNearChunk(ChunkPos pos, double radius) { Set radarList = new HashSet<>(); - for (BlockPos radar: storage.getAll()) { - if (Math.abs(radar.distToCenterSqr(pos.getMiddleBlockX(), radar.getY(), pos.getMiddleBlockZ())) <= radius * radius) radarList.add(radar); + for (RadarStorageData radar: storage.getAll()) { + if (Math.abs(radar.getPos().distToCenterSqr(pos.getMiddleBlockX(), radar.getPos().getY(), pos.getMiddleBlockZ())) <= radius * radius) radarList.add(radar.getPos()); } return radarList; @@ -109,8 +112,8 @@ public Set radarsNearChunk(ChunkPos pos, double radius) { public Set radarsNearPlayer(Player player, double radius) { Set radarList = new HashSet<>(); - for (BlockPos radar: storage.getAll()) { - if (Math.abs(radar.distToCenterSqr(player.getX(), player.getY(), player.getZ())) <= radius * radius) radarList.add(radar); + for (RadarStorageData radar: storage.getAll()) { + if (Math.abs(radar.getPos().distToCenterSqr(player.getX(), player.getY(), player.getZ())) <= radius * radius) radarList.add(radar.getPos()); } return radarList; diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java b/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java index af90357..17f9516 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/RadarMode.java @@ -42,6 +42,7 @@ */ public class RadarMode implements StringRepresentable, Comparable { private static final LinkedHashMap MODES = new LinkedHashMap<>(); + private static boolean disableBaseRendering = false; /** * A "Null" Radar Mode mimicking Minecraft's missing texture. @@ -57,7 +58,7 @@ public class RadarMode implements StringRepresentable, Comparable { * A Radar Mode that is a copy of PMWeather's Reflectivity * @since 0.14.15.6 */ - public static final RadarMode REFLECTIVITY = create(PMWeather.getPath("reflectivity"), prd -> { + public static final RadarMode REFLECTIVITY = create(PMWeather.getPath("reflectivity"), prd -> { Holder biome = ((RadarBlockEntity) prd.renderData().blockEntity()).getNearestBiome(new BlockPos((int) prd.worldPos().x, (int) prd.worldPos().y, (int) prd.worldPos().z)); if (biome != null) return ColorMaps.REFLECTIVITY.getWithBiome(prd.rdbz(), biome, prd.worldPos()); else return ColorMaps.REFLECTIVITY.get(prd.rdbz()); @@ -101,6 +102,24 @@ private RadarMode(ResourceLocation id, Function colorFun this.dotColor = dotColor; } + /** + * Disables all rendering of pixels from any radar mode + * @param disable Whether to disable rendering or not + * @since 0.15.3.3 + */ + public static void disableBaseRendering(boolean disable) { + disableBaseRendering = disable; + } + + /** + * Returns whether base rendering is disabled or not + * @return Base rendering disable state + * @since 0.15.3.3 + */ + public static boolean isBaseRenderingDisabled() { + return disableBaseRendering; + } + /** * Create a new {@link RadarMode} * @param id The {@link ResourceLocation} of this radar mode diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarServerStorage.java similarity index 63% rename from src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java rename to src/main/java/net/nullved/pmweatherapi/radar/storage/RadarServerStorage.java index 78cd4ca..f94cfae 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarServerStorage.java @@ -1,23 +1,28 @@ -package net.nullved.pmweatherapi.radar; +package net.nullved.pmweatherapi.radar.storage; import dev.protomanly.pmweather.block.RadarBlock; +import dev.protomanly.pmweather.multiblock.wsr88d.WSR88DCore; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; +import net.nullved.pmweatherapi.client.radar.WSRClientStorage; import net.nullved.pmweatherapi.data.PMWStorages; -import net.nullved.pmweatherapi.network.S2CRadarsPacket; +import net.nullved.pmweatherapi.network.S2CRadarPacket; import net.nullved.pmweatherapi.network.S2CStoragePacket; import net.nullved.pmweatherapi.storage.IServerStorage; import net.nullved.pmweatherapi.storage.ISyncServerStorage; /** - * A {@link IServerStorage} implementation for {@link RadarBlock}s + * {@link IServerStorage} for {@link RadarBlock}s *

* You should not create a {@link RadarServerStorage}, instead, use {@link PMWStorages#radars()} * - * @since 0.15.1.1 + * @since 0.15.3.3 + * @see RadarStorage + * @see RadarClientStorage */ -public class RadarServerStorage extends RadarStorage implements ISyncServerStorage { +public class RadarServerStorage extends RadarStorage implements ISyncServerStorage { private final ServerLevel level; /** @@ -25,7 +30,7 @@ public class RadarServerStorage extends RadarStorage implements ISyncServerStora *
* Get a radar storage from {@link PMWStorages#radars()} * @param level The level to create this storage for - * @since 0.15.1.1 + * @since 0.15.3.3 */ public RadarServerStorage(ServerLevel level) { super(level.dimension()); @@ -38,7 +43,7 @@ public ServerLevel getLevel() { } @Override - public S2CStoragePacket packet(CompoundTag tag) { - return new S2CRadarsPacket(tag); + public S2CStoragePacket> packet(CompoundTag tag) { + return new S2CRadarPacket(tag); } } diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorage.java similarity index 57% rename from src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java rename to src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorage.java index 8766abf..f06fe02 100644 --- a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorage.java @@ -1,4 +1,4 @@ -package net.nullved.pmweatherapi.radar; +package net.nullved.pmweatherapi.radar.storage; import dev.protomanly.pmweather.block.RadarBlock; @@ -6,15 +6,20 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.radar.RadarClientStorage; import net.nullved.pmweatherapi.storage.IStorage; import net.nullved.pmweatherapi.storage.PMWStorage; /** - * A {@link IStorage} implementation for {@link RadarBlock}s + * {@link PMWStorage} for {@link RadarBlock}s * - * @since 0.15.1.1 + * @since 0.15.3.3 + * @see PMWStorage + * @see RadarServerStorage + * @see RadarClientStorage */ -public abstract class RadarStorage extends PMWStorage { +public abstract class RadarStorage extends PMWStorage { + public static final int VERSION = 2; public static final ResourceLocation ID = PMWeatherAPI.rl("radars"); public RadarStorage(ResourceKey dimension) { @@ -28,8 +33,13 @@ public ResourceLocation getId() { return ID; } + @Override + public ResourceLocation getExpectedDataType() { + return RadarStorageData.ID; + } + @Override public int version() { - return 1; + return VERSION; } } diff --git a/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorageData.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorageData.java new file mode 100644 index 0000000..de46a96 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorageData.java @@ -0,0 +1,50 @@ +package net.nullved.pmweatherapi.radar.storage; + +import dev.protomanly.pmweather.block.RadarBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.radar.RadarMode; +import net.nullved.pmweatherapi.storage.data.StorageData; + +/** + * {@link StorageData} for {@link RadarBlock}s. + * Includes position and radar mode data + * + * @since 0.15.3.3 + * @see StorageData + */ +public class RadarStorageData extends StorageData { + public static final ResourceLocation ID = PMWeatherAPI.rl("radar"); + private RadarMode radarMode; + + public RadarStorageData(BlockPos pos, RadarMode radarMode) { + super(pos); + this.radarMode = radarMode; + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public CompoundTag serializeToNBT() { + CompoundTag tag = super.serializeToNBT(); + tag.putString("radar_mode", radarMode.getSerializedName()); + return tag; + } + + public static RadarStorageData deserializeFromNBT(CompoundTag tag, int version) { + BlockPos bp = deserializeBlockPos(tag); + + if (bp != null) { + RadarMode mode = RadarMode.get(tag.getString("radar_mode")); + return new RadarStorageData(bp, mode); + } else { + return new RadarStorageData(NbtUtils.readBlockPos(tag, "").orElseThrow(() -> new IllegalArgumentException("Could not read BlockPos in RadarStorageData!")), RadarMode.get(tag.getString("radar_mode"))); + } + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRServerStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRServerStorage.java new file mode 100644 index 0000000..31b453d --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRServerStorage.java @@ -0,0 +1,48 @@ +package net.nullved.pmweatherapi.radar.storage; + +import dev.protomanly.pmweather.multiblock.wsr88d.WSR88DCore; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.radar.WSRClientStorage; +import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.network.S2CRadarPacket; +import net.nullved.pmweatherapi.network.S2CStoragePacket; +import net.nullved.pmweatherapi.network.S2CWSRPacket; +import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.storage.ISyncServerStorage; + +/** + * {@link IServerStorage} for {@link WSR88DCore}s + *

+ * You should not create a {@link WSRServerStorage}, instead, use {@link PMWStorages#wsrs()} + * + * @since 0.15.3.3 + * @see WSRStorage + * @see WSRClientStorage + */ +public class WSRServerStorage extends WSRStorage implements ISyncServerStorage { + private final ServerLevel level; + + /** + * DO NOT CALL THIS CONSTRUCTOR!!! + *
+ * Get a radar storage from {@link PMWStorages#wsrs()} + * @param level The level to create this storage for + * @since 0.15.3.3 + */ + public WSRServerStorage(ServerLevel level) { + super(level.dimension()); + this.level = level; + } + + @Override + public ServerLevel getLevel() { + return level; + } + + @Override + public S2CStoragePacket> packet(CompoundTag tag) { + return new S2CWSRPacket(tag); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorage.java new file mode 100644 index 0000000..b714554 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorage.java @@ -0,0 +1,41 @@ +package net.nullved.pmweatherapi.radar.storage; + +import dev.protomanly.pmweather.multiblock.wsr88d.WSR88DCore; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.radar.WSRClientStorage; +import net.nullved.pmweatherapi.storage.PMWStorage; + +/** + * {@link PMWStorage} for {@link WSR88DCore}s + * + * @since 0.15.3.3 + * @see PMWStorage + * @see WSRServerStorage + * @see WSRClientStorage + */ +public abstract class WSRStorage extends PMWStorage { + public static final int VERSION = 1; + public static final ResourceLocation ID = PMWeatherAPI.rl("wsrs"); + + public WSRStorage(ResourceKey dimension) { + super(dimension); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public ResourceLocation getExpectedDataType() { + return WSRStorageData.ID; + } + + @Override + public int version() { + return VERSION; + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorageData.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorageData.java new file mode 100644 index 0000000..4185fd8 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/WSRStorageData.java @@ -0,0 +1,50 @@ +package net.nullved.pmweatherapi.radar.storage; + +import dev.protomanly.pmweather.multiblock.wsr88d.WSR88DCore; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.storage.data.StorageData; + +/** + * {@link StorageData} for {@link WSR88DCore}s. + * Includes position and completion data + * + * @since 0.15.3.3 + * @see StorageData + */ +public class WSRStorageData extends StorageData { + public static final ResourceLocation ID = PMWeatherAPI.rl("wsr"); + private final boolean completed; + + public WSRStorageData(BlockPos pos, boolean completed) { + super(pos); + this.completed = completed; + } + + public boolean isCompleted() { + return completed; + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public CompoundTag serializeToNBT() { + CompoundTag tag = super.serializeToNBT(); + tag.putBoolean("completed", completed); + return tag; + } + + public static WSRStorageData deserializeFromNBT(CompoundTag tag, int version) { + BlockPos bp = deserializeBlockPos(tag); + if (bp != null) { + boolean completed = tag.getBoolean("completed"); + return new WSRStorageData(bp, completed); + } + else throw new IllegalArgumentException("Could not read BlockPos in WSRStorageData!"); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java index 4b80827..aeb89d6 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java @@ -10,6 +10,8 @@ import net.nullved.pmweatherapi.data.PMWStorages; import net.nullved.pmweatherapi.network.PMWNetworking; import net.nullved.pmweatherapi.network.S2CStoragePacket; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.Collection; @@ -18,13 +20,13 @@ * There is a {@link IServerStorage} for each dimension of a save *
* You should only create a {@link IServerStorage} for a level once, instead, use {@link PMWStorages} if built-in or a custom storage handler. - * @since 0.15.1.1 + * @since 0.15.3.3 */ -public interface IServerStorage extends IStorage { +public interface IServerStorage extends IStorage { /** * Gets the level associated with this {@link IServerStorage} * @return A {@link ServerLevel} - * @since 0.15.1.1 + * @since 0.15.3.3 */ ServerLevel getLevel(); @@ -32,14 +34,31 @@ public interface IServerStorage extends IStorage { * Generates a {@link S2CStoragePacket} to be sent to the client * @param tag The {@link CompoundTag} to be sent * @return A {@link S2CStoragePacket} instance - * @since 0.15.1.1 + * @since 0.15.3.3 */ - S2CStoragePacket packet(CompoundTag tag); + S2CStoragePacket> packet(CompoundTag tag); /** - * Syncs all {@link BlockPos} from the storage to the given player + * Syncs all {@link IStorageData} to all players + * @since 0.15.3.3 + */ + default void syncAllToAll() { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "overwrite"); + tag.putBoolean("list", true); + + ListTag list = new ListTag(); + getAll().forEach(data -> list.add(data.serializeToNBT())); + + tag.put("data", list); + + PMWNetworking.serverSendStorageToAll(tag, this::packet); + } + + /** + * Syncs all {@link IStorageData} from the storage to the given player * @param player The {@link Player} to sync all data to - * @since 0.15.1.1 + * @since 0.15.3.3 */ default void syncAllToPlayer(Player player) { CompoundTag tag = new CompoundTag(); @@ -47,9 +66,7 @@ default void syncAllToPlayer(Player player) { tag.putBoolean("list", true); ListTag list = new ListTag(); - for (BlockPos pos: getAll()) { - list.add(NbtUtils.writeBlockPos(pos)); - } + getAll().forEach(data -> list.add(data.serializeToNBT())); tag.put("data", list); @@ -57,32 +74,30 @@ default void syncAllToPlayer(Player player) { } /** - * Syncs new {@link BlockPos} to all clients - * @param pos The new {@link BlockPos} - * @since 0.15.1.1 + * Syncs new {@link IStorageData} to all clients + * @param data The new {@link IStorageData} + * @since 0.15.3.3 */ - default void syncAdd(BlockPos pos) { + default void syncAdd(D data) { CompoundTag tag = new CompoundTag(); tag.putString("operation", "add"); - tag.put("data", NbtUtils.writeBlockPos(pos)); + tag.put("data", data.serializeToNBT()); PMWNetworking.serverSendStorageToAll(tag, this::packet); } /** - * Syncs multiple new {@link BlockPos} to all clients - * @param posList A {@link Collection} of {@link BlockPos} to sync - * @since 0.15.1.1 + * Syncs multiple new {@link IStorageData} to all clients + * @param datum A {@link Collection} of {@link IStorageData} to sync + * @since 0.15.3.3 */ - default void syncAdd(Collection posList) { + default void syncAdd(Collection datum) { CompoundTag tag = new CompoundTag(); tag.putString("operation", "add"); tag.putBoolean("list", true); ListTag list = new ListTag(); - for (BlockPos pos: posList) { - list.add(NbtUtils.writeBlockPos(pos)); - } + datum.forEach(data -> list.add(data.serializeToNBT())); tag.put("data", list); @@ -92,11 +107,12 @@ default void syncAdd(Collection posList) { /** * Syncs a {@link BlockPos} removal to all clients * @param pos The {@link BlockPos} of the radar to remove - * @since 0.15.1.1 + * @since 0.15.3.3 */ default void syncRemove(BlockPos pos) { CompoundTag tag = new CompoundTag(); tag.putString("operation", "remove"); + tag.putString("format", "blockpos"); tag.put("data", NbtUtils.writeBlockPos(pos)); PMWNetworking.serverSendStorageToAll(tag, this::packet); @@ -105,17 +121,47 @@ default void syncRemove(BlockPos pos) { /** * Syncs multiple {@link BlockPos} removals to all clients * @param posList A {@link Collection} of {@link BlockPos} to sync - * @since 0.15.1.1 + * @since 0.15.3.3 + */ + default void syncRemoveByPos(Collection posList) { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "remove"); + tag.putString("format", "blockpos"); + tag.putBoolean("list", true); + + ListTag list = new ListTag(); + posList.forEach(pos -> list.remove(NbtUtils.writeBlockPos(pos))); + + tag.put("data", list); + + PMWNetworking.serverSendStorageToAll(tag, this::packet); + } + + /** + * Syncs a {@link IStorageData} removal to all clients + * @param data The {@link IStorageData} of the radar to remove + * @since 0.15.3.3 + */ + default void syncRemove(D data) { + CompoundTag tag = new CompoundTag(); + tag.putString("operation", "remove"); + tag.put("data", data.serializeToNBT()); + + PMWNetworking.serverSendStorageToAll(tag, this::packet); + } + + /** + * Syncs multiple {@link IStorageData} removals to all clients + * @param datum A {@link Collection} of {@link IStorageData} to sync + * @since 0.15.3.3 */ - default void syncRemove(Collection posList) { + default void syncRemoveByData(Collection datum) { CompoundTag tag = new CompoundTag(); tag.putString("operation", "remove"); tag.putBoolean("list", true); ListTag list = new ListTag(); - for (BlockPos pos: posList) { - list.add(NbtUtils.writeBlockPos(pos)); - } + datum.forEach(data -> list.remove(data.serializeToNBT())); tag.put("data", list); diff --git a/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java index 3e3fbf4..ebbc32f 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java @@ -6,7 +6,9 @@ import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.client.data.IClientStorage; -import net.nullved.pmweatherapi.radar.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.Collection; import java.util.Set; @@ -25,26 +27,28 @@ * For method definitions, see {@link PMWStorage} * * @see PMWStorage - * @since 0.15.1.1 + * @since 0.15.3.3 */ -public interface IStorage { +public interface IStorage { Level getLevel(); ResourceLocation getId(); int version(); void clean(); - Set getAll(); - Set getAllWithinRange(BlockPos base, double radius); - Set getInChunk(ChunkPos pos); - Set getInAdjacentChunks(ChunkPos pos); + Set getAll(); + Set getAllWithinRange(BlockPos base, double radius); + Set getInChunk(ChunkPos pos); + Set getInAdjacentChunks(ChunkPos pos); boolean shouldRecalculate(ChunkPos pos); - void add(BlockPos pos); - void add(Collection pos); + void add(D data); + void add(Collection datum); + void remove(D data); + void removeByData(Collection datum); void remove(BlockPos pos); - void remove(Collection pos); + void removeByPos(Collection pos); CompoundTag save(CompoundTag tag); void read(); diff --git a/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java index ac3f453..0cbbb90 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java @@ -1,27 +1,69 @@ package net.nullved.pmweatherapi.storage; import net.minecraft.core.BlockPos; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.Collection; -public interface ISyncServerStorage extends IServerStorage { - default void addAndSync(BlockPos pos) { - this.add(pos); - this.syncAdd(pos); +public interface ISyncServerStorage extends IServerStorage { + /** + * Shorthand for calling {@link #add(IStorageData)} and {@link #syncAdd(IStorageData)} + * @param data The {@link IStorageData} to add and sync + * @since 0.15.3.3 + */ + default void addAndSync(D data) { + this.add(data); + this.syncAdd(data); } - default void addAndSync(Collection pos) { - this.add(pos); - this.syncAdd(pos); + /** + * Shorthand for calling {@link #add(Collection)} and {@link #syncAdd(Collection)} + * @param datum The {@link Collection} of {@link IStorageData} to add and sync + * @since 0.15.3.3 + */ + default void addAndSync(Collection datum) { + this.add(datum); + this.syncAdd(datum); } + /** + * Shorthand for calling {@link #remove(BlockPos)} and {@link #remove(BlockPos)} + * @param pos The {@link BlockPos} to remove and sync + * @since 0.15.3.3 + */ default void removeAndSync(BlockPos pos) { this.remove(pos); this.syncRemove(pos); } - default void removeAndSync(Collection pos) { - this.remove(pos); - this.syncRemove(pos); + /** + * Shorthand for calling {@link #removeByPos(Collection)} and {@link #syncRemoveByPos(Collection)} + * @param pos The {@link Collection} of {@link BlockPos} to remove and sync + * @since 0.15.3.3 + */ + default void removeAndSyncByPos(Collection pos) { + this.removeByPos(pos); + this.syncRemoveByPos(pos); + } + + /** + * Shorthand for calling {@link #remove(IStorageData)} and {@link #syncRemove(IStorageData)} + * @param data The {@link IStorageData} to remove and sync + * @since 0.15.3.3 + */ + default void removeAndSync(D data) { + this.remove(data); + this.syncRemove(data); + } + + /** + * Shorthand for calling {@link #removeByData(Collection)} and {@link #syncRemoveByData(Collection)} + * @param datum The {@link Collection} of {@link IStorageData} to remove and sync + * @since 0.15.3.3 + */ + default void removeAndSyncByData(Collection datum) { + this.removeByData(datum); + this.syncRemoveByData(datum); } } diff --git a/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java b/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java index ad26dcd..6a2062f 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java @@ -15,69 +15,77 @@ import net.neoforged.neoforge.event.level.ChunkWatchEvent; import net.nullved.pmweatherapi.PMWeatherAPI; import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.client.data.PMWClientStorages; import net.nullved.pmweatherapi.client.radar.RadarClientStorage; import net.nullved.pmweatherapi.data.PMWStorageSavedData; import net.nullved.pmweatherapi.data.PMWStorages; import net.nullved.pmweatherapi.event.PMWEvents; -import net.nullved.pmweatherapi.radar.RadarServerStorage; -import net.nullved.pmweatherapi.radar.RadarStorage; +import net.nullved.pmweatherapi.radar.storage.RadarServerStorage; +import net.nullved.pmweatherapi.radar.storage.RadarStorage; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; /** - * The basic {@link IStorage} implementation that should cover most, if not all, use-cases. + * A basic {@link IStorage} implementation that should cover most, if not all, use-cases. *

- * Using this class automatically adds your storage to {@link PMWStorages}, - * which allows for easy retrieval using {@link PMWStorages#get(ResourceLocation)} + * A "Storage" saves and maintains a list of {@link IStorageData} on the {@link Level} that can be reloaded on world load. + * It does this by separating each {@link IStorageData} by chunk (more specifically, by {@link ChunkPos}) *

- * A "Storage" saves and maintains a list of {@link BlockPos} to the {@link Level} that can be reloaded on world load. - * It does this by separating each {@link BlockPos} by chunk (more specifically, by {@link ChunkPos}) - *

- * Any {@link BlockPos} can be saved, regardless of the type of {@link Block}. - * The use case for {@link RadarStorage} specifically is to store the positions of {@link RadarBlock}s in the world + * Any {@link IStorageData} can be saved, regardless of type, however, both server and clients must expect the same data structure. + * For example, {@link RadarStorage} is meant to store the positions of {@link RadarBlock}s in the world *

* {@link PMWStorage} does not handle syncing radars from the server to the client, instead, - * implement {@link IServerStorage} on a Server Storage and {@link IClientStorage} on a Client Storage + * implement {@link ISyncServerStorage} on a Server Storage and {@link IClientStorage} on a Client Storage. + * To sync using your own method, implement {@link IServerStorage} on the Server Storage instead. + *

+ * For your storage to be saved, you must first register it using {@link PMWStorages#registerStorage(ResourceLocation, Class, Function)} on both sides, + * and {@link PMWClientStorages#registerStorage(ResourceLocation, Class, Function)} on the client-side only *

* For a full implementation example, see {@link RadarStorage}, {@link RadarServerStorage}, and {@link RadarClientStorage} * * @see IStorage * @see IServerStorage * @see IClientStorage - * @since 0.15.1.1 + * @since 0.15.3.3 */ -public abstract class PMWStorage implements IStorage { +public abstract class PMWStorage implements IStorage { + /** - * A {@link Set} of {@link BlockPos} split up by {@link ChunkPos} - * @since 0.15.1.1 + * A {@link Set} of {@link IStorageData} split up by {@link ChunkPos} + * @since 0.15.3.3 */ - private Map> positions = new HashMap<>(); + private final Map> data = new HashMap<>(); /** * The times each {@link ChunkPos} was last checked - * @since 0.15.1.1 + * @since 0.15.3.3 */ - private Map checkTimes = new HashMap<>(); + private final Map checkTimes = new HashMap<>(); /** * The dimension to store {@link BlockPos} for - * @since 0.15.1.1 + * @since 0.15.3.3 */ - private ResourceKey dimension; + private final ResourceKey dimension; @Override public void clean() { - positions.clear(); + data.clear(); checkTimes.clear(); } + public abstract ResourceLocation getExpectedDataType(); + /** * Gets the level associated with this {@link IStorage}. * For the client side, it returns the {@link ClientLevel}. * For the server side, it returns a {@link ServerLevel}. * * @return A {@link Level} instance - * @since 0.15.1.1 + * @since 0.15.3.3 */ public abstract Level getLevel(); @@ -86,7 +94,7 @@ public void clean() { * Used primarily for saving to the file at {@code data/_.dat}. * * @return A {@link ResourceLocation} - * @since 0.15.1.1 + * @since 0.15.3.3 */ public abstract ResourceLocation getId(); @@ -95,7 +103,7 @@ public void clean() { * To disable version data from being saved, return {@code -1} * * @return The version of the saved data - * @since 0.15.1.1 + * @since 0.15.3.3 */ public abstract int version(); @@ -103,32 +111,32 @@ public void clean() { * The base constructor * * @param dimension The dimension of the {@link IStorage} - * @since 0.15.1.1 + * @since 0.15.3.3 */ public PMWStorage(ResourceKey dimension) { this.dimension = dimension; } /** - * Gets a {@link Set} of every {@link BlockPos} saved in this {@link IStorage}, regardless of {@link ChunkPos} + * Gets a {@link Set} of every {@link IStorageData} saved in this {@link IStorage}, regardless of {@link ChunkPos} * - * @return Every saved {@link BlockPos} - * @since 0.15.1.1 + * @return Every saved {@link IStorageData} + * @since 0.15.3.3 */ - public Set getAll() { - return positions.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); + public Set getAll() { + return data.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); } @Override - public Set getAllWithinRange(BlockPos base, double radius) { + public Set getAllWithinRange(BlockPos base, double radius) { int chunks = (int) Math.ceil(radius / 16.0F) + 1; ChunkPos cpos = new ChunkPos(base); - HashSet set = new HashSet<>(); + HashSet set = new HashSet<>(); for (int x = -chunks; x <= chunks; x++) { for (int z = -chunks; z <= chunks; z++) { - for (BlockPos candidate: getInChunk(new ChunkPos(cpos.x + x, cpos.z + z))) { - if (Math.abs(base.distToCenterSqr(candidate.getX(), candidate.getY(), candidate.getZ())) <= radius * radius) set.add(candidate); + for (D candidate: getInChunk(new ChunkPos(cpos.x + x, cpos.z + z))) { + if (Math.abs(base.distToCenterSqr(candidate.getPos().getX(), candidate.getPos().getY(), candidate.getPos().getZ())) <= radius * radius) set.add(candidate); } } } @@ -137,19 +145,19 @@ public Set getAllWithinRange(BlockPos base, double radius) { } /** - * Gets the {@link Set} of {@link BlockPos} for this {@link ChunkPos} + * Gets the {@link Set} of {@link IStorageData} for this {@link ChunkPos} * * @param pos The {@link ChunkPos} to search - * @return A {@link Set} of the {@link BlockPos} in this chunk - * @since 0.15.1.1 + * @return A {@link Set} of the {@link IStorageData} in this chunk + * @since 0.15.3.3 */ - public Set getInChunk(ChunkPos pos) { - return positions.getOrDefault(pos, Set.of()); + public Set getInChunk(ChunkPos pos) { + return data.getOrDefault(pos, Set.of()); } @Override - public Set getInAdjacentChunks(ChunkPos pos) { - Set set = new HashSet<>(); + public Set getInAdjacentChunks(ChunkPos pos) { + Set set = new HashSet<>(); for (int x = -1; x <= 1; x++) { for (int z = -1; z <= 1; z++) { set.addAll(getInChunk(new ChunkPos(pos.x + x, pos.z + z))); @@ -164,7 +172,7 @@ public Set getInAdjacentChunks(ChunkPos pos) { * * @param pos The {@link ChunkPos} to check * @return Whether the data should be recalculated or not - * @since 0.15.1.1 + * @since 0.15.3.3 */ public boolean shouldRecalculate(ChunkPos pos) { if (!checkTimes.containsKey(pos)) { @@ -176,95 +184,157 @@ public boolean shouldRecalculate(ChunkPos pos) { } /** - * Adds a single {@link BlockPos} to the {@link IStorage} + * Adds a single {@link IStorageData} to the {@link IStorage} * - * @param pos The new {@link BlockPos} - * @since 0.15.1.1 + * @param addData The new {@link IStorageData} + * @since 0.15.3.3 */ - public void add(BlockPos pos) { - ChunkPos chunkPos = new ChunkPos(pos); - Set set = positions.computeIfAbsent(chunkPos, c -> new HashSet<>()); - set.add(pos); - positions.put(chunkPos, set); + public void add(D addData) { + ChunkPos chunkPos = new ChunkPos(addData.getPos()); + Set set = data.computeIfAbsent(chunkPos, c -> new HashSet<>()); + + Set exist = set.stream().map(IStorageData::getPos).filter(c -> c.equals(addData.getPos())).collect(Collectors.toSet()); + if (!exist.isEmpty()) { + removeByPos(exist); + } + + set.add(addData); + data.put(chunkPos, set); } /** - * Adds multiple new {@link BlockPos} to the {@link IStorage} + * Adds multiple new {@link IStorageData} to the {@link IStorage} * - * @param pos A {@link Collection} of new {@link BlockPos} - * @since 0.15.1.1 + * @param datum A {@link Collection} of new {@link IStorageData} + * @since 0.15.3.3 */ - public void add(Collection pos) { - pos.forEach(this::add); + public void add(Collection datum) { + datum.forEach(this::add); } /** * Removes a single {@link BlockPos} from the {@link IStorage} * * @param pos The {@link BlockPos} to remove - * @since 0.15.1.1 + * @since 0.15.3.3 */ public void remove(BlockPos pos) { ChunkPos chunkPos = new ChunkPos(pos); - Set set = positions.computeIfAbsent(chunkPos, c -> new HashSet<>()); - set.remove(pos); - positions.put(chunkPos, set); + Set set = data.computeIfAbsent(chunkPos, c -> new HashSet<>()); + + Set exist = set.stream().filter(c -> c.getPos().equals(pos)).collect(Collectors.toSet()); + if (!exist.isEmpty()) { + exist.forEach(set::remove); + } + + data.put(chunkPos, set); } /** * Removes multiple {@link BlockPos} from the {@link IStorage} * * @param pos A {@link Collection} of {@link BlockPos} to remove - * @since 0.15.1.1 + * @since 0.15.3.3 */ - public void remove(Collection pos) { + public void removeByPos(Collection pos) { pos.forEach(this::remove); } + /** + * Removes a single {@link IStorageData} from the {@link IStorage} + * + * @param removedData The {@link IStorageData} to remove + * @since 0.15.3.3 + */ + public void remove(D removedData) { + ChunkPos chunkPos = new ChunkPos(removedData.getPos()); + Set set = data.computeIfAbsent(chunkPos, c -> new HashSet<>()); + set.remove(removedData); + data.put(chunkPos, set); + } + + /** + * Removes multiple {@link IStorageData} from the {@link IStorage} + * + * @param datum A {@link Collection} of {@link IStorageData} to remove + * @since 0.15.3.3 + */ + public void removeByData(Collection datum) { + datum.forEach(this::remove); + } + /** * Saves the data of this {@link IStorage} to a {@link CompoundTag} * * @param tag The pre-existing {@link CompoundTag} * @return A {@link CompoundTag} with storage data - * @since 0.15.1.1 + * @since 0.15.3.3 */ public CompoundTag save(CompoundTag tag) { PMWeatherAPI.LOGGER.info("Saving storage {} to level...", getId()); if (version() != -1) tag.putInt("version", version()); tag.putLong("saveTime", System.currentTimeMillis()); - for (Map.Entry> entry : positions.entrySet()) { - ListTag blockList = new ListTag(); - entry.getValue().forEach(blockPos -> blockList.add(NbtUtils.writeBlockPos(blockPos))); - tag.put(String.valueOf(entry.getKey().toLong()), blockList); + final ResourceLocation[] type = {null}; + for (Map.Entry> entry : data.entrySet()) { + ListTag list = new ListTag(); + entry.getValue().forEach(storageData -> { + CompoundTag ctag = storageData.serializeToNBT(); + if (type[0] == null) { + type[0] = ResourceLocation.parse(ctag.getString("type")); + } + ctag.remove("type"); + list.add(ctag); + }); + tag.put(String.valueOf(entry.getKey().toLong()), list); } + if (type[0] != null) tag.putString("type", type[0].toString()); + PMWeatherAPI.LOGGER.info("Saved storage {} to level", getId()); return tag; } /** * Reads the saved data from the {@link Level} and initializes this {@link IStorage} with the data - * @since 0.15.1.1 + * @since 0.15.3.3 */ public void read() { PMWStorageSavedData savedData = ((ServerLevel) this.getLevel()).getDataStorage().computeIfAbsent(PMWStorageSavedData.factory(), getId().toString().replace(":", "_")); savedData.setStorage(this); PMWeatherAPI.LOGGER.info("Reading storage {} from level...", getId()); CompoundTag data = savedData.getTag(); + String type = this.getExpectedDataType().toString(); + int version = data.getInt("version"); Set chunks = data.getAllKeys(); - chunks.removeAll(Set.of("version", "saveTime")); + chunks.removeAll(Set.of("version", "saveTime", "type")); for (String chunk : chunks) { - Set radars = new HashSet<>(); + Set blocks = new HashSet<>(); + ListTag list = (ListTag) data.get(chunk); + for (int i = 0; i < list.size(); i++) { + try { + if (!list.get(i).getType().equals(CompoundTag.TYPE)) { + // not a compound + CompoundTag ctag = new CompoundTag(); + ctag.put("blockpos", list.get(i)); - ListTag blockList = (ListTag) data.get(chunk); - for (int i = 0; i < blockList.size(); i++) { - int[] bp = blockList.getIntArray(i); - radars.add(new BlockPos(bp[0], bp[1], bp[2])); + if (NbtUtils.readBlockPos(ctag, "blockpos").isPresent()) { + blocks.add(StorageData.deserializeFromNBT(ctag, version)); + } else { + PMWeatherAPI.LOGGER.error("Could not deserialize tag {}! No type data and not a blockpos!", NbtUtils.toPrettyComponent(ctag.get("blockpos"))); + } + } else { + CompoundTag ctag = list.getCompound(i); + if (!type.isEmpty()) ctag.putString("type", type); + blocks.add(StorageData.deserializeFromNBT(ctag, version)); + } + } catch (ClassCastException e) { + PMWeatherAPI.LOGGER.warn("Invalid data entry in storage {} at chunk {}: {}", getId(), chunk, e.getMessage()); + } } - this.positions.put(new ChunkPos(Long.parseLong(chunk)), radars); + this.data.put(new ChunkPos(Long.parseLong(chunk)), blocks); } } } \ No newline at end of file diff --git a/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java index c2b1bbf..a8f6ed4 100644 --- a/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java +++ b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java @@ -5,11 +5,32 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.data.PMWStorages; +import net.nullved.pmweatherapi.storage.data.IStorageData; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.*; import java.util.function.Function; -public class StorageInstance { +/** + * A Storage Instance for a given {@link IServerStorage} type. + *

+ * A Storage Instance holds all of the {@link IServerStorage} instances for each dimension. + *

+ * To get the {@link IServerStorage} for a specific dimension, use {@link #get(ResourceKey)}. + * If you are unsure the {@link IServerStorage} exists and have a {@link ServerLevel}, use {@link #getOrCreate(ServerLevel)}. + *

+ * To load a new dimension, pass the {@link ServerLevel} into {@link #load(ServerLevel)}. + * To remove a dimension, call {@link #remove(ResourceKey)} + *

+ * You should not create {@link StorageInstance}s yourself. + * Instead, get them from {@link PMWStorages#get} + * + * @param The {@link IStorageData} of the {@link IServerStorage} + * @param The {@link IServerStorage} + * @since 0.15.3.3 + */ +public class StorageInstance> { private final ResourceLocation id; private final Class clazz; private final Function creator; @@ -50,13 +71,17 @@ public S get(ResourceKey dimension) { } public S getOrCreate(ServerLevel level) { - return map.computeIfAbsent(level.dimension(), dim -> creator.apply(level)); + return map.computeIfAbsent(level.dimension(), dim -> { + S storage = creator.apply(level); + storage.read(); + return storage; + }); } - public Optional> cast(Class oclazz) { + public > Optional> cast(Class oclazz) { if (oclazz.isAssignableFrom(clazz)) { @SuppressWarnings("unchecked") - StorageInstance casted = new StorageInstance<>(id(), oclazz, sl -> (O) creator.apply(sl)); + StorageInstance casted = new StorageInstance<>(id(), oclazz, sl -> (O) creator.apply(sl)); HashMap, O> backingMap = casted.getBackingMap(); try { diff --git a/src/main/java/net/nullved/pmweatherapi/storage/data/BlockPosData.java b/src/main/java/net/nullved/pmweatherapi/storage/data/BlockPosData.java new file mode 100644 index 0000000..698036a --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/data/BlockPosData.java @@ -0,0 +1,29 @@ +package net.nullved.pmweatherapi.storage.data; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.PMWeatherAPI; + +/** + * A wrapper around {@link StorageData} to give it an ID + * + * @see StorageData + * @since 0.15.3.3 + */ +public class BlockPosData extends StorageData { + public static final ResourceLocation ID = PMWeatherAPI.rl("blockpos"); + + public BlockPosData(BlockPos pos) { + super(pos); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + public static BlockPosData deserializeFromNBT(CompoundTag tag, int version) { + return new BlockPosData(deserializeBlockPos(tag)); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/storage/data/IStorageData.java b/src/main/java/net/nullved/pmweatherapi/storage/data/IStorageData.java new file mode 100644 index 0000000..79ec60c --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/data/IStorageData.java @@ -0,0 +1,20 @@ +package net.nullved.pmweatherapi.storage.data; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; + +/** + * The interface defining Storage Data such as {@link RadarStorageData} + *

+ * For method definitions, see {@link StorageData} + * + * @see StorageData + * @since 0.15.3.3 + */ +public interface IStorageData { + ResourceLocation getId(); + BlockPos getPos(); + CompoundTag serializeToNBT(); +} diff --git a/src/main/java/net/nullved/pmweatherapi/storage/data/StorageData.java b/src/main/java/net/nullved/pmweatherapi/storage/data/StorageData.java new file mode 100644 index 0000000..5b1f8e8 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/data/StorageData.java @@ -0,0 +1,93 @@ +package net.nullved.pmweatherapi.storage.data; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.PMWeatherAPI; +import net.nullved.pmweatherapi.client.data.IClientStorage; +import net.nullved.pmweatherapi.metar.MetarStorageData; +import net.nullved.pmweatherapi.radar.storage.RadarStorageData; +import net.nullved.pmweatherapi.radar.storage.WSRStorageData; +import net.nullved.pmweatherapi.storage.IServerStorage; +import net.nullved.pmweatherapi.storage.IStorage; + +import java.util.Optional; +import java.util.function.BiFunction; + +/** + * A basic {@link IStorageData} implementation that stores and handles a {@link BlockPos} by default. + *

+ * By extending this class, you can add additional data that will be saved as part of the "Storages" system. + *

+ * To register your data for use, you must first register it with {@link StorageDataManager#register(ResourceLocation, BiFunction)} + *

+ * When serializing, you should get the {@link CompoundTag} from calling {@link StorageData#serializeToNBT()}. + * This makes sure that you have both the type and blockpos saved. + *

+ * When deserializing, you can call {@link #deserializeBlockPos(CompoundTag)} to automatically read the saved {@link BlockPos} + *

+ * For some example implementations, view {@link RadarStorageData}, {@link MetarStorageData}, and {@link WSRStorageData} + * + * @since 0.15.3.3 + * @see IStorageData + * @see StorageDataManager + * @see IStorage + * @see IServerStorage + * @see IClientStorage + */ +public abstract class StorageData implements IStorageData { + protected final BlockPos pos; + + public StorageData(BlockPos pos) { + this.pos = pos; + } + + /** + * Get the position saved in this data + * @return A {@link BlockPos} + * @since 0.15.3.3 + */ + @Override + public BlockPos getPos() { + return pos; + } + + /** + * Serialize this storage data to NBT + * @return A {@link CompoundTag} of the serialized data + * @since 0.15.3.3 + */ + @Override + public CompoundTag serializeToNBT() { + CompoundTag tag = new CompoundTag(); + tag.putString("type", getId().toString()); + tag.put("blockpos", NbtUtils.writeBlockPos(pos)); + return tag; + } + + /** + * Deserialize a {@link BlockPos} from a {@link CompoundTag} + * @param tag The {@link CompoundTag} to get the {@link BlockPos} from + * @return The found {@link BlockPos}, or {@code null} + * @since 0.15.3.3 + */ + public static BlockPos deserializeBlockPos(CompoundTag tag) { + Optional bp = NbtUtils.readBlockPos(tag, "blockpos"); + if (bp.isPresent()) return bp.get(); + else PMWeatherAPI.LOGGER.warn("Could not deserialize BlockPos! Tag: {}", NbtUtils.toPrettyComponent(tag)); + return null; + } + + /** + * Shorthand for {@link StorageDataManager#get(CompoundTag, int)} + * @param tag The {@link CompoundTag} + * @param version The version of the data + * @return An {@link IStorageData} instance + * @param The type of {@link IStorageData} + * @since 0.15.3.3 + */ + public static D deserializeFromNBT(CompoundTag tag, int version) { + return StorageDataManager.get(tag, version); + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/storage/data/StorageDataManager.java b/src/main/java/net/nullved/pmweatherapi/storage/data/StorageDataManager.java new file mode 100644 index 0000000..66edb93 --- /dev/null +++ b/src/main/java/net/nullved/pmweatherapi/storage/data/StorageDataManager.java @@ -0,0 +1,42 @@ +package net.nullved.pmweatherapi.storage.data; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.resources.ResourceLocation; +import net.nullved.pmweatherapi.PMWeatherAPI; + +import java.util.HashMap; +import java.util.Optional; +import java.util.function.BiFunction; + +/** + * A manager for {@link IStorageData}. + *

+ * To register a {@link IStorageData}, you must pass a {@link ResourceLocation} of the id, and the deserialization function + * + * @since 0.15.3.3 + * @see IStorageData + * @see StorageData + */ +public class StorageDataManager { + public static final HashMap> map = new HashMap<>(); + + public static void register(ResourceLocation id, BiFunction deserializer) { + map.put(id, deserializer); + } + + public static D get(CompoundTag tag, int version) { + try { + //PMWeatherAPI.LOGGER.info("Getting storage data for type {}", tag.getString("type")); + if (tag.getString("type").isEmpty()) { + Optional bp = NbtUtils.readBlockPos(tag, "blockpos"); + if (bp.isPresent()) return (D) new BlockPosData(bp.get()); + else throw new IllegalArgumentException("No type given and does not meet BlockPos criteria"); + } else return (D) map.get(ResourceLocation.parse(tag.getString("type"))).apply(tag, version); + } catch (Exception e) { + PMWeatherAPI.LOGGER.error("Could not deserialize tag {} of type {}: {}", NbtUtils.toPrettyComponent(tag), tag.get("type"), e.getMessage()); + return null; + } + } +} diff --git a/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java b/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java index cb6fae7..b2353af 100644 --- a/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java +++ b/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java @@ -6,6 +6,7 @@ import net.minecraft.world.level.Level; import net.nullved.pmweatherapi.radar.NearbyRadars; import net.nullved.pmweatherapi.storage.IStorage; +import net.nullved.pmweatherapi.storage.data.StorageData; import java.util.HashSet; import java.util.Set; @@ -52,12 +53,13 @@ public static Set testAround(BlockPos pos, Function * @param storage The {@link IStorage} to check in * @param pos The base {@link BlockPos} * @return A {@link Set} of {@link BlockPos} that are in the {@link IStorage} around the base {@link BlockPos} + * @since 0.15.3.3 */ - public static Set storageCornerAdjacent(IStorage storage, BlockPos pos) { - HashSet set = new HashSet<>(); + public static Set storageCornerAdjacent(IStorage storage, BlockPos pos) { + HashSet set = new HashSet<>(); - for (BlockPos bp: storage.getInAdjacentChunks(new ChunkPos(pos))) { - if (isCornerAdjacent(bp, pos)) set.add(bp); + for (D data: storage.getInAdjacentChunks(new ChunkPos(pos))) { + if (isCornerAdjacent(data.getPos(), pos)) set.add(data); } return set; @@ -90,7 +92,7 @@ public static boolean isRadarAdjacent(Level level, BlockPos pos) { * @param dim The dimension to search in * @param pos The {@link BlockPos} to look by * @return {@code true} if there is a radar adjacent to this block, {@code false} otherwise - * @since 0.15.1.1 + * @since 0.15.3.3 */ public static boolean isRadarCornerAdjacent(ResourceKey dim, BlockPos pos) { Set nearby = NearbyRadars.get(dim).radarsNearBlock(pos, 3); @@ -108,7 +110,7 @@ public static boolean isRadarCornerAdjacent(ResourceKey dim, BlockPos pos * @param level The {@link Level} to search in * @param pos The {@link BlockPos} to look by * @return {@code true} if there is a radar adjacent to this block, {@code false} otherwise - * @since 0.15.1.1 + * @since 0.15.3.3 */ public static boolean isRadarCornerAdjacent(Level level, BlockPos pos) { return isRadarCornerAdjacent(level.dimension(), pos); diff --git a/src/main/resources/pmweatherapi.mixins.json b/src/main/resources/pmweatherapi.mixins.json index f96d20e..35538f4 100644 --- a/src/main/resources/pmweatherapi.mixins.json +++ b/src/main/resources/pmweatherapi.mixins.json @@ -6,9 +6,9 @@ "refmap": "pmweatherapi.refmap.json", "mixins": [ "BlockBehaviourMixin", - "PacketNBTFromClientMixin", "RadarBlockMixin", - "StormMixin" + "StormMixin", + "WSR88DCoreMixin" ], "client": [ "RadarRendererMixin",