···11package org.net.eu.pool.mica
2233import com.mojang.serialization.Codec
44-import it.unimi.dsi.fastutil.longs.Long2ObjectMap
44+import com.mojang.serialization.codecs.RecordCodecBuilder
55+import it.unimi.dsi.fastutil.longs.{Long2IntMap, Long2ObjectMap}
56import net.minecraft.block.{AbstractBlock, Block}
77+import net.minecraft.component.DataComponentTypes
88+import net.minecraft.entity.player.PlayerEntity
69import net.minecraft.item.{Item, ItemUsageContext}
710import net.minecraft.registry.{Registries, Registry, RegistryKey, RegistryKeys}
1111+import net.minecraft.text.Text
812import net.minecraft.util.math.Direction.{Axis, AxisDirection}
99-import net.minecraft.util.{ActionResult, Identifier}
1010-import net.minecraft.util.math.{BlockPos, Direction}
1313+import net.minecraft.util.{ActionResult, Identifier, Uuids}
1414+import net.minecraft.util.math.{BlockPos, Direction, Vec3d}
1515+import net.minecraft.util.shape.{VoxelShape, VoxelShapes}
1116import net.minecraft.world.World
1717+import org.net.eu.pool.mica.RevealRune.Frame
1818+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
1919+2020+import scala.annotation.MacroAnnotation
2121+import scala.collection.mutable
2222+import scala.quoted.{Expr, Quotes}
2323+import scala.reflect.ClassTag
2424+import scala.util.chaining.scalaUtilChainingOps
1225// ifversion(>=2100, <[[
1326import net.minecraft.storage.{ReadView, WriteView}
1427import org.ladysnake.cca.api.v3.component.sync.AutoSyncedComponent
···2033import dev.onyxstudios.cca.api.v3.world.WorldComponentFactoryRegistry
2134// ]]>)
22353636+/**
3737+ * Holds a deferred promise to add a value to a registry. This is returned by e.g. [[cursedRegister]] for you to call in your init function.
3838+ *
3939+ * <b style="color: color-mix(in oklch, 15% currentcolor, red)">Caution:</b> If you have a forced Registrar (as opposed
4040+ * to a by-name / lazy `=> Registrar[_]`), you '''must''' call [[register()]] at some point. '''Failing to do so may
4141+ * cause an intrusive-holder crash!'''
4242+ *
4343+ * @tparam T Type of the value that will be registered.
4444+ */
2345trait Registrar[T]:
2424- val value: T
2525- private[mica] def register(): Unit
4646+ /**
4747+ * Backing value. This is usually an [[Item]] or [[Block]].
4848+ */
4949+ lazy val value: T
5050+5151+ /**
5252+ * Adds [[value]] to the associated registry.
5353+ */
5454+ def register(): Unit
26552756// probably the least cursed part of this code
2857/**
2958 * Cross-version helper for registering items.
3059 *
3160 * @tparam T Concrete item type for generics purposes.
3232- * @param identifier Final identifier of item - passed to the settings in >=1.21.2.
3333- * @param settings Item settings - these must be known ahead-of-time since 1.21.2 makes them hold the registry key.
3434- * @param itemFactory Produces the item from its settings. This **must** use the provided instance!
6161+ * @param identifier Final identifier of item — passed to the settings in >=1.21.2.
6262+ * @param settings Item settings — these must be known ahead-of-time since 1.21.2 makes them hold the registry key.
6363+ * @param itemFactory Produces the item from its settings. This '''must''' use the provided instance!
3564 * @return Object containing the final item, as well as a method that registers it.
3665 */
3766def cursedRegister[T <: Item](identifier: Identifier, settings: Item.Settings)(itemFactory: Item.Settings => T): Registrar[T] =
···3968 val key = RegistryKey.of(RegistryKeys.ITEM, identifier)
4069 lazy val item = itemFactory(settings /*ifversion(>=2102,<[[*/.registryKey(key) /*]]>)*/)
4170 new Registrar[T]:
4242- override val value: T = item
4343-7171+ private var registered = false
7272+ override lazy val value: T =
7373+ if registered then
7474+ item
7575+ else
7676+ throw IllegalStateException(s"Item $identifier was not registered")
4477 override def register(): Unit =
7878+ registered = true
4579 Registry.register(Registries.ITEM, key, item)
46804781/**
4882 * Cross-version helper for registering blocks.
4983 *
5084 * @tparam T Concrete block type for generics purposes.
5151- * @param identifier Final identifier of block - passed to the settings in >=1.21.2.
5252- * @param settings Block settings - these must be known ahead-of-time since 1.21.2 makes them hold the registry key.
5353- * @param blockFactory Produces the block from its settings. This **must** use the provided instance! This function is also passed its return value for e.g. block entities.
8585+ * @param identifier Final identifier of block — passed to the settings in >=1.21.2.
8686+ * @param settings Block settings — these must be known ahead-of-time since 1.21.2 makes them hold the registry key.
8787+ * @param blockFactory Produces the block from its settings. This '''must''' use the provided instance! This function is also passed its return value for e.g. block entities.
5488 * @return Object containing the final block, as well as a method that registers it.
5589 */
5690def cursedRegister[T <: Block](identifier: Identifier, settings: AbstractBlock.Settings)(blockFactory: (=> AbstractBlock.Settings => T) => AbstractBlock.Settings => T): Registrar[T] =
···5892 lazy val fixed: AbstractBlock.Settings => T = blockFactory(fixed)
5993 lazy val block = fixed(settings /*ifversion(>=2100,<[[*/.registryKey(key) /*]]>)*/)
6094 new Registrar[T]:
6161- override val value: T = block
9595+ override lazy val value: T = block
62966397 override def register(): Unit =
6498 Registry.register(Registries.BLOCK, key, block)
6599100100+/**
101101+ * Converts an annotated function into a [[Rune]] implementation and as many [[ThunkFrame]] implementations as needed
102102+ * for its arguments. Optional arguments are not allowed.
103103+ */
104104+case class rune[+T] private(reader: (List[Any]) => (T, List[Any])) extends MacroAnnotation:
105105+ def this()(using ev: Unit <:< T) = this((ev(()), _))
106106+107107+ override def transform(using q: Quotes)(definition: q.reflect.Definition, companion: Option[q.reflect.Definition]): List[q.reflect.Definition] =
108108+ import q.reflect._
109109+// definition match
110110+// case d@DefDef(name, params, returnType, body) =>
111111+// val buf = mutable.Buffer(definition)
112112+// buf ++= companion
113113+// val explicit = params.collect:
114114+// case c@TermParamClause(p) if !c.isGiven => p
115115+// if explicit.length > 1 then
116116+// report.error("method must have exactly one explicit parameter list")
117117+// val args = explicit(1)
118118+// val runeSymbol = Symbol.newClass(d.symbol.owner, s"$name", List(TypeRepr.of[Rune]), s => List(), None)
119119+// buf ++= new register(name).transform(ClassDef(runeSymbol, List(TypeTree.of[Rune]), List()), None)
120120+// // TODO
121121+// buf.toList
122122+// case _ =>
123123+// report.error("@rune may only be used on function definitions")
124124+ List(definition) :++ companion
125125+126126+/**
127127+ * A thunk frame represents a ''pending computation'' on the stack — for example, an addition operator waiting for
128128+ * its arguments.
129129+ */
66130trait ThunkFrame derives HasRegistry:
6767- given Codec[this.type] = compiletime.deferred
6868- val accept: PartialFunction[Abstract[ValueType], ThunkFrame]
131131+ type Data: Codec
132132+ def accept[T: ValueType](data: Data, value: T): BoxedThunk
133133+134134+/**
135135+ * Holds operations and constants for manipulating [[RuneShift!]] values.
136136+ */
137137+object RuneShift:
138138+ /**
139139+ * Selects a rune's offset from the block grid. A rune offset is a composition of three offsets (0 to 3, 0 to 7, 0 to 3)
140140+ * and the rune's facing direction.
141141+ *
142142+ * @note The RuneShift type is represented as an opaque [[Int]], with all values bit-packed within. Using [[asInstanceOf]]
143143+ * from or to [[Int]] is safe, but this is subject to change.
144144+ */
145145+ opaque type RuneShift = Int
146146+ inline def apply(value: Int): RuneShift = value
147147+ def apply(x: Int, y: Int, z: Int, facing: Direction): RuneShift = x & 0b11 | y << 2 & 0b11100 | z << 5 & 0b1100000 | facing.ordinal << 7
148148+ private def x_impl(s: Expr[RuneShift], x: Expr[Int])(using q: Quotes): Expr[Unit] =
149149+ import q.reflect.*
150150+ Assign(s.asTerm, '{${s}(x = ${x})}.asTerm).asExprOf[Unit]
151151+ private def y_impl(s: Expr[RuneShift], y: Expr[Int])(using q: Quotes): Expr[Unit] =
152152+ import q.reflect.*
153153+ Assign(s.asTerm, '{${s}(y = ${y})}.asTerm).asExprOf[Unit]
154154+ private def z_impl(s: Expr[RuneShift], z: Expr[Int])(using q: Quotes): Expr[Unit] =
155155+ import q.reflect.*
156156+ Assign(s.asTerm, '{${s}(z = ${z})}.asTerm).asExprOf[Unit]
157157+ given ops: AnyRef:
158158+ extension (s: RuneShift)
159159+ /**
160160+ * Gets the backing value of the rune shift as a bit-packed [[Int]]. This has no runtime cost.
161161+ */
162162+ @inline def value: Int = s
163163+ /**
164164+ * Returns the X-offset of runes from the block grid: an integer 0 to 3 (inclusive).
165165+ */
166166+ @inline def x = s & 0b11
167167+ /**
168168+ * Returns the Y-offset of runes from the block grid: an integer 0 to 7 (inclusive).
169169+ */
170170+ @inline def y = s >> 2 & 0b111
171171+ /**
172172+ * Returns the Z-offset of runes from the block grid: an integer 0 to 3 (inclusive).
173173+ */
174174+ @inline def z = s >> 5 & 0b11
175175+ /**
176176+ * Returns the facing direction of these runes.
177177+ */
178178+ @inline def facing = Direction.values()(s >> 7 & 0b111)
179179+ /**
180180+ * Updates the rune shift with new values.
181181+ * @param x New x-offset (0 to 3). <b>Invalid offsets are undefined behavior!</b>
182182+ * @param y New y-offset (0 to 7). <b>Invalid offsets are undefined behavior!</b>
183183+ * @param z New z-offset (0 to 3). <b>Invalid offsets are undefined behavior!</b>
184184+ * @param facing New facing direction (any of the 6).
185185+ * @return A [[RuneShift!]] value with the new properties. It should be used for its value: it may or may not be [[eq]] to any other [[RuneShift!]], or even itself.
186186+ */
187187+ @inline def apply(x: Int = s.x, y: Int = s.y, z: Int = s.z, facing: Direction = s.facing): RuneShift = RuneShift(x, y, z, facing)
188188+ def storage(using w: World) = w.getComponent(AbstractRuneStorage.keys(s.value))
189189+ private def facing_impl(s: Expr[RuneShift], facing: Expr[Direction])(using q: Quotes): Expr[Unit] =
190190+ import q.reflect.*
191191+ Assign(s.asTerm, '{${s}(facing = ${facing})}.asTerm).asExprOf[Unit]
192192+193193+ /**
194194+ * Cache of [[VoxelShape]]s for every rune offset, used for speed. Clients are expected to not mutate this array.
195195+ */
196196+ val shapeCache: Array[VoxelShape] = Array.ofDim[VoxelShape](768).tap: m =>
197197+ for shift: RuneShift <- 0 until 768 do
198198+ val middle = (shift.x / 4.0 - 0.5, shift.y / 8.0 - 0.5, shift.z / 4.0 - 0.5)
199199+ m(shift) = VoxelShapes.cuboid(middle._1 - .25, middle._2, middle._3 - .25, middle._1 + .25, middle._2 + .0625, middle._3 + .25)
200200+end RuneShift
201201+export RuneShift.RuneShift
6920270203/**
7171- * A rune is an element of a cast. Runes are read left-to-right and form execution frames on the thunk stack.
204204+ * A rune is the basic element of a cast. Runes fundamentally operate on a list of [[ThunkFrame]]s, but may also access
205205+ * the parsing stream (e.g. [[QuoteRune]]) to store internal data.
206206+ *
207207+ * <b>Note for implementors:</b> Almost all runes don't need access to the parsing stream. In this case, you can use
208208+ * [[SimpleRune]] to avoid implementing extra things.
209209+ *
210210+ * @see [[BoxedRune]]
72211 */
73212trait Rune derives HasRegistry:
213213+ /**
214214+ * Any data this rune needs from parsing to execute. For most runes, this is [[Unit]] (no information), but in some
215215+ * cases other values may be accurate:
216216+ *
217217+ * - [[QuoteRune]] stores a `List[BoxedRune]` preserved for later evaluation.
218218+ * - [[EndQuoteRune]] stores [[Nothing]], as parsing it alone will never succeed.
219219+ */
74220 type Data: Codec
752217676- def surfaceSprite: Identifier
222222+ /**
223223+ * Determines the sprite used for this rune's surface, as the name of a texture in the block atlas.
224224+ *
225225+ * Mods may add custom sprites, but generally, surfaces should use one of the built-in types: the [[Rune.BASIC_TEXTURE basic texture]], [[Rune.SPELL_TEXTURE spell texture]], or [[Rune.IMPETUS_TEXTURE starter texture]].
226226+ *
227227+ * @return The rune's surface texture. The path `foo:bar` corresponds to `assets/foo/textures/bar.png`.
228228+ */
229229+ def surfaceSprite: Identifier = Rune.BASIC_TEXTURE
7723078231 /**
7979- * Reads the rune's information from the list of runes.
232232+ * Reads the rune's information from the list of runes. For most runes, this method does nothing.
80233 *
81234 * @param rhs The list of remaining runes, not including this rune.
8282- * @throws RunesParseError Thrown when the rune is unable to read all of its arguments.
235235+ * @throws RuneError Thrown when the rune is unable to read all of its arguments.
83236 * @return The rune's metadata and the list of remaining runes iff parsing is successful.
84237 */
8585- @throws[RunesParseError]("when this rune cannot parse its arguments")
8686- def read(rhs: List[Rune]): (Data, List[Rune])
238238+ def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (Data, List[(Rune, RuneRef)])
8723988240 /**
8989- * Executes this rune, given its metadata. Executing a rune pushes some [[ThunkFrame]]s to the stack, then pops some.
9090- * @param data The metadata returned from [[read]].
9191- * @param frame The top of the thunk stack. All runes usually will pass data to a thunk after pushing some of its own.
241241+ * Reads and executes this rune. This method usually pops then pushes frames on `frame`.
242242+ *
243243+ * Here's an example of a simple rune that supplies a Unit:
244244+ *
245245+ * {{{
246246+ * // Pass a Unit into the top frame
247247+ * def execute(data: Data, frame: BoxedThunk): BoxedThunk =
248248+ * Unit match
249249+ * case frame.accept(newFrame) => newFrame
250250+ * case _ => ???
251251+ * }}}
252252+ *
253253+ * @param data Extra information returned by [[read]].
254254+ * @param frame The current top of the thunk stack before execution.
92255 * @return The new top of the thunk stack.
93256 */
9494- def execute(data: Data, frame: ThunkFrame): ThunkFrame
257257+ def execute(data: Data, frame: BoxedThunk): BoxedThunk
95258259259+ /**
260260+ * Generated item for this rune to use. You need to handle [[Registrar.register registering this item]] yourself, but not doing so
261261+ * will not incur an intrusive-holders crash unless someone else forces this field first.<hr style="border-style:hidden">
262262+ * <b style="color: color-mix(in oklch, 15% currentcolor, red)">Caution:</b> You must not force this field before the owning rune has been added to the registry. Doing so
263263+ * will crash the game!
264264+ */
96265 lazy val item =
97266 val identifier = registryFor[Rune].getId(this)
98267 if identifier == null then
99268 throw IllegalStateException("Rune.item may not be referenced before the rune is registered")
100269 cursedRegister(identifier, Item.Settings()):
101270 new Item(_):
102102- override def useOnBlock(context: ItemUsageContext): ActionResult =
271271+ override def useOnBlock(using context: ItemUsageContext): ActionResult =
103272 println("GOT HERE 2")
104273 val p = context.getHitPos
105274 val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt)
···115284 q.setZ(0)
116285 b.setZ(b.getZ + 1)
117286 val h = RuneShift(q.getX, q.getY, q.getZ, context.getSide)
118118- val ref = RuneRef(b, h)
287287+ given ref: RuneRef = RuneRef(b, h)
119288 if ref.isEmpty && ref.neighbors.forall(_.isEmpty) then
120289 ref.rune = Rune.this
290290+ try
291291+ println(s"Loading placed NBT")
292292+ val nbt = Uuids.toIntArray(context.getStack.getComponents.get(DataComponentTypes.PROFILE).uuid.get)
293293+ println(s"Loaded array: ${nbt.mkString("Array(", ", ", ")")}")
294294+ ref.heap0 = nbt(0)
295295+ ref.heap1 = nbt(1)
296296+ ref.heap2 = nbt(2)
297297+ ref.heap3 = nbt(3)
298298+ catch case e: Exception =>
299299+ println(s"nvm, caught a ${e} - generating it")
300300+ given PlayerEntity = context.getPlayer
301301+ ref.rune.fillHeap()
302302+ println(s"Now the heap at ${ref} is ${ref.heap0} ${ref.heap1} ${ref.heap2} ${ref.heap3}")
121303 AbstractRuneStorage.sync(summon, h)
122304 println("done !")
123305 ActionResult.SUCCESS
124306 else
125307 println("nah")
126308 ActionResult.PASS
309309+310310+ def computeNeighbors(using r: RuneRef): Set[RuneRef] = Set(r.north.north, r.east.east, r.south.south, r.west.west)
311311+ def ignite(using r: RuneRef, ci: CallbackInfoReturnable[ActionResult])(ctx: ItemUsageContext): Unit = ()
312312+ def fillHeap(using RuneRef, PlayerEntity, World)(): Unit = ()
313313+314314+/**
315315+ * A special case of [[Rune]] that only operates on the [[ThunkFrame]] stack, not modifying parsing. Most runes fall into
316316+ * this category. Using SimpleRune in this case provides a stronger guarantee and could assist in compiler optimization
317317+ * in the future.
318318+ */
319319+trait SimpleRune extends Rune:
320320+ override type Data = Unit
321321+ override given Codec[Data] = summon[Codec[Unit]]
322322+ override def read(rhs: List[(Rune, RuneRef)])(using RuneRef, World): (Data, List[(Rune, RuneRef)]) = ((), rhs)
323323+ override def execute(remainder: Unit, frame: BoxedThunk): BoxedThunk = execute(frame)
324324+ def execute(frame: BoxedThunk): BoxedThunk
325325+326326+/**
327327+ * Holder object for [[Rune! Rune]] registries and common values of [[Rune.surfaceSprite]].
328328+ */
127329object Rune:
330330+ /**
331331+ * Texture used for runes that do not mutate the world.
332332+ */
128333 final val BASIC_TEXTURE = Identifier.of("mica", "block/basic_rune")
334334+ /**
335335+ * Texture used for runes that create [[SideEffect! side effects]] mutating the world.
336336+ */
129337 final val SPELL_TEXTURE = Identifier.of("mica", "block/spell_rune")
338338+ /**
339339+ * Texture used for special runes that fire off a rune-chain.
340340+ */
130341 final val IMPETUS_TEXTURE = Identifier.of("mica", "block/start_rune")
131342132343case class RuneRef(pos: BlockPos, shift: RuneShift):
344344+ def floatPos = Vec3d(pos.getX + shift.x / 4.0, pos.getY + shift.y / 8.0, pos.getZ + shift.z / 4.0)
345345+133346 def offset(dir: Direction): RuneRef =
134347 dir match
135348 case Direction.DOWN =>
···157370 else RuneRef(pos, shift (x = shift.x + 1))
158371159372 def rune(using World): Rune = shift.storage(pos)
160160- def rune_=(r: Rune)(using World): Unit = shift.storage(pos) = r
373373+ def rune_=(r: Rune)(using World): Unit =
374374+ if r eq EmptyRune then
375375+ heap0 = 0
376376+ heap1 = 0
377377+ heap2 = 0
378378+ heap3 = 0
379379+ shift.storage(pos) = r
161380 def isEmpty(using World) = rune == null || rune == EmptyRune
381381+ def heap0(using World): Int = shift.storage.heap0.get(pos.asLong)
382382+ def heap0_=(r: Int)(using World): Unit = shift.storage.heap0.put(pos.asLong, r)
383383+ def heap1(using World): Int = shift.storage.heap1.get(pos.asLong)
384384+ def heap1_=(r: Int)(using World): Unit = shift.storage.heap1.put(pos.asLong, r)
385385+ def heap2(using World): Int = shift.storage.heap2.get(pos.asLong)
386386+ def heap2_=(r: Int)(using World): Unit = shift.storage.heap2.put(pos.asLong, r)
387387+ def heap3(using World): Int = shift.storage.heap3.get(pos.asLong)
388388+ def heap3_=(r: Int)(using World): Unit = shift.storage.heap3.put(pos.asLong, r)
162389163390 def north = offset(Direction.NORTH)
164391 def south = offset(Direction.SOUTH)
165392 def east = offset(Direction.EAST)
166393 def west = offset(Direction.WEST)
167394168168- def neighbors = Seq(north, east, south, west, north.east, north.west, south.east, south.west)
395395+ def neighbors = Set(north, east, south, west, north.east, north.west, south.east, south.west)
396396+ def adjacent = Set(north.north, east.east, south.south, west.west)
169397170398private[mica] trait AbstractRuneStorage extends Component, AutoSyncedComponent:
171171- type Concrete <: ConcreteRuneStorage
399399+ type Concrete <: AbstractRuneStorage
172400 val world: World
173401 val contents: Long2ObjectMap[Rune]
402402+ val heap0: Long2IntMap
403403+ val heap1: Long2IntMap
404404+ val heap2: Long2IntMap
405405+ val heap3: Long2IntMap
174406 given ComponentKey[Concrete] = compiletime.deferred
175407 def apply(pos: BlockPos): Rune = contents.get(Long.box(pos.asLong))
176408 def update(pos: BlockPos, rune: Rune): Unit = rune match
···178410 case EmptyRune => contents.remove(Long.box(pos.asLong))
179411 case _ => contents.put(pos.asLong, rune)
180412413413+def packLong(h: Int, l: Int): Long = (h.toLong << 32) | (l & 0xFFFFFFFFL)
414414+def unpackLong(n: Long): (Int, Int) = ((n >>> 32).toInt, n.toInt)
415415+181416object AbstractRuneStorage:
182417 private[mica] val keys: Array[ComponentKey[? <: AbstractRuneStorage]] = Array.fill(768)(null)
183418 def get(world: World, shift: RuneShift): AbstractRuneStorage = keys(shift.value).get(world)
184419 def sync(world: World, shift: RuneShift): Unit = keys(shift.value).sync(world)
185420186186-@hasRegistry
187421trait ValueType[T: Codec]:
188422 def eq[U: ValueType](x: T, y: U): Boolean
189189-object ValueType423423+ def cast[R: ClassTag](x: T): Option[R] =
424424+ x match
425425+ case x: R => Some(x)
426426+ case _ => None
427427+ def show(x: T): Text
428428+ def codec: Codec[T] = summon
429429+object ValueType:
430430+ given HasRegistry[ValueType[?]] = HasRegistry.derived
···1414import scala.quoted.{Expr, Quotes, ToExpr, Type}
1515import scala.reflect.ClassTag
16161717-case class ModID(name: String)
1717+/**
1818+ * Helper type used with [[register]] for setting your mod ID.
1919+ * @param name Mod ID, pursuant to identifier rules.
2020+ */
2121+case class ModID(name: String):
2222+ Identifier.of(name, "any")
2323+2424+/**
2525+ * Returns the mod ID of the current file.
2626+ */
1827inline def modid(using m: ModID): String = m.name
19282029/**
3030+ * '''Deprecated''': The generated givens are hard to use in code. Derive [[HasRegistry]] instead.
3131+ *
2132 * This macro generates a registry for a class, allowing the [[register @register]] annotation to track its descendants.
2233 */
2334@deprecated("@hasRegistry is hard to use correctly. Opt to derive HasRegistry instead.")
···97108 * Adds a hook to when a given object is registered by [[register @register]]. This could be used to, for example, add registrations to other registries (as [[Rune]] does).
98109 * @tparam T Object to listen for registrations on. This should be the *concrete type* of your object, not of the registry!
99110 */
100100-trait RegisterHook[T]:
111111+private[mica] trait RegisterHook[T]:
101112 def preRegister(target: T, key: Identifier)(using registry: Registry[? >: T]): Boolean = true
102113 def postRegister(target: T, key: Identifier)(using registry: Registry[? >: T]): Unit = ()
103114104115private var registrarState: Option[mutable.Buffer[Expr[Unit]]] = Some(mutable.Buffer.empty)
116116+117117+/**
118118+ * Registers the given value into the best registry for it. This macro may only be applied to `object` literals.
119119+ * @param key Combined with a [[ModID]] in scope to create the value's [[Identifier]]
120120+ */
105121@compileTimeOnly("@register is expanded at compile-time")
106122class register(key: String) extends MacroAnnotation:
107123 override def transform(using q: Quotes)(definition: q.reflect.Definition, companion: Option[q.reflect.Definition]): List[q.reflect.Definition] =
···136152def liftOption[T: Type](expr: Option[Expr[T]])(using Quotes): Expr[Option[T]] = expr match
137153 case Some(value) => '{Some(${value})}
138154 case None => '{None}
139139-// exists at runtime
140140-val inits = mutable.Map.empty[String, Any]
141141-@compileTimeOnly("@register is expanded at compile-time")
142155object register:
156156+ /**
157157+ * '''Implementation detail''' used to track [[register]]-blocks. Do not use.
158158+ * @deprecated Do not use.
159159+ */
160160+ private[register] val _inits = mutable.Map.empty[String, Any]
161161+162162+ /**
163163+ * Executes all registrations created by [[register @register]] annotations and [[apply(R):R* register {...}]] blocks.
164164+ * {{{
165165+ * trait Foo derives HasRegistry
166166+ *
167167+ * @register("foo_impl")
168168+ * object FooImpl extends Foo
169169+ *
170170+ * def init() =
171171+ * register()
172172+ * }}}
173173+ */
174174+ @compileTimeOnly("@register is expanded at compile-time")
143175 inline def apply(): Unit = ${ register.apply_impl }
144144- inline def apply[R](value: => R): R = ${ register.apply_impl('value) }
176176+177177+ /**
178178+ * Appends arbitrary code to the registration queue.
179179+ *
180180+ * > [!CAUTION]
181181+ * > If [[value]] references a local scope not visible from the [[apply():Unit* register()]] invocation, the compiler will crash!
182182+ *
183183+ * @param value
184184+ * @tparam R
185185+ * @return
186186+ */
187187+ @compileTimeOnly("@register is expanded at compile-time")
188188+ inline def apply(value: => Unit): Unit = ${ register.apply_impl('value) }
145189 private def apply_impl(using q: Quotes): Expr[Unit] =
146190 import q.reflect.*
147191 registrarState match
···152196 report.error("Registrations have already been processed")
153197 '{()}
154198 end apply_impl
155155- private def apply_impl[R: Type](body: Expr[R])(using q: Quotes): Expr[R] =
199199+ private def apply_impl(body: Expr[Unit])(using q: Quotes): Expr[Unit] =
156200 import q.reflect.*
157201 registrarState match
158202 case Some(l) =>
159203 val name = Symbol.freshName("register")
160160- l += '{ inits(${Expr(name)}) = ${body} }
161161- '{ inits(${Expr(name)}).asInstanceOf[R] }
204204+ l += body
162205 case None =>
163206 report.error("Registrations have already been processed")
164164- '{??? : R}
207207+ '{()}
165208166209trait HasRegistry[T]:
167210 given registryKey: RegistryKey[Registry[T]]
+892-93
src/main/scala/org/net/eu/pool/mica/Mica.scala
···11package org.net.eu.pool.mica
22+import com.mojang.authlib.properties.PropertyMap
23import com.mojang.datafixers.util.Pair
44+import com.mojang.logging.LogUtils
35import com.mojang.serialization
46import com.mojang.serialization.codecs.RecordCodecBuilder
57import com.mojang.serialization.{Codec, DataResult, Decoder, DynamicOps, Encoder, Lifecycle, MapCodec}
88+import it.unimi.dsi.fastutil.longs.{Long2FloatMap, Long2IntMap, Long2IntMaps, Long2IntOpenHashMap, Long2LongMap}
99+import net.minecraft.component.`type`.ProfileComponent
1010+import net.minecraft.component.{ComponentChanges, ComponentType, DataComponentTypes}
1111+import net.minecraft.entity.{Entity, ItemEntity, LivingEntity}
1212+import net.minecraft.entity.attribute.{EntityAttribute, EntityAttributeInstance}
1313+import net.minecraft.entity.decoration.DisplayEntity.TextDisplayEntity
1414+import net.minecraft.entity.effect.{StatusEffectInstance, StatusEffects}
1515+import net.minecraft.entity.player.PlayerEntity
1616+import net.minecraft.item.ItemStack
1717+import net.minecraft.loot.entry.ItemEntry
1818+import net.minecraft.server.world.ServerWorld
1919+import net.minecraft.sound.{SoundCategory, SoundEvent, SoundEvents}
2020+import net.minecraft.state.property.Property
2121+import net.minecraft.text.TextColor
2222+import net.minecraft.util.Uuids
623import net.minecraft.util.shape.VoxelShapes
2424+import net.minecraft.world.World.ExplosionSourceType
2525+import org.net.eu.pool.mica
2626+import org.slf4j.{Logger, LoggerFactory}
2727+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
7282929+import java.util.{Optional, UUID}
830import java.util.stream.LongStream
3131+import scala.::
3232+import scala.NamedTuple.{AnyNamedTuple, NamedTuple}
3333+import scala.Tuple.:*
3434+import scala.annotation.targetName
3535+import scala.collection.immutable.{AbstractSet, SortedSet}
3636+import scala.compiletime.constValue
937import scala.util.boundary
1038// ifversion(>=2100, <[[
1139import net.minecraft.storage.{ReadView, WriteView}
···4270import scala.collection.immutable.{AbstractSeq, LinearSeq}
4371import scala.compiletime.{deferred, erasedValue, summonInline}
4472import scala.deriving.Mirror
7373+import scala.language.experimental.erasedDefinitions
4574import scala.quoted.{Expr, Quotes}
4675import scala.reflect.ClassTag
4776import scala.tools.nsc.io.Path
···49785079private given ModID = ModID("mica")
51805252-val /* <[[ */ minecraft_version: /* ]]> */ Int = minecraft_version
8181+/**
8282+ * Current serial Minecraft version. This is solely provided for IDE completion; it is expanded before compile-time by m4.
8383+ */
8484+private[mica] val /* <[[ */ minecraft_version: /* ]]> */ Int = minecraft_version
8585+8686+/**
8787+ * Represents a value such that the only thing known about it is that it is a member of a typeclass [[C]]. While this
8888+ * doesn't seem useful on the surface, it can be used to represent things similar to Java interfaces - the type
8989+ * `Abstract[ValueType]` could represent any value for which a [[ValueType]] exists, but without requiring the class
9090+ * itself to add to its `implements` clause.
9191+ * @tparam C Typeclass providing the sole known information about the type.
9292+ */
5393trait Abstract[C[_]]:
9494+ /**
9595+ * Real type of [[value]]. Usually, this is used as a path-dependent type - the actual type is rarely known.
9696+ */
5497 type T: C
9898+ /**
9999+ * Backing value of the abstract.
100100+ */
55101 val value: T
102102+103103+/**
104104+ * Helpers for creating and un-creating [[Abstract]] instances.
105105+ */
56106object Abstract:
5757- def apply[C[_], _T: C](x: _T): Abstract[C] = new Abstract[C]:
5858- type T = _T
5959- val value: _T = x
107107+ /**
108108+ * Construct an [[Abstract]] from a value and the proof of [[C]] in scope.
109109+ * @param x Backing value placed in [[Abstract.value]].
110110+ * @tparam C Typeclass for this [[Abstract]] instance.
111111+ * @tparam T Actual type within the [[Abstract]]. The return type is '''not''' narrowed to this type!
112112+ * @return Abstract instance of [[x]] plus the `C[x.type]` proof in scope.
113113+ */
114114+ def apply[C[_], T: C](x: T): Abstract[C] =
115115+ type _T = T
116116+ new Abstract[C]:
117117+ type T = _T
118118+ val value: _T = x
119119+120120+ /**
121121+ * Splits an [[Abstract]] into its [[Abstract.value]] and proof. This is intended to be used with `given`-patterns:
122122+ * {{{
123123+ * val data: Abstract[C] = ???
124124+ * data match
125125+ * case Abstract(value, given _) =>
126126+ * // brings C[value.type] into scope
127127+ * summon[C[value.type]]
128128+ * }}}
129129+ * @param x An [[Abstract]] value to dissect.
130130+ * @tparam C Typeclass to extract from the [[Abstract]].
131131+ * @return Tuple of the [[Abstract]]'s [[Abstract.value]] and associated proof.
132132+ */
60133 def unapply[C[_]](x: Abstract[C]): Some[(x.T, C[x.T])] =
61134 import x.given
62135 Some((x.value, /* WITH THIS TREASURE I */ summon))
···94167extension (w: OutputStream)
95168 def put[T: ByteCodec as c](x: T): Unit = c.write(w, x)
961699797-trait BitCodec[@specialized T]:
170170+private[mica] trait BitCodec[@specialized T]:
98171 def read(r: BitReader): T
99172 def write(w: BitWriter, x: T): Unit
100100-object BitCodec:
173173+private[mica] object BitCodec:
101174 /**
102175 * The [[Unit]] type is serialized as an empty sequence of bits.
103176 */
···148221 val layers: Array[Option[MultipartLayer]] = Array.fill(16)(None)
149222150223// divert
224224+class immutable extends Annotation
225225+226226+/**
227227+ * A '''side effect''' allows a [[Rune]] to have a greater effect on the world than mere arithmetic. Whereas runes
228228+ * don't have access to a [[ServerWorld]], side effects ''do'', letting them read from and even write to the world.
229229+ * However, they are less flexible than normal values - an execution ''must'' return a side effect to have it executed.
230230+ */
231231+trait SideEffect derives HasRegistry:
232232+ /**
233233+ * Arbitrary data for your side effect to use. For example, an effect that replaces a block would store the target
234234+ * [[BlockPos]], as well as the new [[Block]].
235235+ */
236236+ type Data
237237+ given dataCodec: Codec[Data] = deferred
238238+239239+ /**
240240+ * Return value exposed to Mica code. This could be information such as a position (for a read-only effect), or
241241+ * information about changes made (for example, the old [[Block]]). In the case you have ''nothing'' valuable to
242242+ * return, use [[Unit]].
243243+ */
244244+ type Return: ValueType
245245+ given returnType: ValueType[Return] = deferred
246246+247247+ /**
248248+ * Arbitrary data your side effect might need in [[unexecute]], in addition to your return value. This data is never
249249+ * written to disk, so can be anything. For example, the aforemented replace-block effect could store the associated
250250+ * [[BlockEntity]] of the block.
251251+ */
252252+ type RevertData
253253+254254+ /**
255255+ * Check whether your side effect applies to the world ''before'' any changes. '''This method must not mutate `world`!'''
256256+ * @param data Arbitrary [[Data]] to use (such as a target position, etc.) in your cast.
257257+ * @param world Reference world for checking whether effects apply. '''Do not mutate this world!'''
258258+ */
259259+ @immutable def check(data: Data)(using @immutable world: World @immutable): Unit
260260+ @immutable def check(data: Data, player: PlayerEntity)(using @immutable world: World @immutable): Unit = check(data)
261261+262262+ /**
263263+ * Apply your side effect to the world. This method should be reversible by [[unexecute]], in case a later effect's [[execute]] throws. However, avoid throwing in this method when possible - validate your effect in [[check]] instead.
264264+ * @param data Arbitrary [[Data]] to use (such as a target position, etc.) in your cast.
265265+ * @param world World to apply effects to; the same world as passed to [[check]].
266266+ * @return Information for reversing the effect. Spells should provide reversion data if possible.
267267+ */
268268+ def execute(data: Data)(using world: World): (Return, RevertData)
269269+270270+ /**
271271+ * Apply a side effect that requires a player. This method is otherwise identical to [[execute(data:SideEffect)* the playerless version]] - keep its contract in mind.
272272+ * @param player Player initiating the cast.
273273+ */
274274+ def execute(data: Data, player: PlayerEntity)(using World): (Return, RevertData) = execute(data)
275275+276276+ /**
277277+ * Un-apply your side effect, if possible. This method is rarely called, only in the case [[execute]] breaks its contract. However, it's safest to implement it if you can.
278278+ * @param data The same [[Data]] that [[execute]] was called with.
279279+ * @param return The value returned by [[execute]].
280280+ * @param revert The revert data returned by [[execute]].
281281+ * @param world World to reverse this effect in; the same world as [[execute]].
282282+ */
283283+ def unexecute(data: Data, `return`: Return, revert: RevertData)(using world: World): Unit
284284+285285+ def translationFields(data: Data): Seq[AnyRef] = Seq()
286286+287287+@register("noop")
288288+object NoopEffect extends SideEffect:
289289+ override type Data = Unit
290290+ override type Return = Unit
291291+ override type RevertData = Unit
292292+293293+ override def check(data: Unit)(using world: World): Unit = ()
294294+ override def execute(data: Unit)(using world: World): (Unit, Unit) = ((), ())
295295+ override def unexecute(data: Unit, `return`: Unit, revert: Unit)(using world: World): Unit = ()
296296+297297+given unitValueType: ValueType[Unit]:
298298+ override def eq[U: ValueType as v](x: Unit, y: U): Boolean = v eq unitValueType
299299+ override def show(x: Unit): Text = Text.literal("Unit").styled(_.withColor(0x7a7a7a))
300300+301301+// divert(-1)
151302trait ByteCodec[T]:
152303 def read(r: DataInput): T
153304 def write(w: DataOutput, x: T): Unit
154154-// divert(-1)
155305156306given ByteCodec[MultipartSection]:
157307 override def read(r: InputStream): MultipartSection =
···181331 new ByteCodec[T]:
182332 override def read(r: InputStream): T = c.read(BitReader(r))
183333 override def write(w: OutputStream, x: T): Unit = c.write(BitWriter(w), x)
334334+335335+class sparse extends Annotation
336336+184337// divert
185185-object RuneShift:
186186- opaque type RuneShift = Int
187187- inline def apply(value: Int): RuneShift = value
188188- def apply(x: Int, y: Int, z: Int, facing: Direction): RuneShift = x & 0b11 | y << 2 & 0b11100 | z << 5 & 0b1100000 | facing.ordinal << 7
189189- private def x_impl(s: Expr[RuneShift], x: Expr[Int])(using q: Quotes): Expr[Unit] =
190190- import q.reflect.*
191191- Assign(s.asTerm, '{${s}(x = ${x})}.asTerm).asExprOf[Unit]
192192- private def y_impl(s: Expr[RuneShift], y: Expr[Int])(using q: Quotes): Expr[Unit] =
193193- import q.reflect.*
194194- Assign(s.asTerm, '{${s}(y = ${y})}.asTerm).asExprOf[Unit]
195195- private def z_impl(s: Expr[RuneShift], z: Expr[Int])(using q: Quotes): Expr[Unit] =
196196- import q.reflect.*
197197- Assign(s.asTerm, '{${s}(z = ${z})}.asTerm).asExprOf[Unit]
198198- given ops: AnyRef:
199199- extension (s: RuneShift)
200200- @inline def value: Int = s
201201- @inline def x = s & 0b11
202202- @inline def y = s >> 2 & 0b111
203203- @inline def z = s >> 5 & 0b11
204204- @inline def facing = Direction.values()(s >> 7 & 0b111)
205205- @inline def apply(x: Int = s.x, y: Int = s.y, z: Int = s.z, facing: Direction = s.facing): RuneShift = RuneShift(x, y, z, facing)
206206- def storage(using w: World) = w.getComponent(AbstractRuneStorage.keys(s.value))
207207- private def facing_impl(s: Expr[RuneShift], facing: Expr[Direction])(using q: Quotes): Expr[Unit] =
208208- import q.reflect.*
209209- Assign(s.asTerm, '{${s}(facing = ${facing})}.asTerm).asExprOf[Unit]
210210- val shapeCache: Array[VoxelShape] = Array.ofDim[VoxelShape](768).tap: m =>
211211- for shift: RuneShift <- 0 until 768 do
212212- val middle = (shift.x / 4.0 - 0.5, shift.y / 8.0 - 0.5, shift.z / 4.0 - 0.5)
213213- m(shift) = VoxelShapes.cuboid(middle._1 - .25, middle._2, middle._3 - .25, middle._1 + .25, middle._2 + .0625, middle._3 + .25)
214214-end RuneShift
215215-export RuneShift.RuneShift
338338+339339+def assumed[T, U]: T =:= U = summon[Int =:= Int].asInstanceOf[T =:= U]
340340+341341+inline given tupleCodec: [T <: Tuple] => Codec[T] = compiletime.summonFrom:
342342+ case ev: (T =:= EmptyTuple) => ev.substituteContra(Codec.unit(EmptyTuple))
343343+ case ev: (T =:= (h *: t)) => ev.substituteContra(Codec.pair(summonInline[Codec[h]], tupleCodec[t]).xmap(p => p.getFirst *: p.getSecond, { case h *: t => Pair(h, t) }))
216344217217-class sparse extends Annotation
345345+// divert(-1)
218346219347extension [T] (i: Iterator[T]) def dropAfter(cond: T => Boolean): Iterator[T] =
220348 val split = i.span(!cond(_))
···246374 h.foreach(w.write)
247375 w.write(t | STOP_BIT)
248376 case _ => w.write(STOP_BIT)
377377+// divert
378378+379379+@register("need_side_effect")
380380+object NeedSideEffectFrame extends ThunkFrame:
381381+ override type Data = Unit
382382+ override def accept[T: ValueType as ty](data: Unit, value: T): BoxedThunk =
383383+ ty.cast[BoxedSideEffect](value) match
384384+ case Some(e) => BoxedThunk(GotSideEffectFrame, e)
385385+386386+@register("got_side_effect")
387387+object GotSideEffectFrame extends ThunkFrame:
388388+ override type Data = BoxedSideEffect
389389+ override def accept[T: ValueType](data: BoxedSideEffect, value: T): BoxedThunk =
390390+ //throw RuneError(Text.literal("Too many side effects in one cast"))
391391+ ???
392392+def findRunes(start: RuneRef, prev: Option[RuneRef])(using World): Option[List[(Rune, RuneRef)]] =
393393+ logger.debug(s"findRunes ${start} ${prev}")
394394+ if start.isEmpty then
395395+ None
396396+ else
397397+ val neigh = (start.rune.computeNeighbors(using start) -- prev).flatMap(findRunes(_, Some(start))).toSeq
398398+ neigh match
399399+ case Seq() =>
400400+ logger.debug(s"\tfindRunes ${start} ${prev}, neigh = ${neigh} | no more neighbors; stop here.")
401401+ Some(List((start.rune, start)))
402402+ case Seq(x) =>
403403+ val l = (start.rune, start)::x
404404+ logger.debug(s"\tfindRunes ${start} ${prev}, neigh = ${neigh} | ${l}")
405405+ Some(l)
406406+ case _ =>
407407+ logger.debug(s"\tfindRunes ${start} ${prev}, neigh = ${neigh} | too many neighbors!")
408408+ None
409409+410410+given logger: Logger = LoggerFactory.getLogger(given_ModID.name)
411411+412412+def parseRunes(runes: List[(Rune, RuneRef)])(using World): List[BoxedRune] =
413413+ runes match
414414+ case (head, given RuneRef)::next =>
415415+ val (data, rest) = head.read(next)
416416+ BoxedRune(head, data)::parseRunes(rest)
417417+ case Nil => Nil
418418+419419+@tailrec
420420+def interpretRunes(runes: List[BoxedRune], stack: BoxedThunk): BoxedThunk =
421421+ println(s"Interpreter: runes = $runes, stack = $stack")
422422+ runes match
423423+ case head::next =>
424424+ val newStack = head.rune.execute(head.data, stack)
425425+ println(s"-> $head, stack = $newStack, $next")
426426+ interpretRunes(next, newStack)
427427+ case Nil =>
428428+ println("interpreter finished.")
429429+ stack
430430+431431+@throws[RuneError]
432432+def interpretForSideEffect(runes: List[BoxedRune]): Option[BoxedSideEffect] =
433433+ val ret = interpretRunes(runes, BoxedThunk(NeedSideEffectFrame, ()))
434434+ ret.narrow(GotSideEffectFrame) match
435435+ case Some(value) => Some(value.data)
436436+ case None =>
437437+ println(s"Nothing seems to happen (top of stack is ${ret})")
438438+ None
439439+440440+//given emptyTupleCodec: MapCodec[EmptyTuple] = MapCodec.unit(EmptyTuple)
441441+//inline given namedTupleCodec: [K <: String & Singleton, V: Codec as codec, R <: Tuple: MapCodec as tail] => MapCodec[(K, V) *: R] =
442442+// RecordCodecBuilder.mapCodec[(K, V) *: R]: i =>
443443+// i.group[V, R](
444444+// codec.fieldOf(constValue[K]).forGetter { case h *: _ => h._2 },
445445+// tail.forGetter { case _ *: t => t },
446446+// ).apply(i, (h, t) => (constValue[K], h) *: t)
447447+448448+extension [T1, T2, R1, R2] (ev$1: T1 =:= R1) def zip(ev$2: T2 =:= R2): (T1, T2) =:= (R1, R2) = assumed
449449+extension [T1, T2, R1, R2] (ev: (T1, T2) =:= (R1, R2)) def unzip: (T1 =:= R1, T2 =:= R2) = (assumed, assumed)
450450+451451+//inline given namedTupleCodec: [N <: Tuple, T <: Tuple] => MapCodec[NamedTuple[N, T]] =
452452+// compiletime.summonFrom:
453453+// case ev: ((N, T) =:= (EmptyTuple, EmptyTuple)) => MapCodec.unit(NamedTuple.Empty.asInstanceOf)
454454+// case ev: ((N, T) =:= (k *: ks, v *: vs)) =>
455455+// erased val ev2 = summonInline[k <:< (String & Singleton & k)]
456456+// val name = constValue[k].asInstanceOf[String]
457457+// RecordCodecBuilder.mapCodec[NamedTuple[k *: ks, v *: vs]]: i =>
458458+// i.group[v, vs](
459459+// summonInline[Codec[v]].fieldOf(name).forGetter(???),
460460+// namedTupleCodec[ks, vs].forGetter(???)
461461+// ).apply(i, (h, t) => (name, h) *: t)
462462+463463+//erased def namedTupleCodecTest: MapCodec[((a: Int, b: String))] = summon
249464250465/**
251251- * Exception for when a [[Rune]] cannot fully read its arguments.
466466+ * Exception related to [[Rune]] evaluation, including the position.
252467 *
253253- * @param distance The distance from the *end* the error was thrown.
254254- * @param message
468468+ * @param message Cause of the error, displayed to the caster.
469469+ * @param ref Location of the rune that threw the error.
255470 */
256256-case class RunesParseError(distance: Int, message: Text) extends Exception
471471+case class RuneError(message: Text)(using val ref: RuneRef) extends Exception
257472473473+/**
474474+ * Stores a [[Rune]] together with its [[Rune.Data associated data]]. This allows easily passing around heterogeneous parsed rune values.
475475+ * @param rune The rune stored in this pair (responsible for determining [[data]]'s type).
476476+ * @param data The extra data associated with [[Rune]].
477477+ */
258478case class BoxedRune(rune: Rune, data: rune.Data)
259259-inline given Codec[BoxedRune] =
260260- summonUnlessSeeding[Codec[Rune]]
479479+inline given `dispatch codec for boxed runes`: Codec[BoxedRune] =
480480+ summon[Codec[Rune]]
261481 .dispatch[BoxedRune](_.rune, (r: Rune) =>
262482 import r.given
263483 summon[Codec[r.Data]].xmap(BoxedRune(r, _),
···267487 .fieldOf("value")
268488 // ]]>,)
269489 )
490490+inline given `dispatch codec for boxed values`: Codec[BoxedValue] =
491491+ summon[Codec[ValueType[?]]]
492492+ .dispatch[BoxedValue](_.given_ValueType_T, {
493493+ case r: ValueType[t] =>
494494+ given ValueType[t] = r
495495+ r.codec.xmap(BoxedValue(_)(using r), _.value.asInstanceOf[t])
496496+ // ifversion(>= 2100,<[[
497497+ .fieldOf("value")
498498+ // ]]>,)
499499+ })
500500+given `dispatch codec for boxed thunk frames`: Codec[BoxedThunk] =
501501+ summon[Codec[ThunkFrame]]
502502+ .dispatch[BoxedThunk](_.tag, (r: ThunkFrame) =>
503503+ import r.given
504504+ summon[Codec[r.Data]].xmap(BoxedThunk(r, _),
505505+ // FIXME: Rewrite [dispatch] to avoid this unsafe cast.
506506+ _.data.asInstanceOf[r.Data])
507507+ // ifversion(>= 2100,<[[
508508+ .fieldOf("value")
509509+ // ]]>,)
510510+ )
511511+given `dispatch codec for boxed side-effects`: Codec[BoxedSideEffect] =
512512+ summon[Codec[SideEffect]]
513513+ .dispatch[BoxedSideEffect](_.effect, (r: SideEffect) =>
514514+ import r.given
515515+ summon[Codec[r.Data]].xmap(BoxedSideEffect(r, _),
516516+ // FIXME: Rewrite [dispatch] to avoid this unsafe cast.
517517+ _.data.asInstanceOf[r.Data])
518518+ // ifversion(>= 2100,<[[
519519+ .fieldOf("value")
520520+ // ]]>,)
521521+ )
270522523523+/**
524524+ * Stores a [[ThunkFrame]] together with its [[ThunkFrame.Data associated data]] on the stack.
525525+ * @param tag Type of this frame (must be registered).
526526+ * @param data Extra data for this frame (e.g. already-collected arguments).
527527+ */
528528+case class BoxedThunk(tag: ThunkFrame, data: tag.Data):
529529+ def narrow(to: ThunkFrame): Option[BoxedThunk { val tag: to.type; }] =
530530+ if tag eq to then
531531+ Some(asInstanceOf[BoxedThunk { val tag: to.type; }])
532532+ else
533533+ None
534534+object BoxedThunk:
535535+ inline def unapply[T](box: BoxedThunk): Option[(? <: Singleton & ThunkFrame { type Data = T; }, T)] =
536536+ ???
537537+538538+trait BoxedValue:
539539+ type T: ValueType
540540+ val value: T
541541+ def ==(other: BoxedValue): Boolean =
542542+ import other.given
543543+ summon[ValueType[T]].eq(value, other.value)
544544+ def cast[R: {ValueType, ClassTag}]: Option[R] = summon[ValueType[T]].cast(value)
545545+ def show: Text = summon[ValueType[T]].show(value)
546546+object BoxedValue:
547547+ def apply[T: ValueType](value: T): BoxedValue =
548548+ type _T = T
549549+ val _value = value
550550+ new BoxedValue:
551551+ override type T = _T
552552+ override val value: _T = _value
271553//def dependentCodec[T, R](arg: Codec[T], rhs: [R] )
272554555555+// y'know, I should really make a generic Boxed type
556556+case class BoxedSideEffect(effect: SideEffect, data: effect.Data)
557557+558558+// divert(-1)
273559/**
274560 * Stores additional data out-of-band with existing world chunks. This is useful if you want to e.g. manage chunk loading independently, or
275561 */
···277563278564//object ExplodeRune extends Rune:
279565// type Data = (BlockPos, Int)--
566566+567567+// divert
280568281569/**
282282- * Does nothing. Pushes and pops no frames, consumes no runes, etc.
570570+ * Does nothing. Pushes and pops no frames, consumes no runes, etc. Used as a placeholder for missing runes.
283571 */
284572@register("empty")
285285-object EmptyRune extends Rune:
286286- override type Data = Unit
573573+object EmptyRune extends SimpleRune:
287574 override def surfaceSprite: Identifier = Rune.BASIC_TEXTURE
288288- override def read(rhs: List[Rune]): (Unit, List[Rune]) = ((), rhs)
289289- override def execute(data: Unit, frame: ThunkFrame): ThunkFrame = frame
575575+ override def execute(frame: BoxedThunk): BoxedThunk = frame
576576+ register:
577577+ item.register()
290578291579@register("quote")
292580object QuoteRune extends Rune:
293293- override type Data = Seq[BoxedRune]
581581+ override type Data = Seq[Rune]
294582 override def surfaceSprite: Identifier = Rune.BASIC_TEXTURE
295295- override def read(rhs: List[Rune]): (Seq[BoxedRune], List[Rune]) =
296296- @throws[RunesParseError]
297297- def worker(rhs: List[Rune]): (List[BoxedRune], List[Rune]) =
298298- if rhs.isEmpty then
299299- throw RunesParseError(0, Text.literal("Quote with no corresponding unquote"))
300300- else if rhs.head == EndQuoteRune then
301301- (List.empty, rhs)
302302- else
303303- val r = rhs.head
304304- val (d, t) = r.read(rhs.tail)
305305- val (b, t2) = try
306306- worker(t)
307307- catch
308308- // FIXME: this +1 is going to bite me, isn't it?
309309- case RunesParseError(distance, message) => throw RunesParseError(distance + 1, message)
310310- locally:
311311- (BoxedRune(r, d)::b, t2)
583583+ override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (Seq[Rune], List[(Rune, RuneRef)]) =
584584+ @throws[RuneError]
585585+ def worker(rhs: List[(Rune, RuneRef)])(using ref: RuneRef): (List[Rune], List[(Rune, RuneRef)]) =
586586+ rhs match
587587+ case Nil => throw RuneError(Text.literal("Quote with no corresponding unquote"))
588588+ case (QuoteRune, given RuneRef)::xs =>
589589+ val (t, xt) = worker(xs)
590590+ val (u, xu) = worker(xt)
591591+ (t:::EndQuoteRune::u, xu)
592592+ case (EndQuoteRune, r)::xs => (Nil, xs)
593593+ case x::xs =>
594594+ val t = worker(xs)
595595+ (x._1::t._1, t._2)
312596 worker(rhs)
313313- override def execute(data: Seq[BoxedRune], frame: ThunkFrame): ThunkFrame = ???
597597+ override def execute(data: Seq[Rune], frame: BoxedThunk): BoxedThunk =
598598+ frame.tag.accept(frame.data, data.map(BoxedValue(_)))
599599+ register:
600600+ item.register()
314601602602+/**
603603+ * The registry key of the [[EmptyRune]]. This should be the default rune.
604604+ */
315605lazy val emptyRuneKey = summon[Registry[Rune]].getId(EmptyRune)
316316-sealed trait ConcreteRuneStorage extends AbstractRuneStorage:
606606+private[mica] sealed trait ConcreteRuneStorage extends AbstractRuneStorage:
317607 // ifversion(>=2100, <[[
318608 override def readData(c: ReadView): Unit =
319609 // ]]>, <[[
···321611 // ]]>)
322612 if c.getBoolean("m", true) then
323613 contents.clear()
614614+ heap0.clear()
615615+ heap1.clear()
616616+ heap2.clear()
617617+ heap3.clear()
324618 val n = c.getLong("c", -1L)
325619 if n != -1 then
326620 for
327327- i <- 0L until c.getLong("c", 0L)
621621+ i <- 0L until n
328622 k = c.getLong(s"k$i", 0L)
329623 v = c.getInt(s"v$i", 0)
330624 p = c.getString(s"p$v", emptyRuneKey.toString)
331625 r <- Option.fromNullable(Identifier.tryParse(p))
332626 do
333333- contents(k) = summonUnlessSeeding[Registry[Rune]].get(r)
627627+ contents(k) = summon[Registry[Rune]].get(r)
334628 else
335629 boundary:
336630 var i = 0
···338632 // ifversion(>=2100, <[[
339633 val dh = c.getOptionalIntArray(s"h$i")
340634 val dl = c.getOptionalIntArray(s"d$i")
635635+ val dx0 = c.getOptionalIntArray(s"x${i}0").orElse(Array.emptyIntArray)
636636+ val dx1 = c.getOptionalIntArray(s"x${i}1").orElse(Array.emptyIntArray)
637637+ val dx2 = c.getOptionalIntArray(s"x${i}2").orElse(Array.emptyIntArray)
638638+ val dx3 = c.getOptionalIntArray(s"x${i}3").orElse(Array.emptyIntArray)
341639 // ]]>, <[[
342640 val dh = c.getIntArray(s"h$i")
343641 val dl = c.getIntArray(s"d$i")
642642+ val dx0 = c.getIntArray(s"x${i}0").orElse(Array.emptyIntArray)
643643+ val dx1 = c.getIntArray(s"x${i}1").orElse(Array.emptyIntArray)
644644+ val dx2 = c.getIntArray(s"x${i}2").orElse(Array.emptyIntArray)
645645+ val dx3 = c.getIntArray(s"x${i}3").orElse(Array.emptyIntArray)
344646 // ]]>)
345647 if dh.isEmpty || dl.isEmpty then boundary.break()
346648 val rune = registryFor[Rune].get(Identifier.of(
···348650 c.getString(s"p$i", emptyRuneKey.getPath),
349651 ))
350652 if rune != null then
351351- for (h: Int, l: Int) <- dh.get.zip(dl.get) do
352352- val k: Long = (h.toLong << 32) | (l & 0xFFFFFFFFL);
353353- contents.put(k, rune);
653653+ for ((h: Int, l: Int), i: Int) <- dh.get.zip(dl.get).zipWithIndex do
654654+ val k: Long = packLong(h, l);
655655+ contents.put(k, rune)
656656+ if dx0.length > i then heap0(k) = dx0(i)
657657+ if dx1.length > i then heap1(k) = dx1(i)
658658+ if dx2.length > i then heap2(k) = dx2(i)
659659+ if dx3.length > i then heap3(k) = dx3(i)
354660 i += 1
355661 // ifversion(>=2100, <[[
356662 override def writeData(c: WriteView): Unit =
···363669 val ns = registryFor[Rune].getId(k)
364670 c.putString(s"n$i", ns.getNamespace)
365671 c.putString(s"p$i", ns.getPath)
366366- val (h, l) = v.map(n => ((n >>> 32).toInt, n.toInt)).unzip
672672+ val (h, l) = v.map(unpackLong(_)).unzip
367673 c.putIntArray(s"h$i", h.toArray)
368674 c.putIntArray(s"d$i", l.toArray)
675675+ val (p1, p2) = (for x <- v yield ((heap0(x), heap1(x)), (heap2(x), heap3(x)))).unzip
676676+ val (x0, x1) = p1.unzip; val (x2, x3) = p2.unzip
677677+ c.putIntArray(s"x${i}0", x0.toArray.map(x => if x == null then 0 else x.intValue))
678678+ c.putIntArray(s"x${i}1", x1.toArray.map(x => if x == null then 0 else x.intValue))
679679+ c.putIntArray(s"x${i}2", x2.toArray.map(x => if x == null then 0 else x.intValue))
680680+ c.putIntArray(s"x${i}3", x3.toArray.map(x => if x == null then 0 else x.intValue))
369681 i += 1
682682+683683+@register("double")
684684+object DoubleLiteralRune extends Rune:
685685+ override type Data = Double
686686+ override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (Double, List[(Rune, RuneRef)]) = (java.lang.Double.longBitsToDouble(packLong(ref.heap0, ref.heap1)), rhs)
687687+ override def execute(data: Double, frame: BoxedThunk): BoxedThunk = frame.tag.accept(frame.data, data)
688688+ register:
689689+ DoubleLiteralRune.item.register()
690690+370691// forloop(n, 0, 767, <[[
371692// pushdef(RuneStorage, <[[ifelse($#,0,<[[<[[$0]]>]]><[[n]]>,<[[$0]]><[[<[[(]]>]]><[[$@]]><[[)]]>)]]>)
372372-case class RuneStorage(world: World, contents: Long2ObjectMap[Rune]) extends ConcreteRuneStorage:
693693+private[mica] case class RuneStorage(world: World,
694694+ contents: Long2ObjectMap[Rune] = Duck.mkMap(),
695695+ heap0: Long2IntMap = Long2IntOpenHashMap(),
696696+ heap1: Long2IntMap = Long2IntOpenHashMap(),
697697+ heap2: Long2IntMap = Long2IntOpenHashMap(),
698698+ heap3: Long2IntMap = Long2IntOpenHashMap()
699699+ ) extends ConcreteRuneStorage:
373700 override type Concrete = RuneStorage
374701 contents.defaultReturnValue(EmptyRune)
375375-object RuneStorage:
702702+private[mica] object RuneStorage:
376703 val shift = RuneShift(n)
377704 given key: ComponentKey[RuneStorage] = ComponentRegistry.getOrCreate(Identifier.of("mica", "runes<[[]]>n"), classOf[RuneStorage])
378705 // divert(1)
379706 /*dnl*/val factories: WorldComponentFactoryRegistry = erasedValue/*
380380-*/Duck.register(factories, RuneStorage.key, classOf[RuneStorage], RuneStorage(_, Duck.mkMap()))
707707+*/Duck.register(factories, RuneStorage.key, classOf[RuneStorage], RuneStorage(_))
381708 AbstractRuneStorage.keys(n) = RuneStorage.key
382709 // divert
383710// popdef(<[[RuneStorage]]>)
711711+// ]]>)
712712+// forloop(n, 0, 63, <[[
713713+// pushdef(FluxStorageInfo, <[[ifelse($#,0,<[[<[[$0]]>]]><[[n]]>,<[[$0]]><[[<[[(]]>]]><[[$@]]><[[)]]>)]]>)
714714+private[mica] case class FluxStorageInfo(world: World, contents: Long2FloatMap) extends Component:
715715+ override def readData(readView: ReadView): Unit = ???
716716+ override def writeData(writeView: WriteView): Unit = ???
717717+// divert
718718+// popdef(<[[FluxStorageInfo]]>)
384719// ]]>)
385720721721+given ValueType[Double]:
722722+ override def eq[U: ValueType as v](x: Double, y: U): Boolean = v.cast[Double](y).exists(y => x =~ y)
723723+ override def cast[R: ClassTag as r](x: Double): Option[R] =
724724+ r match
725725+ case ClassTag.Double => Some(x)
726726+ case ClassTag.Int =>
727727+ if x =~ x.round.toInt then
728728+ Some(x.round.toInt)
729729+ else
730730+ None
731731+ case _ => None
732732+733733+ override def show(x: Double): Text = Text.literal(s"$x").styled(_.withColor(0xab3900))
734734+735735+class RuneBeStartinShit private(val direction: Direction) extends Rune:
736736+ override def surfaceSprite: Identifier = Rune.IMPETUS_TEXTURE
737737+ override type Data = Unit
738738+ override def read(rhs: List[(Rune, RuneRef)])(using RuneRef, World): (Unit, List[(Rune, RuneRef)]) = ((), rhs)
739739+ override def execute(data: Unit, frame: BoxedThunk): BoxedThunk = frame
740740+ override def computeNeighbors(using r: RuneRef): Set[RuneRef] = direction match
741741+ case Direction.NORTH => Set(r.north.north)
742742+ case Direction.SOUTH => Set(r.south.south)
743743+ case Direction.WEST => Set(r.west.west)
744744+ case Direction.EAST => Set(r.east.east)
745745+ def register(): Unit =
746746+ Registry.register(registryFor[Rune], Identifier.of("mica", s"arrow_$direction"), this)
747747+ item.register()
748748+object RuneBeStartinShit:
749749+ val north = new RuneBeStartinShit(Direction.NORTH)
750750+ val east = new RuneBeStartinShit(Direction.EAST)
751751+ val south = new RuneBeStartinShit(Direction.SOUTH)
752752+ val west = new RuneBeStartinShit(Direction.WEST)
753753+ def apply(dir: Direction) = dir match
754754+ case Direction.NORTH => north
755755+ case Direction.SOUTH => east
756756+ case Direction.WEST => south
757757+ case Direction.EAST => west
758758+ def unapply(r: RuneBeStartinShit): Some[Direction] = Some(r.direction)
759759+ register:
760760+ RuneBeStartinShit.north.register()
761761+ RuneBeStartinShit.east.register()
762762+ RuneBeStartinShit.south.register()
763763+ RuneBeStartinShit.west.register()
764764+765765+@register("seq")
766766+given ValueType[Seq[BoxedValue]]:
767767+ val color = TextColor.fromRgb(0xb6fc03)
768768+ override def eq[U: ValueType as v](x: Seq[BoxedValue], y: U): Boolean =
769769+ v.cast[Seq[BoxedValue]](y).exists(y => x.zip(y).forall(p => p._1 == p._2))
770770+ override def show(x: Seq[BoxedValue]): Text =
771771+ x match
772772+ case h+:t =>
773773+ val m = Text.literal("(").styled(_.withColor(color))
774774+ m.append(h.show)
775775+ for x <- t do
776776+ m.append(" ").append(x.show)
777777+ m.append(")")
778778+ case Seq() => Text.literal("()").styled(_.withColor(color))
779779+given ValueType[Rune]:
780780+ val color = TextColor.fromRgb(0xffe46e)
781781+ override def eq[U: ValueType as v](x: Rune, y: U): Boolean = v.cast[Rune](y).exists(y => x eq y)
782782+ override def show(x: Rune): Text = Text.literal(registryFor[Rune].getId(x).toTranslationKey("mica.runes"))
783783+784784+@register("fatboy_slim")
785785+object PosLiteral extends Rune:
786786+ override type Data = RuneRef
787787+ override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (RuneRef, List[(Rune, RuneRef)]) = (ref, rhs)
788788+ override def execute(data: RuneRef, frame: BoxedThunk): BoxedThunk = frame.tag.accept(frame.data, data.floatPos)
789789+ register:
790790+ PosLiteral.item.register()
791791+792792+@register("teleport")
793793+object TeleportRune extends Rune:
794794+ override type Data = RuneRef
795795+ @register("teleport")
796796+ object Effect extends SideEffect:
797797+ override type Data = (RuneRef, EntityRef)
798798+ override type Return = Unit
799799+ override type RevertData = Vec3d
800800+ override def check(data: (RuneRef, EntityRef))(using world: World): Unit =
801801+ given RuneRef = data._1
802802+ if !world.getWorldBorder.contains(data._1.pos) then
803803+ throw RuneError(Text.literal("Cannot teleport outside the world border"))
804804+ override def execute(data: (RuneRef, EntityRef))(using world: World): (Unit, Vec3d) =
805805+ val entity = data._2.entity
806806+ if entity == null then
807807+ throw RuneError(Text.literal("Could not find ").append(summon[ValueType[EntityRef]].show(data._2)).append(" in the world"))(using data._1)
808808+ val pos = entity.getPos
809809+ entity.setPosition(data._1.floatPos)
810810+ ((), pos)
811811+ override def unexecute(data: (RuneRef, EntityRef), `return`: Unit, revert: Vec3d)(using world: World): Unit =
812812+ data._2.entity.setPosition(revert)
813813+ @register("teleport/0")
814814+ object Frame extends ThunkFrame:
815815+ override type Data = (RuneRef, BoxedThunk)
816816+ override def accept[T: ValueType as v](data: (RuneRef, BoxedThunk), value: T): BoxedThunk =
817817+ val entity = v.cast[EntityRef](value).get
818818+ println(data)
819819+ data._2.tag.accept(data._2.data, BoxedSideEffect(Effect, (data._1, entity)))
820820+ override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (RuneRef, List[(Rune, RuneRef)]) = (ref, rhs)
821821+ override def execute(data: RuneRef, frame: BoxedThunk): BoxedThunk = BoxedThunk(Frame, (data, frame))
822822+ register:
823823+ TeleportRune.item.register()
824824+given Codec[RuneRef] = RecordCodecBuilder.create: i =>
825825+ i.group(
826826+ Codec.LONG.fieldOf("pos").xmap(BlockPos.fromLong, _.asLong).forGetter(_.pos),
827827+ Codec.INT.fieldOf("dir").xmap(RuneShift.apply, _.value).forGetter(_.shift),
828828+ ).apply(i, RuneRef(_, _))
829829+given Codec[((RuneRef, EntityRef))] = RecordCodecBuilder.create: i =>
830830+ i.group(
831831+ given_Codec_RuneRef.fieldOf("to").forGetter(_._1),
832832+ given_Codec_EntityRef.fieldOf("from").forGetter(_._2),
833833+ ).apply(i, (_, _))
834834+given Codec[((RuneRef, BoxedThunk))] = RecordCodecBuilder.create: i =>
835835+ i.group(
836836+ given_Codec_RuneRef.fieldOf("to").forGetter(_._1),
837837+ `dispatch codec for boxed thunk frames`.fieldOf("from").forGetter(_._2),
838838+ ).apply(i, (_, _))
839839+840840+given `given_Codec_java.lang.Double`: Codec[java.lang.Double] = Codec.DOUBLE
841841+842842+@register("reveal")
843843+private[mica] object RevealRune extends Rune:
844844+ override type Data = RuneRef
845845+ @register("reveal/0")
846846+ object Frame extends ThunkFrame:
847847+ override type Data = (RuneRef, BoxedThunk)
848848+ override def accept[T: ValueType](data: (RuneRef, BoxedThunk), value: T): BoxedThunk = ???
849849+ given Codec[(RuneRef, BoxedThunk)] =
850850+ RecordCodecBuilder.create: i =>
851851+ i.group(
852852+ codec[RuneRef].fieldOf("_1").forGetter(_._1),
853853+ codec[BoxedThunk].fieldOf("_2").forGetter(_._2),
854854+ ).apply(i, (_, _))
855855+ override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (RevealRune.Data, List[(Rune, RuneRef)]) = (summon, rhs)
856856+ override def execute(data: RuneRef, frame: BoxedThunk): BoxedThunk = BoxedThunk(Frame, (data, frame))
857857+ register:
858858+ RevealRune.item.register()
859859+860860+private[mica] object JackBlack:
861861+ def pickaxe(using ci: CallbackInfoReturnable[ActionResult])(ctx: ItemUsageContext): Unit =
862862+ val p = ctx.getHitPos
863863+ val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt)
864864+ val b = BlockPos.Mutable(p.x, p.y, p.z)
865865+ given World = ctx.getWorld
866866+ if q.getX == 4 then
867867+ q.setX(0)
868868+ b.setX(b.getX + 1)
869869+ if q.getY == 8 then
870870+ q.setY(0)
871871+ b.setY(b.getY + 1)
872872+ if q.getZ == 4 then
873873+ q.setZ(0)
874874+ b.setZ(b.getZ + 1)
875875+ val h = RuneShift(q.getX, q.getY, q.getZ, ctx.getSide)
876876+ val ref = RuneRef(b, h)
877877+ if !ref.isEmpty then
878878+ if !given_World.isClient then
879879+ val stack = ItemStack(ref.rune.item.value, 1)
880880+ assert(!stack.isEmpty)
881881+ assert(Registries.ITEM.getId(stack.getItem) != null)
882882+ stack.applyChanges(ComponentChanges.builder().add(DataComponentTypes.PROFILE,
883883+ ProfileComponent(
884884+ Optional.empty,
885885+ Optional.of(Uuids.toUuid(Array(ref.heap0, ref.heap1, ref.heap2, ref.heap3))),
886886+ PropertyMap())).build())
887887+ val ent: ItemEntity = ItemEntity(given_World, p.x, p.y, p.z, stack)
888888+ ent.setPosition(p)
889889+ ent.addVelocity(h.facing.getDoubleVector.multiply(0.1))
890890+ given_World.spawnEntity(ent)
891891+ ref.rune = EmptyRune
892892+ ci.setReturnValue(ActionResult.SUCCESS)
893893+ def flintAndSTEEL_!!(using ci: CallbackInfoReturnable[ActionResult])(ctx: ItemUsageContext): Unit =
894894+ val p = ctx.getHitPos
895895+ val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt)
896896+ val b = BlockPos.Mutable(p.x, p.y, p.z)
897897+ given World = ctx.getWorld
898898+ if q.getX == 4 then
899899+ q.setX(0)
900900+ b.setX(b.getX + 1)
901901+ if q.getY == 8 then
902902+ q.setY(0)
903903+ b.setY(b.getY + 1)
904904+ if q.getZ == 4 then
905905+ q.setZ(0)
906906+ b.setZ(b.getZ + 1)
907907+ val h = RuneShift(q.getX, q.getY, q.getZ, ctx.getSide)
908908+ val ref = RuneRef(b, h)
909909+ ref.rune match
910910+ case RuneBeStartinShit(d) =>
911911+ boundary:
912912+ val runes = findRunes(ref, None).getOrElse:
913913+ ci.setReturnValue(ActionResult.FAIL)
914914+ given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS)
915915+ ctx.getPlayer.sendMessage(Text.literal("The spark fizzles out."), true)
916916+ boundary.break()
917917+ ci.setReturnValue(ActionResult.SUCCESS)
918918+ try
919919+ given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.ITEM_FLINTANDSTEEL_USE, SoundCategory.PLAYERS)
920920+ val boxedRunes = parseRunes(runes)
921921+ interpretForSideEffect(boxedRunes) match
922922+ case Some(value) =>
923923+ println(s"Hello ladies and gentlemen! Today we will be executing ${value}! Are we on the client? ${given_World.isClient}!")
924924+ value.effect.check(value.data)
925925+ value.effect.execute(value.data)
926926+ case None =>
927927+ ctx.getPlayer.sendMessage(Text.literal("Nothing seems to happen..."), true)
928928+ catch
929929+ case e: RuneError =>
930930+ val p = e.ref.floatPos
931931+ given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS)
932932+ ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 5*20))
933933+ ctx.getPlayer.sendMessage(e.message, true)
934934+ case e: (NotImplementedError | MatchError) =>
935935+ e.printStackTrace()
936936+ ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 5*20))
937937+ given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS)
938938+ ctx.getPlayer.sendMessage(Text.literal("You feel strange..."), true)
939939+ case e: Exception =>
940940+ e.printStackTrace()
941941+ ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 15*20))
942942+ ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.BLINDNESS, 15*20))
943943+ given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS)
944944+ ctx.getPlayer.sendMessage(Text.literal("The world is falling apart at the seams..."), true)
945945+ case _ =>
946946+947947+lazy val runeProbe: Registrar[Item] =
948948+ cursedRegister(Identifier.of("mica", "rune_probe"), Item.Settings()):
949949+ new Item(_):
950950+ override def useOnBlock(ctx: ItemUsageContext): ActionResult =
951951+ val p = ctx.getHitPos
952952+ val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt)
953953+ val b = BlockPos.Mutable(p.x, p.y, p.z)
954954+ given World = ctx.getWorld
955955+ if q.getX == 4 then
956956+ q.setX(0)
957957+ b.setX(b.getX + 1)
958958+ if q.getY == 8 then
959959+ q.setY(0)
960960+ b.setY(b.getY + 1)
961961+ if q.getZ == 4 then
962962+ q.setZ(0)
963963+ b.setZ(b.getZ + 1)
964964+ val h = RuneShift(q.getX, q.getY, q.getZ, ctx.getSide)
965965+ val ref = RuneRef(b, h)
966966+ given player: PlayerEntity = ctx.getPlayer
967967+ if given_World.isClient then
968968+ player.sendMessage(Text.literal("Client Rune Info:").styled(_.withColor(0xb36909)), false)
969969+ else
970970+ player.sendMessage(Text.literal("Server Rune Info:").styled(_.withColor(0x06bf72)), false)
971971+ player.sendMessage(Text.literal(s"Ref: ${ref} (${ref.pos.asLong}/${ref.shift.value}) [${ref.shift.x}, ${ref.shift.y}, ${ref.shift.z}, ${ref.shift.facing}]"), false)
972972+ player.sendMessage(Text.literal(s"Rune: ${ref.rune}"), false)
973973+ player.sendMessage(Text.literal(s"Heap: ${ref.heap0} ${ref.heap1} ${ref.heap2} ${ref.heap3}"), false)
974974+ ActionResult.SUCCESS
975975+@register("player_literal")
976976+object PlayerLiteral extends Rune:
977977+ override type Data = EntityRef
978978+ override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (EntityRef, List[(Rune, RuneRef)]) =
979979+ val uuid = EntityRef(Uuids.toUuid(Array(ref.heap0, ref.heap1, ref.heap2, ref.heap3)))
980980+ println(s"playerliteral${ref}sayswhat? ${uuid} obviously")
981981+ (uuid, rhs)
982982+ override def execute(data: EntityRef, frame: BoxedThunk): BoxedThunk =
983983+ println(s"AND HE SHOOTS ${data} TO THE STACK")
984984+ frame.tag.accept(frame.data, data)
985985+ override def fillHeap(using ref: RuneRef, player: PlayerEntity, world: World)(): Unit =
986986+ val uuid = Uuids.toIntArray(player.getUuid)
987987+ ref.heap0 = uuid(0)
988988+ ref.heap1 = uuid(1)
989989+ ref.heap2 = uuid(2)
990990+ ref.heap3 = uuid(3)
991991+ println(s"who up player literal they ${uuid.mkString("Array(", ", ", ")")} (${ref}) on the client? ${world.isClient}")
992992+ register:
993993+ PlayerLiteral.item.register()
994994+995995+@register("entity")
996996+given ValueType[EntityRef]:
997997+ override def eq[U: ValueType as v](x: EntityRef, y: U): Boolean = v.cast[EntityRef](y).exists(y => x.uuid == y.uuid)
998998+ override def show(x: EntityRef): Text = Text.literal(x.uuid.toString.split('-').last).styled(_.withColor(0x9116c9)/*.withFont(Identifier.of("minecraft", "illageralt"))*/)
999999+10001000+@register("side_effect")
10011001+given ValueType[BoxedSideEffect]:
10021002+ override def eq[U: ValueType as v](x: BoxedSideEffect, y: U): Boolean =
10031003+ v.cast[BoxedSideEffect](y).exists(y => x.effect == y.effect && x.data == y.data)
10041004+10051005+ override def show(x: BoxedSideEffect): Text = Text.translatable(registryFor[SideEffect].getId(x.effect).toTranslationKey("mica.side_effect"), x.effect.translationFields(x.data))
10061006+10071007+case class EntityRef(target: UUID | Entity):
10081008+ def uuid: UUID = target match
10091009+ case u: UUID => u
10101010+ case e: Entity => e.getUuid
10111011+ def entity(using w: World): Entity = target match
10121012+ case e: Entity => e
10131013+ case u: UUID => w.getEntity(u)
10141014+given Codec[EntityRef] = Uuids.STRICT_CODEC.xmap(EntityRef(_), _.uuid)
10151015+trait UnaryRune extends SimpleRune:
10161016+ type Arg0: {Codec, ValueType, ClassTag}
10171017+ type Return: ValueType
10181018+ private object ArgFrame extends ThunkFrame:
10191019+ type Data = BoxedThunk
10201020+ override def accept[T: ValueType as v](data: BoxedThunk, value: T): BoxedThunk =
10211021+ v.cast[Arg0](value) match
10221022+ case Some(value) =>
10231023+ data.tag.accept(data.data, run(value))
10241024+ override def execute(frame: BoxedThunk): BoxedThunk = BoxedThunk(ArgFrame, frame)
10251025+ def run(x: Arg0): Return
10261026+ protected def registerFrames(): Unit =
10271027+ val id = registryFor[Rune].getId(this)
10281028+ Registry.register(registryFor[ThunkFrame], id.withSuffixedPath("/0"), ArgFrame)
10291029+trait BinaryRune extends SimpleRune:
10301030+ type Arg0: {Codec, ValueType, ClassTag}
10311031+ type Arg1: {Codec, ValueType, ClassTag}
10321032+ type Return: ValueType
10331033+ private object Arg0Frame extends ThunkFrame:
10341034+ type Data = (frame: BoxedThunk)
10351035+ override def accept[T: ValueType as v](data: (frame: BoxedThunk), value: T): BoxedThunk =
10361036+ v.cast[Arg0](value) match
10371037+ case Some(value) =>
10381038+ BoxedThunk(Arg1Frame, (data.frame, value))
10391039+ private object Arg1Frame extends ThunkFrame:
10401040+ type Data = (frame: BoxedThunk, arg0: Arg0)
10411041+ override def accept[T: ValueType as v](data: (frame: BoxedThunk, arg0: Arg0), value: T): BoxedThunk =
10421042+ v.cast[Arg1](value) match
10431043+ case Some(value) =>
10441044+ val h = data.frame
10451045+ h.tag.accept(h.data, run(data.arg0, value))
10461046+ override def execute(frame: BoxedThunk): BoxedThunk = BoxedThunk(Arg0Frame, (frame = frame))
10471047+ def run(x: Arg0, y: Arg1): Return
10481048+ protected def registerFrames(): Unit =
10491049+ val id = registryFor[Rune].getId(this)
10501050+ if id == null then
10511051+ throw IllegalStateException("Rune frames registered before rune itself")
10521052+ Registry.register(registryFor[ThunkFrame], id.withSuffixedPath("/0"), Arg0Frame)
10531053+ Registry.register(registryFor[ThunkFrame], id.withSuffixedPath("/1"), Arg1Frame)
10541054+10551055+given `codec for just a frame`: Codec[((frame: BoxedThunk))] = codec[BoxedThunk].fieldOf("frame").codec().xmap((frame = _), _.frame)
10561056+given [T: Codec as c] => Codec[((frame: BoxedThunk, arg0: T))] =
10571057+ RecordCodecBuilder.create: i =>
10581058+ i.group(
10591059+ codec[BoxedThunk].fieldOf("frame").forGetter(_.frame),
10601060+ c.fieldOf("arg0").forGetter(_.arg0)
10611061+ ).apply(i, (_, _))
10621062+10631063+@register("add")
10641064+object AdditionRune extends BinaryRune:
10651065+ override type Arg0 = Double
10661066+ override type Arg1 = Double
10671067+ override type Return = Double
10681068+10691069+ override def run(x: Double, y: Double): Double = x + y
10701070+10711071+ register:
10721072+ registerFrames()
10731073+ AdditionRune.item.register()
10741074+10751075+given unmapsYourCodec: [T: MapCodec as c] => Codec[T] = c.codec
10761076+10771077+@register("sub")
10781078+object SubitionRune extends BinaryRune:
10791079+ override type Arg0 = Double
10801080+ override type Arg1 = Double
10811081+ override type Return = Double
10821082+10831083+ override def run(x: Double, y: Double): Double = x - y
10841084+10851085+ register:
10861086+ registerFrames()
10871087+ SubitionRune.item.register()
10881088+10891089+@register("mul")
10901090+object MulitionRune extends BinaryRune:
10911091+ override type Arg0 = Double
10921092+ override type Arg1 = Double
10931093+ override type Return = Double
10941094+10951095+ override def run(x: Double, y: Double): Double = x * y
10961096+10971097+ register:
10981098+ registerFrames()
10991099+ MulitionRune.item.register()
11001100+11011101+given Codec[Double] = Codec.DOUBLE.xmap(locally(_), locally(_))
11021102+11031103+@register("div")
11041104+object DivitionRune extends BinaryRune:
11051105+ override type Arg0 = Double
11061106+ override type Arg1 = Double
11071107+ override type Return = Double
11081108+11091109+ override def run(x: Double, y: Double): Double = x / y
11101110+11111111+ register:
11121112+ registerFrames()
11131113+ DivitionRune.item.register()
11141114+11151115+extension (x: Double) @targetName("~=") def =~(y: Double): Boolean = Math.abs(x - y) < 0.0001
11161116+11171117+given Codec[Vec3d] = Vec3d.CODEC
11181118+given ValueType[Vec3d]:
11191119+ override def eq[U: ValueType as v](x: Vec3d, y: U): Boolean =
11201120+ v.cast[Vec3d](y).exists: y =>
11211121+ x.x =~ y.x && x.y =~ y.y && x.z =~ y.z
11221122+ override def cast[R: ClassTag](x: Vec3d): Option[R] = super.cast(x)
11231123+11241124+ override def show(x: Vec3d): Text = Text.literal(s"{${x.x}, ${x.y}, ${x.z}}")
11251125+11261126+inline def codec[T: Codec as c] = c
11271127+11281128+case class AttributeReference(target: LivingEntity | UUID, attribute: EntityAttribute):
11291129+ def uuid: UUID = target match
11301130+ case e: LivingEntity => e.getUuid
11311131+ case u: UUID => u
11321132+ def entity(using w: World): Option[LivingEntity] = target match
11331133+ case e: LivingEntity => Some(e)
11341134+ case u: UUID => w.getEntity(u) match
11351135+ case null => None
11361136+ case e: LivingEntity => Some(e)
11371137+ case _ => None
11381138+ def attributeInstance(using World): Option[EntityAttributeInstance] = entity.map(_.getAttributeInstance(Registries.ATTRIBUTE.getEntry(attribute)))
11391139+given `codec for entity attributes using the registry`: Codec[EntityAttribute] = Registries.ATTRIBUTE.getCodec
11401140+given `codec for references to entity attributes`: Codec[AttributeReference] =
11411141+ RecordCodecBuilder.create: i =>
11421142+ i.group(
11431143+ Uuids.INT_STREAM_CODEC.fieldOf("entity").forGetter(_.uuid),
11441144+ codec[EntityAttribute].fieldOf("attribute").forGetter(_.attribute),
11451145+ ).apply(i, AttributeReference(_, _))
11461146+given `hey did you know attributes are values`: ValueType[AttributeReference]:
11471147+ override def eq[U: ValueType as v](x: AttributeReference, y: U): Boolean = v.cast[AttributeReference](y).exists(y => (x.uuid == y.uuid) && (x.attribute eq y.target))
11481148+ override def show(x: AttributeReference): Text = Text.translatable(x.attribute.getTranslationKey).styled(_.withColor(0x07b891))
11491149+11501150+@register("nyaboom")
11511151+object ExplosionRune extends BinaryRune:
11521152+ override type Arg0 = Vec3d
11531153+ override type Arg1 = Double
11541154+ override type Return = BoxedSideEffect
11551155+ override def surfaceSprite: Identifier = Rune.SPELL_TEXTURE
11561156+11571157+ @register("nyaboom")
11581158+ object Effect extends SideEffect:
11591159+ override type Data = (pos: Vec3d, power: Double)
11601160+ override type Return = Unit
11611161+ override type RevertData = Unit // good luck un-exploding someone
11621162+11631163+ override def check(data: (pos: Vec3d, power: Double))(using world: World): Unit =
11641164+ // explosions are *probably* fine
11651165+ ()
11661166+ override def execute(data: (pos: Vec3d, power: Double))(using world: World): (Unit, Unit) =
11671167+ if !world.isClient then
11681168+ world.createExplosion(null: Entity, data.pos.x, data.pos.y, data.pos.z, data.power.toFloat, ExplosionSourceType.TRIGGER)
11691169+ ((), ())
11701170+ override def unexecute(data: (pos: Vec3d, power: Double), `return`: Unit, revert: Unit)(using world: World): Unit =
11711171+ // can't unexplode people
11721172+ ()
11731173+11741174+ override def run(pos: Vec3d, power: Double) = BoxedSideEffect(Effect, (pos, power))
11751175+11761176+ register:
11771177+ ExplosionRune.item.register()
11781178+ registerFrames()
11791179+11801180+given Codec[((pos: Vec3d, power: Double))] =
11811181+ RecordCodecBuilder.create[(pos: Vec3d, power: Double)]: i =>
11821182+ i.group(
11831183+ codec[Vec3d].fieldOf("pos").forGetter(_.pos),
11841184+ codec[Double].fieldOf("power").forGetter(_.power),
11851185+ ).apply(i, (pos, power) => (pos = pos, power = power))
11861186+3861187@register("endquote")
3871188object EndQuoteRune extends Rune:
3881189 type Data = Nothing
3891190 override def surfaceSprite: Identifier = Rune.BASIC_TEXTURE
390390- override def read(rhs: List[Rune]): (Nothing, List[Rune]) = throw RunesParseError(rhs.length, Text.literal("Unquote with no corresponding quote"))
391391- override def execute(data: Nothing, frame: ThunkFrame): ThunkFrame = data
11911191+ override def read(rhs: List[(Rune, RuneRef)])(using RuneRef, World): (Nothing, List[(Rune, RuneRef)]) = throw RuneError(Text.literal("Unquote with no corresponding quote"))
11921192+ override def execute(data: Nothing, frame: BoxedThunk): BoxedThunk = data
11931193+11941194+ register:
11951195+ EndQuoteRune.item.register()
3921196// divert
3931197394394-given [T](using Codec[T]): Codec[Seq[T]] = Codec.list[T](summon).xmap(_.toSeq, locally(_))
395395-given Codec[Unit] = Codec.unit(())
396396-given Codec[Nothing] = Codec.of(new Encoder[Nothing]:
11981198+given seqCodec: [T: Codec as c] => Codec[Seq[T]] = Codec.list[T](c).xmap(_.toSeq, locally(_))
11991199+given unitCodec: Codec[Unit] = Codec.unit(())
12001200+given nothingCodec: Codec[Nothing] = Codec.of(new Encoder[Nothing]:
3971201 override def encode[T](input: Nothing, ops: DynamicOps[T], prefix: T): DataResult[T] = input, Decoder.error("Nothing codec"))
39812023991203extension [T] (x: T)
···4061210 case r: R => Some(r)
4071211 case _ => None
4081212409409-class ComponentInitializer extends WorldComponentInitializer:
410410- def registerWorldComponentFactories(factories: WorldComponentFactoryRegistry) =
12131213+private[mica] class ComponentInitializer extends WorldComponentInitializer:
12141214+ def registerWorldComponentFactories(factories: WorldComponentFactoryRegistry): Unit =
4111215 /*dnl*/ () /*
4121216 *///undivert(1)
4131217def init(): Unit =
414414- register()
415415- // eww
416416- println("BELIEVE IT OR NOT WE ARE HERE")
417417- EmptyRune.item.register()
418418- QuoteRune.item.register()
419419- EndQuoteRune.item.register()
420420- println("do you have hair?")12181218+ runeProbe.register()
12191219+ register()