update
i suck at git. to whoever is reading this in the future, please excuse this absolutely terrible commit message and know that i'm doing my best. cheers & love. once dawd³ is in a better shape these commit messages will get better. for now they're terrible because the sheer amount of experimentation makes me forget that i have to `git commit` my work every so often.
This commit is contained in:
parent
367f2fc80e
commit
337c9d08e1
129 changed files with 2072 additions and 244 deletions
|
|
@ -55,6 +55,8 @@ loom {
|
|||
runDir("build/datagen")
|
||||
}
|
||||
}
|
||||
|
||||
accessWidenerPath.set(file("src/main/resources/dawd3.accesswidener"))
|
||||
}
|
||||
|
||||
sourceSets.named(mainSourceSet.name) {
|
||||
|
|
|
|||
BIN
proj/block/fader_bottom.ase
Normal file
BIN
proj/block/fader_bottom.ase
Normal file
Binary file not shown.
BIN
proj/block/fader_handle.ase
Normal file
BIN
proj/block/fader_handle.ase
Normal file
Binary file not shown.
BIN
proj/block/fader_side.ase
Normal file
BIN
proj/block/fader_side.ase
Normal file
Binary file not shown.
BIN
proj/block/fader_top.ase
Normal file
BIN
proj/block/fader_top.ase
Normal file
Binary file not shown.
BIN
proj/block/sine_oscillator_front.ase
Normal file
BIN
proj/block/sine_oscillator_front.ase
Normal file
Binary file not shown.
BIN
proj/block/sine_oscillator_side.ase
Normal file
BIN
proj/block/sine_oscillator_side.ase
Normal file
Binary file not shown.
BIN
proj/block/template/device_side.ase
Normal file
BIN
proj/block/template/device_side.ase
Normal file
Binary file not shown.
BIN
proj/cable_color_palette.ase
Normal file
BIN
proj/cable_color_palette.ase
Normal file
Binary file not shown.
BIN
proj/device/cable.ase
Normal file
BIN
proj/device/cable.ase
Normal file
Binary file not shown.
Binary file not shown.
BIN
proj/item/black_patch_cable.ase
Normal file
BIN
proj/item/black_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/blue_patch_cable.ase
Normal file
BIN
proj/item/blue_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/brown_patch_cable.ase
Normal file
BIN
proj/item/brown_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/cyan_patch_cable.ase
Normal file
BIN
proj/item/cyan_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/gray_patch_cable.ase
Normal file
BIN
proj/item/gray_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/green_patch_cable.ase
Normal file
BIN
proj/item/green_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/light_blue_patch_cable.ase
Normal file
BIN
proj/item/light_blue_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/light_gray_patch_cable.ase
Normal file
BIN
proj/item/light_gray_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/lime_patch_cable.ase
Normal file
BIN
proj/item/lime_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/magenta_patch_cable.ase
Normal file
BIN
proj/item/magenta_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/orange_patch_cable.ase
Normal file
BIN
proj/item/orange_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/pink_patch_cable.ase
Normal file
BIN
proj/item/pink_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/purple_patch_cable.ase
Normal file
BIN
proj/item/purple_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/red_patch_cable.ase
Normal file
BIN
proj/item/red_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/white_patch_cable.ase
Normal file
BIN
proj/item/white_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/yellow_patch_cable.ase
Normal file
BIN
proj/item/yellow_patch_cable.ase
Normal file
Binary file not shown.
|
|
@ -1,4 +1,26 @@
|
|||
// 1.19.2 2022-11-20T23:04:16.520652716 Models
|
||||
e3c6aacd49a6395f37047d3df31f91a18a411267 assets/dawd3/models/item/speaker.json
|
||||
// 1.19.2 2022-11-27T14:23:10.976127707 Models
|
||||
0812a674d14cfc6fbb7c0e2ac1b473bf2afe1965 assets/dawd3/models/item/brown_patch_cable.json
|
||||
bf0e322e33123cb6873c2da4e8c6ab85688deb4e assets/dawd3/models/item/gray_patch_cable.json
|
||||
6a9fb209d82556f5941422a8d047a0ae2af1dc8f assets/dawd3/models/item/sine_oscillator.json
|
||||
215b221d48639e96de11914625a770a389d65b81 assets/dawd3/models/item/green_patch_cable.json
|
||||
e34001d3c974aecfa347d435beb6bc0b9d325897 assets/dawd3/models/item/cyan_patch_cable.json
|
||||
8ba890b28c5ac57c59f19ccc8c72825caac10677 assets/dawd3/models/item/magenta_patch_cable.json
|
||||
f7b47538f17992177e97e06842c0039ae5096b2b assets/dawd3/blockstates/speaker.json
|
||||
bd0adfc8b3dc271042dd4b19a8cace4e0fffedfe assets/dawd3/models/block/speaker.json
|
||||
9cf2cff42345ec60a944d7399b5047323aa8e88c assets/dawd3/models/item/red_patch_cable.json
|
||||
8c6f0307320980a66c70622b0b7c72c8cfe78dc3 assets/dawd3/models/item/light_gray_patch_cable.json
|
||||
5ed33e9ec3c70e8cc027eea6425951d51487437f assets/dawd3/models/item/blue_patch_cable.json
|
||||
83866bdfd40b759257070558c9ccb942e082914a assets/dawd3/blockstates/fader.json
|
||||
a4e8bc89d39021eb8d56ad7735216cb851d67287 assets/dawd3/models/item/light_blue_patch_cable.json
|
||||
0023521ecb90e79515f0fa0088609c70aac9a605 assets/dawd3/models/block/speaker.json
|
||||
8aa966337109315240614d5257eb72f959eba5d8 assets/dawd3/models/item/orange_patch_cable.json
|
||||
12c4bfd825b2476955afcd3bb23c1f736ce68caa assets/dawd3/models/item/yellow_patch_cable.json
|
||||
aab1bce7ec4e7c7dccd3d33d4242de12b63a981d assets/dawd3/models/item/white_patch_cable.json
|
||||
6cdda36e539e23acf3db70d761338e88932a6ebb assets/dawd3/models/item/purple_patch_cable.json
|
||||
e3c6aacd49a6395f37047d3df31f91a18a411267 assets/dawd3/models/item/speaker.json
|
||||
32bb0e6e3bf75b9005602e8fb1042ad5d41286ad assets/dawd3/models/item/lime_patch_cable.json
|
||||
956d8f117df95cf62c8cac375cff853df96840d6 assets/dawd3/models/item/pink_patch_cable.json
|
||||
71fd99012b4338600f0a11dcadb9a97a6e0a15e5 assets/dawd3/models/block/fader.json
|
||||
3b1811bab3ba394ba03b67ac2efc72cb35316dc8 assets/dawd3/blockstates/sine_oscillator.json
|
||||
9c18a8292a0c9990cd23bebf5c6191c2114ccc6d assets/dawd3/models/item/black_patch_cable.json
|
||||
d300f52d5aa2dcb2ec7d9a0cdb1138104e016d83 assets/dawd3/models/item/fader.json
|
||||
0d759623578e206f437f2d9976443a773721a9f3 assets/dawd3/models/block/sine_oscillator.json
|
||||
|
|
|
|||
19
src/main/generated/assets/dawd3/blockstates/fader.json
Normal file
19
src/main/generated/assets/dawd3/blockstates/fader.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=east": {
|
||||
"model": "dawd3:block/fader",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north": {
|
||||
"model": "dawd3:block/fader"
|
||||
},
|
||||
"facing=south": {
|
||||
"model": "dawd3:block/fader",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west": {
|
||||
"model": "dawd3:block/fader",
|
||||
"y": 270
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"variants": {
|
||||
"facing=east": {
|
||||
"model": "dawd3:block/sine_oscillator",
|
||||
"y": 90
|
||||
},
|
||||
"facing=north": {
|
||||
"model": "dawd3:block/sine_oscillator"
|
||||
},
|
||||
"facing=south": {
|
||||
"model": "dawd3:block/sine_oscillator",
|
||||
"y": 180
|
||||
},
|
||||
"facing=west": {
|
||||
"model": "dawd3:block/sine_oscillator",
|
||||
"y": 270
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/main/generated/assets/dawd3/models/block/fader.json
Normal file
77
src/main/generated/assets/dawd3/models/block/fader.json
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"parent": "block/block",
|
||||
"elements": [
|
||||
{
|
||||
"faces": {
|
||||
"down": {
|
||||
"cullface": "down",
|
||||
"texture": "#bottom"
|
||||
},
|
||||
"east": {
|
||||
"cullface": "east",
|
||||
"texture": "#right"
|
||||
},
|
||||
"north": {
|
||||
"cullface": "north",
|
||||
"texture": "#front"
|
||||
},
|
||||
"south": {
|
||||
"cullface": "south",
|
||||
"texture": "#back"
|
||||
},
|
||||
"up": {
|
||||
"cullface": "up",
|
||||
"texture": "#top"
|
||||
},
|
||||
"west": {
|
||||
"cullface": "west",
|
||||
"texture": "#left"
|
||||
}
|
||||
},
|
||||
"from": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"to": [
|
||||
16.0,
|
||||
8.0,
|
||||
16.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"faces": {
|
||||
"north": {
|
||||
"cullface": "north",
|
||||
"texture": "#port",
|
||||
"uv": [
|
||||
4.0,
|
||||
0.0,
|
||||
8.0,
|
||||
4.0
|
||||
]
|
||||
}
|
||||
},
|
||||
"from": [
|
||||
6.0,
|
||||
3.0,
|
||||
-0.01
|
||||
],
|
||||
"to": [
|
||||
10.0,
|
||||
7.0,
|
||||
0.01
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures": {
|
||||
"back": "dawd3:block/fader_side",
|
||||
"bottom": "dawd3:block/fader_bottom",
|
||||
"front": "dawd3:block/fader_side",
|
||||
"left": "dawd3:block/fader_side",
|
||||
"particle": "dawd3:block/fader_side",
|
||||
"port": "dawd3:device/port",
|
||||
"right": "dawd3:block/fader_side",
|
||||
"top": "dawd3:block/fader_top"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"parent": "block/block",
|
||||
"elements": [
|
||||
{
|
||||
"faces": {
|
||||
"down": {
|
||||
"cullface": "down",
|
||||
"texture": "#bottom"
|
||||
},
|
||||
"east": {
|
||||
"cullface": "east",
|
||||
"texture": "#right"
|
||||
},
|
||||
"north": {
|
||||
"cullface": "north",
|
||||
"texture": "#front"
|
||||
},
|
||||
"south": {
|
||||
"cullface": "south",
|
||||
"texture": "#back"
|
||||
},
|
||||
"up": {
|
||||
"cullface": "up",
|
||||
"texture": "#top"
|
||||
},
|
||||
"west": {
|
||||
"cullface": "west",
|
||||
"texture": "#left"
|
||||
}
|
||||
},
|
||||
"from": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"to": [
|
||||
16.0,
|
||||
16.0,
|
||||
16.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"faces": {
|
||||
"north": {
|
||||
"cullface": "north",
|
||||
"texture": "#port",
|
||||
"uv": [
|
||||
0.0,
|
||||
0.0,
|
||||
4.0,
|
||||
4.0
|
||||
]
|
||||
}
|
||||
},
|
||||
"from": [
|
||||
9.0,
|
||||
6.0,
|
||||
-0.01
|
||||
],
|
||||
"to": [
|
||||
13.0,
|
||||
10.0,
|
||||
0.01
|
||||
]
|
||||
},
|
||||
{
|
||||
"faces": {
|
||||
"north": {
|
||||
"cullface": "north",
|
||||
"texture": "#port",
|
||||
"uv": [
|
||||
4.0,
|
||||
0.0,
|
||||
8.0,
|
||||
4.0
|
||||
]
|
||||
}
|
||||
},
|
||||
"from": [
|
||||
3.0,
|
||||
6.0,
|
||||
-0.01
|
||||
],
|
||||
"to": [
|
||||
7.0,
|
||||
10.0,
|
||||
0.01
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures": {
|
||||
"back": "dawd3:block/sine_oscillator_side",
|
||||
"bottom": "dawd3:block/sine_oscillator_side",
|
||||
"front": "dawd3:block/sine_oscillator_front",
|
||||
"left": "dawd3:block/sine_oscillator_side",
|
||||
"particle": "dawd3:block/sine_oscillator_side",
|
||||
"port": "dawd3:device/port",
|
||||
"right": "dawd3:block/sine_oscillator_side",
|
||||
"top": "dawd3:block/sine_oscillator_side"
|
||||
}
|
||||
}
|
||||
|
|
@ -5,11 +5,11 @@
|
|||
"faces": {
|
||||
"down": {
|
||||
"cullface": "down",
|
||||
"texture": "#side"
|
||||
"texture": "#bottom"
|
||||
},
|
||||
"east": {
|
||||
"cullface": "east",
|
||||
"texture": "#side"
|
||||
"texture": "#right"
|
||||
},
|
||||
"north": {
|
||||
"cullface": "north",
|
||||
|
|
@ -17,15 +17,15 @@
|
|||
},
|
||||
"south": {
|
||||
"cullface": "south",
|
||||
"texture": "#side"
|
||||
"texture": "#back"
|
||||
},
|
||||
"up": {
|
||||
"cullface": "up",
|
||||
"texture": "#side"
|
||||
"texture": "#top"
|
||||
},
|
||||
"west": {
|
||||
"cullface": "west",
|
||||
"texture": "#side"
|
||||
"texture": "#left"
|
||||
}
|
||||
},
|
||||
"from": [
|
||||
|
|
@ -53,21 +53,25 @@
|
|||
}
|
||||
},
|
||||
"from": [
|
||||
6.5,
|
||||
6.5,
|
||||
6.0,
|
||||
3.0,
|
||||
15.99
|
||||
],
|
||||
"to": [
|
||||
9.5,
|
||||
9.5,
|
||||
10.0,
|
||||
7.0,
|
||||
16.01
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures": {
|
||||
"back": "dawd3:block/speaker_side",
|
||||
"bottom": "dawd3:block/speaker_side",
|
||||
"front": "dawd3:block/speaker_front",
|
||||
"particle": "#side",
|
||||
"left": "dawd3:block/speaker_side",
|
||||
"particle": "dawd3:block/speaker_side",
|
||||
"port": "dawd3:device/port",
|
||||
"side": "dawd3:block/speaker_side"
|
||||
"right": "dawd3:block/speaker_side",
|
||||
"top": "dawd3:block/speaker_side"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/black_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/blue_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/brown_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/cyan_patch_cable"
|
||||
}
|
||||
}
|
||||
3
src/main/generated/assets/dawd3/models/item/fader.json
Normal file
3
src/main/generated/assets/dawd3/models/item/fader.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"parent": "dawd3:block/fader"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/gray_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/green_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/light_blue_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/light_gray_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/lime_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/magenta_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/orange_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/pink_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/purple_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/red_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"parent": "dawd3:block/sine_oscillator"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/white_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "dawd3:item/yellow_patch_cable"
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,9 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
|||
import net.liquidev.d3r.D3r
|
||||
import net.liquidev.dawd3.audio.Audio
|
||||
import net.liquidev.dawd3.block.Blocks
|
||||
import net.liquidev.dawd3.block.entity.registerBlockEntityEvents
|
||||
import net.liquidev.dawd3.block.entity.registerClientBlockEntityEvents
|
||||
import net.liquidev.dawd3.item.Items
|
||||
import net.liquidev.dawd3.net.Packets
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
|
|
@ -34,7 +35,9 @@ object Mod : ModInitializer, ClientModInitializer {
|
|||
D3r.unload()
|
||||
}
|
||||
|
||||
registerBlockEntityEvents()
|
||||
registerClientBlockEntityEvents()
|
||||
Blocks.initializeClient()
|
||||
Packets.registerClientReceivers()
|
||||
}
|
||||
|
||||
private fun loggerName(name: String?): String =
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package net.liquidev.dawd3.audio
|
|||
|
||||
import net.liquidev.d3r.D3r
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.generator.GeneratorWithProcessingState
|
||||
import net.liquidev.dawd3.audio.generator.MixGenerator
|
||||
import net.liquidev.dawd3.audio.unit.Decibels
|
||||
|
||||
/** Audio system and common settings. */
|
||||
object Audio {
|
||||
val logger = Mod.logger<Audio>()
|
||||
private val logger = Mod.logger<Audio>()
|
||||
|
||||
const val sampleRate = 48000
|
||||
const val sampleRateF = sampleRate.toFloat()
|
||||
|
|
@ -18,13 +18,15 @@ object Audio {
|
|||
private val outputStreamId: Int
|
||||
|
||||
val mixer = MixGenerator()
|
||||
private val processingStateAdapter = GeneratorWithProcessingState(mixer)
|
||||
val processingState get() = processingStateAdapter.processingState
|
||||
|
||||
init {
|
||||
logger.info("initializing")
|
||||
logger.info("${Decibels(-3.0f).toAmplitude()}")
|
||||
D3r.openDefaultHost()
|
||||
outputDeviceId = D3r.openDefaultOutputDevice()
|
||||
outputStreamId = D3r.openOutputStream(outputDeviceId, sampleRate, 1, bufferSize, mixer)
|
||||
outputStreamId =
|
||||
D3r.openOutputStream(outputDeviceId, sampleRate, 1, bufferSize, processingStateAdapter)
|
||||
D3r.startPlayback(outputStreamId)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ package net.liquidev.dawd3.audio.device
|
|||
interface Device {
|
||||
fun process(sampleCount: Int, channels: Int)
|
||||
|
||||
fun visitInputPorts(visit: (PortName, InputPort) -> Unit) {}
|
||||
fun visitOutputPorts(visit: (PortName, OutputPort) -> Unit) {}
|
||||
fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {}
|
||||
fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package net.liquidev.dawd3.audio.device
|
||||
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
interface DeviceDescriptor {
|
||||
val id: Identifier
|
||||
}
|
||||
|
|
@ -23,8 +23,15 @@ class DeviceInstance(val state: Device) {
|
|||
}
|
||||
}
|
||||
|
||||
fun process(sampleCount: Int, channels: Int) {
|
||||
state.process(sampleCount, channels)
|
||||
fun process(sampleCount: Int, channels: Int, processingState: ProcessingState) {
|
||||
if (this !in processingState.processedDevices) {
|
||||
println("processing $this")
|
||||
processingState.processedDevices.add(this)
|
||||
for ((_, port) in inputPortsByName) {
|
||||
port.connectedOutput?.owningDevice?.process(sampleCount, channels, processingState)
|
||||
}
|
||||
state.process(sampleCount, channels)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,45 @@
|
|||
package net.liquidev.dawd3.audio.device
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
|
||||
/** Device utility functions. */
|
||||
object Devices {
|
||||
private val logger = Mod.logger<Devices>()
|
||||
|
||||
data class InputAndOutputDevicePortPairs<T>(
|
||||
val outputDevice: T,
|
||||
val outputPort: OutputPortName,
|
||||
val inputDevice: T,
|
||||
val inputPort: InputPortName,
|
||||
)
|
||||
|
||||
fun <T> sortPortsByInputAndOutput(
|
||||
fromData: T,
|
||||
fromPort: PortName,
|
||||
toData: T,
|
||||
toPort: PortName,
|
||||
): InputAndOutputDevicePortPairs<T>? =
|
||||
when {
|
||||
fromPort is OutputPortName && toPort is InputPortName -> InputAndOutputDevicePortPairs(
|
||||
outputDevice = fromData,
|
||||
outputPort = fromPort,
|
||||
inputDevice = toData,
|
||||
inputPort = toPort,
|
||||
)
|
||||
fromPort is InputPortName && toPort is OutputPortName -> InputAndOutputDevicePortPairs(
|
||||
outputDevice = toData,
|
||||
outputPort = toPort,
|
||||
inputDevice = fromData,
|
||||
inputPort = fromPort,
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun makeConnection(
|
||||
from: DeviceInstance,
|
||||
outputPortName: PortName,
|
||||
to: DeviceInstance,
|
||||
inputPortName: PortName
|
||||
inputPortName: PortName,
|
||||
) {
|
||||
val outputPort = from.outputPortsByName[outputPortName]
|
||||
val inputPort = to.inputPortsByName[inputPortName]
|
||||
|
|
@ -20,34 +53,58 @@ object Devices {
|
|||
|
||||
inputPort.connectedOutput = outputPort
|
||||
outputPort.connectedInputs.add(inputPort)
|
||||
logger.debug("made connection between ports $outputPortName ($outputPort) and $inputPortName ($inputPort)")
|
||||
}
|
||||
|
||||
private fun severAllConnectionsInInputPort(inputPort: InputPort): Int {
|
||||
var total = 0
|
||||
val connectedOutput = inputPort.connectedOutput
|
||||
if (connectedOutput != null) {
|
||||
connectedOutput.connectedInputs.remove(inputPort)
|
||||
inputPort.connectedOutput = null
|
||||
total += 1
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
private fun severAllConnectionsInOutputPort(outputPort: OutputPort): Int {
|
||||
var total = 0
|
||||
for (port in outputPort.connectedInputs) {
|
||||
port.connectedOutput = null
|
||||
}
|
||||
total += outputPort.connectedInputs.size
|
||||
outputPort.connectedInputs.clear()
|
||||
return total
|
||||
}
|
||||
|
||||
/** Disconnects everything from the given port. Returns the number of ports disconnected. */
|
||||
fun severAllConnections(device: DeviceInstance, portName: PortName): Int {
|
||||
fun severAllConnectionsInPort(device: DeviceInstance, portName: PortName): Int {
|
||||
var total = 0
|
||||
|
||||
val inputPort = device.inputPortsByName[portName]
|
||||
if (inputPort != null) {
|
||||
val connectedOutput = inputPort.connectedOutput
|
||||
if (connectedOutput != null) {
|
||||
connectedOutput.connectedInputs.remove(inputPort)
|
||||
inputPort.connectedOutput = null
|
||||
total += 1
|
||||
}
|
||||
total += severAllConnectionsInInputPort(inputPort)
|
||||
}
|
||||
|
||||
val outputPort = device.outputPortsByName[portName]
|
||||
if (outputPort != null) {
|
||||
for (port in outputPort.connectedInputs) {
|
||||
port.connectedOutput = null
|
||||
}
|
||||
total += outputPort.connectedInputs.size
|
||||
outputPort.connectedInputs.clear()
|
||||
total += severAllConnectionsInOutputPort(outputPort)
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
fun severAllConnectionsInDevice(device: DeviceInstance): Int {
|
||||
var total = 0
|
||||
for ((_, inputPort) in device.inputPortsByName) {
|
||||
total += severAllConnectionsInInputPort(inputPort)
|
||||
}
|
||||
for ((_, outputPort) in device.outputPortsByName) {
|
||||
total += severAllConnectionsInOutputPort(outputPort)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
private fun noSuchPort(device: DeviceInstance, name: PortName): NoSuchPortException =
|
||||
NoSuchPortException("device $device does not have port with name $name")
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package net.liquidev.dawd3.audio.device
|
||||
|
||||
class ProcessingState {
|
||||
val processedDevices = mutableSetOf<DeviceInstance>()
|
||||
|
||||
fun reset() {
|
||||
processedDevices.clear()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package net.liquidev.dawd3.audio.device
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
|
||||
/**
|
||||
* Sim is the simulator for device graphs.
|
||||
*/
|
||||
class Sim {
|
||||
private companion object {
|
||||
val logger = Mod.logger<Sim>()
|
||||
}
|
||||
|
||||
private val stack = arrayListOf<DeviceInstance>()
|
||||
private val evalList = arrayListOf<DeviceInstance>()
|
||||
private val visited = arrayListOf<DeviceInstance>()
|
||||
|
||||
/**
|
||||
* Builds a simulation sequence: establishes the order in which devices in a graph have to be
|
||||
* processed so that we arrive at the input signals fed into the terminal.
|
||||
*/
|
||||
fun rebuild(terminal: DeviceInstance) {
|
||||
stack.add(terminal)
|
||||
|
||||
evalList.clear()
|
||||
while (stack.isNotEmpty()) {
|
||||
val device = stack.removeAt(stack.size - 1)
|
||||
evalList.add(device)
|
||||
for ((_, inputPort) in device.inputPortsByName) {
|
||||
val connectedOutputPort = inputPort.connectedOutput
|
||||
if (connectedOutputPort != null) {
|
||||
stack.add(connectedOutputPort.owningDevice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("rebuilt device graph; new evaluation order (reversed): $evalList")
|
||||
}
|
||||
|
||||
/** Process all devices in the built sequence. */
|
||||
fun simulate(sampleCount: Int, channels: Int) {
|
||||
for (device in evalList.asReversed()) {
|
||||
device.process(sampleCount, channels)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
package net.liquidev.dawd3.audio.device
|
||||
|
||||
import net.liquidev.dawd3.audio.AudioBuffer
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class InputPort {
|
||||
sealed class Port {
|
||||
lateinit var owningDevice: DeviceInstance
|
||||
}
|
||||
|
||||
class InputPort : Port() {
|
||||
private companion object {
|
||||
val emptyBuffer = AudioBuffer()
|
||||
}
|
||||
|
||||
var connectedOutput: OutputPort? = null
|
||||
lateinit var owningDevice: DeviceInstance
|
||||
|
||||
/**
|
||||
* Convenience function that returns an empty buffer if there is no connected port, or the
|
||||
|
|
@ -24,15 +28,76 @@ class InputPort {
|
|||
}
|
||||
}
|
||||
|
||||
class OutputPort(bufferCount: Int) {
|
||||
class OutputPort(bufferCount: Int) : Port() {
|
||||
init {
|
||||
require(bufferCount >= 1) { "output port must have at least one buffer" }
|
||||
}
|
||||
|
||||
val buffers = Array(bufferCount) { AudioBuffer() }
|
||||
val connectedInputs = hashSetOf<InputPort>()
|
||||
lateinit var owningDevice: DeviceInstance
|
||||
}
|
||||
|
||||
/** Marker class that port name markers should inherit from. */
|
||||
abstract class PortName
|
||||
enum class PortDirection {
|
||||
Input,
|
||||
Output,
|
||||
}
|
||||
|
||||
/** Marker interface that port name markers should inherit from. */
|
||||
sealed interface PortName {
|
||||
val id: Identifier
|
||||
val direction: PortDirection
|
||||
|
||||
companion object {
|
||||
private val registry = hashMapOf<Identifier, PortName>()
|
||||
|
||||
internal fun register(name: PortName) {
|
||||
assert(name.id !in registry) { "there must not be two ports with the same name" }
|
||||
registry[name.id] = name
|
||||
}
|
||||
|
||||
fun fromString(name: String): PortName? {
|
||||
return registry[Identifier(name)]
|
||||
}
|
||||
|
||||
internal fun idInDevice(deviceId: Identifier, name: String): Identifier =
|
||||
Identifier(deviceId.namespace, "${deviceId.path}/$name")
|
||||
}
|
||||
}
|
||||
|
||||
class InputPortName(override val id: Identifier) : PortName {
|
||||
override val direction = PortDirection.Input
|
||||
|
||||
init {
|
||||
PortName.register(this)
|
||||
}
|
||||
|
||||
constructor(
|
||||
parent: Identifier,
|
||||
name: String,
|
||||
) : this(PortName.idInDevice(parent, name))
|
||||
|
||||
override fun toString(): String = id.toString()
|
||||
}
|
||||
|
||||
class OutputPortName private constructor(
|
||||
override val id: Identifier,
|
||||
private val instanceOf: OutputPortName?,
|
||||
) : PortName {
|
||||
override val direction = PortDirection.Output
|
||||
|
||||
init {
|
||||
PortName.register(this)
|
||||
}
|
||||
|
||||
constructor(
|
||||
parent: Identifier,
|
||||
name: String,
|
||||
) : this(PortName.idInDevice(parent, name), instanceOf = null)
|
||||
|
||||
override fun toString(): String = id.toString()
|
||||
|
||||
fun makeInstanced(instanceName: String) =
|
||||
OutputPortName(PortName.idInDevice(id, instanceName), instanceOf = this)
|
||||
|
||||
fun resolveInstance() = instanceOf ?: this
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
package net.liquidev.dawd3.audio.devices
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.Device
|
||||
import net.liquidev.dawd3.audio.device.DeviceDescriptor
|
||||
import net.liquidev.dawd3.audio.device.OutputPort
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.audio.device.OutputPortName
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class ConstantDevice(var value: Float) : Device {
|
||||
object Output : PortName()
|
||||
companion object : DeviceDescriptor {
|
||||
override val id = Identifier(Mod.id, "constant")
|
||||
val outputPort = OutputPortName(id, "output")
|
||||
}
|
||||
|
||||
val output = OutputPort(bufferCount = 1)
|
||||
|
||||
|
|
@ -16,7 +22,7 @@ class ConstantDevice(var value: Float) : Device {
|
|||
}
|
||||
}
|
||||
|
||||
override fun visitOutputPorts(visit: (PortName, OutputPort) -> Unit) {
|
||||
visit(Output, output)
|
||||
override fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {
|
||||
visit(outputPort, output)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,22 @@
|
|||
package net.liquidev.dawd3.audio.devices
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.Audio
|
||||
import net.liquidev.dawd3.audio.device.Device
|
||||
import net.liquidev.dawd3.audio.device.InputPort
|
||||
import net.liquidev.dawd3.audio.device.OutputPort
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.audio.device.*
|
||||
import net.minecraft.util.Identifier
|
||||
import kotlin.math.sin
|
||||
|
||||
private const val twoPi = 2.0f * kotlin.math.PI.toFloat()
|
||||
|
||||
class SineOscillatorDevice : Device {
|
||||
object FrequencyCV : PortName()
|
||||
object Output : PortName()
|
||||
companion object : DeviceDescriptor {
|
||||
override val id = Identifier(Mod.id, "sine_oscillator")
|
||||
val frequencyCVPort = InputPortName(id, "frequency_cv")
|
||||
val outputPort = OutputPortName(id, "output")
|
||||
}
|
||||
|
||||
val frequencyCV = InputPort()
|
||||
val output = OutputPort(bufferCount = 1)
|
||||
private val frequencyCV = InputPort()
|
||||
private val output = OutputPort(bufferCount = 1)
|
||||
|
||||
private var phase = 0.0f
|
||||
|
||||
|
|
@ -29,11 +31,11 @@ class SineOscillatorDevice : Device {
|
|||
}
|
||||
}
|
||||
|
||||
override fun visitInputPorts(visit: (PortName, InputPort) -> Unit) {
|
||||
visit(FrequencyCV, frequencyCV)
|
||||
override fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {
|
||||
visit(frequencyCVPort, frequencyCV)
|
||||
}
|
||||
|
||||
override fun visitOutputPorts(visit: (PortName, OutputPort) -> Unit) {
|
||||
visit(Output, output)
|
||||
override fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {
|
||||
visit(outputPort, output)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,17 @@
|
|||
package net.liquidev.dawd3.audio.devices
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.Device
|
||||
import net.liquidev.dawd3.audio.device.DeviceDescriptor
|
||||
import net.liquidev.dawd3.audio.device.InputPort
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.audio.device.InputPortName
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class TerminalDevice : Device {
|
||||
object Input : PortName()
|
||||
companion object : DeviceDescriptor {
|
||||
override val id = Identifier(Mod.id, "terminal")
|
||||
val inputPort = InputPortName(id, "input")
|
||||
}
|
||||
|
||||
val input = InputPort()
|
||||
|
||||
|
|
@ -15,7 +21,7 @@ class TerminalDevice : Device {
|
|||
// speakers.
|
||||
}
|
||||
|
||||
override fun visitInputPorts(visit: (PortName, InputPort) -> Unit) {
|
||||
visit(Input, input)
|
||||
override fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {
|
||||
visit(inputPort, input)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,19 @@
|
|||
package net.liquidev.dawd3.audio.generator
|
||||
|
||||
import net.liquidev.dawd3.audio.Audio
|
||||
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||
import net.liquidev.dawd3.audio.device.Sim
|
||||
import net.liquidev.dawd3.audio.devices.TerminalDevice
|
||||
|
||||
/** Audio generator that evaluates a device graph. */
|
||||
class DeviceGraphGenerator : AudioGenerator() {
|
||||
private val sim = Sim()
|
||||
private val terminalDeviceState = TerminalDevice()
|
||||
val terminalDevice = DeviceInstance(terminalDeviceState)
|
||||
|
||||
/**
|
||||
* This should be called whenever the device graph changes to reestablish what order devices
|
||||
* should be processed in.
|
||||
*/
|
||||
fun rebuildSim() {
|
||||
sim.rebuild(terminalDevice)
|
||||
}
|
||||
|
||||
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
||||
sim.simulate(sampleCount, channelCount)
|
||||
// TODO: Maybe passing in the static processingState here is not the cleanest way to go
|
||||
// about things, but I don't see how we could inject that context into this function
|
||||
// without jumping through significant hoops.
|
||||
terminalDevice.process(sampleCount, channelCount, Audio.processingState)
|
||||
val buffer = terminalDeviceState.input.getConnectedOutputBuffer(0, sampleCount)
|
||||
for (i in 0 until sampleCount) {
|
||||
output[i] = buffer[i]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package net.liquidev.dawd3.audio.generator
|
||||
|
||||
import net.liquidev.dawd3.audio.device.ProcessingState
|
||||
|
||||
/**
|
||||
* Adapter for adding device ProcessingState into an existing generator.
|
||||
* The processing state is reset with every call to generate().
|
||||
*/
|
||||
class GeneratorWithProcessingState(val inner: AudioGenerator) : AudioGenerator() {
|
||||
val processingState = ProcessingState()
|
||||
|
||||
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
||||
inner.generate(output, sampleCount, channelCount)
|
||||
processingState.reset()
|
||||
}
|
||||
}
|
||||
|
|
@ -11,19 +11,19 @@ class MixGenerator : AudioGenerator() {
|
|||
val logger = Mod.logger<MixGenerator>()
|
||||
}
|
||||
|
||||
private val taskQueue = TaskQueue()
|
||||
private val taskQueue = TaskQueue<Unit, Unit>()
|
||||
private val channels = arrayListOf<WeakReference<Channel<AudioGenerator>>>()
|
||||
|
||||
fun <T : AudioGenerator> createChannel(generator: T): Channel<T> {
|
||||
val channel = Channel(taskQueue, generator)
|
||||
val weak = WeakReference(channel as Channel<AudioGenerator>)
|
||||
taskQueue.execute { channels.add(weak) }
|
||||
taskQueue.enqueue { channels.add(weak) }
|
||||
return channel
|
||||
}
|
||||
|
||||
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
||||
// Flush task queue as soon as possible to reduce latency.
|
||||
taskQueue.flush()
|
||||
taskQueue.flush(Unit)
|
||||
reapStoppedChannels()
|
||||
|
||||
for (i in 0 until sampleCount) {
|
||||
|
|
@ -61,7 +61,7 @@ class MixGenerator : AudioGenerator() {
|
|||
}
|
||||
|
||||
class Channel<out T : AudioGenerator>(
|
||||
private val taskQueue: TaskQueue,
|
||||
private val taskQueue: TaskQueue<Unit, Unit>,
|
||||
val generator: T,
|
||||
) {
|
||||
private companion object {
|
||||
|
|
@ -75,7 +75,7 @@ class MixGenerator : AudioGenerator() {
|
|||
|
||||
/** Shuts down the channel on the next audio generation request. */
|
||||
fun stop() {
|
||||
taskQueue.execute { playing = false }
|
||||
taskQueue.enqueue { playing = false }
|
||||
}
|
||||
|
||||
fun finalize() {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package net.liquidev.dawd3.audio.generator
|
||||
|
||||
import net.liquidev.dawd3.audio.Audio
|
||||
import net.liquidev.dawd3.audio.unit.Amplitude
|
||||
import kotlin.math.sin
|
||||
|
||||
class SineOscGenerator(frequency: Float, private val amplitude: Amplitude) : AudioGenerator() {
|
||||
class SineOscGenerator(frequency: Float) : AudioGenerator() {
|
||||
private val phaseStep = (1.0f / Audio.sampleRate.toFloat()) * frequency
|
||||
private var phase = 0.0f
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
package net.liquidev.dawd3.block
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry
|
||||
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.block.device.AnyDeviceBlockDescriptor
|
||||
import net.liquidev.dawd3.block.device.DeviceBlock
|
||||
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||
import net.liquidev.dawd3.block.device.DeviceBlockEntityRenderer
|
||||
import net.liquidev.dawd3.block.devices.FaderBlockDescriptor
|
||||
import net.liquidev.dawd3.block.devices.SineOscillatorBlockDescriptor
|
||||
import net.liquidev.dawd3.block.devices.SpeakerBlockDescriptor
|
||||
import net.liquidev.dawd3.item.Items
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.Material
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.item.BlockItem
|
||||
import net.minecraft.util.Identifier
|
||||
|
|
@ -43,8 +50,27 @@ object Blocks {
|
|||
return registeredDeviceBlock
|
||||
}
|
||||
|
||||
// To not have to mess with rendering the vertices ourselves we just use a baked model that's
|
||||
// represented as a block.
|
||||
val patchCablePlug = Registry.register(
|
||||
Registry.BLOCK,
|
||||
Identifier(Mod.id, "patch_cable_plug"),
|
||||
Block(FabricBlockSettings.of(Material.METAL))
|
||||
)
|
||||
|
||||
// Device blocks
|
||||
val speaker = registerDeviceBlock(SpeakerBlockDescriptor)
|
||||
val sineOscillator = registerDeviceBlock(SineOscillatorBlockDescriptor)
|
||||
val fader = registerDeviceBlock(FaderBlockDescriptor)
|
||||
|
||||
fun initialize() {}
|
||||
|
||||
fun initializeClient() {
|
||||
for ((_, deviceBlock) in deviceBlocks) {
|
||||
BlockEntityRendererRegistry.register(
|
||||
deviceBlock.blockEntity,
|
||||
::DeviceBlockEntityRenderer
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,70 @@
|
|||
package net.liquidev.dawd3.block.device
|
||||
|
||||
import net.liquidev.dawd3.common.*
|
||||
import net.liquidev.dawd3.item.PatchCableItem
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec3f
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
|
||||
class DeviceBlock(private val descriptor: AnyDeviceBlockDescriptor) :
|
||||
BlockWithEntity(descriptor.blockSettings),
|
||||
BlockEntityProvider {
|
||||
|
||||
val outlineCuboids = HorizontalDirection.values().map { direction ->
|
||||
val cuboid = descriptor.cuboid
|
||||
val centerOrigin = Vec3f(0.5f, 0.5f, 0.5f)
|
||||
val from = cuboid.fromF - centerOrigin
|
||||
val to = cuboid.toF - centerOrigin
|
||||
Cuboids.newF(direction.rotateY(from) + centerOrigin, direction.rotateY(to) + centerOrigin)
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(Properties.HORIZONTAL_FACING)
|
||||
}
|
||||
|
||||
override fun getPlacementState(context: ItemPlacementContext): BlockState =
|
||||
defaultState.with(Properties.HORIZONTAL_FACING, context.playerFacing.opposite)
|
||||
defaultState.with(
|
||||
Properties.HORIZONTAL_FACING,
|
||||
context.playerFacing.opposite
|
||||
)
|
||||
|
||||
@Deprecated("do not call directly")
|
||||
@Deprecated("do not call this function directly")
|
||||
override fun getRenderType(state: BlockState): BlockRenderType =
|
||||
BlockRenderType.MODEL
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity =
|
||||
DeviceBlockEntity.factory(descriptor).create(pos, state)
|
||||
|
||||
@Deprecated("do not call this function directly")
|
||||
override fun onStateReplaced(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
position: BlockPos,
|
||||
newState: BlockState,
|
||||
moved: Boolean,
|
||||
) {
|
||||
PatchCableItem.onBlockDestroyed(position)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
super.onStateReplaced(state, world, position, newState, moved)
|
||||
}
|
||||
|
||||
@Deprecated("do not call this function directly")
|
||||
override fun getOutlineShape(
|
||||
state: BlockState,
|
||||
world: BlockView,
|
||||
pos: BlockPos,
|
||||
context: ShapeContext,
|
||||
): VoxelShape {
|
||||
val direction = HorizontalDirection.fromDirection(state[Properties.HORIZONTAL_FACING])!!
|
||||
return VoxelShapes.cuboid(outlineCuboids[direction.index])
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,20 @@
|
|||
package net.liquidev.dawd3.block.device
|
||||
|
||||
import FaceTextures
|
||||
import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings
|
||||
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||
import net.liquidev.dawd3.common.Cuboids
|
||||
import net.minecraft.block.AbstractBlock
|
||||
import net.minecraft.block.Material
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Box
|
||||
|
||||
typealias AnyDeviceBlockDescriptor = DeviceBlockDescriptor<DeviceBlockDescriptor.ClientState, Any>
|
||||
|
||||
interface DeviceBlockDescriptor<out CS : DeviceBlockDescriptor.ClientState, out ServerState> {
|
||||
val id: Identifier
|
||||
|
||||
val blockSettings: AbstractBlock.Settings
|
||||
// The default block settings are used for metal-enclosed modules.
|
||||
get() = FabricBlockSettings
|
||||
|
|
@ -18,7 +22,12 @@ interface DeviceBlockDescriptor<out CS : DeviceBlockDescriptor.ClientState, out
|
|||
.hardness(5.0f)
|
||||
.resistance(6.0f)
|
||||
|
||||
val portLayout: Array<PhysicalPort>
|
||||
/** The cuboid that the main part of the block should fill. */
|
||||
val cuboid: Box
|
||||
get() = Cuboids.fullBlock
|
||||
val portLayout: PhysicalPortLayout
|
||||
val faceTextures: FaceTextures
|
||||
get() = FaceTextures.withFrontAndSide { id }
|
||||
|
||||
fun onClientLoad(world: ClientWorld): CS
|
||||
fun onClientUnload(state: @UnsafeVariance CS, world: ClientWorld) {}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
package net.liquidev.dawd3.block.device
|
||||
|
||||
import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.Devices
|
||||
import net.liquidev.dawd3.audio.device.InputPortName
|
||||
import net.liquidev.dawd3.audio.device.OutputPortName
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.block.Blocks
|
||||
import net.liquidev.dawd3.block.entity.D3BlockEntity
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtElement
|
||||
import net.minecraft.nbt.NbtHelper
|
||||
import net.minecraft.nbt.NbtList
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
private typealias DeviceBlockFactory = FabricBlockEntityTypeBuilder.Factory<DeviceBlockEntity>
|
||||
|
|
@ -14,19 +23,64 @@ class DeviceBlockEntity(
|
|||
type: BlockEntityType<DeviceBlockEntity>,
|
||||
blockPos: BlockPos,
|
||||
blockState: BlockState,
|
||||
private val descriptor: AnyDeviceBlockDescriptor,
|
||||
val descriptor: AnyDeviceBlockDescriptor,
|
||||
) : D3BlockEntity(type, blockPos, blockState) {
|
||||
companion object {
|
||||
fun factory(descriptor: AnyDeviceBlockDescriptor): DeviceBlockFactory =
|
||||
DeviceBlockFactory { blockPos, blockState ->
|
||||
val type by lazy { Blocks.deviceBlocks[descriptor.id]!!.blockEntity }
|
||||
DeviceBlockEntity(type, blockPos, blockState, descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private var clientState: DeviceBlockDescriptor.ClientState? = null
|
||||
private var serverState: Any? = null
|
||||
|
||||
internal data class InputConnection(
|
||||
val blockPosition: BlockPos,
|
||||
val outputPortName: OutputPortName,
|
||||
val color: Byte,
|
||||
)
|
||||
|
||||
internal val inputConnections = hashMapOf<InputPortName, InputConnection>()
|
||||
|
||||
override fun readNbt(nbt: NbtCompound) {
|
||||
super.readNbt(nbt)
|
||||
|
||||
val nbtConnections = nbt.getList("connections", NbtElement.COMPOUND_TYPE.toInt())
|
||||
|
||||
for (i in 0 until nbtConnections.size) {
|
||||
val connectionNbt = nbtConnections.getCompound(i)
|
||||
|
||||
val outputString = connectionNbt.getString("output")
|
||||
val output = PortName.fromString(outputString) ?: continue
|
||||
if (output !is OutputPortName) {
|
||||
logger.error("NBT declares 'output' field that refers to an input port")
|
||||
continue
|
||||
}
|
||||
|
||||
val inputString = connectionNbt.getString("input")
|
||||
val input = PortName.fromString(inputString) ?: continue
|
||||
if (input !is InputPortName) {
|
||||
logger.error("NBT declares 'input' field that refers to an output port")
|
||||
continue
|
||||
}
|
||||
|
||||
val blockPosition = NbtHelper.toBlockPos(connectionNbt.getCompound("position"))
|
||||
val color = connectionNbt.getByte("color")
|
||||
|
||||
inputConnections[input] = InputConnection(blockPosition, outputPortName = output, color)
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeNbt(nbt: NbtCompound) {
|
||||
super.writeNbt(nbt)
|
||||
|
||||
val connectionsNbt = NbtList()
|
||||
for ((inputPortName, connection) in inputConnections) {
|
||||
val connectionNbt = NbtCompound()
|
||||
connectionNbt.putString("output", connection.outputPortName.toString())
|
||||
connectionNbt.putString("input", inputPortName.toString())
|
||||
connectionNbt.put("position", NbtHelper.fromBlockPos(connection.blockPosition))
|
||||
connectionNbt.putByte("color", connection.color)
|
||||
connectionsNbt.add(connectionNbt)
|
||||
}
|
||||
nbt.put("connections", connectionsNbt)
|
||||
}
|
||||
|
||||
override fun onClientLoad(world: ClientWorld) {
|
||||
clientState = descriptor.onClientLoad(world)
|
||||
}
|
||||
|
|
@ -35,6 +89,72 @@ class DeviceBlockEntity(
|
|||
val clientState = clientState
|
||||
if (clientState != null) {
|
||||
descriptor.onClientUnload(clientState, world)
|
||||
Devices.severAllConnectionsInDevice(clientState.logicalDevice)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun reapInvalidConnections() {
|
||||
inputConnections.keys.removeAll {
|
||||
descriptor.portLayout[it] == null
|
||||
}
|
||||
val world = world
|
||||
if (world != null) {
|
||||
inputConnections.values.removeAll {
|
||||
world.getBlockEntity(it.blockPosition) !is DeviceBlockEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val logger = Mod.logger<DeviceBlockEntity>()
|
||||
|
||||
fun factory(descriptor: AnyDeviceBlockDescriptor): DeviceBlockFactory =
|
||||
DeviceBlockFactory { blockPos, blockState ->
|
||||
val type by lazy { Blocks.deviceBlocks[descriptor.id]!!.blockEntity }
|
||||
DeviceBlockEntity(type, blockPos, blockState, descriptor)
|
||||
}
|
||||
|
||||
fun connectLogicalDevices(
|
||||
fromBlockEntity: DeviceBlockEntity,
|
||||
fromPort: PortName,
|
||||
toBlockEntity: DeviceBlockEntity,
|
||||
toPort: PortName,
|
||||
cableColor: Byte,
|
||||
) {
|
||||
val (outputBlockEntity, outputPort, inputBlockEntity, inputPort) = Devices.sortPortsByInputAndOutput(
|
||||
fromBlockEntity,
|
||||
fromPort,
|
||||
toBlockEntity,
|
||||
toPort
|
||||
) ?: throw PortDirectionException("connected ports must be of opposing directions")
|
||||
val outputDevice = outputBlockEntity.clientState?.logicalDevice ?: return
|
||||
val inputDevice = inputBlockEntity.clientState?.logicalDevice ?: return
|
||||
inputBlockEntity.inputConnections[inputPort] =
|
||||
InputConnection(outputBlockEntity.pos, outputPort, cableColor)
|
||||
Devices.makeConnection(
|
||||
outputDevice,
|
||||
outputPort.resolveInstance(),
|
||||
inputDevice,
|
||||
inputPort
|
||||
)
|
||||
}
|
||||
|
||||
fun connectPhysicalDevices(
|
||||
fromBlockEntity: DeviceBlockEntity,
|
||||
fromPort: PortName,
|
||||
toBlockEntity: DeviceBlockEntity,
|
||||
toPort: PortName,
|
||||
cableColor: Byte,
|
||||
) {
|
||||
val (outputBlockEntity, outputPort, inputBlockEntity, inputPort) = Devices.sortPortsByInputAndOutput(
|
||||
fromBlockEntity,
|
||||
fromPort,
|
||||
toBlockEntity,
|
||||
toPort,
|
||||
) ?: throw PortDirectionException("connected ports must be of opposing directions")
|
||||
inputBlockEntity.inputConnections[inputPort] =
|
||||
InputConnection(outputBlockEntity.pos, outputPort, cableColor)
|
||||
outputBlockEntity.markDirty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
package net.liquidev.dawd3.block.device
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.block.Blocks
|
||||
import net.liquidev.dawd3.common.*
|
||||
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||
import net.liquidev.dawd3.item.PatchCableItem
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.*
|
||||
import net.minecraft.client.render.RenderLayer.MultiPhaseParameters
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory
|
||||
import net.minecraft.client.util.SpriteIdentifier
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.screen.PlayerScreenHandler
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Quaternion
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.math.Vec3f
|
||||
import net.minecraft.world.World
|
||||
import java.util.function.Function
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
|
||||
class DeviceBlockEntityRenderer(context: BlockEntityRendererFactory.Context) : BlockEntityRenderer<DeviceBlockEntity> {
|
||||
private companion object {
|
||||
val logger = Mod.logger<DeviceBlockEntityRenderer>()
|
||||
|
||||
val renderLayer: RenderLayer = RenderLayer.of(
|
||||
"solid",
|
||||
VertexFormats.POSITION_COLOR_TEXTURE_LIGHT,
|
||||
VertexFormat.DrawMode.TRIANGLE_STRIP,
|
||||
2048,
|
||||
MultiPhaseParameters.builder()
|
||||
.lightmap(RenderPhase.ENABLE_LIGHTMAP)
|
||||
.shader(RenderPhase.SOLID_SHADER)
|
||||
.texture(RenderPhase.BLOCK_ATLAS_TEXTURE)
|
||||
.cull(RenderPhase.DISABLE_CULLING)
|
||||
.build(true)
|
||||
)
|
||||
val renderLayerFactory = Function<Identifier, RenderLayer> { renderLayer }
|
||||
|
||||
const val cableThickness = 0.03f
|
||||
const val cableSegmentCount = 6
|
||||
const val cableSag = 0.2f
|
||||
val cableColorsSprite = SpriteIdentifier(
|
||||
PlayerScreenHandler.BLOCK_ATLAS_TEXTURE,
|
||||
Identifier(Mod.id, "device/cable")
|
||||
)
|
||||
}
|
||||
|
||||
private val blockRenderer = context.renderManager
|
||||
private val patchCablePlug = Blocks.patchCablePlug.defaultState
|
||||
|
||||
override fun rendersOutsideBoundingBox(blockEntity: DeviceBlockEntity): Boolean {
|
||||
// TODO: This needs to return true if the block entity has cables attached.
|
||||
return true
|
||||
}
|
||||
|
||||
override fun render(
|
||||
blockEntity: DeviceBlockEntity,
|
||||
tickDelta: Float,
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
light: Int,
|
||||
overlay: Int,
|
||||
) {
|
||||
val world = blockEntity.world ?: return
|
||||
val blockState = world.getBlockState(blockEntity.pos)
|
||||
if (blockState.block !is DeviceBlock) {
|
||||
// render() gets called for a frame while the block is still air after it's destroyed.
|
||||
return
|
||||
}
|
||||
|
||||
renderPlugsInsidePorts(
|
||||
world,
|
||||
blockState,
|
||||
blockEntity,
|
||||
matrixStack,
|
||||
vertexConsumers,
|
||||
overlay
|
||||
)
|
||||
}
|
||||
|
||||
private fun rotateToFaceFront(
|
||||
blockState: BlockState,
|
||||
matrices: MatrixStack,
|
||||
) {
|
||||
// Choose center as the transform origin. That way we can rotate by kπ/2 to get all the
|
||||
// possible horizontal orientations of the block.
|
||||
matrices.translate(0.5, 0.0, 0.5)
|
||||
val facing = HorizontalDirection.fromDirection(blockState[Properties.HORIZONTAL_FACING])!!
|
||||
matrices.multiply(Quaternion.fromEulerXyz(0f, -facing.angle, 0f))
|
||||
}
|
||||
|
||||
private fun renderPlugsInsidePorts(
|
||||
world: World,
|
||||
blockState: BlockState,
|
||||
blockEntity: DeviceBlockEntity,
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
overlay: Int,
|
||||
) {
|
||||
val facing = HorizontalDirection.fromDirection(blockState[Properties.HORIZONTAL_FACING])!!
|
||||
|
||||
matrixStack.push()
|
||||
rotateToFaceFront(blockState, matrixStack)
|
||||
for ((_, connection) in PatchCableItem.ongoingConnections) {
|
||||
if (connection.blockPosition == blockEntity.pos) {
|
||||
val port = blockEntity.descriptor.portLayout[connection.portName]
|
||||
if (port != null) {
|
||||
renderPlugInsidePort(
|
||||
world, blockEntity, facing, port, matrixStack, vertexConsumers, overlay
|
||||
)
|
||||
} else {
|
||||
logger.error("ongoing connection references port name '${connection.portName.id}' which does not exist on the block entity")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: There's probably a better place to call this.
|
||||
// Maybe we should actually not call this at all here?
|
||||
// I'm worried that for a frame a device could be something other than DeviceBlockEntity
|
||||
// and we'll get plugs without cables rendering permanently. Which would be bad.
|
||||
blockEntity.reapInvalidConnections()
|
||||
|
||||
for ((portName, _) in blockEntity.inputConnections) {
|
||||
val inputPort = blockEntity.descriptor.portLayout[portName]!!
|
||||
renderPlugInsidePort(
|
||||
world, blockEntity, facing, inputPort, matrixStack, vertexConsumers, overlay
|
||||
)
|
||||
|
||||
}
|
||||
matrixStack.pop()
|
||||
|
||||
for ((_, connection) in blockEntity.inputConnections) {
|
||||
val outputBlockEntity =
|
||||
world.getBlockEntity(connection.blockPosition) as DeviceBlockEntity
|
||||
val outputBlockState = world.getBlockState(connection.blockPosition)
|
||||
val outputPort = outputBlockEntity.descriptor.portLayout[connection.outputPortName]
|
||||
if (outputPort != null) {
|
||||
val delta = (outputBlockEntity.pos - blockEntity.pos).toVec3d()
|
||||
matrixStack.push()
|
||||
matrixStack.translate(delta.x, delta.y, delta.z)
|
||||
rotateToFaceFront(outputBlockState, matrixStack)
|
||||
renderPlugInsidePort(
|
||||
world,
|
||||
outputBlockEntity,
|
||||
facing,
|
||||
outputPort,
|
||||
matrixStack,
|
||||
vertexConsumers,
|
||||
overlay
|
||||
)
|
||||
matrixStack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
for ((inputPortName, connection) in blockEntity.inputConnections) {
|
||||
val startAbsolute = blockEntity.pos.toVec3d()
|
||||
val endAbsolute = connection.blockPosition.toVec3d()
|
||||
|
||||
// TODO: Null checks here?
|
||||
val inputPort = blockEntity.descriptor.portLayout[inputPortName]!!
|
||||
val outputBlockState = world.getBlockState(connection.blockPosition)
|
||||
// TODO: The line below will crash if the block is destroyed (becomes air)
|
||||
// Though it never crashed for me during testing?
|
||||
val outputBlockFacing =
|
||||
HorizontalDirection.fromDirection(outputBlockState[Properties.HORIZONTAL_FACING])!!
|
||||
val outputBlockEntity =
|
||||
world.getBlockEntity(connection.blockPosition) as DeviceBlockEntity
|
||||
val outputPort = outputBlockEntity.descriptor.portLayout[connection.outputPortName]!!
|
||||
|
||||
val protrusionAmount = 0.69f // nice
|
||||
val start = inputPort.blockCablePosition(facing, protrusionAmount)
|
||||
val end = outputPort.blockCablePosition(outputBlockFacing, protrusionAmount)
|
||||
val delta = (endAbsolute - startAbsolute).toVec3f() + end
|
||||
|
||||
renderCable(
|
||||
world,
|
||||
matrixStack,
|
||||
vertexConsumers,
|
||||
worldFrom = blockEntity.pos.toVec3d(),
|
||||
start,
|
||||
delta,
|
||||
connection.color.toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFacingInWorldSpace(
|
||||
blockFacing: HorizontalDirection,
|
||||
portFacing: HorizontalDirection,
|
||||
) =
|
||||
blockFacing + portFacing - HorizontalDirection.North
|
||||
|
||||
private fun renderPlugInsidePort(
|
||||
world: World,
|
||||
blockEntity: DeviceBlockEntity,
|
||||
blockFacing: HorizontalDirection,
|
||||
port: PhysicalPort,
|
||||
matrices: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
overlay: Int,
|
||||
) {
|
||||
val positionOnSide =
|
||||
(DeviceBlockModel.relativeToAbsolutePortPosition(port.position) + DeviceBlockModel.portSize / 2f) / 16f
|
||||
val face = port.side.face
|
||||
matrices.push()
|
||||
matrices.multiply(
|
||||
Quaternion.fromEulerXyz(0f, face.angle, 0f)
|
||||
)
|
||||
matrices.translate(1f - positionOnSide.x.toDouble(), 1f - positionOnSide.y.toDouble(), 0.0)
|
||||
|
||||
val facing = getFacingInWorldSpace(blockFacing, face)
|
||||
val light =
|
||||
WorldRenderer.getLightmapCoordinates(world, blockEntity.pos.add(facing.vector))
|
||||
blockRenderer.renderBlockAsEntity(
|
||||
patchCablePlug, matrices, vertexConsumers, light, overlay
|
||||
)
|
||||
|
||||
matrices.pop()
|
||||
}
|
||||
|
||||
private fun catenary(sag: Float, t: Float): Float {
|
||||
// Not an actual catenary, but close enough.
|
||||
val u = 2f * t - 1f
|
||||
return sag * (u * u - 1f)
|
||||
}
|
||||
|
||||
private fun renderCable(
|
||||
world: World,
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
worldFrom: Vec3d,
|
||||
from: Vec3f,
|
||||
to: Vec3f,
|
||||
colorIndex: Float,
|
||||
) {
|
||||
val forward = to - from
|
||||
val right = forward.copy()
|
||||
right.cross(Vec3f.POSITIVE_Y)
|
||||
right.normalize()
|
||||
right.multiplyComponentwise(cableThickness, cableThickness, cableThickness)
|
||||
val up = right.copy()
|
||||
up.cross(forward)
|
||||
up.normalize()
|
||||
up.multiplyComponentwise(cableThickness, cableThickness, cableThickness)
|
||||
renderCableWithThicknessVector(
|
||||
world,
|
||||
matrixStack,
|
||||
vertexConsumers,
|
||||
worldFrom,
|
||||
from,
|
||||
to,
|
||||
thicknessVector = up,
|
||||
colorIndex
|
||||
)
|
||||
renderCableWithThicknessVector(
|
||||
world,
|
||||
matrixStack,
|
||||
vertexConsumers,
|
||||
worldFrom,
|
||||
from,
|
||||
to,
|
||||
thicknessVector = right,
|
||||
colorIndex
|
||||
)
|
||||
}
|
||||
|
||||
private fun renderCableWithThicknessVector(
|
||||
world: World,
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
worldFrom: Vec3d,
|
||||
from: Vec3f,
|
||||
to: Vec3f,
|
||||
thicknessVector: Vec3f,
|
||||
colorIndex: Float,
|
||||
) {
|
||||
val vertexBuffer = cableColorsSprite.getVertexConsumer(
|
||||
vertexConsumers,
|
||||
renderLayerFactory
|
||||
) as SpriteTexturedVertexConsumer
|
||||
val matrices = matrixStack.peek()
|
||||
// ↑ Cast needed due to different interface method resolution rules in Kotlin.
|
||||
// See comment in addCableSegment.
|
||||
|
||||
val uLeft = colorIndex / 16f
|
||||
val uRight = (colorIndex + 1) / 16f
|
||||
|
||||
addCableSegment(
|
||||
world,
|
||||
matrices,
|
||||
vertexBuffer,
|
||||
worldFrom.x,
|
||||
worldFrom.y,
|
||||
worldFrom.z,
|
||||
from.x,
|
||||
from.y,
|
||||
from.z,
|
||||
thicknessVector,
|
||||
uLeft,
|
||||
uRight,
|
||||
v = 0f,
|
||||
)
|
||||
|
||||
val sag = cableSag + abs(to.y - from.y) * 0.2f
|
||||
for (i in 1 until cableSegmentCount) {
|
||||
val t = i.toFloat() / cableSegmentCount.toFloat()
|
||||
val sagOffset = catenary(sag, t)
|
||||
val x = lerp(from.x, to.x, t)
|
||||
val y = lerp(from.y, to.y, t) + sagOffset
|
||||
val z = lerp(from.z, to.z, t)
|
||||
addCableSegment(
|
||||
world,
|
||||
matrices,
|
||||
vertexBuffer,
|
||||
worldFrom.x,
|
||||
worldFrom.y,
|
||||
worldFrom.z,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
thicknessVector,
|
||||
uLeft,
|
||||
uRight,
|
||||
v = t,
|
||||
)
|
||||
}
|
||||
|
||||
addCableSegment(
|
||||
world,
|
||||
matrices,
|
||||
vertexBuffer,
|
||||
worldFrom.x,
|
||||
worldFrom.y,
|
||||
worldFrom.z,
|
||||
to.x,
|
||||
to.y,
|
||||
to.z,
|
||||
thicknessVector,
|
||||
uLeft,
|
||||
uRight,
|
||||
v = 1f,
|
||||
)
|
||||
}
|
||||
|
||||
private fun addCableSegment(
|
||||
world: World,
|
||||
matrices: MatrixStack.Entry,
|
||||
vertexBuffer: SpriteTexturedVertexConsumer,
|
||||
originX: Double,
|
||||
originY: Double,
|
||||
originZ: Double,
|
||||
x: Float,
|
||||
y: Float,
|
||||
z: Float,
|
||||
thickness: Vec3f,
|
||||
uLeft: Float,
|
||||
uRight: Float,
|
||||
v: Float,
|
||||
) {
|
||||
val blockPosition = BlockPos(
|
||||
floor(originX + x.toDouble()).toInt(),
|
||||
floor(originY + y.toDouble()).toInt(),
|
||||
floor(originZ + z.toDouble()).toInt(),
|
||||
)
|
||||
val light =
|
||||
WorldRenderer.getLightmapCoordinates(world, blockPosition)
|
||||
|
||||
// NOTE: Unlike in Java, where we can chain these method calls, in Kotlin we have to
|
||||
// specify the receiver explicitly for each one. This is because Kotlin resolves interface
|
||||
// methods a little differently: instead of always resolving dynamically, it will instead
|
||||
// always resolve default methods statically. Which is not what we want here, because that
|
||||
// gives us bad UVs.
|
||||
vertexBuffer.vertex(
|
||||
matrices.positionMatrix,
|
||||
x - thickness.x,
|
||||
y - thickness.y,
|
||||
z - thickness.z,
|
||||
)
|
||||
vertexBuffer.color(255, 255, 255, 255)
|
||||
vertexBuffer.texture(uLeft, v)
|
||||
vertexBuffer.light(light)
|
||||
vertexBuffer.next()
|
||||
|
||||
vertexBuffer.vertex(
|
||||
matrices.positionMatrix,
|
||||
x + thickness.x,
|
||||
y + thickness.y,
|
||||
z + thickness.z,
|
||||
)
|
||||
vertexBuffer.color(255, 255, 255, 255)
|
||||
vertexBuffer.texture(uRight, v)
|
||||
vertexBuffer.light(light)
|
||||
vertexBuffer.next()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +1,82 @@
|
|||
package net.liquidev.dawd3.block.device
|
||||
|
||||
import net.liquidev.dawd3.audio.device.OutputPortName
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.common.Affine2x2
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.liquidev.dawd3.common.Affine2x2f
|
||||
import net.liquidev.dawd3.common.HorizontalDirection
|
||||
import net.liquidev.dawd3.common.div
|
||||
import net.liquidev.dawd3.common.plus
|
||||
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||
import net.minecraft.util.math.Vec2f
|
||||
import net.minecraft.util.math.Vec3f
|
||||
|
||||
/** The physical appearance of a port. */
|
||||
data class PhysicalPort(
|
||||
val port: PortName,
|
||||
/** The coordinates are relative, in range [0.0, 1.0] where 0 is top-left and 1 is bottom-right. */
|
||||
val position: Vec2f,
|
||||
val side: Side,
|
||||
val logicalPort: PortName,
|
||||
) {
|
||||
/** Where to place the port on the model. */
|
||||
enum class Side(
|
||||
/** The transform matrix to apply to ports' coordinates in the model. */
|
||||
val transform: Affine2x2,
|
||||
val modelTransform: Affine2x2f,
|
||||
/** The model face on which the port is placed. */
|
||||
val face: Direction,
|
||||
val face: HorizontalDirection,
|
||||
) {
|
||||
Front(
|
||||
transform = Affine2x2(1f, 0f, 0f, 1f),
|
||||
face = Direction.NORTH,
|
||||
modelTransform = Affine2x2f(1f, 0f, 0f, 1f),
|
||||
face = HorizontalDirection.North,
|
||||
),
|
||||
Back(
|
||||
transform = Affine2x2(-1f, 0f, 0f, -1f, translateX = 16f, translateY = 16f),
|
||||
face = Direction.SOUTH,
|
||||
modelTransform = Affine2x2f(-1f, 0f, 0f, -1f, translateX = 16f, translateY = 16f),
|
||||
face = HorizontalDirection.South,
|
||||
),
|
||||
}
|
||||
|
||||
/** Where the port's patch cable cord is located relative to a block's center. */
|
||||
fun blockCablePosition(
|
||||
blockFacing: HorizontalDirection,
|
||||
protrusionAmount: Float,
|
||||
): Vec3f {
|
||||
val facing = (blockFacing + side.face).clockwise()
|
||||
val absolutePosition =
|
||||
DeviceBlockModel.relativeToAbsolutePortPosition(position) + DeviceBlockModel.portSize / 2f
|
||||
val invertedPosition = Vec2f(1f - absolutePosition.x / 16f, 1f - absolutePosition.y / 16f)
|
||||
val forward = Vec3f(protrusionAmount, invertedPosition.y - 0.5f, invertedPosition.x - 0.5f)
|
||||
return facing.rotateY(forward) + Vec3f(0.5f, 0.5f, 0.5f)
|
||||
}
|
||||
|
||||
companion object {
|
||||
class LayoutBuilder {
|
||||
internal val layout = PhysicalPortLayout()
|
||||
|
||||
fun port(portName: PortName, physicalPort: PhysicalPort) {
|
||||
layout[portName] = physicalPort
|
||||
}
|
||||
|
||||
fun port(portName: PortName, position: Vec2f, side: Side) {
|
||||
port(portName, PhysicalPort(position, side, portName))
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to create physical ports whose name is different from the logical
|
||||
* port name, to allow for aliasing multiple physical ports onto a single logical port.
|
||||
*
|
||||
* Note that this is only allowed for output ports, because input ports cannot have
|
||||
* multiple signals going into them.
|
||||
*/
|
||||
fun port(portName: OutputPortName, instanceName: String, position: Vec2f, side: Side) {
|
||||
port(portName.makeInstanced(instanceName), PhysicalPort(position, side, portName))
|
||||
}
|
||||
}
|
||||
|
||||
fun layout(build: LayoutBuilder.() -> Unit): PhysicalPortLayout {
|
||||
val builder = LayoutBuilder()
|
||||
build(builder)
|
||||
return builder.layout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias PhysicalPortLayout = HashMap<PortName, PhysicalPort>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import net.minecraft.util.Identifier
|
||||
|
||||
interface FaceTextures {
|
||||
val front: Identifier
|
||||
val back: Identifier
|
||||
val left: Identifier
|
||||
val right: Identifier
|
||||
val top: Identifier
|
||||
val bottom: Identifier
|
||||
|
||||
val particle: Identifier
|
||||
|
||||
companion object {
|
||||
fun withFrontAndSide(lazyId: () -> Identifier): FaceTextures = object : FaceTextures {
|
||||
private val id get() = lazyId()
|
||||
private val side = Identifier(id.namespace, "block/${id.path}_side")
|
||||
|
||||
override val front = Identifier(id.namespace, "block/${id.path}_front")
|
||||
override val back = side
|
||||
override val left = side
|
||||
override val right = side
|
||||
override val top = side
|
||||
override val bottom = side
|
||||
override val particle = side
|
||||
}
|
||||
|
||||
fun withTopSideAndBottom(lazyId: () -> Identifier): FaceTextures = object : FaceTextures {
|
||||
private val id get() = lazyId()
|
||||
private val side = Identifier(id.namespace, "block/${id.path}_side")
|
||||
|
||||
override val front = side
|
||||
override val back = side
|
||||
override val left = side
|
||||
override val right = side
|
||||
override val top = Identifier(id.namespace, "block/${id.path}_top")
|
||||
override val bottom = Identifier(id.namespace, "block/${id.path}_bottom")
|
||||
override val particle = side
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package net.liquidev.dawd3.block.device
|
||||
|
||||
class PortDirectionException(what: String) : Exception(what)
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package net.liquidev.dawd3.block.devices
|
||||
|
||||
import FaceTextures
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||
import net.liquidev.dawd3.audio.devices.ConstantDevice
|
||||
import net.liquidev.dawd3.block.device.DeviceBlockDescriptor
|
||||
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||
import net.liquidev.dawd3.common.Cuboids
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec2f
|
||||
|
||||
object FaderBlockDescriptor : DeviceBlockDescriptor<FaderBlockDescriptor.ClientState, Unit> {
|
||||
override val id = Identifier(Mod.id, "fader")
|
||||
|
||||
override val cuboid = Cuboids.halfBlock
|
||||
override val portLayout = PhysicalPort.layout {
|
||||
port(
|
||||
ConstantDevice.outputPort,
|
||||
instanceName = "front",
|
||||
position = Vec2f(0.5f, 0.75f),
|
||||
side = PhysicalPort.Side.Front,
|
||||
)
|
||||
}
|
||||
override val faceTextures = FaceTextures.withTopSideAndBottom { id }
|
||||
|
||||
class ClientState : DeviceBlockDescriptor.ClientState {
|
||||
override val logicalDevice = DeviceInstance(ConstantDevice(value = 440.0f))
|
||||
}
|
||||
|
||||
override fun onClientLoad(world: ClientWorld) = ClientState()
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package net.liquidev.dawd3.block.devices
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||
import net.liquidev.dawd3.audio.devices.SineOscillatorDevice
|
||||
import net.liquidev.dawd3.block.device.DeviceBlockDescriptor
|
||||
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec2f
|
||||
|
||||
object SineOscillatorBlockDescriptor : DeviceBlockDescriptor<SineOscillatorBlockDescriptor.ClientState, Unit> {
|
||||
override val id = Identifier(Mod.id, "sine_oscillator")
|
||||
|
||||
override val portLayout = PhysicalPort.layout {
|
||||
port(
|
||||
SineOscillatorDevice.frequencyCVPort,
|
||||
position = Vec2f(0.25f, 0.5f),
|
||||
side = PhysicalPort.Side.Front,
|
||||
)
|
||||
port(
|
||||
SineOscillatorDevice.outputPort,
|
||||
position = Vec2f(0.75f, 0.5f),
|
||||
side = PhysicalPort.Side.Front
|
||||
)
|
||||
}
|
||||
|
||||
class ClientState : DeviceBlockDescriptor.ClientState {
|
||||
override val logicalDevice = DeviceInstance(SineOscillatorDevice())
|
||||
}
|
||||
|
||||
override fun onClientLoad(world: ClientWorld) = ClientState()
|
||||
}
|
||||
|
|
@ -22,9 +22,9 @@ object SpeakerBlockDescriptor : DeviceBlockDescriptor<SpeakerBlockDescriptor.Cli
|
|||
.hardness(2.0f).resistance(6.0f)
|
||||
.sounds(BlockSoundGroup.WOOD)!!
|
||||
|
||||
override val portLayout = arrayOf(
|
||||
PhysicalPort(TerminalDevice.Input, Vec2f(0.5f, 0.5f), PhysicalPort.Side.Back)
|
||||
)
|
||||
override val portLayout = PhysicalPort.layout {
|
||||
port(TerminalDevice.inputPort, position = Vec2f(0.5f, 0.75f), side = PhysicalPort.Side.Back)
|
||||
}
|
||||
|
||||
class ClientState : DeviceBlockDescriptor.ClientState {
|
||||
internal val channel: MixGenerator.Channel<DeviceGraphGenerator>
|
||||
|
|
@ -33,15 +33,12 @@ object SpeakerBlockDescriptor : DeviceBlockDescriptor<SpeakerBlockDescriptor.Cli
|
|||
init {
|
||||
val generator = DeviceGraphGenerator()
|
||||
channel = Audio.mixer.createChannel(generator)
|
||||
|
||||
val terminal = generator.terminalDevice
|
||||
logicalDevice = terminal
|
||||
|
||||
generator.rebuildSim()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClientLoad(world: ClientWorld): ClientState = ClientState()
|
||||
override fun onClientLoad(world: ClientWorld) = ClientState()
|
||||
|
||||
override fun onClientUnload(state: ClientState, world: ClientWorld) {
|
||||
state.channel.stop()
|
||||
|
|
|
|||
|
|
@ -4,15 +4,26 @@ import net.minecraft.block.BlockState
|
|||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.Packet
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
abstract class D3BlockEntity(
|
||||
type: BlockEntityType<out D3BlockEntity>,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
) :
|
||||
BlockEntity(type, pos, state) {
|
||||
) : BlockEntity(type, pos, state) {
|
||||
|
||||
open fun onClientLoad(world: ClientWorld) {}
|
||||
open fun onClientUnload(world: ClientWorld) {}
|
||||
|
||||
override fun toUpdatePacket(): Packet<ClientPlayPacketListener> {
|
||||
return BlockEntityUpdateS2CPacket.create(this)
|
||||
}
|
||||
|
||||
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||
return createNbt()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package net.liquidev.dawd3.block.entity
|
|||
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents
|
||||
|
||||
fun registerBlockEntityEvents() {
|
||||
fun registerClientBlockEntityEvents() {
|
||||
ClientBlockEntityEvents.BLOCK_ENTITY_LOAD.register { blockEntity, world ->
|
||||
if (blockEntity is D3BlockEntity) {
|
||||
blockEntity.onClientLoad(world)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import net.minecraft.util.math.Vec2f
|
|||
import net.minecraft.util.math.Vec3f
|
||||
|
||||
/** 2x2 matrix. */
|
||||
data class Affine2x2(
|
||||
data class Affine2x2f(
|
||||
val xx: Float,
|
||||
val xy: Float,
|
||||
val yx: Float,
|
||||
20
src/main/kotlin/net/liquidev/dawd3/common/Mat2x3f.kt
Normal file
20
src/main/kotlin/net/liquidev/dawd3/common/Mat2x3f.kt
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package net.liquidev.dawd3.common
|
||||
|
||||
import net.minecraft.util.math.Vec2f
|
||||
import net.minecraft.util.math.Vec3f
|
||||
|
||||
/** Matrix with two rows and three columns. */
|
||||
class Mat2x3f(
|
||||
val xx: Float,
|
||||
val xy: Float,
|
||||
val xz: Float,
|
||||
val yx: Float,
|
||||
val yy: Float,
|
||||
val yz: Float,
|
||||
) {
|
||||
operator fun times(vec: Vec3f) =
|
||||
Vec2f(
|
||||
vec.x * xx + vec.y * xy + vec.z * xz,
|
||||
vec.x * yx + vec.y * yy + vec.z * yz
|
||||
)
|
||||
}
|
||||
21
src/main/kotlin/net/liquidev/dawd3/common/Mat3x2f.kt
Normal file
21
src/main/kotlin/net/liquidev/dawd3/common/Mat3x2f.kt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package net.liquidev.dawd3.common
|
||||
|
||||
import net.minecraft.util.math.Vec2f
|
||||
import net.minecraft.util.math.Vec3f
|
||||
|
||||
/** Matrix with three columns and two rows. */
|
||||
class Mat3x2f(
|
||||
val xx: Float,
|
||||
val xy: Float,
|
||||
val yx: Float,
|
||||
val yy: Float,
|
||||
val zx: Float,
|
||||
val zy: Float,
|
||||
) {
|
||||
operator fun times(vec: Vec2f): Vec3f =
|
||||
Vec3f(
|
||||
vec.x * xx + vec.y * xy,
|
||||
vec.x * yx + vec.y * yy,
|
||||
vec.x * zx + vec.y * zy,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
package net.liquidev.dawd3.common
|
||||
|
||||
import java.util.concurrent.Executor
|
||||
import java.util.function.Function
|
||||
|
||||
class TaskQueue : Executor {
|
||||
private val tasks = arrayListOf<Runnable>()
|
||||
class TaskQueue<T, R> {
|
||||
private val tasks = arrayListOf<Function<T, R>>()
|
||||
|
||||
override fun execute(task: Runnable) {
|
||||
fun enqueue(task: Function<T, R>) {
|
||||
tasks.add(task)
|
||||
}
|
||||
|
||||
fun flush() {
|
||||
fun flush(argument: T) {
|
||||
for (task in tasks) {
|
||||
task.run()
|
||||
task.apply(argument)
|
||||
}
|
||||
tasks.clear()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,147 @@
|
|||
package net.liquidev.dawd3.common
|
||||
|
||||
import net.minecraft.util.math.Vec2f
|
||||
import net.minecraft.util.math.Vec3f
|
||||
import net.minecraft.util.math.*
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
operator fun Vec2f.plus(other: Vec2f): Vec2f = Vec2f(x + other.x, y + other.y)
|
||||
operator fun Vec2f.minus(other: Vec2f): Vec2f = Vec2f(x - other.x, y - other.y)
|
||||
operator fun Vec2f.times(other: Vec2f): Vec2f = Vec2f(x * other.x, y * other.y)
|
||||
operator fun Vec2f.div(other: Vec2f): Vec2f = Vec2f(x / other.x, y / other.y)
|
||||
operator fun Vec2f.div(other: Float): Vec2f = Vec2f(x / other, y / other)
|
||||
|
||||
fun Vec2f.format() = "($x, $y)"
|
||||
|
||||
operator fun Vec3f.plus(other: Vec3f): Vec3f = Vec3f(x + other.x, y + other.y, z + other.z)
|
||||
operator fun Vec3f.minus(other: Vec3f): Vec3f = Vec3f(x - other.x, y - other.y, z - other.z)
|
||||
operator fun Vec3f.times(other: Vec3f): Vec3f = Vec3f(x * other.x, y * other.y, z * other.z)
|
||||
operator fun Vec3f.times(other: Float): Vec3f = Vec3f(x * other, y * other, z * other)
|
||||
operator fun Vec3f.div(other: Vec3f): Vec3f = Vec3f(x / other.x, y / other.y, z / other.z)
|
||||
|
||||
fun Vec3f.min(other: Vec3f): Vec3f = Vec3f(min(x, other.x), min(y, other.y), min(z, other.z))
|
||||
fun Vec3f.max(other: Vec3f): Vec3f = Vec3f(max(x, other.x), max(y, other.y), max(z, other.z))
|
||||
|
||||
val Vec3f.lengthSquared get() = x * x + y * y + z * z
|
||||
val Vec3f.length get() = sqrt(lengthSquared)
|
||||
|
||||
fun Vec3f.normalize() {
|
||||
val length = length
|
||||
if (length != 0f) {
|
||||
set(x / length, y / length, z / length)
|
||||
} else {
|
||||
set(0f, 0f, 0f)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Vec3d.plus(other: Vec3d): Vec3d = Vec3d(x + other.x, y + other.y, z + other.z)
|
||||
operator fun Vec3d.minus(other: Vec3d): Vec3d = Vec3d(x - other.x, y - other.y, z - other.z)
|
||||
operator fun Vec3d.times(other: Vec3d): Vec3d = Vec3d(x * other.x, y * other.y, z * other.z)
|
||||
operator fun Vec3d.div(other: Vec3d): Vec3d = Vec3d(x / other.x, y / other.y, z / other.z)
|
||||
|
||||
operator fun BlockPos.plus(other: BlockPos): BlockPos =
|
||||
BlockPos(x + other.x, y + other.y, z + other.z)
|
||||
|
||||
operator fun BlockPos.minus(other: BlockPos): BlockPos =
|
||||
BlockPos(x - other.x, y - other.y, z - other.z)
|
||||
|
||||
private object PlaneMatrices3Dto2D {
|
||||
val xyPlane = Mat2x3f(1f, 0f, 0f, 0f, 1f, 0f)
|
||||
val zyPlane = Mat2x3f(0f, 0f, 1f, 0f, 1f, 0f)
|
||||
val xzPlane = Mat2x3f(1f, 0f, 0f, 0f, 0f, 1f)
|
||||
}
|
||||
|
||||
val Direction.to2DPlane
|
||||
get() =
|
||||
when (this) {
|
||||
Direction.DOWN -> PlaneMatrices3Dto2D.xzPlane
|
||||
Direction.UP -> PlaneMatrices3Dto2D.xzPlane
|
||||
Direction.NORTH -> PlaneMatrices3Dto2D.xyPlane
|
||||
Direction.SOUTH -> PlaneMatrices3Dto2D.xyPlane
|
||||
Direction.WEST -> PlaneMatrices3Dto2D.zyPlane
|
||||
Direction.EAST -> PlaneMatrices3Dto2D.zyPlane
|
||||
}
|
||||
|
||||
private object CorrectionMatrices {
|
||||
val invertXandY = Affine2x2f(-1f, 0f, 0f, -1f, translateX = 1f, translateY = 1f)
|
||||
val invertYOnly = Affine2x2f(1f, 0f, 0f, -1f, translateX = 0f, translateY = 1f)
|
||||
}
|
||||
|
||||
enum class HorizontalDirection(
|
||||
val index: Int,
|
||||
val x: Int,
|
||||
val z: Int,
|
||||
val angle: Float,
|
||||
val direction: Direction,
|
||||
) {
|
||||
East(0, x = 1, z = 0, angle = 0f, direction = Direction.EAST),
|
||||
South(1, x = 0, z = 1, angle = 0.5f * PI.toFloat(), direction = Direction.SOUTH),
|
||||
West(2, x = -1, z = 0, angle = PI.toFloat(), direction = Direction.WEST),
|
||||
North(3, x = 0, z = -1, angle = 1.5f * PI.toFloat(), direction = Direction.NORTH);
|
||||
|
||||
operator fun plus(other: HorizontalDirection): HorizontalDirection =
|
||||
fromIndex((index + other.index) % values.size)
|
||||
|
||||
operator fun minus(other: HorizontalDirection): HorizontalDirection =
|
||||
fromIndex((index - other.index).mod(values.size))
|
||||
|
||||
fun clockwise() = plus(South)
|
||||
fun counterClockwise() = minus(South)
|
||||
|
||||
fun rotateY(vec: Vec3f): Vec3f =
|
||||
when (this) {
|
||||
East -> vec
|
||||
North -> Vec3f(vec.z, vec.y, -vec.x)
|
||||
West -> Vec3f(-vec.x, vec.y, -vec.z)
|
||||
South -> Vec3f(-vec.z, vec.y, vec.x)
|
||||
}
|
||||
|
||||
val faceCorrection
|
||||
get() =
|
||||
when (this) {
|
||||
East -> CorrectionMatrices.invertXandY
|
||||
South -> CorrectionMatrices.invertYOnly
|
||||
West -> CorrectionMatrices.invertYOnly
|
||||
North -> CorrectionMatrices.invertXandY
|
||||
}
|
||||
|
||||
val vector get() = Vec3i(x, 0, z)
|
||||
|
||||
companion object {
|
||||
private val values = values()
|
||||
|
||||
fun fromIndex(i: Int) = values[i]
|
||||
|
||||
fun fromDirection(direction: Direction): HorizontalDirection? =
|
||||
when (direction) {
|
||||
Direction.EAST -> East
|
||||
Direction.SOUTH -> South
|
||||
Direction.WEST -> West
|
||||
Direction.NORTH -> North
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Vec3d.toVec3f() = Vec3f(x.toFloat(), y.toFloat(), z.toFloat())
|
||||
fun Vec3f.toVec3d() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble())
|
||||
fun Vec3i.toVec3f() = Vec3f(x.toFloat(), y.toFloat(), z.toFloat())
|
||||
fun BlockPos.toVec3d() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble())
|
||||
|
||||
fun pointInRectangle(point: Vec2f, topLeft: Vec2f, bottomRight: Vec2f) =
|
||||
point.x >= topLeft.x && point.y >= topLeft.y &&
|
||||
point.x <= bottomRight.x && point.y <= bottomRight.y
|
||||
|
||||
fun lerp(a: Float, b: Float, t: Float): Float =
|
||||
a + t * (b - a)
|
||||
|
||||
object Cuboids {
|
||||
val fullBlock = Box(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
|
||||
val halfBlock = Box(0.0, 0.0, 0.0, 1.0, 0.5, 1.0)
|
||||
|
||||
fun newF(from: Vec3f, to: Vec3f) = Box(from.toVec3d(), to.toVec3d())
|
||||
}
|
||||
|
||||
val Box.fromF get() = Vec3f(minX.toFloat(), minY.toFloat(), minZ.toFloat())
|
||||
val Box.toF get() = Vec3f(maxX.toFloat(), maxY.toFloat(), maxZ.toFloat())
|
||||
|
|
|
|||
10
src/main/kotlin/net/liquidev/dawd3/common/world.kt
Normal file
10
src/main/kotlin/net/liquidev/dawd3/common/world.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package net.liquidev.dawd3.common
|
||||
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
fun World.syncBlockToClients(position: BlockPos) {
|
||||
val state = getBlockState(position)
|
||||
updateListeners(position, state, state, Block.NOTIFY_LISTENERS)
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@ import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider
|
|||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.block.Blocks
|
||||
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||
import net.liquidev.dawd3.item.BasicItem
|
||||
import net.liquidev.dawd3.item.Items
|
||||
import net.minecraft.data.client.*
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.*
|
||||
|
|
@ -19,7 +21,9 @@ class ModelDatagen(generator: FabricDataGenerator) : FabricModelProvider(generat
|
|||
|
||||
for ((id, deviceBlock) in Blocks.deviceBlocks) {
|
||||
val modelId = Identifier(Mod.id, "block/${id.path}")
|
||||
generator.modelCollector.accept(modelId) { DeviceBlockModel.generate(id, deviceBlock) }
|
||||
generator.modelCollector.accept(modelId) {
|
||||
DeviceBlockModel.generate(deviceBlock)
|
||||
}
|
||||
generator.blockStateCollector.accept(horizontallyRotatableBlockState(id, deviceBlock))
|
||||
}
|
||||
}
|
||||
|
|
@ -36,14 +40,21 @@ class ModelDatagen(generator: FabricDataGenerator) : FabricModelProvider(generat
|
|||
.coordinate(BlockStateModelGenerator.createNorthDefaultHorizontalRotationStates())
|
||||
}
|
||||
|
||||
|
||||
override fun generateItemModels(generator: ItemModelGenerator) {
|
||||
logger.info("generating item models")
|
||||
logger.info("generating block item models")
|
||||
for ((id, deviceBlock) in Blocks.deviceBlocks) {
|
||||
generator.register(
|
||||
deviceBlock.item.item.item,
|
||||
Model(Optional.of(Identifier(Mod.id, "block/${id.path}")), Optional.empty())
|
||||
)
|
||||
}
|
||||
|
||||
logger.info("generating other item models")
|
||||
for (registered in Items.registry.registered) {
|
||||
val item = registered.item.item
|
||||
if (item is BasicItem) {
|
||||
generator.register(registered.item.item, Models.GENERATED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package net.liquidev.dawd3.datagen.device
|
|||
|
||||
import com.google.gson.JsonObject
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.PortDirection
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.block.Blocks
|
||||
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||
import net.liquidev.dawd3.common.*
|
||||
|
|
@ -11,86 +13,87 @@ import net.liquidev.dawd3.datagen.json.faces
|
|||
import net.liquidev.dawd3.datagen.json.uvRect
|
||||
import net.liquidev.dawd3.datagen.jsonArray
|
||||
import net.liquidev.dawd3.datagen.jsonObject
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Vec2f
|
||||
import net.minecraft.util.math.Vec3f
|
||||
|
||||
object DeviceBlockModel {
|
||||
private val portSize = Vec2f(3f, 3f)
|
||||
val portSize = Vec2f(4f, 4f)
|
||||
private const val portTexture = "${Mod.id}:device/port"
|
||||
|
||||
private fun getSideTexture(id: Identifier) =
|
||||
"${Mod.id}:block/${id.path}_side"
|
||||
|
||||
private fun getFrontTexture(id: Identifier) =
|
||||
"${Mod.id}:block/${id.path}_front"
|
||||
|
||||
fun generate(
|
||||
id: Identifier,
|
||||
deviceBlock: Blocks.RegisteredDeviceBlock,
|
||||
): JsonObject {
|
||||
fun generate(deviceBlock: Blocks.RegisteredDeviceBlock): JsonObject {
|
||||
val faceTextures = deviceBlock.descriptor.faceTextures
|
||||
return jsonObject {
|
||||
add("parent", "block/block")
|
||||
|
||||
add("textures", jsonObject {
|
||||
add("side", getSideTexture(id))
|
||||
add("front", getFrontTexture(id))
|
||||
add("front", faceTextures.front.toString())
|
||||
add("back", faceTextures.back.toString())
|
||||
add("right", faceTextures.right.toString())
|
||||
add("left", faceTextures.left.toString())
|
||||
add("top", faceTextures.top.toString())
|
||||
add("bottom", faceTextures.bottom.toString())
|
||||
add("particle", faceTextures.particle.toString())
|
||||
add("port", portTexture)
|
||||
add("particle", "#side")
|
||||
})
|
||||
|
||||
add("elements", jsonArray {
|
||||
add(blockElement)
|
||||
add(getBlockElement(deviceBlock.descriptor.cuboid))
|
||||
|
||||
val descriptor = deviceBlock.descriptor
|
||||
for (port in descriptor.portLayout) {
|
||||
add(getPortElement(port))
|
||||
for ((portName, port) in descriptor.portLayout) {
|
||||
add(getPortElement(portName, port))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private val blockElement = element(
|
||||
from = Vec3f(0f, 0f, 0f),
|
||||
to = Vec3f(16f, 16f, 16f),
|
||||
private fun getBlockElement(cuboid: Box) = element(
|
||||
from = Vec3f(cuboid.minX.toFloat(), cuboid.minY.toFloat(), cuboid.minZ.toFloat()) * 16f,
|
||||
to = Vec3f(cuboid.maxX.toFloat(), cuboid.maxY.toFloat(), cuboid.maxZ.toFloat()) * 16f,
|
||||
faces = faces(
|
||||
north = face(texture = "#front", cullface = "north"),
|
||||
east = face(texture = "#side", cullface = "east"),
|
||||
south = face(texture = "#side", cullface = "south"),
|
||||
west = face(texture = "#side", cullface = "west"),
|
||||
up = face(texture = "#side", cullface = "up"),
|
||||
down = face(texture = "#side", cullface = "down"),
|
||||
east = face(texture = "#right", cullface = "east"),
|
||||
south = face(texture = "#back", cullface = "south"),
|
||||
west = face(texture = "#left", cullface = "west"),
|
||||
up = face(texture = "#top", cullface = "up"),
|
||||
down = face(texture = "#bottom", cullface = "down"),
|
||||
)
|
||||
)
|
||||
|
||||
private val portPlayArea = Vec2f(16f, 16f) - portSize
|
||||
private fun relativeToAbsolutePortPosition(position: Vec2f): Vec2f {
|
||||
val inv = Vec2f(1f - position.x, 1f - position.y)
|
||||
return inv * portPlayArea
|
||||
fun relativeToAbsolutePortPosition(position: Vec2f): Vec2f {
|
||||
return position * portPlayArea
|
||||
}
|
||||
|
||||
private fun getPortElement(port: PhysicalPort): JsonObject {
|
||||
val bottomLeft2D = relativeToAbsolutePortPosition(port.position)
|
||||
private fun getPortElement(portName: PortName, port: PhysicalPort): JsonObject {
|
||||
val bottomLeft2D =
|
||||
relativeToAbsolutePortPosition(Vec2f(1f - port.position.x, 1f - port.position.y))
|
||||
val topRight2D = bottomLeft2D + portSize
|
||||
val bottomLeft = Vec3f(bottomLeft2D.x, bottomLeft2D.y, -0.01f)
|
||||
val topRight = Vec3f(topRight2D.x, topRight2D.y, 0.01f)
|
||||
|
||||
val rotatedBottomLeft = port.side.transform.timesXZ(bottomLeft)
|
||||
val rotatedTopRight = port.side.transform.timesXZ(topRight)
|
||||
val rotatedBottomLeft = port.side.modelTransform.timesXZ(bottomLeft)
|
||||
val rotatedTopRight = port.side.modelTransform.timesXZ(topRight)
|
||||
|
||||
val min = rotatedBottomLeft.min(rotatedTopRight)
|
||||
val max = rotatedBottomLeft.max(rotatedTopRight)
|
||||
|
||||
val uvX = when (portName.direction) {
|
||||
PortDirection.Input -> 0f
|
||||
PortDirection.Output -> 4f
|
||||
}
|
||||
|
||||
return element(
|
||||
from = min,
|
||||
to = max,
|
||||
faces = jsonObject {
|
||||
val faceName = port.side.face.getName()
|
||||
val faceName = port.side.face.direction.getName()
|
||||
add(
|
||||
faceName, face(
|
||||
texture = "#port",
|
||||
cullface = faceName,
|
||||
uv = uvRect(0f, 0f, 4f, 4f)
|
||||
uv = uvRect(uvX, 0f, uvX + 4f, 4f)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
6
src/main/kotlin/net/liquidev/dawd3/item/BasicItem.kt
Normal file
6
src/main/kotlin/net/liquidev/dawd3/item/BasicItem.kt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package net.liquidev.dawd3.item
|
||||
|
||||
import net.minecraft.item.Item
|
||||
|
||||
/** Parent class for telling apart block items from other items. */
|
||||
abstract class BasicItem(settings: Settings) : Item(settings)
|
||||
|
|
@ -36,13 +36,32 @@ object Items {
|
|||
)
|
||||
|
||||
// Tools
|
||||
val patchCable =
|
||||
addItem("patch_cable", PatchCable(FabricItemSettings().group(ItemGroup.REDSTONE)))
|
||||
private fun coloredPatchCable(name: String, color: Byte) =
|
||||
addItem(name, PatchCableItem(FabricItemSettings().group(ItemGroup.REDSTONE), color))
|
||||
|
||||
val patchCables = arrayOf(
|
||||
coloredPatchCable("white_patch_cable", color = 0),
|
||||
coloredPatchCable("orange_patch_cable", color = 1),
|
||||
coloredPatchCable("magenta_patch_cable", color = 2),
|
||||
coloredPatchCable("light_blue_patch_cable", color = 3),
|
||||
coloredPatchCable("yellow_patch_cable", color = 4),
|
||||
coloredPatchCable("lime_patch_cable", color = 5),
|
||||
coloredPatchCable("pink_patch_cable", color = 6),
|
||||
coloredPatchCable("gray_patch_cable", color = 7),
|
||||
coloredPatchCable("light_gray_patch_cable", color = 8),
|
||||
coloredPatchCable("cyan_patch_cable", color = 9),
|
||||
coloredPatchCable("purple_patch_cable", color = 10),
|
||||
coloredPatchCable("blue_patch_cable", color = 11),
|
||||
coloredPatchCable("brown_patch_cable", color = 12),
|
||||
coloredPatchCable("green_patch_cable", color = 13),
|
||||
coloredPatchCable("red_patch_cable", color = 14),
|
||||
coloredPatchCable("black_patch_cable", color = 15),
|
||||
)
|
||||
|
||||
fun addItem(name: Identifier, item: Item): D3Registry.Registered<RegisteredItem> =
|
||||
registry.add(name, RegisteredItem(item))
|
||||
|
||||
fun addItem(name: String, item: Item) =
|
||||
private fun addItem(name: String, item: Item) =
|
||||
addItem(Identifier(Mod.id, name), item)
|
||||
|
||||
data class RegisteredItem(
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
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<ItemStack> {
|
||||
user.playSound(SoundEvents.BLOCK_METAL_PLACE, 1.0f, 1.0f)
|
||||
return TypedActionResult.success(user.getStackInHand(hand))
|
||||
}
|
||||
}
|
||||
141
src/main/kotlin/net/liquidev/dawd3/item/PatchCableItem.kt
Normal file
141
src/main/kotlin/net/liquidev/dawd3/item/PatchCableItem.kt
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
package net.liquidev.dawd3.item
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.PlayerLookup
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||
import net.liquidev.dawd3.common.*
|
||||
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||
import net.liquidev.dawd3.net.ConnectPorts
|
||||
import net.liquidev.dawd3.net.StartConnectingPorts
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemUsageContext
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec2f
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
class PatchCableItem(settings: Settings, val color: Byte) : BasicItem(settings) {
|
||||
override fun useOnBlock(context: ItemUsageContext): ActionResult {
|
||||
val blockEntity = context.world.getBlockEntity(context.blockPos)
|
||||
if (!context.world.isClient && blockEntity is DeviceBlockEntity) {
|
||||
val side = HorizontalDirection.fromDirection(context.side)
|
||||
if (side != null) {
|
||||
val usePosition =
|
||||
calculateUsePositionOnHorizontalFace(context.hitPos, context.blockPos, side)
|
||||
for ((portName, port) in blockEntity.descriptor.portLayout) {
|
||||
if (isUsedOnPort(port, usePosition)) {
|
||||
useOnPort(context, portName)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ActionResult.success(context.world.isClient)
|
||||
}
|
||||
|
||||
private fun calculateUsePositionOnHorizontalFace(
|
||||
hitPosition: Vec3d,
|
||||
blockPosition: BlockPos,
|
||||
side: HorizontalDirection,
|
||||
): Vec2f {
|
||||
val relativeHitPosition = (hitPosition - blockPosition.toVec3d()).toVec3f()
|
||||
val faceCorrection = side.faceCorrection
|
||||
return faceCorrection * (side.direction.to2DPlane * relativeHitPosition)
|
||||
}
|
||||
|
||||
private fun isUsedOnPort(
|
||||
port: PhysicalPort,
|
||||
usePosition: Vec2f,
|
||||
): Boolean {
|
||||
val usePositionInPixels = usePosition * Vec2f(16f, 16f)
|
||||
val portTopLeft = DeviceBlockModel.relativeToAbsolutePortPosition(port.position)
|
||||
val portBottomRight = portTopLeft + DeviceBlockModel.portSize
|
||||
return pointInRectangle(usePositionInPixels, portTopLeft, portBottomRight)
|
||||
}
|
||||
|
||||
private fun useOnPort(context: ItemUsageContext, portName: PortName) {
|
||||
val player = context.player
|
||||
if (player == null || player !is ServerPlayerEntity) {
|
||||
return
|
||||
}
|
||||
|
||||
val ongoingConnection = ongoingConnections[player]
|
||||
if (ongoingConnection == null) {
|
||||
startConnecting(player, OngoingConnection(context.blockPos, portName, color))
|
||||
ServerPlayNetworking.send(
|
||||
player,
|
||||
StartConnectingPorts.id,
|
||||
StartConnectingPorts(context.blockPos, portName.toString(), color).serialize()
|
||||
)
|
||||
} else {
|
||||
if (portName.direction != ongoingConnection.portName.direction) {
|
||||
val world = context.world as ServerWorld
|
||||
|
||||
val fromPosition = ongoingConnection.blockPosition
|
||||
val fromPort = ongoingConnection.portName
|
||||
val toPosition = context.blockPos
|
||||
|
||||
val witnesses = PlayerLookup.tracking(world, fromPosition).toHashSet()
|
||||
witnesses.addAll(PlayerLookup.tracking(world, toPosition))
|
||||
for (witness in witnesses) {
|
||||
ServerPlayNetworking.send(
|
||||
witness,
|
||||
ConnectPorts.id,
|
||||
ConnectPorts(
|
||||
fromPosition,
|
||||
fromPort.id.toString(),
|
||||
toPosition,
|
||||
portName.id.toString(),
|
||||
ongoingConnection.color,
|
||||
).serialize()
|
||||
)
|
||||
}
|
||||
|
||||
val fromBlockEntity = world.getBlockEntity(fromPosition)
|
||||
val toBlockEntity = world.getBlockEntity(toPosition)
|
||||
if (fromBlockEntity !is DeviceBlockEntity || toBlockEntity !is DeviceBlockEntity) {
|
||||
logger.error("from or to-block entity is no longer a device")
|
||||
return
|
||||
}
|
||||
DeviceBlockEntity.connectPhysicalDevices(
|
||||
fromBlockEntity,
|
||||
fromPort,
|
||||
toBlockEntity,
|
||||
portName,
|
||||
color,
|
||||
)
|
||||
|
||||
clearOngoingConnection(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class OngoingConnection(
|
||||
val blockPosition: BlockPos,
|
||||
val portName: PortName,
|
||||
val color: Byte,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private var logger = Mod.logger<PatchCableItem>()
|
||||
|
||||
internal val ongoingConnections = hashMapOf<PlayerEntity, OngoingConnection>()
|
||||
|
||||
private fun clearOngoingConnection(player: PlayerEntity) {
|
||||
ongoingConnections.remove(player)
|
||||
}
|
||||
|
||||
internal fun onBlockDestroyed(blockPosition: BlockPos) {
|
||||
ongoingConnections.values.removeAll { it.blockPosition == blockPosition }
|
||||
}
|
||||
|
||||
fun startConnecting(player: PlayerEntity, connection: OngoingConnection) {
|
||||
ongoingConnections[player] = connection
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/main/kotlin/net/liquidev/dawd3/mixin/NonModelTextures.kt
Normal file
27
src/main/kotlin/net/liquidev/dawd3/mixin/NonModelTextures.kt
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package net.liquidev.dawd3.mixin
|
||||
|
||||
import net.liquidev.dawd3.render.Textures
|
||||
import net.minecraft.client.render.TexturedRenderLayers
|
||||
import net.minecraft.client.util.SpriteIdentifier
|
||||
import org.spongepowered.asm.mixin.Mixin
|
||||
import org.spongepowered.asm.mixin.injection.At
|
||||
import org.spongepowered.asm.mixin.injection.Inject
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
|
||||
import java.util.function.Consumer
|
||||
|
||||
@Suppress("UNUSED")
|
||||
@Mixin(TexturedRenderLayers::class)
|
||||
abstract class NonModelTextures {
|
||||
private companion object {
|
||||
@Inject(
|
||||
method = ["addDefaultTextures(Ljava/util/function/Consumer;)V"],
|
||||
at = [At(value = "TAIL")]
|
||||
)
|
||||
@JvmStatic
|
||||
private fun addDefaultTextures(adder: Consumer<SpriteIdentifier>, ci: CallbackInfo) {
|
||||
for (texture in Textures.nonModel) {
|
||||
adder.accept(texture)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
src/main/kotlin/net/liquidev/dawd3/net/ConnectPorts.kt
Normal file
88
src/main/kotlin/net/liquidev/dawd3/net/ConnectPorts.kt
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package net.liquidev.dawd3.net
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
|
||||
data class ConnectPorts(
|
||||
val fromPosition: BlockPos,
|
||||
val fromPort: String,
|
||||
val toPosition: BlockPos,
|
||||
val toPort: String,
|
||||
val color: Byte,
|
||||
) {
|
||||
companion object {
|
||||
private val logger = Mod.logger<ConnectPorts>()
|
||||
|
||||
val id = Identifier(Mod.id, "connect_ports")
|
||||
|
||||
fun registerClientReceiver() {
|
||||
ClientPlayNetworking.registerGlobalReceiver(id) { client, _, buffer, _ ->
|
||||
val packet = deserialize(buffer)
|
||||
client.execute {
|
||||
val world = client.world
|
||||
if (world != null) {
|
||||
packet.handleInWorld(world)
|
||||
} else {
|
||||
logger.warn("packet received without a client world")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserialize(buffer: PacketByteBuf): ConnectPorts {
|
||||
return ConnectPorts(
|
||||
fromPosition = buffer.readBlockPos(),
|
||||
fromPort = buffer.readString(),
|
||||
toPosition = buffer.readBlockPos(),
|
||||
toPort = buffer.readString(),
|
||||
color = buffer.readByte(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun serialize(): PacketByteBuf {
|
||||
val buffer = PacketByteBufs.create()
|
||||
buffer.writeBlockPos(fromPosition)
|
||||
buffer.writeString(fromPort)
|
||||
buffer.writeBlockPos(toPosition)
|
||||
buffer.writeString(toPort)
|
||||
buffer.writeByte(color.toInt()) // bruh
|
||||
return buffer
|
||||
}
|
||||
|
||||
private fun handleInWorld(world: World) {
|
||||
val fromBlockEntity = world.getBlockEntity(fromPosition)
|
||||
val toBlockEntity = world.getBlockEntity(toPosition)
|
||||
if (fromBlockEntity !is DeviceBlockEntity || toBlockEntity !is DeviceBlockEntity) {
|
||||
logger.warn("packet received with block entities not being device block entities")
|
||||
return
|
||||
}
|
||||
|
||||
val fromPortName = PortName.fromString(fromPort)
|
||||
if (fromPortName == null) {
|
||||
logger.warn("port $fromPort does not exist on the from-device")
|
||||
return
|
||||
}
|
||||
val toPortName = PortName.fromString(toPort)
|
||||
if (toPortName == null) {
|
||||
logger.warn("port $toPort does not exist on the to-device")
|
||||
return
|
||||
}
|
||||
|
||||
logger.debug("connecting logical device ports ($fromBlockEntity):($fromPortName) -> ($toBlockEntity):($toPortName)")
|
||||
DeviceBlockEntity.connectLogicalDevices(
|
||||
fromBlockEntity,
|
||||
fromPortName,
|
||||
toBlockEntity,
|
||||
toPortName,
|
||||
color,
|
||||
)
|
||||
}
|
||||
}
|
||||
8
src/main/kotlin/net/liquidev/dawd3/net/Packets.kt
Normal file
8
src/main/kotlin/net/liquidev/dawd3/net/Packets.kt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package net.liquidev.dawd3.net
|
||||
|
||||
object Packets {
|
||||
fun registerClientReceivers() {
|
||||
StartConnectingPorts.registerClientReceiver()
|
||||
ConnectPorts.registerClientReceiver()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package net.liquidev.dawd3.net
|
||||
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.liquidev.dawd3.audio.device.PortName
|
||||
import net.liquidev.dawd3.item.PatchCableItem
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
class StartConnectingPorts(val blockPosition: BlockPos, val portName: String, val color: Byte) {
|
||||
companion object {
|
||||
private val logger = Mod.logger<ConnectPorts>()
|
||||
|
||||
val id = Identifier(Mod.id, "start_connecting_ports")
|
||||
|
||||
fun registerClientReceiver() {
|
||||
ClientPlayNetworking.registerGlobalReceiver(id) { client, _, buffer, _ ->
|
||||
val packet = deserialize(buffer)
|
||||
client.execute {
|
||||
val player = client.player
|
||||
if (player != null) {
|
||||
packet.handleForPlayer(player)
|
||||
} else {
|
||||
logger.warn("packet received without a client player")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserialize(buffer: PacketByteBuf): StartConnectingPorts {
|
||||
return StartConnectingPorts(
|
||||
blockPosition = buffer.readBlockPos(),
|
||||
portName = buffer.readString(),
|
||||
color = buffer.readByte(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun serialize(): PacketByteBuf {
|
||||
val buffer = PacketByteBufs.create()
|
||||
buffer.writeBlockPos(blockPosition)
|
||||
buffer.writeString(portName)
|
||||
buffer.writeByte(color.toInt())
|
||||
return buffer
|
||||
}
|
||||
|
||||
private fun handleForPlayer(player: PlayerEntity) {
|
||||
val portName = PortName.fromString(portName)
|
||||
if (portName == null) {
|
||||
logger.warn("invalid port name ${this.portName}")
|
||||
return
|
||||
}
|
||||
|
||||
PatchCableItem.startConnecting(
|
||||
player,
|
||||
PatchCableItem.OngoingConnection(blockPosition, portName, color)
|
||||
)
|
||||
}
|
||||
}
|
||||
19
src/main/kotlin/net/liquidev/dawd3/render/Textures.kt
Normal file
19
src/main/kotlin/net/liquidev/dawd3/render/Textures.kt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package net.liquidev.dawd3.render
|
||||
|
||||
import net.liquidev.dawd3.Mod
|
||||
import net.minecraft.client.util.SpriteIdentifier
|
||||
import net.minecraft.screen.PlayerScreenHandler
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object Textures {
|
||||
/**
|
||||
* The set of textures that are not referenced by models but need to be loaded into the
|
||||
* block atlas.
|
||||
*/
|
||||
val nonModel = setOf(
|
||||
SpriteIdentifier(
|
||||
PlayerScreenHandler.BLOCK_ATLAS_TEXTURE,
|
||||
Identifier(Mod.id, "device/cable")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"variants": {
|
||||
"": { "model": "dawd3:device/patch_cable_plug" }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,20 @@
|
|||
{
|
||||
"itemGroup.dawd3.main": "dawd³",
|
||||
"item.dawd3.patch_cable": "Patch Cable",
|
||||
"item.dawd3.white_patch_cable": "White Patch Cable",
|
||||
"item.dawd3.orange_patch_cable": "Orange Patch Cable",
|
||||
"item.dawd3.magenta_patch_cable": "Magenta Patch Cable",
|
||||
"item.dawd3.light_blue_patch_cable": "Light Blue Patch Cable",
|
||||
"item.dawd3.yellow_patch_cable": "Yellow Patch Cable",
|
||||
"item.dawd3.lime_patch_cable": "Lime Patch Cable",
|
||||
"item.dawd3.pink_patch_cable": "Pink Patch Cable",
|
||||
"item.dawd3.gray_patch_cable": "Gray Patch Cable",
|
||||
"item.dawd3.light_gray_patch_cable": "Light Gray Patch Cable",
|
||||
"item.dawd3.cyan_patch_cable": "Cyan Patch Cable",
|
||||
"item.dawd3.purple_patch_cable": "Purple Patch Cable",
|
||||
"item.dawd3.brown_patch_cable": "Brown Patch Cable",
|
||||
"item.dawd3.blue_patch_cable": "Blue Patch Cable",
|
||||
"item.dawd3.green_patch_cable": "Green Patch Cable",
|
||||
"item.dawd3.red_patch_cable": "Red Patch Cable",
|
||||
"item.dawd3.black_patch_cable": "Black Patch Cable",
|
||||
"block.dawd3.speaker": "Speaker"
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"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" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue