repo for my hex addons :3
0
fork

Configure Feed

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

merge progress 2

+1967 -1155
+14
TODO.md
··· 1 + # underevaluate 2 + * hermes' gambling <ne,qqqeeaqq> 3 + * find other things 4 + # in general 5 + * make checklist of hexic features 6 + - [x] patchworks 7 + - [x] murmur 8 + - [x] greater reveal 9 + - [ ] mediaweave 10 + * find other neat things to do 11 + 12 + --- 13 + old todo list: 14 + 1 15 # before next release 2 16 * `ktwznkmp` making Noetic Gateway always go to a demiplane's center 3 17 * `ktwznkmp` special demiplane iota handling
+237 -205
build.gradle.kts
··· 6 6 import kotlin.io.path.exists 7 7 import kotlin.io.path.readText 8 8 import groovy.json.JsonOutput 9 + import `java.nio`.file.Files; 10 + import kotlin.io.path.deleteIfExists 9 11 10 12 plugins { 11 13 id("fabric-loom") version "1.13-SNAPSHOT" ··· 16 18 id("org.eu.net.pool.mc-plugin") version "0.1.1" 17 19 } 18 20 19 - try { 20 - tasks.named("downloadRenderDoc") { 21 - setProperty("output", file("$buildDir/renderdoc_1.37.tar.gz")) 22 - } 21 + allprojects { 22 + try { 23 + tasks.named("downloadRenderDoc") { 24 + setProperty("output", file("$buildDir/renderdoc_1.37.tar.gz")) 25 + } 23 26 24 - tasks.named("extractRenderDoc") { 25 - enabled = false 26 - } 27 + tasks.named("extractRenderDoc") { 28 + enabled = false 29 + } 27 30 28 - val erd by tasks.register<Sync>("myExtractRenderDoc") { 29 - dependsOn("downloadRenderDoc") 30 - from(tarTree(resources.gzip("$buildDir/renderdoc_1.37.tar.gz"))) 31 - into("$buildDir/renderdoc") 31 + val erd by tasks.register<Sync>("myExtractRenderDoc") { 32 + dependsOn("downloadRenderDoc") 33 + from(tarTree(resources.gzip("$buildDir/renderdoc_1.37.tar.gz"))) 34 + into("$buildDir/renderdoc") 35 + } 36 + 37 + tasks.named("runClientRenderDoc") { 38 + dependsOn(erd) 39 + } 40 + } catch (ignored: UnknownTaskException) {} 41 + } 42 + 43 + val release: Boolean = !System.getenv("release").isNullOrEmpty() 44 + allprojects { 45 + val p = P(project) 46 + val modid: String by project.properties 47 + ext.set("p", p) 48 + version = project.property("mod_version") as String 49 + if (!release) version = "${version}+${p.commit_id.take(7)}" 50 + group = rootProject.property("maven_group") as String 51 + println("configuring $modid ($project) v$version @ $group") 52 + plugins.withId("java") { 53 + base { 54 + archivesName.set(modid) 55 + } 56 + java { 57 + toolchain.languageVersion = JavaLanguageVersion.of(17) 58 + withSourcesJar() 59 + } 60 + 61 + tasks.named<Jar>("jar").configure { 62 + from("LICENSE") { 63 + rename { "LICENSE_$modid" } 64 + } 65 + duplicatesStrategy = DuplicatesStrategy.WARN 66 + } 32 67 } 33 68 34 - tasks.named("runClientRenderDoc") { 35 - dependsOn(erd) 69 + plugins.withId("scala") { 70 + scala { 71 + scalaVersion = "3.7.1" 72 + } 36 73 } 37 - } catch (ignored: UnknownTaskException) {} 38 74 39 - loom.runs["client"].programArgs += listOf("--username", "Player", "--uuid", "bd346dd5-ac1c-427d-87e8-73bdd4bf3e13") 75 + plugins.withId("fabric-loom") { 76 + loom { 77 + splitEnvironmentSourceSets() 78 + runs["client"].programArgs += listOf("--username", "Player", "--uuid", "9e1b34e3-8031-4623-8918-eb7914ab564b") 40 79 41 - //tasks.withType<RenderDocR>() 80 + mods { 81 + register(modid) { 82 + sourceSet("main") 83 + sourceSet("client") 84 + } 85 + } 42 86 43 - val release: Boolean = !System.getenv("release").isNullOrEmpty() 44 - val p = P(project) 45 - project.ext.set("p", p) 46 - version = project.property("mod_version") as String 47 - val py_version: String by project.properties 48 - val wheelPath = file("dist/hexdoc_hexic-$version.$py_version-py3-none-any.whl") 49 - if (!release) version = "$version+${p.commit_id.take(7)}" 50 - group = project.property("maven_group") as String 87 + mixin.useLegacyMixinAp = false 88 + } 51 89 52 - base { 53 - archivesName.set(project.property("archives_base_name") as String) 54 - } 90 + fabricApi { 91 + configureTests { 92 + modId = modid 93 + eula = true 94 + } 95 + } 55 96 56 - val targetJavaVersion = 17 57 - java { 58 - toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 59 - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task 60 - // if it is present. 61 - // If you remove this line, sources will not be generated. 62 - withSourcesJar() 63 - } 97 + dependencies { 98 + modLocalRuntime("maven.modrinth:ears:1.4.7+fabric-1.20") 99 + } 64 100 65 - scala { 66 - scalaVersion = "3.7.1" 67 - } 101 + if (project != rootProject) { 102 + tasks.named("runClient") { 103 + doFirst { 104 + val rootOptions = rootProject.file("run/options.txt").toPath() 105 + val options = file("run/options.txt").toPath() 106 + options.deleteIfExists() 107 + Files.createSymbolicLink(options, rootOptions) 108 + } 109 + } 110 + } 68 111 69 - loom { 70 - splitEnvironmentSourceSets() 112 + tasks.processResources { 113 + val bookRoot = destinationDir.resolve("assets/hexcasting/patchouli_books/thehexbook") 114 + val langRoot = destinationDir.resolve("assets/$modid/lang") 71 115 72 - mods { 73 - register("hexic") { 74 - sourceSet("main") 75 - sourceSet("client") 116 + doLast { 117 + bookRoot.list()?.forEach { lang -> 118 + val langFile = langRoot.resolve("$lang.json") 119 + if (langFile.exists()) { 120 + val entries = JsonSlurper().parseText(langFile.readText()) as MutableMap<String, String> 121 + var n = 0 122 + for (bookFile in bookRoot.resolve(lang).walkTopDown()) { 123 + if (bookFile.isFile) { 124 + val json = JsonSlurper().parseText(bookFile.readText()) 125 + if (json !is Map<*, *>) continue 126 + json as MutableMap<Any, Any> 127 + val name = json["name"] 128 + if (name is String) { 129 + entries["text.$modid.book.${n}"] = name 130 + json["name"] = "text.$modid.book.${n}" 131 + n++ 132 + } 133 + val pages = json["pages"] 134 + if (pages !is MutableList<*>) continue 135 + pages as MutableList<Any> 136 + for (i in pages.indices) { 137 + val page = pages[i] 138 + if (page is String) { 139 + entries["text.$modid.book.${n}"] = page 140 + pages[i] = "text.$modid.book.${n}" 141 + n++ 142 + } else if (page is MutableMap<*, *>) { 143 + page as MutableMap<Any, Any> 144 + for (key in listOf("text", "title", "header")) { 145 + val text = page[key] 146 + if (text != null && text is String) { 147 + entries["text.$modid.book.${n}"] = text 148 + page[key] = "text.$modid.book.${n}" 149 + n++ 150 + } 151 + } 152 + } 153 + } 154 + bookFile.writeText(JsonOutput.toJson(json)) 155 + } 156 + } 157 + langFile.writeText(JsonOutput.toJson(entries)) 158 + } 159 + } 160 + } 76 161 } 77 162 } 78 - 79 - mixin.useLegacyMixinAp = false 163 + println("configured $project: release=$release, configured version: $version ($group)") 80 164 } 165 + val p: P by ext 166 + val py_version: String by project.properties 167 + val wheelPath = file("dist/hexdoc_hexic-$version.$py_version-py3-none-any.whl") 81 168 82 169 fabricApi { 83 170 configureDataGeneration { ··· 97 184 // into("$buildDir/hexxy4") 98 185 //} 99 186 100 - repositories { 101 - fun exactRepo(url: String, vararg groups: String, recursive: Boolean = true) { 102 - exclusiveContent { 103 - forRepository { 104 - maven(url) 105 - } 106 - filter { 107 - for (group in groups) { 108 - if (recursive) { 109 - includeGroupAndSubgroups(group) 110 - } else { 111 - includeGroup(group) 187 + allprojects { 188 + repositories { 189 + fun exactRepo(url: String, vararg groups: String, recursive: Boolean = true) { 190 + exclusiveContent { 191 + forRepository { 192 + maven(url) 193 + } 194 + filter { 195 + for (group in groups) { 196 + if (recursive) { 197 + includeGroupAndSubgroups(group) 198 + } else { 199 + includeGroup(group) 200 + } 112 201 } 113 202 } 114 203 } 115 204 } 116 - } 117 205 118 - mavenCentral() 119 - exactRepo("https://api.modrinth.com/maven", 120 - "maven.modrinth") 121 - exactRepo("https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/", 122 - "com.eliotlash.mclib", 123 - "software.bernie.geckolib") 124 - exactRepo("https://jitpack.io/", 125 - "com.github.Chocohead", 126 - "com.github.LlamaLad7", 127 - "com.github.Virtuoel", 128 - "com.github.mattidragon") 129 - exactRepo("https://maven.blamejared.com/", 130 - "at.petra-k", 131 - "com.samsthenerd.inline", 132 - "gay.object", 133 - "miyucomics.hexpose", 134 - "net.darkhax.openloader", 135 - "vazkii.patchouli") 136 - exactRepo("https://maven.hexxy.media/", 137 - "io.github.tropheusj", 138 - "ram.talia") 139 - exactRepo("https://maven.jamieswhiteshirt.com/libs-release/", 140 - "com.jamieswhiteshirt") 141 - exactRepo("https://maven.kosmx.dev/", 142 - "dev.kosmx") 143 - exactRepo("https://maven.ladysnake.org/releases/", 144 - "dev.onyxstudios") 145 - exactRepo("https://maven.pool.net.eu.org/", 146 - "dev.kineticcat.hexportation", 147 - "miyucomics.hexcellular", 148 - "miyucomics.hexical", 149 - "miyucomics.overevaluate", 150 - "org.eu.net.pool", 151 - "poollovernathan") 152 - exactRepo("https://maven.shedaniel.me/", 153 - "dev.architectury", 154 - "me.shedaniel") 155 - exactRepo("https://maven.terraformersmc.com/", 156 - "com.terraformersmc", 157 - "dev.emi") 158 - exactRepo("https://repo.sleeping.town/", 159 - "com.unascribed") 160 - exactRepo("https://masa.dy.fi/maven/", 161 - "carpet") 162 - exactRepo("https://maven.nucleoid.xyz/", 163 - "xyz.nucleoid") 206 + mavenCentral() 207 + exactRepo("https://api.modrinth.com/maven", 208 + "maven.modrinth") 209 + exactRepo("https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/", 210 + "com.eliotlash.mclib", 211 + "software.bernie.geckolib") 212 + exactRepo("https://jitpack.io/", 213 + "com.github.Chocohead", 214 + "com.github.LlamaLad7", 215 + "com.github.Virtuoel", 216 + "com.github.mattidragon") 217 + exactRepo("https://maven.blamejared.com/", 218 + "at.petra-k", 219 + "com.samsthenerd.inline", 220 + "gay.object", 221 + "miyucomics.hexpose", 222 + "net.darkhax.openloader", 223 + "vazkii.patchouli") 224 + exactRepo("https://maven.hexxy.media/", 225 + "io.github.tropheusj", 226 + "ram.talia") 227 + exactRepo("https://maven.jamieswhiteshirt.com/libs-release/", 228 + "com.jamieswhiteshirt") 229 + exactRepo("https://maven.kosmx.dev/", 230 + "dev.kosmx") 231 + exactRepo("https://maven.ladysnake.org/releases/", 232 + "dev.onyxstudios") 233 + exactRepo("https://pool.net.eu.org/", 234 + "dev.kineticcat.hexportation", 235 + "miyucomics.hexcellular", 236 + "miyucomics.hexical", 237 + "miyucomics.overevaluate", 238 + "org.eu.net.pool", 239 + "poollovernathan") 240 + exactRepo("https://maven.shedaniel.me/", 241 + "dev.architectury", 242 + "me.shedaniel") 243 + exactRepo("https://maven.terraformersmc.com/", 244 + "com.terraformersmc", 245 + "dev.emi") 246 + exactRepo("https://repo.sleeping.town/", 247 + "com.unascribed") 248 + exactRepo("https://masa.dy.fi/maven/", 249 + "carpet") 250 + exactRepo("https://maven.nucleoid.xyz/", 251 + "xyz.nucleoid") 252 + } 164 253 } 165 254 166 255 fun download(url: String, name: String = file(url).name): Download { ··· 260 349 261 350 val minecraft_version = "1.20.1" 262 351 modDepends(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.5.0")!!)!!) 263 - modDepends(modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}")!!) 264 - modDepends(include(modImplementation("poollovernathan.fabric:mod-tools:1.1.5+1.20.1")!!)!!) 265 - include(api("org.scala-lang:scala3-library_3:3.7.1")!!) 266 - include(api("org.scala-lang:scala-library:2.13.6")!!) 267 - modDepends(modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.2-pre-751")!!) 268 - modImplementation("at.petra-k.paucal:paucal-fabric-$minecraft_version:0.6.0-pre-118") 352 + implementation(project(":util", "namedElements")) 353 + modImplementation("io.github.tropheusj:serialization-hooks:0.4.99999") 354 + modImplementation("poollovernathan.fabric:mod-tools:1.1.5+1.20.1") 355 + modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.3") 269 356 modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 270 357 modDepends(include(implementation("com.github.Chocohead:Fabric-ASM:v2.3")!!)!!) 271 358 modCompileOnly("dev.kineticcat.hexportation:hexportation-fabric-1.20.1-fabric-fabric:0.0.3") 272 359 modCompileOnly("carpet:fabric-carpet:1.20-1.+") 360 + modLocalRuntime("maven.modrinth:lithium:mc1.20.1-0.11.4-fabric") 273 361 // modRuntimeOnly("carpet:fabric-carpet:1.20-1.+") 274 362 compat("gay.object.ioticblocks:ioticblocks-fabric:1.0.2+1.20.1") 275 - modImplementation("io.github.tropheusj:serialization-hooks:0.4.99999") 276 363 modImplementation(files("./libs/oneironaut-fabric-1.20.1-0.5.0-476cee2.jar")) 277 364 compat("maven.modrinth:hexcassettes:1.1.4") 278 365 modLocalRuntime("maven.modrinth:trinkets:3.7.2") ··· 290 377 include(modApi("xyz.nucleoid:fantasy:0.4.11+1.20-rc1")!!) 291 378 // modImplementation("miyucomics:hexpose:1.0.0") 292 379 // modImplementation(files("hexical-2.0.0.jar")) 293 - val cardinal_version = "5.2.3" 294 - modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-base:$cardinal_version") 295 - modDepends(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-block:$cardinal_version")!!) 296 - modDepends(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-entity:$cardinal_version")!!) 297 - modDepends(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-item:$cardinal_version")!!) 298 - modDepends(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-level:$cardinal_version")!!) 299 - modDepends(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-world:$cardinal_version")!!) 300 - modRuntimeOnly("dev.onyxstudios.cardinal-components-api:cardinal-components-api:$cardinal_version") 301 380 include(implementation("net.bytebuddy:byte-buddy:1.17.7")!!) 302 381 include(implementation("net.bytebuddy:byte-buddy-agent:1.17.7")!!) 303 382 ··· 356 435 } 357 436 } 358 437 359 - entrypoint("org.eu.net.pool.hexic.Hexic\$package::init") 360 - entrypoint("org.eu.net.pool.hexic.client.HexicClient\$package::init", Environment.Client) 361 - entrypoint("fabric-datagen", "org.eu.net.pool.hexic.client.HexicClient\$package::datagen") 362 - entrypoint("mm:early_risers", "org.eu.net.pool.hexic.EarlyRiser\$package::warCrimes") 438 + conflicts("valkyrienskies", "*") // need to figure out how to create dimensions without causing a crash 439 + 440 + entrypoint("org.eu.net.pool.hexic.main\$package::init") 441 + entrypoint("org.eu.net.pool.hexic.client.main\$package::init", Environment.Client) 442 + entrypoint("fabric-datagen", "org.eu.net.pool.hexic.client.main\$package::datagen") 443 + entrypoint("mm:early_risers", "org.eu.net.pool.hexic.early_riser\$package::warCrimes") 363 444 entrypoint("cardinal-components", "org.eu.net.pool.hexic.ComponentInit") 364 445 mixins("hexic.mixins.json") 365 446 mixins("hexic.client.mixins.json", Environment.Client) ··· 375 456 dependsOn(cloth) 376 457 dependsOn(*downloadedBags.values.toTypedArray()) 377 458 val itemsRoot = destinationDir.resolve("assets/hexic/textures/item") 378 - val langRoot = destinationDir.resolve("assets/hexic/lang") 379 - val bookRoot = destinationDir.resolve("assets/hexcasting/patchouli_books/thehexbook") 380 459 doLast { 381 460 for ((name, color) in colors) { 382 461 exec { ··· 441 520 } 442 521 } 443 522 444 - doLast { 445 - for (lang in bookRoot.list()) { 446 - val langFile = langRoot.resolve("$lang.json") 447 - if (langFile.exists()) { 448 - val entries = JsonSlurper().parseText(langFile.readText()) as MutableMap<String, String> 449 - var n = 0 450 - for (bookFile in bookRoot.resolve(lang).walkTopDown()) { 451 - if (bookFile.isFile) { 452 - val json = JsonSlurper().parseText(bookFile.readText()) 453 - if (json !is Map<*, *>) continue 454 - json as MutableMap<Any, Any> 455 - val name = json["name"] 456 - if (name is String) { 457 - entries["text.hexic.book.${n}"] = name 458 - json["name"] = "text.hexic.book.${n}" 459 - n++ 460 - } 461 - val pages = json["pages"] 462 - if (pages !is MutableList<*>) continue 463 - pages as MutableList<Any> 464 - for (i in pages.indices) { 465 - val page = pages[i] 466 - if (page is String) { 467 - entries["text.hexic.book.${n}"] = page 468 - pages[i] = "text.hexic.book.${n}" 469 - n++ 470 - } else if (page is MutableMap<*, *>) { 471 - page as MutableMap<Any, Any> 472 - for (key in listOf("text", "title", "header")) { 473 - val text = page[key] 474 - if (text != null && text is String) { 475 - entries["text.hexic.book.${n}"] = text 476 - page[key] = "text.hexic.book.${n}" 477 - n++ 478 - } 479 - } 480 - } 481 - } 482 - bookFile.writeText(JsonOutput.toJson(json)) 483 - } 484 - } 485 - langFile.writeText(JsonOutput.toJson(entries)) 486 - } 487 - } 488 - } 489 - 490 523 eachFile { 491 524 if (name.endsWith(".ase")) { 492 525 exec { ··· 502 535 } 503 536 } 504 537 505 - tasks.withType<AbstractArchiveTask> { 506 - isPreserveFileTimestamps = false 507 - isReproducibleFileOrder = true 508 - } 509 - 510 - tasks.withType<JavaCompile>().configureEach { 511 - // ensure that the encoding is set to UTF-8, no matter what the system default is 512 - // this fixes some edge cases with special characters not displaying correctly 513 - // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html 514 - // If Javadoc is generated, this must be specified in that task too. 515 - options.encoding = "UTF-8" 516 - options.release.set(targetJavaVersion) 517 - } 538 + allprojects { 539 + tasks.withType<AbstractArchiveTask> { 540 + isPreserveFileTimestamps = false 541 + isReproducibleFileOrder = true 542 + } 518 543 519 - tasks.withType<ScalaCompile>().configureEach { 520 - scalaCompileOptions.additionalParameters.addAll(listOf("-explain-cyclic", "-Ydebug-cyclic", "-experimental", "-feature", "-Ycc-debug")) 521 - } 544 + tasks.withType<JavaCompile>().configureEach { 545 + // ensure that the encoding is set to UTF-8, no matter what the system default is 546 + // this fixes some edge cases with special characters not displaying correctly 547 + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html 548 + // If Javadoc is generated, this must be specified in that task too. 549 + options.encoding = "UTF-8" 550 + options.release.set(17) 551 + } 522 552 523 - tasks.jar { 524 - from("LICENSE") { 525 - rename { "${it}_${project.base.archivesName}" } 553 + tasks.withType<ScalaCompile>().configureEach { 554 + scalaCompileOptions.additionalParameters.addAll(listOf("-explain-cyclic", "-Ydebug-cyclic", "-experimental", "-feature", "-Ycc-debug")) 526 555 } 527 - duplicatesStrategy = DuplicatesStrategy.WARN 528 556 } 529 557 530 558 val wheelFiles by lazy { ··· 705 733 } 706 734 707 735 // configure the maven publication 708 - publishing { 709 - publications { 710 - create<MavenPublication>("mavenJava") { 711 - artifactId = project.property("archives_base_name") as String 712 - from(components["java"]) 713 - } 714 - } 736 + allprojects { 737 + afterEvaluate { 738 + publishing { 739 + publications { 740 + create<MavenPublication>("mavenJava") { 741 + artifactId = project.property("modid") as String 742 + from(components["java"]) 743 + } 744 + } 715 745 716 - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 717 - repositories { 718 - // Add repositories to publish to here. 719 - // Notice: This block does NOT have the same function as the block in the top level. 720 - // The repositories here will be used for publishing your artifact, not for 721 - // retrieving dependencies. 746 + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. 747 + repositories { 748 + maven("https://pool.net.eu.org/") { 749 + name = "poolMaven" 750 + credentials(PasswordCredentials::class.java) 751 + } 752 + } 753 + } 722 754 } 723 755 }
+2 -2
gradle.properties
··· 8 8 loader_version=0.16.14 9 9 scala_loader_version=0.3.1.11 10 10 # Mod Properties 11 - mod_version=1.4.7-pre.1 11 + modid=hexic 12 + mod_version=2.0.0-alpha.01 12 13 py_version=1.0 13 14 maven_group=org.eu.net.pool 14 - archives_base_name=hexic 15 15 # Dependencies 16 16 # check this on https://modmuss50.me/fabric.html 17 17 fabric_version=0.92.6+1.20.1
+45
project/hexxychests/build.gradle.kts
··· 1 + plugins { 2 + id("fabric-loom") version "1.13-SNAPSHOT" 3 + id("scala") 4 + kotlin("jvm") version "2.2.0" 5 + id("maven-publish") 6 + id("de.undercouch.download") version "5.6.0" 7 + id("org.eu.net.pool.mc-plugin") version "0.1.1" 8 + } 9 + 10 + dependencies { 11 + minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") 12 + mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") 13 + modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") 14 + modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") 15 + api("org.scala-lang:scala3-library_3:3.7.1") 16 + api("org.scala-lang:scala-library:2.13.6") 17 + api(project(":util", "namedElements")) 18 + modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.3") 19 + modImplementation("at.petra-k.paucal:paucal-fabric-$minecraft_version:0.6.0-pre-118") 20 + modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 21 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-block:5.2.3") 22 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-item:5.2.3") 23 + } 24 + 25 + tasks.processResources { 26 + preprocessor { 27 + fabricMod("hexxychests", version as String) { 28 + name = "Hex In Yo' Chests" 29 + description = "A Hex Casting addon for manipulating iotas." 30 + license = "LGPL-3.0" 31 + icon = "assets/hexxychests/icon.png" 32 + 33 + author("pool") { 34 + put("discord", "https://discord.com/users/758407438251720795") 35 + } 36 + 37 + depends("mod-tools", "^1.1.5+1.20.1") 38 + depends("phlib", "0.1.1") 39 + depends("hexcasting", ">=0.11.2") 40 + 41 + entrypoint("org.eu.net.pool.hexxychests.main\$package::init") 42 + mixins("hexxychests.mixins.json") 43 + } 44 + } 45 + }
+9
project/hexxychests/gradle.properties
··· 1 + minecraft_version=1.20.1 2 + yarn_mappings=1.20.1+build.10 3 + loader_version=0.16.14 4 + scala_loader_version=0.3.1.11 5 + mod_version=0.1.0 6 + maven_group=org.eu.net.pool 7 + modid=hexxychests 8 + archives_base_name=hexxychests 9 + fabric_version=0.92.6+1.20.1
+7
project/hexxychests/src/main/resources/data/hexcasting/tags/action/per_world_pattern.json
··· 1 + { 2 + "values": [ 3 + "hexxychests:findview", 4 + "hexxychests:moveconcept", 5 + "hexxychests:moveentity" 6 + ] 7 + }
+5
project/hexxychests/src/main/resources/data/hexcasting/tags/action/requires_enlightenment.json
··· 1 + { 2 + "values": [ 3 + "hexxychests:moveentity" 4 + ] 5 + }
+7
project/hexxychests/src/main/resources/hexxychests.mixins.json
··· 1 + { 2 + "package": "org.eu.net.pool.hexxychests.mixin", 3 + "parent": "phlib.mixins.json", 4 + "mixins": [ 5 + "AbstractFurnaceBlockEntityMixin" 6 + ] 7 + }
+372
project/hexxychests/src/main/scala/org/eu/net/pool/hexxychests/main.scala
··· 1 + package org.eu.net.pool.hexxychests 2 + import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic 3 + import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus 4 + import at.petrak.hexcasting.api.casting.eval.CastingEnvironment 5 + import at.petrak.hexcasting.api.casting.iota.{DoubleIota, Iota, IotaType, NullIota, Vec3Iota} 6 + import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapInvalidIota} 7 + import com.mojang.serialization.Codec 8 + import com.samsthenerd.inline.api.data.ItemInlineData 9 + import net.fabricmc.fabric.api.dimension.v1.FabricDimensions 10 + import net.fabricmc.fabric.api.event.{Event, EventFactory} 11 + import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant 12 + import net.fabricmc.fabric.api.transfer.v1.item.{ItemStorage, ItemVariant} 13 + import net.fabricmc.fabric.api.transfer.v1.storage.{Storage, TransferVariant} 14 + import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant 15 + import net.fabricmc.fabric.api.transfer.v1.transaction.{Transaction, TransactionContext} 16 + import net.minecraft.block.BlockState 17 + import net.minecraft.block.entity.AbstractFurnaceBlockEntity 18 + import net.minecraft.entity.Entity 19 + import net.minecraft.fluid.Fluid 20 + import net.minecraft.item.Item 21 + import net.minecraft.nbt.{NbtCompound, NbtElement, NbtList, NbtLong} 22 + import net.minecraft.registry.{RegistryKey, RegistryKeys} 23 + import net.minecraft.server.MinecraftServer 24 + import net.minecraft.server.world.ServerWorld 25 + import net.minecraft.text.{HoverEvent, MutableText, Text} 26 + import net.minecraft.util.Identifier 27 + import net.minecraft.util.math.{BlockPos, Box} 28 + import net.minecraft.world.{TeleportTarget, World} 29 + import org.eu.net.pool.phlib.{*, given} 30 + import org.slf4j.{Logger, LoggerFactory} 31 + 32 + import java.util.UUID 33 + import scala.collection.mutable 34 + import scala.reflect.ClassTag 35 + import scala.util.{Failure, Success, Using} 36 + 37 + private[hexxychests] given Conversion[String, Identifier] = Identifier.of("hexxychests", _) 38 + private[hexxychests] given Logger = LoggerFactory.getLogger("hexxychests") 39 + 40 + trait InventoryView(val viewType: InventoryView.Type[?]) extends InventoryView.Handler: 41 + def isTruthy = true 42 + def serialize: NbtCompound = NbtCompound().tap(_.putString("id", InventoryView.registry.getId(viewType).toString)) 43 + object InventoryView extends Registrar[InventoryView.Type[?]]("inventory"): 44 + trait Type[+T <: InventoryView]: 45 + def deserialize(data: NbtCompound)(using ServerWorld): Option[T] 46 + trait Handler: 47 + def apply(idx: Int)(using CastingEnvironment): Option[SlotReference] = None 48 + def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 0 49 + def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 0 50 + def capacity(variant: TransferVariant[?])(using TransactionContext, CastingEnvironment): Long = 51 + Using(summon[TransactionContext].openNested()): tx => 52 + given TransactionContext = tx 53 + tryExtract(variant, Long.MaxValue) 54 + match 55 + case Success(n) => n 56 + case Failure(ex) => 57 + given_Logger.error("capacity", ex) 58 + 0L 59 + def entities(using TransactionContext): Set[Entity] = Set() 60 + @throws[Mishap] 61 + def teleportEntity(ent: Entity)(using TransactionContext, CastingEnvironment): Boolean = false 62 + object Events: 63 + val forEntity: Event[Entity => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, _ => Seq(), fns => e => fns.flatMap(_(e))) 64 + val forBlock: Event[(BlockPos, BlockState) => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, (_, _) => Seq(), fns => (pos, state) => fns.flatMap(_(pos, state))) 65 + // 'implementation restriction' my ass 66 + val forIota: Event[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]] = EventFactory.createArrayBacked(classOf, PartialFunction.empty, new java.util.function.Function[Array[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]], CastingEnvironment ?=> PartialFunction[Iota, InventoryView]]: 67 + def apply(fns: Array[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]]): CastingEnvironment ?=> PartialFunction[Iota, InventoryView] = (((_: CastingEnvironment) ?=> PartialFunction.empty[Iota, InventoryView]) /: fns) { _ orElse _ } 68 + ) 69 + abstract class OfMerged(viewType: InventoryView.Type[?], views: => Seq[Handler]) extends InventoryView(viewType): 70 + def getViews = views 71 + override def apply(idx: Int)(using CastingEnvironment): Option[SlotReference] = views.collectFirst(hexicVisibilityHack.unlifted(_(idx))) 72 + override def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = LazyList.from(views).scanLeft(0L)((n, view) => view.tryExtract(variant, amount - n) + n).findFirstOrLast(_ >= amount).getOrElse(0) 73 + override def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = LazyList.from(views).scanLeft(0L)((n, view) => view.tryInsert(variant, amount - n) + n).findFirstOrLast(_ >= amount).getOrElse(0) 74 + override def capacity(variant: TransferVariant[?])(using TransactionContext, CastingEnvironment) = views.map(_.capacity(variant)).sum 75 + override def entities(using TransactionContext) = views.flatMap(_.entities).toSet 76 + override def teleportEntity(ent: Entity)(using TransactionContext, CastingEnvironment): Boolean = views.iterator∃(_.teleportEntity(ent)) 77 + class OfSum private(private[InventoryView] val views: Seq[InventoryView]) extends OfMerged(typeOfSum, views): 78 + override def isTruthy = views ∃(_.isTruthy) 79 + override def serialize = 80 + val c = NbtCompound() 81 + val list = NbtList() 82 + for view <- views do list.add(view.serialize) 83 + c.put("c", list) 84 + c 85 + object OfSum: 86 + def apply(views: InventoryView*) = new OfSum( 87 + views.flatMap: 88 + case v: OfSum => v.views 89 + case v => Iterable(v) 90 + ) 91 + class OfEntity(id: UUID)(using server: MinecraftServer) extends OfMerged(typeOfEntity, server.getWorlds.collectFirst(hexicVisibilityHack.unlifted(w => Option(w.getEntity(id)).map(Events.forEntity.invoker()(_)(using w)))).getOrElse(Seq())): 92 + override def serialize = 93 + val c = super.serialize 94 + c.putLong("m", id.getMostSignificantBits) 95 + c.putLong("l", id.getLeastSignificantBits) 96 + c 97 + class OfBlock(pos: BlockPos)(using world: ServerWorld) extends OfMerged(typeOfBlock, Events.forBlock.invoker()(pos, world.getBlockState(pos))): 98 + override def serialize = 99 + val c = super.serialize 100 + val w = world.getRegistryKey.getValue 101 + if w.getNamespace != "minecraft" then c.put("m", w.getNamespace) 102 + if w.getPath != "overworld" then c.put("w", w.getPath) 103 + c.putLong("p", pos.asLong) 104 + c 105 + class OfExactEntity(entity: => Entity)(using ServerWorld) extends InventoryView(typeOfExactEntity): 106 + override val viewType = typeOfExactEntity 107 + override def entities(using TransactionContext) = Set(entity) 108 + override def serialize = 109 + val c = super.serialize 110 + // TODO 111 + c 112 + def deserialize(data: NbtCompound)(using ServerWorld): Option[InventoryView] = for 113 + id <- Option(Identifier.tryParse(data.getString("id"))) 114 + viewType <- Option(InventoryView.registry.get(id)) 115 + view <- viewType.deserialize(data) 116 + yield view 117 + private given typeOfSum: InventoryView.Type[OfSum]: 118 + override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfSum] = 119 + Some(OfSum((for 120 + n <- 0 until data.getInt("n") 121 + key = "_" + Integer.toString(n + 10, 36) 122 + c = data.getCompound(key) 123 + view <- InventoryView.deserialize(c) 124 + yield view)*)) 125 + private given typeOfEntity: InventoryView.Type[OfEntity]: 126 + override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfEntity] = ??? 127 + private given typeOfBlock: InventoryView.Type[OfBlock]: 128 + override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfBlock] = 129 + for 130 + case posLong: NbtLong <- Option(data.get("p")) 131 + pos = BlockPos.fromLong(posLong.longValue) 132 + namespace = Option(data.getString("m")).filter(!_.isBlank) getOrElse "minecraft" 133 + path = Option(data.getString("w")).filter(!_.isBlank) getOrElse "overworld" 134 + key = RegistryKey.of(RegistryKeys.WORLD, Identifier.of(namespace, path)) 135 + given ServerWorld <- Option(summon[ServerWorld].getServer.getWorld(key)) 136 + yield OfBlock(pos) 137 + private given typeOfExactEntity: InventoryView.Type[OfExactEntity]: 138 + override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfExactEntity] = ??? 139 + registry("sum") = typeOfSum 140 + registry("entity") = typeOfEntity 141 + registry("block") = typeOfBlock 142 + registry("exact") = typeOfExactEntity 143 + Events.forBlock.register: (pos, state) => 144 + val storage = ItemStorage.SIDED.find(summon, pos, null): Storage[ItemVariant] 145 + if storage == null then Seq() 146 + else Seq( 147 + new Handler: 148 + override def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 149 + variant match 150 + case i: ItemVariant => storage.insert(i, amount, summon) 151 + case _ => 0 152 + override def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 153 + variant match 154 + case i: ItemVariant => storage.extract(i, amount, summon) 155 + case _ => 0 156 + ) 157 + Events.forBlock.register: (pos, state) => 158 + if state.isTransparent(summon, pos) then 159 + Seq(new Handler: 160 + override def entities(using TransactionContext): Set[Entity] = summon[World].getOtherEntities(null, Box.of(pos.toCenterPos, 0.5, 0.5, 0.5), _ => true).toSet 161 + override def teleportEntity(ent: Entity)(using TransactionContext, CastingEnvironment): Boolean = 162 + var currEnt = ent 163 + doSnapshot((currEnt.getPos, currEnt.getWorld.asInstanceOf[ServerWorld]), snapshot => currEnt = FabricDimensions.teleport(currEnt, snapshot._2, TeleportTarget(snapshot._1, currEnt.getVelocity, currEnt.getYaw, currEnt.getPitch)))(pos.toCenterPos.subtract(0, 0.25, 0), summon) 164 + true 165 + ) 166 + else Seq() 167 + trait SingleVariantHandler(variant: TransferVariant[?]) extends Handler: 168 + override def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = if variant == this.variant then trySubtract(amount) else 0L 169 + override def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = if variant == this.variant then tryAdd(amount) else 0L 170 + override def capacity(variant: TransferVariant[?])(using TransactionContext, CastingEnvironment): Long = if variant == this.variant then cap else 0L 171 + def trySubtract(amount: Long)(using TransactionContext, CastingEnvironment): Long 172 + def tryAdd(amount: Long)(using TransactionContext, CastingEnvironment): Long 173 + def cap(using TransactionContext, CastingEnvironment): Long 174 + 175 + Events.forBlock.register: (pos, state) => 176 + summon[ServerWorld].getBlockEntity(pos) match 177 + case e: BlockEntityAbstractImpetus => Seq(new SingleVariantHandler(SingletonVariant.media) { 178 + private def mediaCapacity = 9000000000000000000L - e.getMedia 179 + override def trySubtract(amount: Long)(using TransactionContext, CastingEnvironment): Long = 180 + val toExtract = amount min e.getMedia 181 + doSnapshot(e.getMedia, e.setMedia)(e.getMedia - toExtract) 182 + toExtract 183 + override def tryAdd(amount: Long)(using TransactionContext, CastingEnvironment): Long = 184 + val toInsert = amount min mediaCapacity 185 + doSnapshot(e.getMedia, e.setMedia)(e.getMedia + toInsert) 186 + toInsert 187 + override def cap(using TransactionContext, CastingEnvironment): Long = mediaCapacity 188 + }) 189 + case _ => Seq() 190 + Events.forBlock.register: (pos, state) => 191 + summon[ServerWorld].getBlockEntity(pos) match 192 + case e: AbstractFurnaceBlockEntity => Seq( 193 + new SingleVariantHandler(SingletonVariant.heat): 194 + override def trySubtract(amount: Long)(using TransactionContext, CastingEnvironment): Long = 195 + val action = (amount min e.burnTime).toInt 196 + doSnapshot(e.burnTime, e.burnTime = _)(e.burnTime - action) 197 + action 198 + override def tryAdd(amount: Long)(using TransactionContext, CastingEnvironment): Long = 199 + val action = (amount min cap).toInt 200 + doSnapshot(e.burnTime, e.burnTime = _)(e.burnTime + action) 201 + doSnapshot(e.fuelTime, e.fuelTime = _)(e.fuelTime max e.burnTime) 202 + action 203 + override def cap(using TransactionContext, CastingEnvironment): Long = (5*60*20) - e.burnTime 204 + ) 205 + case _ => Seq() 206 + given Conversion[AbstractFurnaceBlockEntity, AbstractFurnaceBlockEntityAccess] = _.asInstanceOf // by mixin 207 + trait AbstractFurnaceBlockEntityAccess: 208 + def burnTime: Int 209 + def burnTime_=(burnTime: Int): Unit 210 + def fuelTime: Int 211 + def fuelTime_=(fuelTime: Int): Unit 212 + 213 + private [hexxychests] val conceptScale = mutable.Map[ClassTag[? <: TransferVariant[?]], Double]().withDefaultValue(1.0) 214 + def setConceptScale[T <: TransferVariant[?]: ClassTag as ct](scale: Int) = 215 + if conceptScale.isDefinedAt(ct) && conceptScale(ct) != scale then 216 + throw IllegalStateException(s"Conflicting scales ${conceptScale(ct)} and $scale defined for class $ct") 217 + else 218 + conceptScale(ct) = scale 219 + 220 + 221 + def doSnapshot[T](current: => T, apply: T => Unit)(toApply: T)(using tx: TransactionContext): Unit = 222 + object partip extends SnapshotParticipant[T]: 223 + override def createSnapshot(): T = current 224 + override def readSnapshot(snapshot: T): Unit = apply(snapshot) 225 + partip.updateSnapshots(tx) 226 + partip.readSnapshot(toApply) 227 + 228 + object BoxedView extends IotaType[BoxedView.Instance]: 229 + InventoryView 230 + class Instance(val view: InventoryView) extends Iota(BoxedView, view): 231 + export view.{isTruthy, serialize} 232 + override def toleratesOther(that: Iota): Boolean = that match 233 + case that: BoxedView.Instance => view == that.view 234 + case _ => false 235 + override def deserialize(tag: NbtElement, world: ServerWorld): Instance = 236 + given ServerWorld = world; 237 + (for 238 + case c: NbtCompound <- Some(tag) 239 + view <- InventoryView.deserialize(c) 240 + yield Instance(view)).orNull 241 + override def display(tag: NbtElement): Text = "[View]".styled(_.withColor(color)) 242 + override def color: Int = 0xa59e7c 243 + given this.type = this 244 + 245 + trait SlotReference: 246 + def item(using CastingEnvironment): Item 247 + def nbt(using CastingEnvironment): Option[NbtCompound] 248 + def count(using CastingEnvironment): Long 249 + @throws[Mishap] 250 + def nbt_=(using Transaction, CastingEnvironment)(nbt: Option[NbtCompound]): Unit 251 + @throws[Mishap] 252 + def count_=(using Transaction, CastingEnvironment)(count: Long): Unit 253 + object SlotReference extends Registrar[SlotReference.Type[?]]("slot"): 254 + class Type[T <: SlotReference: Codec] 255 + 256 + def init() = 257 + hexXplat.getIotaTypeRegistry("variant") = VariantIota 258 + hexXplat.getIotaTypeRegistry("reference") = BoxedView 259 + Patterns.register("findview", e"addaadewewedaaddqwawqddaadewewedaaddqwawdeeweee"): 260 + // 2026-01-01 pool: nathan, we call this 'jank'. why would you do this? 261 + inline def lookup = InventoryView.Events.forIota.invoker()(using compiletime.summonInline) 262 + Patterns.mkConstAction(1): 263 + case Seq(lookup(view)) => Seq(BoxedView.Instance(view)) 264 + case Seq(iota) => throw MishapInvalidIota.ofType(iota, 0, "hexic:view") 265 + InventoryView.Events.forIota.register: 266 + case block: Vec3Iota => 267 + val pos = BlockPos.ofFloored(block.getVec3) 268 + summon[CastingEnvironment].assertPosInRangeForEditing(pos) 269 + InventoryView.OfBlock(pos) 270 + hexXplat.getArithmeticRegistry("view") = arith("view", 271 + Arithmetic.ADD -> { 272 + (view1: BoxedView.Instance, view2: BoxedView.Instance) => Seq(BoxedView.Instance(InventoryView.OfSum(view1.view, view2.view))) 273 + } 274 + ) 275 + setConceptScale[FluidVariant](81000) 276 + setConceptScale[SingletonVariant.media.type](10000) 277 + setConceptScale[SingletonVariant.heat.type](20) 278 + Patterns.register("moveconcept", se"wawdwawqdewewedqwawdwaw"): 279 + Patterns.mkConstAction(4): 280 + case Seq(isIota[BoxedView.Instance, 3](from), isIota[BoxedView.Instance, 2](into), typ: VariantIota[?], isIota[DoubleIota, 0](count)) => 281 + Using.resource(Transaction.openOuter()): 282 + case tx@given TransactionContext => 283 + val key = ClassTag(typ.data.getClass) 284 + val scale = conceptScale(key) 285 + val toExtract = (scale * count.getDouble).toLong 286 + val extract = from.view.tryExtract(typ.data, toExtract) 287 + if extract < toExtract then 288 + ??? // TODO: mishap 289 + val insert = into.view.tryInsert(typ.data, extract) 290 + if insert < extract then 291 + ??? // TODO: mishap 292 + tx.commit() 293 + Seq(DoubleIota(insert / scale)) 294 + Patterns.register("moveentity", e"edeeewawdweaaddaqwqwqaddaaewdwawewdqd"): 295 + Patterns.mkConstAction(3): 296 + case Seq(isIota[BoxedView.Instance, 2](from), isIota[BoxedView.Instance, 1](into), isIota[DoubleIota, 0](count)) => 297 + Using.resource(Transaction.openOuter()): 298 + case tx@given TransactionContext => 299 + val count = from.view.entities.count(into.view.teleportEntity) 300 + if count > 0 then tx.commit() 301 + Seq(DoubleIota(count)) 302 + 303 + //noinspection UnstableApiUsage 304 + case class VariantIota[T: ClassTag](data: TransferVariant[T], key: RegistryKey[VariantIota.Reader]) extends Iota(VariantIota, data): 305 + override def isTruthy: Boolean = true 306 + override def toleratesOther(that: Iota): Boolean = 307 + that match 308 + case v: VariantIota[T] => key == v.key && data == v.data 309 + case _ => false 310 + override def serialize: NbtElement = 311 + data.toNbt tap(_.putString("type", key.getValue.toString)) 312 + //noinspection UnstableApiUsage 313 + object VariantIota extends IotaType[VariantIota[?]], Registrar[VariantIota.Reader]("transfer_variants"): 314 + given IotaType[VariantIota[?]] = this 315 + type Reader = NbtCompound => Option[VariantIota.TaggedVariant] 316 + trait TaggedVariant: 317 + type T: ClassTag 318 + def variant: TransferVariant[T] 319 + def display: Text 320 + def color: Int = 0x720a0a 321 + private[hexxychests] def parseVariant(c: NbtCompound): Option[(TaggedVariant, RegistryKey[Reader])] = 322 + for 323 + i <- Option(Identifier.tryParse(c.getString("type"))) 324 + entry <- Option.fromNullable(registry.get(i)) 325 + parsed <- entry(c) 326 + yield (parsed, RegistryKey.of(VariantIota, i)) 327 + end parseVariant 328 + def deserialize(using NbtElement, ServerWorld): VariantIota[?] | Null = 329 + summon[NbtElement] match 330 + case c: NbtCompound => 331 + parseVariant(c) match 332 + case Some((t, k)) => 333 + import t.given 334 + VariantIota(t.variant, k) 335 + case None => null 336 + case _ => null 337 + end deserialize 338 + override def display(e: NbtElement): Text = parseVariant(e.downcast).fold(NullIota.DISPLAY)(_._1.display) 339 + end display 340 + registry(Identifier("item")) = c => 341 + val s = ItemVariant.fromNbt(c) 342 + Option.unless(s.isBlank): 343 + new TaggedVariant: 344 + type T = Item 345 + def variant: TransferVariant[Item] = s 346 + def display: Text = t"${s.getItem.getName(s.toStack)}: ${ItemInlineData(s.toStack).asText(true)}" 347 + .styled(_.withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_ITEM, HoverEvent.ItemStackContent(s.toStack)))) 348 + registry(Identifier("fluid")) = c => 349 + val s = FluidVariant.fromNbt(c) 350 + Option.unless(s.isBlank): 351 + new TaggedVariant: 352 + type T = Fluid 353 + def variant: TransferVariant[Fluid] = s 354 + def display: MutableText = t"${s.getFluid.getDefaultState.getBlockState.getBlock.getName}: ${ItemInlineData.make(s.getFluid.getBucketItem.getDefaultStack)}" 355 + registry("media") = c => 356 + Some(new TaggedVariant: 357 + type T = SingletonVariant 358 + def variant = SingletonVariant.media 359 + def display: Text = Text.literal("Media").styled(_.withColor(0x74b3f2))) 360 + 361 + //noinspection UnstableApiUsage 362 + class SingletonVariant extends TransferVariant[SingletonVariant]: 363 + def getNbt = NbtCompound() 364 + def getObject: this.type = this 365 + def isBlank = false 366 + def toNbt = NbtCompound() 367 + def toPacket(buf: net.minecraft.network.PacketByteBuf): Unit = () 368 + object SingletonVariant: 369 + // 2026-01-01 pool: implementation restriction: these must be proper subclasses since classtags are relevant 370 + object media extends SingletonVariant() 371 + object heat extends SingletonVariant() 372 + object energy extends SingletonVariant()
+60
project/hexxytounge/build.gradle.kts
··· 1 + plugins { 2 + id("fabric-loom") version "1.13-SNAPSHOT" 3 + id("scala") 4 + kotlin("jvm") version "2.2.0" 5 + id("maven-publish") 6 + id("de.undercouch.download") version "5.6.0" 7 + id("org.eu.net.pool.mc-plugin") version "0.1.1" 8 + } 9 + 10 + dependencies { 11 + minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") 12 + mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") 13 + modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") 14 + modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") 15 + api("org.scala-lang:scala3-library_3:3.7.1") 16 + api("org.scala-lang:scala-library:2.13.6") 17 + api(project(":util", "namedElements")) 18 + include(modImplementation("maven.modrinth:jsonpatcher:1.0.0-beta.4+mc.1.20.1")!!) 19 + implementation("com.github.mattidragon:JsonPatcherLang:v1.0.0-beta.3") // trans maven.modrinth:jsonpatcher 20 + modImplementation("com.github.mattidragon:ConfigToolkit:v1.0.0") // trans maven.modrinth:jsonpatcher 21 + modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.3") 22 + modImplementation("at.petra-k.paucal:paucal-fabric-$minecraft_version:0.6.0-pre-118") 23 + modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 24 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-entity:5.2.3") 25 + modImplementation("ram.talia.moreiotas:moreiotas-fabric-$minecraft_version:0.1.1") { exclude(module = "serialization-hooks") } 26 + } 27 + 28 + tasks.processResources { 29 + preprocessor { 30 + fabricMod("hexxytounge" /* typo'd but too late to change */, version as String) { 31 + name = "Tongued Hexxy" 32 + description = "Adds chat manipulation to Hex Casting because that's certainly a great idea." 33 + license = "LGPL-3.0" 34 + icon = "hexxytounge.icon.png" 35 + 36 + author("pool") { 37 + put("discord", "https://discord.com/users/758407438251720795") 38 + } 39 + 40 + depends("mod-tools", "^1.1.5+1.20.1") 41 + depends("phlib", "0.1.1") 42 + depends("hexcasting", ">=0.11.2") 43 + depends("moreiotas", ">=0.1.1") 44 + depends("cardinal-components-entity", "^5.2.3") 45 + depends("jsonpatcher", "^1.0.0-beta") 46 + recommends("hexpose", "*") 47 + breaks("hexic", "<2.0.0") 48 + 49 + entrypoint("org.eu.net.pool.hexxytounge.main\$package::init") 50 + entrypoint("cardinal-components", "org.eu.net.pool.hexxytounge.Components") 51 + mixins("hexxytongge.mixins.json") 52 + custom { 53 + array("cardinal-components") { 54 + put("hexxytounge:murmur") 55 + put("hexxytounge:reveal") 56 + } 57 + } 58 + } 59 + } 60 + }
+9
project/hexxytounge/gradle.properties
··· 1 + minecraft_version=1.20.1 2 + yarn_mappings=1.20.1+build.10 3 + loader_version=0.16.14 4 + scala_loader_version=0.3.1.11 5 + mod_version=0.1.0 6 + maven_group=org.eu.net.pool 7 + modid=hexxytognue 8 + archives_base_name=hexxytognue 9 + fabric_version=0.92.6+1.20.1
+69
project/hexxytounge/src/client/scala/org/eu/net/pool/hexxytounge/client.scala
··· 1 + package org.eu.net.pool.hexxytounge 2 + 3 + import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking 4 + import net.fabricmc.fabric.api.networking.v1.PacketByteBufs 5 + import net.minecraft.client.MinecraftClient 6 + import net.minecraft.client.gui.screen.ChatScreen 7 + 8 + import scala.annotation.experimental 9 + 10 + given Conversion[ChatScreen, mixin.ChatScreenAccess] = _.asInstanceOf 11 + 12 + var lastMurmur = None: Option[String] 13 + def tick() = 14 + val currentMurmur = MinecraftClient.getInstance.currentScreen match 15 + case null => None 16 + case c: ChatScreen => Some(c.chatField.getText) 17 + case _ => None 18 + if currentMurmur != lastMurmur then 19 + lastMurmur = currentMurmur 20 + val buf = PacketByteBufs.create() 21 + buf.writeBoolean(currentMurmur.isDefined) 22 + currentMurmur.foreach(buf.writeString) 23 + try 24 + ClientPlayNetworking.send("murmur", buf) 25 + catch 26 + case _: IllegalStateException => 27 + 28 + package mixin: 29 + import com.llamalad7.mixinextras.injector.ModifyExpressionValue 30 + import com.llamalad7.mixinextras.sugar.Local 31 + import net.minecraft.client.MinecraftClient 32 + import net.minecraft.client.gui.hud.{ChatHud, ChatHudLine, MessageIndicator} 33 + import net.minecraft.client.gui.screen.ChatScreen 34 + import net.minecraft.client.gui.widget.TextFieldWidget 35 + import org.eu.net.pool.hexxytounge 36 + import org.eu.net.pool.phlib.given 37 + import org.spongepowered.asm.mixin.{Mixin, Unique} 38 + import org.spongepowered.asm.mixin.gen.Accessor 39 + import org.spongepowered.asm.mixin.injection.{At, Inject} 40 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfo 41 + 42 + @Mixin(Array(classOf[MinecraftClient])) 43 + private[mixin] class MinecraftClientMixin: 44 + @Inject(at = Array(new At(value = "HEAD")), method = Array("tick")) 45 + def tick(using CallbackInfo) = 46 + hexxytounge.tick() 47 + @Mixin(Array(classOf[ChatScreen])) 48 + trait ChatScreenAccess: 49 + @Accessor("chatField") val chatField: TextFieldWidget 50 + @Mixin(Array(classOf[ChatHud])) 51 + @experimental 52 + private[mixin] class ChatHudMixin: 53 + @Unique private def patch(original: java.util.List[ChatHudLine.Visible], currentTick: Int): java.util.List[ChatHudLine.Visible] = { 54 + val p = MinecraftClient.getInstance.player 55 + if p != null then 56 + val lines = p.component[RevealComponent].lines.map(line => new ChatHudLine.Visible(currentTick, line.asOrderedText, MessageIndicator.system, true)) 57 + println(lines) 58 + if lines.nonEmpty then 59 + lines.reverse ++ original 60 + else 61 + original 62 + else 63 + original 64 + } 65 + 66 + @ModifyExpressionValue(method = Array("render"), at = Array(new At(value = "FIELD", target = "Lnet/minecraft/client/gui/hud/ChatHud;visibleMessages:Ljava/util/List;"))) 67 + def modifyDrawnMessages(original: java.util.List[ChatHudLine.Visible], @Local(ordinal = 0, argsOnly = true) currentTick: Int) = patch(original, currentTick) 68 + @ModifyExpressionValue(method = Array("getTextStyleAt", "getIndicatorAt"), at = Array(new At(value = "FIELD", target = "Lnet/minecraft/client/gui/hud/ChatHud;visibleMessages:Ljava/util/List;"))) 69 + def modifyLookedUpMessages(original: java.util.List[ChatHudLine.Visible]) = patch(original, 0)
+4
project/hexxytounge/src/main/resources/assets/hexxytounge/lang/en_us.json
··· 1 + { 2 + "hexcasting.action.hexxytounge:murmur": "Murmur Reflection", 3 + "book.hexxytounge.page.murmur": "Finds the region of my mind known as the 'chat box' and adds its contents to the stack. If it cannot be found, adds Null instead." 4 + }
+4
project/hexxytounge/src/main/resources/assets/hexxytounge/lang/zh_cn.json
··· 1 + { 2 + "hexcasting.action.hexxytounge:murmur": "私语之精思", 3 + "book.hexxytounge.page.murmur": "找到我意识中名为“聊天栏”的区域,将其内容压栈;无法找到则返回 Null。" 4 + }
+12
project/hexxytounge/src/main/resources/hexxytongge.mixins.json
··· 1 + { 2 + "required": true, 3 + "minVersion": "0.8", 4 + "package": "org.eu.net.pool.hexxytounge.mixin", 5 + "compatibilityLevel": "JAVA_17", 6 + "mixins": [], 7 + "client": [ 8 + "ChatHudMixin", 9 + "ChatScreenAccess", 10 + "MinecraftClientMixin" 11 + ] 12 + }
+85
project/hexxytounge/src/main/scala/org/eu/net/pool/hexxytounge/main.scala
··· 1 + package org.eu.net.pool.hexxytounge 2 + 3 + import at.petrak.hexcasting.api.casting.eval.CastingEnvironment 4 + import at.petrak.hexcasting.api.casting.iota.{Iota, IotaType, ListIota, NullIota} 5 + import at.petrak.hexcasting.api.casting.mishaps.MishapBadCaster 6 + import com.google.gson.JsonElement 7 + import com.mojang.serialization.JsonOps 8 + import dev.onyxstudios.cca.api.v3.component.sync.AutoSyncedComponent 9 + import dev.onyxstudios.cca.api.v3.component.{Component, ComponentAccess, ComponentKey, ComponentRegistry} 10 + import dev.onyxstudios.cca.api.v3.entity.{EntityComponentFactoryRegistry, EntityComponentInitializer, RespawnCopyStrategy} 11 + import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking 12 + import net.minecraft.nbt.{NbtCompound, NbtElement} 13 + import net.minecraft.server.network.ServerPlayerEntity 14 + import net.minecraft.text.Text 15 + import net.minecraft.util.Identifier 16 + import org.eu.net.pool.phlib.{Events as PhEvents, *, given} 17 + import org.slf4j.{Logger, LoggerFactory} 18 + import ram.talia.moreiotas.api.casting.iota.StringIota 19 + 20 + import scala.collection.immutable.* 21 + import scala.language.implicitConversions 22 + 23 + private[hexxytounge] given Logger = LoggerFactory.getLogger("hexxytounge") 24 + private[hexxytounge] given Conversion[String, Identifier] = Identifier.of("hexxytounge", _) 25 + 26 + private[hexxytounge] case class MurmurCache(var value: Option[String]) extends Component: 27 + override def readFromNbt(tag: NbtCompound): Unit = 28 + if tag.getBoolean("active") then 29 + value = Some(tag.getString("value")) 30 + else 31 + value = None 32 + override def writeToNbt(tag: NbtCompound): Unit = 33 + tag.putBoolean("active", false) 34 + for value <- value do 35 + tag.putBoolean("active", true) 36 + tag.putString("value", value) 37 + private[hexxytounge] object MurmurCache: 38 + given ComponentKey[MurmurCache] = ComponentRegistry.getOrCreate("murmur", classOf[MurmurCache]) 39 + 40 + private[hexxytounge] case class RevealComponent(var lines: Seq[Text]) extends AutoSyncedComponent: 41 + override def readFromNbt(tag: NbtCompound): Unit = 42 + lines = for n <- 0 until tag.getInt("lineCount") yield Text.Serializer.fromJson(tag(s"line$n")) // nbt is json apparently? 43 + override def writeToNbt(tag: NbtCompound): Unit = 44 + tag.putInt("lineCount", lines.size) 45 + for (line, n) <- lines.zipWithIndex do tag(s"line$n") = Text.Serializer.toJsonTree(line) 46 + private[hexxytounge] object RevealComponent: 47 + given ComponentKey[RevealComponent] = ComponentRegistry.getOrCreate("reveal", classOf[RevealComponent]) 48 + 49 + class Components extends EntityComponentInitializer: 50 + override def registerEntityComponentFactories(registry: EntityComponentFactoryRegistry): Unit = 51 + registry.registerForPlayers(summon[ComponentKey[MurmurCache]], _ => MurmurCache(None), RespawnCopyStrategy.ALWAYS_COPY) 52 + registry.registerForPlayers(summon[ComponentKey[RevealComponent]], _ => RevealComponent(Seq.empty), RespawnCopyStrategy.LOSSLESS_ONLY) 53 + 54 + def keyOf[C <: Component: ComponentKey as key] = key 55 + 56 + extension (c: ComponentAccess) 57 + def component[C <: Component: ComponentKey as key]: C = c.getComponent(key) 58 + def syncComponent[C <: Component: ComponentKey as key](): Unit = c.syncComponent(key) 59 + 60 + object hasComponent: 61 + def unapply[C <: Component: ComponentKey](ctx: ComponentAccess): Option[C] = 62 + try 63 + Some(ctx.component[C]) 64 + catch case _: NoSuchElementException => 65 + None 66 + 67 + def init() = 68 + Patterns.register("reveal", ne"deqed" ): 69 + Patterns.mkConstAction(1, 0): 70 + case Seq(iota: Iota) => 71 + locally(summon[CastingEnvironment]).getCastingEntity match 72 + case null => throw MishapBadCaster() 73 + case p: ServerPlayerEntity => 74 + p.component[RevealComponent].lines = iota match 75 + case s: ListIota => s.getList.map(_.display).toSeq 76 + //case m: MapIota => m.map.toSeq.map(p => IotaType.getDisplay(p._1) -> IotaType.getDisplay(p._2)).sortBy(_._1.getString) 77 + case _: NullIota => Seq() 78 + case _ => Seq(iota.display) 79 + p.syncComponent[RevealComponent]() 80 + Seq() 81 + case _ => throw MishapBadCaster() 82 + Patterns.register("murmur", e"wwaqwa"): 83 + Patterns.mkLiteral: (env, _) ?=> 84 + hasComponent.unapply[MurmurCache](env.getCastingEntity).fold(throw MishapBadCaster())(_.value.fold(NullIota())(StringIota.make)) 85 + ServerPlayNetworking.registerGlobalReceiver("murmur", { case (_, hasComponent[MurmurCache](c), _, buf, _) => c.value = Option.when(buf.readBoolean())(buf.readString()) }: ServerPlayNetworking.PlayChannelHandler)
+49
project/iotaworks/build.gradle.kts
··· 1 + plugins { 2 + id("fabric-loom") version "1.13-SNAPSHOT" 3 + id("scala") 4 + kotlin("jvm") version "2.2.0" 5 + id("maven-publish") 6 + id("de.undercouch.download") version "5.6.0" 7 + id("org.eu.net.pool.mc-plugin") version "0.1.1" 8 + } 9 + 10 + dependencies { 11 + minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") 12 + mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") 13 + modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") 14 + modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") 15 + api("org.scala-lang:scala3-library_3:3.7.1") 16 + api("org.scala-lang:scala-library:2.13.6") 17 + api(project(":util", "namedElements")) 18 + modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.3") 19 + modImplementation("at.petra-k.paucal:paucal-fabric-$minecraft_version:0.6.0-pre-118") 20 + modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 21 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-block:5.2.3") 22 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-item:5.2.3") 23 + modApi("miyucomics.hexcellular:hexcellular:1.1.0") 24 + } 25 + 26 + tasks.processResources { 27 + preprocessor { 28 + fabricMod("iotaworks", version as String) { 29 + name = "Iotaworks" 30 + description = "A Hex Casting addon for manipulating iotas." 31 + license = "LGPL-3.0" 32 + icon = "assets/iotaworks/icon.png" 33 + 34 + author("pool") { 35 + put("discord", "https://discord.com/users/758407438251720795") 36 + } 37 + 38 + depends("mod-tools", "^1.1.5+1.20.1") 39 + depends("phlib", "0.1.1") 40 + depends("hexcasting", ">=0.11.2") 41 + depends("hexcellular", "^1.0.4") 42 + recommends("hexpose", "*") 43 + breaks("hexic", "<2.0.0") 44 + 45 + entrypoint("org.eu.net.pool.iotaworks.main\$package::init") 46 + mixins("iotaworks.mixins.json") 47 + } 48 + } 49 + }
+9
project/iotaworks/gradle.properties
··· 1 + minecraft_version=1.20.1 2 + yarn_mappings=1.20.1+build.10 3 + loader_version=0.16.14 4 + scala_loader_version=0.3.1.11 5 + mod_version=0.1.0 6 + maven_group=org.eu.net.pool 7 + modid=iotaworks 8 + archives_base_name=iotaworks 9 + fabric_version=0.92.6+1.20.1
project/iotaworks/src/main/resources/assets/iotaworks/icon.png

