From 2929f2cfcfe395ab90e2fa63aa01452803bcb91e Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 02:08:17 -0400 Subject: [PATCH 01/13] Check for offline playerness without needing a boolean --- .../api/casting/iota/EntityIota.java | 26 +++++++++---------- .../api/casting/mishaps/MishapOthersName.kt | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index a116d42ac6..05da1fd6b5 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -15,13 +15,17 @@ import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.component.ResolvableProfile; +import net.minecraft.world.level.storage.LevelResource; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Optional; import java.util.UUID; @@ -29,17 +33,15 @@ public class EntityIota extends Iota { private final UUID entityId; @Nullable private final Component entityName; - private boolean isPlayer; public EntityIota(@NotNull Entity e) { - this(e.getUUID(), getEntityNameWithInline(e), e instanceof Player); + this(e.getUUID(), getEntityNameWithInline(e)); } - public EntityIota(UUID entityId, @Nullable Component entityName, boolean isPlayer) { + public EntityIota(UUID entityId, @Nullable Component entityName) { super(() -> HexIotaTypes.ENTITY.get()); this.entityId = entityId; this.entityName = entityName; - this.isPlayer = isPlayer; } public UUID getEntityId() { @@ -54,8 +56,10 @@ public Entity getEntity(ServerLevel level) { return entityName; } - public boolean isPlayer() { - return isPlayer; + public boolean isPlayer(MinecraftServer server) { + Path playerDataDir = server.getWorldPath(LevelResource.PLAYER_DATA_DIR); + Path playerFile = playerDataDir.resolve(entityId.toString() + ".dat"); + return Files.exists(playerFile); } @Override @@ -99,22 +103,18 @@ private static Component getEntityNameWithInline(Entity entity) { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> inst.group( UUIDUtil.CODEC.fieldOf("entityId").forGetter(EntityIota::getEntityId), - ComponentSerialization.CODEC.optionalFieldOf("entityName").forGetter(iota -> Optional.ofNullable(iota.getEntityName())), - Codec.BOOL.fieldOf("isPlayer").orElse(true).forGetter(EntityIota::isPlayer) - ).apply(inst, (a, b, c) -> new EntityIota(a, b.orElse(null), c))); + ComponentSerialization.CODEC.optionalFieldOf("entityName").forGetter(iota -> Optional.ofNullable(iota.getEntityName())) + ).apply(inst, (a, b) -> new EntityIota(a, b.orElse(null)))); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( UUIDUtil.STREAM_CODEC, EntityIota::getEntityId, ByteBufCodecs.optional(ComponentSerialization.STREAM_CODEC), iota -> Optional.ofNullable(iota.getEntityName()), - ByteBufCodecs.BOOL, EntityIota::isPlayer, - (a, b, c) -> new EntityIota(a, b.orElse(null), c) + (a, b) -> new EntityIota(a, b.orElse(null)) ); @Override public boolean validate(EntityIota iota, ServerLevel level) { var entity = iota.getEntity(level); - // update isPlayer so older non-player entity iotas are not protected - iota.isPlayer = (entity instanceof Player); return entity != null; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt index 91eb4760e2..4e6478887b 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt @@ -46,7 +46,7 @@ class MishapOthersName(val confidant: Player?) : Mishap() { if(datumToCheck is EntityIota) { val ent = datumToCheck.getEntity(level) - if (ent == null && datumToCheck.isPlayer) + if (ent == null && datumToCheck.isPlayer(level.server)) return MishapOthersName(null) if(ent is Player && ent != caster) return MishapOthersName(ent) From d7435b82d537d2cba1e421030807e21b2a31692e Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 02:09:09 -0400 Subject: [PATCH 02/13] Don't null missing entities; throw mishap when used instead --- .../at/petrak/hexcasting/api/casting/ActionUtils.kt | 3 ++- .../petrak/hexcasting/api/casting/iota/EntityIota.java | 6 ------ .../api/casting/mishaps/MishapEntityNotFound.kt | 10 +++++++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt index 0ab2799c2c..89430d14b2 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt @@ -4,6 +4,7 @@ package at.petrak.hexcasting.api.casting import at.petrak.hexcasting.api.casting.iota.* import at.petrak.hexcasting.api.casting.math.HexPattern +import at.petrak.hexcasting.api.casting.mishaps.MishapEntityNotFound import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.utils.TreeList @@ -38,7 +39,7 @@ fun List.getDouble(idx: Int, argc: Int = 0): Double { fun List.getEntity(level: ServerLevel, idx: Int, argc: Int = 0): Entity { val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } if (x is EntityIota) { - return x.getEntity(level) + return x.getEntity(level) ?: throw MishapEntityNotFound.of(x) } else { throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity") } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index 05da1fd6b5..6ce9af721d 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -112,12 +112,6 @@ private static Component getEntityNameWithInline(Entity entity) { (a, b) -> new EntityIota(a, b.orElse(null)) ); - @Override - public boolean validate(EntityIota iota, ServerLevel level) { - var entity = iota.getEntity(level); - return entity != null; - } - @Override public MapCodec codec() { return CODEC; diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapEntityNotFound.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapEntityNotFound.kt index a66675d18e..539537b78e 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapEntityNotFound.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapEntityNotFound.kt @@ -1,6 +1,7 @@ package at.petrak.hexcasting.api.casting.mishaps import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.iota.EntityIota import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.pigment.FrozenPigment import at.petrak.hexcasting.api.utils.TreeList @@ -15,10 +16,17 @@ class MishapEntityNotFound(val entityId: UUID, val entityName: Component?) : Mis dyeColor(DyeColor.BROWN) override fun execute(env: CastingEnvironment, errorCtx: Context, stack: TreeList): TreeList { - env.mishapEnvironment.nauseate(3 * 20) + env.mishapEnvironment.nauseate(5 * 20) return stack } override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) = error("entity_not_found", entityName?.plainCopy()?.aqua ?: Component.literal(entityId.toString()).withStyle(ChatFormatting.AQUA)) + + companion object { + @JvmStatic + fun of(entityIota: EntityIota): MishapEntityNotFound { + return MishapEntityNotFound(entityIota.entityId, entityIota.entityName) + } + } } From 08f00a889568d06c8e124c4efbc479fe81fa00e5 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 02:20:49 -0400 Subject: [PATCH 03/13] Document new mishap in the book --- .../resources/assets/hexcasting/lang/en_us.flatten.json5 | 4 ++-- .../thehexbook/en_us/entries/casting/mishaps.json | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index 4cbf32ca26..a382aa7110 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -1065,7 +1065,7 @@ wrong_dimension: "cannot see %s from %s", entity_too_far: "%s is out of range", - entity_not_found: "%s was not found, either dead or unloaded", + entity_not_found: "%s could not be found in the world", immune_entity: "cannot alter %s", eval_too_much: "Evaluated too many patterns", no_item: "needs %s but got nothing", @@ -1556,7 +1556,7 @@ true_name: "I attempted to $(l:patterns/readwrite#hexcasting:write)$(action)save a reference/$ to another player to a permanent medium.$(br2)Causes black sparks, and robs me of my sight for approximately one minute.", "entity_not_found.title": "Entity Not Found", - entity_not_found: "I attempted to affect an entity that doesn't exist. Two cases for this; one is if the entity is simply dead. The other, and more peculiar to me, is if it is unable to be \"observed\" from afar.$(br2)Causes brown sparks, and causes me to feel sick for a moment, as if I had stood up too fast.", + entity_not_found: "I attempted to affect an entity that doesn't exist. This can happen if the entity is simply dead, or if it is unable to be \"observed\" from afar.$(br2)Causes brown sparks, and causes me to feel sick for a moment, as if I had stood up too fast.", "disabled.title": "Disallowed Action", disabled: "I tried to cast an action that has been disallowed by a server administrator.$(br2)Causes black sparks.", diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json index 45fdc0aa70..43fbe808c8 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json @@ -53,6 +53,11 @@ "title": "hexcasting.page.mishaps.entity_immune.title", "text": "hexcasting.page.mishaps.entity_immune" }, + { + "type": "patchouli:text", + "title": "hexcasting.page.mishaps.entity_not_found.title", + "text": "hexcasting.page.mishaps.entity_not_found" + }, { "type": "patchouli:text", "title": "hexcasting.page.mishaps.math_error.title", From 566c8d11d70a817e57b2f4f1012999557f7c900a Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 02:25:43 -0400 Subject: [PATCH 04/13] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3379b6631..f7de33a40b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Added Simulate, which causes the next pattern drawn to be simulated (to check for mishaps) rather than executed ([#1194](https://github.com/FallingColors/HexMod/pull/1194)) @Robotgiggle - Added the `hex_unbreakable` tag for blocks that should be immune to Break Block regardless of the configured mining tier ([#1186](https://github.com/FallingColors/HexMod/pull/1186)) @Robotgiggle @slava110 - Added a new Ancient Cypher hex that impulses nearby items towards the caster ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid +- Added a new mishap for trying to interact with a nonexistent or unloaded entity ([#1299](https://github.com/FallingColors/HexMod/pull/1299)) @slava110 @Robotgiggle ### Changed @@ -21,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - The pattern itself is now a special handler with a variable tail length - The tail length determines how many extra iotas from the original stack are included in each iteration's stack - Iotas included via the above process are now popped from the original stack +- Entity iotas no longer become Null if the referenced entity ceases to exist or becomes unloaded ([#1299](https://github.com/FallingColors/HexMod/pull/1299)) @Robotgiggle ### Changed From 9630d25cf4e112f7bbefe1808e7517ae81f919e1 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 13:42:58 -0400 Subject: [PATCH 05/13] Check for null in the other entity getter methods --- .../java/at/petrak/hexcasting/api/casting/ActionUtils.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt index 89430d14b2..08b34ca2c7 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt @@ -86,7 +86,7 @@ fun List.getBool(idx: Int, argc: Int = 0): Boolean { fun List.getItemEntity(level: ServerLevel, idx: Int, argc: Int = 0): ItemEntity { val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } if (x is EntityIota) { - val e = x.getEntity(level) + val e = x.getEntity(level) ?: throw MishapEntityNotFound.of(x) if (e is ItemEntity) return e } @@ -96,7 +96,7 @@ fun List.getItemEntity(level: ServerLevel, idx: Int, argc: Int = 0): ItemE fun List.getPlayer(level: ServerLevel, idx: Int, argc: Int = 0): ServerPlayer { val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } if (x is EntityIota) { - val e = x.getEntity(level) + val e = x.getEntity(level) ?: throw MishapEntityNotFound.of(x) if (e is ServerPlayer) return e } @@ -106,7 +106,7 @@ fun List.getPlayer(level: ServerLevel, idx: Int, argc: Int = 0): ServerPla fun List.getMob(level: ServerLevel, idx: Int, argc: Int = 0): Mob { val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } if (x is EntityIota) { - val e = x.getEntity(level) + val e = x.getEntity(level) ?: throw MishapEntityNotFound.of(x) if (e is Mob) return e } @@ -116,7 +116,7 @@ fun List.getMob(level: ServerLevel, idx: Int, argc: Int = 0): Mob { fun List.getLivingEntityButNotArmorStand(level: ServerLevel, idx: Int, argc: Int = 0): LivingEntity { val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } if (x is EntityIota) { - val e = x.getEntity(level) + val e = x.getEntity(level) ?: throw MishapEntityNotFound.of(x) if (e is LivingEntity && e !is ArmorStand) return e } From d515c78fd177554c8293e3efc518f35b337ed7c3 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 13:51:35 -0400 Subject: [PATCH 06/13] Tweak book description --- .../main/resources/assets/hexcasting/lang/en_us.flatten.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index a382aa7110..abcd28cca0 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -1556,7 +1556,7 @@ true_name: "I attempted to $(l:patterns/readwrite#hexcasting:write)$(action)save a reference/$ to another player to a permanent medium.$(br2)Causes black sparks, and robs me of my sight for approximately one minute.", "entity_not_found.title": "Entity Not Found", - entity_not_found: "I attempted to affect an entity that doesn't exist. This can happen if the entity is simply dead, or if it is unable to be \"observed\" from afar.$(br2)Causes brown sparks, and causes me to feel sick for a moment, as if I had stood up too fast.", + entity_not_found: "I attempted to affect an entity that doesn't exist. This can happen if the entity is dead, or if it is unable to be \"observed\" from afar.$(br2)Causes brown sparks, and causes me to feel sick for a moment, as if I had stood up too fast.", "disabled.title": "Disallowed Action", disabled: "I tried to cast an action that has been disallowed by a server administrator.$(br2)Causes black sparks.", From c3084f7b2f2a90a8e91282c7fb811f9f9f31ee5b Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 18:03:25 -0400 Subject: [PATCH 07/13] Only read from disk on server startup --- .../api/casting/iota/EntityIota.java | 27 ++++++++++++++----- .../api/casting/mishaps/MishapOthersName.kt | 2 +- .../hexcasting/fabric/FabricHexInitializer.kt | 6 ++++- .../hexcasting/forge/ForgeHexInitializer.java | 7 ++++- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index 6ce9af721d..8b8065ee00 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -1,7 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; -import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.samsthenerd.inline.api.InlineAPI; @@ -24,12 +23,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.HashSet; import java.util.Optional; import java.util.UUID; public class EntityIota extends Iota { + private static final HashSet playerUUIDs = new HashSet(); private final UUID entityId; @Nullable private final Component entityName; @@ -56,10 +55,8 @@ public Entity getEntity(ServerLevel level) { return entityName; } - public boolean isPlayer(MinecraftServer server) { - Path playerDataDir = server.getWorldPath(LevelResource.PLAYER_DATA_DIR); - Path playerFile = playerDataDir.resolve(entityId.toString() + ".dat"); - return Files.exists(playerFile); + public boolean uuidIsPlayer() { + return playerUUIDs.contains(entityId); } @Override @@ -97,7 +94,23 @@ private static Component getEntityNameWithInline(Entity entity) { return baseName.append(Component.literal(": ")).append(inlineEnt); } + // This finds anyone who has ever joined the server by reading the UUIDs from the playerdata folder + // We can't just use GameProfileCache because it only stores 1000 UUIDs and only keeps them for 30 days + public static void initPlayerUUIDs(MinecraftServer server) { + var playerDataDir = server.getWorldPath(LevelResource.PLAYER_DATA_DIR).toFile(); + String[] uuidStrings = playerDataDir.list(); + if (uuidStrings == null) return; + for (String string : uuidStrings) { + if (string.endsWith(".dat")) { + UUID playerId = UUID.fromString(string.substring(0, string.length() - 4)); + playerUUIDs.add(playerId); + } + } + } + public static void addPlayerUUID(UUID newUUID) { + playerUUIDs.add(newUUID); + } public static IotaType TYPE = new IotaType<>() { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt index 4e6478887b..d7bc57f723 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapOthersName.kt @@ -46,7 +46,7 @@ class MishapOthersName(val confidant: Player?) : Mishap() { if(datumToCheck is EntityIota) { val ent = datumToCheck.getEntity(level) - if (ent == null && datumToCheck.isPlayer(level.server)) + if (ent == null && datumToCheck.uuidIsPlayer()) return MishapOthersName(null) if(ent is Player && ent != caster) return MishapOthersName(ent) diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexInitializer.kt b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexInitializer.kt index 878e371aeb..980c9585eb 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexInitializer.kt +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexInitializer.kt @@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.addldata.ADMediaHolder import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers import at.petrak.hexcasting.api.casting.ActionRegistryEntry import at.petrak.hexcasting.api.casting.iota.DoubleIota +import at.petrak.hexcasting.api.casting.iota.EntityIota import at.petrak.hexcasting.api.item.HexHolderItem import at.petrak.hexcasting.api.item.IotaHolderItem import at.petrak.hexcasting.api.item.MediaHolderItem @@ -23,7 +24,6 @@ import at.petrak.hexcasting.common.casting.actions.spells.great.OpAltiora import at.petrak.hexcasting.common.command.PatternResKeyArgument import at.petrak.hexcasting.common.entities.HexEntities import at.petrak.hexcasting.common.items.ItemJewelerHammer -import at.petrak.hexcasting.common.items.magic.ItemMediaBattery import at.petrak.hexcasting.common.items.storage.ItemScroll import at.petrak.hexcasting.common.lib.* import at.petrak.hexcasting.common.lib.hex.* @@ -50,6 +50,7 @@ import net.fabricmc.api.ModInitializer import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback import net.fabricmc.fabric.api.entity.event.v1.EntityElytraEvents +import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents import net.fabricmc.fabric.api.event.player.AttackBlockCallback @@ -133,6 +134,7 @@ object FabricHexInitializer : ModInitializer { ServerLifecycleEvents.SERVER_STARTED.register { server -> + EntityIota.initPlayerUUIDs(server) if (!patternRegistryIsProcessed) { PatternRegistryManifest.processRegistry(server.overworld()) patternRegistryIsProcessed = true @@ -143,6 +145,8 @@ object FabricHexInitializer : ModInitializer { ServerTickEvents.END_WORLD_TICK.register(OpFlight::tickAllPlayers) ServerTickEvents.END_WORLD_TICK.register(OpAltiora::checkAllPlayers) + ServerPlayerEvents.JOIN.register { player -> EntityIota.addPlayerUUID(player.uuid) } + CommandRegistrationCallback.EVENT.register { dp, _, _ -> HexCommands.register(dp) } LootTableEvents.MODIFY.register { key, builder, _, _ -> diff --git a/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexInitializer.java b/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexInitializer.java index 73c5f84471..3f76b0cd07 100644 --- a/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexInitializer.java +++ b/Neoforge/src/main/java/at/petrak/hexcasting/forge/ForgeHexInitializer.java @@ -2,6 +2,7 @@ import at.petrak.hexcasting.api.HexAPI; import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers; +import at.petrak.hexcasting.api.casting.iota.EntityIota; import at.petrak.hexcasting.api.mod.HexConfig; import at.petrak.hexcasting.api.mod.HexStatistics; import at.petrak.hexcasting.common.blocks.behavior.HexComposting; @@ -35,7 +36,6 @@ import at.petrak.hexcasting.xplat.IXplatAbstractions; import com.samsthenerd.inline.utils.cradles.EntTypeCradle; import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -229,11 +229,16 @@ private static void initListeners() { }); evBus.addListener((ServerStartedEvent evt) -> { + EntityIota.initPlayerUUIDs(evt.getServer()); if (patternRegistryIsProcessed) return; PatternRegistryManifest.processRegistry(evt.getServer().overworld()); patternRegistryIsProcessed = true; }); + evBus.addListener((PlayerEvent.PlayerLoggedInEvent evt) -> { + EntityIota.addPlayerUUID(evt.getEntity().getUUID()); + }); + evBus.addListener((RegisterCommandsEvent evt) -> HexCommands.register(evt.getDispatcher())); evBus.addListener((PlayerEvent.BreakSpeed evt) -> { From 6e2a5181a078e6f0d949fa8e2edd55dcf73665b2 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 19:29:57 -0400 Subject: [PATCH 08/13] Allow binding cleric impeti with offline truenames --- .../hexcasting/api/casting/iota/EntityIota.java | 8 +++++++- .../circles/impetuses/BlockRedstoneImpetus.java | 13 +++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index 8b8065ee00..958b9c0f34 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.storage.LevelResource; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.apache.commons.lang3.StringUtils; import java.util.HashSet; import java.util.Optional; @@ -55,6 +56,11 @@ public Entity getEntity(ServerLevel level) { return entityName; } + public @Nullable String getRawName() { + if (entityName == null) return null; + return entityName.getString().split(":")[0]; + } + public boolean uuidIsPlayer() { return playerUUIDs.contains(entityId); } @@ -102,7 +108,7 @@ public static void initPlayerUUIDs(MinecraftServer server) { if (uuidStrings == null) return; for (String string : uuidStrings) { if (string.endsWith(".dat")) { - UUID playerId = UUID.fromString(string.substring(0, string.length() - 4)); + UUID playerId = UUID.fromString(StringUtils.removeEnd(string, ".dat")); playerUUIDs.add(playerId); } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockRedstoneImpetus.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockRedstoneImpetus.java index 6d3954e2f8..2e1fc09bb2 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockRedstoneImpetus.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/impetuses/BlockRedstoneImpetus.java @@ -6,6 +6,7 @@ import at.petrak.hexcasting.common.lib.HexBlockEntities; import at.petrak.hexcasting.common.lib.HexSounds; import at.petrak.hexcasting.xplat.IXplatAbstractions; +import com.mojang.authlib.GameProfile; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; @@ -71,11 +72,19 @@ protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Lev var stored = datumContainer.readIota(); if (stored instanceof EntityIota eieio) { var entity = eieio.getEntity(sLevel); + GameProfile profile = null; if (entity instanceof Player iotaPlayer) { + profile = iotaPlayer.getGameProfile(); + } else if (entity == null && eieio.uuidIsPlayer()) { + var rawName = eieio.getRawName(); + if (rawName != null) { + profile = new GameProfile(eieio.getEntityId(), rawName); + } + } + if (profile != null) { // phew, we got something - tile.setPlayer(iotaPlayer.getGameProfile(), entity.getUUID()); + tile.setPlayer(profile, eieio.getEntityId()); tile.sync(); - level.playSound(null, pos, HexSounds.IMPETUS_REDSTONE_DING.value(), SoundSource.BLOCKS, 1f, 1f); return ItemInteractionResult.sidedSuccess(level.isClientSide); From a5ff8963d77947ad4fbadb20cc8d31f2b8798118 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Sun, 13 Sep 2026 20:44:37 -0400 Subject: [PATCH 09/13] Handle invalid playerdata filenames --- .../at/petrak/hexcasting/api/casting/iota/EntityIota.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index 958b9c0f34..56f051a43d 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -108,8 +108,8 @@ public static void initPlayerUUIDs(MinecraftServer server) { if (uuidStrings == null) return; for (String string : uuidStrings) { if (string.endsWith(".dat")) { - UUID playerId = UUID.fromString(StringUtils.removeEnd(string, ".dat")); - playerUUIDs.add(playerId); + try { playerUUIDs.add(UUID.fromString(StringUtils.removeEnd(string, ".dat"))); } + catch (IllegalArgumentException ignored) {} } } } From ea2904eb6745b623f638c2d46a2ced0f1038a776 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Mon, 14 Sep 2026 22:46:41 -0400 Subject: [PATCH 10/13] Make nonexistent entities falsy --- .../java/at/petrak/hexcasting/api/casting/iota/EntityIota.java | 2 +- .../common/casting/actions/math/logic/OpCoerceToBool.kt | 3 +++ .../main/resources/assets/hexcasting/lang/en_us.flatten.json5 | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index 56f051a43d..a0d1f5f0af 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -74,7 +74,7 @@ public boolean toleratesOther(Iota that) { @Override public boolean isTruthy() { - return true; + throw new UnsupportedOperationException("To check truthiness, call getEntity() and see if it's non-null"); } @Override diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt index d9996457be..ddac47cf20 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt @@ -3,12 +3,15 @@ package at.petrak.hexcasting.common.casting.actions.math.logic import at.petrak.hexcasting.api.casting.asActionResult import at.petrak.hexcasting.api.casting.castables.ConstMediaAction import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.iota.EntityIota import at.petrak.hexcasting.api.casting.iota.Iota object OpCoerceToBool : ConstMediaAction { override val argc = 1 override fun execute(args: List, env: CastingEnvironment): List { + if (args[0] is EntityIota) + return ((args[0] as EntityIota).getEntity(env.world) != null).asActionResult return (args[0].isTruthy).asActionResult } } diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index abcd28cca0..935317ab34 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -1934,7 +1934,7 @@ }, logic: { - bool_coerce: "Convert an argument to a boolean. The number $(thing)0/$, $(thing)Null/$, False, the vector (0, 0, 0), and the empty list become False; everything else becomes True.", + bool_coerce: "Convert an argument to a boolean. The number 0, $(thing)Null/$, False, the vector (0, 0, 0), the empty list, and any entity that cannot be found become False; everything else becomes True.", bool_to_number: "Convert a boolean to a number; True becomes $(thing)1/$, and False becomes $(thing)0/$.", not: "If the argument is True, return False; if it is False, return True.", or: "Returns True if at least one of the arguments are True; otherwise returns False.", From 5650adf29acccb6ee88eafa6e849dc277cd813a0 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Mon, 14 Sep 2026 22:55:32 -0400 Subject: [PATCH 11/13] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7de33a40b..40d3a734d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - The tail length determines how many extra iotas from the original stack are included in each iteration's stack - Iotas included via the above process are now popped from the original stack - Entity iotas no longer become Null if the referenced entity ceases to exist or becomes unloaded ([#1299](https://github.com/FallingColors/HexMod/pull/1299)) @Robotgiggle +- Augur's Purification now returns false for nonexistent or unloaded entity iotas ([#1299](https://github.com/FallingColors/HexMod/pull/1299)) @Robotgiggle ### Changed From 688e917f80aa75bb82fb07955c58379b0902b333 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Mon, 14 Sep 2026 23:21:25 -0400 Subject: [PATCH 12/13] Un-special-case the entity falsiness --- .../at/petrak/hexcasting/api/casting/iota/BooleanIota.java | 3 ++- .../petrak/hexcasting/api/casting/iota/ContinuationIota.java | 3 ++- .../at/petrak/hexcasting/api/casting/iota/DoubleIota.java | 3 ++- .../at/petrak/hexcasting/api/casting/iota/EntityIota.java | 5 +++-- .../at/petrak/hexcasting/api/casting/iota/GarbageIota.java | 3 ++- .../java/at/petrak/hexcasting/api/casting/iota/Iota.java | 3 ++- .../java/at/petrak/hexcasting/api/casting/iota/ListIota.java | 3 ++- .../java/at/petrak/hexcasting/api/casting/iota/NullIota.java | 3 ++- .../at/petrak/hexcasting/api/casting/iota/PatternIota.java | 2 +- .../java/at/petrak/hexcasting/api/casting/iota/Vec3Iota.java | 3 ++- .../common/casting/actions/math/logic/OpCoerceToBool.kt | 4 +--- 11 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/BooleanIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/BooleanIota.java index dbba7b8efd..b927a95bf9 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/BooleanIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/BooleanIota.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; @@ -21,7 +22,7 @@ public boolean getBool() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { return this.getBool(); } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ContinuationIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ContinuationIota.java index e05780f9bd..a6abe700f0 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ContinuationIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ContinuationIota.java @@ -1,6 +1,7 @@ package at.petrak.hexcasting.api.casting.iota; import at.petrak.hexcasting.api.casting.eval.CastResult; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType; import at.petrak.hexcasting.api.casting.eval.vm.CastingVM; import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation; @@ -33,7 +34,7 @@ public SpellContinuation getContinuation() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { return true; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/DoubleIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/DoubleIota.java index af56b063cd..ebfb90f00b 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/DoubleIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/DoubleIota.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.api.utils.HexUtils; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; import com.mojang.serialization.Codec; @@ -26,7 +27,7 @@ public double getDouble() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { return this.getDouble() != 0.0; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index a0d1f5f0af..f41a01a2e1 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -73,8 +74,8 @@ public boolean toleratesOther(Iota that) { } @Override - public boolean isTruthy() { - throw new UnsupportedOperationException("To check truthiness, call getEntity() and see if it's non-null"); + public boolean isTruthy(CastingEnvironment env) { + return getEntity(env.getWorld()) != null; } @Override diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/GarbageIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/GarbageIota.java index a2da4ea5a4..50a03011e6 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/GarbageIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/GarbageIota.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; import com.mojang.serialization.MapCodec; import net.minecraft.ChatFormatting; @@ -23,7 +24,7 @@ public GarbageIota() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { return false; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java index 0839d919e4..2fec7d9f28 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java @@ -1,6 +1,7 @@ package at.petrak.hexcasting.api.casting.iota; import at.petrak.hexcasting.api.casting.eval.CastResult; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType; import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect; import at.petrak.hexcasting.api.casting.eval.vm.CastingVM; @@ -35,7 +36,7 @@ protected Iota(@NotNull Supplier> type) { return this.type.get(); } - abstract public boolean isTruthy(); + abstract public boolean isTruthy(CastingEnvironment env); /** * Compare this to another object, within a tolerance. diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java index bb429ce555..cea021eb7c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.api.mod.HexConfig; import at.petrak.hexcasting.api.utils.TreeList; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; @@ -48,7 +49,7 @@ public TreeList getList() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { return !this.getList().isEmpty(); } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/NullIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/NullIota.java index 01bb0ce255..201684c8a2 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/NullIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/NullIota.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; import com.mojang.serialization.MapCodec; import net.minecraft.ChatFormatting; @@ -20,7 +21,7 @@ public NullIota() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { return false; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java index 25267fc1d4..4c27ed43c0 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java @@ -49,7 +49,7 @@ public HexPattern getPattern() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { return true; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Vec3Iota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Vec3Iota.java index ba234ea8c2..df0f1dfb6f 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Vec3Iota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Vec3Iota.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api.casting.iota; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.api.utils.HexUtils; import at.petrak.hexcasting.common.lib.hex.HexIotaTypes; import com.mojang.serialization.MapCodec; @@ -29,7 +30,7 @@ public Vec3 getVec3() { } @Override - public boolean isTruthy() { + public boolean isTruthy(CastingEnvironment env) { var v = this.getVec3(); return !(v.x == 0.0 && v.y == 0.0 && v.z == 0.0); } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt index ddac47cf20..3485b4cb95 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/math/logic/OpCoerceToBool.kt @@ -10,8 +10,6 @@ object OpCoerceToBool : ConstMediaAction { override val argc = 1 override fun execute(args: List, env: CastingEnvironment): List { - if (args[0] is EntityIota) - return ((args[0] as EntityIota).getEntity(env.world) != null).asActionResult - return (args[0].isTruthy).asActionResult + return (args[0].isTruthy(env)).asActionResult } } From 8e864698eb990b60762539ca86af91780be08239 Mon Sep 17 00:00:00 2001 From: Robotgiggle Date: Tue, 15 Sep 2026 14:09:37 -0400 Subject: [PATCH 13/13] Allow truthiness checking without an env just in case --- .../java/at/petrak/hexcasting/api/casting/iota/EntityIota.java | 1 + .../main/java/at/petrak/hexcasting/api/casting/iota/Iota.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java index f41a01a2e1..cd426603e3 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/EntityIota.java @@ -75,6 +75,7 @@ public boolean toleratesOther(Iota that) { @Override public boolean isTruthy(CastingEnvironment env) { + if (env == null) return true; return getEntity(env.getWorld()) != null; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java index 2fec7d9f28..5a8b9127a2 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java @@ -36,7 +36,7 @@ protected Iota(@NotNull Supplier> type) { return this.type.get(); } - abstract public boolean isTruthy(CastingEnvironment env); + abstract public boolean isTruthy(@Nullable CastingEnvironment env); /** * Compare this to another object, within a tolerance.