repo for my hex addons :3
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

write all the code

+1431 -209
+45 -14
build.gradle.kts
··· 40 40 overwrite(true) 41 41 onlyIf { !dest.exists() } 42 42 } 43 - val soundsDir by tasks.register<Sync>("sounds") { 43 + val soundsDir by tasks.register<Sync>("assets") { 44 44 dependsOn(assetsArchive) 45 45 inputs.file(assetsArchive.dest) 46 46 val interest = "minecraft-assets-$minecraft_version/assets/minecraft/sounds" 47 47 from(tarTree(assetsArchive.dest)) { 48 - include("$interest/**") 49 48 exclude("**/_list.json") 50 - eachFile { 51 - path = path.replace(interest, "") 52 - } 53 49 } 54 - into("$buildDir/mcSounds") 50 + val outDir = file("$buildDir/mc-assets") 51 + into(outDir) 52 + outputs.dir(outDir) 55 53 // FIXME: hack 56 54 doLast { 57 - file("$buildDir/mcSounds/minecraft-assets-$minecraft_version").deleteRecursively() 55 + outDir.listFiles().forEach { 56 + it.listFiles().forEach { 57 + it.renameTo(outDir.resolve(it.name)) 58 + } 59 + } 58 60 } 59 61 } 60 62 val synthSounds by tasks.register("synthSounds") { ··· 104 106 archivesName.set(project.property("archives_base_name") as String) 105 107 } 106 108 109 + sourceSets { 110 + main { 111 + scala { 112 + srcDirs += file("src/main/java") 113 + } 114 + java { 115 + srcDirs.clear() 116 + } 117 + } 118 + } 119 + 107 120 val targetJavaVersion = 21 108 121 java { 109 122 toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) ··· 170 183 include(modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}+$minecraft_version")!!) 171 184 include(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-base:$cca_version")!!) 172 185 include(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-world:$cca_version")!!) 186 + include(modImplementation("io.github.0x3c50.renderer:renderer-fabric:2.1.2")!!) 187 + //include(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-item:$cca_version")!!) 173 188 } 174 189 175 190 object Statics { ··· 270 285 put("github", "https://github.com/dinosore-rs") 271 286 } 272 287 } 288 + map { 289 + put("name", "afamiliarquiet") 290 + put("role", "stole your fops code") // she stole it first 291 + map("contact") { 292 + put("discord", "https://discord.com/users/813502272355958844") 293 + put("github", "https://github.com/afamiliarquiet") 294 + } 295 + } 273 296 } 274 297 map("contact") { 275 - 298 + put("issues", "https://codeberg.org/poollovernathan/mica/issues") 299 + put("homepage", "https://codeberg.org/poollovernathan/mica") 300 + put("sources", "git+https://codeberg.org/poollovernathan/mica") 276 301 } 277 302 put("license", "GPL-3.0") 278 303 put("icon", "assets.mica/icon.png") ··· 303 328 put("fabric-language-scala", ">=${project.properties["scala_loader_version"]}") 304 329 put("fabric", "*") 305 330 put("minecraft", minecraft_version) 331 + // put("cardinal-components-item", ">=$cca_version") 306 332 put("cardinal-components-world", ">=$cca_version") 307 333 } 308 - map("breaks") { 309 - put("eclipse", "*") // politics - mod has a weird HWID check I don't like 310 - } 311 334 map("custom") { 312 335 array("cardinal-components") { 313 336 for (i in 0..767) { ··· 320 343 rename { "fabric.mod.json" } 321 344 } 322 345 346 + duplicatesStrategy = DuplicatesStrategy.INCLUDE 347 + 323 348 exclude("**/.cache/**") 349 + } 350 + 351 + tasks.register<ScalaDoc>("scaladocAll") { 352 + dependsOn("classes") 353 + dependsOn("clientClasses") 354 + source(files("$buildDir/generated*Scala/**")) 355 + destinationDir = file("$buildDir/docs/scaladoc") 324 356 } 325 357 326 358 tasks.named("ideaSyncTask") { ··· 439 471 } 440 472 441 473 tasks.withType<ScalaCompile> { 442 - scalaCompileOptions.additionalParameters.add("-experimental") 443 - scalaCompileOptions.additionalParameters.add("-explain-cyclic") 444 - // scalaCompileOptions.additionalParameters.add("-Ydebug-cyclic") 474 + scalaCompileOptions.additionalParameters.addAll(listOf("-experimental", "-explain-cyclic", "-Xprint-suspension", "-Ydebug", "-Xprint:typer")) 475 + scalaCompileOptions.forkOptions.jvmArgs!!.add("-Xmx1G") 445 476 } 446 477 447 478 tasks.jar {
+1 -1
gradle.properties
··· 17 17 kotlin_loader_version=1.13.4+kotlin.2.2.0 18 18 scala_loader_version=0.3.1.11 19 19 # Mod Properties 20 - mod_version=0.1.0 20 + mod_version=0.1.0-SNAPSHOT 21 21 maven_group=org.net.eu.pool.mica 22 22 archives_base_name=mica 23 23 # Dependencies
+105 -57
src/client/scala/org/net/eu/pool/mica/client/MicaClient.scala
··· 8 8 import net.fabricmc.fabric.api.renderer.v1.render.RenderLayerHelper 9 9 import net.minecraft.client.MinecraftClient 10 10 import net.minecraft.client.gl.RenderPipelines 11 - import net.minecraft.client.render.{BlockRenderLayer, RenderLayers, TexturedRenderLayers, VertexConsumer} 11 + import net.minecraft.client.render.{BlockRenderLayer, OverlayTexture, RenderLayer, RenderLayers, TexturedRenderLayers, VertexConsumer, VertexConsumerProvider} 12 12 import net.minecraft.client.texture.{Sprite, SpriteAtlasTexture, SpriteLoader} 13 13 import net.minecraft.client.util.math.MatrixStack 14 14 import net.minecraft.util.Identifier 15 - import net.minecraft.util.math.{BlockPos, Direction} 16 - import org.net.eu.pool.mica.{AbstractRuneStorage, EmptyRune, EndQuoteRune, HasRegistry, registryFor, QuoteRune, Rune, RuneShift, given} 15 + import net.minecraft.util.math.{BlockPos, Direction, MathHelper} 16 + import org.net.eu.pool.mica.{AbstractRuneStorage, EmptyRune, EndQuoteRune, HasRegistry, QuoteRune, Rune, RuneShift, registryFor, given} 17 17 import net.minecraft.client.data.{BlockStateModelGenerator, ItemModelGenerator, ItemModels, Model, ModelIds, ModelSupplier} 18 18 import net.minecraft.client.render.item.model.ItemModel 19 19 import net.minecraft.util.math.Direction.Axis ··· 35 35 def sprite: Sprite = blockAtlas(r.spriteTexture) 36 36 def surface: Sprite = blockAtlas(r.surfaceSprite) 37 37 38 + class Renderer(val buffer: VertexConsumer, val matrices: MatrixStack): 39 + var sprite: Option[Sprite] = None 40 + 41 + def forSprite(uv: UV): UV = 42 + sprite match 43 + case Some(value) => ( 44 + u = MathHelper.lerp(uv.u, value.getMinU, value.getMaxU), 45 + v = MathHelper.lerp(uv.v, value.getMinV, value.getMaxV), 46 + ) 47 + case None => uv 48 + 49 + def vert(vertex: Vertex, normal: V3): Unit = 50 + val Vertex(pos, uv, color, light, overlay) = vertex 51 + val newUV = forSprite(uv) 52 + buffer.vertex(matrices.peek, pos.x, pos.y, pos.z) 53 + .texture(newUV.u, newUV.v) 54 + .color(color.r, color.g, color.b, color.a) 55 + .light(light._1, light._2) 56 + .overlay(overlay._1, overlay._2) 57 + .normal(matrices.peek, normal.x, normal.y, normal.z) 58 + 59 + // vtx0 ─── vtx1 60 + // │ │ 61 + // vtx3 ─── vtx2 62 + def quad(vtx0: Vertex, vtx1: Vertex, vtx2: Vertex, vtx3: Vertex, normal: V3): Unit = 63 + vert(vtx0, normal) 64 + vert(vtx1, normal) 65 + vert(vtx2, normal) 66 + vert(vtx3, normal) 67 + 68 + def rect(vtx0: Vertex, vtx1: Vertex, vtx2: Vertex, normal: V3): Unit = 69 + // vtx0 ─── vtx1 70 + // │ │ 71 + // vtx3 ─── vtx2 72 + val vtx3 = Vertex(pos = vtx0.pos + (vtx2.pos - vtx1.pos), uv = vtx0.uv + (vtx2.uv - vtx1.uv), color = vtx1.color, light = vtx1.light, overlay = vtx1.overlay) 73 + quad(vtx0, vtx1, vtx2, vtx3, normal = normal) 74 + object Renderer: 75 + def get(using VertexConsumer, MatrixStack): Renderer = Renderer(summon, summon) 76 + def forLayer(layer: RenderLayer)(using p: VertexConsumerProvider, m: MatrixStack) = Renderer(p.getBuffer(layer), m) 77 + 38 78 given v3Ext: AnyRef with 39 79 extension (v: V3) 40 80 def +(other: V3): V3 = ··· 48 88 def -(other: UV): UV = 49 89 (u = v.u - other.u, v = v.v - other.v) 50 90 51 - def vert(vertex: Vertex, normal: V3)(using c: VertexConsumer, stack: MatrixStack): Unit = 52 - val Vertex(pos, uv, color, light, overlay) = vertex 53 - c.vertex(stack.peek, pos.x, pos.y, pos.z) 54 - .texture(uv.u, uv.v) 55 - .color(color.r, color.g, color.b, color.a) 56 - .light(light._1, light._2) 57 - .overlay(overlay._1, overlay._1) 58 - .normal(stack.peek, normal.x, normal.y, normal.z) 59 - 60 - // vtx0 ─── vtx1 61 - // │ │ 62 - // vtx3 ─── vtx2 63 - def quad(vtx0: Vertex, vtx1: Vertex, vtx2: Vertex, vtx3: Vertex, normal: V3)(using VertexConsumer, MatrixStack): Unit = 64 - vert(vtx0, normal) 65 - vert(vtx1, normal) 66 - vert(vtx2, normal) 67 - vert(vtx3, normal) 68 - 69 - def rect(vtx0: Vertex, vtx1: Vertex, vtx2: Vertex, normal: V3)(using VertexConsumer, MatrixStack): Unit = 70 - // vtx0 ─── vtx1 71 - // │ │ 72 - // vtx3 ─── vtx2 73 - val vtx3 = Vertex(pos = vtx0.pos + (vtx2.pos - vtx1.pos), uv = vtx0.uv + (vtx2.uv - vtx1.uv), color = vtx1.color, light = vtx1.light, overlay = vtx1.overlay) 74 - quad(vtx0, vtx1, vtx2, vtx3, normal = normal) 75 - 76 - def withMatrices[T](body: => T)(using m: MatrixStack): T = 91 + inline def withMatrices[T](body: => T)(using m: MatrixStack): T = 77 92 m.push() 78 93 try 79 94 body ··· 81 96 m.pop() 82 97 83 98 def init(): Unit = 84 - WorldRenderEvents.BEFORE_ENTITIES.register: ctx => 85 - given buf: VertexConsumer = ctx.consumers.getBuffer(TexturedRenderLayers.getEntityCutout) 86 - given matrices: MatrixStack = ctx.matrixStack() 99 + WorldRenderEvents.AFTER_ENTITIES.register: ctx => 100 + given matrices: MatrixStack = ctx.matrixStack 101 + given VertexConsumerProvider = ctx.consumers 102 + given r: Renderer = Renderer.forLayer(TexturedRenderLayers.getEntityCutout) 87 103 withMatrices: 88 104 matrices.translate(ctx.camera.getPos.negate) 89 105 var cur = BlockPos.Mutable() 106 + RenderSystem.teardownOverlayColor() 90 107 for (k, i) <- AbstractRuneStorage.keys.zipWithIndex do 91 108 val c: AbstractRuneStorage = ctx.world.getComponent(k) 92 109 withMatrices: 93 110 val shift = RuneShift(i) 94 - matrices.translate(shift.x / 4.0 - 0.5, shift.y / 8.0 - 0.5, shift.z / 4.0 - 0.5) 111 + matrices.translate(shift.x / 4.0, shift.y / 8.0, shift.z / 4.0) 95 112 c.contents.forEach: (pos, rune) => 96 113 cur.set(pos) 97 114 // TODO: more quads 98 - quad( 99 - Vertex(pos = (cur.getX + 0.25f, cur.getY + 0.625f, cur.getZ + 0.25f), uv = (rune.surface.getMinU, rune.surface.getMinV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 100 - Vertex(pos = (cur.getX + 0.25f, cur.getY + 0.625f, cur.getZ + 0.75f), uv = (rune.surface.getMinU, rune.surface.getMaxV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 101 - Vertex(pos = (cur.getX + 0.75f, cur.getY + 0.625f, cur.getZ + 0.75f), uv = (rune.surface.getMaxU, rune.surface.getMaxV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 102 - Vertex(pos = (cur.getX + 0.75f, cur.getY + 0.625f, cur.getZ + 0.25f), uv = (rune.surface.getMaxU, rune.surface.getMinV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 103 - normal = (0, 1, 0), 104 - ) 105 - quad( 106 - Vertex(pos = (cur.getX + 0.25f, cur.getY + 0.626f, cur.getZ + 0.25f), uv = (rune.sprite.getMinU, rune.sprite.getMinV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 107 - Vertex(pos = (cur.getX + 0.25f, cur.getY + 0.626f, cur.getZ + 0.75f), uv = (rune.sprite.getMinU, rune.sprite.getMaxV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 108 - Vertex(pos = (cur.getX + 0.75f, cur.getY + 0.626f, cur.getZ + 0.75f), uv = (rune.sprite.getMaxU, rune.sprite.getMaxV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 109 - Vertex(pos = (cur.getX + 0.75f, cur.getY + 0.626f, cur.getZ + 0.25f), uv = (rune.sprite.getMaxU, rune.sprite.getMinV), color = (1, 1, 1, 1), light = (255, 255), overlay = (0, 15)), 110 - normal = (0, 1, 0), 111 - ) 115 + withMatrices: 116 + matrices.translate(cur.getX, cur.getY, cur.getZ) 117 + val color: RGB = (1, 1, 1, 1) 118 + val light: I2 = (255, 0) 119 + val overlay: I2 = (0, 10) // magic number owo scary 120 + r.sprite = Some(rune.surface) 121 + r.quad( 122 + Vertex(pos = (-0.25f, 0.125f, -0.25f), uv = (0, 0), color = color, light = light, overlay = overlay), 123 + Vertex(pos = (-0.25f, 0.125f, 0.25f), uv = (0, 1), color = color, light = light, overlay = overlay), 124 + Vertex(pos = (0.25f, 0.125f, 0.25f), uv = (1, 1), color = color, light = light, overlay = overlay), 125 + Vertex(pos = (0.25f, 0.125f, -0.25f), uv = (1, 0), color = color, light = light, overlay = overlay), 126 + normal = (0, 1, 0), 127 + ) 128 + r.quad( 129 + Vertex(pos = (-0.25f, 0f, -0.25f), uv = (0, 0), color = color, light = light, overlay = overlay), 130 + Vertex(pos = (-0.25f, 0.125f, -0.25f), uv = (1, 0), color = color, light = light, overlay = overlay), 131 + Vertex(pos = (0.25f, 0.125f, -0.25f), uv = (1, 1), color = color, light = light, overlay = overlay), 132 + Vertex(pos = (0.25f, 0f, -0.25f), uv = (0, 1), color = color, light = light, overlay = overlay), 133 + normal = (0, 0, -1) 134 + ) 135 + r.sprite = Some(rune.sprite) 136 + r.quad( 137 + Vertex(pos = (-0.25f, 0.126f, -0.25f), uv = (0, 0), color = color, light = light, overlay = overlay), 138 + Vertex(pos = (-0.25f, 0.126f, 0.25f), uv = (0, 1), color = color, light = light, overlay = overlay), 139 + Vertex(pos = (0.25f, 0.126f, 0.25f), uv = (1, 1), color = color, light = light, overlay = overlay), 140 + Vertex(pos = (0.25f, 0.126f, -0.25f), uv = (1, 0), color = color, light = light, overlay = overlay), 141 + normal = (0, 1, 0), 142 + ) 112 143 113 144 class ModelBuilder extends ModelSupplier: 145 + private var parent: Option[Identifier] = None 114 146 private val elements = JsonArray() 115 147 private val textures = JsonObject() 148 + private val display = JsonObject() 116 149 opaque type Key = String 117 150 class Element private[ModelBuilder](faces: JsonObject): 118 151 def face(dir: Direction, texture: Key, uv: (from: UV, to: UV) = null, cullface: Direction = null, rotation: 0 | 90 | 180 | 270 = 0, tintindex: Int = -1): Unit = 119 152 if faces.has(dir.toString) then 120 153 throw IllegalArgumentException(s"Duplicate face $dir in element") 121 154 val faceObj = JsonObject() 122 - faces.addProperty("texture", s"%$texture") 123 - if uv != null then faces.add("uv", fromTuple((uv.from.u: Float, uv.from.v: Float, uv.to.u: Float, uv.to.v: Float))) 124 - if rotation != 0 then faces.addProperty("rotation", rotation) 125 - if tintindex >= 0 then faces.addProperty("tintindex", tintindex) 155 + faceObj.addProperty("texture", s"#$texture") 156 + if uv != null then faceObj.add("uv", fromTuple((uv.from.u: Float, uv.from.v: Float, uv.to.u: Float, uv.to.v: Float))) 157 + if rotation != 0 then faceObj.addProperty("rotation", rotation) 158 + if tintindex >= 0 then faceObj.addProperty("tintindex", tintindex) 126 159 faces.add(dir.toString, faceObj) 127 160 private def fromTuple(t: V3): JsonArray = 128 161 val ary = JsonArray() ··· 160 193 rotObject.addProperty("rescale", rotation.rescale) 161 194 el.add("rotation", rotObject) 162 195 val facesObj = JsonObject() 196 + el.add("faces", facesObj) 163 197 elements.add(el) 164 - Element(el) 198 + Element(facesObj) 199 + def parent(name: Identifier): Unit = 200 + parent match 201 + case None => parent = Some(name) 202 + case Some(value) => throw IllegalArgumentException(s"Attempt to change parent to '$name', but it is already specified as '$value'") 203 + 204 + def display(name: String, translation: V3, rotation: V3, scale: V3): Unit = 205 + if display.has(name) then 206 + throw IllegalArgumentException(s"Duplicate display $name") 207 + val obj = JsonObject() 208 + obj.add("rotation", fromTuple(rotation)) 209 + obj.add("translation", fromTuple(translation)) 210 + obj.add("scale", fromTuple(scale)) 211 + display.add(name, obj) 165 212 166 213 override def get: JsonElement = 167 214 val obj = JsonObject() 168 215 obj.add("textures", textures) 169 216 obj.add("elements", elements) 217 + obj.add("display", display) 218 + parent.foreach(i => obj.addProperty("parent", i.toString)) 170 219 obj 171 220 172 221 def datagenRune(rune: Rune)(using pack: FabricDataGenerator#Pack) = ··· 178 227 gen.register(rune.item.value) 179 228 gen.modelCollector.accept(ModelIds.getItemModelId(rune.item.value), { 180 229 val m = ModelBuilder() 230 + m.parent(Identifier.ofVanilla("block/stone_pressure_plate")) 181 231 val surface = m.texture("surface", rune.surfaceSprite) 182 232 val sprite = m.texture("sprite", rune.spriteTexture) 183 - val bottom = m.element(from = (4, 0, 4), to = (8, 2, 8)) 184 - bottom.face(Direction.UP, texture = surface, uv = (from = (0, 0), to = (1, 1))) 233 + val bottom = m.element(from = (4, 0, 4), to = (12, 2, 12)) 234 + bottom.face(Direction.UP, texture = surface, uv = (from = (0, 0), to = (16, 16))) 185 235 m 186 236 }) 187 237 } ··· 189 239 190 240 def datagen(using gen: FabricDataGenerator): Unit = 191 241 given FabricDataGenerator#Pack = gen.createPack() 192 - datagenRune(EmptyRune) 193 - datagenRune(QuoteRune) 194 - datagenRune(EndQuoteRune) 242 + registryFor[Rune].forEach(datagenRune(_))
+5 -1
src/main/resources/mica.mixins.json
··· 3 3 "minVersion": "0.8", 4 4 "package": "org.net.eu.pool.mica.mixin", 5 5 "compatibilityLevel": "JAVA_17", 6 - "mixins": [], 6 + "mixins": [ 7 + "BrainMixin", 8 + "FlintAndSteelItemMixin", 9 + "ItemMixin" 10 + ], 7 11 "injectors": { 8 12 "defaultRequire": 1 9 13 },
+274 -33
src/main/scala/org/net/eu/pool/mica/Defs.scala
··· 1 1 package org.net.eu.pool.mica 2 2 3 3 import com.mojang.serialization.Codec 4 - import it.unimi.dsi.fastutil.longs.Long2ObjectMap 4 + import com.mojang.serialization.codecs.RecordCodecBuilder 5 + import it.unimi.dsi.fastutil.longs.{Long2IntMap, Long2ObjectMap} 5 6 import net.minecraft.block.{AbstractBlock, Block} 7 + import net.minecraft.component.DataComponentTypes 8 + import net.minecraft.entity.player.PlayerEntity 6 9 import net.minecraft.item.{Item, ItemUsageContext} 7 10 import net.minecraft.registry.{Registries, Registry, RegistryKey, RegistryKeys} 11 + import net.minecraft.text.Text 8 12 import net.minecraft.util.math.Direction.{Axis, AxisDirection} 9 - import net.minecraft.util.{ActionResult, Identifier} 10 - import net.minecraft.util.math.{BlockPos, Direction} 13 + import net.minecraft.util.{ActionResult, Identifier, Uuids} 14 + import net.minecraft.util.math.{BlockPos, Direction, Vec3d} 15 + import net.minecraft.util.shape.{VoxelShape, VoxelShapes} 11 16 import net.minecraft.world.World 17 + import org.net.eu.pool.mica.RevealRune.Frame 18 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable 19 + 20 + import scala.annotation.MacroAnnotation 21 + import scala.collection.mutable 22 + import scala.quoted.{Expr, Quotes} 23 + import scala.reflect.ClassTag 24 + import scala.util.chaining.scalaUtilChainingOps 12 25 // ifversion(>=2100, <[[ 13 26 import net.minecraft.storage.{ReadView, WriteView} 14 27 import org.ladysnake.cca.api.v3.component.sync.AutoSyncedComponent ··· 20 33 import dev.onyxstudios.cca.api.v3.world.WorldComponentFactoryRegistry 21 34 // ]]>) 22 35 36 + /** 37 + * 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. 38 + * 39 + * <b style="color: color-mix(in oklch, 15% currentcolor, red)">Caution:</b> If you have a forced Registrar (as opposed 40 + * to a by-name / lazy `=> Registrar[_]`), you '''must''' call [[register()]] at some point. '''Failing to do so may 41 + * cause an intrusive-holder crash!''' 42 + * 43 + * @tparam T Type of the value that will be registered. 44 + */ 23 45 trait Registrar[T]: 24 - val value: T 25 - private[mica] def register(): Unit 46 + /** 47 + * Backing value. This is usually an [[Item]] or [[Block]]. 48 + */ 49 + lazy val value: T 50 + 51 + /** 52 + * Adds [[value]] to the associated registry. 53 + */ 54 + def register(): Unit 26 55 27 56 // probably the least cursed part of this code 28 57 /** 29 58 * Cross-version helper for registering items. 30 59 * 31 60 * @tparam T Concrete item type for generics purposes. 32 - * @param identifier Final identifier of item - passed to the settings in >=1.21.2. 33 - * @param settings Item settings - these must be known ahead-of-time since 1.21.2 makes them hold the registry key. 34 - * @param itemFactory Produces the item from its settings. This **must** use the provided instance! 61 + * @param identifier Final identifier of item — passed to the settings in >=1.21.2. 62 + * @param settings Item settings — these must be known ahead-of-time since 1.21.2 makes them hold the registry key. 63 + * @param itemFactory Produces the item from its settings. This '''must''' use the provided instance! 35 64 * @return Object containing the final item, as well as a method that registers it. 36 65 */ 37 66 def cursedRegister[T <: Item](identifier: Identifier, settings: Item.Settings)(itemFactory: Item.Settings => T): Registrar[T] = ··· 39 68 val key = RegistryKey.of(RegistryKeys.ITEM, identifier) 40 69 lazy val item = itemFactory(settings /*ifversion(>=2102,<[[*/.registryKey(key) /*]]>)*/) 41 70 new Registrar[T]: 42 - override val value: T = item 43 - 71 + private var registered = false 72 + override lazy val value: T = 73 + if registered then 74 + item 75 + else 76 + throw IllegalStateException(s"Item $identifier was not registered") 44 77 override def register(): Unit = 78 + registered = true 45 79 Registry.register(Registries.ITEM, key, item) 46 80 47 81 /** 48 82 * Cross-version helper for registering blocks. 49 83 * 50 84 * @tparam T Concrete block type for generics purposes. 51 - * @param identifier Final identifier of block - passed to the settings in >=1.21.2. 52 - * @param settings Block settings - these must be known ahead-of-time since 1.21.2 makes them hold the registry key. 53 - * @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. 85 + * @param identifier Final identifier of block — passed to the settings in >=1.21.2. 86 + * @param settings Block settings — these must be known ahead-of-time since 1.21.2 makes them hold the registry key. 87 + * @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. 54 88 * @return Object containing the final block, as well as a method that registers it. 55 89 */ 56 90 def cursedRegister[T <: Block](identifier: Identifier, settings: AbstractBlock.Settings)(blockFactory: (=> AbstractBlock.Settings => T) => AbstractBlock.Settings => T): Registrar[T] = ··· 58 92 lazy val fixed: AbstractBlock.Settings => T = blockFactory(fixed) 59 93 lazy val block = fixed(settings /*ifversion(>=2100,<[[*/.registryKey(key) /*]]>)*/) 60 94 new Registrar[T]: 61 - override val value: T = block 95 + override lazy val value: T = block 62 96 63 97 override def register(): Unit = 64 98 Registry.register(Registries.BLOCK, key, block) 65 99 100 + /** 101 + * Converts an annotated function into a [[Rune]] implementation and as many [[ThunkFrame]] implementations as needed 102 + * for its arguments. Optional arguments are not allowed. 103 + */ 104 + case class rune[+T] private(reader: (List[Any]) => (T, List[Any])) extends MacroAnnotation: 105 + def this()(using ev: Unit <:< T) = this((ev(()), _)) 106 + 107 + override def transform(using q: Quotes)(definition: q.reflect.Definition, companion: Option[q.reflect.Definition]): List[q.reflect.Definition] = 108 + import q.reflect._ 109 + // definition match 110 + // case d@DefDef(name, params, returnType, body) => 111 + // val buf = mutable.Buffer(definition) 112 + // buf ++= companion 113 + // val explicit = params.collect: 114 + // case c@TermParamClause(p) if !c.isGiven => p 115 + // if explicit.length > 1 then 116 + // report.error("method must have exactly one explicit parameter list") 117 + // val args = explicit(1) 118 + // val runeSymbol = Symbol.newClass(d.symbol.owner, s"$name", List(TypeRepr.of[Rune]), s => List(), None) 119 + // buf ++= new register(name).transform(ClassDef(runeSymbol, List(TypeTree.of[Rune]), List()), None) 120 + // // TODO 121 + // buf.toList 122 + // case _ => 123 + // report.error("@rune may only be used on function definitions") 124 + List(definition) :++ companion 125 + 126 + /** 127 + * A thunk frame represents a ''pending computation'' on the stack — for example, an addition operator waiting for 128 + * its arguments. 129 + */ 66 130 trait ThunkFrame derives HasRegistry: 67 - given Codec[this.type] = compiletime.deferred 68 - val accept: PartialFunction[Abstract[ValueType], ThunkFrame] 131 + type Data: Codec 132 + def accept[T: ValueType](data: Data, value: T): BoxedThunk 133 + 134 + /** 135 + * Holds operations and constants for manipulating [[RuneShift!]] values. 136 + */ 137 + object RuneShift: 138 + /** 139 + * 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) 140 + * and the rune's facing direction. 141 + * 142 + * @note The RuneShift type is represented as an opaque [[Int]], with all values bit-packed within. Using [[asInstanceOf]] 143 + * from or to [[Int]] is safe, but this is subject to change. 144 + */ 145 + opaque type RuneShift = Int 146 + inline def apply(value: Int): RuneShift = value 147 + def apply(x: Int, y: Int, z: Int, facing: Direction): RuneShift = x & 0b11 | y << 2 & 0b11100 | z << 5 & 0b1100000 | facing.ordinal << 7 148 + private def x_impl(s: Expr[RuneShift], x: Expr[Int])(using q: Quotes): Expr[Unit] = 149 + import q.reflect.* 150 + Assign(s.asTerm, '{${s}(x = ${x})}.asTerm).asExprOf[Unit] 151 + private def y_impl(s: Expr[RuneShift], y: Expr[Int])(using q: Quotes): Expr[Unit] = 152 + import q.reflect.* 153 + Assign(s.asTerm, '{${s}(y = ${y})}.asTerm).asExprOf[Unit] 154 + private def z_impl(s: Expr[RuneShift], z: Expr[Int])(using q: Quotes): Expr[Unit] = 155 + import q.reflect.* 156 + Assign(s.asTerm, '{${s}(z = ${z})}.asTerm).asExprOf[Unit] 157 + given ops: AnyRef: 158 + extension (s: RuneShift) 159 + /** 160 + * Gets the backing value of the rune shift as a bit-packed [[Int]]. This has no runtime cost. 161 + */ 162 + @inline def value: Int = s 163 + /** 164 + * Returns the X-offset of runes from the block grid: an integer 0 to 3 (inclusive). 165 + */ 166 + @inline def x = s & 0b11 167 + /** 168 + * Returns the Y-offset of runes from the block grid: an integer 0 to 7 (inclusive). 169 + */ 170 + @inline def y = s >> 2 & 0b111 171 + /** 172 + * Returns the Z-offset of runes from the block grid: an integer 0 to 3 (inclusive). 173 + */ 174 + @inline def z = s >> 5 & 0b11 175 + /** 176 + * Returns the facing direction of these runes. 177 + */ 178 + @inline def facing = Direction.values()(s >> 7 & 0b111) 179 + /** 180 + * Updates the rune shift with new values. 181 + * @param x New x-offset (0 to 3). <b>Invalid offsets are undefined behavior!</b> 182 + * @param y New y-offset (0 to 7). <b>Invalid offsets are undefined behavior!</b> 183 + * @param z New z-offset (0 to 3). <b>Invalid offsets are undefined behavior!</b> 184 + * @param facing New facing direction (any of the 6). 185 + * @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. 186 + */ 187 + @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) 188 + def storage(using w: World) = w.getComponent(AbstractRuneStorage.keys(s.value)) 189 + private def facing_impl(s: Expr[RuneShift], facing: Expr[Direction])(using q: Quotes): Expr[Unit] = 190 + import q.reflect.* 191 + Assign(s.asTerm, '{${s}(facing = ${facing})}.asTerm).asExprOf[Unit] 192 + 193 + /** 194 + * Cache of [[VoxelShape]]s for every rune offset, used for speed. Clients are expected to not mutate this array. 195 + */ 196 + val shapeCache: Array[VoxelShape] = Array.ofDim[VoxelShape](768).tap: m => 197 + for shift: RuneShift <- 0 until 768 do 198 + val middle = (shift.x / 4.0 - 0.5, shift.y / 8.0 - 0.5, shift.z / 4.0 - 0.5) 199 + m(shift) = VoxelShapes.cuboid(middle._1 - .25, middle._2, middle._3 - .25, middle._1 + .25, middle._2 + .0625, middle._3 + .25) 200 + end RuneShift 201 + export RuneShift.RuneShift 69 202 70 203 /** 71 - * A rune is an element of a cast. Runes are read left-to-right and form execution frames on the thunk stack. 204 + * A rune is the basic element of a cast. Runes fundamentally operate on a list of [[ThunkFrame]]s, but may also access 205 + * the parsing stream (e.g. [[QuoteRune]]) to store internal data. 206 + * 207 + * <b>Note for implementors:</b> Almost all runes don't need access to the parsing stream. In this case, you can use 208 + * [[SimpleRune]] to avoid implementing extra things. 209 + * 210 + * @see [[BoxedRune]] 72 211 */ 73 212 trait Rune derives HasRegistry: 213 + /** 214 + * Any data this rune needs from parsing to execute. For most runes, this is [[Unit]] (no information), but in some 215 + * cases other values may be accurate: 216 + * 217 + * - [[QuoteRune]] stores a `List[BoxedRune]` preserved for later evaluation. 218 + * - [[EndQuoteRune]] stores [[Nothing]], as parsing it alone will never succeed. 219 + */ 74 220 type Data: Codec 75 221 76 - def surfaceSprite: Identifier 222 + /** 223 + * Determines the sprite used for this rune's surface, as the name of a texture in the block atlas. 224 + * 225 + * 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]]. 226 + * 227 + * @return The rune's surface texture. The path `foo:bar` corresponds to `assets/foo/textures/bar.png`. 228 + */ 229 + def surfaceSprite: Identifier = Rune.BASIC_TEXTURE 77 230 78 231 /** 79 - * Reads the rune's information from the list of runes. 232 + * Reads the rune's information from the list of runes. For most runes, this method does nothing. 80 233 * 81 234 * @param rhs The list of remaining runes, not including this rune. 82 - * @throws RunesParseError Thrown when the rune is unable to read all of its arguments. 235 + * @throws RuneError Thrown when the rune is unable to read all of its arguments. 83 236 * @return The rune's metadata and the list of remaining runes iff parsing is successful. 84 237 */ 85 - @throws[RunesParseError]("when this rune cannot parse its arguments") 86 - def read(rhs: List[Rune]): (Data, List[Rune]) 238 + def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (Data, List[(Rune, RuneRef)]) 87 239 88 240 /** 89 - * Executes this rune, given its metadata. Executing a rune pushes some [[ThunkFrame]]s to the stack, then pops some. 90 - * @param data The metadata returned from [[read]]. 91 - * @param frame The top of the thunk stack. All runes usually will pass data to a thunk after pushing some of its own. 241 + * Reads and executes this rune. This method usually pops then pushes frames on `frame`. 242 + * 243 + * Here's an example of a simple rune that supplies a Unit: 244 + * 245 + * {{{ 246 + * // Pass a Unit into the top frame 247 + * def execute(data: Data, frame: BoxedThunk): BoxedThunk = 248 + * Unit match 249 + * case frame.accept(newFrame) => newFrame 250 + * case _ => ??? 251 + * }}} 252 + * 253 + * @param data Extra information returned by [[read]]. 254 + * @param frame The current top of the thunk stack before execution. 92 255 * @return The new top of the thunk stack. 93 256 */ 94 - def execute(data: Data, frame: ThunkFrame): ThunkFrame 257 + def execute(data: Data, frame: BoxedThunk): BoxedThunk 95 258 259 + /** 260 + * Generated item for this rune to use. You need to handle [[Registrar.register registering this item]] yourself, but not doing so 261 + * will not incur an intrusive-holders crash unless someone else forces this field first.<hr style="border-style:hidden"> 262 + * <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 263 + * will crash the game! 264 + */ 96 265 lazy val item = 97 266 val identifier = registryFor[Rune].getId(this) 98 267 if identifier == null then 99 268 throw IllegalStateException("Rune.item may not be referenced before the rune is registered") 100 269 cursedRegister(identifier, Item.Settings()): 101 270 new Item(_): 102 - override def useOnBlock(context: ItemUsageContext): ActionResult = 271 + override def useOnBlock(using context: ItemUsageContext): ActionResult = 103 272 println("GOT HERE 2") 104 273 val p = context.getHitPos 105 274 val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt) ··· 115 284 q.setZ(0) 116 285 b.setZ(b.getZ + 1) 117 286 val h = RuneShift(q.getX, q.getY, q.getZ, context.getSide) 118 - val ref = RuneRef(b, h) 287 + given ref: RuneRef = RuneRef(b, h) 119 288 if ref.isEmpty && ref.neighbors.forall(_.isEmpty) then 120 289 ref.rune = Rune.this 290 + try 291 + println(s"Loading placed NBT") 292 + val nbt = Uuids.toIntArray(context.getStack.getComponents.get(DataComponentTypes.PROFILE).uuid.get) 293 + println(s"Loaded array: ${nbt.mkString("Array(", ", ", ")")}") 294 + ref.heap0 = nbt(0) 295 + ref.heap1 = nbt(1) 296 + ref.heap2 = nbt(2) 297 + ref.heap3 = nbt(3) 298 + catch case e: Exception => 299 + println(s"nvm, caught a ${e} - generating it") 300 + given PlayerEntity = context.getPlayer 301 + ref.rune.fillHeap() 302 + println(s"Now the heap at ${ref} is ${ref.heap0} ${ref.heap1} ${ref.heap2} ${ref.heap3}") 121 303 AbstractRuneStorage.sync(summon, h) 122 304 println("done !") 123 305 ActionResult.SUCCESS 124 306 else 125 307 println("nah") 126 308 ActionResult.PASS 309 + 310 + def computeNeighbors(using r: RuneRef): Set[RuneRef] = Set(r.north.north, r.east.east, r.south.south, r.west.west) 311 + def ignite(using r: RuneRef, ci: CallbackInfoReturnable[ActionResult])(ctx: ItemUsageContext): Unit = () 312 + def fillHeap(using RuneRef, PlayerEntity, World)(): Unit = () 313 + 314 + /** 315 + * A special case of [[Rune]] that only operates on the [[ThunkFrame]] stack, not modifying parsing. Most runes fall into 316 + * this category. Using SimpleRune in this case provides a stronger guarantee and could assist in compiler optimization 317 + * in the future. 318 + */ 319 + trait SimpleRune extends Rune: 320 + override type Data = Unit 321 + override given Codec[Data] = summon[Codec[Unit]] 322 + override def read(rhs: List[(Rune, RuneRef)])(using RuneRef, World): (Data, List[(Rune, RuneRef)]) = ((), rhs) 323 + override def execute(remainder: Unit, frame: BoxedThunk): BoxedThunk = execute(frame) 324 + def execute(frame: BoxedThunk): BoxedThunk 325 + 326 + /** 327 + * Holder object for [[Rune! Rune]] registries and common values of [[Rune.surfaceSprite]]. 328 + */ 127 329 object Rune: 330 + /** 331 + * Texture used for runes that do not mutate the world. 332 + */ 128 333 final val BASIC_TEXTURE = Identifier.of("mica", "block/basic_rune") 334 + /** 335 + * Texture used for runes that create [[SideEffect! side effects]] mutating the world. 336 + */ 129 337 final val SPELL_TEXTURE = Identifier.of("mica", "block/spell_rune") 338 + /** 339 + * Texture used for special runes that fire off a rune-chain. 340 + */ 130 341 final val IMPETUS_TEXTURE = Identifier.of("mica", "block/start_rune") 131 342 132 343 case class RuneRef(pos: BlockPos, shift: RuneShift): 344 + def floatPos = Vec3d(pos.getX + shift.x / 4.0, pos.getY + shift.y / 8.0, pos.getZ + shift.z / 4.0) 345 + 133 346 def offset(dir: Direction): RuneRef = 134 347 dir match 135 348 case Direction.DOWN => ··· 157 370 else RuneRef(pos, shift (x = shift.x + 1)) 158 371 159 372 def rune(using World): Rune = shift.storage(pos) 160 - def rune_=(r: Rune)(using World): Unit = shift.storage(pos) = r 373 + def rune_=(r: Rune)(using World): Unit = 374 + if r eq EmptyRune then 375 + heap0 = 0 376 + heap1 = 0 377 + heap2 = 0 378 + heap3 = 0 379 + shift.storage(pos) = r 161 380 def isEmpty(using World) = rune == null || rune == EmptyRune 381 + def heap0(using World): Int = shift.storage.heap0.get(pos.asLong) 382 + def heap0_=(r: Int)(using World): Unit = shift.storage.heap0.put(pos.asLong, r) 383 + def heap1(using World): Int = shift.storage.heap1.get(pos.asLong) 384 + def heap1_=(r: Int)(using World): Unit = shift.storage.heap1.put(pos.asLong, r) 385 + def heap2(using World): Int = shift.storage.heap2.get(pos.asLong) 386 + def heap2_=(r: Int)(using World): Unit = shift.storage.heap2.put(pos.asLong, r) 387 + def heap3(using World): Int = shift.storage.heap3.get(pos.asLong) 388 + def heap3_=(r: Int)(using World): Unit = shift.storage.heap3.put(pos.asLong, r) 162 389 163 390 def north = offset(Direction.NORTH) 164 391 def south = offset(Direction.SOUTH) 165 392 def east = offset(Direction.EAST) 166 393 def west = offset(Direction.WEST) 167 394 168 - def neighbors = Seq(north, east, south, west, north.east, north.west, south.east, south.west) 395 + def neighbors = Set(north, east, south, west, north.east, north.west, south.east, south.west) 396 + def adjacent = Set(north.north, east.east, south.south, west.west) 169 397 170 398 private[mica] trait AbstractRuneStorage extends Component, AutoSyncedComponent: 171 - type Concrete <: ConcreteRuneStorage 399 + type Concrete <: AbstractRuneStorage 172 400 val world: World 173 401 val contents: Long2ObjectMap[Rune] 402 + val heap0: Long2IntMap 403 + val heap1: Long2IntMap 404 + val heap2: Long2IntMap 405 + val heap3: Long2IntMap 174 406 given ComponentKey[Concrete] = compiletime.deferred 175 407 def apply(pos: BlockPos): Rune = contents.get(Long.box(pos.asLong)) 176 408 def update(pos: BlockPos, rune: Rune): Unit = rune match ··· 178 410 case EmptyRune => contents.remove(Long.box(pos.asLong)) 179 411 case _ => contents.put(pos.asLong, rune) 180 412 413 + def packLong(h: Int, l: Int): Long = (h.toLong << 32) | (l & 0xFFFFFFFFL) 414 + def unpackLong(n: Long): (Int, Int) = ((n >>> 32).toInt, n.toInt) 415 + 181 416 object AbstractRuneStorage: 182 417 private[mica] val keys: Array[ComponentKey[? <: AbstractRuneStorage]] = Array.fill(768)(null) 183 418 def get(world: World, shift: RuneShift): AbstractRuneStorage = keys(shift.value).get(world) 184 419 def sync(world: World, shift: RuneShift): Unit = keys(shift.value).sync(world) 185 420 186 - @hasRegistry 187 421 trait ValueType[T: Codec]: 188 422 def eq[U: ValueType](x: T, y: U): Boolean 189 - object ValueType 423 + def cast[R: ClassTag](x: T): Option[R] = 424 + x match 425 + case x: R => Some(x) 426 + case _ => None 427 + def show(x: T): Text 428 + def codec: Codec[T] = summon 429 + object ValueType: 430 + given HasRegistry[ValueType[?]] = HasRegistry.derived
+53 -10
src/main/scala/org/net/eu/pool/mica/Macros.scala src/main/scala/org/net/eu/pool/mica/AAA_Macros.scala
··· 14 14 import scala.quoted.{Expr, Quotes, ToExpr, Type} 15 15 import scala.reflect.ClassTag 16 16 17 - case class ModID(name: String) 17 + /** 18 + * Helper type used with [[register]] for setting your mod ID. 19 + * @param name Mod ID, pursuant to identifier rules. 20 + */ 21 + case class ModID(name: String): 22 + Identifier.of(name, "any") 23 + 24 + /** 25 + * Returns the mod ID of the current file. 26 + */ 18 27 inline def modid(using m: ModID): String = m.name 19 28 20 29 /** 30 + * '''Deprecated''': The generated givens are hard to use in code. Derive [[HasRegistry]] instead. 31 + * 21 32 * This macro generates a registry for a class, allowing the [[register @register]] annotation to track its descendants. 22 33 */ 23 34 @deprecated("@hasRegistry is hard to use correctly. Opt to derive HasRegistry instead.") ··· 97 108 * 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). 98 109 * @tparam T Object to listen for registrations on. This should be the *concrete type* of your object, not of the registry! 99 110 */ 100 - trait RegisterHook[T]: 111 + private[mica] trait RegisterHook[T]: 101 112 def preRegister(target: T, key: Identifier)(using registry: Registry[? >: T]): Boolean = true 102 113 def postRegister(target: T, key: Identifier)(using registry: Registry[? >: T]): Unit = () 103 114 104 115 private var registrarState: Option[mutable.Buffer[Expr[Unit]]] = Some(mutable.Buffer.empty) 116 + 117 + /** 118 + * Registers the given value into the best registry for it. This macro may only be applied to `object` literals. 119 + * @param key Combined with a [[ModID]] in scope to create the value's [[Identifier]] 120 + */ 105 121 @compileTimeOnly("@register is expanded at compile-time") 106 122 class register(key: String) extends MacroAnnotation: 107 123 override def transform(using q: Quotes)(definition: q.reflect.Definition, companion: Option[q.reflect.Definition]): List[q.reflect.Definition] = ··· 136 152 def liftOption[T: Type](expr: Option[Expr[T]])(using Quotes): Expr[Option[T]] = expr match 137 153 case Some(value) => '{Some(${value})} 138 154 case None => '{None} 139 - // exists at runtime 140 - val inits = mutable.Map.empty[String, Any] 141 - @compileTimeOnly("@register is expanded at compile-time") 142 155 object register: 156 + /** 157 + * '''Implementation detail''' used to track [[register]]-blocks. Do not use. 158 + * @deprecated Do not use. 159 + */ 160 + private[register] val _inits = mutable.Map.empty[String, Any] 161 + 162 + /** 163 + * Executes all registrations created by [[register @register]] annotations and [[apply(R):R* register {...}]] blocks. 164 + * {{{ 165 + * trait Foo derives HasRegistry 166 + * 167 + * @register("foo_impl") 168 + * object FooImpl extends Foo 169 + * 170 + * def init() = 171 + * register() 172 + * }}} 173 + */ 174 + @compileTimeOnly("@register is expanded at compile-time") 143 175 inline def apply(): Unit = ${ register.apply_impl } 144 - inline def apply[R](value: => R): R = ${ register.apply_impl('value) } 176 + 177 + /** 178 + * Appends arbitrary code to the registration queue. 179 + * 180 + * > [!CAUTION] 181 + * > If [[value]] references a local scope not visible from the [[apply():Unit* register()]] invocation, the compiler will crash! 182 + * 183 + * @param value 184 + * @tparam R 185 + * @return 186 + */ 187 + @compileTimeOnly("@register is expanded at compile-time") 188 + inline def apply(value: => Unit): Unit = ${ register.apply_impl('value) } 145 189 private def apply_impl(using q: Quotes): Expr[Unit] = 146 190 import q.reflect.* 147 191 registrarState match ··· 152 196 report.error("Registrations have already been processed") 153 197 '{()} 154 198 end apply_impl 155 - private def apply_impl[R: Type](body: Expr[R])(using q: Quotes): Expr[R] = 199 + private def apply_impl(body: Expr[Unit])(using q: Quotes): Expr[Unit] = 156 200 import q.reflect.* 157 201 registrarState match 158 202 case Some(l) => 159 203 val name = Symbol.freshName("register") 160 - l += '{ inits(${Expr(name)}) = ${body} } 161 - '{ inits(${Expr(name)}).asInstanceOf[R] } 204 + l += body 162 205 case None => 163 206 report.error("Registrations have already been processed") 164 - '{??? : R} 207 + '{()} 165 208 166 209 trait HasRegistry[T]: 167 210 given registryKey: RegistryKey[Registry[T]]
+892 -93
src/main/scala/org/net/eu/pool/mica/Mica.scala
··· 1 1 package org.net.eu.pool.mica 2 + import com.mojang.authlib.properties.PropertyMap 2 3 import com.mojang.datafixers.util.Pair 4 + import com.mojang.logging.LogUtils 3 5 import com.mojang.serialization 4 6 import com.mojang.serialization.codecs.RecordCodecBuilder 5 7 import com.mojang.serialization.{Codec, DataResult, Decoder, DynamicOps, Encoder, Lifecycle, MapCodec} 8 + import it.unimi.dsi.fastutil.longs.{Long2FloatMap, Long2IntMap, Long2IntMaps, Long2IntOpenHashMap, Long2LongMap} 9 + import net.minecraft.component.`type`.ProfileComponent 10 + import net.minecraft.component.{ComponentChanges, ComponentType, DataComponentTypes} 11 + import net.minecraft.entity.{Entity, ItemEntity, LivingEntity} 12 + import net.minecraft.entity.attribute.{EntityAttribute, EntityAttributeInstance} 13 + import net.minecraft.entity.decoration.DisplayEntity.TextDisplayEntity 14 + import net.minecraft.entity.effect.{StatusEffectInstance, StatusEffects} 15 + import net.minecraft.entity.player.PlayerEntity 16 + import net.minecraft.item.ItemStack 17 + import net.minecraft.loot.entry.ItemEntry 18 + import net.minecraft.server.world.ServerWorld 19 + import net.minecraft.sound.{SoundCategory, SoundEvent, SoundEvents} 20 + import net.minecraft.state.property.Property 21 + import net.minecraft.text.TextColor 22 + import net.minecraft.util.Uuids 6 23 import net.minecraft.util.shape.VoxelShapes 24 + import net.minecraft.world.World.ExplosionSourceType 25 + import org.net.eu.pool.mica 26 + import org.slf4j.{Logger, LoggerFactory} 27 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable 7 28 29 + import java.util.{Optional, UUID} 8 30 import java.util.stream.LongStream 31 + import scala.:: 32 + import scala.NamedTuple.{AnyNamedTuple, NamedTuple} 33 + import scala.Tuple.:* 34 + import scala.annotation.targetName 35 + import scala.collection.immutable.{AbstractSet, SortedSet} 36 + import scala.compiletime.constValue 9 37 import scala.util.boundary 10 38 // ifversion(>=2100, <[[ 11 39 import net.minecraft.storage.{ReadView, WriteView} ··· 42 70 import scala.collection.immutable.{AbstractSeq, LinearSeq} 43 71 import scala.compiletime.{deferred, erasedValue, summonInline} 44 72 import scala.deriving.Mirror 73 + import scala.language.experimental.erasedDefinitions 45 74 import scala.quoted.{Expr, Quotes} 46 75 import scala.reflect.ClassTag 47 76 import scala.tools.nsc.io.Path ··· 49 78 50 79 private given ModID = ModID("mica") 51 80 52 - val /* <[[ */ minecraft_version: /* ]]> */ Int = minecraft_version 81 + /** 82 + * Current serial Minecraft version. This is solely provided for IDE completion; it is expanded before compile-time by m4. 83 + */ 84 + private[mica] val /* <[[ */ minecraft_version: /* ]]> */ Int = minecraft_version 85 + 86 + /** 87 + * Represents a value such that the only thing known about it is that it is a member of a typeclass [[C]]. While this 88 + * doesn't seem useful on the surface, it can be used to represent things similar to Java interfaces - the type 89 + * `Abstract[ValueType]` could represent any value for which a [[ValueType]] exists, but without requiring the class 90 + * itself to add to its `implements` clause. 91 + * @tparam C Typeclass providing the sole known information about the type. 92 + */ 53 93 trait Abstract[C[_]]: 94 + /** 95 + * Real type of [[value]]. Usually, this is used as a path-dependent type - the actual type is rarely known. 96 + */ 54 97 type T: C 98 + /** 99 + * Backing value of the abstract. 100 + */ 55 101 val value: T 102 + 103 + /** 104 + * Helpers for creating and un-creating [[Abstract]] instances. 105 + */ 56 106 object Abstract: 57 - def apply[C[_], _T: C](x: _T): Abstract[C] = new Abstract[C]: 58 - type T = _T 59 - val value: _T = x 107 + /** 108 + * Construct an [[Abstract]] from a value and the proof of [[C]] in scope. 109 + * @param x Backing value placed in [[Abstract.value]]. 110 + * @tparam C Typeclass for this [[Abstract]] instance. 111 + * @tparam T Actual type within the [[Abstract]]. The return type is '''not''' narrowed to this type! 112 + * @return Abstract instance of [[x]] plus the `C[x.type]` proof in scope. 113 + */ 114 + def apply[C[_], T: C](x: T): Abstract[C] = 115 + type _T = T 116 + new Abstract[C]: 117 + type T = _T 118 + val value: _T = x 119 + 120 + /** 121 + * Splits an [[Abstract]] into its [[Abstract.value]] and proof. This is intended to be used with `given`-patterns: 122 + * {{{ 123 + * val data: Abstract[C] = ??? 124 + * data match 125 + * case Abstract(value, given _) => 126 + * // brings C[value.type] into scope 127 + * summon[C[value.type]] 128 + * }}} 129 + * @param x An [[Abstract]] value to dissect. 130 + * @tparam C Typeclass to extract from the [[Abstract]]. 131 + * @return Tuple of the [[Abstract]]'s [[Abstract.value]] and associated proof. 132 + */ 60 133 def unapply[C[_]](x: Abstract[C]): Some[(x.T, C[x.T])] = 61 134 import x.given 62 135 Some((x.value, /* WITH THIS TREASURE I */ summon)) ··· 94 167 extension (w: OutputStream) 95 168 def put[T: ByteCodec as c](x: T): Unit = c.write(w, x) 96 169 97 - trait BitCodec[@specialized T]: 170 + private[mica] trait BitCodec[@specialized T]: 98 171 def read(r: BitReader): T 99 172 def write(w: BitWriter, x: T): Unit 100 - object BitCodec: 173 + private[mica] object BitCodec: 101 174 /** 102 175 * The [[Unit]] type is serialized as an empty sequence of bits. 103 176 */ ··· 148 221 val layers: Array[Option[MultipartLayer]] = Array.fill(16)(None) 149 222 150 223 // divert 224 + class immutable extends Annotation 225 + 226 + /** 227 + * A '''side effect''' allows a [[Rune]] to have a greater effect on the world than mere arithmetic. Whereas runes 228 + * don't have access to a [[ServerWorld]], side effects ''do'', letting them read from and even write to the world. 229 + * However, they are less flexible than normal values - an execution ''must'' return a side effect to have it executed. 230 + */ 231 + trait SideEffect derives HasRegistry: 232 + /** 233 + * Arbitrary data for your side effect to use. For example, an effect that replaces a block would store the target 234 + * [[BlockPos]], as well as the new [[Block]]. 235 + */ 236 + type Data 237 + given dataCodec: Codec[Data] = deferred 238 + 239 + /** 240 + * Return value exposed to Mica code. This could be information such as a position (for a read-only effect), or 241 + * information about changes made (for example, the old [[Block]]). In the case you have ''nothing'' valuable to 242 + * return, use [[Unit]]. 243 + */ 244 + type Return: ValueType 245 + given returnType: ValueType[Return] = deferred 246 + 247 + /** 248 + * Arbitrary data your side effect might need in [[unexecute]], in addition to your return value. This data is never 249 + * written to disk, so can be anything. For example, the aforemented replace-block effect could store the associated 250 + * [[BlockEntity]] of the block. 251 + */ 252 + type RevertData 253 + 254 + /** 255 + * Check whether your side effect applies to the world ''before'' any changes. '''This method must not mutate `world`!''' 256 + * @param data Arbitrary [[Data]] to use (such as a target position, etc.) in your cast. 257 + * @param world Reference world for checking whether effects apply. '''Do not mutate this world!''' 258 + */ 259 + @immutable def check(data: Data)(using @immutable world: World @immutable): Unit 260 + @immutable def check(data: Data, player: PlayerEntity)(using @immutable world: World @immutable): Unit = check(data) 261 + 262 + /** 263 + * 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. 264 + * @param data Arbitrary [[Data]] to use (such as a target position, etc.) in your cast. 265 + * @param world World to apply effects to; the same world as passed to [[check]]. 266 + * @return Information for reversing the effect. Spells should provide reversion data if possible. 267 + */ 268 + def execute(data: Data)(using world: World): (Return, RevertData) 269 + 270 + /** 271 + * 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. 272 + * @param player Player initiating the cast. 273 + */ 274 + def execute(data: Data, player: PlayerEntity)(using World): (Return, RevertData) = execute(data) 275 + 276 + /** 277 + * 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. 278 + * @param data The same [[Data]] that [[execute]] was called with. 279 + * @param return The value returned by [[execute]]. 280 + * @param revert The revert data returned by [[execute]]. 281 + * @param world World to reverse this effect in; the same world as [[execute]]. 282 + */ 283 + def unexecute(data: Data, `return`: Return, revert: RevertData)(using world: World): Unit 284 + 285 + def translationFields(data: Data): Seq[AnyRef] = Seq() 286 + 287 + @register("noop") 288 + object NoopEffect extends SideEffect: 289 + override type Data = Unit 290 + override type Return = Unit 291 + override type RevertData = Unit 292 + 293 + override def check(data: Unit)(using world: World): Unit = () 294 + override def execute(data: Unit)(using world: World): (Unit, Unit) = ((), ()) 295 + override def unexecute(data: Unit, `return`: Unit, revert: Unit)(using world: World): Unit = () 296 + 297 + given unitValueType: ValueType[Unit]: 298 + override def eq[U: ValueType as v](x: Unit, y: U): Boolean = v eq unitValueType 299 + override def show(x: Unit): Text = Text.literal("Unit").styled(_.withColor(0x7a7a7a)) 300 + 301 + // divert(-1) 151 302 trait ByteCodec[T]: 152 303 def read(r: DataInput): T 153 304 def write(w: DataOutput, x: T): Unit 154 - // divert(-1) 155 305 156 306 given ByteCodec[MultipartSection]: 157 307 override def read(r: InputStream): MultipartSection = ··· 181 331 new ByteCodec[T]: 182 332 override def read(r: InputStream): T = c.read(BitReader(r)) 183 333 override def write(w: OutputStream, x: T): Unit = c.write(BitWriter(w), x) 334 + 335 + class sparse extends Annotation 336 + 184 337 // divert 185 - object RuneShift: 186 - opaque type RuneShift = Int 187 - inline def apply(value: Int): RuneShift = value 188 - def apply(x: Int, y: Int, z: Int, facing: Direction): RuneShift = x & 0b11 | y << 2 & 0b11100 | z << 5 & 0b1100000 | facing.ordinal << 7 189 - private def x_impl(s: Expr[RuneShift], x: Expr[Int])(using q: Quotes): Expr[Unit] = 190 - import q.reflect.* 191 - Assign(s.asTerm, '{${s}(x = ${x})}.asTerm).asExprOf[Unit] 192 - private def y_impl(s: Expr[RuneShift], y: Expr[Int])(using q: Quotes): Expr[Unit] = 193 - import q.reflect.* 194 - Assign(s.asTerm, '{${s}(y = ${y})}.asTerm).asExprOf[Unit] 195 - private def z_impl(s: Expr[RuneShift], z: Expr[Int])(using q: Quotes): Expr[Unit] = 196 - import q.reflect.* 197 - Assign(s.asTerm, '{${s}(z = ${z})}.asTerm).asExprOf[Unit] 198 - given ops: AnyRef: 199 - extension (s: RuneShift) 200 - @inline def value: Int = s 201 - @inline def x = s & 0b11 202 - @inline def y = s >> 2 & 0b111 203 - @inline def z = s >> 5 & 0b11 204 - @inline def facing = Direction.values()(s >> 7 & 0b111) 205 - @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) 206 - def storage(using w: World) = w.getComponent(AbstractRuneStorage.keys(s.value)) 207 - private def facing_impl(s: Expr[RuneShift], facing: Expr[Direction])(using q: Quotes): Expr[Unit] = 208 - import q.reflect.* 209 - Assign(s.asTerm, '{${s}(facing = ${facing})}.asTerm).asExprOf[Unit] 210 - val shapeCache: Array[VoxelShape] = Array.ofDim[VoxelShape](768).tap: m => 211 - for shift: RuneShift <- 0 until 768 do 212 - val middle = (shift.x / 4.0 - 0.5, shift.y / 8.0 - 0.5, shift.z / 4.0 - 0.5) 213 - m(shift) = VoxelShapes.cuboid(middle._1 - .25, middle._2, middle._3 - .25, middle._1 + .25, middle._2 + .0625, middle._3 + .25) 214 - end RuneShift 215 - export RuneShift.RuneShift 338 + 339 + def assumed[T, U]: T =:= U = summon[Int =:= Int].asInstanceOf[T =:= U] 340 + 341 + inline given tupleCodec: [T <: Tuple] => Codec[T] = compiletime.summonFrom: 342 + case ev: (T =:= EmptyTuple) => ev.substituteContra(Codec.unit(EmptyTuple)) 343 + 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) })) 216 344 217 - class sparse extends Annotation 345 + // divert(-1) 218 346 219 347 extension [T] (i: Iterator[T]) def dropAfter(cond: T => Boolean): Iterator[T] = 220 348 val split = i.span(!cond(_)) ··· 246 374 h.foreach(w.write) 247 375 w.write(t | STOP_BIT) 248 376 case _ => w.write(STOP_BIT) 377 + // divert 378 + 379 + @register("need_side_effect") 380 + object NeedSideEffectFrame extends ThunkFrame: 381 + override type Data = Unit 382 + override def accept[T: ValueType as ty](data: Unit, value: T): BoxedThunk = 383 + ty.cast[BoxedSideEffect](value) match 384 + case Some(e) => BoxedThunk(GotSideEffectFrame, e) 385 + 386 + @register("got_side_effect") 387 + object GotSideEffectFrame extends ThunkFrame: 388 + override type Data = BoxedSideEffect 389 + override def accept[T: ValueType](data: BoxedSideEffect, value: T): BoxedThunk = 390 + //throw RuneError(Text.literal("Too many side effects in one cast")) 391 + ??? 392 + def findRunes(start: RuneRef, prev: Option[RuneRef])(using World): Option[List[(Rune, RuneRef)]] = 393 + logger.debug(s"findRunes ${start} ${prev}") 394 + if start.isEmpty then 395 + None 396 + else 397 + val neigh = (start.rune.computeNeighbors(using start) -- prev).flatMap(findRunes(_, Some(start))).toSeq 398 + neigh match 399 + case Seq() => 400 + logger.debug(s"\tfindRunes ${start} ${prev}, neigh = ${neigh} | no more neighbors; stop here.") 401 + Some(List((start.rune, start))) 402 + case Seq(x) => 403 + val l = (start.rune, start)::x 404 + logger.debug(s"\tfindRunes ${start} ${prev}, neigh = ${neigh} | ${l}") 405 + Some(l) 406 + case _ => 407 + logger.debug(s"\tfindRunes ${start} ${prev}, neigh = ${neigh} | too many neighbors!") 408 + None 409 + 410 + given logger: Logger = LoggerFactory.getLogger(given_ModID.name) 411 + 412 + def parseRunes(runes: List[(Rune, RuneRef)])(using World): List[BoxedRune] = 413 + runes match 414 + case (head, given RuneRef)::next => 415 + val (data, rest) = head.read(next) 416 + BoxedRune(head, data)::parseRunes(rest) 417 + case Nil => Nil 418 + 419 + @tailrec 420 + def interpretRunes(runes: List[BoxedRune], stack: BoxedThunk): BoxedThunk = 421 + println(s"Interpreter: runes = $runes, stack = $stack") 422 + runes match 423 + case head::next => 424 + val newStack = head.rune.execute(head.data, stack) 425 + println(s"-> $head, stack = $newStack, $next") 426 + interpretRunes(next, newStack) 427 + case Nil => 428 + println("interpreter finished.") 429 + stack 430 + 431 + @throws[RuneError] 432 + def interpretForSideEffect(runes: List[BoxedRune]): Option[BoxedSideEffect] = 433 + val ret = interpretRunes(runes, BoxedThunk(NeedSideEffectFrame, ())) 434 + ret.narrow(GotSideEffectFrame) match 435 + case Some(value) => Some(value.data) 436 + case None => 437 + println(s"Nothing seems to happen (top of stack is ${ret})") 438 + None 439 + 440 + //given emptyTupleCodec: MapCodec[EmptyTuple] = MapCodec.unit(EmptyTuple) 441 + //inline given namedTupleCodec: [K <: String & Singleton, V: Codec as codec, R <: Tuple: MapCodec as tail] => MapCodec[(K, V) *: R] = 442 + // RecordCodecBuilder.mapCodec[(K, V) *: R]: i => 443 + // i.group[V, R]( 444 + // codec.fieldOf(constValue[K]).forGetter { case h *: _ => h._2 }, 445 + // tail.forGetter { case _ *: t => t }, 446 + // ).apply(i, (h, t) => (constValue[K], h) *: t) 447 + 448 + extension [T1, T2, R1, R2] (ev$1: T1 =:= R1) def zip(ev$2: T2 =:= R2): (T1, T2) =:= (R1, R2) = assumed 449 + extension [T1, T2, R1, R2] (ev: (T1, T2) =:= (R1, R2)) def unzip: (T1 =:= R1, T2 =:= R2) = (assumed, assumed) 450 + 451 + //inline given namedTupleCodec: [N <: Tuple, T <: Tuple] => MapCodec[NamedTuple[N, T]] = 452 + // compiletime.summonFrom: 453 + // case ev: ((N, T) =:= (EmptyTuple, EmptyTuple)) => MapCodec.unit(NamedTuple.Empty.asInstanceOf) 454 + // case ev: ((N, T) =:= (k *: ks, v *: vs)) => 455 + // erased val ev2 = summonInline[k <:< (String & Singleton & k)] 456 + // val name = constValue[k].asInstanceOf[String] 457 + // RecordCodecBuilder.mapCodec[NamedTuple[k *: ks, v *: vs]]: i => 458 + // i.group[v, vs]( 459 + // summonInline[Codec[v]].fieldOf(name).forGetter(???), 460 + // namedTupleCodec[ks, vs].forGetter(???) 461 + // ).apply(i, (h, t) => (name, h) *: t) 462 + 463 + //erased def namedTupleCodecTest: MapCodec[((a: Int, b: String))] = summon 249 464 250 465 /** 251 - * Exception for when a [[Rune]] cannot fully read its arguments. 466 + * Exception related to [[Rune]] evaluation, including the position. 252 467 * 253 - * @param distance The distance from the *end* the error was thrown. 254 - * @param message 468 + * @param message Cause of the error, displayed to the caster. 469 + * @param ref Location of the rune that threw the error. 255 470 */ 256 - case class RunesParseError(distance: Int, message: Text) extends Exception 471 + case class RuneError(message: Text)(using val ref: RuneRef) extends Exception 257 472 473 + /** 474 + * Stores a [[Rune]] together with its [[Rune.Data associated data]]. This allows easily passing around heterogeneous parsed rune values. 475 + * @param rune The rune stored in this pair (responsible for determining [[data]]'s type). 476 + * @param data The extra data associated with [[Rune]]. 477 + */ 258 478 case class BoxedRune(rune: Rune, data: rune.Data) 259 - inline given Codec[BoxedRune] = 260 - summonUnlessSeeding[Codec[Rune]] 479 + inline given `dispatch codec for boxed runes`: Codec[BoxedRune] = 480 + summon[Codec[Rune]] 261 481 .dispatch[BoxedRune](_.rune, (r: Rune) => 262 482 import r.given 263 483 summon[Codec[r.Data]].xmap(BoxedRune(r, _), ··· 267 487 .fieldOf("value") 268 488 // ]]>,) 269 489 ) 490 + inline given `dispatch codec for boxed values`: Codec[BoxedValue] = 491 + summon[Codec[ValueType[?]]] 492 + .dispatch[BoxedValue](_.given_ValueType_T, { 493 + case r: ValueType[t] => 494 + given ValueType[t] = r 495 + r.codec.xmap(BoxedValue(_)(using r), _.value.asInstanceOf[t]) 496 + // ifversion(>= 2100,<[[ 497 + .fieldOf("value") 498 + // ]]>,) 499 + }) 500 + given `dispatch codec for boxed thunk frames`: Codec[BoxedThunk] = 501 + summon[Codec[ThunkFrame]] 502 + .dispatch[BoxedThunk](_.tag, (r: ThunkFrame) => 503 + import r.given 504 + summon[Codec[r.Data]].xmap(BoxedThunk(r, _), 505 + // FIXME: Rewrite [dispatch] to avoid this unsafe cast. 506 + _.data.asInstanceOf[r.Data]) 507 + // ifversion(>= 2100,<[[ 508 + .fieldOf("value") 509 + // ]]>,) 510 + ) 511 + given `dispatch codec for boxed side-effects`: Codec[BoxedSideEffect] = 512 + summon[Codec[SideEffect]] 513 + .dispatch[BoxedSideEffect](_.effect, (r: SideEffect) => 514 + import r.given 515 + summon[Codec[r.Data]].xmap(BoxedSideEffect(r, _), 516 + // FIXME: Rewrite [dispatch] to avoid this unsafe cast. 517 + _.data.asInstanceOf[r.Data]) 518 + // ifversion(>= 2100,<[[ 519 + .fieldOf("value") 520 + // ]]>,) 521 + ) 270 522 523 + /** 524 + * Stores a [[ThunkFrame]] together with its [[ThunkFrame.Data associated data]] on the stack. 525 + * @param tag Type of this frame (must be registered). 526 + * @param data Extra data for this frame (e.g. already-collected arguments). 527 + */ 528 + case class BoxedThunk(tag: ThunkFrame, data: tag.Data): 529 + def narrow(to: ThunkFrame): Option[BoxedThunk { val tag: to.type; }] = 530 + if tag eq to then 531 + Some(asInstanceOf[BoxedThunk { val tag: to.type; }]) 532 + else 533 + None 534 + object BoxedThunk: 535 + inline def unapply[T](box: BoxedThunk): Option[(? <: Singleton & ThunkFrame { type Data = T; }, T)] = 536 + ??? 537 + 538 + trait BoxedValue: 539 + type T: ValueType 540 + val value: T 541 + def ==(other: BoxedValue): Boolean = 542 + import other.given 543 + summon[ValueType[T]].eq(value, other.value) 544 + def cast[R: {ValueType, ClassTag}]: Option[R] = summon[ValueType[T]].cast(value) 545 + def show: Text = summon[ValueType[T]].show(value) 546 + object BoxedValue: 547 + def apply[T: ValueType](value: T): BoxedValue = 548 + type _T = T 549 + val _value = value 550 + new BoxedValue: 551 + override type T = _T 552 + override val value: _T = _value 271 553 //def dependentCodec[T, R](arg: Codec[T], rhs: [R] ) 272 554 555 + // y'know, I should really make a generic Boxed type 556 + case class BoxedSideEffect(effect: SideEffect, data: effect.Data) 557 + 558 + // divert(-1) 273 559 /** 274 560 * Stores additional data out-of-band with existing world chunks. This is useful if you want to e.g. manage chunk loading independently, or 275 561 */ ··· 277 563 278 564 //object ExplodeRune extends Rune: 279 565 // type Data = (BlockPos, Int)-- 566 + 567 + // divert 280 568 281 569 /** 282 - * Does nothing. Pushes and pops no frames, consumes no runes, etc. 570 + * Does nothing. Pushes and pops no frames, consumes no runes, etc. Used as a placeholder for missing runes. 283 571 */ 284 572 @register("empty") 285 - object EmptyRune extends Rune: 286 - override type Data = Unit 573 + object EmptyRune extends SimpleRune: 287 574 override def surfaceSprite: Identifier = Rune.BASIC_TEXTURE 288 - override def read(rhs: List[Rune]): (Unit, List[Rune]) = ((), rhs) 289 - override def execute(data: Unit, frame: ThunkFrame): ThunkFrame = frame 575 + override def execute(frame: BoxedThunk): BoxedThunk = frame 576 + register: 577 + item.register() 290 578 291 579 @register("quote") 292 580 object QuoteRune extends Rune: 293 - override type Data = Seq[BoxedRune] 581 + override type Data = Seq[Rune] 294 582 override def surfaceSprite: Identifier = Rune.BASIC_TEXTURE 295 - override def read(rhs: List[Rune]): (Seq[BoxedRune], List[Rune]) = 296 - @throws[RunesParseError] 297 - def worker(rhs: List[Rune]): (List[BoxedRune], List[Rune]) = 298 - if rhs.isEmpty then 299 - throw RunesParseError(0, Text.literal("Quote with no corresponding unquote")) 300 - else if rhs.head == EndQuoteRune then 301 - (List.empty, rhs) 302 - else 303 - val r = rhs.head 304 - val (d, t) = r.read(rhs.tail) 305 - val (b, t2) = try 306 - worker(t) 307 - catch 308 - // FIXME: this +1 is going to bite me, isn't it? 309 - case RunesParseError(distance, message) => throw RunesParseError(distance + 1, message) 310 - locally: 311 - (BoxedRune(r, d)::b, t2) 583 + override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (Seq[Rune], List[(Rune, RuneRef)]) = 584 + @throws[RuneError] 585 + def worker(rhs: List[(Rune, RuneRef)])(using ref: RuneRef): (List[Rune], List[(Rune, RuneRef)]) = 586 + rhs match 587 + case Nil => throw RuneError(Text.literal("Quote with no corresponding unquote")) 588 + case (QuoteRune, given RuneRef)::xs => 589 + val (t, xt) = worker(xs) 590 + val (u, xu) = worker(xt) 591 + (t:::EndQuoteRune::u, xu) 592 + case (EndQuoteRune, r)::xs => (Nil, xs) 593 + case x::xs => 594 + val t = worker(xs) 595 + (x._1::t._1, t._2) 312 596 worker(rhs) 313 - override def execute(data: Seq[BoxedRune], frame: ThunkFrame): ThunkFrame = ??? 597 + override def execute(data: Seq[Rune], frame: BoxedThunk): BoxedThunk = 598 + frame.tag.accept(frame.data, data.map(BoxedValue(_))) 599 + register: 600 + item.register() 314 601 602 + /** 603 + * The registry key of the [[EmptyRune]]. This should be the default rune. 604 + */ 315 605 lazy val emptyRuneKey = summon[Registry[Rune]].getId(EmptyRune) 316 - sealed trait ConcreteRuneStorage extends AbstractRuneStorage: 606 + private[mica] sealed trait ConcreteRuneStorage extends AbstractRuneStorage: 317 607 // ifversion(>=2100, <[[ 318 608 override def readData(c: ReadView): Unit = 319 609 // ]]>, <[[ ··· 321 611 // ]]>) 322 612 if c.getBoolean("m", true) then 323 613 contents.clear() 614 + heap0.clear() 615 + heap1.clear() 616 + heap2.clear() 617 + heap3.clear() 324 618 val n = c.getLong("c", -1L) 325 619 if n != -1 then 326 620 for 327 - i <- 0L until c.getLong("c", 0L) 621 + i <- 0L until n 328 622 k = c.getLong(s"k$i", 0L) 329 623 v = c.getInt(s"v$i", 0) 330 624 p = c.getString(s"p$v", emptyRuneKey.toString) 331 625 r <- Option.fromNullable(Identifier.tryParse(p)) 332 626 do 333 - contents(k) = summonUnlessSeeding[Registry[Rune]].get(r) 627 + contents(k) = summon[Registry[Rune]].get(r) 334 628 else 335 629 boundary: 336 630 var i = 0 ··· 338 632 // ifversion(>=2100, <[[ 339 633 val dh = c.getOptionalIntArray(s"h$i") 340 634 val dl = c.getOptionalIntArray(s"d$i") 635 + val dx0 = c.getOptionalIntArray(s"x${i}0").orElse(Array.emptyIntArray) 636 + val dx1 = c.getOptionalIntArray(s"x${i}1").orElse(Array.emptyIntArray) 637 + val dx2 = c.getOptionalIntArray(s"x${i}2").orElse(Array.emptyIntArray) 638 + val dx3 = c.getOptionalIntArray(s"x${i}3").orElse(Array.emptyIntArray) 341 639 // ]]>, <[[ 342 640 val dh = c.getIntArray(s"h$i") 343 641 val dl = c.getIntArray(s"d$i") 642 + val dx0 = c.getIntArray(s"x${i}0").orElse(Array.emptyIntArray) 643 + val dx1 = c.getIntArray(s"x${i}1").orElse(Array.emptyIntArray) 644 + val dx2 = c.getIntArray(s"x${i}2").orElse(Array.emptyIntArray) 645 + val dx3 = c.getIntArray(s"x${i}3").orElse(Array.emptyIntArray) 344 646 // ]]>) 345 647 if dh.isEmpty || dl.isEmpty then boundary.break() 346 648 val rune = registryFor[Rune].get(Identifier.of( ··· 348 650 c.getString(s"p$i", emptyRuneKey.getPath), 349 651 )) 350 652 if rune != null then 351 - for (h: Int, l: Int) <- dh.get.zip(dl.get) do 352 - val k: Long = (h.toLong << 32) | (l & 0xFFFFFFFFL); 353 - contents.put(k, rune); 653 + for ((h: Int, l: Int), i: Int) <- dh.get.zip(dl.get).zipWithIndex do 654 + val k: Long = packLong(h, l); 655 + contents.put(k, rune) 656 + if dx0.length > i then heap0(k) = dx0(i) 657 + if dx1.length > i then heap1(k) = dx1(i) 658 + if dx2.length > i then heap2(k) = dx2(i) 659 + if dx3.length > i then heap3(k) = dx3(i) 354 660 i += 1 355 661 // ifversion(>=2100, <[[ 356 662 override def writeData(c: WriteView): Unit = ··· 363 669 val ns = registryFor[Rune].getId(k) 364 670 c.putString(s"n$i", ns.getNamespace) 365 671 c.putString(s"p$i", ns.getPath) 366 - val (h, l) = v.map(n => ((n >>> 32).toInt, n.toInt)).unzip 672 + val (h, l) = v.map(unpackLong(_)).unzip 367 673 c.putIntArray(s"h$i", h.toArray) 368 674 c.putIntArray(s"d$i", l.toArray) 675 + val (p1, p2) = (for x <- v yield ((heap0(x), heap1(x)), (heap2(x), heap3(x)))).unzip 676 + val (x0, x1) = p1.unzip; val (x2, x3) = p2.unzip 677 + c.putIntArray(s"x${i}0", x0.toArray.map(x => if x == null then 0 else x.intValue)) 678 + c.putIntArray(s"x${i}1", x1.toArray.map(x => if x == null then 0 else x.intValue)) 679 + c.putIntArray(s"x${i}2", x2.toArray.map(x => if x == null then 0 else x.intValue)) 680 + c.putIntArray(s"x${i}3", x3.toArray.map(x => if x == null then 0 else x.intValue)) 369 681 i += 1 682 + 683 + @register("double") 684 + object DoubleLiteralRune extends Rune: 685 + override type Data = Double 686 + 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) 687 + override def execute(data: Double, frame: BoxedThunk): BoxedThunk = frame.tag.accept(frame.data, data) 688 + register: 689 + DoubleLiteralRune.item.register() 690 + 370 691 // forloop(n, 0, 767, <[[ 371 692 // pushdef(RuneStorage, <[[ifelse($#,0,<[[<[[$0]]>]]><[[n]]>,<[[$0]]><[[<[[(]]>]]><[[$@]]><[[)]]>)]]>) 372 - case class RuneStorage(world: World, contents: Long2ObjectMap[Rune]) extends ConcreteRuneStorage: 693 + private[mica] case class RuneStorage(world: World, 694 + contents: Long2ObjectMap[Rune] = Duck.mkMap(), 695 + heap0: Long2IntMap = Long2IntOpenHashMap(), 696 + heap1: Long2IntMap = Long2IntOpenHashMap(), 697 + heap2: Long2IntMap = Long2IntOpenHashMap(), 698 + heap3: Long2IntMap = Long2IntOpenHashMap() 699 + ) extends ConcreteRuneStorage: 373 700 override type Concrete = RuneStorage 374 701 contents.defaultReturnValue(EmptyRune) 375 - object RuneStorage: 702 + private[mica] object RuneStorage: 376 703 val shift = RuneShift(n) 377 704 given key: ComponentKey[RuneStorage] = ComponentRegistry.getOrCreate(Identifier.of("mica", "runes<[[]]>n"), classOf[RuneStorage]) 378 705 // divert(1) 379 706 /*dnl*/val factories: WorldComponentFactoryRegistry = erasedValue/* 380 - */Duck.register(factories, RuneStorage.key, classOf[RuneStorage], RuneStorage(_, Duck.mkMap())) 707 + */Duck.register(factories, RuneStorage.key, classOf[RuneStorage], RuneStorage(_)) 381 708 AbstractRuneStorage.keys(n) = RuneStorage.key 382 709 // divert 383 710 // popdef(<[[RuneStorage]]>) 711 + // ]]>) 712 + // forloop(n, 0, 63, <[[ 713 + // pushdef(FluxStorageInfo, <[[ifelse($#,0,<[[<[[$0]]>]]><[[n]]>,<[[$0]]><[[<[[(]]>]]><[[$@]]><[[)]]>)]]>) 714 + private[mica] case class FluxStorageInfo(world: World, contents: Long2FloatMap) extends Component: 715 + override def readData(readView: ReadView): Unit = ??? 716 + override def writeData(writeView: WriteView): Unit = ??? 717 + // divert 718 + // popdef(<[[FluxStorageInfo]]>) 384 719 // ]]>) 385 720 721 + given ValueType[Double]: 722 + override def eq[U: ValueType as v](x: Double, y: U): Boolean = v.cast[Double](y).exists(y => x =~ y) 723 + override def cast[R: ClassTag as r](x: Double): Option[R] = 724 + r match 725 + case ClassTag.Double => Some(x) 726 + case ClassTag.Int => 727 + if x =~ x.round.toInt then 728 + Some(x.round.toInt) 729 + else 730 + None 731 + case _ => None 732 + 733 + override def show(x: Double): Text = Text.literal(s"$x").styled(_.withColor(0xab3900)) 734 + 735 + class RuneBeStartinShit private(val direction: Direction) extends Rune: 736 + override def surfaceSprite: Identifier = Rune.IMPETUS_TEXTURE 737 + override type Data = Unit 738 + override def read(rhs: List[(Rune, RuneRef)])(using RuneRef, World): (Unit, List[(Rune, RuneRef)]) = ((), rhs) 739 + override def execute(data: Unit, frame: BoxedThunk): BoxedThunk = frame 740 + override def computeNeighbors(using r: RuneRef): Set[RuneRef] = direction match 741 + case Direction.NORTH => Set(r.north.north) 742 + case Direction.SOUTH => Set(r.south.south) 743 + case Direction.WEST => Set(r.west.west) 744 + case Direction.EAST => Set(r.east.east) 745 + def register(): Unit = 746 + Registry.register(registryFor[Rune], Identifier.of("mica", s"arrow_$direction"), this) 747 + item.register() 748 + object RuneBeStartinShit: 749 + val north = new RuneBeStartinShit(Direction.NORTH) 750 + val east = new RuneBeStartinShit(Direction.EAST) 751 + val south = new RuneBeStartinShit(Direction.SOUTH) 752 + val west = new RuneBeStartinShit(Direction.WEST) 753 + def apply(dir: Direction) = dir match 754 + case Direction.NORTH => north 755 + case Direction.SOUTH => east 756 + case Direction.WEST => south 757 + case Direction.EAST => west 758 + def unapply(r: RuneBeStartinShit): Some[Direction] = Some(r.direction) 759 + register: 760 + RuneBeStartinShit.north.register() 761 + RuneBeStartinShit.east.register() 762 + RuneBeStartinShit.south.register() 763 + RuneBeStartinShit.west.register() 764 + 765 + @register("seq") 766 + given ValueType[Seq[BoxedValue]]: 767 + val color = TextColor.fromRgb(0xb6fc03) 768 + override def eq[U: ValueType as v](x: Seq[BoxedValue], y: U): Boolean = 769 + v.cast[Seq[BoxedValue]](y).exists(y => x.zip(y).forall(p => p._1 == p._2)) 770 + override def show(x: Seq[BoxedValue]): Text = 771 + x match 772 + case h+:t => 773 + val m = Text.literal("(").styled(_.withColor(color)) 774 + m.append(h.show) 775 + for x <- t do 776 + m.append(" ").append(x.show) 777 + m.append(")") 778 + case Seq() => Text.literal("()").styled(_.withColor(color)) 779 + given ValueType[Rune]: 780 + val color = TextColor.fromRgb(0xffe46e) 781 + override def eq[U: ValueType as v](x: Rune, y: U): Boolean = v.cast[Rune](y).exists(y => x eq y) 782 + override def show(x: Rune): Text = Text.literal(registryFor[Rune].getId(x).toTranslationKey("mica.runes")) 783 + 784 + @register("fatboy_slim") 785 + object PosLiteral extends Rune: 786 + override type Data = RuneRef 787 + override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (RuneRef, List[(Rune, RuneRef)]) = (ref, rhs) 788 + override def execute(data: RuneRef, frame: BoxedThunk): BoxedThunk = frame.tag.accept(frame.data, data.floatPos) 789 + register: 790 + PosLiteral.item.register() 791 + 792 + @register("teleport") 793 + object TeleportRune extends Rune: 794 + override type Data = RuneRef 795 + @register("teleport") 796 + object Effect extends SideEffect: 797 + override type Data = (RuneRef, EntityRef) 798 + override type Return = Unit 799 + override type RevertData = Vec3d 800 + override def check(data: (RuneRef, EntityRef))(using world: World): Unit = 801 + given RuneRef = data._1 802 + if !world.getWorldBorder.contains(data._1.pos) then 803 + throw RuneError(Text.literal("Cannot teleport outside the world border")) 804 + override def execute(data: (RuneRef, EntityRef))(using world: World): (Unit, Vec3d) = 805 + val entity = data._2.entity 806 + if entity == null then 807 + throw RuneError(Text.literal("Could not find ").append(summon[ValueType[EntityRef]].show(data._2)).append(" in the world"))(using data._1) 808 + val pos = entity.getPos 809 + entity.setPosition(data._1.floatPos) 810 + ((), pos) 811 + override def unexecute(data: (RuneRef, EntityRef), `return`: Unit, revert: Vec3d)(using world: World): Unit = 812 + data._2.entity.setPosition(revert) 813 + @register("teleport/0") 814 + object Frame extends ThunkFrame: 815 + override type Data = (RuneRef, BoxedThunk) 816 + override def accept[T: ValueType as v](data: (RuneRef, BoxedThunk), value: T): BoxedThunk = 817 + val entity = v.cast[EntityRef](value).get 818 + println(data) 819 + data._2.tag.accept(data._2.data, BoxedSideEffect(Effect, (data._1, entity))) 820 + override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (RuneRef, List[(Rune, RuneRef)]) = (ref, rhs) 821 + override def execute(data: RuneRef, frame: BoxedThunk): BoxedThunk = BoxedThunk(Frame, (data, frame)) 822 + register: 823 + TeleportRune.item.register() 824 + given Codec[RuneRef] = RecordCodecBuilder.create: i => 825 + i.group( 826 + Codec.LONG.fieldOf("pos").xmap(BlockPos.fromLong, _.asLong).forGetter(_.pos), 827 + Codec.INT.fieldOf("dir").xmap(RuneShift.apply, _.value).forGetter(_.shift), 828 + ).apply(i, RuneRef(_, _)) 829 + given Codec[((RuneRef, EntityRef))] = RecordCodecBuilder.create: i => 830 + i.group( 831 + given_Codec_RuneRef.fieldOf("to").forGetter(_._1), 832 + given_Codec_EntityRef.fieldOf("from").forGetter(_._2), 833 + ).apply(i, (_, _)) 834 + given Codec[((RuneRef, BoxedThunk))] = RecordCodecBuilder.create: i => 835 + i.group( 836 + given_Codec_RuneRef.fieldOf("to").forGetter(_._1), 837 + `dispatch codec for boxed thunk frames`.fieldOf("from").forGetter(_._2), 838 + ).apply(i, (_, _)) 839 + 840 + given `given_Codec_java.lang.Double`: Codec[java.lang.Double] = Codec.DOUBLE 841 + 842 + @register("reveal") 843 + private[mica] object RevealRune extends Rune: 844 + override type Data = RuneRef 845 + @register("reveal/0") 846 + object Frame extends ThunkFrame: 847 + override type Data = (RuneRef, BoxedThunk) 848 + override def accept[T: ValueType](data: (RuneRef, BoxedThunk), value: T): BoxedThunk = ??? 849 + given Codec[(RuneRef, BoxedThunk)] = 850 + RecordCodecBuilder.create: i => 851 + i.group( 852 + codec[RuneRef].fieldOf("_1").forGetter(_._1), 853 + codec[BoxedThunk].fieldOf("_2").forGetter(_._2), 854 + ).apply(i, (_, _)) 855 + override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (RevealRune.Data, List[(Rune, RuneRef)]) = (summon, rhs) 856 + override def execute(data: RuneRef, frame: BoxedThunk): BoxedThunk = BoxedThunk(Frame, (data, frame)) 857 + register: 858 + RevealRune.item.register() 859 + 860 + private[mica] object JackBlack: 861 + def pickaxe(using ci: CallbackInfoReturnable[ActionResult])(ctx: ItemUsageContext): Unit = 862 + val p = ctx.getHitPos 863 + val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt) 864 + val b = BlockPos.Mutable(p.x, p.y, p.z) 865 + given World = ctx.getWorld 866 + if q.getX == 4 then 867 + q.setX(0) 868 + b.setX(b.getX + 1) 869 + if q.getY == 8 then 870 + q.setY(0) 871 + b.setY(b.getY + 1) 872 + if q.getZ == 4 then 873 + q.setZ(0) 874 + b.setZ(b.getZ + 1) 875 + val h = RuneShift(q.getX, q.getY, q.getZ, ctx.getSide) 876 + val ref = RuneRef(b, h) 877 + if !ref.isEmpty then 878 + if !given_World.isClient then 879 + val stack = ItemStack(ref.rune.item.value, 1) 880 + assert(!stack.isEmpty) 881 + assert(Registries.ITEM.getId(stack.getItem) != null) 882 + stack.applyChanges(ComponentChanges.builder().add(DataComponentTypes.PROFILE, 883 + ProfileComponent( 884 + Optional.empty, 885 + Optional.of(Uuids.toUuid(Array(ref.heap0, ref.heap1, ref.heap2, ref.heap3))), 886 + PropertyMap())).build()) 887 + val ent: ItemEntity = ItemEntity(given_World, p.x, p.y, p.z, stack) 888 + ent.setPosition(p) 889 + ent.addVelocity(h.facing.getDoubleVector.multiply(0.1)) 890 + given_World.spawnEntity(ent) 891 + ref.rune = EmptyRune 892 + ci.setReturnValue(ActionResult.SUCCESS) 893 + def flintAndSTEEL_!!(using ci: CallbackInfoReturnable[ActionResult])(ctx: ItemUsageContext): Unit = 894 + val p = ctx.getHitPos 895 + val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt) 896 + val b = BlockPos.Mutable(p.x, p.y, p.z) 897 + given World = ctx.getWorld 898 + if q.getX == 4 then 899 + q.setX(0) 900 + b.setX(b.getX + 1) 901 + if q.getY == 8 then 902 + q.setY(0) 903 + b.setY(b.getY + 1) 904 + if q.getZ == 4 then 905 + q.setZ(0) 906 + b.setZ(b.getZ + 1) 907 + val h = RuneShift(q.getX, q.getY, q.getZ, ctx.getSide) 908 + val ref = RuneRef(b, h) 909 + ref.rune match 910 + case RuneBeStartinShit(d) => 911 + boundary: 912 + val runes = findRunes(ref, None).getOrElse: 913 + ci.setReturnValue(ActionResult.FAIL) 914 + given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS) 915 + ctx.getPlayer.sendMessage(Text.literal("The spark fizzles out."), true) 916 + boundary.break() 917 + ci.setReturnValue(ActionResult.SUCCESS) 918 + try 919 + given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.ITEM_FLINTANDSTEEL_USE, SoundCategory.PLAYERS) 920 + val boxedRunes = parseRunes(runes) 921 + interpretForSideEffect(boxedRunes) match 922 + case Some(value) => 923 + println(s"Hello ladies and gentlemen! Today we will be executing ${value}! Are we on the client? ${given_World.isClient}!") 924 + value.effect.check(value.data) 925 + value.effect.execute(value.data) 926 + case None => 927 + ctx.getPlayer.sendMessage(Text.literal("Nothing seems to happen..."), true) 928 + catch 929 + case e: RuneError => 930 + val p = e.ref.floatPos 931 + given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS) 932 + ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 5*20)) 933 + ctx.getPlayer.sendMessage(e.message, true) 934 + case e: (NotImplementedError | MatchError) => 935 + e.printStackTrace() 936 + ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 5*20)) 937 + given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS) 938 + ctx.getPlayer.sendMessage(Text.literal("You feel strange..."), true) 939 + case e: Exception => 940 + e.printStackTrace() 941 + ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.NAUSEA, 15*20)) 942 + ctx.getPlayer.addStatusEffect(StatusEffectInstance(StatusEffects.BLINDNESS, 15*20)) 943 + given_World.playSound(ctx.getPlayer, p.x, p.y, p.z, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.PLAYERS) 944 + ctx.getPlayer.sendMessage(Text.literal("The world is falling apart at the seams..."), true) 945 + case _ => 946 + 947 + lazy val runeProbe: Registrar[Item] = 948 + cursedRegister(Identifier.of("mica", "rune_probe"), Item.Settings()): 949 + new Item(_): 950 + override def useOnBlock(ctx: ItemUsageContext): ActionResult = 951 + val p = ctx.getHitPos 952 + val q = BlockPos.Mutable((p.x * 4).round.toInt, (p.y * 8).round.toInt, (p.z * 4).round.toInt) 953 + val b = BlockPos.Mutable(p.x, p.y, p.z) 954 + given World = ctx.getWorld 955 + if q.getX == 4 then 956 + q.setX(0) 957 + b.setX(b.getX + 1) 958 + if q.getY == 8 then 959 + q.setY(0) 960 + b.setY(b.getY + 1) 961 + if q.getZ == 4 then 962 + q.setZ(0) 963 + b.setZ(b.getZ + 1) 964 + val h = RuneShift(q.getX, q.getY, q.getZ, ctx.getSide) 965 + val ref = RuneRef(b, h) 966 + given player: PlayerEntity = ctx.getPlayer 967 + if given_World.isClient then 968 + player.sendMessage(Text.literal("Client Rune Info:").styled(_.withColor(0xb36909)), false) 969 + else 970 + player.sendMessage(Text.literal("Server Rune Info:").styled(_.withColor(0x06bf72)), false) 971 + 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) 972 + player.sendMessage(Text.literal(s"Rune: ${ref.rune}"), false) 973 + player.sendMessage(Text.literal(s"Heap: ${ref.heap0} ${ref.heap1} ${ref.heap2} ${ref.heap3}"), false) 974 + ActionResult.SUCCESS 975 + @register("player_literal") 976 + object PlayerLiteral extends Rune: 977 + override type Data = EntityRef 978 + override def read(rhs: List[(Rune, RuneRef)])(using ref: RuneRef, world: World): (EntityRef, List[(Rune, RuneRef)]) = 979 + val uuid = EntityRef(Uuids.toUuid(Array(ref.heap0, ref.heap1, ref.heap2, ref.heap3))) 980 + println(s"playerliteral${ref}sayswhat? ${uuid} obviously") 981 + (uuid, rhs) 982 + override def execute(data: EntityRef, frame: BoxedThunk): BoxedThunk = 983 + println(s"AND HE SHOOTS ${data} TO THE STACK") 984 + frame.tag.accept(frame.data, data) 985 + override def fillHeap(using ref: RuneRef, player: PlayerEntity, world: World)(): Unit = 986 + val uuid = Uuids.toIntArray(player.getUuid) 987 + ref.heap0 = uuid(0) 988 + ref.heap1 = uuid(1) 989 + ref.heap2 = uuid(2) 990 + ref.heap3 = uuid(3) 991 + println(s"who up player literal they ${uuid.mkString("Array(", ", ", ")")} (${ref}) on the client? ${world.isClient}") 992 + register: 993 + PlayerLiteral.item.register() 994 + 995 + @register("entity") 996 + given ValueType[EntityRef]: 997 + override def eq[U: ValueType as v](x: EntityRef, y: U): Boolean = v.cast[EntityRef](y).exists(y => x.uuid == y.uuid) 998 + override def show(x: EntityRef): Text = Text.literal(x.uuid.toString.split('-').last).styled(_.withColor(0x9116c9)/*.withFont(Identifier.of("minecraft", "illageralt"))*/) 999 + 1000 + @register("side_effect") 1001 + given ValueType[BoxedSideEffect]: 1002 + override def eq[U: ValueType as v](x: BoxedSideEffect, y: U): Boolean = 1003 + v.cast[BoxedSideEffect](y).exists(y => x.effect == y.effect && x.data == y.data) 1004 + 1005 + override def show(x: BoxedSideEffect): Text = Text.translatable(registryFor[SideEffect].getId(x.effect).toTranslationKey("mica.side_effect"), x.effect.translationFields(x.data)) 1006 + 1007 + case class EntityRef(target: UUID | Entity): 1008 + def uuid: UUID = target match 1009 + case u: UUID => u 1010 + case e: Entity => e.getUuid 1011 + def entity(using w: World): Entity = target match 1012 + case e: Entity => e 1013 + case u: UUID => w.getEntity(u) 1014 + given Codec[EntityRef] = Uuids.STRICT_CODEC.xmap(EntityRef(_), _.uuid) 1015 + trait UnaryRune extends SimpleRune: 1016 + type Arg0: {Codec, ValueType, ClassTag} 1017 + type Return: ValueType 1018 + private object ArgFrame extends ThunkFrame: 1019 + type Data = BoxedThunk 1020 + override def accept[T: ValueType as v](data: BoxedThunk, value: T): BoxedThunk = 1021 + v.cast[Arg0](value) match 1022 + case Some(value) => 1023 + data.tag.accept(data.data, run(value)) 1024 + override def execute(frame: BoxedThunk): BoxedThunk = BoxedThunk(ArgFrame, frame) 1025 + def run(x: Arg0): Return 1026 + protected def registerFrames(): Unit = 1027 + val id = registryFor[Rune].getId(this) 1028 + Registry.register(registryFor[ThunkFrame], id.withSuffixedPath("/0"), ArgFrame) 1029 + trait BinaryRune extends SimpleRune: 1030 + type Arg0: {Codec, ValueType, ClassTag} 1031 + type Arg1: {Codec, ValueType, ClassTag} 1032 + type Return: ValueType 1033 + private object Arg0Frame extends ThunkFrame: 1034 + type Data = (frame: BoxedThunk) 1035 + override def accept[T: ValueType as v](data: (frame: BoxedThunk), value: T): BoxedThunk = 1036 + v.cast[Arg0](value) match 1037 + case Some(value) => 1038 + BoxedThunk(Arg1Frame, (data.frame, value)) 1039 + private object Arg1Frame extends ThunkFrame: 1040 + type Data = (frame: BoxedThunk, arg0: Arg0) 1041 + override def accept[T: ValueType as v](data: (frame: BoxedThunk, arg0: Arg0), value: T): BoxedThunk = 1042 + v.cast[Arg1](value) match 1043 + case Some(value) => 1044 + val h = data.frame 1045 + h.tag.accept(h.data, run(data.arg0, value)) 1046 + override def execute(frame: BoxedThunk): BoxedThunk = BoxedThunk(Arg0Frame, (frame = frame)) 1047 + def run(x: Arg0, y: Arg1): Return 1048 + protected def registerFrames(): Unit = 1049 + val id = registryFor[Rune].getId(this) 1050 + if id == null then 1051 + throw IllegalStateException("Rune frames registered before rune itself") 1052 + Registry.register(registryFor[ThunkFrame], id.withSuffixedPath("/0"), Arg0Frame) 1053 + Registry.register(registryFor[ThunkFrame], id.withSuffixedPath("/1"), Arg1Frame) 1054 + 1055 + given `codec for just a frame`: Codec[((frame: BoxedThunk))] = codec[BoxedThunk].fieldOf("frame").codec().xmap((frame = _), _.frame) 1056 + given [T: Codec as c] => Codec[((frame: BoxedThunk, arg0: T))] = 1057 + RecordCodecBuilder.create: i => 1058 + i.group( 1059 + codec[BoxedThunk].fieldOf("frame").forGetter(_.frame), 1060 + c.fieldOf("arg0").forGetter(_.arg0) 1061 + ).apply(i, (_, _)) 1062 + 1063 + @register("add") 1064 + object AdditionRune extends BinaryRune: 1065 + override type Arg0 = Double 1066 + override type Arg1 = Double 1067 + override type Return = Double 1068 + 1069 + override def run(x: Double, y: Double): Double = x + y 1070 + 1071 + register: 1072 + registerFrames() 1073 + AdditionRune.item.register() 1074 + 1075 + given unmapsYourCodec: [T: MapCodec as c] => Codec[T] = c.codec 1076 + 1077 + @register("sub") 1078 + object SubitionRune extends BinaryRune: 1079 + override type Arg0 = Double 1080 + override type Arg1 = Double 1081 + override type Return = Double 1082 + 1083 + override def run(x: Double, y: Double): Double = x - y 1084 + 1085 + register: 1086 + registerFrames() 1087 + SubitionRune.item.register() 1088 + 1089 + @register("mul") 1090 + object MulitionRune extends BinaryRune: 1091 + override type Arg0 = Double 1092 + override type Arg1 = Double 1093 + override type Return = Double 1094 + 1095 + override def run(x: Double, y: Double): Double = x * y 1096 + 1097 + register: 1098 + registerFrames() 1099 + MulitionRune.item.register() 1100 + 1101 + given Codec[Double] = Codec.DOUBLE.xmap(locally(_), locally(_)) 1102 + 1103 + @register("div") 1104 + object DivitionRune extends BinaryRune: 1105 + override type Arg0 = Double 1106 + override type Arg1 = Double 1107 + override type Return = Double 1108 + 1109 + override def run(x: Double, y: Double): Double = x / y 1110 + 1111 + register: 1112 + registerFrames() 1113 + DivitionRune.item.register() 1114 + 1115 + extension (x: Double) @targetName("~=") def =~(y: Double): Boolean = Math.abs(x - y) < 0.0001 1116 + 1117 + given Codec[Vec3d] = Vec3d.CODEC 1118 + given ValueType[Vec3d]: 1119 + override def eq[U: ValueType as v](x: Vec3d, y: U): Boolean = 1120 + v.cast[Vec3d](y).exists: y => 1121 + x.x =~ y.x && x.y =~ y.y && x.z =~ y.z 1122 + override def cast[R: ClassTag](x: Vec3d): Option[R] = super.cast(x) 1123 + 1124 + override def show(x: Vec3d): Text = Text.literal(s"{${x.x}, ${x.y}, ${x.z}}") 1125 + 1126 + inline def codec[T: Codec as c] = c 1127 + 1128 + case class AttributeReference(target: LivingEntity | UUID, attribute: EntityAttribute): 1129 + def uuid: UUID = target match 1130 + case e: LivingEntity => e.getUuid 1131 + case u: UUID => u 1132 + def entity(using w: World): Option[LivingEntity] = target match 1133 + case e: LivingEntity => Some(e) 1134 + case u: UUID => w.getEntity(u) match 1135 + case null => None 1136 + case e: LivingEntity => Some(e) 1137 + case _ => None 1138 + def attributeInstance(using World): Option[EntityAttributeInstance] = entity.map(_.getAttributeInstance(Registries.ATTRIBUTE.getEntry(attribute))) 1139 + given `codec for entity attributes using the registry`: Codec[EntityAttribute] = Registries.ATTRIBUTE.getCodec 1140 + given `codec for references to entity attributes`: Codec[AttributeReference] = 1141 + RecordCodecBuilder.create: i => 1142 + i.group( 1143 + Uuids.INT_STREAM_CODEC.fieldOf("entity").forGetter(_.uuid), 1144 + codec[EntityAttribute].fieldOf("attribute").forGetter(_.attribute), 1145 + ).apply(i, AttributeReference(_, _)) 1146 + given `hey did you know attributes are values`: ValueType[AttributeReference]: 1147 + 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)) 1148 + override def show(x: AttributeReference): Text = Text.translatable(x.attribute.getTranslationKey).styled(_.withColor(0x07b891)) 1149 + 1150 + @register("nyaboom") 1151 + object ExplosionRune extends BinaryRune: 1152 + override type Arg0 = Vec3d 1153 + override type Arg1 = Double 1154 + override type Return = BoxedSideEffect 1155 + override def surfaceSprite: Identifier = Rune.SPELL_TEXTURE 1156 + 1157 + @register("nyaboom") 1158 + object Effect extends SideEffect: 1159 + override type Data = (pos: Vec3d, power: Double) 1160 + override type Return = Unit 1161 + override type RevertData = Unit // good luck un-exploding someone 1162 + 1163 + override def check(data: (pos: Vec3d, power: Double))(using world: World): Unit = 1164 + // explosions are *probably* fine 1165 + () 1166 + override def execute(data: (pos: Vec3d, power: Double))(using world: World): (Unit, Unit) = 1167 + if !world.isClient then 1168 + world.createExplosion(null: Entity, data.pos.x, data.pos.y, data.pos.z, data.power.toFloat, ExplosionSourceType.TRIGGER) 1169 + ((), ()) 1170 + override def unexecute(data: (pos: Vec3d, power: Double), `return`: Unit, revert: Unit)(using world: World): Unit = 1171 + // can't unexplode people 1172 + () 1173 + 1174 + override def run(pos: Vec3d, power: Double) = BoxedSideEffect(Effect, (pos, power)) 1175 + 1176 + register: 1177 + ExplosionRune.item.register() 1178 + registerFrames() 1179 + 1180 + given Codec[((pos: Vec3d, power: Double))] = 1181 + RecordCodecBuilder.create[(pos: Vec3d, power: Double)]: i => 1182 + i.group( 1183 + codec[Vec3d].fieldOf("pos").forGetter(_.pos), 1184 + codec[Double].fieldOf("power").forGetter(_.power), 1185 + ).apply(i, (pos, power) => (pos = pos, power = power)) 1186 + 386 1187 @register("endquote") 387 1188 object EndQuoteRune extends Rune: 388 1189 type Data = Nothing 389 1190 override def surfaceSprite: Identifier = Rune.BASIC_TEXTURE 390 - override def read(rhs: List[Rune]): (Nothing, List[Rune]) = throw RunesParseError(rhs.length, Text.literal("Unquote with no corresponding quote")) 391 - override def execute(data: Nothing, frame: ThunkFrame): ThunkFrame = data 1191 + override def read(rhs: List[(Rune, RuneRef)])(using RuneRef, World): (Nothing, List[(Rune, RuneRef)]) = throw RuneError(Text.literal("Unquote with no corresponding quote")) 1192 + override def execute(data: Nothing, frame: BoxedThunk): BoxedThunk = data 1193 + 1194 + register: 1195 + EndQuoteRune.item.register() 392 1196 // divert 393 1197 394 - given [T](using Codec[T]): Codec[Seq[T]] = Codec.list[T](summon).xmap(_.toSeq, locally(_)) 395 - given Codec[Unit] = Codec.unit(()) 396 - given Codec[Nothing] = Codec.of(new Encoder[Nothing]: 1198 + given seqCodec: [T: Codec as c] => Codec[Seq[T]] = Codec.list[T](c).xmap(_.toSeq, locally(_)) 1199 + given unitCodec: Codec[Unit] = Codec.unit(()) 1200 + given nothingCodec: Codec[Nothing] = Codec.of(new Encoder[Nothing]: 397 1201 override def encode[T](input: Nothing, ops: DynamicOps[T], prefix: T): DataResult[T] = input, Decoder.error("Nothing codec")) 398 1202 399 1203 extension [T] (x: T) ··· 406 1210 case r: R => Some(r) 407 1211 case _ => None 408 1212 409 - class ComponentInitializer extends WorldComponentInitializer: 410 - def registerWorldComponentFactories(factories: WorldComponentFactoryRegistry) = 1213 + private[mica] class ComponentInitializer extends WorldComponentInitializer: 1214 + def registerWorldComponentFactories(factories: WorldComponentFactoryRegistry): Unit = 411 1215 /*dnl*/ () /* 412 1216 *///undivert(1) 413 1217 def init(): Unit = 414 - register() 415 - // eww 416 - println("BELIEVE IT OR NOT WE ARE HERE") 417 - EmptyRune.item.register() 418 - QuoteRune.item.register() 419 - EndQuoteRune.item.register() 420 - println("do you have hair?") 1218 + runeProbe.register() 1219 + register()
+8
src/main/scala/org/net/eu/pool/mica/mixin/BrainMixin.java
··· 1 + package org.net.eu.pool.mica.mixin; 2 + 3 + import net.minecraft.entity.ai.brain.Brain; 4 + import org.spongepowered.asm.mixin.Mixin; 5 + 6 + @Mixin(Brain.class) 7 + public class BrainMixin { 8 + }
+18
src/main/scala/org/net/eu/pool/mica/mixin/FlintAndSteelItemMixin.java
··· 1 + package org.net.eu.pool.mica.mixin; 2 + 3 + import net.minecraft.item.FlintAndSteelItem; 4 + import net.minecraft.item.ItemUsageContext; 5 + import net.minecraft.util.ActionResult; 6 + import org.net.eu.pool.mica.JackBlack$; 7 + import org.spongepowered.asm.mixin.Mixin; 8 + import org.spongepowered.asm.mixin.injection.At; 9 + import org.spongepowered.asm.mixin.injection.Inject; 10 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 11 + 12 + @Mixin(FlintAndSteelItem.class) 13 + public class FlintAndSteelItemMixin { 14 + @Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true) 15 + void startRunes(ItemUsageContext context, CallbackInfoReturnable<ActionResult> cir) { 16 + JackBlack$.MODULE$.flintAndSTEEL_$bang$bang(cir, context); 17 + } 18 + }
+27
src/main/scala/org/net/eu/pool/mica/mixin/ItemMixin.java
··· 1 + package org.net.eu.pool.mica.mixin; 2 + 3 + import net.minecraft.item.Item; 4 + import net.minecraft.item.ItemUsageContext; 5 + import net.minecraft.registry.entry.RegistryEntry; 6 + import net.minecraft.registry.tag.ItemTags; 7 + import net.minecraft.util.ActionResult; 8 + import org.net.eu.pool.mica.JackBlack; 9 + import org.net.eu.pool.mica.JackBlack$; 10 + import org.spongepowered.asm.mixin.Final; 11 + import org.spongepowered.asm.mixin.Mixin; 12 + import org.spongepowered.asm.mixin.Shadow; 13 + import org.spongepowered.asm.mixin.injection.At; 14 + import org.spongepowered.asm.mixin.injection.Inject; 15 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 16 + 17 + @Mixin(net.minecraft.item.Item.class) 18 + public class ItemMixin { 19 + @Shadow @Final private RegistryEntry.Reference<Item> registryEntry; 20 + 21 + @Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true) 22 + void useOnBlock(ItemUsageContext context, CallbackInfoReturnable<ActionResult> cir) { 23 + if (registryEntry.isIn(ItemTags.PICKAXES)) { 24 + JackBlack$.MODULE$.pickaxe(cir, context); 25 + } 26 + } 27 + }
+3
src/main/scala/org/net/eu/pool/mica/package.scala
··· 1 + package org.net.eu.pool 2 + 3 + package object mica