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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -21,6 +22,8 @@ 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
- Augur's Purification now returns false for nonexistent or unloaded entity iotas ([#1299](https://github.com/FallingColors/HexMod/pull/1299)) @Robotgiggle

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -38,7 +39,7 @@ fun List<Iota>.getDouble(idx: Int, argc: Int = 0): Double {
fun List<Iota>.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")
}
Expand Down Expand Up @@ -85,7 +86,7 @@ fun List<Iota>.getBool(idx: Int, argc: Int = 0): Boolean {
fun List<Iota>.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
}
Expand All @@ -95,7 +96,7 @@ fun List<Iota>.getItemEntity(level: ServerLevel, idx: Int, argc: Int = 0): ItemE
fun List<Iota>.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
}
Expand All @@ -105,7 +106,7 @@ fun List<Iota>.getPlayer(level: ServerLevel, idx: Int, argc: Int = 0): ServerPla
fun List<Iota>.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
}
Expand All @@ -115,7 +116,7 @@ fun List<Iota>.getMob(level: ServerLevel, idx: Int, argc: Int = 0): Mob {
fun List<Iota>.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
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -21,7 +22,7 @@ public boolean getBool() {
}

@Override
public boolean isTruthy() {
public boolean isTruthy(CastingEnvironment env) {
return this.getBool();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -33,7 +34,7 @@ public SpellContinuation getContinuation() {
}

@Override
public boolean isTruthy() {
public boolean isTruthy(CastingEnvironment env) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -26,7 +27,7 @@ public double getDouble() {
}

@Override
public boolean isTruthy() {
public boolean isTruthy(CastingEnvironment env) {
return this.getDouble() != 0.0;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.samsthenerd.inline.api.InlineAPI;
Expand All @@ -15,31 +15,34 @@
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 org.apache.commons.lang3.StringUtils;

import java.util.HashSet;
import java.util.Optional;
import java.util.UUID;

public class EntityIota extends Iota {
private static final HashSet<UUID> playerUUIDs = new HashSet<UUID>();
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() {
Expand All @@ -54,8 +57,13 @@ public Entity getEntity(ServerLevel level) {
return entityName;
}

public boolean isPlayer() {
return isPlayer;
public @Nullable String getRawName() {
if (entityName == null) return null;
return entityName.getString().split(":")[0];
}

public boolean uuidIsPlayer() {
return playerUUIDs.contains(entityId);
}

@Override
Expand All @@ -66,8 +74,9 @@ public boolean toleratesOther(Iota that) {
}

@Override
public boolean isTruthy() {
return true;
public boolean isTruthy(CastingEnvironment env) {
if (env == null) return true;
return getEntity(env.getWorld()) != null;
}
Comment thread
Robotgiggle marked this conversation as resolved.

@Override
Expand All @@ -93,31 +102,37 @@ 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")) {
try { playerUUIDs.add(UUID.fromString(StringUtils.removeEnd(string, ".dat"))); }
catch (IllegalArgumentException ignored) {}
}
}
}

public static void addPlayerUUID(UUID newUUID) {
playerUUIDs.add(newUUID);
}
Comment on lines +105 to +121

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like these being public...


public static IotaType<EntityIota> TYPE = new IotaType<>() {
public static final MapCodec<EntityIota> 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<RegistryFriendlyByteBuf, EntityIota> 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;
}

@Override
public MapCodec<EntityIota> codec() {
return CODEC;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -23,7 +24,7 @@ public GarbageIota() {
}

@Override
public boolean isTruthy() {
public boolean isTruthy(CastingEnvironment env) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -35,7 +36,7 @@ protected Iota(@NotNull Supplier<IotaType<? extends Iota>> type) {
return this.type.get();
}

abstract public boolean isTruthy();
abstract public boolean isTruthy(@Nullable CastingEnvironment env);

/**
* Compare this to another object, within a tolerance.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -48,7 +49,7 @@ public TreeList<Iota> getList() {
}

@Override
public boolean isTruthy() {
public boolean isTruthy(CastingEnvironment env) {
return !this.getList().isEmpty();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,7 +21,7 @@ public NullIota() {
}

@Override
public boolean isTruthy() {
public boolean isTruthy(CastingEnvironment env) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public HexPattern getPattern() {
}

@Override
public boolean isTruthy() {
public boolean isTruthy(CastingEnvironment env) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<Iota>): TreeList<Iota> {
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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.uuidIsPlayer())
return MishapOthersName(null)
if(ent is Player && ent != caster)
return MishapOthersName(ent)
Expand Down
Loading
Loading