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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.function.UnaryOperator;

public abstract class Iota {
@NotNull
Expand Down Expand Up @@ -102,6 +103,24 @@ public boolean executable() {
return null;
}

/**
* Applies the given operator to all iotas in this iota's tree (recursive subIotas), including this iota.
* @param visitor Applied to this iota then every element of the resulting iota's tree.
* @return The iota after all subiotas have been visited (and potentially replaced).
*/
public Iota visit(UnaryOperator<Iota> visitor) {
return visitor.apply(this).visitChildren(visitor);
}

/**
* Applies the given operator to all iotas in this iota's tree, **excluding** this iota. This must be called on an
* iota just before it is returned from {@link Iota#visit}. You should try to preserve your iota's identity if
* the operator doesn't modify anything.
*/
protected Iota visitChildren(UnaryOperator<Iota> visitor) {
return this;
}

/**
* This method is called to determine whether the iota is above the max serialisation depth/serialisation count limits.
* This is an alternative to deriving subIotas for if your Iota is a datastructure of variable size over something that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.UnaryOperator;

import static java.lang.Math.max;

Expand Down Expand Up @@ -97,6 +99,18 @@ public boolean toleratesOther(Iota that) {
return this.getList();
}

@Override
protected Iota visitChildren(UnaryOperator<Iota> visitor) {
var out = new ArrayList<Iota>();
boolean conserve = true;
for (Iota orig : getList()) {
var replacement = orig.visit(visitor);
conserve &= replacement == orig;
Comment thread
poolcritter marked this conversation as resolved.
out.add(replacement);
}
return conserve ? this : new ListIota(out);
}

@Override
public int size() {
return size;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package at.petrak.hexcasting.xplat

import at.petrak.hexcasting.api.addldata.ADHexHolder
import at.petrak.hexcasting.api.addldata.ADIotaHolder
import at.petrak.hexcasting.api.addldata.ADMediaHolder
import at.petrak.hexcasting.api.addldata.ADVariantItem
import at.petrak.hexcasting.api.casting.ActionRegistryEntry
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic
import at.petrak.hexcasting.api.casting.castables.SpecialHandler
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM
import at.petrak.hexcasting.api.casting.eval.vm.ContinuationFrame
import at.petrak.hexcasting.api.casting.iota.IotaType
import at.petrak.hexcasting.api.pigment.ColorProvider
import at.petrak.hexcasting.api.pigment.FrozenPigment
import at.petrak.hexcasting.api.player.AltioraAbility
import at.petrak.hexcasting.api.player.FlightAbility
import at.petrak.hexcasting.api.player.Sentinel
import at.petrak.hexcasting.common.lib.HexRegistries
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import at.petrak.hexcasting.common.msgs.IMessage
import at.petrak.hexcasting.interop.pehkui.PehkuiInterop
import com.google.common.base.Suppliers
import com.mojang.serialization.Lifecycle
import net.minecraft.core.BlockPos
import net.minecraft.core.MappedRegistry
import net.minecraft.core.Registry
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.server.Bootstrap
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Tier
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.material.Fluid
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition
import net.minecraft.world.phys.Vec3
import java.util.function.BiFunction
import java.util.function.Supplier

/** A dummy instance of [IXplatAbstractions] to be used in tests. All methods of this implementation throw unconditionally.
* @throws IllegalStateException always, no matter what
*/
internal class DummyXplatAbstractions: IXplatAbstractions {
companion object {
/** must call .get() at least once before constructing iotas */
val forbiddenMagics: Supplier<Unit> = Suppliers.memoize {
try { Bootstrap.bootStrap() } catch (_: Throwable) {}
HexIotaTypes.registerTypes { t, id -> Registry.register(HexIotaTypes.REGISTRY, id, t) }
}
}
override fun platform(): Platform? = error("Found use of DummyXplatAbstractions.")
override fun isModPresent(id: String): Boolean = error("Found use of DummyXplatAbstractions.")
override fun isPhysicalClient(): Boolean = error("Found use of DummyXplatAbstractions.")
override fun initPlatformSpecific() = error("Found use of DummyXplatAbstractions.")
override fun sendPacketToPlayer(target: ServerPlayer, packet: IMessage) = error("Found use of DummyXplatAbstractions.")
override fun sendPacketNear(pos: Vec3, radius: Double, dimension: ServerLevel, packet: IMessage) = error("Found use of DummyXplatAbstractions.")
override fun sendPacketTracking(entity: Entity, packet: IMessage) = error("Found use of DummyXplatAbstractions.")
override fun toVanillaClientboundPacket(message: IMessage): Packet<ClientGamePacketListener> = error("Found use of DummyXplatAbstractions.")
override fun setBrainsweepAddlData(mob: Mob) = error("Found use of DummyXplatAbstractions.")
override fun isBrainswept(mob: Mob): Boolean = error("Found use of DummyXplatAbstractions.")
override fun setPigment(target: Player, colorizer: FrozenPigment?): FrozenPigment? = error("Found use of DummyXplatAbstractions.")
override fun setSentinel(target: Player, sentinel: Sentinel?) = error("Found use of DummyXplatAbstractions.")
override fun setFlight(target: ServerPlayer, flight: FlightAbility?) = error("Found use of DummyXplatAbstractions.")
override fun setAltiora(target: Player, altiora: AltioraAbility?) = error("Found use of DummyXplatAbstractions.")
override fun setStaffcastImage(target: ServerPlayer, image: CastingImage?) = error("Found use of DummyXplatAbstractions.")
override fun setPatterns(target: ServerPlayer, patterns: List<ResolvedPattern?>) = error("Found use of DummyXplatAbstractions.")
override fun getFlight(player: ServerPlayer): FlightAbility? = error("Found use of DummyXplatAbstractions.")
override fun getAltiora(player: Player): AltioraAbility? = error("Found use of DummyXplatAbstractions.")
override fun getPigment(player: Player): FrozenPigment = error("Found use of DummyXplatAbstractions.")
override fun getSentinel(player: Player): Sentinel? = error("Found use of DummyXplatAbstractions.")
override fun getStaffcastVM(player: ServerPlayer, hand: InteractionHand): CastingVM = error("Found use of DummyXplatAbstractions.")
override fun getPatternsSavedInUi(player: ServerPlayer): List<ResolvedPattern> = error("Found use of DummyXplatAbstractions.")
override fun clearCastingData(player: ServerPlayer) = error("Found use of DummyXplatAbstractions.")
override fun findMediaHolder(stack: ItemStack): ADMediaHolder? = error("Found use of DummyXplatAbstractions.")
override fun findMediaHolder(player: ServerPlayer): ADMediaHolder? = error("Found use of DummyXplatAbstractions.")
override fun findDataHolder(stack: ItemStack): ADIotaHolder? = error("Found use of DummyXplatAbstractions.")
override fun findDataHolder(entity: Entity): ADIotaHolder? = error("Found use of DummyXplatAbstractions.")
override fun findHexHolder(stack: ItemStack): ADHexHolder? = error("Found use of DummyXplatAbstractions.")
override fun findVariantHolder(stack: ItemStack): ADVariantItem? = error("Found use of DummyXplatAbstractions.")
override fun isPigment(stack: ItemStack) = error("Found use of DummyXplatAbstractions.")
override fun getColorProvider(pigment: FrozenPigment): ColorProvider? = error("Found use of DummyXplatAbstractions.")
override fun addEquipSlotFabric(slot: EquipmentSlot): Item.Properties? = error("Found use of DummyXplatAbstractions.")
override fun <T : BlockEntity?> createBlockEntityType(func: BiFunction<BlockPos?, BlockState?, T?>, vararg blocks: Block?): BlockEntityType<T?>? = error("Found use of DummyXplatAbstractions.")
override fun tryPlaceFluid(level: Level, hand: InteractionHand, pos: BlockPos, fluid: Fluid): Boolean = error("Found use of DummyXplatAbstractions.")
override fun drainAllFluid(level: Level, pos: BlockPos): Boolean = error("Found use of DummyXplatAbstractions.")
override fun isCorrectTierForDrops(tier: Tier, bs: BlockState): Boolean = error("Found use of DummyXplatAbstractions.")
override fun getUnsealedIngredient(stack: ItemStack): Ingredient? = error("Found use of DummyXplatAbstractions.")
override fun tags(): IXplatTags? = error("Found use of DummyXplatAbstractions.")
override fun isShearsCondition(): LootItemCondition.Builder? = error("Found use of DummyXplatAbstractions.")
override fun getModName(namespace: String): String? = error("Found use of DummyXplatAbstractions.")
override fun getActionRegistry(): Registry<ActionRegistryEntry?>? = error("Found use of DummyXplatAbstractions.")
override fun getSpecialHandlerRegistry(): Registry<SpecialHandler.Factory<*>?>? = error("Found use of DummyXplatAbstractions.")
override fun getIotaTypeRegistry(): Registry<IotaType<*>> = MappedRegistry(HexRegistries.IOTA_TYPE, Lifecycle.stable())
override fun getArithmeticRegistry(): Registry<Arithmetic?>? = error("Found use of DummyXplatAbstractions.")
override fun getContinuationTypeRegistry(): Registry<ContinuationFrame.Type<*>?>? = error("Found use of DummyXplatAbstractions.")
override fun getEvalSoundRegistry(): Registry<EvalSound?>? = error("Found use of DummyXplatAbstractions.")
override fun isBreakingAllowed(world: ServerLevel, pos: BlockPos, state: BlockState, player: Player?): Boolean = error("Found use of DummyXplatAbstractions.")
override fun isPlacingAllowed(world: ServerLevel, pos: BlockPos, blockStack: ItemStack, player: Player?): Boolean = error("Found use of DummyXplatAbstractions.")
override fun getPehkuiApi(): PehkuiInterop.ApiAbstraction? = error("Found use of DummyXplatAbstractions.")
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;

import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.ServiceLoader;
import java.util.UUID;
Expand All @@ -56,6 +57,7 @@
/**
* more like IHexplatAbstracts lmaooooooo
*/
@ParametersAreNonnullByDefault
public interface IXplatAbstractions {
Platform platform();

Expand Down
112 changes: 112 additions & 0 deletions Common/src/test/java/iota_endofunctors_tests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import at.petrak.hexcasting.api.casting.iota.DoubleIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota
import at.petrak.hexcasting.api.casting.iota.NullIota
import at.petrak.hexcasting.api.casting.iota.PatternIota
import at.petrak.hexcasting.api.casting.math.HexDir
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.xplat.DummyXplatAbstractions
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.opentest4j.AssertionFailedError
import java.util.function.UnaryOperator

internal class IotaEndofunctorsTests {
fun exampleWalker(iota: Iota) =
when (iota) {
is DoubleIota -> DoubleIota(iota.double + 1)
is ListIota -> if (iota.list.any { it is DoubleIota && it.double == 7.0 }) { NullIota() } else { iota }
else -> iota
}

internal class IotaComparator(val inner: Iota) {
override fun equals(other: Any?): Boolean =
when (other) {
is IotaComparator -> Iota.tolerates(inner, other.inner)
is Iota -> Iota.tolerates(inner, other)
else -> false
}
override fun hashCode(): Int = inner.hashCode()
override fun toString(): String = inner.display().string
}

companion object {
@BeforeAll
@JvmStatic
fun ensureBlackMagicExecuted() {
DummyXplatAbstractions.forbiddenMagics.get()
}
}

internal fun assertTolerates(left: Iota, right: Iota, message: String? = "iotas were not equal") {
Assertions.assertEquals(IotaComparator(left), IotaComparator(right), message)
}

internal fun assertNotTolerates(left: Iota, right: Iota, message: String? = "iotas were equal") {
Assertions.assertNotEquals(IotaComparator(left), IotaComparator(right), message)
}

@Test
fun `my tests are working properly`() {
Assertions.assertAll(
{ assertTolerates(DoubleIota(42.0), DoubleIota(42.0)) },
{ assertNotTolerates(DoubleIota(42.0), DoubleIota(47.0)) },
)
}

@Test
fun `a sole double iota should be incremented`() {
assertTolerates(DoubleIota(43.0), DoubleIota(42.0).visit(::exampleWalker))
}

@Test
fun `a list of untouched iotas should be conserved`() {
val iota = ListIota(listOf(
PatternIota(HexPattern.fromAnglesUnchecked("aqaaw", HexDir.SOUTH_EAST)),
PatternIota(HexPattern.fromAnglesUnchecked("aqaawa", HexDir.SOUTH_EAST)),
PatternIota(HexPattern.fromAnglesUnchecked("aqaawaw", HexDir.SOUTH_EAST)),
))
Assertions.assertSame(iota, iota.visit(::exampleWalker))
}

@Test
fun `iotas should be visited recursively`() {
val orig = ListIota(listOf(
DoubleIota(1.0),
ListIota(listOf(
DoubleIota(2.0),
ListIota(listOf(
DoubleIota(3.0),
))
))
))
val expected = ListIota(listOf(
DoubleIota(2.0),
ListIota(listOf(
DoubleIota(3.0),
ListIota(listOf(
DoubleIota(4.0),
))
))
))
assertTolerates(expected, orig.visit(::exampleWalker))
}

@Test
fun `lists should be visited before their elements`() {
val orig = ListIota(listOf(
ListIota(listOf(DoubleIota(5.0))),
ListIota(listOf(DoubleIota(6.0))),
ListIota(listOf(DoubleIota(7.0))),
ListIota(listOf(DoubleIota(9.0))),
))
val expected = ListIota(listOf(
ListIota(listOf(DoubleIota(6.0))),
ListIota(listOf(DoubleIota(7.0))),
NullIota(),
ListIota(listOf(DoubleIota(10.0))),
))
assertTolerates(expected, orig.visit(::exampleWalker))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
at.petrak.hexcasting.xplat.DummyXplatAbstractions