This is a binary file and will not be displayed.

+5
project/iotaworks/src/main/resources/assets/iotaworks/lang/en_us.json
··· 1 + { 2 + "modmenu.nameTranslation.iotaworks": "Iotaworks", 3 + "modmenu.descriptionTranslation.iotaworks": "A Hex Casting addon for manipulating iotas.", 4 + "hexcasting.action.iotaworks:metatable": "Patchwork Exaltation" 5 + }
+5
project/iotaworks/src/main/resources/assets/iotaworks/lang/zh_cn.json
··· 1 + { 2 + "modmenu.nameTranslation.iotaworks": "Iota工程", 3 + "modmenu.descriptionTranslation.iotaworks": "能操纵iota本身的咒法学附属。", 4 + "hexcasting.action.iotaworks:metatable": "修补之提整" 5 + }
+11
project/iotaworks/src/main/resources/iotaworks.mixins.json
··· 1 + { 2 + "required": true, 3 + "minVersion": "0.8", 4 + "package": "org.eu.net.pool.iotaworks.mixin", 5 + "compatibilityLevel": "JAVA_17", 6 + "mixins": [ 7 + "HexPatternMixin", 8 + "HexPattern$CompanionMixin", 9 + "PatternIotaMixin" 10 + ] 11 + }
+168
project/iotaworks/src/main/scala/org/eu/net/pool/iotaworks/main.scala
··· 1 + package org.eu.net.pool 2 + package iotaworks 3 + 4 + import at.petrak.hexcasting.api.casting.eval.vm.{CastingImage, CastingVM, SpellContinuation} 5 + import at.petrak.hexcasting.api.casting.eval.{CastResult, CastingEnvironment, ResolvedPatternType} 6 + import at.petrak.hexcasting.api.casting.iota.{GarbageIota, Iota, IotaType, PatternIota, DoubleIota, NullIota, Vec3Iota} 7 + import at.petrak.hexcasting.api.casting.math.HexPattern 8 + import at.petrak.hexcasting.api.casting.mishaps.Mishap 9 + import at.petrak.hexcasting.api.casting.mishaps.Mishap.Context 10 + import at.petrak.hexcasting.api.pigment.FrozenPigment 11 + import at.petrak.hexcasting.common.casting.actions.eval.OpEval 12 + import com.google.gson.JsonElement 13 + import miyucomics.hexcellular.{PropertyIota, StateStorage} 14 + import net.minecraft.nbt.{NbtCompound, NbtList, NbtElement, NbtString} 15 + import net.minecraft.server.world.ServerWorld 16 + import net.minecraft.text.Text 17 + import net.minecraft.util.{DyeColor, Identifier} 18 + import org.eu.net.pool.phlib.{Events as PhEvents, *, given} 19 + import org.slf4j.{Logger, LoggerFactory} 20 + 21 + import java.{lang, util, util as ju} 22 + import at.petrak.hexcasting.api.casting.eval.vm.ContinuationFrame 23 + import kotlin.Pair 24 + import at.petrak.hexcasting.api.casting.eval.vm.ContinuationFrame.Type 25 + import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound 26 + import at.petrak.hexcasting.common.lib.hex.HexEvalSounds 27 + 28 + private[iotaworks] given Logger = LoggerFactory.getLogger("iotaworks") 29 + private[iotaworks] given Conversion[String, Identifier] = Identifier.of("iotaworks", _) 30 + given IotaType[PropertyIota] = PropertyIota.TYPE 31 + given IotaType[PatternIota] = PatternIota.TYPE 32 + 33 + abstract case class AbstractMetatableIota(iotaType: MetatableIotaType & Singleton, userdata: Iota, override val display: Text, metatable: String, readonlyMetatable: Boolean) extends Iota(iotaType, (userdata, display, metatable, readonlyMetatable)): 34 + override def subIotas(): lang.Iterable[Iota] = util.List.of(userdata) 35 + override def toleratesOther(that: Iota): Boolean = that match 36 + case AbstractMetatableIota(_, u, _, m, _) => metatable == m && Iota.tolerates(userdata, u) 37 + case _ => Iota.tolerates(userdata, that) 38 + override def serialize(): NbtElement = NbtCompound().tap: c => 39 + c.put("userdata", IotaType.serialize(userdata)) 40 + c.put("display", Text.Serializer.toJsonTree(display).convertDynamic) 41 + c.put("metatable", metatable) 42 + c.putBoolean("ro", readonlyMetatable) 43 + def meta(using world: ServerWorld): MapIota = 44 + StateStorage.Companion.getProperty(world, metatable) match 45 + case m: MapIota => m 46 + case i => throw MishapBadMetatable(metatable, i, readonlyMetatable) 47 + def meta_=(using world: ServerWorld)(x: MapIota): Unit = 48 + StateStorage.Companion.setProperty(world, metatable, x) 49 + infix def mro(key: HexPattern)(using world: ServerWorld): Option[Iota] = 50 + meta.get(PatternIota(key)).orElse: 51 + userdata match 52 + case m: AbstractMetatableIota => m mro key 53 + case _ => None 54 + override def isTruthy: Boolean = true 55 + override def executable: Boolean = true 56 + override def execute(using vm: CastingVM, world: ServerWorld, continuation: SpellContinuation): CastResult = callMetamethod(se"deaqq")(vm.getImage, continuation).getOrElse(super.execute(vm, world, continuation)) 57 + override def size = userdata.size + 1 58 + def callMetamethod(using env: CastingEnvironment)(key: HexPattern)(image: CastingImage, continuation: SpellContinuation): Option[CastResult] = 59 + for callee <- mro(key) yield 60 + val result = OpEval.INSTANCE.exec(env, image, continuation, image.getStack :+ userdata, callee) 61 + CastResult(callee, result.getNewContinuation, result.getNewImage, result.getSideEffects, ResolvedPatternType.EVALUATED, result.getSound) 62 + class MishapBadMetatable(name: String, value: Iota, readonly: Boolean) extends Mishap(): 63 + override def errorMessage(env: CastingEnvironment, ctx: Context): Text = Text.translatable("hexic.bad_metatable", name, value.display) 64 + override def accentColor(env: CastingEnvironment, ctx: Context): FrozenPigment = dyeColor(DyeColor.GRAY) 65 + override def execute(env: CastingEnvironment, ctx: Context, stack: ju.List[Iota]): Unit = 66 + stack(stack.length - 1) = GarbageIota() 67 + if !readonly then StateStorage.Companion.setProperty(env.getWorld, name, GarbageIota()) 68 + 69 + case class MetatableIotaType private[pool](override val color: Int) extends IotaType[AbstractMetatableIota]: 70 + class Instance(userdata: Iota, display: Text, metatable: String, readonlyMetatable: Boolean) extends AbstractMetatableIota(MetatableIotaType.this, userdata, display, metatable, readonlyMetatable) 71 + override def deserialize(tag: NbtElement, world: ServerWorld): Instance = 72 + val c = tag.downcast[NbtCompound] 73 + Instance( 74 + userdata = IotaType.deserialize(c.get("userdata").downcast[NbtCompound], world), 75 + display = Text.Serializer.fromJson(c.get("display").convertDynamic: JsonElement), 76 + metatable = c.get("metatable").downcast[NbtString].asString, 77 + readonlyMetatable = c.getBoolean("ro"), 78 + ) 79 + override def display(tag: NbtElement): Text = Text.Serializer.fromJson(tag.downcast[NbtCompound].get("display").convertDynamic: JsonElement) 80 + 81 + object MetatableIotaType: 82 + val validValues = Seq(0x0, 0x3, 0x6, 0x9, 0xC, 0xF) 83 + val colors: Map[(Int, Int, Int), MetatableIotaType] = (for r <- validValues; g <- validValues; b <- validValues yield (r, g, b) -> MetatableIotaType((r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b)).toMap 84 + println(s"Metatables: $colors") 85 + 86 + class DeltaFrame(delta: Int) extends ContinuationFrame: 87 + def breakDownwards(stack: ju.List[? <: Iota]): Pair[java.lang.Boolean, ju.List[Iota]] = Pair(true, stack.toSeq) 88 + def getType(): Type[DeltaFrame] = DeltaFrame 89 + def serializeToNBT = 90 + val c = NbtCompound() 91 + c.putInt("d", delta) 92 + c 93 + def size = 0 94 + def evaluate(cont: SpellContinuation, world: ServerWorld, vm: CastingVM): CastResult = 95 + CastResult(GarbageIota(), cont, DeltaFrame.shift(vm.getImage, delta)(using world), Seq(), ResolvedPatternType.EVALUATED, HexEvalSounds.NOTHING) 96 + object DeltaFrame extends ContinuationFrame.Type[DeltaFrame]: 97 + def deserializeFromNBT(data: NbtCompound, world: ServerWorld): DeltaFrame = 98 + DeltaFrame(data.getInt("d")) 99 + def shift(image: CastingImage, delta: Int)(using ServerWorld): CastingImage = 100 + val data = image.getUserData 101 + val list: NbtList = data.getList("iotaworks:stack", NbtElement.COMPOUND_TYPE) // stack[0] .. stack[n] | list[n] .. list[0] 102 + // We can always optimize this later. Go with the dumb method for now. 103 + val newStack = collection.mutable.Buffer.from(image.getStack) 104 + if delta > 0 then 105 + // move from end of stack to end of hold 106 + delta times list.add(IotaType.serialize(if newStack.nonEmpty then newStack.remove(newStack.length - 1) else NullIota())) 107 + else 108 + // move from end of hold to end of stack 109 + (-delta) times newStack.append(if list.nonEmpty then IotaType.deserialize(list.remove(list.length - 1).downcast, summon) else NullIota()) 110 + data.put("iotaworks:stack", list) 111 + CastingImage(newStack, image.getParenCount, image.getParenthesized, image.getEscapeNext, image.getOpsConsumed, data, null) 112 + 113 + private[iotaworks] object Extern: 114 + def handleExecute(pattern: PatternIota, vm: CastingVM, world: ServerWorld, continuation: SpellContinuation, original: (CastingVM, ServerWorld, SpellContinuation) => CastResult): CastResult = 115 + val delta = pattern.getPattern.asInstanceOf[HexPatternAccessor].depth 116 + if delta != 0 then 117 + vm.setImage(DeltaFrame.shift(vm.getImage, delta)(using world)) 118 + original(vm, world, continuation.pushFrame(DeltaFrame(-delta))) 119 + else 120 + original(vm, world, continuation) 121 + 122 + trait HexPatternAccessor: 123 + var depth: Int 124 + 125 + def init() = 126 + for ((_, c), i) <- MetatableIotaType.colors.zipWithIndex do iotaTypeRegistry(s"meta/$i") = c 127 + Patterns.register("metatable", se"deaqqwqqqeaeqqqeadedaqaaee"): 128 + Patterns.mkConstAction(4): 129 + case Seq(userdata, display, isIota[Vec3Iota, 1](color), isIota[PropertyIota, 0](metatable)) => 130 + val r = clamp(color.getVec3.x)(0.0, 1.0).*(5).round.toInt 131 + assume(0 until 6 contains r) 132 + val g = clamp(color.getVec3.y)(0.0, 1.0).*(5).round.toInt 133 + assume(0 until 6 contains g) 134 + val b = clamp(color.getVec3.z)(0.0, 1.0).*(5).round.toInt 135 + assume(0 until 6 contains b) 136 + Seq: 137 + val ty = MetatableIotaType.colors((r * 3, g * 3, b * 3)) 138 + ty.Instance(userdata, display.display, metatable.getName, metatable.getReadonly) 139 + if isDev then Patterns.register("metatable_abridged", se"ded"): 140 + Patterns.mkConstAction(3): 141 + case Seq(iota, key, value) => 142 + val ty = MetatableIotaType.colors(0, 0, 0) 143 + val pw = ty.Instance(iota, Text.literal("Test"), "iotaworks", false) 144 + pw.meta = MapIota() + (key -> value) 145 + Seq(pw) 146 + Patterns.register("set_subscript", w"eeedewa"): 147 + Patterns.mkConstAction(2): 148 + case Seq(isIota[PatternIota, 1](pat), isIota[DoubleIota, 0](num)) => 149 + // TODO: this should use iotaInt 150 + val realPat = pat.getPattern 151 + val pat2 = HexPattern(realPat.getStartDir, realPat.getAngles) 152 + pat2.asInstanceOf[HexPatternAccessor].depth = realPat.asInstanceOf[HexPatternAccessor].depth + num.getDouble.toInt 153 + Seq(PatternIota(pat2)) 154 + Patterns.register("get_subscript", nw"dwqaqqq"): 155 + Patterns.mkConstAction(1): 156 + case Seq(isIota[PatternIota, 0](pat)) => 157 + Seq(DoubleIota(pat.getPattern.asInstanceOf[HexPatternAccessor].depth)) 158 + PhEvents.registryLookup.register: 159 + case (r, i) if r == hexXplat.getIotaTypeRegistry && i.getNamespace == "hexic" && i.getPath.startsWith("meta/") => r(i.getPath) 160 + PhEvents.beforePatternExecute.register: 161 + Function.unlift: 162 + case (p, vm, given ServerWorld, cont) => 163 + given CastingEnvironment = vm.getEnv 164 + // this is probably cursed 165 + for 166 + case rest:+(m: AbstractMetatableIota) <- Option(vm.getStack).map(_.toSeq) 167 + result <- m.callMetamethod(p.getPattern)(vm.getImage.withStack(_ => rest), cont) 168 + yield result
+35
project/iotaworks/src/main/scala/org/eu/net/pool/iotaworks/mixin/HexPattern$CompanionMixin.java
··· 1 + package org.eu.net.pool.iotaworks.mixin; 2 + 3 + import org.eu.net.pool.iotaworks.HexPatternAccessor; 4 + 5 + import org.spongepowered.asm.mixin.Mixin; 6 + import org.spongepowered.asm.mixin.Mutable; 7 + import org.spongepowered.asm.mixin.Shadow; 8 + import org.spongepowered.asm.mixin.injection.Inject; 9 + import org.spongepowered.asm.mixin.injection.At; 10 + 11 + import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 12 + import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 13 + import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 14 + import com.llamalad7.mixinextras.sugar.Local; 15 + import com.mojang.datafixers.util.Either; 16 + import com.mojang.serialization.Codec; 17 + import com.mojang.serialization.codecs.RecordCodecBuilder; 18 + 19 + import at.petrak.hexcasting.api.casting.math.HexPattern; 20 + import net.minecraft.nbt.NbtCompound; 21 + 22 + @Mixin(HexPattern.Companion.class) 23 + class HexPattern$CompanionMixin { 24 + @WrapMethod(method = "fromNBT") 25 + public HexPattern fromNBT(NbtCompound c, Operation<HexPattern> original) { 26 + // see stupid reasoning in HexPatternMixin 27 + if (c.get("parent") instanceof NbtCompound c1) { 28 + HexPattern p = original.call(c1); 29 + ((HexPatternAccessor) (Object) p).depth_$eq(c.getInt("level")); 30 + return p; 31 + } else { 32 + return original.call(c); 33 + } 34 + } 35 + }
+56
project/iotaworks/src/main/scala/org/eu/net/pool/iotaworks/mixin/HexPatternMixin.java
··· 1 + package org.eu.net.pool.iotaworks.mixin; 2 + 3 + import org.eu.net.pool.iotaworks.HexPatternAccessor; 4 + 5 + import org.spongepowered.asm.mixin.Mixin; 6 + import org.spongepowered.asm.mixin.Mutable; 7 + import org.spongepowered.asm.mixin.Shadow; 8 + import org.spongepowered.asm.mixin.injection.Inject; 9 + import org.spongepowered.asm.mixin.injection.At; 10 + 11 + import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 12 + import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 13 + import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 14 + import com.llamalad7.mixinextras.sugar.Local; 15 + import com.mojang.datafixers.util.Either; 16 + import com.mojang.serialization.Codec; 17 + import com.mojang.serialization.codecs.RecordCodecBuilder; 18 + 19 + import at.petrak.hexcasting.api.casting.math.HexPattern; 20 + import net.minecraft.nbt.NbtCompound; 21 + 22 + @Mixin(HexPattern.class) 23 + public class HexPatternMixin implements HexPatternAccessor { 24 + private int depth = 0; 25 + public int depth() { return depth; } 26 + public void depth_$eq(int depth) { this.depth = depth; } 27 + 28 + @Shadow @Mutable public static Codec CODEC = null; // we don't actually use this field, but we need to @Mutable it so the wrapop can use it 29 + 30 + @WrapOperation(method = "<clinit>", at = @At(value = "FIELD", opcode = 179, ordinal = 1)) 31 + private static void wrapCodec(Codec<HexPattern> codec, Operation<Void> original) { 32 + original.call(Codec.<HexPattern, HexPattern>either(RecordCodecBuilder.create(b -> 33 + b.<HexPattern, Integer>group( 34 + codec.fieldOf("parent").forGetter(p -> p), 35 + Codec.INT.fieldOf("level").forGetter(p -> ((HexPatternMixin) (Object) p).depth) 36 + ).apply(b, (p, l) -> { 37 + ((HexPatternMixin) (Object) p).depth = l; 38 + return p; 39 + }) 40 + ), codec).xmap(e -> { 41 + HexPattern value[] = new HexPattern[1]; 42 + e.ifLeft(v -> { value[0] = v; }); 43 + e.ifRight(v -> { value[0] = v; }); 44 + return value[0]; 45 + }, Either::left)); 46 + } 47 + 48 + @WrapMethod(method = "serializeToNBT") 49 + private NbtCompound wrapSerialize(Operation<NbtCompound> original) { 50 + // ideally we'd just shove it into the compound, but codecs can't do that 51 + NbtCompound c = new NbtCompound(); 52 + c.put("parent", original.call()); 53 + c.putInt("level", depth); 54 + return c; 55 + } 56 + }
+52
project/iotaworks/src/main/scala/org/eu/net/pool/iotaworks/mixin/PatternIotaMixin.java
··· 1 + package org.eu.net.pool.iotaworks.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.iota.PatternIota; 4 + import at.petrak.hexcasting.api.casting.math.HexPattern; 5 + import at.petrak.hexcasting.api.casting.eval.CastResult; 6 + import at.petrak.hexcasting.api.casting.eval.vm.CastingVM; 7 + import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation; 8 + import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; 9 + import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 10 + import net.minecraft.text.Text; 11 + import net.minecraft.server.world.ServerWorld; 12 + import net.minecraft.text.MutableText; 13 + import org.eu.net.pool.iotaworks.Extern; 14 + import scala.collection.mutable.StringBuilder; 15 + 16 + import org.eu.net.pool.iotaworks.HexPatternAccessor; 17 + import org.spongepowered.asm.mixin.Mixin; 18 + import org.spongepowered.asm.mixin.injection.Inject; 19 + import org.spongepowered.asm.mixin.injection.At; 20 + 21 + @Mixin(PatternIota.class) 22 + public class PatternIotaMixin { 23 + @WrapMethod(method = "display(Lat/petrak/hexcasting/api/casting/math/HexPattern;)V") 24 + private static Text wrappedDisplay(HexPattern pat, Operation<Text> original) { 25 + int level = ((HexPatternAccessor) (Object) pat).depth(); 26 + StringBuilder buf = new StringBuilder(); 27 + boolean negative = level < 0; 28 + if (negative) level *= -1; 29 + while (level > 0) { 30 + switch (level % 10) { 31 + case 0: buf.addOne('⁰'); break; 32 + case 1: buf.addOne('¹'); break; 33 + case 2: buf.addOne('²'); break; 34 + case 3: buf.addOne('³'); break; 35 + case 4: buf.addOne('⁴'); break; 36 + case 5: buf.addOne('⁵'); break; 37 + case 6: buf.addOne('⁶'); break; 38 + case 7: buf.addOne('⁷'); break; 39 + case 8: buf.addOne('⁸'); break; 40 + case 9: buf.addOne('⁹'); break; 41 + } 42 + level /= 10; 43 + } 44 + if (negative) buf.addOne('⁻'); 45 + return ((MutableText) original.call(pat)).append(buf.reverse().toString()); 46 + } 47 + 48 + @WrapMethod(method = "execute") 49 + CastResult wrappedExecute(CastingVM vm, ServerWorld world, SpellContinuation continuation, Operation<CastResult> original) { 50 + return Extern.handleExecute((PatternIota) (Object) this, vm, world, continuation, original::call); 51 + } 52 + }
+5
settings.gradle.kts
··· 17 17 } 18 18 } 19 19 } 20 + 21 + include("util", "iotaworks", "hexxytounge", "hexxychests") 22 + project(":iotaworks").projectDir = file("project/iotaworks") 23 + project(":hexxytounge").projectDir = file("project/hexxytounge") 24 + project(":hexxychests").projectDir = file("project/hexxychests")
+2 -3
src/client/resources/hexic.client.mixins.json
··· 4 4 "package": "org.eu.net.pool.hexic.mixin.client", 5 5 "compatibilityLevel": "JAVA_17", 6 6 "client": [ 7 - "ChatHudMixin", 7 + "org.eu.net.pool.hexxytounge.mixin.ChatHudMixin", 8 8 "ChatInputSuggestorMixin", 9 9 "ChatScreenAccess", 10 10 "ChatScreenMixin", 11 - "InventoryScreenMixin", 12 - "MinecraftClientMixin" 11 + "InventoryScreenMixin" 13 12 ], 14 13 "injectors": { 15 14 "defaultRequire": 1
+4 -20
src/client/scala/org/eu/net/pool/hexic/client/HexicClient.scala src/client/scala/org/eu/net/pool/hexic/client/main.scala
··· 1 - package org.eu.net.pool.hexic 1 + package org.eu.net.pool 2 + package hexic 2 3 package client 3 4 4 5 import at.petrak.hexcasting.api.item.PigmentItem ··· 50 51 import scala.util.boundary.Label 51 52 import scala.util.chaining.scalaUtilChainingOps 52 53 54 + import phlib.{_, given} 55 + 53 56 given client: MinecraftClient = MinecraftClient.getInstance 54 57 55 58 inline def foldLocalPlayer[R](default: => R)(ifPresent: ClientPlayerEntity => R): R = ··· 60 63 var lastMurmur: Option[String] = None 61 64 62 65 object Hooks: 63 - def clientTick(): Unit = 64 - val currentMurmur = client.currentScreen match 65 - case null => None 66 - case c: ChatScreenAccess => Some(c.getChatField.getText) 67 - case _ => None 68 - if currentMurmur != lastMurmur then 69 - if isDev then println(s"Sending murmur: ${currentMurmur}") 70 - lastMurmur = currentMurmur 71 - val buf = PacketByteBufs.create() 72 - buf.writeBoolean(currentMurmur.isDefined) 73 - currentMurmur.foreach(buf.writeString) 74 - try 75 - ClientPlayNetworking.send("murmur", buf) 76 - catch 77 - case _: IllegalStateException => 78 66 def provideRenderText(string: String, firstCharacterIndex: Int, field: TextFieldWidget, original: OrderedText): OrderedText = 79 67 foldLocalPlayer(original): p => 80 68 val c = p.getComponent(PlayerInfoComponent.key) ··· 325 313 "deleteworld" -> "Shatter Demiplane", 326 314 "drop" -> "Rejection Distillation", 327 315 "dye_offhand" -> "Apply Pigment", 328 - "empty_map" -> "Vacant Reflection: Map", 329 316 "erase" -> "Erase Block", 330 317 "extract" -> "Excisor's Gambit", 331 318 "fox" -> "Vulpine Gambit", ··· 339 326 "make_cme" -> "Thoth's Pseudogambit", 340 327 "makeworld" -> "Conjure Demiplane", 341 328 "malloc" -> "Allocator's Purification", 342 - "metatable" -> "Patchwork Exaltation", 343 329 "modulo" -> "Modulus Distillation II", 344 - "murmur" -> "Murmur Reflection", 345 330 "nbt/deserialize" -> "Importer's Purification", 346 331 "nbt/lift1" -> "Secretary's Purification: Byte", 347 332 "nbt/lift2" -> "Secretary's Purification: Short", ··· 403 388 gen.add("book.hexic.page.erase", "Erases the _Hex or iota contained within a dropped item or block. Costs one dust per item.") 404 389 gen.add("book.hexic.page.get_other_caster", "Adds the closest sentient being, excluding me, to the stack.") 405 390 gen.add("book.hexic.page.modulo", "Similar to Modulus, but differs for negative numbers: -8 %%₁ 3 = -2, but -8 %%₂ 3 = 1.") 406 - gen.add("book.hexic.page.murmur", "Adds the phrase on the $(o)tip of my tongue/$ to the stack, regardless of whether I intend to say it.") 407 391 gen.add("hexdoc.hexic.description", "Miscellaneous neat features and QoL patterns for Hex Casting") 408 392 gen.add("hexdoc.hexic.title", "Hexic") 409 393 gen.add("hexic.media.external", "Media")
-33
src/client/scala/org/eu/net/pool/hexic/mixin/client/ChatHudMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin.client; 2 - 3 - import com.llamalad7.mixinextras.injector.ModifyExpressionValue; 4 - import com.llamalad7.mixinextras.sugar.Local; 5 - import net.minecraft.client.MinecraftClient; 6 - import net.minecraft.client.gui.hud.ChatHud; 7 - import net.minecraft.client.gui.hud.ChatHudLine; 8 - import net.minecraft.client.gui.hud.MessageIndicator; 9 - import net.minecraft.client.network.ClientPlayerEntity; 10 - import net.minecraft.text.Text; 11 - import org.eu.net.pool.hexic.PlayerInfoComponent; 12 - import org.spongepowered.asm.mixin.Mixin; 13 - import org.spongepowered.asm.mixin.injection.At; 14 - import scala.reflect.ClassTag; 15 - 16 - import java.util.ArrayList; 17 - import java.util.List; 18 - 19 - import static scala.jdk.javaapi.CollectionConverters.*; 20 - 21 - @Mixin(ChatHud.class) 22 - public class ChatHudMixin { 23 - @ModifyExpressionValue(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/hud/ChatHud;visibleMessages:Ljava/util/List;")) 24 - List<ChatHudLine.Visible> modifyVisibleMessages(List<ChatHudLine.Visible> original, @Local(ordinal = 0, argsOnly = true) int currentTick) { 25 - ArrayList ls = new ArrayList(); 26 - ClientPlayerEntity p = MinecraftClient.getInstance().player; 27 - if (p != null) 28 - for (Text t: asJava(p.getComponent(PlayerInfoComponent.key()).chatLines())) 29 - ls.add(0, new ChatHudLine.Visible(currentTick, t.asOrderedText(), MessageIndicator.system(), true)); 30 - ls.addAll(original); 31 - return ls; 32 - } 33 - }
-12
src/client/scala/org/eu/net/pool/hexic/mixin/client/ChatScreenAccess.java
··· 1 - package org.eu.net.pool.hexic.mixin.client; 2 - 3 - import net.minecraft.client.gui.screen.ChatScreen; 4 - import net.minecraft.client.gui.widget.TextFieldWidget; 5 - import org.spongepowered.asm.mixin.Mixin; 6 - import org.spongepowered.asm.mixin.gen.Accessor; 7 - 8 - @Mixin(ChatScreen.class) 9 - public interface ChatScreenAccess { 10 - @Accessor 11 - public TextFieldWidget getChatField(); 12 - }
-22
src/client/scala/org/eu/net/pool/hexic/mixin/client/MinecraftClientMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin.client; 2 - 3 - import net.minecraft.client.MinecraftClient; 4 - import net.minecraft.client.gui.screen.ChatScreen; 5 - import net.minecraft.client.gui.screen.Screen; 6 - import org.eu.net.pool.hexic.client.Hooks$; 7 - import org.jetbrains.annotations.Nullable; 8 - import org.spongepowered.asm.mixin.Mixin; 9 - import org.spongepowered.asm.mixin.Shadow; 10 - import org.spongepowered.asm.mixin.injection.At; 11 - import org.spongepowered.asm.mixin.injection.Inject; 12 - import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 - 14 - @Mixin(MinecraftClient.class) 15 - public class MinecraftClientMixin { 16 - @Shadow @Nullable public Screen currentScreen; 17 - 18 - @Inject(at = @At("HEAD"), method = "tick") 19 - void tick(CallbackInfo ci) { 20 - Hooks$.MODULE$.clientTick(); 21 - } 22 - }
+3 -3
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/greater_reveal.json project/hexxytounge/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexxytongue/greater_reveal.json
··· 7 7 "Thus, I've devised a new pattern of my own. It's similar to $(l:hexcasting:patterns/basics#hexcasting:print)$(action)Reveal/$, but the message is lodged well within my mind, and I forget it instantly if I replace it with something else. It can also take a list of messages, for convenience.", 8 8 { 9 9 "type": "hexcasting:pattern", 10 - "anchor": "hexic:reveal", 11 - "op_id": "hexic:reveal", 10 + "anchor": "hexxytounge:reveal", 11 + "op_id": "hexxytounge:reveal", 12 12 "input": "[iota] | iota", 13 13 "output": "", 14 - "text": "Takes a list of iotas (or a single non-iota, which is treated as a list with one element) and displays them permanently to the caster. Displayed iotas survive all methods that would clear a normal Reveal, and can only be cleared by another casting of Greater Reveal." 14 + "text": "Takes a list of iotas (or a single non-iota, which is treated as a list with one element) and embeds them in my mind persistently. Displayed iotas survive all methods that would clear a normal Reveal, and can only be cleared by another casting of Greater Reveal or my untimely demise." 15 15 }, 16 16 "$(o)To be a Hexcaster is to glimpse a truth that might best be hidden./$" 17 17 ]
+2 -2
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/maps.json util/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/phlib/maps.json
··· 7 7 "Maps are an efficient structure holding pairings of some iota to some other iota. While this is perfectly possible using merely list operations, dedicated map iotas use some arcane substance called a '$(thing)Hash/$'. While this doesn't reduce the $(o)operations/$ required to access a map, it desperately reduces the load on Nature, nearing O(1) for most access.", 8 8 { 9 9 "type": "hexcasting:pattern", 10 - "op_id": "hexic:empty_map", 11 - "anchor": "hexic:empty_map", 10 + "op_id": "phlib:empty_map", 11 + "anchor": "phlib:empty_map", 12 12 "input": "", 13 13 "output": "map<⊥, ⊥>", 14 14 "text": "Creates a new empty map for my usage."
+4 -4
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/hexic/metatables.json project/iotaworks/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/addon/iotaworks/metatables.json
··· 6 6 "pages": [ 7 7 "I've discovered a way to create a new type of iota — an abhorrence against Nature of my own doing. This pattern weaves a 'sheath' of _media out of some iota, letting me customize several attributes of the new iota. Though this can make my spells more expressive, this ability is not to be taken lightly — I cannot imagine the mischief others would do with these 'patchwork' iotas.", 8 8 "To assemble a patchwork iota, I must provide four source iotas:$(br)$(li)The 'userdata' iota, passed to my overload _Hexes.$(li)The 'display' iota, visible to my eyes.$(li)A color for the iota, as an RGB vector in the 0-1 range.$(li)A pointer to an $(thing)overload map./$", 9 - "Such an 'overload map' is merely a regular $(l:addon/hexic/maps)$(thing)Map/$ iota (mapping patterns to the _Hexes they should be replaced with), but stored within a $(l:properties)$(thing)Property/$. I assume that Nature's reason for boxing like this is to save space: it's likely I'll have many patched iotas all using the same overloads. This also gives me the convenience of updating every instance with merely $(l:properties#hexcellular:set_property)$(action)Schrödinger's Gambit/$, rather than finding and updating them all by hand.", 9 + "Such an 'overload map' is merely a regular $(l:addon/phlib/maps)$(thing)Map/$ iota (mapping patterns to the _Hexes they should be replaced with), but stored within a $(l:properties)$(thing)Property/$. I assume that Nature's reason for boxing like this is to save space: it's likely I'll have many patched iotas all using the same overloads. This also gives me the convenience of updating every instance with merely $(l:properties#hexcellular:set_property)$(action)Schrödinger's Gambit/$, rather than finding and updating them all by hand.", 10 10 { 11 11 "type": "hexcasting:pattern", 12 - "anchor": "hexic:metatable", 13 - "op_id": "hexic:metatable", 12 + "anchor": "iotaworks:metatable", 13 + "op_id": "iotaworks:metatable", 14 14 "input": "iota, iota, vec, property", 15 15 "output": "patchwork", 16 16 "text": "$(br)Constructs a Patchwork iota from the userdata (iota stored inside), display iota (visible to my eyes), color, and an overload map." ··· 28 28 { 29 29 "type": "patchouli:text", 30 30 "flag": "mod:hexdoc", 31 - "text": "These $(thing)overload maps/$ resemble a technique I've seen before — the concept of redefining patterns on a fundamental level resembles the moon cultists' (little time to explain) $(l:https://www.lua.org/pil/13.html)metatables/$, which allow associating a table with another table to redefine its operations. When my 'metamethods' are invoked, the userdata iota is pushed to the top of the stack. All other information about the patchwork disintegrates: I'll need to recreate it from scratch (maybe I should use a 'constructor' macro?) if I want to return a new iota of the same type." 31 + "text": "These $(thing)overload maps/$ resemble a technique I've heard of before — the concept of redefining patterns on a fundamental level resembles the moon cultists' (little time to explain) $(l:https://www.lua.org/pil/13.html)metatables/$, which allow associating a table with another table to redefine its operations. When my 'metamethods' are invoked, the userdata iota is pushed to the top of the stack. All other information about the patchwork disintegrates: I'll need to recreate it from scratch (maybe I should use a 'constructor' macro?) if I want to return a new iota of the same type." 32 32 }, 33 33 { 34 34 "type": "patchouli:text",
+3 -3
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/greater_reveal.json project/hexxytounge/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexxytongue/greater_reveal.json
··· 7 7 "因此,我自己设计了一个图案。它的功能和$(l:hexcasting:patterns/basics#hexcasting:print)$(action)揭示/$类似,但发来的消息会牢牢嵌在我的意识里,而且一经替换就会立刻遗忘。为方便起见,它还能接受一个消息列表。", 8 8 { 9 9 "type": "hexcasting:pattern", 10 - "anchor": "hexic:reveal", 11 - "op_id": "hexic:reveal", 10 + "anchor": "hexxytounge:reveal", 11 + "op_id": "hexxytounge:reveal", 12 12 "input": "[iota] | iota", 13 13 "output": "", 14 - "text": "接受 iota 列表(或单个非 iota,此时视作单元素列表)并永久性将其展示给施法者。清除普通揭示的方法无法清除此操作展示的 iota,而是只能通过再次施放卓越揭示清除。" 14 + "text": "接受 iota 列表(或单个非 iota,此时视作单元素列表)并永久性将其嵌入我的意识。清除普通揭示的方法无法清除此操作展示的 iota,而是只能通过再次施放卓越揭示或死亡清除。" 15 15 }, 16 16 "$(o)成为咒术师,就是去探索本不应被知晓的真相。/$" 17 17 ]
+4 -2
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/lists2.json
··· 63 63 }, 64 64 { 65 65 "type": "hexcasting:pattern", 66 + "header": "外积之馏化", 66 67 "op_id": "hexcasting:mul", 67 68 "anchor": "hexcasting:mul", 68 69 "input": "[a], [b]", ··· 71 72 }, 72 73 { 73 74 "type": "hexcasting:pattern", 74 - "op_id": "hexcasting:mul", 75 - "anchor": "hexcasting:mul", 75 + "header": "内积之馏化", 76 + "op_id": "hexcasting:div", 77 + "anchor": "hexcasting:div", 76 78 "input": "[a], [b]", 77 79 "output": "[[a, b]]", 78 80 "text": "计算两列表的内积。即纵向排布两列表的元素,而后相邻排列两列表,将每行的元素对组为列表返回(跳过未配对的元素)。"
+2 -2
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/maps.json util/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/phlib/maps.json
··· 7 7 "映射是能存储 iota 对的高效结构。虽然这种功能只靠列表操作也能做到,但专门设计的映射 iota 用到了某种奥秘物件,称作“$(thing)哈希/$”。这种区别并不会减少访问映射所需$(o)运算符/$的数量,却能大幅降低自然的负载,大多数访问下接近于 O(1)。", 8 8 { 9 9 "type": "hexcasting:pattern", 10 - "op_id": "hexic:empty_map", 11 - "anchor": "hexic:empty_map", 10 + "op_id": "phlib:empty_map", 11 + "anchor": "phlib:empty_map", 12 12 "input": "", 13 13 "output": "map<⊥, ⊥>", 14 14 "text": "创建一个空映射以供使用。"
+4 -4
src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/hexic/metatables.json project/iotaworks/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/zh_cn/entries/addon/iotaworks/metatables.json
··· 6 6 "pages": [ 7 7 "我找到了制造新种类 iota 的方法——由我对自然行径的憎恶催生。这个图案会在 iota 外部编织一层$(media)媒质/$“鞘”,以让我自定义新 iota 的多个属性。虽然这么做会让我的咒术更昂贵,但也不应轻视这种力量——我无法想象,其他人靠这些“修补”iota 能干出什么恶事。", 8 8 "构造修补 iota 时,必须提供 4 个来源 iota:$(br)$(li)自定义数据(userdata),真正传递给重载$(hex)咒术/$的 iota。$(li)所谓“展示”iota,我视角下 iota 的样子。$(li)Iota 的颜色,形式为 RGB 向量,分量值域为 0 到 1。$(li)指向$(thing)重载映射/$的指针。", 9 - "此处提到的“重载映射”只是一个普通的$(l:addon/hexic/maps)$(thing)映射/$ iota(图案至$(hex)咒术/$的映射,后者替代前者执行),但需整体存储在$(l:properties)$(thing)质性/$内。我推测自然进行封装是为了省空间,因为我可能会在多个修补 iota 中用到同一个重载映射。这同时也方便我更新所有实例——用一条$(l:properties#hexcellular:set_property)$(action)薛定谔之策略/$就可以了,不需要逐一找出更新。", 9 + "此处提到的“重载映射”只是一个普通的$(l:addon/phlib/maps)$(thing)映射/$ iota(图案至$(hex)咒术/$的映射,后者替代前者执行),但需整体存储在$(l:properties)$(thing)质性/$内。我推测自然进行封装是为了省空间,因为我可能会在多个修补 iota 中用到同一个重载映射。这同时也方便我更新所有实例——用一条$(l:properties#hexcellular:set_property)$(action)薛定谔之策略/$就可以了,不需要逐一找出更新。", 10 10 { 11 11 "type": "hexcasting:pattern", 12 - "anchor": "hexic:metatable", 13 - "op_id": "hexic:metatable", 12 + "anchor": "iotaworks:metatable", 13 + "op_id": "iotaworks:metatable", 14 14 "input": "iota, iota, vec, property", 15 15 "output": "patchwork", 16 16 "text": "$(br)使用自定义数据(userdata,内部存储的 iota)、展示 iota(实际看见的 iota)、颜色、重载映射创建一个修补 iota。" ··· 28 28 { 29 29 "type": "patchouli:text", 30 30 "flag": "mod:hexdoc", 31 - "text": "这些$(thing)重载映射/$和我之前见过的一种技巧很像:在基础层面上重新定义图案。那些月之邪教徒(没位置解释)用到的$(l:https://www.lua.org/pil/13.html)元表/$——关联两张表来重新定义运算符——也跟它很类似。每当唤起我这边的“元方法”时,即会将自定义数据压栈。修补 iota 的其他信息则都遗失了;如果要返回同种 iota,就需要从头再构建一遍。也许可以用个“构造”宏?" 31 + "text": "这些$(thing)重载映射/$和我之前听过的一种技巧很像:在基础层面上重新定义图案。那些月之邪教徒(没位置解释)用到的$(l:https://www.lua.org/pil/13.html)元表/$——关联两张表来重新定义运算符——也跟它很类似。每当唤起我这边的“元方法”时,即会将自定义数据压栈。修补 iota 的其他信息则都遗失了;如果要返回同种 iota,就需要从头再构建一遍。也许可以用个“构造”宏?" 32 32 }, 33 33 { 34 34 "type": "patchouli:text",
+3 -3
src/main/resources/assets/hexic/jsonpatch/strings.jsonpatch project/hexxytounge/src/main/resources/assets/hexxytounge/jsonpatch/strings.jsonpatch
··· 3 3 4 4 $pages = arrays.insert($pages, 11, { 5 5 type: "hexcasting:pattern", 6 - op_id: "hexic:murmur", 7 - anchor: "hexic:murmur", 6 + op_id: "hexxytounge:murmur", 7 + anchor: "hexxytounge:murmur", 8 8 input: "", 9 9 output: "str", 10 - text: "book.hexic.page.murmur" 10 + text: "book.hexxytounge.page.murmur" 11 11 });
+4 -4
src/main/resources/assets/hexic/lang/zh_cn.json
··· 3 3 "book.hexic.page.erase": "清除物品实体或方块中的$(hex)咒术/$或 iota。每个物品消耗 1 个紫水晶粉。", 4 4 "book.hexic.page.get_other_caster": "将离我最近的有智慧存在压入栈,我自己不计入统计范围。", 5 5 "book.hexic.page.modulo": "与余数之馏化类似,但两者在负数上的表现不同:-8 %%₁ 3 = -2,而 -8 %%₂ 3 = 1。", 6 - "book.hexic.page.murmur": "将$(o)在我嘴边/$的话语压入栈中,后续是否要说出不影响结果。", 6 + "hexcasting.action.hexic:deleteworld": "击碎半位面", 7 7 "hexcasting.action.hexic:drop": "拒斥之馏化", 8 8 "hexcasting.action.hexic:dye_offhand": "应用染色剂", 9 - "hexcasting.action.hexic:empty_map": "空无之精思:映射", 10 9 "hexcasting.action.hexic:erase": "清除方块", 11 10 "hexcasting.action.hexic:extract": "切除器之策略", 12 11 "hexcasting.action.hexic:fox": "狐狸之策略", ··· 17 16 "hexcasting.action.hexic:jvm/class_of_payload": "判类器之纯化,第一型", 18 17 "hexcasting.action.hexic:jvm/newinstance_boxed": "构造器之纯化,第一型", 19 18 "hexcasting.action.hexic:jvm/newinstance_unboxed": "构造器之纯化,第二型", 19 + "hexcasting.action.hexic:make_cme": "托特之伪策略", 20 + "hexcasting.action.hexic:makeworld": "构筑半位面", 20 21 "hexcasting.action.hexic:malloc": "分配器之纯化", 21 - "hexcasting.action.hexic:metatable": "修补之提整", 22 22 "hexcasting.action.hexic:modulo": "余数之馏化,第二型", 23 - "hexcasting.action.hexic:murmur": "私语之精思", 24 23 "hexcasting.action.hexic:nbt/deserialize": "导入器之纯化", 25 24 "hexcasting.action.hexic:nbt/lift1": "秘书之纯化:字节", 26 25 "hexcasting.action.hexic:nbt/lift2": "秘书之纯化:短整型", ··· 36 35 "hexcasting.action.hexic:nbt/serialize": "导出器之纯化", 37 36 "hexcasting.action.hexic:reveal": "卓越揭示", 38 37 "hexcasting.action.hexic:rotate": "摩天轮之馏化", 38 + "hexcasting.action.hexic:snow": "召雪", 39 39 "hexcasting.action.hexic:staffcast_factory": "Lani之卓越策略", 40 40 "hexcasting.action.hexic:staffcast_factory/lazy": "Lani之初等策略", 41 41 "hexcasting.action.hexic:take": "维持之馏化",
+1 -4
src/main/resources/data/hexcasting/tags/action/per_world_pattern.json
··· 2 2 "values": [ 3 3 "hexcasting:craft/artifact", 4 4 "hexcasting:craft/trinket", 5 - "hexic:snow", 6 - "hexic:findview", 7 - "hexic:moveconcept", 8 - "hexic:moveentity" 5 + "hexic:snow" 9 6 ] 10 7 }
+1 -4
src/main/resources/data/hexcasting/tags/action/requires_enlightenment.json
··· 1 1 { 2 2 "values": [ 3 - "hexic:snow", 4 - "hexic:findview", 5 - "hexic:moveconcept", 6 - "hexic:moveentity" 3 + "hexic:snow" 7 4 ] 8 5 }
+1 -1
src/main/resources/hexic.mixins.json
··· 7 7 "AbstractFurnaceBlockEntityMixin", 8 8 "ActionRegistryEntryMixin", 9 9 "BiomeMixin", 10 + "CastingEnvironmentMixin", 10 11 "CastingVMMixin", 11 12 "DimIotaMixin", 12 13 "EntityMixin", ··· 26 27 "OpTickMixin", 27 28 "OpTickMixin$SpellMixin", 28 29 "PacketByteBufMixin", 29 - "PatternIotaMixin", 30 30 "PlayerInventoryMixin", 31 31 "SimpleRegistryMixin", 32 32 "StaffCastEnvMixin",
+2 -1
src/main/scala/org/eu/net/pool/hexic/EarlyRiser.scala src/main/scala/org/eu/net/pool/hexic/early_riser.scala
··· 1 - package org.eu.net.pool.hexic 1 + package org.eu.net.pool 2 + package hexic 2 3 3 4 import com.chocohead.mm.api.{ClassTinkerers, EnumAdder} 4 5 import com.google.common.base.Charsets
+44 -806
src/main/scala/org/eu/net/pool/hexic/Hexic.scala src/main/scala/org/eu/net/pool/hexic/main.scala
··· 1 1 //noinspection NotImplementedCode 2 - package org.eu.net.pool.hexic 2 + package org.eu.net.pool 3 + package hexic 3 4 4 5 import at.petrak.hexcasting.api.addldata.ADMediaHolder 5 6 import at.petrak.hexcasting.api.casting.{ActionRegistryEntry, ParticleSpray, RenderedSpell, SpellList} 6 7 import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic 7 8 import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator 8 9 import at.petrak.hexcasting.api.casting.castables.{Action, ConstMediaAction, OperationAction, SpecialHandler, SpellAction} 9 - import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus 10 10 import at.petrak.hexcasting.api.casting.eval.env.PlayerBasedCastEnv 11 11 import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect.DoMishap 12 12 import at.petrak.hexcasting.api.casting.eval.sideeffects.{EvalSound, OperatorSideEffect} ··· 14 14 import at.petrak.hexcasting.api.casting.eval.{CastResult, CastingEnvironment, CastingEnvironmentComponent, MishapEnvironment, OperationResult, ResolvedPattern, ResolvedPatternType} 15 15 import at.petrak.hexcasting.api.casting.iota.* 16 16 import at.petrak.hexcasting.api.casting.math.{HexDir, HexPattern} 17 - import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapBadCaster, MishapBadOffhandItem, MishapInternalException, MishapInvalidIota, MishapInvalidOperatorArgs, MishapNotEnoughArgs, MishapOthersName, MishapTooManyCloseParens} 17 + import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapBadCaster, MishapBadOffhandItem, MishapInvalidIota, MishapInvalidOperatorArgs, MishapNotEnoughArgs, MishapOthersName, MishapTooManyCloseParens} 18 18 import at.petrak.hexcasting.api.pigment.FrozenPigment 19 19 import at.petrak.hexcasting.api.utils.{HexUtils, MediaHelper} 20 20 import at.petrak.hexcasting.common.lib.{HexAttributes, HexItems, HexRegistries, HexSounds} ··· 47 47 import miyucomics.hexical.features.pigments.{PigmentIota, PigmentIotaKt} 48 48 import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings 49 49 import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback 50 - import net.fabricmc.fabric.api.event.{Event, EventFactory} 51 50 import net.fabricmc.fabric.api.item.v1.FabricItemSettings 52 51 import net.fabricmc.fabric.api.transfer.v1.fluid.{FluidConstants, FluidVariant} 53 - import net.fabricmc.fabric.api.transfer.v1.item.{ItemStorage, ItemVariant} 54 - import net.fabricmc.fabric.api.transfer.v1.storage.{Storage, TransferVariant} 52 + import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant 53 + import net.fabricmc.fabric.api.transfer.v1.storage.TransferVariant 55 54 import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant 56 55 import net.fabricmc.fabric.api.transfer.v1.transaction.{Transaction, TransactionContext} 57 56 import net.fabricmc.loader.api.FabricLoader 58 57 import net.minecraft.Bootstrap 59 - import net.minecraft.block.entity.AbstractFurnaceBlockEntity 60 58 import net.minecraft.block.{AbstractBlock, Block, BlockRenderType, BlockState, BlockWithEntity, ShapeContext} 61 59 import net.minecraft.command.argument.{EntityArgumentType, NbtElementArgumentType, UuidArgumentType} 62 60 import net.minecraft.command.{CommandException, EntitySelector} ··· 74 72 import net.minecraft.server.world.ServerWorld 75 73 import net.minecraft.text.{HoverEvent, LiteralTextContent, MutableText, Style, Text, TextColor, TextContent, Texts} 76 74 import net.minecraft.util.dynamic.Codecs 77 - import net.minecraft.util.math.{BlockPos, ChunkPos, Direction, Vec3d, Box as MCBox} 75 + import net.minecraft.util.math.{BlockPos, ChunkPos, Direction, Vec3d} 78 76 import net.minecraft.util.{ActionResult, Arm, ClickType, DyeColor, Formatting, Hand, Identifier, Rarity, TypedActionResult, Util, Uuids, WorldSavePath} 79 77 import net.minecraft.world.biome.Biome 80 78 import net.minecraft.world.{BlockView, TeleportTarget, World} ··· 94 92 import java.nio.file.{Files, Path, Paths, StandardOpenOption} 95 93 import java.util.{Optional, UUID} 96 94 import java.{lang, util} 97 - import scala.annotation.meta.getter 98 95 import scala.annotation.unchecked.uncheckedVariance 99 96 import scala.annotation.{elidable, experimental, showAsInfix, tailrec, targetName, unused} 100 - import scala.collection.{IterableOnceOps, IterableOps} 101 97 import scala.ref.WeakReference 102 - import scala.util.{Failure, Success, Try, Using} 103 - export scala.collection.convert.ImplicitConversions.* 98 + import scala.util.{Failure, Success, Try} 104 99 import scala.collection.mutable 105 100 import scala.compiletime.summonFrom 106 101 import scala.concurrent.{Await, ExecutionContext, Future, Promise} ··· 109 104 import scala.language.{dynamics, existentials, implicitConversions, postfixOps, reflectiveCalls} 110 105 import scala.reflect.{ClassTag, classTag} 111 106 import scala.util.{NotGiven, Random, TupledFunction, boundary} 112 - import scala.util.chaining.given 113 107 import at.petrak.hexcasting.api.casting.mishaps.Mishap.Context 114 108 import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.PlayChannelHandler 115 109 import net.fabricmc.fabric.api.networking.v1.{FabricPacket, PacketByteBufs, PacketSender, PacketType, ServerPlayNetworking} ··· 192 186 import net.minecraft.world.chunk.{ChunkStatus, WorldChunk} 193 187 194 188 import scala.concurrent.duration.Duration 189 + import phlib.{Events as PhEvents, *, given} 195 190 196 191 given Logger = LoggerFactory.getLogger("hexic") 197 - 198 - val fabric = FabricLoader.getInstance 199 - val isDev = fabric.isDevelopmentEnvironment 192 + given Conversion[String, Identifier] = Identifier.of("hexic", _) 200 193 201 194 extension (i: Iota) 202 195 def asIotaType[T <: Iota: ClassTag](idx: Int, expected: => Text): T = i match ··· 238 231 //given Outcome[Iota]: 239 232 // override def ->: 240 233 241 - extension [T] (r: Registry[T]) 242 - def apply(key: Identifier | RegistryKey[?] | Int) = 243 - key match 244 - case i: Identifier => r.get(i) 245 - case i: Int => r.get(i) 246 - case k: RegistryKey[?] => r.get(k.asInstanceOf[RegistryKey[T]]) 247 - def update(key: Identifier | RegistryKey[?], value: T) = 248 - key match 249 - case i: Identifier => Registry.register(r, i, value) 250 - case k: RegistryKey[?] => Registry.register(r, k.asInstanceOf[RegistryKey[T]], value) 251 - 252 234 class :?[T, G](private val value: T)(using private val proof: G) 253 235 object :? : 254 236 given wrap[T, G](using G): Conversion[T, T :? G] = x => :?(x) ··· 258 240 override def apply(x: T): G ?=> T = _ ?=> x 259 241 import :?.given 260 242 261 - object Patterns: 262 - def mkAction(body: (CastingEnvironment, ServerWorld) ?=> (CastingImage, SpellContinuation) => (OperationResult | CastResult | (CastingImage, SpellContinuation, EvalSound, Seq[OperatorSideEffect]))): Action = 263 - (env: CastingEnvironment, image: CastingImage, cont: SpellContinuation) => 264 - try 265 - body(using env, env.getWorld)(image, cont) match 266 - case res: OperationResult => res 267 - case res: CastResult => OperationResult(res.getNewData, res.getSideEffects, res.getContinuation, res.getSound) 268 - case (img, cont, sound, effects) => OperationResult(img, effects, cont, sound) 269 - catch 270 - case _: NotImplementedError => throw MishapTodo() 271 - case e: MatchError => 272 - e.printStackTrace() 273 - throw MishapInvalidIota(image.getStack.lastOption.getOrElse(throw MishapNotEnoughArgs(1, 0).tap(_.initCause(e))), 0, "unknown").tap(_.initCause(e)) 274 - def mkConstAction(argc: Int, mediaCost: Long = 0)(body: (CastingEnvironment, ServerWorld) ?=> Seq[Iota] => Seq[Iota]): Action = 275 - new ConstMediaAction: 276 - import ConstMediaAction.DefaultImpls as d 277 - override def getArgc: Int = argc 278 - override def getMediaCost: Long = mediaCost 279 - override def execute(list: util.List[? <: Iota], castingEnvironment: CastingEnvironment): util.List[Iota] = 280 - body(using castingEnvironment, castingEnvironment.getWorld)(list.toSeq) 281 - override def executeWithOpCount(list: util.List[? <: Iota], castingEnvironment: CastingEnvironment): ConstMediaAction.CostMediaActionResult = d.executeWithOpCount(this, list, castingEnvironment) 282 - override def operate(castingEnvironment: CastingEnvironment, castingImage: CastingImage, spellContinuation: SpellContinuation): OperationResult = d.operate(this, castingEnvironment, castingImage, spellContinuation) 283 - def mkLiteral(value: (CastingEnvironment, ServerWorld) ?=> Iota): Action = 284 - mkConstAction(0): (args: Seq[Iota]) => 285 - args :+ value 286 - def register(id: Identifier, pattern: => HexPattern)(body: => Action): Unit = 287 - boundary: 288 - val p = try pattern catch case _: NotImplementedError => 289 - given_Logger.warn(s"No pattern for action $id") 290 - boundary.break() 291 - lazy val b = try body catch case _: NotImplementedError => throw MishapTodo() 292 - actionRegistry(id) = ActionRegistryEntry(p, new Action { export b._ }) 293 - def arithmetic(id: Identifier, pattern: HexPattern): Unit = 294 - Patterns.register(id, pattern): 295 - OperationAction(pattern) 296 - 297 - val hexXplat: IXplatAbstractions = IXplatAbstractions.INSTANCE 298 - 299 - extension (ctx: StringContext) def ifModLoaded(`then`: => Unit, `else`: => Unit = {}): Unit = 300 - if isDev || fabric.isModLoaded(ctx.parts(0)) then 301 - `then` 302 - else 303 - `else` 304 - 305 243 sealed abstract class PropertyAccessIota(name: String, direction: "head" | "tail")(using world: ServerWorld) extends Iota(PropertyAccessIota.Type, ()): 306 244 def property: Iota = StateStorage.Companion.getProperty(world, name) 307 245 def property_=(x: Iota): Unit = StateStorage.Companion.setProperty(world, name, x) ··· 426 364 case (k, v) if ClassTag(action.getClass) <:< k => v 427 365 .getOrElse(pattern) 428 366 429 - trait SlotReference: 430 - def item(using CastingEnvironment): Item 431 - def nbt(using CastingEnvironment): Option[NbtCompound] 432 - def count(using CastingEnvironment): Long 433 - @throws[Mishap] 434 - def nbt_=(using Transaction, CastingEnvironment)(nbt: Option[NbtCompound]): Unit 435 - @throws[Mishap] 436 - def count_=(using Transaction, CastingEnvironment)(count: Long): Unit 437 - 438 367 class PlayerInfoComponent( 439 368 val player: PlayerEntity, 440 369 var murmur: Option[String] = None, ··· 540 469 p.waitFor() 541 470 : Runnable 542 471 543 - lazy val iotaTypeRegistry = hexXplat.getIotaTypeRegistry 544 - lazy val actionRegistry = hexXplat.getActionRegistry 545 - 546 472 trait ServerAware[T <: IotaType[?]]: 547 473 type Iota = T match { case IotaType[t] => t } 548 474 def setServer(iota: Iota, server: String): Unit 549 475 550 476 given [T <: java.lang.Enum[T]: ClassTag as ct] => FromString[T]: 551 477 override def fromString(s: String): T = Enum.valueOf[T](ct.runtimeClass.asInstanceOf[Class[T]], s) 552 - 553 - extension [T] (x: T | Null) 554 - inline def ?[R](f: T => R): R | Null = x match 555 - case null => null 556 - case x: T => f(x) 557 - inline def ??(y: T): T = x match 558 - case null => y 559 - case x: T => x 560 478 561 479 case class Pen private [hexic] (color: DyeColor) extends Item(Item.Settings().maxCount(1)): 562 480 override def use(world: World, player: PlayerEntity, hand: Hand): TypedActionResult[ItemStack] = ··· 753 671 754 672 def toRoman(value: Int): String = 755 673 "M" * (value / 1000) + ("", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM").productElement(value % 1000 / 100) + ("", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC").productElement(value % 100 / 10) + ("", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX").productElement(value % 10) 756 - 757 - private [hexic] val conceptScale = mutable.Map[ClassTag[? <: TransferVariant[?]], Double]().withDefaultValue(1.0) 758 - def setConceptScale[T <: TransferVariant[?]: ClassTag as ct](scale: Int) = 759 - if conceptScale.isDefinedAt(ct) && conceptScale(ct) != scale then 760 - throw IllegalStateException(s"Conflicting scales ${conceptScale(ct)} and $scale defined for class $ct") 761 - else 762 - conceptScale(ct) = scale 763 674 764 675 private [hexic] object Extern: 765 676 private [hexic] val worlds = mutable.Set[WeakReference[World]]() ··· 829 740 override def execute(env: CastingEnvironment, ctx: Context, stack: util.List[Iota]): Unit = 830 741 stack.add(PatternIota(p)) 831 742 ) 832 - def splat(original: (args: util.List[Iota], env: CastingEnvironment) => util.List[Iota])(args: util.List[Iota], env: CastingEnvironment): util.List[Iota] = 833 - try 834 - original(args, env) 835 - catch case e: MishapInvalidIota => 836 - args.last match 837 - case m: MapIota => m.toList 838 - case _ => throw MishapInvalidIota(e.getPerpetrator, e.getReverseIdx, Text.translatable("text.hexic.or_map", e.getExpected)).initCause(e); 839 743 private [hexic] def getPocketName(pocket: String) = Text.of(pocketNames(getPocketID(Identifier.tryParse(pocket)).get)) 840 744 841 745 val _ = ··· 861 765 862 766 val aLotOfMedia = (200000 /* max phial size */ * 6 /* phials per small pouch */ * 4 /* small pouches per large pouch */ * (36 /* inventory slots */ + 4 /* armor slots */ + 2 /* offhands */) + 20 /* healthcasting */) * MediaConstants.DUST_UNIT 863 767 768 + class Event[T, R](default: T => R) extends (T => R): 769 + private var current = default 770 + def apply(x: T): R = current(x) 771 + def apply(fn: PartialFunction[T, R]): Unit = 772 + val old = current 773 + current = fn.applyOrElse(_, old) 774 + 775 + val useItemEvent = Event[(Item, ItemUsageContext, ItemUsageContext => ActionResult), ActionResult](p => p._3(p._2)) 776 + 864 777 trait HasCodec: 865 778 def getCodec: Codec[? <: this.type] 866 779 given [T <: Mishap] => Conversion[T, HasCodec] = _.asInstanceOf ··· 894 807 cache.synchronized: 895 808 cache.computeIfAbsent(x, f(_)) 896 809 810 + def iotaInt(iota: Iota, er: => Nothing): Int = 811 + iota match 812 + case d: DoubleIota => 813 + val n = d.getDouble 814 + val i = n.toInt 815 + if (i - n).abs > DoubleIota.TOLERANCE then 816 + er 817 + else 818 + i 819 + case _ => er 820 + @targetName("iotaInt max") 821 + def iotaInt(iota: Iota, max: Int, er: => Nothing): Int = 822 + val x = iotaInt(iota, er) 823 + if x > max then 824 + er 825 + else 826 + x 827 + @targetName("iotaInt under") 828 + def iotaInt(iota: Iota, under: Int, er: => Nothing): Int = 829 + val x = iotaInt(iota, er) 830 + if x >= under then 831 + er 832 + else 833 + x 834 + 897 835 trait MutableFunction[T, R] extends (T => R): 898 836 def update(key: T, value: R): Unit 899 837 ··· 921 859 case i: IOException => summon[Logger].warn("Failed to read properties", i) 922 860 iotaTypeRegistry("location") = LocationIota 923 861 iotaTypeRegistry("nbt") = NbtIota 924 - iotaTypeRegistry("variant") = VariantIota 925 - iotaTypeRegistry("map") = MapIota 926 862 iotaTypeRegistry("tripwire") = TripwireIota.getType 927 863 iotaTypeRegistry("access") = PropertyAccessIota.Type 928 - iotaTypeRegistry("reference") = BoxedView 929 - for ((_, c), i) <- MetatableIotaType.colors.zipWithIndex do iotaTypeRegistry(s"meta/$i") = c 930 864 hexXplat.getContinuationTypeRegistry("tripwire") = TripwireIota.Frame 931 865 for (color, item) <- Mediaweave.colors do 932 866 Registries.ITEM(s"${color.asString}_mediaweave") = item ··· 1094 1028 Patterns.mkLiteral(NbtIota(NbtIntArray(Array[Int]()))) 1095 1029 Patterns.register("nbt/literal/array4", (HexDir.EAST, "eedwaqqewww")): 1096 1030 Patterns.mkLiteral(NbtIota(NbtLongArray(Array[Long]()))) 1097 - Patterns.register("empty_map", (HexDir.EAST, "dqdwdqd")): 1098 - Patterns.mkLiteral(MapIota()) 1099 1031 Patterns.register("prop_fi", sw"aawqe"): 1100 1032 Patterns.mkConstAction(1): 1101 1033 case Seq(x: PropertyIota) => Seq(PropertyAccessIota.Writer(x.getName, "head")) ··· 1316 1248 ), 1317 1249 ) 1318 1250 }) 1319 - hexXplat.getArithmeticRegistry("maps") = 1320 - import Arithmetic.* 1321 - arith("map", 1322 - ADD -> ((a: MapIota, b: MapIota) => a ++ b), 1323 - SUB -> ((a: MapIota, b: MapIota) => a -- b), 1324 - ABS -> ((a: MapIota) => DoubleIota(a.map.size)), 1325 - INDEX -> ((a: MapIota, k: Iota) => a(k)), 1326 - UNAPPEND -> ((a: MapIota) => a.lastOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.init), 1327 - INDEX_OF -> ((a: MapIota, v: Iota) => 1328 - val c = IotaType.serialize(v) 1329 - a.update(_.filter(_._2 == c))), 1330 - REMOVE -> ((a: MapIota, k: Iota) => a - k), 1331 - REPLACE -> ((a: MapIota, k: Iota, v: Iota) => a + (k -> v)), 1332 - UNCONS -> ((a: MapIota) => a.headOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.tail), 1333 - AND -> ((a: MapIota, b: MapIota) => a & b), 1334 - OR -> ((a: MapIota, b: MapIota) => b ++ a), 1335 - XOR -> ((a: MapIota, b: MapIota) => a ^ b), 1336 - GREATER -> ((a: MapIota, b: MapIota) => a.map.containsAll(b.map) && a.map != b.map), 1337 - LESS -> ((a: MapIota, b: MapIota) => b.map.containsAll(a.map) && a.map != b.map), 1338 - GREATER_EQ -> ((a: MapIota, b: MapIota) => a.map containsAll b.map), 1339 - LESS_EQ -> ((a: MapIota, b: MapIota) => b.map containsAll a.map), 1340 - ) 1341 1251 def fox(tr: PlayerEntity ?=> PartialFunction[Option[FoxEntity.Type], Option[FoxEntity.Type]]): Action = 1342 1252 Patterns.mkAction: (img, cont) => 1343 1253 img.getStack.lastOption match ··· 1420 1330 case _ => null 1421 1331 ): SpecialHandler.Factory[? <: SpecialHandler] 1422 1332 CommandRegistrationCallback.EVENT.register: (d, r, e) => 1423 - d.getRoot.addChild(LiteralArgumentBuilder.literal[ServerCommandSource]("gimmeiota") 1424 - .requires(c => c.hasPermissionLevel(2) || (c.getPlayer != null && c.getPlayer.isCreative)) 1425 - .`then`(RequiredArgumentBuilder.argument("type", Interop.reat(r, HexRegistries.IOTA_TYPE)) 1426 - .`then`(RequiredArgumentBuilder.argument[ServerCommandSource, NbtElement]("data", NbtElementArgumentType.nbtElement()) 1427 - .executes(c => 1428 - val t = Interop.gre[IotaType[?]](c, "type", HexRegistries.IOTA_TYPE) 1429 - val d = NbtElementArgumentType getNbtElement(c, "data") 1430 - val p = c.getSource.getPlayer 1431 - if p == null then 1432 - throw CommandException("Command must be run by a player") 1433 - try 1434 - t.value deserialize(d, c.getSource.getWorld) match 1435 - case null => throw CommandException("Iota did not accept the given data") 1436 - case r: Iota => 1437 - p gimmeIota r 1438 - c.getSource sendFeedback(() => Text.translatable("Pushed %s to stack", try r.display catch case x: (Exception | Error) => x.getMessage), true) 1439 - 1 1440 - case x => throw CommandException(s"${x} is not an iota") 1441 - catch 1442 - case x: IllegalArgumentException => throw CommandException(x.getMessage) 1443 - ).build() 1444 - ).build() 1445 - ).`then`( 1446 - RequiredArgumentBuilder.argument[ServerCommandSource, EntitySelector]("entity", EntityArgumentType.entity()) 1447 - .executes(c => 1448 - val p = c.getSource.getPlayer 1449 - if p == null then 1450 - throw CommandException("Command must be run by a player") 1451 - val r = EntityIota(EntityArgumentType getEntity(c, "entity")) tap p.gimmeIota 1452 - c.getSource.sendFeedback(() => Text.translatable("Pushed %s to stack", r.display), true) 1453 - 1 1454 - ).build() 1455 - ).build()) 1456 1333 def planeAction(name: String)(body: MinecraftServer ?=> UUID => Int): Unit = 1457 1334 d.getRoot.addChild(LiteralArgumentBuilder.literal[ServerCommandSource](name).pipe: c => 1458 1335 c.requires(_.hasPermissionLevel(2)) ··· 1779 1656 null // kotlin bullshit 1780 1657 ), cont, HexEvalSounds.HERMES, Seq()) 1781 1658 case _ => throw MishapBadCaster() 1782 - Patterns.register("findview", e"addaadewewedaaddqwawqddaadewewedaaddqwawdeeweee"): 1783 - inline def lookup = InventoryView.Events.forIota.invoker()(using compiletime.summonInline) 1784 - Patterns.mkConstAction(1): 1785 - case Seq(lookup(view)) => Seq(BoxedView.Instance(view)) 1786 - case Seq(iota) => throw MishapInvalidIota.ofType(iota, 0, "hexic:view") 1787 - InventoryView.Events.forIota.register: 1788 - case block: Vec3Iota => 1789 - val pos = BlockPos.ofFloored(block.getVec3) 1790 - summon[CastingEnvironment].assertPosInRangeForEditing(pos) 1791 - InventoryView.OfBlock(pos) 1792 - hexXplat.getArithmeticRegistry("view") = arith("view", 1793 - Arithmetic.ADD -> { 1794 - (view1: BoxedView.Instance, view2: BoxedView.Instance) => Seq(BoxedView.Instance(InventoryView.OfSum(view1.view, view2.view))) 1795 - } 1796 - ) 1797 - setConceptScale[FluidVariant](81000) 1798 - setConceptScale[MediaVariant.type](10000) 1799 - setConceptScale[HeatVariant.type](20) 1800 - Patterns.register("moveconcept", se"wawdwawqdewewedqwawdwaw"): 1801 - Patterns.mkConstAction(4): 1802 - case Seq(isIota[BoxedView.Instance, 3](from), isIota[BoxedView.Instance, 2](into), typ: VariantIota[?], isIota[DoubleIota, 0](count)) => 1803 - Using.resource(Transaction.openOuter()): 1804 - case tx@given TransactionContext => 1805 - val key = ClassTag(typ.data.getClass) 1806 - val scale = conceptScale(key) 1807 - val toExtract = (scale * count.getDouble).toLong 1808 - val extract = from.view.tryExtract(typ.data, toExtract) 1809 - if extract < toExtract then 1810 - ??? // TODO: mishap 1811 - val insert = into.view.tryInsert(typ.data, extract) 1812 - if insert < extract then 1813 - ??? // TODO: mishap 1814 - tx.commit() 1815 - Seq(DoubleIota(insert / scale)) 1816 - Patterns.register("moveentity", e"edeeewawdweaaddaqwqwqaddaaewdwawewdqd"): 1817 - Patterns.mkConstAction(3): 1818 - case Seq(isIota[BoxedView.Instance, 2](from), isIota[BoxedView.Instance, 1](into), isIota[DoubleIota, 0](count)) => 1819 - Using.resource(Transaction.openOuter()): 1820 - case tx@given TransactionContext => 1821 - val count = from.view.entities.count(into.view.teleportEntity) 1822 - if count > 0 then tx.commit() 1823 - Seq(DoubleIota(count)) 1824 - Patterns.register("metatable", se"deaqqwqqqeaeqqqeadedaqaaee"): 1825 - Patterns.mkConstAction(4): 1826 - case Seq(userdata, display, isIota[Vec3Iota, 1](color), isIota[PropertyIota, 0](metatable)) => 1827 - val r = clamp(color.getVec3.x)(0.0, 1.0).*(5).round.toInt 1828 - assume(0 until 6 contains r) 1829 - val g = clamp(color.getVec3.y)(0.0, 1.0).*(5).round.toInt 1830 - assume(0 until 6 contains g) 1831 - val b = clamp(color.getVec3.z)(0.0, 1.0).*(5).round.toInt 1832 - assume(0 until 6 contains b) 1833 - Seq: 1834 - val ty = MetatableIotaType.colors((r * 3, g * 3, b * 3)) 1835 - ty.Instance(userdata, display.display, metatable.getName, metatable.getReadonly) 1836 1659 Patterns.register("get_other_caster", nw"ede"): 1837 1660 Patterns.mkLiteral: 1838 1661 val players: Set[LivingEntity] = summon[CastingEnvironment].getWorld.getPlayers.toSet ··· 1983 1806 Seq(ListIota(ls take n), ListIota(ls drop (n+1)), ls(n)) 1984 1807 case Seq(ary: ListIota, nr) => throw MishapInvalidIota.ofType(nr, 0, "int") 1985 1808 case Seq(ary, _) => throw MishapInvalidIota.ofType(ary, 1, "list") 1986 - Patterns.register("murmur", e"wwaqwa"): 1987 - Patterns.mkLiteral: 1988 - locally(summon[CastingEnvironment]).getCastingEntity match 1989 - case null => throw MishapBadCaster() 1990 - case p: ServerPlayerEntity => p.getComponent(PlayerInfoComponent.key).murmur.fold(NullIota())(StringIota.make) 1991 - case _ => throw MishapBadCaster() 1992 1809 Patterns.register("make_cme", ne"dqqd"): 1993 1810 Patterns.mkAction: (img, cont) => 1994 1811 img.getStack.toSeq.reverse match ··· 2043 1860 case Seq(i, _: ListIota, _*) => throw MishapInvalidIota.ofType(i, 0, "list") 2044 1861 case Seq(_, i, _*) => throw MishapInvalidIota.ofType(i, 1, "list") 2045 1862 case s => throw MishapNotEnoughArgs(2, s.size) 2046 - Patterns.register("reveal", ne"deqed"): 2047 - Patterns.mkConstAction(1, 0): 2048 - case Seq(iota: Iota) => 2049 - locally(summon[CastingEnvironment]).getCastingEntity match 2050 - case null => throw MishapBadCaster() 2051 - case p: ServerPlayerEntity => 2052 - p.getComponent(PlayerInfoComponent.key).chatLines = iota match 2053 - case s: ListIota => s.getList.map(_.display).toSeq 2054 - case _ => Seq(iota.display) 2055 - p.syncComponent(PlayerInfoComponent.key) 2056 - Seq() 2057 - case _ => throw MishapBadCaster() 2058 - ServerPlayNetworking.registerGlobalReceiver("murmur", (_, player, _, buf, _) => 2059 - val in = Option.when(buf.readBoolean())(buf.readString()) 2060 - if isDev then println(s"${player.getName.getString} murmurs: $in") 2061 - player.getComponent(PlayerInfoComponent.key).murmur = in) 2062 1863 lazy val messageFrameType: ContinuationFrame.Type[MessageFrame] = (c: NbtCompound, world: ServerWorld) => 2063 1864 val id = Uuids.toUuid(c.getIntArray("id")) 2064 1865 MessageFrame(id, Text.Serializer.fromJson(NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, c.getCompound("t"))), world.getServer.getPlayerManager.getPlayer(id)) ··· 2156 1957 val image = CastingImage(context :+ StringIota.make(text), 0, Seq(), false, 0, NbtCompound(), null) 2157 1958 val instrs = s.getList.asScala.toSeq 2158 1959 val vm = CastingVM(image, env) 2159 - val view = vm.queueExecuteAndWrapIotas(instrs.asJava, player.getServerWorld, SpellContinuation.NotDone(MessageFrame(player.getUuid, stack.getName, player), SpellContinuation.Done.INSTANCE)) 1960 + val view = vm.queueExecuteAndWrapIotas(instrs :+ ContinuationIota(SpellContinuation.NotDone(MessageFrame(player.getUuid, stack.getName, player), SpellContinuation.Done.INSTANCE)), player.getServerWorld) 2160 1961 val packet = MsgNewSpiralPatternsS2C(player.getUuid, instrs.collect { case p: PatternIota => p.getPattern }.asJava, 140) 2161 1962 hexXplat.sendPacketToPlayer(player, packet) 2162 1963 hexXplat.sendPacketTracking(player, packet) ··· 2173 1974 finally 2174 1975 out.close() 2175 1976 2176 - def iotaInt(iota: Iota, er: => Nothing): Int = 2177 - iota match 2178 - case d: DoubleIota => 2179 - val n = d.getDouble 2180 - val i = n.toInt 2181 - if (i - n).abs > DoubleIota.TOLERANCE then 2182 - er 2183 - else 2184 - i 2185 - case _ => er 2186 - @targetName("iotaInt max") 2187 - def iotaInt(iota: Iota, max: Int, er: => Nothing): Int = 2188 - val x = iotaInt(iota, er) 2189 - if x > max then 2190 - er 2191 - else 2192 - x 2193 - @targetName("iotaInt under") 2194 - def iotaInt(iota: Iota, under: Int, er: => Nothing): Int = 2195 - val x = iotaInt(iota, er) 2196 - if x >= under then 2197 - er 2198 - else 2199 - x 2200 - 2201 - extension [T, R] (f: T => R) def ∘ [U](g: U => T) = (x: U) => f(g(x)) 2202 - def wrapReturn[T](body: (T => Nothing) => T): T = body(return _) 2203 - def wrapThrow[T, E <: Throwable](body: (E => Nothing) => T): T = wrapReturn[Try[T]](r => Success(body(r∘Failure))).get 2204 - 2205 - inline def debugln(inline msg: String): Unit = 2206 - if isDev then println(msg) else () 2207 - 2208 - extension [T] (inline x: T) inline def trace(inline key: String): T = 2209 - debugln(s"$key: ${compiletime.codeOf(x)} = $x") 2210 - x 2211 - extension [T] (inline x: T) inline def trace(): T = 2212 - debugln(s"${compiletime.codeOf(x)} = $x") 2213 - x 2214 - 2215 - def propagateMishaps[T](env: CastingEnvironment)(body: => T): T = 2216 - wrapThrow[T, Mishap]: doThrow => 2217 - object key extends CastingEnvironmentComponent.Key[?] 2218 - env.addExtension: 2219 - new CastingEnvironmentComponent with CastingEnvironmentComponent.PostExecution: 2220 - override def getKey: CastingEnvironmentComponent.Key[?] = key 2221 - override def onPostExecution(result: CastResult): Unit = 2222 - result.getSideEffects.collectFirst: 2223 - case m: OperatorSideEffect.DoMishap => 2224 - if isDev then println(s"Propagating mishap: $m") 2225 - doThrow(m.getMishap) 2226 - try body finally env.removeExtension(key) 2227 - 2228 - def clamp[@specialized T: Ordering](x: T)(min: T, max: T): T = 2229 - assume(max > min) 2230 - if x < min then min 2231 - else if x > max then max 2232 - else x 2233 - 2234 - trait InventoryView(val viewType: InventoryView.Type[?]) extends InventoryView.Handler: 2235 - def isTruthy = true 2236 - def serialize: NbtCompound = NbtCompound().tap(_.putString("id", InventoryView.registry.getId(viewType).toString)) 2237 - object InventoryView extends Registrar[InventoryView.Type[?]]("inventory"): 2238 - trait Type[+T <: InventoryView]: 2239 - def deserialize(data: NbtCompound)(using ServerWorld): Option[T] 2240 - trait Handler: 2241 - def apply(idx: Int)(using CastingEnvironment): Option[SlotReference] = None 2242 - def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 0 2243 - def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 0 2244 - def capacity(variant: TransferVariant[?])(using TransactionContext, CastingEnvironment): Long = 2245 - Using(summon[TransactionContext].openNested()): tx => 2246 - given TransactionContext = tx 2247 - tryExtract(variant, Long.MaxValue) 2248 - match 2249 - case Success(n) => n 2250 - case Failure(ex) => 2251 - given_Logger.error("capacity", ex) 2252 - 0L 2253 - def entities(using TransactionContext): Set[Entity] = Set() 2254 - @throws[Mishap] 2255 - def teleportEntity(ent: Entity)(using TransactionContext, CastingEnvironment): Boolean = false 2256 - object Events: 2257 - val forEntity: Event[Entity => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, _ => Seq(), fns => e => fns.flatMap(_(e))) 2258 - val forBlock: Event[(BlockPos, BlockState) => ServerWorld ?=> Seq[Handler]] = EventFactory.createArrayBacked(classOf, (_, _) => Seq(), fns => (pos, state) => fns.flatMap(_(pos, state))) 2259 - // 'implementation restriction' my ass 2260 - val forIota: Event[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]] = EventFactory.createArrayBacked(classOf, PartialFunction.empty, new util.function.Function[Array[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]], CastingEnvironment ?=> PartialFunction[Iota, InventoryView]]: 2261 - def apply(fns: Array[CastingEnvironment ?=> PartialFunction[Iota, InventoryView]]): CastingEnvironment ?=> PartialFunction[Iota, InventoryView] = (((_: CastingEnvironment) ?=> PartialFunction.empty[Iota, InventoryView]) /: fns) { _ orElse _ } 2262 - ) 2263 - abstract class OfMerged(viewType: InventoryView.Type[?], views: => Seq[Handler]) extends InventoryView(viewType): 2264 - def getViews = views 2265 - override def apply(idx: Int)(using CastingEnvironment): Option[SlotReference] = views.collectFirst(hexicVisibilityHack.unlifted(_(idx))) 2266 - override def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = LazyList.from(views).scanLeft(0L)((n, view) => view.tryExtract(variant, amount - n) + n).findFirstOrLast(_ >= amount).getOrElse(0) 2267 - override def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = LazyList.from(views).scanLeft(0L)((n, view) => view.tryInsert(variant, amount - n) + n).findFirstOrLast(_ >= amount).getOrElse(0) 2268 - override def capacity(variant: TransferVariant[?])(using TransactionContext, CastingEnvironment) = views.map(_.capacity(variant)).sum 2269 - override def entities(using TransactionContext) = views.flatMap(_.entities).toSet 2270 - override def teleportEntity(ent: Entity)(using TransactionContext, CastingEnvironment): Boolean = views.iterator∃(_.teleportEntity(ent)) 2271 - class OfSum private(private[InventoryView] val views: Seq[InventoryView]) extends OfMerged(typeOfSum, views): 2272 - override def isTruthy = views ∃(_.isTruthy) 2273 - override def serialize = 2274 - val c = NbtCompound() 2275 - val list = NbtList() 2276 - for view <- views do list.add(view.serialize) 2277 - c.put("c", list) 2278 - c 2279 - object OfSum: 2280 - def apply(views: InventoryView*) = new OfSum( 2281 - views.flatMap: 2282 - case v: OfSum => v.views 2283 - case v => Iterable(v) 2284 - ) 2285 - class OfEntity(id: UUID)(using server: MinecraftServer) extends OfMerged(typeOfEntity, server.getWorlds.collectFirst(hexicVisibilityHack.unlifted(w => Option(w.getEntity(id)).map(Events.forEntity.invoker()(_)(using w)))).getOrElse(Seq())): 2286 - override def serialize = 2287 - val c = super.serialize 2288 - c.putLong("m", id.getMostSignificantBits) 2289 - c.putLong("l", id.getLeastSignificantBits) 2290 - c 2291 - class OfBlock(pos: BlockPos)(using world: ServerWorld) extends OfMerged(typeOfBlock, Events.forBlock.invoker()(pos, world.getBlockState(pos))): 2292 - override def serialize = 2293 - val c = super.serialize 2294 - val w = world.getRegistryKey.getValue 2295 - if w.getNamespace != "minecraft" then c.put("m", w.getNamespace) 2296 - if w.getPath != "overworld" then c.put("w", w.getPath) 2297 - c.putLong("p", pos.asLong) 2298 - c 2299 - class OfExactEntity(entity: => Entity)(using ServerWorld) extends InventoryView(typeOfExactEntity): 2300 - override val viewType = typeOfExactEntity 2301 - override def entities(using TransactionContext) = Set(entity) 2302 - override def serialize = 2303 - val c = super.serialize 2304 - // TODO 2305 - c 2306 - def deserialize(data: NbtCompound)(using ServerWorld): Option[InventoryView] = for 2307 - id <- Option(Identifier.tryParse(data.getString("id"))) 2308 - viewType <- Option(InventoryView.registry.get(id)) 2309 - view <- viewType.deserialize(data) 2310 - yield view 2311 - private given typeOfSum: InventoryView.Type[OfSum]: 2312 - override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfSum] = 2313 - Some(OfSum((for 2314 - n <- 0 until data.getInt("n") 2315 - key = "_" + Integer.toString(n + 10, 36) 2316 - c = data.getCompound(key) 2317 - view <- InventoryView.deserialize(c) 2318 - yield view)*)) 2319 - private given typeOfEntity: InventoryView.Type[OfEntity]: 2320 - override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfEntity] = ??? 2321 - private given typeOfBlock: InventoryView.Type[OfBlock]: 2322 - override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfBlock] = 2323 - for 2324 - case posLong: NbtLong <- Option(data.get("p")) 2325 - pos = BlockPos.fromLong(posLong.longValue) 2326 - namespace = Option(data.getString("m")).filter(!_.isBlank) getOrElse "minecraft" 2327 - path = Option(data.getString("w")).filter(!_.isBlank) getOrElse "overworld" 2328 - key = RegistryKey.of(RegistryKeys.WORLD, Identifier.of(namespace, path)) 2329 - given ServerWorld <- Option(summon[ServerWorld].getServer.getWorld(key)) 2330 - yield OfBlock(pos) 2331 - private given typeOfExactEntity: InventoryView.Type[OfExactEntity]: 2332 - override def deserialize(data: NbtCompound)(using ServerWorld): Option[OfExactEntity] = ??? 2333 - registry("sum") = typeOfSum 2334 - registry("entity") = typeOfEntity 2335 - registry("block") = typeOfBlock 2336 - registry("exact") = typeOfExactEntity 2337 - Events.forBlock.register: (pos, state) => 2338 - val storage = ItemStorage.SIDED.find(summon, pos, null): Storage[ItemVariant] 2339 - if storage == null then Seq() 2340 - else Seq( 2341 - new Handler: 2342 - override def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 2343 - variant match 2344 - case i: ItemVariant => storage.insert(i, amount, summon) 2345 - case _ => 0 2346 - override def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = 2347 - variant match 2348 - case i: ItemVariant => storage.extract(i, amount, summon) 2349 - case _ => 0 2350 - ) 2351 - Events.forBlock.register: (pos, state) => 2352 - if state.isTransparent(summon, pos) then 2353 - Seq(new Handler: 2354 - override def entities(using TransactionContext): Set[Entity] = summon[World].getOtherEntities(null, MCBox.of(pos.toCenterPos, 0.5, 0.5, 0.5), _ => true).toSet 2355 - override def teleportEntity(ent: Entity)(using TransactionContext, CastingEnvironment): Boolean = 2356 - var currEnt = ent 2357 - doSnapshot((currEnt.getPos, currEnt.getWorld.asInstanceOf[ServerWorld]), snapshot => currEnt = FabricDimensions.teleport(currEnt, snapshot._2, TeleportTarget(snapshot._1, currEnt.getVelocity, currEnt.getYaw, currEnt.getPitch)))(pos.toCenterPos.subtract(0, 0.25, 0), summon) 2358 - true 2359 - ) 2360 - else Seq() 2361 - trait SingleVariantHandler(variant: TransferVariant[?]) extends Handler: 2362 - override def tryExtract(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = if variant == this.variant then trySubtract(amount) else 0L 2363 - override def tryInsert(variant: TransferVariant[?], amount: Long)(using TransactionContext, CastingEnvironment): Long = if variant == this.variant then tryAdd(amount) else 0L 2364 - override def capacity(variant: TransferVariant[?])(using TransactionContext, CastingEnvironment): Long = if variant == this.variant then cap else 0L 2365 - def trySubtract(amount: Long)(using TransactionContext, CastingEnvironment): Long 2366 - def tryAdd(amount: Long)(using TransactionContext, CastingEnvironment): Long 2367 - def cap(using TransactionContext, CastingEnvironment): Long 2368 - 2369 - Events.forBlock.register: (pos, state) => 2370 - summon[ServerWorld].getBlockEntity(pos) match 2371 - case e: BlockEntityAbstractImpetus => Seq(new SingleVariantHandler(MediaVariant) { 2372 - private def mediaCapacity = 9000000000000000000L - e.getMedia 2373 - override def trySubtract(amount: Long)(using TransactionContext, CastingEnvironment): Long = 2374 - val toExtract = amount min e.getMedia 2375 - doSnapshot(e.getMedia, e.setMedia)(e.getMedia - toExtract) 2376 - toExtract 2377 - override def tryAdd(amount: Long)(using TransactionContext, CastingEnvironment): Long = 2378 - val toInsert = amount min mediaCapacity 2379 - doSnapshot(e.getMedia, e.setMedia)(e.getMedia + toInsert) 2380 - toInsert 2381 - override def cap(using TransactionContext, CastingEnvironment): Long = mediaCapacity 2382 - }) 2383 - case _ => Seq() 2384 - Events.forBlock.register: (pos, state) => 2385 - summon[ServerWorld].getBlockEntity(pos) match 2386 - case e: AbstractFurnaceBlockEntity => Seq( 2387 - new SingleVariantHandler(HeatVariant): 2388 - override def trySubtract(amount: Long)(using TransactionContext, CastingEnvironment): Long = 2389 - val action = (amount min e.burnTime).toInt 2390 - doSnapshot(e.burnTime, e.burnTime = _)(e.burnTime - action) 2391 - action 2392 - override def tryAdd(amount: Long)(using TransactionContext, CastingEnvironment): Long = 2393 - val action = (amount min cap).toInt 2394 - doSnapshot(e.burnTime, e.burnTime = _)(e.burnTime + action) 2395 - doSnapshot(e.fuelTime, e.fuelTime = _)(e.fuelTime max e.burnTime) 2396 - action 2397 - override def cap(using TransactionContext, CastingEnvironment): Long = (5*60*20) - e.burnTime 2398 - ) 2399 - case _ => Seq() 2400 - given Conversion[AbstractFurnaceBlockEntity, AbstractFurnaceBlockEntityAccess] = _.asInstanceOf // by mixin 2401 - trait AbstractFurnaceBlockEntityAccess: 2402 - def burnTime: Int 2403 - def burnTime_=(burnTime: Int): Unit 2404 - def fuelTime: Int 2405 - def fuelTime_=(fuelTime: Int): Unit 2406 - 2407 - def doSnapshot[T](current: => T, apply: T => Unit)(toApply: T)(using tx: TransactionContext): Unit = 2408 - object partip extends SnapshotParticipant[T]: 2409 - override def createSnapshot(): T = current 2410 - override def readSnapshot(snapshot: T): Unit = apply(snapshot) 2411 - partip.updateSnapshots(tx) 2412 - partip.readSnapshot(toApply) 2413 - 2414 - object BoxedView extends IotaType[BoxedView.Instance]: 2415 - InventoryView 2416 - class Instance(val view: InventoryView) extends Iota(BoxedView, view): 2417 - export view.{isTruthy, serialize} 2418 - override def toleratesOther(that: Iota): Boolean = that match 2419 - case that: BoxedView.Instance => view == that.view 2420 - case _ => false 2421 - override def deserialize(tag: NbtElement, world: ServerWorld): Instance = 2422 - given ServerWorld = world; 2423 - (for 2424 - case c: NbtCompound <- Some(tag) 2425 - view <- InventoryView.deserialize(c) 2426 - yield Instance(view)).orNull 2427 - override def display(tag: NbtElement): Text = "[View]".styled(_.withColor(color)) 2428 - override def color: Int = 0xa59e7c 2429 - given this.type = this 2430 - 2431 - object SlotReference extends Registrar[SlotReference.Type[?]]("slot"): 2432 - class Type[T <: SlotReference: Codec] 2433 - 2434 - object isIota: 2435 - def unapply[T <: Iota: IotaType as ty: ClassTag, I <: Int: Const as i](iota: Iota): Some[T] = 2436 - iota match 2437 - case iota: T => Some(iota) 2438 - case _ => throw MishapInvalidIota(iota, i, ty.typeName) 2439 - 2440 1977 given IotaType[PropertyIota] = PropertyIota.TYPE 2441 1978 given IotaType[Vec3Iota] = Vec3Iota.TYPE 2442 1979 ··· 2453 1990 opaque type Attrition = Unit 2454 1991 object Attrition extends Registrar[Attrition]("attrition") 2455 1992 2456 - @tailrec 2457 - def finishOperation(p: OperationResult)(using env: CastingEnvironment): OperationResult = 2458 - p.getNewContinuation match 2459 - case c: SpellContinuation.Done => p 2460 - case c: SpellContinuation.NotDone => 2461 - finishCast(c.getFrame.evaluate(c.getNext, env.getWorld, CastingVM(p.getNewImage, env)), p.getNewImage) 2462 - 2463 - inline def finishCast(p: CastResult, oldImage: CastingImage)(using env: CastingEnvironment): OperationResult = 2464 - finishOperation(OperationResult(p.getNewData??oldImage, p.getSideEffects, p.getContinuation, p.getSound)) 2465 - 2466 1993 type subtypes[T, R <: T] = T 2467 1994 //case class StaffcastFrame(owner: ServerPlayerEntity, oldImage: CastingImage) extends ContinuationFrame: 2468 1995 // override def getType: ContinuationFrame.Type[StaffcastFrame] = StaffcastFrame ··· 2477 2004 // def deserializeFromNBT(data: NbtCompound, world: ServerWorld): StaffcastFrame = ??? 2478 2005 2479 2006 val fadedScrolls: TagKey[ActionRegistryEntry] = TagKey.of(HexRegistries.ACTION, "faded_scrolls") 2480 - 2481 - extension (ctx: StringContext) 2482 - def ne(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.NORTH_EAST) 2483 - def e(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.EAST) 2484 - def se(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.SOUTH_EAST) 2485 - def nw(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.NORTH_WEST) 2486 - def w(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.WEST) 2487 - def sw(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.SOUTH_WEST) 2488 2007 2489 2008 extension (text: Text) 2490 2009 def +(other: Text): MutableText = Text.literal("").append(text).append(other) ··· 2522 2041 given Codec[Nonce] = Uuids.CODEC.xmap(Nonce(_), _.id) 2523 2042 given Conversion[Nonce, Text] = _.id.toString.takeRight(6).pipe(Text.literal).styled(_.withFont(Identifier("minecraft:illageralt"))) 2524 2043 2525 - extension (p: ServerPlayerEntity) def gimmeIota(iota: Iota): Unit = 2526 - val m = p.getComponent(HexCardinalComponents.STAFFCAST_IMAGE) 2527 - m.setImage(m.getVM(Hand.MAIN_HAND).getImage.withStack(_ ++ Vector(iota))) 2528 - 2529 2044 object elementTag: 2530 2045 def apply[T <: NbtElement](l: AbstractNbtList[T]): ClassTag[T] = 2531 2046 l match ··· 2568 2083 case y: T => Some((x, y)) 2569 2084 case _ => None 2570 2085 2571 - implicit class IterExt[T](i: IterableOps[T, ?, ?]): 2572 - export i.{exists => ∃, forall => ∀} 2573 - def findFirstOrLast(p: T => Boolean): Option[T] = 2574 - boundary: 2575 - (None /: i): 2576 - case (ctx, x) => 2577 - if p(x) then 2578 - boundary.break(Some(x)) 2579 - else 2580 - Some(x) 2581 - implicit class IterOnceExt[T](i: IterableOnceOps[T, ?, ?]): 2582 - export i.{exists => ∃, forall => ∀} 2583 - def findFirstOrLast(p: T => Boolean): Option[T] = 2584 - boundary: 2585 - (None /: i): 2586 - case (ctx, x) => 2587 - if p(x) then 2588 - boundary.break(Some(x)) 2589 - else 2590 - Some(x) 2591 - 2592 - //noinspection UnstableApiUsage 2593 - class NoDataVariant extends TransferVariant[NoDataVariant]: 2594 - def getNbt = NbtCompound() 2595 - def getObject: this.type = this 2596 - def isBlank = false 2597 - def toNbt = NbtCompound() 2598 - def toPacket(buf: net.minecraft.network.PacketByteBuf): Unit = () 2599 - object MediaVariant extends NoDataVariant 2600 - object HeatVariant extends NoDataVariant 2601 - object EnergyVariant extends NoDataVariant 2602 - 2603 2086 trait FromIota[T]: 2604 2087 def convert(iota: Iota): Option[T] 2605 2088 object FromIota: ··· 2685 2168 val l = NbtList() 2686 2169 data.forEach(l.add(_)) 2687 2170 l 2688 - 2689 - extension [T](l: util.AbstractList[T]) 2690 - def apply(n: Int): T = l.get(n) 2691 - def update(n: Int, x: T): Unit = l.set(n, x) 2692 - extension (c: NbtCompound) 2693 - def apply(k: String): NbtElement | Null = c.get(k) 2694 - def update(k: String, v: NbtElement | Null): Unit = c.put(k, v) 2695 2171 2696 2172 trait PigmentHolderItem: 2697 2173 this: Item => ··· 2710 2186 throw MishapInvalidIota.of(d, idx, "int") 2711 2187 v.round.intValue 2712 2188 2713 - extension (i: CastingImage) 2714 - def withStack(m: Seq[Iota] => Seq[Iota]): CastingImage = i.copy(util.ArrayList(m(i.getStack.asScala.toSeq).asJavaCollection), i.getParenCount, i.getParenthesized, i.getEscapeNext, i.getOpsConsumed, i.getUserData) 2715 - extension (o: OperationResult) 2716 - def withStack(m: Seq[Iota] => Seq[Iota]): OperationResult = o.copy(o.getNewImage.withStack(m), o.getSideEffects, o.getNewContinuation, o.getSound) 2717 - 2718 - inline def arith(name: String, inline ops: (HexPattern, AnyRef)*) = ${ arithImpl('name, 'ops) } 2719 - 2720 2189 trait Selector[-T, R]: 2721 2190 def apply(target: T): R 2722 2191 def update(target: T, value: R): Unit ··· 2759 2228 case iota: target.type => iota 2760 2229 case _ => throw IllegalStateException("Iota changed types or became null during serialization") 2761 2230 2762 - inline given DynamicOps[JsonElement] = JsonOps.COMPRESSED 2763 - extension [T: DynamicOps as t] (x: T) def convertDynamic[R: DynamicOps as r]: R = t.convertTo(r, x) 2764 - 2765 - given (vm: CastingVM) => CastingEnvironment = vm.getEnv 2766 - given envGetWorld: (env: CastingEnvironment) => ServerWorld = env.getWorld 2767 - 2768 2231 object border extends Block(AbstractBlock.Settings.create().dropsNothing().allowsSpawning((_, _, _, _) => false).sounds(BlockSoundGroup.STONE).requiresTool().strength(100.0F, 1200.0F).luminance(_ => 14)) 2769 2232 def getPocketID(key: Identifier): Option[UUID] = 2770 2233 if key.getNamespace == "hexic" && key.getPath.startsWith("fresh-") then ··· 2781 2244 (piece +: Iterator.continually(piece).takeWhile(_ => rand.nextInt(5) == 0).toSeq).mkString("-") 2782 2245 val pocketNames = memo((id: UUID) => pocketName(using Random(id.getLeastSignificantBits))) 2783 2246 2784 - abstract case class AbstractMetatableIota(iotaType: MetatableIotaType & Singleton, userdata: Iota, override val display: Text, metatable: String, readonlyMetatable: Boolean) extends Iota(iotaType, (userdata, display, metatable, readonlyMetatable)): 2785 - override def subIotas(): lang.Iterable[Iota] = util.List.of(userdata) 2786 - override def toleratesOther(that: Iota): Boolean = that match 2787 - case AbstractMetatableIota(_, u, _, m, _) => metatable == m && Iota.tolerates(userdata, u) 2788 - case _ => Iota.tolerates(userdata, that) 2789 - override def serialize(): NbtElement = NbtCompound().tap: c => 2790 - c.put("userdata", IotaType.serialize(userdata)) 2791 - c.put("display", Text.Serializer.toJsonTree(display).convertDynamic) 2792 - c.put("metatable", metatable) 2793 - c.putBoolean("ro", readonlyMetatable) 2794 - def meta(using world: ServerWorld): MapIota = 2795 - StateStorage.Companion.getProperty(world, metatable) match 2796 - case m: MapIota => m 2797 - case i => throw MishapBadMetatable(metatable, i, readonlyMetatable) 2798 - def meta_=(using world: ServerWorld)(x: MapIota): Unit = 2799 - StateStorage.Companion.setProperty(world, metatable, x) 2800 - infix def mro(key: HexPattern)(using world: ServerWorld): Option[Iota] = 2801 - meta.get(PatternIota(key)).orElse: 2802 - userdata match 2803 - case m: AbstractMetatableIota => m mro key 2804 - case _ => None 2805 - override def isTruthy: Boolean = true 2806 - override def executable: Boolean = true 2807 - override def execute(using vm: CastingVM, world: ServerWorld, continuation: SpellContinuation): CastResult = callMetamethod(se"deaqq")(vm.getImage, continuation) 2808 - override def size = userdata.size + 1 2809 - def callMetamethod(using env: CastingEnvironment)(key: HexPattern)(image: CastingImage, continuation: SpellContinuation): CastResult = 2810 - val callee = mro(key).getOrElse(PatternIota(key)) 2811 - val result = OpEval.INSTANCE.exec(env, image, continuation, image.getStack.init :+ userdata, callee) 2812 - CastResult(callee, result.getNewContinuation, result.getNewImage, result.getSideEffects, ResolvedPatternType.EVALUATED, result.getSound) 2813 - class MishapBadMetatable(name: String, value: Iota, readonly: Boolean) extends Mishap(): 2814 - override def errorMessage(env: CastingEnvironment, ctx: Context): Text = Text.translatable("hexic.bad_metatable", name, value.display) 2815 - override def accentColor(env: CastingEnvironment, ctx: Context): FrozenPigment = dyeColor(DyeColor.GRAY) 2816 - override def execute(env: CastingEnvironment, ctx: Context, stack: ju.List[Iota]): Unit = 2817 - stack(stack.length - 1) = GarbageIota() 2818 - if !readonly then StateStorage.Companion.setProperty(env.getWorld, name, GarbageIota()) 2819 - 2820 - private[hexic] object metatableHook: 2821 - extension (p: PatternIota) def executeHook(using Label[CastResult], ServerWorld)(using vm: CastingVM)(continuation: SpellContinuation): Unit = 2822 - vm.getStack.lastOption match 2823 - case Some(m: AbstractMetatableIota) => 2824 - for x <- m mro p.getPattern do 2825 - // this is probably cursed 2826 - val result = m.callMetamethod(p.getPattern)(vm.getImage.withStack(_.init), continuation) 2827 - boundary.break(result) 2828 - case _ => 2829 - 2830 - case class MetatableIotaType private[hexic](override val color: Int) extends IotaType[AbstractMetatableIota]: 2831 - class Instance(userdata: Iota, display: Text, metatable: String, readonlyMetatable: Boolean) extends AbstractMetatableIota(MetatableIotaType.this, userdata, display, metatable, readonlyMetatable) 2832 - override def deserialize(tag: NbtElement, world: ServerWorld): Instance = 2833 - val c = tag.downcast[NbtCompound] 2834 - Instance( 2835 - userdata = IotaType.deserialize(c.get("userdata").downcast[NbtCompound], world), 2836 - display = Text.Serializer.fromJson(c.get("display").convertDynamic: JsonElement), 2837 - metatable = c.get("metatable").downcast[NbtString].asString, 2838 - readonlyMetatable = c.getBoolean("ro"), 2839 - ) 2840 - override def display(tag: NbtElement): Text = Text.Serializer.fromJson(tag.downcast[NbtCompound].get("display").convertDynamic: JsonElement) 2841 - 2842 - object MetatableIotaType: 2843 - val validValues = Seq(0x0, 0x3, 0x6, 0x9, 0xC, 0xF) 2844 - val colors: (Int, Int, Int) :> MetatableIotaType = (for r <- validValues; g <- validValues; b <- validValues yield (r, g, b) -> MetatableIotaType((r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b)).toMap 2845 - println(s"Metatables: $colors") 2846 - 2847 2247 object TripwireIota extends Iota(new IotaType[TripwireIota.type]: 2848 2248 override def deserialize(tag: NbtElement, world: ServerWorld): TripwireIota.type = TripwireIota 2849 2249 override def color: Int = 0xba4216 ··· 2910 2310 @targetName("hexic$MediaContainerProvider$getMediaContainer") 2911 2311 def getMediaContainer(c: Context): Option[MediaContainer] 2912 2312 2913 - given Codec[Text] = Codecs.TEXT 2914 - given DynamicOps[NbtElement] = NbtOps.INSTANCE 2915 - 2916 - given IotaType[DoubleIota] = DoubleIota.TYPE 2917 - given IotaType[StringIota] = StringIota.TYPE 2918 - given IotaType[LocationIota] = LocationIota 2919 - given IotaType[NbtIota] = NbtIota 2920 - 2921 - given (vm: CastingVM) => CastingImage = vm.getImage 2922 - given Conversion[CastingVM, CastingImage] = _.getImage 2923 - given Conversion[CastingVM, CastingEnvironment] = _.getEnv 2924 - 2925 - given Conversion[String, NbtString] = NbtString.of 2926 - given Conversion[NbtString, String] = _.asString 2927 - 2928 - extension (e: NbtElement) 2929 - def downcast[T <: NbtElement: NbtType] = HexUtils.downcast(e, summon[NbtType[T]]) 2930 - 2931 - given NbtType[NbtString] = NbtString.TYPE 2932 - given NbtType[NbtByte] = NbtByte.TYPE 2933 - given NbtType[NbtShort] = NbtShort.TYPE 2934 - given NbtType[NbtInt] = NbtInt.TYPE 2935 - given NbtType[NbtLong] = NbtLong.TYPE 2936 - given NbtType[NbtFloat] = NbtFloat.TYPE 2937 - given NbtType[NbtDouble] = NbtDouble.TYPE 2938 - given NbtType[NbtByteArray] = NbtByteArray.TYPE 2939 - given NbtType[NbtIntArray] = NbtIntArray.TYPE 2940 - given NbtType[NbtLongArray] = NbtLongArray.TYPE 2941 - given NbtType[NbtList] = NbtList.TYPE 2942 - given NbtType[NbtCompound] = NbtCompound.TYPE 2943 - given NbtType[NbtEnd] = NbtEnd.TYPE 2944 - 2945 2313 case class NbtIota(data: NbtElement) extends Iota(NbtIota, data): 2946 2314 override def isTruthy: Boolean = data match 2947 2315 case d: AbstractNbtNumber => d.numberValue != 0 ··· 2953 2321 case that: NbtIota => this.data == that.data 2954 2322 case _ => this.data == that 2955 2323 override def serialize: NbtElement = data 2956 - //noinspection UnstableApiUsage 2957 - case class VariantIota[T: ClassTag](data: TransferVariant[T], key: RegistryKey[VariantIota.Reader]) extends Iota(VariantIota, data): 2958 - override def isTruthy: Boolean = true 2959 - override def toleratesOther(that: Iota): Boolean = 2960 - that match 2961 - case v: VariantIota[T] => key == v.key && data == v.data 2962 - case _ => false 2963 - override def serialize: NbtElement = 2964 - data.toNbt tap(_.putString("type", key.getValue.toString)) 2965 - //noinspection UnstableApiUsage 2966 - object VariantIota extends IotaType[VariantIota[?]], Registrar[VariantIota.Reader]("transfer_variants"): 2967 - given IotaType[VariantIota[?]] = this 2968 - type Reader = NbtCompound => Option[VariantIota.TaggedVariant] 2969 - trait TaggedVariant: 2970 - type T: ClassTag 2971 - def variant: TransferVariant[T] 2972 - def display: Text 2973 - def color: Int = 0x720a0a 2974 - private[hexic] def parseVariant(c: NbtCompound): Option[(TaggedVariant, RegistryKey[Reader])] = 2975 - for 2976 - i <- Option(Identifier.tryParse(c.getString("type"))) 2977 - entry <- Option.fromNullable(registry.get(i)) 2978 - parsed <- entry(c) 2979 - yield (parsed, RegistryKey.of(VariantIota, i)) 2980 - end parseVariant 2981 - def deserialize(using NbtElement, ServerWorld): VariantIota[?] | Null = 2982 - summon[NbtElement] match 2983 - case c: NbtCompound => 2984 - parseVariant(c) match 2985 - case Some((t, k)) => 2986 - import t.given 2987 - VariantIota(t.variant, k) 2988 - case None => null 2989 - case _ => null 2990 - end deserialize 2991 - override def display(e: NbtElement): Text = parseVariant(e.downcast).fold(NullIota.DISPLAY)(_._1.display) 2992 - end display 2993 - registry(Identifier("item")) = c => 2994 - val s = ItemVariant.fromNbt(c) 2995 - Option.unless(s.isBlank): 2996 - new TaggedVariant: 2997 - type T = Item 2998 - def variant: TransferVariant[Item] = s 2999 - def display: Text = t"${s.getItem.getName(s.toStack)}: ${ItemInlineData(s.toStack).asText(true)}" 3000 - .styled(_.withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_ITEM, HoverEvent.ItemStackContent(s.toStack)))) 3001 - registry(Identifier("fluid")) = c => 3002 - val s = FluidVariant.fromNbt(c) 3003 - Option.unless(s.isBlank): 3004 - new TaggedVariant: 3005 - type T = Fluid 3006 - def variant: TransferVariant[Fluid] = s 3007 - def display: MutableText = t"${s.getFluid.getDefaultState.getBlockState.getBlock.getName}: ${ItemInlineData.make(s.getFluid.getBucketItem.getDefaultStack)}" 3008 - def singleton(x: NoDataVariant, disp: Text): NbtCompound => Some[TaggedVariant] = 3009 - val ret = Some(new TaggedVariant: 3010 - type T = NoDataVariant 3011 - def variant: x.type = x 3012 - def display: Text = disp) 3013 - _ => ret 3014 - registry("media") = singleton(MediaVariant, Text.literal("Media").styled(_.withColor(0x74b3f2))) 3015 - registry("heat") = singleton(HeatVariant, Text.literal("Heat").styled(_.withColor(0xe08355))) 3016 2324 3017 2325 object registerHopperEndpoint extends (() => Unit): 3018 2326 def apply(): Unit = ··· 3040 2348 3041 2349 extension [A, B] (p: (A, B)) 3042 2350 infix def both[R, S](f: (A => R) & (B => S)): (R, S) = (f(p._1), f(p._2)) 3043 - case class MapIota(map: Map[NbtCompound, NbtCompound] = Map(), trusted: Boolean = false)(using val world: ServerWorld) extends Iota(MapIota, map): 3044 - def get(key: Iota): Option[Iota] = map.get(IotaType.serialize(key)).map(IotaType.deserialize(_, summon)) 3045 - def apply(key: Iota): Iota = get(key) getOrElse NullIota() 3046 - def -(keys: Iota*): MapIota = MapIota(map -- (keys map IotaType.serialize)) 3047 - def --(other: MapIota): MapIota = MapIota(map -- other.map.keys) 3048 - def +(pairs: (Iota, Iota)*): MapIota = MapIota(map ++ pairs.map(_ both IotaType.serialize)) 3049 - def ++(other: MapIota): MapIota = MapIota(map ++ other.map) 3050 - def update(f: map.type => Map[NbtCompound, NbtCompound]): MapIota = MapIota(map pipe f) 3051 - def head: (Iota, Iota) = map.head both(IotaType.deserialize(_, summon)) 3052 - def tail: MapIota = MapIota(map.tail) 3053 - def init: MapIota = MapIota(map.init) 3054 - def last: (Iota, Iota) = map.last both(IotaType.deserialize(_, summon)) 3055 - def headOption: Option[(Iota, Iota)] = map.headOption map(_.both(IotaType.deserialize(_, summon))) 3056 - def lastOption: Option[(Iota, Iota)] = map.lastOption map(_.both(IotaType.deserialize(_, summon))) 3057 - def &(other: MapIota): MapIota = MapIota(map.filter(_._1 pipe other.map.contains)) 3058 - def ^(other: MapIota): MapIota = mutable.Map[NbtCompound, NbtCompound]().tap(m => 3059 - m.addAll(map) 3060 - for ((k, v) <- other.map) do 3061 - if m contains k then 3062 - m -= k 3063 - else 3064 - m += (k -> v) 3065 - ) pipe (_.toMap) pipe (new MapIota(_)) 3066 - def toList: util.List[Iota] = map.flatMap((k, v) => Seq(k: Iota, v: Iota)).toSeq 3067 - override def isTruthy: Boolean = map.nonEmpty 3068 - override def toleratesOther(iota: Iota): Boolean = iota match 3069 - case m: MapIota => map == m.map 3070 - case _ => false 3071 - override def serialize(): NbtElement = NbtList().tap: l => 3072 - map.toVector.foreach(p => NbtCompound().tap(c => 3073 - c.put("k", p._1) 3074 - c.put("v", p._2)) tap l.add) 3075 - override def size = map.toSeq.map(_.size + _.size - 1).sum + 1 3076 - override def subIotas(): lang.Iterable[Iota] = 3077 - if trusted then 3078 - // skip deserialization for performance 3079 - // this is safe because `trusted` can only be true if truenames have 3080 - // already been checked, and we override `size` 3081 - Seq() 3082 - else 3083 - toList 3084 - object MapIota extends IotaType[MapIota]: 3085 - def color: Int = 0xb0641c 3086 - def deserialize(using data: NbtElement, world: ServerWorld): MapIota = 3087 - val l = HexUtils.downcast(data, NbtList.TYPE) 3088 - def o = HexUtils.downcast(_, NbtCompound.TYPE) 3089 - l.map(o) 3090 - .map(c => ((c("k") pipe o) -> (c("v") pipe o))) 3091 - .toMap[NbtCompound, NbtCompound] 3092 - .pipe(new MapIota(_, trusted = true)) 3093 - def display(data: NbtElement): Text = 3094 - val items = HexUtils.downcast(data, NbtList.TYPE) 3095 - val output: MutableText = "[" 3096 - output.styled(_.withColor(color)) 3097 - if items.nonEmpty then 3098 - def castToCompound = HexUtils.downcast(_, NbtCompound.TYPE) 3099 - val itemPair = items.map(castToCompound).iterator 3100 - def writePair(pair: NbtCompound) = 3101 - output.append(try IotaType.getDisplay(pair("k") pipe castToCompound) catch case e => t"∞" formatted Formatting.RED) 3102 - output.append(" → ") 3103 - output.append(try IotaType.getDisplay(pair("v") pipe castToCompound) catch case e => t"∞" formatted Formatting.RED) 3104 - writePair(itemPair.next()) 3105 - while itemPair.hasNext do 3106 - output.append(", ") 3107 - writePair(itemPair.next()) 3108 - else 3109 - output.append("→") 3110 - output.append("]") 3111 - output 3112 2351 trait IotaCoercion[T]: 3113 2352 typ: IotaType[I] => 3114 2353 // need _root_ path, since `typ` could theoretically have these as members ··· 3117 2356 def downcast[R: ClassTag](t: Any): Option[R] = t match 3118 2357 case r: R => Some(r) 3119 2358 case _ => None 3120 - given Conversion[String, MutableText] = Text.literal 3121 2359 object NbtIota extends IotaType[NbtIota]: 3122 2360 def name: Text = ("NBT": MutableText).styled(_.withColor(color)) 3123 2361 def color: Int = Formatting.DARK_AQUA.getColorValue
+3 -7
src/main/scala/org/eu/net/pool/hexic/Util.scala util/src/main/scala/org/eu/net/pool/phlib/util.scala
··· 1 1 //noinspection NotImplementedCode 2 - package org.eu.net.pool.hexic 2 + package org.eu.net.pool 3 + package phlib 3 4 4 5 import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic 5 6 import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator ··· 13 14 import at.petrak.hexcasting.api.pigment.FrozenPigment 14 15 import at.petrak.hexcasting.common.lib.HexItems 15 16 import at.petrak.hexcasting.common.lib.hex.HexEvalSounds 16 - import com.chocohead.mm.api.EnumAdder 17 17 import com.mojang.serialization.{Codec, DynamicOps} 18 18 import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder 19 19 import net.minecraft.entity.LivingEntity ··· 33 33 import scala.util.chaining.given 34 34 35 35 given [T]: Conversion[RegistryKey[Registry[T]], ? <: Registry[T]] = Registries.REGISTRIES.asInstanceOf[Registry[Registry[T]]].get(_) 36 - given Conversion[String, Identifier] = Identifier.of("hexic", _) 37 36 38 37 trait Registrar[T](val id: Identifier): 39 38 val key: RegistryKey[Registry[T]] = RegistryKey.ofRegistry[T](id) ··· 50 49 51 50 def unless(cond: Boolean)(body: => Unit): Unit = if (!cond) body 52 51 53 - def generateAppropriateColors_impl(e: Expr[EnumAdder])(using q: Quotes): Expr[EnumAdder] = 54 - // FIXME: this is wrong 55 - DyeColor.values.foldLeft(e)((e, c) => '{ ${ e }.addEnum(${ Expr(s"HEXIC$$PEN_WITH_COLOR_${c.asString}") }, (${Expr(c.getMapColor.color)}: Int): Integer, (${Expr(c.getSignColor)}: Int): Integer, java.lang.Boolean.TRUE) }) 56 - 57 52 extension (n: Int) 58 53 def times(b: => Unit) = 59 54 for (i <- 1 to n) { ··· 69 64 case t => Seq(t) 70 65 71 66 // My level of sanity is slightly lower than the Hex Caster's sanity. 67 + inline def arith(name: String, inline ops: (HexPattern, AnyRef)*) = ${ arithImpl('name, 'ops) } 72 68 def arithImpl(using q: Quotes)(name: Expr[String], args: Expr[Seq[(HexPattern, AnyRef)]]): Expr[Arithmetic] = 73 69 import q.reflect.* 74 70 val Varargs(xs) = args: @unchecked
+11
src/main/scala/org/eu/net/pool/hexic/early_riser_macros.scala
··· 1 + package org.eu.net.pool 2 + package hexic 3 + 4 + import scala.quoted.Expr 5 + import com.chocohead.mm.api.EnumAdder 6 + import scala.quoted.Quotes 7 + import net.minecraft.util.DyeColor 8 + 9 + def generateAppropriateColors_impl(e: Expr[EnumAdder])(using q: Quotes): Expr[EnumAdder] = 10 + // FIXME: this is wrong 11 + DyeColor.values.foldLeft(e)((e, c) => '{ ${ e }.addEnum(${ Expr(s"HEXIC$$PEN_WITH_COLOR_${c.asString}") }, (${Expr(c.getMapColor.color)}: Int): Integer, (${Expr(c.getSignColor)}: Int): Integer, java.lang.Boolean.TRUE) })
+2 -2
src/main/scala/org/eu/net/pool/hexic/mixin/AbstractFurnaceBlockEntityMixin.java project/hexxychests/src/main/scala/org/eu/net/pool/hexxychests/mixin/AbstractFurnaceBlockEntityMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin; 1 + package org.eu.net.pool.hexxychests.mixin; 2 2 3 3 import net.minecraft.block.entity.AbstractFurnaceBlockEntity; 4 - import org.eu.net.pool.hexic.AbstractFurnaceBlockEntityAccess; 4 + import org.eu.net.pool.hexxychests.AbstractFurnaceBlockEntityAccess; 5 5 import org.spongepowered.asm.mixin.Mixin; 6 6 import org.spongepowered.asm.mixin.gen.Accessor; 7 7
+1 -1
src/main/scala/org/eu/net/pool/hexic/mixin/BiomeMixin.java
··· 11 11 12 12 @Mixin(Biome.class) 13 13 public class BiomeMixin { 14 - @Inject(at = @At("HEAD"), method = "getTemperature*", cancellable = true) 14 + @Inject(at = @At("HEAD"), method = {"getTemperature()F", "getTemperature(Lnet/minecraft/util/math/BlockPos;)F"}, cancellable = true) 15 15 void preGetTemperature(CallbackInfoReturnable<Float> ci) { 16 16 World world = Extern.getWorld((Biome) (Object) this); 17 17 if (world.getLevelProperties().getComponent(ServerInfoComponent.key()).endSnowTick() > world.getTime()) ci.setReturnValue(0.1f);
+24
src/main/scala/org/eu/net/pool/hexic/mixin/CastingEnvironmentMixin.java
··· 1 + package org.eu.net.pool.hexic.mixin; 2 + 3 + import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; 4 + import net.minecraft.entity.Entity; 5 + import net.minecraft.server.world.ServerWorld; 6 + import net.minecraft.util.math.Vec3d; 7 + import org.spongepowered.asm.mixin.Mixin; 8 + import org.spongepowered.asm.mixin.Shadow; 9 + import org.spongepowered.asm.mixin.injection.At; 10 + import org.spongepowered.asm.mixin.injection.Inject; 11 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 + 13 + @Mixin(CastingEnvironment.class) 14 + public abstract class CastingEnvironmentMixin { 15 + @Shadow public abstract ServerWorld getWorld(); 16 + 17 + @Inject(at = @At("HEAD"), method = "isVecInWorld", cancellable = true) 18 + void modifyVecInWorld(Vec3d vec, CallbackInfoReturnable<Boolean> cir) { 19 + var id = getWorld().getRegistryKey().getValue(); 20 + if (id.getNamespace().equals("hexic") && id.getPath().startsWith("fresh-")) { 21 + cir.setReturnValue(vec.x >= 1 && vec.y >= 1 && vec.z >= 1 && vec.x < 10 && vec.y < 11 && vec.z < 11); 22 + } 23 + } 24 + }
+8 -5
src/main/scala/org/eu/net/pool/hexic/mixin/PatternIotaMixin.java util/src/main/scala/org/eu/net/pool/phlib/mixin/PatternIotaMixin.java
··· 1 - package org.eu.net.pool.hexic.mixin; 1 + package org.eu.net.pool.phlib.mixin; 2 2 3 3 import at.petrak.hexcasting.api.casting.eval.CastResult; 4 4 import at.petrak.hexcasting.api.casting.eval.vm.CastingVM; 5 5 import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation; 6 6 import at.petrak.hexcasting.api.casting.iota.PatternIota; 7 7 import net.minecraft.server.world.ServerWorld; 8 - import org.eu.net.pool.hexic.Interop; 9 - import org.eu.net.pool.hexic.metatableHook; 8 + import scala.Tuple4; 9 + 10 + import org.eu.net.pool.phlib.Events; 10 11 import org.spongepowered.asm.mixin.Mixin; 11 12 import org.spongepowered.asm.mixin.injection.At; 12 13 import org.spongepowered.asm.mixin.injection.Inject; ··· 16 17 public class PatternIotaMixin { 17 18 @Inject(at = @At(value = "INVOKE", ordinal = 0, target = "Lat/petrak/hexcasting/api/casting/iota/PatternIota;getPattern()Lat/petrak/hexcasting/api/casting/math/HexPattern;", shift = At.Shift.BEFORE), method = "execute", cancellable = true) 18 19 void execute(CastingVM vm, ServerWorld world, SpellContinuation continuation, CallbackInfoReturnable<CastResult> cir) { 19 - // ignore IDE error here; it works at compile-time 20 - Interop.callScalaReturnable(cir, l -> metatableHook.executeHook((PatternIota) (Object) this, l, world, vm, continuation)); 20 + // OLD: Interop.callScalaReturnable(cir, l -> metatableHook.executeHook((PatternIota) (Object) this, l, world, vm, continuation)); 21 + if (Events.beforePatternExecute().invoker().unapply(new Tuple4<>((PatternIota) (Object) this, vm, world, continuation)) instanceof scala.Some<CastResult> result) { 22 + cir.setReturnValue(result.value()); 23 + } 21 24 } 22 25 }
src/main/scala/scala/hexicVisibilityHack/hack.scala project/hexxychests/src/main/scala/scala/hexicVisibilityHack/hack.scala
+53
util/build.gradle.kts
··· 1 + plugins { 2 + id("fabric-loom") version "1.13-SNAPSHOT" 3 + id("scala") 4 + kotlin("jvm") version "2.2.0" 5 + id("maven-publish") 6 + id("de.undercouch.download") version "5.6.0" 7 + id("org.eu.net.pool.mc-plugin") version "0.1.1" 8 + } 9 + 10 + dependencies { 11 + minecraft("com.mojang:minecraft:${project.property("minecraft_version")}") 12 + mappings("net.fabricmc:yarn:${project.property("yarn_mappings")}:v2") 13 + modImplementation("net.fabricmc:fabric-loader:${project.property("loader_version")}") 14 + modImplementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") 15 + api("org.scala-lang:scala3-library_3:3.7.1") 16 + api("org.scala-lang:scala-library:2.13.6") 17 + modApi("poollovernathan.fabric:mod-tools:1.1.5+1.20.1") 18 + modApi("vazkii.patchouli:Patchouli:$minecraft_version-84-FABRIC") 19 + modImplementation("at.petra-k.hexcasting:hexcasting-fabric-$minecraft_version:0.11.3") 20 + modImplementation("at.petra-k.paucal:paucal-fabric-$minecraft_version:0.6.0-pre-118") 21 + modImplementation("com.samsthenerd.inline:inline-fabric:$minecraft_version-1.0.1") 22 + modApi("io.github.tropheusj:serialization-hooks:0.4.99999") 23 + val cardinal_version = "5.2.3" 24 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-base:$cardinal_version") 25 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-block:$cardinal_version") 26 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-entity:$cardinal_version") 27 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-item:$cardinal_version") 28 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-level:$cardinal_version") 29 + modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-world:$cardinal_version") 30 + modRuntimeOnly("dev.onyxstudios.cardinal-components-api:cardinal-components-api:$cardinal_version") 31 + } 32 + 33 + tasks.processResources { 34 + preprocessor { 35 + fabricMod("phlib", version as String) { 36 + name = "PoolHexLib" 37 + description = "Internal library for my Hex Casting addons." 38 + license = "LGPL-3.0" 39 + icon = "assets/phlib/icon.png" 40 + 41 + author("pool") { 42 + put("discord", "https://discord.com/users/758407438251720795") 43 + } 44 + 45 + depends("mod-tools", "^1.1.5+1.20.1") 46 + depends("hexcasting", ">=0.11.2") 47 + breaks("hexic", "<2.0.0") 48 + 49 + entrypoint("org.eu.net.pool.phlib.main\$package::init") 50 + mixins("phlib.mixins.json") 51 + } 52 + } 53 + }
+9
util/gradle.properties
··· 1 + minecraft_version=1.20.1 2 + yarn_mappings=1.20.1+build.10 3 + loader_version=0.16.14 4 + scala_loader_version=0.3.1.11 5 + mod_version=0.1.1 6 + maven_group=org.eu.net.pool 7 + modid=phlib 8 + archives_base_name=phlib 9 + fabric_version=0.92.6+1.20.1
+3
util/src/main/resources/assets/phlib/lang/en_us.json
··· 1 + { 2 + "hexcasting.action.phlib:empty_map": "Vacant Reflection: Map" 3 + }
+3
util/src/main/resources/assets/phlib/lang/zh_cn.json
··· 1 + { 2 + "hexcasting.action.phlib:empty_map": "空无之精思:映射" 3 + }
+10
util/src/main/resources/phlib.mixins.json
··· 1 + { 2 + "required": true, 3 + "minVersion": "0.8", 4 + "package": "org.eu.net.pool.phlib.mixin", 5 + "compatibilityLevel": "JAVA_17", 6 + "mixins": [ 7 + "PatternIotaMixin", 8 + "SimpleRegistryMixin" 9 + ] 10 + }
+295
util/src/main/scala/org/eu/net/pool/phlib/main.scala
··· 1 + package org.eu.net.pool 2 + package phlib 3 + 4 + import at.petrak.hexcasting.api.utils.HexUtils 5 + import com.google.gson.JsonElement 6 + import com.mojang.serialization.{Codec, DynamicOps, JsonOps} 7 + import net.minecraft.nbt.{NbtByte, NbtByteArray, NbtDouble, NbtEnd, NbtFloat, NbtInt, NbtIntArray, NbtList, NbtLong, NbtLongArray, NbtOps, NbtShort, NbtString, NbtType} 8 + import net.minecraft.util.dynamic.Codecs 9 + import at.petrak.hexcasting.api.addldata.ADMediaHolder 10 + import scala.collection.{IterableOnceOps, IterableOps} 11 + import at.petrak.hexcasting.api.casting.{ActionRegistryEntry, ParticleSpray, RenderedSpell, SpellList} 12 + import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic 13 + import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator 14 + import at.petrak.hexcasting.api.casting.castables.{Action, ConstMediaAction, OperationAction, SpecialHandler, SpellAction} 15 + import at.petrak.hexcasting.api.casting.eval.env.PlayerBasedCastEnv 16 + import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect.DoMishap 17 + import at.petrak.hexcasting.api.casting.eval.sideeffects.{EvalSound, OperatorSideEffect} 18 + import at.petrak.hexcasting.api.casting.eval.vm.{CastingImage, CastingVM, ContinuationFrame, FrameEvaluate, SpellContinuation} 19 + import at.petrak.hexcasting.api.casting.eval.{CastResult, CastingEnvironment, CastingEnvironmentComponent, MishapEnvironment, OperationResult, ResolvedPattern, ResolvedPatternType} 20 + import at.petrak.hexcasting.api.casting.iota.* 21 + import at.petrak.hexcasting.api.casting.math.{HexDir, HexPattern} 22 + import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapBadCaster, MishapBadOffhandItem, MishapInvalidIota, MishapInvalidOperatorArgs, MishapNotEnoughArgs, MishapOthersName, MishapTooManyCloseParens} 23 + import at.petrak.hexcasting.common.lib.HexRegistries 24 + import at.petrak.hexcasting.fabric.cc.HexCardinalComponents 25 + import com.mojang.brigadier.builder.{LiteralArgumentBuilder, RequiredArgumentBuilder} 26 + import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback 27 + import net.minecraft.command.{CommandException, EntitySelector} 28 + import net.minecraft.command.argument.{EntityArgumentType, NbtElementArgumentType, RegistryEntryArgumentType} 29 + import net.minecraft.nbt.{NbtCompound, NbtElement} 30 + import net.minecraft.server.command.ServerCommandSource 31 + import net.minecraft.server.network.ServerPlayerEntity 32 + import net.minecraft.server.world.ServerWorld 33 + import net.minecraft.util.Hand 34 + 35 + import scala.annotation.tailrec 36 + import scala.math.Ordered.orderingToOrdered 37 + import scala.reflect.ClassTag 38 + import scala.util.{Failure, Success, Try} 39 + export scala.collection.convert.ImplicitConversions.* 40 + export scala.util.chaining._ 41 + import java.util 42 + import net.fabricmc.loader.api.FabricLoader 43 + import net.minecraft.util.Identifier 44 + import scala.util.boundary 45 + import org.slf4j.LoggerFactory 46 + import org.slf4j.Logger 47 + import at.petrak.hexcasting.xplat.IXplatAbstractions 48 + import net.minecraft.registry.Registry 49 + import net.minecraft.registry.RegistryKey 50 + import net.minecraft.text.MutableText 51 + import net.minecraft.text.Text 52 + import net.fabricmc.fabric.api.event.Event 53 + import net.fabricmc.fabric.api.event.EventFactory 54 + 55 + val fabric = FabricLoader.getInstance 56 + val isDev = fabric.isDevelopmentEnvironment 57 + lazy val iotaTypeRegistry = hexXplat.getIotaTypeRegistry 58 + lazy val actionRegistry = hexXplat.getActionRegistry 59 + 60 + private[phlib] given logger: Logger = LoggerFactory.getLogger("phlib") 61 + private[phlib] given Conversion[String, Identifier] = Identifier.of("phlib", _) 62 + 63 + implicit class RegistryOps[T](r: Registry[T]) extends AnyRef: 64 + def apply(key: Identifier | RegistryKey[?] | Int) = 65 + key match 66 + case i: Identifier => r.get(i) 67 + case i: Int => r.get(i) 68 + case k: RegistryKey[?] => r.get(k.asInstanceOf[RegistryKey[T]]) 69 + def update(key: Identifier | RegistryKey[?], value: T) = 70 + key match 71 + case i: Identifier => Registry.register(r, i, value) 72 + case k: RegistryKey[?] => Registry.register(r, k.asInstanceOf[RegistryKey[T]], value) 73 + 74 + extension [T](l: util.AbstractList[T]) 75 + def apply(n: Int): T = l.get(n) 76 + def update(n: Int, x: T): Unit = l.set(n, x) 77 + extension (c: NbtCompound) 78 + def apply(k: String): NbtElement | Null = c.get(k) 79 + def update(k: String, v: NbtElement | Null): Unit = c.put(k, v) 80 + 81 + inline given DynamicOps[JsonElement] = JsonOps.COMPRESSED 82 + extension [T: DynamicOps as t] (x: T) def convertDynamic[R: DynamicOps as r]: R = t.convertTo(r, x) 83 + 84 + given (vm: CastingVM) => CastingEnvironment = vm.getEnv 85 + given envGetWorld: (env: CastingEnvironment) => ServerWorld = env.getWorld 86 + 87 + given Conversion[String, MutableText] = Text.literal 88 + 89 + given Codec[Text] = Codecs.TEXT 90 + given DynamicOps[NbtElement] = NbtOps.INSTANCE 91 + 92 + given IotaType[DoubleIota] = DoubleIota.TYPE 93 + 94 + given (vm: CastingVM) => CastingImage = vm.getImage 95 + given Conversion[CastingVM, CastingImage] = _.getImage 96 + given Conversion[CastingVM, CastingEnvironment] = _.getEnv 97 + given Conversion[String, NbtString] = NbtString.of 98 + given Conversion[NbtString, String] = _.asString 99 + 100 + extension (e: NbtElement) 101 + def downcast[T <: NbtElement: NbtType] = HexUtils.downcast(e, summon[NbtType[T]]) 102 + 103 + given NbtType[NbtString] = NbtString.TYPE 104 + given NbtType[NbtByte] = NbtByte.TYPE 105 + given NbtType[NbtShort] = NbtShort.TYPE 106 + given NbtType[NbtInt] = NbtInt.TYPE 107 + given NbtType[NbtLong] = NbtLong.TYPE 108 + given NbtType[NbtFloat] = NbtFloat.TYPE 109 + given NbtType[NbtDouble] = NbtDouble.TYPE 110 + given NbtType[NbtByteArray] = NbtByteArray.TYPE 111 + given NbtType[NbtIntArray] = NbtIntArray.TYPE 112 + given NbtType[NbtLongArray] = NbtLongArray.TYPE 113 + given NbtType[NbtList] = NbtList.TYPE 114 + given NbtType[NbtCompound] = NbtCompound.TYPE 115 + given NbtType[NbtEnd] = NbtEnd.TYPE 116 + 117 + @tailrec 118 + def finishOperation(p: OperationResult)(using env: CastingEnvironment): OperationResult = 119 + p.getNewContinuation match 120 + case c: SpellContinuation.Done => p 121 + case c: SpellContinuation.NotDone => 122 + finishCast(c.getFrame.evaluate(c.getNext, env.getWorld, CastingVM(p.getNewImage, env)), p.getNewImage) 123 + 124 + inline def finishCast(p: CastResult, oldImage: CastingImage)(using env: CastingEnvironment): OperationResult = 125 + finishOperation(OperationResult(p.getNewData??oldImage, p.getSideEffects, p.getContinuation, p.getSound)) 126 + 127 + extension [T] (x: T | Null) 128 + inline def ?[R](f: T => R): R | Null = x match 129 + case null => null 130 + case x: T => f(x) 131 + inline def ??(y: T): T = x match 132 + case null => y 133 + case x: T => x 134 + 135 + extension (i: CastingImage) 136 + def withStack(m: Seq[Iota] => Seq[Iota]): CastingImage = i.copy(util.ArrayList(m(i.getStack.toSeq)), i.getParenCount, i.getParenthesized, i.getEscapeNext, i.getOpsConsumed, i.getUserData) 137 + extension (o: OperationResult) 138 + def withStack(m: Seq[Iota] => Seq[Iota]): OperationResult = o.copy(o.getNewImage.withStack(m), o.getSideEffects, o.getNewContinuation, o.getSound) 139 + 140 + extension (ctx: StringContext) 141 + def ne(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.NORTH_EAST) 142 + def e(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.EAST) 143 + def se(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.SOUTH_EAST) 144 + def nw(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.NORTH_WEST) 145 + def w(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.WEST) 146 + def sw(args: String*): HexPattern = HexPattern.fromAngles(ctx.s(args*), HexDir.SOUTH_WEST) 147 + 148 + extension [T, R] (f: T => R) def ∘ [U](g: U => T) = (x: U) => f(g(x)) 149 + def wrapReturn[T](body: (T => Nothing) => T): T = body(return _) 150 + def wrapThrow[T, E <: Throwable](body: (E => Nothing) => T): T = wrapReturn[Try[T]](r => Success(body(r∘Failure))).get 151 + 152 + def propagateMishaps[T](env: CastingEnvironment)(body: => T): T = 153 + wrapThrow[T, Mishap]: doThrow => 154 + object key extends CastingEnvironmentComponent.Key[?] 155 + env.addExtension: 156 + new CastingEnvironmentComponent with CastingEnvironmentComponent.PostExecution: 157 + override def getKey: CastingEnvironmentComponent.Key[?] = key 158 + 159 + override def onPostExecution(result: CastResult): Unit = 160 + result.getSideEffects.collectFirst: 161 + case m: OperatorSideEffect.DoMishap => 162 + if isDev then println(s"Propagating mishap: $m") 163 + doThrow(m.getMishap) 164 + try body finally env.removeExtension(key) 165 + 166 + def clamp[@specialized T: Ordering](x: T)(min: T, max: T): T = 167 + assume(max > min) 168 + if x < min then min 169 + else if x > max then max 170 + else x 171 + 172 + object isIota: 173 + inline def unapply[T <: Iota : IotaType as ty : ClassTag, I <: Int & Singleton](iota: Iota): Some[T] = 174 + iota match 175 + case iota: T => Some(iota) 176 + case _ => throw MishapInvalidIota(iota, compiletime.constValue[I], ty.typeName) 177 + 178 + given IotaType[Vec3Iota] = Vec3Iota.TYPE 179 + 180 + object Patterns: 181 + def mkAction(body: (CastingEnvironment, ServerWorld) ?=> (CastingImage, SpellContinuation) => (OperationResult | CastResult | (CastingImage, SpellContinuation, EvalSound, Seq[OperatorSideEffect]))): Action = 182 + (env: CastingEnvironment, image: CastingImage, cont: SpellContinuation) => 183 + try 184 + body(using env, env.getWorld)(image, cont) match 185 + case res: OperationResult => res 186 + case res: CastResult => OperationResult(res.getNewData, res.getSideEffects, res.getContinuation, res.getSound) 187 + case (img, cont, sound, effects) => OperationResult(img, effects, cont, sound) 188 + catch 189 + case _: NotImplementedError => throw MishapTodo() 190 + case e: MatchError => 191 + e.printStackTrace() 192 + throw MishapInvalidIota(image.getStack.lastOption.getOrElse(throw MishapNotEnoughArgs(1, 0).tap(_.initCause(e))), 0, "unknown").tap(_.initCause(e)) 193 + def mkConstAction(argc: Int, mediaCost: Long = 0)(body: (CastingEnvironment, ServerWorld) ?=> Seq[Iota] => Seq[Iota]): Action = 194 + new ConstMediaAction: 195 + import ConstMediaAction.DefaultImpls as d 196 + override def getArgc: Int = argc 197 + override def getMediaCost: Long = mediaCost 198 + override def execute(list: util.List[? <: Iota], castingEnvironment: CastingEnvironment): util.List[Iota] = 199 + body(using castingEnvironment, castingEnvironment.getWorld)(list.toSeq) 200 + override def executeWithOpCount(list: util.List[? <: Iota], castingEnvironment: CastingEnvironment): ConstMediaAction.CostMediaActionResult = d.executeWithOpCount(this, list, castingEnvironment) 201 + override def operate(castingEnvironment: CastingEnvironment, castingImage: CastingImage, spellContinuation: SpellContinuation): OperationResult = d.operate(this, castingEnvironment, castingImage, spellContinuation) 202 + def mkLiteral(value: (CastingEnvironment, ServerWorld) ?=> Iota): Action = 203 + mkConstAction(0): (args: Seq[Iota]) => 204 + args :+ value 205 + def register(id: Identifier, pattern: => HexPattern)(body: => Action): Unit = 206 + boundary: 207 + val p = try pattern catch case _: NotImplementedError => 208 + logger.warn(s"No pattern for action $id") 209 + boundary.break() 210 + lazy val b = try body catch case _: NotImplementedError => throw MishapTodo() 211 + actionRegistry(id) = ActionRegistryEntry(p, new Action { export b._ }) 212 + def arithmetic(id: Identifier, pattern: HexPattern): Unit = 213 + Patterns.register(id, pattern): 214 + OperationAction(pattern) 215 + 216 + val hexXplat: IXplatAbstractions = IXplatAbstractions.INSTANCE 217 + implicit class IterExt[T](i: IterableOps[T, ?, ?]): 218 + export i.{exists => ∃, forall => ∀} 219 + def findFirstOrLast(p: T => Boolean): Option[T] = 220 + boundary: 221 + (None /: i): 222 + case (ctx, x) => 223 + if p(x) then 224 + boundary.break(Some(x)) 225 + else 226 + Some(x) 227 + implicit class IterOnceExt[T](i: IterableOnceOps[T, ?, ?]): 228 + export i.{exists => ∃, forall => ∀} 229 + def findFirstOrLast(p: T => Boolean): Option[T] = 230 + boundary: 231 + (None /: i): 232 + case (ctx, x) => 233 + if p(x) then 234 + boundary.break(Some(x)) 235 + else 236 + Some(x) 237 + 238 + extension (ctx: StringContext) def ifModLoaded(`then`: => Unit, `else`: => Unit = {}): Unit = 239 + if isDev || fabric.isModLoaded(ctx.parts(0)) then 240 + `then` 241 + else 242 + `else` 243 + 244 + extension (p: ServerPlayerEntity) def gimmeIota(iota: Iota): Unit = 245 + val m = p.getComponent(HexCardinalComponents.STAFFCAST_IMAGE) 246 + m.setImage(m.getVM(Hand.MAIN_HAND).getImage.withStack(_ ++ Vector(iota))) 247 + 248 + // this is out-of-scope for phlib but I have no idea where else to put it 249 + def init() = 250 + hexXplat.getIotaTypeRegistry("map") = MapIota 251 + hexXplat.getArithmeticRegistry("maps") = mapArithmetic 252 + Events.registryLookup.register: 253 + case (r, i) if r == hexXplat.getIotaTypeRegistry && i == Identifier.of("hexic", "map") => MapIota 254 + Patterns.register("empty_map", e"dqdwdqd"): 255 + Patterns.mkLiteral(MapIota()) 256 + CommandRegistrationCallback.EVENT.register: (d, r, e) => 257 + d.getRoot.addChild(LiteralArgumentBuilder.literal[ServerCommandSource]("gimmeiota") 258 + .requires(c => c.hasPermissionLevel(2) || (c.getPlayer != null && c.getPlayer.isCreative)) 259 + .`then`(RequiredArgumentBuilder.argument("type", RegistryEntryArgumentType.registryEntry(r, HexRegistries.IOTA_TYPE)) 260 + .`then`(RequiredArgumentBuilder.argument[ServerCommandSource, NbtElement]("data", NbtElementArgumentType.nbtElement()) 261 + .executes(c => 262 + val t = RegistryEntryArgumentType.getRegistryEntry(c, "type", HexRegistries.IOTA_TYPE) 263 + val d = NbtElementArgumentType.getNbtElement(c, "data") 264 + val p = c.getSource.getPlayer 265 + if p == null then 266 + throw CommandException("Command must be run by a player") 267 + try 268 + t.value.deserialize(d, c.getSource.getWorld) match 269 + case null => throw CommandException("Iota did not accept the given data") 270 + case r: Iota => 271 + p.gimmeIota(r) 272 + c.getSource.sendFeedback(() => Text.translatable("Pushed %s to stack", try r.display catch case x: (Exception | Error) => x.getMessage), true) 273 + 1 274 + case x => throw CommandException(s"${x} is not an iota") 275 + catch 276 + case x: IllegalArgumentException => throw CommandException(x.getMessage) 277 + ).build() 278 + ).build() 279 + ).`then`( 280 + RequiredArgumentBuilder.argument[ServerCommandSource, EntitySelector]("entity", EntityArgumentType.entity()) 281 + .executes(c => 282 + val p = c.getSource.getPlayer 283 + if p == null then 284 + throw CommandException("Command must be run by a player") 285 + val r = EntityIota(EntityArgumentType getEntity(c, "entity")) tap p.gimmeIota 286 + c.getSource.sendFeedback(() => Text.translatable("Pushed %s to stack", r.display), true) 287 + 1 288 + ).build() 289 + ).build() 290 + ) 291 + 292 + object Events: 293 + def partialEvent[T, R]: Event[PartialFunction[T, R]] = EventFactory.createArrayBacked(classOf, PartialFunction.empty, ary => (PartialFunction.empty /: ary) (_ orElse _)) 294 + val beforePatternExecute: Event[PartialFunction[(PatternIota, CastingVM, ServerWorld, SpellContinuation), CastResult]] = partialEvent 295 + val registryLookup: Event[PartialFunction[(Registry[?], Identifier), ?]] = partialEvent
+105
util/src/main/scala/org/eu/net/pool/phlib/maps.scala
··· 1 + package org.eu.net.pool 2 + package phlib 3 + 4 + import at.petrak.hexcasting.api.casting.iota.DoubleIota 5 + import at.petrak.hexcasting.api.casting.iota.Iota 6 + import at.petrak.hexcasting.api.casting.iota.NullIota 7 + import net.minecraft.nbt.NbtCompound 8 + import at.petrak.hexcasting.api.casting.iota.IotaType 9 + import net.minecraft.nbt.NbtList 10 + import net.minecraft.nbt.NbtElement 11 + import net.minecraft.text.{MutableText, Text} 12 + import net.minecraft.util.Formatting 13 + 14 + import java.util.List => JavaList 15 + import net.minecraft.server.world.ServerWorld 16 + import at.petrak.hexcasting.api.utils.HexUtils 17 + 18 + case class MapIota(map: Map[NbtCompound, NbtCompound] = Map(), trusted: Boolean = false)(using val world: ServerWorld) extends Iota(MapIota, map): 19 + def get(key: Iota): Option[Iota] = map.get(IotaType.serialize(key)).map(IotaType.deserialize(_, summon)) 20 + def apply(key: Iota): Iota = get(key) getOrElse NullIota() 21 + def -(keys: Iota*): MapIota = MapIota(map -- (keys map IotaType.serialize)) 22 + def --(other: MapIota): MapIota = MapIota(map -- other.map.keys) 23 + def +(pairs: (Iota, Iota)*): MapIota = MapIota(map ++ pairs.map(p => (IotaType.serialize(p._1), IotaType.serialize(p._2)))) 24 + def ++(other: MapIota): MapIota = MapIota(map ++ other.map) 25 + def update(f: map.type => Map[NbtCompound, NbtCompound]): MapIota = MapIota(f(map)) 26 + def head: (Iota, Iota) = (IotaType.deserialize(map.head._1, summon), IotaType.deserialize(map.head._2, summon)) 27 + def tail: MapIota = MapIota(map.tail) 28 + def init: MapIota = MapIota(map.init) 29 + def last: (Iota, Iota) = (IotaType.deserialize(map.last._1, summon), IotaType.deserialize(map.last._2, summon)) 30 + def headOption: Option[(Iota, Iota)] = map.headOption.map(e => (IotaType.deserialize(e._1, summon), IotaType.deserialize(e._2, summon))) 31 + def lastOption: Option[(Iota, Iota)] = map.lastOption.map(e => (IotaType.deserialize(e._1, summon), IotaType.deserialize(e._2, summon))) 32 + def &(other: MapIota): MapIota = MapIota(map.filter(_._1 pipe other.map.contains)) 33 + def ^(other: MapIota): MapIota = new MapIota: 34 + (map /: other.map): (map, e) => 35 + e match 36 + case (k@map(_), _) => map - k 37 + case (_, _) => map + e 38 + def toList: JavaList[Iota] = map.flatMap((k, v) => Seq(k: Iota, v: Iota)).toSeq 39 + override def isTruthy: Boolean = map.nonEmpty 40 + override def toleratesOther(iota: Iota): Boolean = iota match 41 + case m: MapIota => map == m.map 42 + case _ => false 43 + override def serialize(): NbtElement = NbtList().tap: l => 44 + map.toVector.foreach(p => NbtCompound().tap(c => 45 + c.put("k", p._1) 46 + c.put("v", p._2)) tap l.add) 47 + override def size = map.toSeq.map(_.size + _.size - 1).sum + 1 48 + override def subIotas(): java.lang.Iterable[Iota] = 49 + if trusted then 50 + // skip deserialization for performance 51 + // this is safe because `trusted` can only be true if truenames have 52 + // already been checked, and we override `size` 53 + Seq() 54 + else 55 + toList 56 + object MapIota extends IotaType[MapIota]: 57 + def color: Int = 0xb0641c 58 + def deserialize(using data: NbtElement, world: ServerWorld): MapIota = 59 + val l = HexUtils.downcast(data, NbtList.TYPE) 60 + val o = HexUtils.downcast(_, NbtCompound.TYPE) 61 + l.map(o) 62 + .map(c => (o(c("k")), o(c("v")))) 63 + .toMap[NbtCompound, NbtCompound] 64 + .pipe(new MapIota(_, trusted = true)) 65 + def display(data: NbtElement): Text = 66 + val items = HexUtils.downcast(data, NbtList.TYPE) 67 + val output: MutableText = "[" 68 + output.styled(_.withColor(color)) 69 + if items.nonEmpty then 70 + def castToCompound = HexUtils.downcast(_, NbtCompound.TYPE) 71 + val itemPair = items.map(castToCompound).iterator 72 + def writePair(pair: NbtCompound) = 73 + output.append(try IotaType.getDisplay(pair("k") pipe castToCompound) catch case e => t"∞" formatted Formatting.RED) 74 + output.append(" → ") 75 + output.append(try IotaType.getDisplay(pair("v") pipe castToCompound) catch case e => t"∞" formatted Formatting.RED) 76 + writePair(itemPair.next()) 77 + while itemPair.hasNext do 78 + output.append(", ") 79 + writePair(itemPair.next()) 80 + else 81 + output.append("→") 82 + output.append("]") 83 + output 84 + val mapArithmetic = 85 + import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic.* 86 + arith("map", 87 + ADD -> ((a: MapIota, b: MapIota) => a ++ b), 88 + SUB -> ((a: MapIota, b: MapIota) => a -- b), 89 + ABS -> ((a: MapIota) => DoubleIota(a.map.size)), 90 + INDEX -> ((a: MapIota, k: Iota) => a(k)), 91 + UNAPPEND -> ((a: MapIota) => a.lastOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.init), 92 + INDEX_OF -> ((a: MapIota, v: Iota) => 93 + val c = IotaType.serialize(v) 94 + a.update(_.filter(_._2 == c))), 95 + REMOVE -> ((a: MapIota, k: Iota) => a - k), 96 + REPLACE -> ((a: MapIota, k: Iota, v: Iota) => a + (k -> v)), 97 + UNCONS -> ((a: MapIota) => a.headOption.map(p => Seq(p._1, p._2)).getOrElse(Seq(NullIota(), NullIota())) prepended a.tail), 98 + AND -> ((a: MapIota, b: MapIota) => a & b), 99 + OR -> ((a: MapIota, b: MapIota) => b ++ a), 100 + XOR -> ((a: MapIota, b: MapIota) => a ^ b), 101 + GREATER -> ((a: MapIota, b: MapIota) => a.map.containsAll(b.map) && a.map != b.map), 102 + LESS -> ((a: MapIota, b: MapIota) => b.map.containsAll(a.map) && a.map != b.map), 103 + GREATER_EQ -> ((a: MapIota, b: MapIota) => a.map containsAll b.map), 104 + LESS_EQ -> ((a: MapIota, b: MapIota) => b.map containsAll a.map), 105 + )
+20
util/src/main/scala/org/eu/net/pool/phlib/mixin/SimpleRegistryMixin.java
··· 1 + package org.eu.net.pool.phlib.mixin; 2 + 3 + import net.minecraft.registry.Registry; 4 + import net.minecraft.registry.SimpleDefaultedRegistry; 5 + import net.minecraft.registry.SimpleRegistry; 6 + import net.minecraft.util.Identifier; 7 + import org.eu.net.pool.phlib.Events; 8 + import org.spongepowered.asm.mixin.Mixin; 9 + import org.spongepowered.asm.mixin.injection.At; 10 + import org.spongepowered.asm.mixin.injection.Inject; 11 + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 + import scala.Tuple2; 13 + 14 + @Mixin({SimpleRegistry.class, SimpleDefaultedRegistry.class}) 15 + public abstract class SimpleRegistryMixin<T> implements Registry<T> { 16 + @Inject(at = @At("HEAD"), method = "get(Lnet/minecraft/util/Identifier;)Ljava/lang/Object;", cancellable = true) 17 + void preGet(Identifier id, CallbackInfoReturnable<T> cir) { 18 + Events.registryLookup().invoker().unapply(new Tuple2<>(this, id)).foreach(x -> { cir.setReturnValue((T) x); return null; }); 19 + } 20 + }