From 9a69d9ecd4b313c19dbb90084caa9ddb9bcf3a59 Mon Sep 17 00:00:00 2001 From: Brady McDonough Date: Sun, 4 Jan 2026 17:34:43 -0700 Subject: [PATCH 1/7] Added CustomShardTier logic, pre-test state --- .../java/io/wispforest/affinity/Affinity.java | 3 + .../affinity/misc/util/FuturesUtil.java | 11 ++ .../attunedshards/AttunedShardTier.java | 3 +- .../attunedshards/AttunedShardTierString.java | 44 ++++++++ .../attunedshards/AttunedShardTiers.java | 4 +- .../object/attunedshards/CustomShardTier.java | 85 ++++++++++++++ .../CustomShardTierJsonFile.java | 34 ++++++ .../CustomShardTierRegistry.java | 104 ++++++++++++++++++ .../data/affinity/tiers/default.json | 10 ++ 9 files changed, 293 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java create mode 100644 src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java create mode 100644 src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java create mode 100644 src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java create mode 100644 src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java create mode 100644 src/main/resources/data/affinity/tiers/default.json diff --git a/src/main/java/io/wispforest/affinity/Affinity.java b/src/main/java/io/wispforest/affinity/Affinity.java index 32588684..3e3a5c8a 100644 --- a/src/main/java/io/wispforest/affinity/Affinity.java +++ b/src/main/java/io/wispforest/affinity/Affinity.java @@ -13,6 +13,7 @@ import io.wispforest.affinity.endec.ReflectiveMinecraftEndecs; import io.wispforest.affinity.network.AffinityNetwork; import io.wispforest.affinity.object.*; +import io.wispforest.affinity.object.attunedshards.CustomShardTierRegistry; import io.wispforest.affinity.worldgen.AffinityStructures; import io.wispforest.affinity.worldgen.AffinityWorldgen; import io.wispforest.owo.Owo; @@ -119,6 +120,8 @@ public void onInitialize() { )); }); + CustomShardTierRegistry.initialize(); + if (!Owo.DEBUG) return; AffinityDebugCommands.register(); } diff --git a/src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java b/src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java new file mode 100644 index 00000000..9214ea83 --- /dev/null +++ b/src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java @@ -0,0 +1,11 @@ +package io.wispforest.affinity.misc.util; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class FuturesUtil { + + public static CompletableFuture allOf(List> futuresList) { + return CompletableFuture.allOf(futuresList.toArray(new CompletableFuture[futuresList.size()])); + } +} diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java index 62903382..86f355d1 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java @@ -50,8 +50,7 @@ default boolean isNone() { @NotNull static AttunedShardTier forItem(Item item) { if (item instanceof AttunedShardItem shardItem) return shardItem.tier(); - if (item == Items.AMETHYST_SHARD) return AttunedShardTiers.CRUDE; - return AttunedShardTiers.NONE; + return CustomShardTierRegistry.REGISTRY.getOrDefault(item, AttunedShardTiers.NONE); } } diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java new file mode 100644 index 00000000..f20a0362 --- /dev/null +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java @@ -0,0 +1,44 @@ +package io.wispforest.affinity.object.attunedshards; + +import io.wispforest.affinity.Affinity; + +import io.wispforest.endec.Endec; + +import java.util.Locale; + +public class AttunedShardTierString { + + public static Endec ENDEC = Endec.STRING.xmap(AttunedShardTierString::new, AttunedShardTierString::getTierName); + + private String tierName; + private AttunedShardTiers variant; + + public AttunedShardTierString(String tierName) { + this.variant = AttunedShardTiers.valueOf(tierName.toUpperCase(Locale.ROOT)); + this.tierName = tierName; + + if (AttunedShardTiers.NONE == this.variant) { + Affinity.LOGGER.info("Named tier could not be matched", tierName); + this.tierName = "NONE"; + } + } + + public AttunedShardTierString(AttunedShardTiers tier) { + String name = tier.name(); + if (!name.equals("NONE")) { + name.toLowerCase(Locale.ROOT); + } + + this.variant = tier; + this.tierName = name; + } + + public String getTierName() { + return this.tierName; + } + + public AttunedShardTiers getTierVariant() { + return this.variant; + } + +} diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java index 6369844d..def0d368 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java @@ -7,8 +7,6 @@ import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.screen.PlayerScreenHandler; -import java.util.Locale; - public enum AttunedShardTiers implements AttunedShardTier { NONE(0, 5), @@ -28,7 +26,7 @@ public enum AttunedShardTiers implements AttunedShardTier { this.maxTransfer = maxTransfer; this.maxDistance = maxDistance; - final var name = this.name().toLowerCase(Locale.ROOT); + final var name = this.name().toLowerCase(); this.translationKey = "shard_tier.affinity." + name; if (Affinity.onClient()) { diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java new file mode 100644 index 00000000..45d9f833 --- /dev/null +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java @@ -0,0 +1,85 @@ +package io.wispforest.affinity.object.attunedshards; + +import io.wispforest.affinity.Affinity; +import io.wispforest.endec.Endec; +import io.wispforest.endec.impl.StructEndecBuilder; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.screen.PlayerScreenHandler; + +public class CustomShardTier implements AttunedShardTier { + public static final Endec ENDEC = StructEndecBuilder.of( + Endec.LONG.fieldOf("maxTransfer", CustomShardTier::maxTransfer), + Endec.INT.fieldOf("maxDistance", CustomShardTier::maxDistance), + AttunedShardTierString.ENDEC.fieldOf("tierName", CustomShardTier::_getASTS), + CustomShardTier::new + ); + + private long maxTransfer; + private int maxDistance; + private AttunedShardTierString tierName; + private String translationKey; + + @Environment(EnvType.CLIENT) + private SpriteIdentifier sprite; + + private void constructHelper(long tf, int d, AttunedShardTierString tier) { + this.maxDistance = d; + this.maxTransfer = tf; + this.tierName = tier; + String tierName = tier.getTierName(); + this.translationKey = "shard_tier.affinity." + tierName; + + if (Affinity.onClient()) { + this.sprite = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, + Affinity.id("block/" + tierName + "_aethum_flux_node_shard")); + } + } + + public CustomShardTier(long tf, int d, AttunedShardTierString tier) { + this.constructHelper(tf, d, tier); + } + + public CustomShardTier(long tf, int d, AttunedShardTiers tier) { + AttunedShardTierString asts = new AttunedShardTierString(tier); + this.constructHelper(tf, d, asts); + } + + public CustomShardTier(long tf, int d, String tierName) { + AttunedShardTierString asts = new AttunedShardTierString(tierName); + this.constructHelper(tf, d, asts); + } + + @Override + public long maxTransfer() { + return this.maxTransfer; + } + + @Override + public int maxDistance() { + return this.maxDistance; + } + + public AttunedShardTierString _getASTS() { + return this.tierName; + } + + public String getTierName() { + return this.tierName.getTierName(); + } + + public AttunedShardTier getTier() { + return this.tierName.getTierVariant(); + } + + @Override + public String translationKey() { + return this.translationKey; + } + + @Environment(EnvType.CLIENT) + public SpriteIdentifier sprite() { + return this.sprite; + } +} diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java new file mode 100644 index 00000000..d4ad4afe --- /dev/null +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java @@ -0,0 +1,34 @@ +package io.wispforest.affinity.object.attunedshards; + +import io.wispforest.affinity.endec.BuiltInEndecs; +import io.wispforest.endec.Endec; +import io.wispforest.endec.impl.StructEndecBuilder; +import net.minecraft.item.Item; +import net.minecraft.registry.Registries; + +import java.util.List; + +public class CustomShardTierJsonFile { + + public static final Endec ENDEC = StructEndecBuilder.of( + BuiltInEndecs.ofRegistry(Registries.ITEM).listOf().fieldOf("items",CustomShardTierJsonFile::getItems), + CustomShardTier.ENDEC.fieldOf("tier", CustomShardTierJsonFile::getTier), + CustomShardTierJsonFile::new + ); + + private List items; + private CustomShardTier tier; + + public CustomShardTierJsonFile (List items, CustomShardTier tier) { + this.items = items; + this.tier = tier; + } + + public List getItems() { + return items; + } + + public CustomShardTier getTier() { + return tier; + } +} diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java new file mode 100644 index 00000000..5076c48b --- /dev/null +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java @@ -0,0 +1,104 @@ +package io.wispforest.affinity.object.attunedshards; + +import com.google.gson.*; +import io.wispforest.affinity.Affinity; +import io.wispforest.affinity.misc.util.FuturesUtil; +import io.wispforest.endec.format.gson.GsonDeserializer; +import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.minecraft.item.Item; + +import net.minecraft.item.Items; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ResourceType; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import java.util.List; +import java.util.HashMap; +import java.util.stream.Collectors; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.NoSuchElementException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +public class CustomShardTierRegistry { + + public static HashMap REGISTRY = new HashMap<>(); + + public static void initialize() { + ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new Loader()); + } + + private static void registerItems(List itemList, AttunedShardTier tier) { + REGISTRY.putAll(itemList.stream().collect(Collectors.toMap(item -> item, ign-> tier))); + } + + public static void registerItems(CustomShardTierJsonFile jsonFile) { + registerItems(jsonFile.getItems(), jsonFile.getTier()); + + } + + private static class Loader implements IdentifiableResourceReloadListener { + + @Override + public CompletableFuture reload(Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor) { + + CompletableFuture.supplyAsync(() -> { + CustomShardTierRegistry.REGISTRY.clear(); + return null; + }, prepareExecutor).thenCompose(synchronizer::whenPrepared); + + return FuturesUtil.allOf( manager.findAllResources( + "affinity/tiers", + path -> path.toString().endsWith(".json") + ).entrySet().parallelStream().map(ent -> + CompletableFuture.supplyAsync( + () -> { try (BufferedReader reader = manager.getResource(ent.getKey()).get().getReader()) { + return reader; + } catch (IOException | NoSuchElementException e) { + Affinity.LOGGER.error("Couldn't open resource described by {}", ent.getKey(), e); + } + return Reader.nullReader();}, + applyExecutor + ).thenAccept(reader -> { + JsonElement json = JsonParser.parseReader(reader); + if (json.isJsonNull()) { + return; + } + + try { + CustomShardTierJsonFile result = CustomShardTierJsonFile.ENDEC.decodeFully(GsonDeserializer::of, json); + CustomShardTierRegistry.registerItems(result); + } catch (JsonParseException e) { + Affinity.LOGGER.error("Parsing failed", e); + } + + try { + reader.close(); + } catch (IOException e) { + Affinity.LOGGER.error("Couldn't close file (?)", e); + }} + ).thenRun( () -> { + /* INVARIANTS */ + if (CustomShardTierRegistry.REGISTRY.isEmpty()) { + Affinity.LOGGER.error("Affinity Shard registry reloaded with no json entries."); + CustomShardTierRegistry.REGISTRY.put(Items.AMETHYST_SHARD, AttunedShardTiers.CRUDE); + } + }) + ).toList()); + } + + @Override + public String getName() { + return Affinity.MOD_ID; + } + + @Override + public Identifier getFabricId() { + return Affinity.id("tiers"); + } + } +} diff --git a/src/main/resources/data/affinity/tiers/default.json b/src/main/resources/data/affinity/tiers/default.json new file mode 100644 index 00000000..de766172 --- /dev/null +++ b/src/main/resources/data/affinity/tiers/default.json @@ -0,0 +1,10 @@ +{ + "items": [ + "minecraft:amethyst_shard" + ], + "tier": { + "maxDistance": 5, + "maxTransfer": 5, + "tierName": "CRUDE" + } +} \ No newline at end of file From 7532d3307df99dd2c51465416fa587ca6bea9e89 Mon Sep 17 00:00:00 2001 From: Brady McDonough Date: Sat, 10 Jan 2026 13:19:48 -0700 Subject: [PATCH 2/7] Replaced hardcoded constants with method hits. Base working state. --- .../impl/AethumFluxNodeBlockEntity.java | 55 +++++++-------- ...GravitonTransducerBlockEntityRenderer.java | 2 +- .../attunedshards/AttunedShardTier.java | 5 ++ .../attunedshards/AttunedShardTierString.java | 2 +- .../attunedshards/AttunedShardTiers.java | 4 ++ .../object/attunedshards/CustomShardTier.java | 12 ++-- .../CustomShardTierJsonFile.java | 2 +- .../CustomShardTierRegistry.java | 67 +++++++++---------- 8 files changed, 70 insertions(+), 79 deletions(-) diff --git a/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java b/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java index 2142b321..423840b0 100644 --- a/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java +++ b/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java @@ -15,6 +15,7 @@ import io.wispforest.affinity.misc.util.MathUtil; import io.wispforest.affinity.misc.util.NbtUtil; import io.wispforest.affinity.object.AffinityBlocks; +import io.wispforest.affinity.object.attunedshards.AttunedShardTier; import io.wispforest.affinity.object.attunedshards.AttunedShardTiers; import io.wispforest.owo.ops.ItemOps; import io.wispforest.owo.particles.ClientParticles; @@ -379,45 +380,35 @@ protected void writeNbt(NbtCompound nbt) { @Override public ActionResult onUse(PlayerEntity player, Hand hand, BlockHitResult hit) { var playerStack = player.getStackInHand(hand); + if (playerStack.isEmpty()) { + return ActionResult.PASS; + } - if (!playerStack.isEmpty()) { - - if (playerStack.isOf(Items.AMETHYST_SHARD)) { - if (this.shard.isEmpty()) { - this.shard = ItemOps.singleCopy(playerStack); - this.tier = AttunedShardTiers.CRUDE; - - ItemOps.decrementPlayerHandItem(player, hand); - - updatePropertyCache(); - this.markDirty(false); - - return ActionResult.SUCCESS; - } else if (this.isUpgradeable && this.outerShardCount < this.outerShards.size()) { - ListUtil.addItem(this.outerShards, ItemOps.singleCopy(playerStack)); - - ItemOps.decrementPlayerHandItem(player, hand); - - updatePropertyCache(); - this.markDirty(false); + var shardTier = AttunedShardTier.forItem(playerStack.getItem()); + if (shardTier.isNone()) { + return ActionResult.PASS; + } - return ActionResult.SUCCESS; - } - } else if (this.isUpgradeable() && this.shard.isEmpty() && playerStack.getItem() instanceof AttunedShardItem shardItem) { + if (this.shard.isEmpty()) { + if (shardTier.tier() == AttunedShardTiers.CRUDE || this.isUpgradeable) { this.shard = ItemOps.singleCopy(playerStack); - this.tier = shardItem.tier(); - - ItemOps.decrementPlayerHandItem(player, hand); - - updatePropertyCache(); - this.markDirty(false); - - return ActionResult.SUCCESS; + this.tier = shardTier; + } else { + return ActionResult.PASS; } + } else if (shardTier.tier() == AttunedShardTiers.CRUDE && this.isUpgradeable) { + ListUtil.addItem(this.outerShards, ItemOps.singleCopy(playerStack)); + } else { + return ActionResult.PASS; } - return ActionResult.PASS; + ItemOps.decrementPlayerHandItem(player, hand); + + updatePropertyCache(); + this.markDirty(false); + + return ActionResult.SUCCESS; } public ActionResult onAttack(PlayerEntity player) { diff --git a/src/main/java/io/wispforest/affinity/client/render/blockentity/GravitonTransducerBlockEntityRenderer.java b/src/main/java/io/wispforest/affinity/client/render/blockentity/GravitonTransducerBlockEntityRenderer.java index c160f127..27cbd2df 100644 --- a/src/main/java/io/wispforest/affinity/client/render/blockentity/GravitonTransducerBlockEntityRenderer.java +++ b/src/main/java/io/wispforest/affinity/client/render/blockentity/GravitonTransducerBlockEntityRenderer.java @@ -34,7 +34,7 @@ public GravitonTransducerBlockEntityRenderer(BlockEntityRendererFactory.Context @Override protected void render(GravitonTransducerBlockEntity entity, float tickDelta, float frameDelta, long time, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) { var shardTier = AttunedShardTier.forItem(entity.shard().getItem()); - if (shardTier == AttunedShardTiers.NONE) return; + if (shardTier.isNone()) return; matrices.push(); matrices.translate(.5, .5 + Math.sin(time / 1000f) * .1f, .5); diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java index 86f355d1..5844d12d 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java @@ -26,6 +26,11 @@ public interface AttunedShardTier { */ int maxDistance(); + /** + * @return The Tier variant of this shard + * */ + AttunedShardTiers tier(); + /** * @return The translation key of this tier, used in the * HUD when looking at a node with a shard of this tier diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java index f20a0362..91c95025 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java @@ -34,7 +34,7 @@ public AttunedShardTierString(AttunedShardTiers tier) { } public String getTierName() { - return this.tierName; + return this.tierName.toLowerCase(Locale.ROOT); } public AttunedShardTiers getTierVariant() { diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java index def0d368..2d1be364 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java @@ -45,6 +45,10 @@ public int maxDistance() { return this.maxDistance; } + public AttunedShardTiers tier() { + return this; + } + @Override public String translationKey() { return translationKey; diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java index 45d9f833..8fc4508a 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTier.java @@ -61,16 +61,12 @@ public int maxDistance() { return this.maxDistance; } - public AttunedShardTierString _getASTS() { - return this.tierName; - } - - public String getTierName() { - return this.tierName.getTierName(); + public AttunedShardTiers tier() { + return this.tierName.getTierVariant(); } - public AttunedShardTier getTier() { - return this.tierName.getTierVariant(); + public AttunedShardTierString _getASTS() { + return this.tierName; } @Override diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java index d4ad4afe..a660d1d9 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierJsonFile.java @@ -11,7 +11,7 @@ public class CustomShardTierJsonFile { public static final Endec ENDEC = StructEndecBuilder.of( - BuiltInEndecs.ofRegistry(Registries.ITEM).listOf().fieldOf("items",CustomShardTierJsonFile::getItems), + BuiltInEndecs.ofRegistry(Registries.ITEM).listOf().fieldOf("items", CustomShardTierJsonFile::getItems), CustomShardTier.ENDEC.fieldOf("tier", CustomShardTierJsonFile::getTier), CustomShardTierJsonFile::new ); diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java index 5076c48b..5261d57c 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java @@ -9,6 +9,7 @@ import net.minecraft.item.Item; import net.minecraft.item.Items; +import net.minecraft.registry.Registry; import net.minecraft.resource.ResourceManager; import net.minecraft.resource.ResourceType; import net.minecraft.util.Identifier; @@ -29,10 +30,12 @@ public class CustomShardTierRegistry { public static HashMap REGISTRY = new HashMap<>(); public static void initialize() { + Affinity.LOGGER.info("Affinity shard tier reload listener registered."); ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new Loader()); } private static void registerItems(List itemList, AttunedShardTier tier) { + Affinity.LOGGER.info("Affinity registering items under tier {}", tier.toString()); REGISTRY.putAll(itemList.stream().collect(Collectors.toMap(item -> item, ign-> tier))); } @@ -45,50 +48,42 @@ private static class Loader implements IdentifiableResourceReloadListener { @Override public CompletableFuture reload(Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor) { - CompletableFuture.supplyAsync(() -> { CustomShardTierRegistry.REGISTRY.clear(); return null; }, prepareExecutor).thenCompose(synchronizer::whenPrepared); - return FuturesUtil.allOf( manager.findAllResources( - "affinity/tiers", + return FuturesUtil.allOf( manager.findResources( + "tiers", path -> path.toString().endsWith(".json") ).entrySet().parallelStream().map(ent -> - CompletableFuture.supplyAsync( - () -> { try (BufferedReader reader = manager.getResource(ent.getKey()).get().getReader()) { - return reader; - } catch (IOException | NoSuchElementException e) { - Affinity.LOGGER.error("Couldn't open resource described by {}", ent.getKey(), e); - } - return Reader.nullReader();}, - applyExecutor - ).thenAccept(reader -> { - JsonElement json = JsonParser.parseReader(reader); - if (json.isJsonNull()) { - return; - } - - try { - CustomShardTierJsonFile result = CustomShardTierJsonFile.ENDEC.decodeFully(GsonDeserializer::of, json); - CustomShardTierRegistry.registerItems(result); - } catch (JsonParseException e) { - Affinity.LOGGER.error("Parsing failed", e); + CompletableFuture.runAsync( + () -> { + try (BufferedReader reader = ent.getValue().getReader()) { + try { + JsonElement json = JsonParser.parseReader(reader); + if (json.isJsonNull()) { + return; + } + + CustomShardTierJsonFile result = CustomShardTierJsonFile.ENDEC.decodeFully(GsonDeserializer::of, json); + CustomShardTierRegistry.registerItems(result); + + } catch (JsonParseException e) { + Affinity.LOGGER.error("Parsing failed", e); + } + } catch (IOException | NoSuchElementException e) { + Affinity.LOGGER.error("Couldn't open resource described by {}", ent.getKey(), e); + } + }, applyExecutor + )).toList() + ).thenRun( () -> { + /* INVARIANTS */ + if (CustomShardTierRegistry.REGISTRY.isEmpty()) { + Affinity.LOGGER.debug("Affinity Shard registry reloaded with no json entries."); + CustomShardTierRegistry.REGISTRY.put(Items.AMETHYST_SHARD, AttunedShardTiers.CRUDE); } - - try { - reader.close(); - } catch (IOException e) { - Affinity.LOGGER.error("Couldn't close file (?)", e); - }} - ).thenRun( () -> { - /* INVARIANTS */ - if (CustomShardTierRegistry.REGISTRY.isEmpty()) { - Affinity.LOGGER.error("Affinity Shard registry reloaded with no json entries."); - CustomShardTierRegistry.REGISTRY.put(Items.AMETHYST_SHARD, AttunedShardTiers.CRUDE); - } - }) - ).toList()); + }); } @Override From bd3dec3d4e2eee48835af5d227b40fb735a97424 Mon Sep 17 00:00:00 2001 From: Brady McDonough Date: Sat, 10 Jan 2026 13:32:10 -0700 Subject: [PATCH 3/7] Missed info parameter --- .../affinity/object/attunedshards/AttunedShardTierString.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java index 91c95025..278c9abc 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTierString.java @@ -18,7 +18,7 @@ public AttunedShardTierString(String tierName) { this.tierName = tierName; if (AttunedShardTiers.NONE == this.variant) { - Affinity.LOGGER.info("Named tier could not be matched", tierName); + Affinity.LOGGER.info("Named tier {} could not be matched", tierName); this.tierName = "NONE"; } } From 90e7874e0919303c57e2752ffe0e026dc90f21f5 Mon Sep 17 00:00:00 2001 From: Brady McDonough Date: Sat, 10 Jan 2026 14:02:58 -0700 Subject: [PATCH 4/7] Removed extra logger call --- .../object/attunedshards/CustomShardTierRegistry.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java index 5261d57c..eb7a5a54 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java @@ -9,7 +9,6 @@ import net.minecraft.item.Item; import net.minecraft.item.Items; -import net.minecraft.registry.Registry; import net.minecraft.resource.ResourceManager; import net.minecraft.resource.ResourceType; import net.minecraft.util.Identifier; @@ -20,7 +19,6 @@ import java.util.stream.Collectors; import java.io.BufferedReader; import java.io.IOException; -import java.io.Reader; import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -30,12 +28,10 @@ public class CustomShardTierRegistry { public static HashMap REGISTRY = new HashMap<>(); public static void initialize() { - Affinity.LOGGER.info("Affinity shard tier reload listener registered."); ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new Loader()); } private static void registerItems(List itemList, AttunedShardTier tier) { - Affinity.LOGGER.info("Affinity registering items under tier {}", tier.toString()); REGISTRY.putAll(itemList.stream().collect(Collectors.toMap(item -> item, ign-> tier))); } From 36728e8b33c224089a3ee051de51a79db90a481e Mon Sep 17 00:00:00 2001 From: Brady McDonough Date: Sat, 10 Jan 2026 14:03:58 -0700 Subject: [PATCH 5/7] Removed extra imports --- .../affinity/object/attunedshards/AttunedShardTier.java | 1 - .../affinity/object/attunedshards/AttunedShardTiers.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java index 5844d12d..40618391 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java @@ -5,7 +5,6 @@ import net.fabricmc.api.Environment; import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.item.Item; -import net.minecraft.item.Items; import org.jetbrains.annotations.NotNull; /** diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java index 2d1be364..081b2bfa 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java @@ -3,7 +3,6 @@ import io.wispforest.affinity.Affinity; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.screen.PlayerScreenHandler; From 895f610017ce81d40dcd76c201fa804b807a43ae Mon Sep 17 00:00:00 2001 From: Brady McDonough Date: Sat, 10 Jan 2026 14:03:58 -0700 Subject: [PATCH 6/7] Removed extra imports --- .../affinity/blockentity/impl/AethumFluxNodeBlockEntity.java | 2 -- .../affinity/object/attunedshards/AttunedShardTier.java | 1 - .../affinity/object/attunedshards/AttunedShardTiers.java | 1 - 3 files changed, 4 deletions(-) diff --git a/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java b/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java index 423840b0..5def6d0c 100644 --- a/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java +++ b/src/main/java/io/wispforest/affinity/blockentity/impl/AethumFluxNodeBlockEntity.java @@ -10,7 +10,6 @@ import io.wispforest.affinity.blockentity.template.ShardBearingAethumNetworkMemberBlockEntity; import io.wispforest.affinity.blockentity.template.TickedBlockEntity; import io.wispforest.affinity.client.render.CuboidRenderer; -import io.wispforest.affinity.item.AttunedShardItem; import io.wispforest.affinity.misc.util.ListUtil; import io.wispforest.affinity.misc.util.MathUtil; import io.wispforest.affinity.misc.util.NbtUtil; @@ -26,7 +25,6 @@ import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.DustParticleEffect; import net.minecraft.text.Text; diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java index 5844d12d..40618391 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java @@ -5,7 +5,6 @@ import net.fabricmc.api.Environment; import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.item.Item; -import net.minecraft.item.Items; import org.jetbrains.annotations.NotNull; /** diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java index 2d1be364..081b2bfa 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTiers.java @@ -3,7 +3,6 @@ import io.wispforest.affinity.Affinity; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.screen.PlayerScreenHandler; From c070a2882ff9581f7ff4e66ba3a14eca9917bfce Mon Sep 17 00:00:00 2001 From: Brady McDonough Date: Sat, 10 Jan 2026 23:56:45 -0700 Subject: [PATCH 7/7] Fixed race condition in Registry's reload logic --- .../affinity/misc/util/FuturesUtil.java | 2 +- .../attunedshards/AttunedShardTier.java | 2 +- .../CustomShardTierRegistry.java | 72 +++++++++++-------- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java b/src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java index 9214ea83..074f2de6 100644 --- a/src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java +++ b/src/main/java/io/wispforest/affinity/misc/util/FuturesUtil.java @@ -5,7 +5,7 @@ public class FuturesUtil { - public static CompletableFuture allOf(List> futuresList) { + public static CompletableFuture allOf(List futuresList) { return CompletableFuture.allOf(futuresList.toArray(new CompletableFuture[futuresList.size()])); } } diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java index 40618391..93cc2cd2 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/AttunedShardTier.java @@ -54,7 +54,7 @@ default boolean isNone() { @NotNull static AttunedShardTier forItem(Item item) { if (item instanceof AttunedShardItem shardItem) return shardItem.tier(); - return CustomShardTierRegistry.REGISTRY.getOrDefault(item, AttunedShardTiers.NONE); + return CustomShardTierRegistry.getOrDefault(item, AttunedShardTiers.NONE); } } diff --git a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java index eb7a5a54..8b4bd0f5 100644 --- a/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java +++ b/src/main/java/io/wispforest/affinity/object/attunedshards/CustomShardTierRegistry.java @@ -25,12 +25,21 @@ public class CustomShardTierRegistry { - public static HashMap REGISTRY = new HashMap<>(); + private static HashMap REGISTRY = new HashMap<>(); public static void initialize() { ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(new Loader()); } + public static AttunedShardTier getOrDefault(Item i, AttunedShardTier d) { + return REGISTRY.getOrDefault(i, d); + } + + public static void clear() { + Affinity.LOGGER.info("{} items cleared from shard tier registry.", REGISTRY.size()); + REGISTRY.clear(); + } + private static void registerItems(List itemList, AttunedShardTier tier) { REGISTRY.putAll(itemList.stream().collect(Collectors.toMap(item -> item, ign-> tier))); } @@ -44,42 +53,43 @@ private static class Loader implements IdentifiableResourceReloadListener { @Override public CompletableFuture reload(Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor) { - CompletableFuture.supplyAsync(() -> { - CustomShardTierRegistry.REGISTRY.clear(); - return null; - }, prepareExecutor).thenCompose(synchronizer::whenPrepared); - - return FuturesUtil.allOf( manager.findResources( - "tiers", - path -> path.toString().endsWith(".json") - ).entrySet().parallelStream().map(ent -> - CompletableFuture.runAsync( - () -> { - try (BufferedReader reader = ent.getValue().getReader()) { - try { - JsonElement json = JsonParser.parseReader(reader); - if (json.isJsonNull()) { - return; + CompletableFuture prepareStep = CompletableFuture.supplyAsync(() -> { + CustomShardTierRegistry.clear(); + return null; + }, prepareExecutor).thenCompose(synchronizer::whenPrepared); + + CompletableFuture applyStep = FuturesUtil.allOf( manager.findResources( + "tiers", + path -> path.toString().endsWith(".json") + ).entrySet().parallelStream().map(ent -> + prepareStep.thenAcceptAsync(v -> { + try (BufferedReader reader = ent.getValue().getReader()) { + try { + JsonElement json = JsonParser.parseReader(reader); + if (json.isJsonNull()) { + return; + } + + CustomShardTierJsonFile result = CustomShardTierJsonFile.ENDEC.decodeFully(GsonDeserializer::of, json); + CustomShardTierRegistry.registerItems(result); + + } catch (JsonParseException e) { + Affinity.LOGGER.error("Parsing failed", e); + } + } catch (IOException | NoSuchElementException e) { + Affinity.LOGGER.error("Couldn't open resource described by {}", ent.getKey(), e); } - - CustomShardTierJsonFile result = CustomShardTierJsonFile.ENDEC.decodeFully(GsonDeserializer::of, json); - CustomShardTierRegistry.registerItems(result); - - } catch (JsonParseException e) { - Affinity.LOGGER.error("Parsing failed", e); - } - } catch (IOException | NoSuchElementException e) { - Affinity.LOGGER.error("Couldn't open resource described by {}", ent.getKey(), e); - } - }, applyExecutor - )).toList() - ).thenRun( () -> { + }, applyExecutor)).toList() + ).thenRunAsync( () -> { /* INVARIANTS */ if (CustomShardTierRegistry.REGISTRY.isEmpty()) { Affinity.LOGGER.debug("Affinity Shard registry reloaded with no json entries."); CustomShardTierRegistry.REGISTRY.put(Items.AMETHYST_SHARD, AttunedShardTiers.CRUDE); } - }); + Affinity.LOGGER.info("Affinity Shard Registry loaded with {} items", CustomShardTierRegistry.REGISTRY.size()); + }, applyExecutor); + + return applyStep; } @Override