diff --git a/gradle.properties b/gradle.properties
index d7cd7bd..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.2
+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 290e612..acd7c1d 100644
--- a/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java
+++ b/src/main/java/net/nullved/pmweatherapi/PMWeatherAPI.java
@@ -12,13 +12,28 @@
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.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.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";
@@ -40,9 +55,14 @@ public PMWeatherAPI(IEventBus modEventBus, ModContainer modContainer) {
}
private void commonSetup(FMLCommonSetupEvent event) {
-// if (!ModList.get().isLoaded("pmweather")) {
-// throw new RuntimeException("ProtoManly's Weather not detected!");
-// }
+ StorageDataManager.register(BlockPosData.ID, BlockPosData::deserializeFromNBT);
+ StorageDataManager.register(RadarStorageData.ID, RadarStorageData::deserializeFromNBT);
+ StorageDataManager.register(MetarStorageData.ID, MetarStorageData::deserializeFromNBT);
+ StorageDataManager.register(WSRStorageData.ID, WSRStorageData::deserializeFromNBT);
+
+ 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) {
@@ -50,10 +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);
}
-
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
new file mode 100644
index 0000000..0adad50
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/client/data/IClientStorage.java
@@ -0,0 +1,107 @@
+package net.nullved.pmweatherapi.client.data;
+
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.core.BlockPos;
+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.3.3
+ */
+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);
+ }
+
+ /**
+ * Syncs data from a {@link S2CStoragePacket} with operation {@code add} into this storage's memory
+ * @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_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 {
+ 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
+ * @param tag The {@link CompoundTag} of the data
+ * @since 0.15.3.3
+ */
+ default void syncRemove(CompoundTag tag) {
+ 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 {
+ 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 28f3378..f2ad8dd 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,32 @@
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.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.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;
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 +40,122 @@ 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<>();
+
+ private static ClientLevel lastLevel;
/**
- * Resets this client's internal radar storage
+ * Gets the {@link ClientStorageInstance} of the {@link RadarClientStorage}
+ * @return The {@link ClientStorageInstance}
* @since 0.14.15.3
*/
- public static void resetRadars() {
- radar = null;
+ 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
+ * Gets the {@link ClientStorageInstance} of the {@link MetarClientStorage}
+ * @return The {@link ClientStorageInstance}
+ * @since 0.15.3.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 metars() {
+ return get(MetarStorage.ID, MetarClientStorage.class).orElseThrow();
+ }
- return radar;
+ /**
+ * 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();
}
/**
- * 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
+ * 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 void init(Level level) {
- lastLevel = level;
- if (level != null) {
- radar = new RadarClientStorage(level.dimension());
+ 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) {
+ loadDimension(curLevel);
}
+
+ ClientStorageInstance, ?> csi = STORAGE_INSTANCES.get(location);
+ if (csi.get() == null) {
+ csi.load(curLevel);
+ }
+
+ return csi;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Gets all {@link ClientStorageInstance}s
+ * @return A {@link Collection} of all {@link ClientStorageInstance}s
+ * @since 0.15.3.3
+ */
+ public static Collection extends ClientStorageInstance, ?>> getAll() {
+ return STORAGE_INSTANCES.values();
+ }
+
+ /**
+ * Resets all data for all {@link ClientStorageInstance}s
+ * @since 0.15.3.3
+ */
+ public static void resetAll() {
+ getAll().forEach(ClientStorageInstance::clear);
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * 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 b06f7d8..9136a23 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,38 @@
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.resources.ResourceKey;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.world.level.Level;
+import net.minecraft.client.multiplayer.ClientLevel;
+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;
+import net.nullved.pmweatherapi.radar.storage.RadarStorage;
+import net.nullved.pmweatherapi.radar.storage.RadarStorageData;
/**
- * 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#radars()}
+ * @since 0.15.3.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
+ * Get a radar storage from {@link PMWClientStorages#radars()}
+ * @param clientLevel The {@link ClientLevel} to create this storage for
+ * @since 0.15.3.3
*/
- public RadarClientStorage(ResourceKey dimension) {
- super(dimension);
+ public RadarClientStorage(ClientLevel clientLevel) {
+ super(clientLevel.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.15.3.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/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
new file mode 100644
index 0000000..16c9513
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/client/storage/ClientStorageInstance.java
@@ -0,0 +1,71 @@
+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 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;
+
+/**
+ * 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;
+ 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/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..b37d257
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/command/StoragesCommand.java
@@ -0,0 +1,158 @@
+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.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;
+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("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(1, 2048))
+ .executes(StoragesCommand::serverAll))
+ .executes(StoragesCommand::serverAll))
+ .then(Commands.literal("adjacentChunks")
+ .executes(StoragesCommand::serverAdjacentChunks))
+ .executes(StoragesCommand::serverAll)
+ )
+ );
+ }
+
+ private static int clientAll(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 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 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 execClient(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();
+ IClientStorage stg = (IClientStorage) PMWClientStorages.get(storage).get();
+
+ 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(" 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(), 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;
+ }
+}
diff --git a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java
index 2c1daa3..d9ff6fe 100644
--- a/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java
+++ b/src/main/java/net/nullved/pmweatherapi/data/PMWSavedData.java
@@ -3,12 +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.3.3 | For removal in 0.16.0.0 | Using new Storages system
*/
+@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
new file mode 100644
index 0000000..fc63946
--- /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.15.3.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.15.3.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.15.3.3
+ */
+ public void setStorage(IStorage> storage) {
+ this.storage = storage;
+ }
+
+ /**
+ * Gets the {@link SavedData} from the {@link Level}
+ * @return A data {@link CompoundTag}
+ * @since 0.15.3.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..402a04c 100644
--- a/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java
+++ b/src/main/java/net/nullved/pmweatherapi/data/PMWStorages.java
@@ -1,39 +1,151 @@
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.radar.RadarServerStorage;
-import net.nullved.pmweatherapi.radar.RadarStorage;
-import net.nullved.pmweatherapi.util.StringProperty;
+import net.nullved.pmweatherapi.PMWeatherAPI;
+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;
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 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 get(RadarStorage.ID, RadarServerStorage.class).orElseThrow();
}
/**
- * 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
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 extends StorageInstance, ?>> getAll() {
+ return STORAGE_INSTANCES.values();
+ }
+
+ /**
+ * 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 extends IServerStorage>> getForDimension(ResourceKey dimension) {
+ return getAll().stream().map(si -> si.get(dimension)).toList();
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * 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 RadarServerStorage getRadar(Level dim) {
- return getRadar(dim.dimension());
+ 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 3d51687..40a2891 100644
--- a/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java
+++ b/src/main/java/net/nullved/pmweatherapi/event/PMWEvents.java
@@ -1,63 +1,126 @@
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.NearbyRadarsCommand;
+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) {
- 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) {
+ PMWeatherAPI.LOGGER.debug("Syncing stoage {}", isss.getId().toString());
+ isss.syncAllToPlayer(event.getEntity());
+ }
+ }
+ });
+
+ PMWeatherAPI.LOGGER.info("Synced all sync-storages to joined player {}", event.getEntity().getDisplayName().getString());
}
@SubscribeEvent
- public static void onChunkLoadEvent(ChunkWatchEvent.Sent event) {
- RadarServerStorage radarStorage = PMWStorages.getRadar(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 onPlayerChangeDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
+ PMWStorages.getForDimension(event.getTo()).forEach(iss -> {
+ 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 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.addRadars(radars);
- radarStorage.syncAdd(radars);
+ MetarStorageData newMsd = new MetarStorageData(pos, temp, dew, (float) windAngle, (float) windspeed, riskV);
+ updated.add(newMsd);
+ });
+ entry.getValue().addAndSync(updated);
+ });
}
}
@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.loadDimension(slevel);
+ PMWeatherAPI.LOGGER.info("Loaded storages for dimension {}", slevel.dimension().location());
}
}
@@ -65,7 +128,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.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 36a70e4..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,28 +34,37 @@ 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));
}
}
@Override
public String getModID() {
- return PMWeatherAPI.MODID + "_test";
+ 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 extends IClientStorage> 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 dec4c09..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;
@@ -17,18 +32,43 @@ 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(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)));
}
}
@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);
+ } 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 ba62f7b..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.getRadar(player.level()).addRadar(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 74c5670..867461b 100644
--- a/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java
+++ b/src/main/java/net/nullved/pmweatherapi/network/PMWNetworking.java
@@ -1,34 +1,33 @@
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;
/**
* 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
* @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);
}
/**
@@ -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 super RegistryFriendlyByteBuf, T> codec, BiConsumer handler, Object... args) {
+ public static void registerClientboundPacket(CustomPacketPayload.Type type, StreamCodec super RegistryFriendlyByteBuf, T> 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
- * @param tag The tag to send, generated by the {@link net.nullved.pmweatherapi.radar.RadarServerStorage}
- * @since 0.14.15.3
+ * 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 RadarServerStorage}
+ * @since 0.15.3.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.15.3.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/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 extends CustomPacketPayload> type() {
+ return TYPE;
+ }
+}
diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarPacket.java
new file mode 100644
index 0000000..e65ddb6
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarPacket.java
@@ -0,0 +1,43 @@
+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.radar.RadarClientStorage;
+
+/**
+ * The packet that syncs radars from the server to the client, using the Storages system
+ * @since 0.15.3.3
+ */
+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 S2CRadarPacket}
+ * @param tag The {@link CompoundTag} to send with the packet
+ * @since 0.15.3.3
+ */
+ public S2CRadarPacket(CompoundTag tag) {
+ super(tag);
+ }
+
+ /**
+ * Gets the {@link RadarClientStorage} that is receiving data
+ * @return The {@link RadarClientStorage}
+ * @since 0.15.3.3
+ */
+ @Override
+ public RadarClientStorage getStorage() {
+ return PMWClientStorages.radars().get();
+ }
+
+ @Override
+ public Type extends CustomPacketPayload> type() {
+ return TYPE;
+ }
+}
diff --git a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java b/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java
deleted file mode 100644
index d0a4d34..0000000
--- a/src/main/java/net/nullved/pmweatherapi/network/S2CRadarsPacket.java
+++ /dev/null
@@ -1,72 +0,0 @@
-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;
-
-/**
- * A packet for sending radar information from Server -> Client (S2C)
- * @param tag The {@link CompoundTag} to send
- * @since 0.14.15.3
- */
-public record S2CRadarsPacket(CompoundTag tag) implements CustomPacketPayload {
- public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.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
- */
- 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.15.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);
- }
- }
-
- /**
- * Gets the packet type
- * @return The packet type
- * @since 0.14.15.3
- */
- @Override
- public Type extends CustomPacketPayload> 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..2ddc217
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/network/S2CStoragePacket.java
@@ -0,0 +1,81 @@
+package net.nullved.pmweatherapi.network;
+
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.RegistryFriendlyByteBuf;
+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;
+
+/**
+ * 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.3.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.15.3.3
+ */
+ public abstract C 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.15.3.3
+ */
+ public S2CStoragePacket(RegistryFriendlyByteBuf buf) {
+ this(buf.readNbt());
+ }
+
+ /**
+ * Gets the {@link CompoundTag} send with the packet
+ * @return The packet data
+ * @since 0.15.3.3
+ */
+ public CompoundTag tag() {
+ return tag;
+ }
+
+ /**
+ * Writes the data into the given {@link FriendlyByteBuf}
+ * @param buf The {@link FriendlyByteBuf} to write into
+ * @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.3.3
+ */
+ public void handle(Player player) {
+ try {
+ String operation = tag.getString("operation");
+ C 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/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 extends CustomPacketPayload> 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 91886c1..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;
@@ -40,7 +43,7 @@ private NearbyRadars(RadarStorage storage) {
*/
@OnlyIn(Dist.CLIENT)
public static NearbyRadars client() {
- return new NearbyRadars(PMWClientStorages.getRadars());
+ return new NearbyRadars(PMWClientStorages.radars().get());
}
/**
@@ -50,7 +53,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 +76,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 (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.getAllRadars()) {
- if (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.getAllRadars()) {
- if (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/RadarServerStorage.java
deleted file mode 100644
index 58d486a..0000000
--- a/src/main/java/net/nullved/pmweatherapi/radar/RadarServerStorage.java
+++ /dev/null
@@ -1,131 +0,0 @@
-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 net.minecraft.server.level.ServerLevel;
-import net.minecraft.world.entity.player.Player;
-import net.nullved.pmweatherapi.network.PMWNetworking;
-import net.nullved.pmweatherapi.data.PMWStorages;
-
-import java.util.Collection;
-
-/**
- * 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
- */
-public class RadarServerStorage extends RadarStorage {
- private final ServerLevel level;
-
- /**
- * DO NOT CALL THIS CONSTRUCTOR!!!
- *
- * Get a radar storage from {@link PMWStorages#getRadar}
- * @param level The level to create this storage for
- * @since 0.14.15.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);
- }
-}
diff --git a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java
deleted file mode 100644
index 8a439e6..0000000
--- a/src/main/java/net/nullved/pmweatherapi/radar/RadarStorage.java
+++ /dev/null
@@ -1,240 +0,0 @@
-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 net.minecraft.resources.ResourceKey;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.world.level.ChunkPos;
-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;
-
-/**
- * Saves all the radars to a file to be saved and loaded from
- */
-public abstract class RadarStorage {
- private Map> radars = new HashMap<>();
- private Map checkTimes = new HashMap<>();
- private ResourceKey dimension;
-
- public abstract Level getLevel();
-
- 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);
- }
-
- 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);
- }
-
- PMWeatherAPI.LOGGER.info("Saved radars to level");
- return tag;
- }
-
- 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);
- }
- }
-
-// 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/radar/storage/RadarServerStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarServerStorage.java
new file mode 100644
index 0000000..f94cfae
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarServerStorage.java
@@ -0,0 +1,49 @@
+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.S2CRadarPacket;
+import net.nullved.pmweatherapi.network.S2CStoragePacket;
+import net.nullved.pmweatherapi.storage.IServerStorage;
+import net.nullved.pmweatherapi.storage.ISyncServerStorage;
+
+/**
+ * {@link IServerStorage} for {@link RadarBlock}s
+ *
+ * You should not create a {@link RadarServerStorage}, instead, use {@link PMWStorages#radars()}
+ *
+ * @since 0.15.3.3
+ * @see RadarStorage
+ * @see RadarClientStorage
+ */
+public class RadarServerStorage extends RadarStorage implements ISyncServerStorage {
+ private final ServerLevel level;
+
+ /**
+ * DO NOT CALL THIS CONSTRUCTOR!!!
+ *
+ * Get a radar storage from {@link PMWStorages#radars()}
+ * @param level The level to create this storage for
+ * @since 0.15.3.3
+ */
+ public RadarServerStorage(ServerLevel level) {
+ super(level.dimension());
+ this.level = level;
+ }
+
+ @Override
+ public ServerLevel getLevel() {
+ return level;
+ }
+
+ @Override
+ public S2CStoragePacket extends IClientStorage> packet(CompoundTag tag) {
+ return new S2CRadarPacket(tag);
+ }
+}
diff --git a/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorage.java b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorage.java
new file mode 100644
index 0000000..f06fe02
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/radar/storage/RadarStorage.java
@@ -0,0 +1,45 @@
+package net.nullved.pmweatherapi.radar.storage;
+
+
+import dev.protomanly.pmweather.block.RadarBlock;
+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.RadarClientStorage;
+import net.nullved.pmweatherapi.storage.IStorage;
+import net.nullved.pmweatherapi.storage.PMWStorage;
+
+/**
+ * {@link PMWStorage} for {@link RadarBlock}s
+ *
+ * @since 0.15.3.3
+ * @see PMWStorage
+ * @see RadarServerStorage
+ * @see RadarClientStorage
+ */
+public abstract class RadarStorage extends PMWStorage {
+ public static final int VERSION = 2;
+ public static final ResourceLocation ID = PMWeatherAPI.rl("radars");
+
+ public RadarStorage(ResourceKey dimension) {
+ super(dimension);
+ }
+
+ public abstract Level getLevel();
+
+ @Override
+ public ResourceLocation getId() {
+ return ID;
+ }
+
+ @Override
+ public ResourceLocation getExpectedDataType() {
+ return RadarStorageData.ID;
+ }
+
+ @Override
+ public int version() {
+ 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 extends IClientStorage> 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
new file mode 100644
index 0000000..aeb89d6
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/storage/IServerStorage.java
@@ -0,0 +1,170 @@
+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 net.nullved.pmweatherapi.storage.data.IStorageData;
+import net.nullved.pmweatherapi.storage.data.StorageData;
+
+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.15.3.3
+ */
+public interface IServerStorage extends IStorage {
+ /**
+ * Gets the level associated with this {@link IServerStorage}
+ * @return A {@link ServerLevel}
+ * @since 0.15.3.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.15.3.3
+ */
+ S2CStoragePacket extends IClientStorage> packet(CompoundTag tag);
+
+ /**
+ * 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.3.3
+ */
+ default void syncAllToPlayer(Player player) {
+ 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.serverSendStorageToPlayer(tag, this::packet, player);
+ }
+
+ /**
+ * Syncs new {@link IStorageData} to all clients
+ * @param data The new {@link IStorageData}
+ * @since 0.15.3.3
+ */
+ default void syncAdd(D data) {
+ CompoundTag tag = new CompoundTag();
+ tag.putString("operation", "add");
+ tag.put("data", data.serializeToNBT());
+
+ PMWNetworking.serverSendStorageToAll(tag, this::packet);
+ }
+
+ /**
+ * 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 datum) {
+ CompoundTag tag = new CompoundTag();
+ tag.putString("operation", "add");
+ tag.putBoolean("list", true);
+
+ ListTag list = new ListTag();
+ datum.forEach(data -> list.add(data.serializeToNBT()));
+
+ 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.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);
+ }
+
+ /**
+ * Syncs multiple {@link BlockPos} removals to all clients
+ * @param posList A {@link Collection} of {@link BlockPos} to sync
+ * @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 syncRemoveByData(Collection datum) {
+ CompoundTag tag = new CompoundTag();
+ tag.putString("operation", "remove");
+ tag.putBoolean("list", true);
+
+ ListTag list = new ListTag();
+ datum.forEach(data -> list.remove(data.serializeToNBT()));
+
+ 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..ebbc32f
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/storage/IStorage.java
@@ -0,0 +1,55 @@
+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.storage.RadarStorage;
+import net.nullved.pmweatherapi.storage.data.IStorageData;
+import net.nullved.pmweatherapi.storage.data.StorageData;
+
+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.15.3.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(D data);
+ void add(Collection datum);
+ void remove(D data);
+ void removeByData(Collection datum);
+ void remove(BlockPos 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
new file mode 100644
index 0000000..0cbbb90
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/storage/ISyncServerStorage.java
@@ -0,0 +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 {
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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
new file mode 100644
index 0000000..6a2062f
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/storage/PMWStorage.java
@@ -0,0 +1,340 @@
+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.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.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;
+
+
+/**
+ * A basic {@link IStorage} implementation that should cover most, if not all, use-cases.
+ *
+ * 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})
+ *
+ * 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 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.3.3
+ */
+public abstract class PMWStorage implements IStorage {
+
+ /**
+ * A {@link Set} of {@link IStorageData} split up by {@link ChunkPos}
+ * @since 0.15.3.3
+ */
+ private final Map> data = new HashMap<>();
+ /**
+ * The times each {@link ChunkPos} was last checked
+ * @since 0.15.3.3
+ */
+ private final Map checkTimes = new HashMap<>();
+ /**
+ * The dimension to store {@link BlockPos} for
+ * @since 0.15.3.3
+ */
+ private final ResourceKey dimension;
+
+ @Override
+ public void clean() {
+ 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.3.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.15.3.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.15.3.3
+ */
+ public abstract int version();
+
+ /**
+ * The base constructor
+ *
+ * @param dimension The dimension of the {@link IStorage}
+ * @since 0.15.3.3
+ */
+ public PMWStorage(ResourceKey dimension) {
+ this.dimension = dimension;
+ }
+
+ /**
+ * Gets a {@link Set} of every {@link IStorageData} saved in this {@link IStorage}, regardless of {@link ChunkPos}
+ *
+ * @return Every saved {@link IStorageData}
+ * @since 0.15.3.3
+ */
+ public Set getAll() {
+ return data.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 (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);
+ }
+ }
+ }
+
+ return set;
+ }
+
+ /**
+ * 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 IStorageData} in this chunk
+ * @since 0.15.3.3
+ */
+ public Set getInChunk(ChunkPos pos) {
+ return data.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.15.3.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 IStorageData} to the {@link IStorage}
+ *
+ * @param addData The new {@link IStorageData}
+ * @since 0.15.3.3
+ */
+ 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 IStorageData} to the {@link IStorage}
+ *
+ * @param datum A {@link Collection} of new {@link IStorageData}
+ * @since 0.15.3.3
+ */
+ 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.3.3
+ */
+ public void remove(BlockPos pos) {
+ ChunkPos chunkPos = new ChunkPos(pos);
+ 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.3.3
+ */
+ 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.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());
+
+ 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.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", "type"));
+
+ for (String chunk : chunks) {
+ 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));
+
+ 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.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
new file mode 100644
index 0000000..a8f6ed4
--- /dev/null
+++ b/src/main/java/net/nullved/pmweatherapi/storage/StorageInstance.java
@@ -0,0 +1,110 @@
+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 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;
+
+/**
+ * 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;
+ private final HashMap, S> map = new HashMap<>();
+
+ public StorageInstance(ResourceLocation id, Class clazz, Function creator) {
+ this.id = id;
+ this.clazz = clazz;
+ 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 -> {
+ S storage = creator.apply(level);
+ storage.read();
+ return storage;
+ });
+ }
+
+ public > Optional> cast(Class oclazz) {
+ if (oclazz.isAssignableFrom(clazz)) {
+ @SuppressWarnings("unchecked")
+ 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(), oclazz.cast(entry.getValue()));
+ }
+
+ 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(ServerLevel 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/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/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 a287e83..b2353af 100644
--- a/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java
+++ b/src/main/java/net/nullved/pmweatherapi/util/PMWUtils.java
@@ -2,10 +2,69 @@
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 net.nullved.pmweatherapi.storage.data.StorageData;
+
+import java.util.HashSet;
+import java.util.Set;
+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());
+ int dz = Math.abs(a.getZ() - b.getZ());
+
+ 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<>();
+
+ 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;
+ }
+
+ /**
+ * 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}
+ * @since 0.15.3.3
+ */
+ public static Set storageCornerAdjacent(IStorage storage, BlockPos pos) {
+ HashSet set = new HashSet<>();
+
+ for (D data: storage.getInAdjacentChunks(new ChunkPos(pos))) {
+ if (isCornerAdjacent(data.getPos(), pos)) set.add(data);
+ }
+
+ return set;
+ }
+
/**
* Determines if a radar is next to the given {@link BlockPos}
* @param dim The dimension to search in
@@ -27,4 +86,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.15.3.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.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",