From 684cec7c6e821b411ea89ef7e50adc18485b26e8 Mon Sep 17 00:00:00 2001 From: lqdev Date: Fri, 18 Nov 2022 18:13:03 +0100 Subject: [PATCH] initial commit --- .gitignore | 37 + README.md | 3 + build.gradle.kts | 81 ++ d3r/Cargo.lock | 1106 +++++++++++++++++ d3r/Cargo.toml | 16 + d3r/README.md | 8 + d3r/build.gradle.kts | 13 + d3r/src/lib.rs | 214 ++++ d3r/src/registry.rs | 62 + gradle.properties | 25 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 52818 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 234 ++++ gradlew.bat | 89 ++ proj/block/module_generic.ase | Bin 0 -> 614 bytes proj/block/speaker_front.ase | Bin 0 -> 627 bytes proj/icon.ase | Bin 0 -> 702 bytes proj/item/dawd3.ase | Bin 0 -> 769 bytes proj/item/patch_cable.ase | Bin 0 -> 672 bytes proj/tilde.ase | Bin 0 -> 612 bytes proj/tilde.png | Bin 0 -> 13323 bytes settings.gradle.kts | 13 + .../net/liquidev/d3r/AudioOutputStream.java | 7 + src/main/java/net/liquidev/d3r/D3r.java | 88 ++ .../java/net/liquidev/d3r/D3rException.java | 7 + .../kotlin/net/liquidev/dawd3/D3Registry.kt | 23 + src/main/kotlin/net/liquidev/dawd3/Mod.kt | 36 + .../kotlin/net/liquidev/dawd3/block/Blocks.kt | 38 + .../net/liquidev/dawd3/block/SpeakerBlock.kt | 46 + .../dawd3/block/SpeakerBlockEntity.kt | 20 + .../kotlin/net/liquidev/dawd3/item/Items.kt | 52 + .../net/liquidev/dawd3/item/PatchCable.kt | 16 + .../dawd3/mixin/SoundManagerAccessor.kt | 12 + .../dawd3/mixin/SoundSystemAccessor.kt | 12 + .../liquidev/dawd3/sound/AudioGenerator.kt | 27 + .../liquidev/dawd3/sound/SineOscGenerator.kt | 21 + .../kotlin/net/liquidev/dawd3/sound/Sound.kt | 32 + .../assets/dawd3/blockstates/speaker.json | 8 + src/main/resources/assets/dawd3/icon.png | Bin 0 -> 307 bytes .../resources/assets/dawd3/lang/en_us.json | 3 + .../dawd3/models/block/module_with_front.json | 21 + .../assets/dawd3/models/block/speaker.json | 6 + .../assets/dawd3/models/item/dawd3.json | 6 + .../assets/dawd3/models/item/patch_cable.json | 6 + .../assets/dawd3/models/item/speaker.json | 3 + .../dawd3/textures/block/module_side.png | Bin 0 -> 125 bytes .../dawd3/textures/block/speaker_front.png | Bin 0 -> 149 bytes .../assets/dawd3/textures/item/dawd3.png | Bin 0 -> 230 bytes .../dawd3/textures/item/patch_cable.png | Bin 0 -> 246 bytes src/main/resources/dawd3.mixins.json | 12 + src/main/resources/fabric.mod.json | 42 + 51 files changed, 2450 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 build.gradle.kts create mode 100644 d3r/Cargo.lock create mode 100644 d3r/Cargo.toml create mode 100644 d3r/README.md create mode 100644 d3r/build.gradle.kts create mode 100644 d3r/src/lib.rs create mode 100644 d3r/src/registry.rs create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 proj/block/module_generic.ase create mode 100644 proj/block/speaker_front.ase create mode 100644 proj/icon.ase create mode 100644 proj/item/dawd3.ase create mode 100644 proj/item/patch_cable.ase create mode 100644 proj/tilde.ase create mode 100644 proj/tilde.png create mode 100644 settings.gradle.kts create mode 100644 src/main/java/net/liquidev/d3r/AudioOutputStream.java create mode 100644 src/main/java/net/liquidev/d3r/D3r.java create mode 100644 src/main/java/net/liquidev/d3r/D3rException.java create mode 100644 src/main/kotlin/net/liquidev/dawd3/D3Registry.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/Mod.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/block/Blocks.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlock.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlockEntity.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/item/Items.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/item/PatchCable.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/mixin/SoundManagerAccessor.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/mixin/SoundSystemAccessor.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/sound/AudioGenerator.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/sound/SineOscGenerator.kt create mode 100644 src/main/kotlin/net/liquidev/dawd3/sound/Sound.kt create mode 100644 src/main/resources/assets/dawd3/blockstates/speaker.json create mode 100644 src/main/resources/assets/dawd3/icon.png create mode 100644 src/main/resources/assets/dawd3/lang/en_us.json create mode 100644 src/main/resources/assets/dawd3/models/block/module_with_front.json create mode 100644 src/main/resources/assets/dawd3/models/block/speaker.json create mode 100644 src/main/resources/assets/dawd3/models/item/dawd3.json create mode 100644 src/main/resources/assets/dawd3/models/item/patch_cable.json create mode 100644 src/main/resources/assets/dawd3/models/item/speaker.json create mode 100644 src/main/resources/assets/dawd3/textures/block/module_side.png create mode 100644 src/main/resources/assets/dawd3/textures/block/speaker_front.png create mode 100644 src/main/resources/assets/dawd3/textures/item/dawd3.png create mode 100644 src/main/resources/assets/dawd3/textures/item/patch_cable.png create mode 100644 src/main/resources/dawd3.mixins.json create mode 100644 src/main/resources/fabric.mod.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..758a9a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# gradle + +.gradle/ +build/ +out/ +classes/ + +# eclipse + +*.launch + +# idea + +.idea/ +*.iml +*.ipr +*.iws + +# vscode + +.settings/ +.vscode/ +bin/ +.classpath +.project + +# macos + +*.DS_Store + +# fabric + +run/ + +# Rust + +target/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bb1b20 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# dawd³ + +dawd³ (dawd cubed) - Minecraft audio experiments. diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..49717f2 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,81 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("fabric-loom") + kotlin("jvm").version(System.getProperty("kotlin_version")) + id("fr.stardustenterprises.rust.wrapper") version "3.2.5" apply false + id("fr.stardustenterprises.rust.importer") version "3.2.5" +} + +base { archivesName.set(project.extra["archives_base_name"] as String) } +version = project.extra["mod_version"] as String +group = project.extra["maven_group"] as String + +repositories {} + +dependencies { + minecraft("com.mojang", "minecraft", project.extra["minecraft_version"] as String) + mappings("net.fabricmc", "yarn", project.extra["yarn_mappings"] as String, null, "v2") + modImplementation("net.fabricmc", "fabric-loader", project.extra["loader_version"] as String) + modImplementation("net.fabricmc.fabric-api", "fabric-api", project.extra["fabric_version"] as String) + modImplementation( + "net.fabricmc", + "fabric-language-kotlin", + project.extra["fabric_language_kotlin_version"] as String + ) + rust(project(":d3r")) +} + +subprojects { + group = "net.liquidev.d3r" + version = "0.1.0" +} + +rustImport { + baseDir.set("/d3r") + layout.set("flat") +} + +tasks { + val javaVersion = JavaVersion.toVersion((project.extra["java_version"] as String).toInt()) + + withType { + options.encoding = "UTF-8" + sourceCompatibility = javaVersion.toString() + targetCompatibility = javaVersion.toString() + options.release.set(javaVersion.toString().toInt()) + } + + withType { + kotlinOptions { + jvmTarget = javaVersion.toString() + } + } + + jar { + from("LICENSE") { rename { "${it}_${base.archivesName}" } } + } + + processResources { + filesMatching("fabric.mod.json") { + expand( + mutableMapOf( + "version" to project.extra["mod_version"] as String, + "fabricloader" to project.extra["loader_version"] as String, + "fabric_api" to project.extra["fabric_version"] as String, + "fabric_language_kotlin" to project.extra["fabric_language_kotlin_version"] as String, + "minecraft" to project.extra["minecraft_version"] as String, + "java" to project.extra["java_version"] as String + ) + ) + } + filesMatching("*.mixins.json") { expand(mutableMapOf("java" to project.extra["java_version"] as String)) } + } + + java { + toolchain { languageVersion.set(JavaLanguageVersion.of(javaVersion.toString())) } + sourceCompatibility = javaVersion + targetCompatibility = javaVersion + withSourcesJar() + } +} diff --git a/d3r/Cargo.lock b/d3r/Cargo.lock new file mode 100644 index 0000000..e3f902d --- /dev/null +++ b/d3r/Cargo.lock @@ -0,0 +1,1106 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "alsa" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" +dependencies = [ + "alsa-sys", + "bitflags", + "libc", + "nix 0.23.1", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "bindgen" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cc" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "coreaudio-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" +dependencies = [ + "bitflags", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9444b94b8024feecc29e01a9706c69c1e26bfee480221c90764200cfd778fb" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cpal" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73413ddcb69c398125f5529714492e070c64c6a090ad5b01d8c082b320a0809" +dependencies = [ + "alsa", + "core-foundation-sys", + "coreaudio-rs", + "jni 0.19.0", + "js-sys", + "libc", + "mach", + "ndk 0.7.0", + "ndk-context", + "nix 0.25.0", + "oboe", + "once_cell", + "parking_lot", + "stdweb", + "thiserror", + "web-sys", + "windows", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "d3r" +version = "0.1.0" +dependencies = [ + "bitvec", + "cpal", + "env_logger", + "jni 0.20.0", + "log", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "ndk" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys 0.3.0", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags", + "jni-sys", + "ndk-sys 0.4.0", + "num_enum", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "ndk-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "oboe" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" +dependencies = [ + "jni 0.19.0", + "ndk 0.6.0", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd" +dependencies = [ + "cc", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "raw-window-handle" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" +dependencies = [ + "cty", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +dependencies = [ + "windows_aarch64_msvc 0.37.0", + "windows_i686_gnu 0.37.0", + "windows_i686_msvc 0.37.0", + "windows_x86_64_gnu 0.37.0", + "windows_x86_64_msvc 0.37.0", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/d3r/Cargo.toml b/d3r/Cargo.toml new file mode 100644 index 0000000..f1efdf0 --- /dev/null +++ b/d3r/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "d3r" +description = "dawd³ audio runtime" +version = "0.1.0" +edition = "2021" + +[lib] +name = "dawd3_d3r" +crate-type = ["cdylib"] + +[dependencies] +bitvec = "1.0.1" +cpal = "0.14.1" +env_logger = "0.9.3" +jni = "0.20.0" +log = "0.4.17" diff --git a/d3r/README.md b/d3r/README.md new file mode 100644 index 0000000..c866111 --- /dev/null +++ b/d3r/README.md @@ -0,0 +1,8 @@ +# d3r + +**d3r** is a low-level realtime sound output library for Java, developed as the audio backend of dawd³. +The Java sources can be found [here](../src/main/java/net/liquidev/d3r). + +d3r exposes the API of [CPAL] to Java through the use of JNI. + + [CPAL]: https://lib.rs/crates/cpal diff --git a/d3r/build.gradle.kts b/d3r/build.gradle.kts new file mode 100644 index 0000000..1e76c1d --- /dev/null +++ b/d3r/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("fr.stardustenterprises.rust.wrapper") +} + +rust { + command.set("cargo") + cargoInstallTargets.set(true) + + release.set(true) + +// targets += target("x86_64-pc-windows-gnu", "dawd3_d3r.dll") + targets += target("x86_64-unknown-linux-gnu", "libdawd3_d3r.so") +} diff --git a/d3r/src/lib.rs b/d3r/src/lib.rs new file mode 100644 index 0000000..013f8c1 --- /dev/null +++ b/d3r/src/lib.rs @@ -0,0 +1,214 @@ +use std::cell::RefCell; +use std::fmt::Display; + +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use cpal::{BufferSize, Device, Host, SampleRate, Stream, StreamConfig}; +use jni::objects::{JClass, JMethodID, JObject}; +use jni::signature::ReturnType; +use jni::sys::jvalue; +use jni::JNIEnv; +use log::{error, info, warn, LevelFilter}; + +use crate::registry::Registry; + +mod registry; + +struct GlobalState { + host: Option, + devices: Registry, + streams: Registry, +} + +thread_local! { + static STATE: RefCell = RefCell::new(GlobalState { + host: None, + devices: Registry::new(), + streams: Registry::new(), + }); +} + +fn with_global_state(f: impl FnOnce(&mut GlobalState) -> T) -> T { + STATE.with(|state| { + let mut state = state.borrow_mut(); + f(&mut state) + }) +} + +fn try_with_global_state(env: JNIEnv, f: impl FnOnce(&mut GlobalState) -> Result) -> T +where + T: Default, + E: Display, +{ + let result = with_global_state(|state| f(state)); + match result { + Ok(value) => value, + Err(error) => { + let _ = env.throw_new("net/liquidev/d3r/D3rException", error.to_string()); + T::default() + } + } +} + +#[no_mangle] +pub extern "system" fn Java_net_liquidev_d3r_D3r_initialize(_env: JNIEnv, _: JClass) { + env_logger::builder() + .filter(Some("dawd3_d3r"), LevelFilter::Trace) + .format_timestamp(None) + .init(); + info!("d3r initialized successfully") +} + +#[no_mangle] +pub extern "system" fn Java_net_liquidev_d3r_D3r_openDefaultHost(env: JNIEnv, _: JClass) { + try_with_global_state(env, |state| { + if state.host.is_some() { + Err("Audio host already open") + } else { + let host = cpal::default_host(); + info!("using host: {:?}", host.id()); + state.host = Some(host); + Ok(()) + } + }) +} + +#[no_mangle] +pub extern "system" fn Java_net_liquidev_d3r_D3r_openDefaultOutputDevice( + env: JNIEnv, + _: JClass, +) -> u32 { + try_with_global_state(env, |state| { + let Some(host) = &state.host else { return Err("Host is not open"); }; + let Some(device) = host.default_output_device() else { return Err("No default output device found"); }; + if let Ok(name) = device.name() { + info!("default output device opened successfully: {name}"); + } else { + warn!("default output device opened successfully, but could not obtain its name"); + } + Ok(state.devices.add(device)) + }) +} + +fn generate_audio( + output: &mut [f32], + config: &StreamConfig, + env: JNIEnv, + generator: JObject, + method: JMethodID, +) -> Result<(), String> { + let buffer = env + .call_method_unchecked( + generator, + method, + ReturnType::Array, + &[ + jvalue { + i: output.len() as i32, + }, + jvalue { + i: config.channels as i32, + }, + ], + ) + .map_err(|e| format!("error while calling into the audio generator: {e}"))?; + let buffer = buffer.l().expect("buffer must be an object"); + let len = env + .get_array_length(buffer.into_raw()) + .expect("buffer must be a float[]"); + if (len as usize) < output.len() { + return Err(format!( + "audio buffer length is too short (expected {}, but generator returned {len})", + output.len() + )); + } + env.get_float_array_region(buffer.into_raw(), 0, output) + .expect("buffer must be a float[]"); + + Ok(()) +} + +#[no_mangle] +pub extern "system" fn Java_net_liquidev_d3r_D3r_openOutputStream( + env: JNIEnv, + _: JClass, + output_device_id: u32, + sample_rate: u32, + channel_count: u16, + buffer_size: u32, + generator: JObject, +) -> u32 { + try_with_global_state(env, |state| { + let Some(device) = state.devices.get(output_device_id) else { return Err("Invalid output device ID".to_string()); }; + let config = StreamConfig { + channels: channel_count, + sample_rate: SampleRate(sample_rate), + buffer_size: if buffer_size == 0 { + BufferSize::Default + } else { + BufferSize::Fixed(buffer_size) + }, + }; + + let class = env.get_object_class(generator).map_err(|e| e.to_string())?; + let generate_method = env + .get_method_id(class, "getOutputBuffer", "(II)[F") + .map_err(|e| e.to_string())?; + + let generator_ref = env.new_global_ref(generator).map_err(|e| e.to_string())?; + let jvm = env.get_java_vm().map_err(|e| e.to_string())?; + let mut initialized = false; + let stream = device + .build_output_stream( + &config.clone(), + move |data: &mut [f32], _| { + if !initialized { + if let Err(error) = jvm.attach_current_thread_permanently() { + error!("cannot attach JVM to audio thread: {error}"); + // TODO: propagate the error? + return; + } + initialized = true; + } + let env = jvm + .get_env() + .expect("thread should be attached at this point"); + let generator = generator_ref.as_obj(); + if let Err(error) = + generate_audio(data, &config, env, generator, generate_method) + { + error!("{error}"); + } + }, + |error| { + error!("{error}"); // lol + }, + ) + .map_err(|e| e.to_string())?; + Ok(state.streams.add(stream)) + }) +} + +#[no_mangle] +pub extern "system" fn Java_net_liquidev_d3r_D3r_closeOutputStream( + env: JNIEnv, + _: JClass, + output_stream_id: u32, +) { + try_with_global_state(env, |state| { + let Some(stream) = state.devices.remove(output_stream_id) else { return Err("Invalid output stream ID"); }; + drop(stream); + Ok(()) + }); +} + +#[no_mangle] +pub extern "system" fn Java_net_liquidev_d3r_D3r_startPlayback( + env: JNIEnv, + _: JClass, + output_stream_id: u32, +) { + try_with_global_state(env, |state| { + let Some(stream) = state.streams.get(output_stream_id) else { return Err("Invalid output stream ID".to_string()); }; + stream.play().map_err(|e| e.to_string()) + }); +} diff --git a/d3r/src/registry.rs b/d3r/src/registry.rs new file mode 100644 index 0000000..6bebefc --- /dev/null +++ b/d3r/src/registry.rs @@ -0,0 +1,62 @@ +//! Registry that attaches names to objects. + +use std::mem::MaybeUninit; +use bitvec::vec::BitVec; + +pub struct Registry { + next_free: u32, + free_list: Vec, + valid: BitVec, + store: Vec>, +} + +impl Registry { + pub fn new() -> Self { + Self { + next_free: 0, + free_list: vec![], + valid: BitVec::new(), + store: vec![] + } + } + + pub fn add(&mut self, item: T) -> u32 { + if let Some(id) = self.free_list.pop() { + self.valid.set(id as usize, true); + self.store[id as usize].write(item); + id + } else { + let free = self.next_free; + self.next_free += 1; + self.valid.push(true); + self.store.push(MaybeUninit::new(item)); + free + } + } + + pub fn remove(&mut self, index: u32) -> Option { + if self.valid[index as usize] { + self.valid.set(index as usize, false); + let item = std::mem::replace(&mut self.store[index as usize], MaybeUninit::uninit()); + Some(unsafe { item.assume_init() }) + } else { + None + } + } + + pub fn get(&self, index: u32) -> Option<&T> { + if self.valid[index as usize] { + Some(unsafe { self.store[index as usize].assume_init_ref() }) + } else { + None + } + } + + pub fn get_mut(&mut self, index: u32) -> Option<&mut T> { + if self.valid[index as usize] { + Some(unsafe { self.store[index as usize].assume_init_mut() }) + } else { + None + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..ece606c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,25 @@ +########################################################################## +# Standard Properties +kotlin.code.style = official +org.gradle.jvmargs = -Xmx1G +org.gradle.warning.mode = all +########################################################################## +# Standard Fabric Dependencies +# Check these on https://fabricmc.net/develop/ +minecraft_version = 1.19.2 +yarn_mappings = 1.19.2+build.28 +loader_version = 0.14.10 +# Fabric API +fabric_version = 0.66.0+1.19.2 +loom_version = 1.0-SNAPSHOT +java_version = 17 +########################################################################## +# Mod Properties +mod_version = 0.1.0 +maven_group = net.liquidev.dawd3 +archives_base_name = dawd3 +########################################################################## +# Kotlin Dependencies +systemProp.kotlin_version = 1.7.20 +fabric_language_kotlin_version = 1.8.5+kotlin.1.7.20 +########################################################################## diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..deedc7fa5e6310eac3148a7dd0b1f069b07364cb GIT binary patch literal 52818 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

8Oc_+rjPiz&suPEG2VDz8xlNB|s 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ff75c23 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..1b6c787 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..477c896 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega \ No newline at end of file diff --git a/proj/block/module_generic.ase b/proj/block/module_generic.ase new file mode 100644 index 0000000000000000000000000000000000000000..69f032a298db6c803827d300eaec16a4ebabdcea GIT binary patch literal 614 zcmYdeVqkc%l#xMzL4ZMlfsuiMA%y`MC^ImE1Q|FOIFN;@#(u`g!0_uME7(?61_lN( z1_lOp1+bM!cD!L=V31T$hKeXbX=LjE|NsBN{QnFLTw9y2Sp=Kbm+wngG z1KTOD{|pTEZ-V|aFdX{b2NHh;(#zWS0mQag_@99xgCX}n1H&pX`C1sH{lvu{vVbHk)J^>`Tuy? ze+GuvJ3Ia}FbIf%M3gjL{xdKb*~I>5VDJbj{m;NKZQ1_+3=Eg=y!>Cxz`(!)iV2W+ zA->{bS7A_*Ww6m?2sLBqc4Rx{RsSaF(Cn-Ks2 literal 0 HcmV?d00001 diff --git a/proj/block/speaker_front.ase b/proj/block/speaker_front.ase new file mode 100644 index 0000000000000000000000000000000000000000..a09f132bd4ad1dffa15ddfdc56cb988a7e908c4f GIT binary patch literal 627 zcmXSBVqkc%l#xMzL4ZMlfsuiMA%y`MC^ImE1Q|FOIFN;@#{SI6!0_uME7(?61_lN( z1_lOp1+bM!cD!L=V31T$hKeXbX=LjE|NsBN{QnFLTw9y2Sp=Kbm+wngG z1KTOD{|pTEZ-V|aFdX{b2NHh;(#zWS0mQag_@99xgCX}n1H&pX`C1sH{lvu{vVbHk)J^>`Tuy? ze+GuvJ3Ia}FbIf%M3gjL{xdKb*~I>5VDJbj{m;NKZQ1_+3=Eg=y!>Cxz`(!)iV2W+ zA->{bS7A_*Ww6m?2sLBqc4Rx{RsSaF(C6 icB~L^O}^~kbuaL@`&;>*ZjlrKXEb8FY5)M%0G{{& literal 0 HcmV?d00001 diff --git a/proj/icon.ase b/proj/icon.ase new file mode 100644 index 0000000000000000000000000000000000000000..862818119cea49d375628abafad8e509384d4ef4 GIT binary patch literal 702 zcmdnT#K7=iDI*@1(7zIATrbp zM0Pv=XJBAE<@KL|q5e(Ke+Gs_zxzPquRwZP`#yl!77PC~Fk~>~{%2rV1twn$gVg`_ z^ZU=haNEG(KLf)Y28RC(46B2b{xdMtYH|N(U?>m}_|L#_+6N^5zk%yN1H=Er@*wgv z$R+^!{gp&O~I literal 0 HcmV?d00001 diff --git a/proj/item/dawd3.ase b/proj/item/dawd3.ase new file mode 100644 index 0000000000000000000000000000000000000000..22cf9d864e45c869602aa84d1ec83c076e5d307d GIT binary patch literal 769 zcmZQ%W?*=*l#xMzL4ZMlfsuiMA%y`MC^ImE1Q|FOIFN;@#%^R{VEFZs9c(K*0|SE? z0|NuQ0@z91FNv0AgD#{LjFU!I1l(fngPxd@T%8|J%>+KLf*U z1B3qz409M5{xdMF4pREhz)-8j{hxuMKtSL>1H)+_kof-wuKx@S{}0Q9$j=~`{C~Xc zKLf+-ogM!f7z9K>B1)Pr{}~vJY-0a2FnENN{%2sAwru}@28PRbUj8p;U|?VY#RSN^ z5MObzt1zg@GT3M`gqksQJF=bfs(%x7=yxB(tN*Nh9~dkaW-#QgV#s|h?DgBv@3w)# z9I)$ZwYUoe1VD~z-~u`4v&#R+%U?R z`1Hd~%<>A?*8EpFvABEkYqOkRomZVt$T>X|l2&hdqsZdH$((feAOnMcZ2F5l0)mZ58((x#8Spms9kjPU(qXOqIvQ_U`{tH(vk$ nfB4h?n|_sl)8GDX2$nUHpDvg5X7RTF1us>Xurmm%|C$Q`>~ziU literal 0 HcmV?d00001 diff --git a/proj/item/patch_cable.ase b/proj/item/patch_cable.ase new file mode 100644 index 0000000000000000000000000000000000000000..de10a2d471b181a281e06226307db00a376a9f43 GIT binary patch literal 672 zcmZ3$#K7=iDIw9y2Sp=Kbm+wngG z1KTOD{|pTEZ-V|aFdX{b2NHh;(#zWS0mQag_@99xgCX}n1H&pX`C1sH{lvu{vVbHk)J^>`Tuy? ze+GuvJ3Ia}FbIf%M3gjL{xdKb*~I>5VDJbj{m;NKZQ1_+3=Eg=y!>Cxz`(!)iV2W+ zA->{bS7A_*Ww6m?2sLBqc4Rx{RsSaF(Cv19WMbeEYY(f0n43@O+ lPDpfJBlpBYF^s?J&g0M8dbS3oFERyX4_q>0@OjV@2LJ)1v9JID literal 0 HcmV?d00001 diff --git a/proj/tilde.ase b/proj/tilde.ase new file mode 100644 index 0000000000000000000000000000000000000000..39cecf6e926265b826bdb7db33ce1a0b45a78ece GIT binary patch literal 612 zcmYdcVqkc%l#zjrftf*pfsuiMA%y`MC^ImE1Q|FOIFN;@#(u)c!0_uME7(?61_lN( z1_lOp1+bM!cD!L=V31T$hKeXbX=LjE|NsBN{QnFLTw9y2Sp=Kbm+wngG z1KTOD{|pTEZ-V|aFdX{b2NHh;(#zWS0mQag_@99xgCX}n1H&pX`C1sH{lvu{vVbHk)J^>`Tuy? ze+GuvJ3Ia}FbIf%M3gjL{xdKb*~I>5VDJbj{m;NKZQ1_+3=Eg=y!>Cxz`(!)iV2W+ zA->{bS7A_*Ww6m?2sLBqc4Rx{RsSaF(Cch_I?GUU82*yrodkdS=3XwE!_0}Wq`X3b+baI9{>UD;)6h8x#D ze_?=u2me{@VQc}VAB<3TgTO(4D3hUuqYlPYaQVOtV=Fev!qgjT5fnl@^0VRdeLY0AmVYHS3C56#y7ZeJkjRl6$#sVlIFffcZ z7C_-J+E^HEEP&Dh1H)(!3=|HdjRjCBj5ZcPp+J?!Lgmi*@2|gbFl;NgxW7!DVZ+8$ zOFz({$?K;kwu}vo_Vd?Y7G}6HZ?^t{=8q3Y2Q@~kWl+jv7_F8Y?91LQU~^F5XiD%9 zXpq?FaZo+1Vi8Nsou$rGn||h}GmiG_z$s<4s0IZCRf_7-@nr^v(UJ$0oJL0vL7^}@ zdN?|I2ucSG3=E^S3@9K*Ynjo}Lr^+k7%roSpYr5X85o3jg4O~+JvN%gN7FboDGbUH z@-^4%^f$XR7~HG0dH;r&q37EfIferTx8E=_FdUG`+s9!4`0(hOlF|AaoP { + try { + Files.delete(p); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + LOGGER.warn("unload() called twice or without calling load() first"); + } + } + + private static native void initialize(); + + // Host + + public static native void openDefaultHost(); + + // Output device + + public static native int openDefaultOutputDevice(); + + public static native void closeOutputDevice(int outputDeviceId); + + // Output stream + + public static native int openOutputStream(int outputDeviceId, int sampleRate, short channelCount, int bufferSize, AudioOutputStream generator); + + public static native void closeOutputStream(int outputStreamId); + + public static native void startPlayback(int outputStreamId); +} diff --git a/src/main/java/net/liquidev/d3r/D3rException.java b/src/main/java/net/liquidev/d3r/D3rException.java new file mode 100644 index 0000000..f8e60da --- /dev/null +++ b/src/main/java/net/liquidev/d3r/D3rException.java @@ -0,0 +1,7 @@ +package net.liquidev.d3r; + +public class D3rException extends Exception { + D3rException(String what) { + super(what); + } +} diff --git a/src/main/kotlin/net/liquidev/dawd3/D3Registry.kt b/src/main/kotlin/net/liquidev/dawd3/D3Registry.kt new file mode 100644 index 0000000..7f568c7 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/D3Registry.kt @@ -0,0 +1,23 @@ +package net.liquidev.dawd3 + +import net.minecraft.util.Identifier + +abstract class D3Registry { + abstract fun doRegister(identifier: Identifier, item: T) + + var registered = arrayListOf>() + + fun add(id: String, item: T): Registered { + val entry = Registered(Identifier(Mod.id, id), item) + registered.add(entry) + return entry + } + + fun registerAll() { + registered.forEach { reg -> + doRegister(reg.identifier, reg.item) + } + } + + data class Registered(val identifier: Identifier, val item: T) +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/Mod.kt b/src/main/kotlin/net/liquidev/dawd3/Mod.kt new file mode 100644 index 0000000..ed95be5 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/Mod.kt @@ -0,0 +1,36 @@ +package net.liquidev.dawd3 + +import net.fabricmc.api.ClientModInitializer +import net.fabricmc.api.ModInitializer +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents +import net.liquidev.d3r.D3r +import net.liquidev.dawd3.block.Blocks +import net.liquidev.dawd3.item.Items +import net.liquidev.dawd3.sound.Sound +import org.slf4j.LoggerFactory + +@Suppress("UNUSED") +object Mod : ModInitializer, ClientModInitializer { + const val id = "dawd3" + + private val logger = LoggerFactory.getLogger("dawd³") + + override fun onInitialize() { + logger.info("hello, sound traveler! welcome to the dawd³ experience") + + Blocks.blockRegistry.registerAll() + Items.registry.registerAll() + } + + override fun onInitializeClient() { + logger.info("booting up sound engine") + D3r.load() + Sound.forceInitializationNow() + + ClientLifecycleEvents.CLIENT_STOPPING.register { + logger.info("shutting down sound engine") + Sound.deinitialize() + D3r.unload() + } + } +} diff --git a/src/main/kotlin/net/liquidev/dawd3/block/Blocks.kt b/src/main/kotlin/net/liquidev/dawd3/block/Blocks.kt new file mode 100644 index 0000000..73bd3e5 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/block/Blocks.kt @@ -0,0 +1,38 @@ +package net.liquidev.dawd3.block + +import net.fabricmc.fabric.api.item.v1.FabricItemSettings +import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings +import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder +import net.liquidev.dawd3.D3Registry +import net.liquidev.dawd3.Mod +import net.liquidev.dawd3.item.Items +import net.minecraft.block.Block +import net.minecraft.block.Material +import net.minecraft.item.BlockItem +import net.minecraft.util.Identifier +import net.minecraft.util.registry.Registry + +object Blocks { + var blockRegistry = object : D3Registry() { + override fun doRegister(identifier: Identifier, item: Block) { + Registry.register(Registry.BLOCK, identifier, item) + } + } + + val speaker = add("speaker", SpeakerBlock(moduleBlockSettings())) + val speakerEntity = Registry.register( + Registry.BLOCK_ENTITY_TYPE, + Identifier(Mod.id, "speaker"), + FabricBlockEntityTypeBuilder.create(::SpeakerBlockEntity, speaker.item).build(), + ) + + private fun moduleBlockSettings() = FabricBlockSettings + .of(Material.METAL) + .hardness(5.0f) + .resistance(6.0f) + + private fun add(name: String, block: Block): D3Registry.Registered { + Items.addItem(name, BlockItem(block, FabricItemSettings().group(Items.creativeTab))) + return blockRegistry.add(name, block) + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlock.kt b/src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlock.kt new file mode 100644 index 0000000..7a02619 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlock.kt @@ -0,0 +1,46 @@ +package net.liquidev.dawd3.block + +import net.minecraft.block.* +import net.minecraft.block.entity.BlockEntity +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.ItemPlacementContext +import net.minecraft.item.ItemStack +import net.minecraft.state.StateManager +import net.minecraft.state.property.Properties +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World + +class SpeakerBlock(settings: Settings) : BlockWithEntity(settings), BlockEntityProvider { + override fun appendProperties(builder: StateManager.Builder) { + builder.add(Properties.HORIZONTAL_FACING) + } + + override fun getPlacementState(ctx: ItemPlacementContext): BlockState { + return defaultState.with(Properties.HORIZONTAL_FACING, ctx.playerFacing.opposite) + } + + override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity { + return SpeakerBlockEntity(pos, state) + } + + override fun getRenderType(state: BlockState): BlockRenderType { + return BlockRenderType.MODEL + } + + override fun onPlaced( + world: World, + pos: BlockPos, + state: BlockState, + placer: LivingEntity?, + itemStack: ItemStack, + ) { + println("Speaker placed") + } + + override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) { + println("Speaker broken, deinitializing block entity") + val blockEntity = world.getBlockEntity(pos) as SpeakerBlockEntity + blockEntity.deinit() + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlockEntity.kt b/src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlockEntity.kt new file mode 100644 index 0000000..19e1d7c --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/block/SpeakerBlockEntity.kt @@ -0,0 +1,20 @@ +package net.liquidev.dawd3.block + +import net.minecraft.block.BlockState +import net.minecraft.block.entity.BlockEntity +import net.minecraft.util.math.BlockPos + +class SpeakerBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Blocks.speakerEntity, pos, state) { + init { + println("Speaker block entity created") + + } + + fun deinit() { + println("Speaker block entity deinitialized") + } + + private fun finalize() { + deinit() + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/item/Items.kt b/src/main/kotlin/net/liquidev/dawd3/item/Items.kt new file mode 100644 index 0000000..2b1b18e --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/item/Items.kt @@ -0,0 +1,52 @@ +package net.liquidev.dawd3.item + +import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder +import net.fabricmc.fabric.api.item.v1.FabricItemSettings +import net.liquidev.dawd3.D3Registry +import net.liquidev.dawd3.Mod +import net.minecraft.item.Item +import net.minecraft.item.ItemGroup +import net.minecraft.util.Identifier +import net.minecraft.util.registry.Registry + +object Items { + var registry = object : D3Registry() { + override fun doRegister(identifier: Identifier, item: RegisteredItem) { + Registry.register(Registry.ITEM, identifier, item.item) + } + } + + // Creative tab + val creativeTab: ItemGroup = FabricItemGroupBuilder.create(Identifier(Mod.id, "main")) + .icon { dawd3.item.item.defaultStack } + .appendItems { list -> + registry.registered.forEach { reg -> + val item = reg.item + if (item.showInCreativeTab) { + list.add(reg.item.item.defaultStack) + } + } + } + .build() + + // Icon for creative tab + val dawd3 = registry.add("dawd3", RegisteredItem(Item(FabricItemSettings())).hiddenFromCreativeTab()) + + // Tools + val patchCable = addItem("patch_cable", PatchCable(FabricItemSettings().group(ItemGroup.REDSTONE))) + + fun addItem(name: String, item: Item) { + registry.add(name, RegisteredItem(item)) + } + + data class RegisteredItem( + val item: Item, + var showInCreativeTab: Boolean = true, + ) { + fun hiddenFromCreativeTab(): RegisteredItem { + this.showInCreativeTab = false + return this + } + } +} + diff --git a/src/main/kotlin/net/liquidev/dawd3/item/PatchCable.kt b/src/main/kotlin/net/liquidev/dawd3/item/PatchCable.kt new file mode 100644 index 0000000..54fd88a --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/item/PatchCable.kt @@ -0,0 +1,16 @@ +package net.liquidev.dawd3.item + +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.sound.SoundEvents +import net.minecraft.util.Hand +import net.minecraft.util.TypedActionResult +import net.minecraft.world.World + +class PatchCable(settings: Settings) : Item(settings) { + override fun use(world: World, user: PlayerEntity, hand: Hand): TypedActionResult { + user.playSound(SoundEvents.BLOCK_METAL_PLACE, 1.0f, 1.0f) + return TypedActionResult.success(user.getStackInHand(hand)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/mixin/SoundManagerAccessor.kt b/src/main/kotlin/net/liquidev/dawd3/mixin/SoundManagerAccessor.kt new file mode 100644 index 0000000..812c20e --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/mixin/SoundManagerAccessor.kt @@ -0,0 +1,12 @@ +package net.liquidev.dawd3.mixin + +import net.minecraft.client.sound.SoundManager +import net.minecraft.client.sound.SoundSystem +import org.spongepowered.asm.mixin.Mixin +import org.spongepowered.asm.mixin.gen.Accessor + +@Mixin(SoundManager::class) +interface SoundManagerAccessor { + @Accessor + fun getSoundSystem(): SoundSystem +} diff --git a/src/main/kotlin/net/liquidev/dawd3/mixin/SoundSystemAccessor.kt b/src/main/kotlin/net/liquidev/dawd3/mixin/SoundSystemAccessor.kt new file mode 100644 index 0000000..1b873cc --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/mixin/SoundSystemAccessor.kt @@ -0,0 +1,12 @@ +package net.liquidev.dawd3.mixin + +import net.minecraft.client.sound.Channel +import net.minecraft.client.sound.SoundSystem +import org.spongepowered.asm.mixin.Mixin +import org.spongepowered.asm.mixin.gen.Accessor + +@Mixin(SoundSystem::class) +interface SoundSystemAccessor { + @Accessor + fun getChannel(): Channel +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/sound/AudioGenerator.kt b/src/main/kotlin/net/liquidev/dawd3/sound/AudioGenerator.kt new file mode 100644 index 0000000..5446ce1 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/sound/AudioGenerator.kt @@ -0,0 +1,27 @@ +package net.liquidev.dawd3.sound + +import net.liquidev.d3r.AudioOutputStream + +abstract class AudioGenerator : AudioOutputStream { + private var outputBuffer: FloatArray? = null + + private fun allocateOutputBuffer(sampleCount: Int): FloatArray { + val inOutputBuffer = outputBuffer + if (inOutputBuffer == null || inOutputBuffer.size < sampleCount) { + outputBuffer = FloatArray(sampleCount) + } + return outputBuffer!! + } + + abstract fun generate(output: FloatArray, sampleCount: Int, channels: Int) + + override fun getOutputBuffer(sampleCount: Int, channels: Int): FloatArray { + val outputBuffer = allocateOutputBuffer(sampleCount) + generate(outputBuffer, sampleCount, channels) + return outputBuffer + } + + override fun error(message: String) { + println("Error reported by audio stream: $message") + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/sound/SineOscGenerator.kt b/src/main/kotlin/net/liquidev/dawd3/sound/SineOscGenerator.kt new file mode 100644 index 0000000..f6d5240 --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/sound/SineOscGenerator.kt @@ -0,0 +1,21 @@ +package net.liquidev.dawd3.sound + +import kotlin.math.sin + +class SineOscGenerator(frequency: Float, private val amplitude: Float) : AudioGenerator() { + private val phaseStep = (1.0f / Sound.sampleRate.toFloat()) * frequency + private var phase = 0.0f + + private fun synthesize(): Float { + phase += phaseStep + phase %= 1.0f + return sin(phase * 2.0f * kotlin.math.PI.toFloat()) * amplitude + } + + override fun generate(output: FloatArray, sampleCount: Int, channels: Int) { + for (i in 0 until sampleCount) { + val sample = synthesize() + output[i] = sample + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/net/liquidev/dawd3/sound/Sound.kt b/src/main/kotlin/net/liquidev/dawd3/sound/Sound.kt new file mode 100644 index 0000000..18ef4cc --- /dev/null +++ b/src/main/kotlin/net/liquidev/dawd3/sound/Sound.kt @@ -0,0 +1,32 @@ +package net.liquidev.dawd3.sound + +import net.liquidev.d3r.D3r + +/** Common sound utilities. */ +object Sound { + const val sampleRate = 48000 + private const val bufferSize = 256 + + val outputDeviceId: Int + val outputStreamId: Int + + init { + D3r.openDefaultHost() + outputDeviceId = D3r.openDefaultOutputDevice() + outputStreamId = D3r.openOutputStream( + outputDeviceId, + sampleRate, + 1, + bufferSize, + SineOscGenerator(frequency = 440.0f, amplitude = 0.5f) + ) + D3r.startPlayback(outputStreamId) + } + + fun forceInitializationNow() {} + + fun deinitialize() { + D3r.closeOutputStream(outputStreamId) + D3r.closeOutputDevice(outputDeviceId) + } +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/blockstates/speaker.json b/src/main/resources/assets/dawd3/blockstates/speaker.json new file mode 100644 index 0000000..0016ef0 --- /dev/null +++ b/src/main/resources/assets/dawd3/blockstates/speaker.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "dawd3:block/speaker" }, + "facing=east": { "model": "dawd3:block/speaker", "y": 90 }, + "facing=south": { "model": "dawd3:block/speaker", "y": 180 }, + "facing=west": { "model": "dawd3:block/speaker", "y": 270 } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/icon.png b/src/main/resources/assets/dawd3/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..326ce941957559dad893d8a6911aaef0fb161400 GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tG7>k44ofy`glX=O&z>pc> z6XMFi!0`Y7e*s}}E_M|L6f2hHgi;Q(pCNf)4%eV|ewSweJIi#lj4R+*J&@ zuZ6vS`}y5AFqp%@usTSoR*SnpK;X0w!~X`Z|A*!Oe^&Ybc-iZn9ReaMN}4W4HnAQd zrPG$}zkKKA^^CL@1_lP@k|4iekd^=OgC!q=`xqD)JUv|;Lo9le6I2)^+(aiVZQ$fk zU@2oY&}^}3W0GKA&!pzWB{6eD^ud-#7Cx*#Eusz^8dTW%?N~EDMffP(=VhsFWbV;Y a2x4UD%3@h-T5)7C1IScYKbLh*2~7aMHF+oi literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/dawd3/lang/en_us.json b/src/main/resources/assets/dawd3/lang/en_us.json new file mode 100644 index 0000000..00510bb --- /dev/null +++ b/src/main/resources/assets/dawd3/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "item.dawd3.patch_cable": "Patch Cable" +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/models/block/module_with_front.json b/src/main/resources/assets/dawd3/models/block/module_with_front.json new file mode 100644 index 0000000..3636bf9 --- /dev/null +++ b/src/main/resources/assets/dawd3/models/block/module_with_front.json @@ -0,0 +1,21 @@ +{ + "parent": "block/block", + "textures": { + "side": "dawd3:block/module_side", + "particle": "#side" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 16, 16], + "faces": { + "down": { "texture": "#side", "cullface": "down" }, + "up": { "texture": "#side", "cullface": "up" }, + "north": { "texture": "#front", "cullface": "north" }, + "east": { "texture": "#side", "cullface": "east" }, + "south": { "texture": "#side", "cullface": "south" }, + "west": { "texture": "#side", "cullface": "west" } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/models/block/speaker.json b/src/main/resources/assets/dawd3/models/block/speaker.json new file mode 100644 index 0000000..7a6ef38 --- /dev/null +++ b/src/main/resources/assets/dawd3/models/block/speaker.json @@ -0,0 +1,6 @@ +{ + "parent": "dawd3:block/module_with_front", + "textures": { + "front": "dawd3:block/speaker_front" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/models/item/dawd3.json b/src/main/resources/assets/dawd3/models/item/dawd3.json new file mode 100644 index 0000000..254b4e4 --- /dev/null +++ b/src/main/resources/assets/dawd3/models/item/dawd3.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "dawd3:item/dawd3" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/models/item/patch_cable.json b/src/main/resources/assets/dawd3/models/item/patch_cable.json new file mode 100644 index 0000000..38bcc5a --- /dev/null +++ b/src/main/resources/assets/dawd3/models/item/patch_cable.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "dawd3:item/patch_cable" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/models/item/speaker.json b/src/main/resources/assets/dawd3/models/item/speaker.json new file mode 100644 index 0000000..5bbb1c4 --- /dev/null +++ b/src/main/resources/assets/dawd3/models/item/speaker.json @@ -0,0 +1,3 @@ +{ + "parent": "dawd3:block/speaker" +} \ No newline at end of file diff --git a/src/main/resources/assets/dawd3/textures/block/module_side.png b/src/main/resources/assets/dawd3/textures/block/module_side.png new file mode 100644 index 0000000000000000000000000000000000000000..f47fc1d829bc9b8f1d974c257ba2515e0c81032f GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z+mp_ z;uvDlo4lvy`P2W0m#;INz?^e-i$nz9{9VT`T)yKqjge>TRUV%wFI^e}nca9LOc|IY dG|!%6I4UP(cPID2VFm^U22WQ%mvv4FO#r@gDB1u3 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/dawd3/textures/block/speaker_front.png b/src/main/resources/assets/dawd3/textures/block/speaker_front.png new file mode 100644 index 0000000000000000000000000000000000000000..38806f8d6abe11d2296c5d995516bbe70787d813 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z~Jxc z;uvDlo4lvy`P2W0m#;INz?^e-i$nz9{9VT`T)yKqjge>TRUV%gN$=zA6}s9OMVRmH ztvjHgvdUq?u}lWTS?;k0ub6)AXpoSSGzgkCmvPpHV|MlPAI@N4U|{fc^>bP0l+XkK Dpk44ofy`glX=O&z_7s6 z#WBR9_h`^b-opkwlbOu9OB&uyez+;5E>?AZ&VjloH#TW*XXJ8eV3b$V3>4dGZBoU+ zFnz_UH+@1AjzkGCs5FQ%DX$b4otZQ%sA26oBSFVsjSCDy?IM0#NGrbC>3=Qf+*Ogr z{i2i2&oPHCefRD>>y5Y>_qpX>U+gwrx7(O;_ezWM;`47Ke;Lk8-K8nf_fzoStdmpN k?q|v9ewg$t{t@${)byH7yICz67#J8lUHx3vIVCg!0K+9%MgRZ+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/dawd3/textures/item/patch_cable.png b/src/main/resources/assets/dawd3/textures/item/patch_cable.png new file mode 100644 index 0000000000000000000000000000000000000000..0eec2b1894f305b2802ee7b10f4a576b6d09a175 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z_7v7 z#WBR9H#uQ}*p8a#PwQ(wt*GF2 z2=BSfdPJk8F`j*YvXq0)2M*1@_P)FN4k|AZF&67!taFUx*_IZ?Q|B1BLeIfJmWSs^ vHMc9{CdC~l@%ui1+P|*LBJiQB2m?c>vtRqW#E0Ds3=9mOu6{1-oD!M=${fabricloader}", + "fabric-api": ">=${fabric_api}", + "fabric-language-kotlin": ">=${fabric_language_kotlin}", + "minecraft": ">=${minecraft}", + "java": ">=${java}" + } +}