summary refs log tree commit diff stats
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/kotlin/space/autistic/radio/client/PirateRadioClient.kt35
-rw-r--r--src/client/kotlin/space/autistic/radio/client/PirateRadioDataGenerator.kt62
-rw-r--r--src/client/kotlin/space/autistic/radio/client/PirateRadioEntityModelLayers.kt18
-rw-r--r--src/client/kotlin/space/autistic/radio/client/antenna/AntennaModel.kt19
-rw-r--r--src/client/kotlin/space/autistic/radio/client/antenna/AntennaModelFactory.kt7
-rw-r--r--src/client/kotlin/space/autistic/radio/client/antenna/NullModel.kt13
-rw-r--r--src/client/kotlin/space/autistic/radio/client/antenna/WasmAntennaFactory.kt97
-rw-r--r--src/client/kotlin/space/autistic/radio/client/entity/ElectronicsTraderEntityRenderer.kt23
-rw-r--r--src/client/kotlin/space/autistic/radio/client/gui/FmReceiverScreen.kt11
9 files changed, 285 insertions, 0 deletions
diff --git a/src/client/kotlin/space/autistic/radio/client/PirateRadioClient.kt b/src/client/kotlin/space/autistic/radio/client/PirateRadioClient.kt
new file mode 100644
index 0000000..54b7640
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/PirateRadioClient.kt
@@ -0,0 +1,35 @@
+package space.autistic.radio.client
+
+import com.mojang.brigadier.CommandDispatcher
+import net.fabricmc.api.ClientModInitializer
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
+import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry
+import net.minecraft.client.MinecraftClient
+import net.minecraft.command.CommandRegistryAccess
+import org.slf4j.LoggerFactory
+import space.autistic.radio.PirateRadio
+import space.autistic.radio.PirateRadio.MOD_ID
+import space.autistic.radio.PirateRadioEntityTypes
+import space.autistic.radio.client.entity.ElectronicsTraderEntityRenderer
+import space.autistic.radio.client.gui.FmReceiverScreen
+
+object PirateRadioClient : ClientModInitializer {
+    private val logger = LoggerFactory.getLogger(MOD_ID)
+
+    override fun onInitializeClient() {
+        EntityRendererRegistry.register(PirateRadioEntityTypes.ELECTRONICS_TRADER, ::ElectronicsTraderEntityRenderer)
+        PirateRadioEntityModelLayers.initialize()
+        ClientCommandRegistrationCallback.EVENT.register { dispatcher, _ ->
+            dispatcher.register(
+                ClientCommandManager.literal("fm").executes {
+                    it.source.client.send {
+                        it.source.client.setScreen(FmReceiverScreen())
+                    }
+                    0
+                }
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/PirateRadioDataGenerator.kt b/src/client/kotlin/space/autistic/radio/client/PirateRadioDataGenerator.kt
new file mode 100644
index 0000000..b5130a1
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/PirateRadioDataGenerator.kt
@@ -0,0 +1,62 @@
+package space.autistic.radio.client
+
+import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint
+import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator
+import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider
+import net.minecraft.data.client.BlockStateModelGenerator
+import net.minecraft.data.client.ItemModelGenerator
+import net.minecraft.data.client.Models
+import net.minecraft.data.server.recipe.RecipeExporter
+import net.minecraft.data.server.recipe.RecipeProvider
+import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder
+import net.minecraft.item.ItemStack
+import net.minecraft.recipe.Ingredient
+import net.minecraft.recipe.Recipe
+import net.minecraft.recipe.ShapelessRecipe
+import net.minecraft.recipe.book.CraftingRecipeCategory
+import net.minecraft.recipe.book.RecipeCategory
+import net.minecraft.registry.RegistryWrapper
+import net.minecraft.util.Identifier
+import net.minecraft.util.collection.DefaultedList
+import space.autistic.radio.PirateRadio.MOD_ID
+import space.autistic.radio.PirateRadioItems
+import java.util.concurrent.CompletableFuture
+
+class PirateRadioItemModelGenerator(output: FabricDataOutput) : FabricModelProvider(output) {
+    override fun generateBlockStateModels(modelGenerator: BlockStateModelGenerator) {
+    }
+
+    override fun generateItemModels(modelGenderator: ItemModelGenerator) {
+        modelGenderator.register(PirateRadioItems.SBC, Models.GENERATED)
+        modelGenderator.register(PirateRadioItems.WIRE, Models.GENERATED)
+        modelGenderator.register(PirateRadioItems.POWERBANK, Models.GENERATED)
+        modelGenderator.register(PirateRadioItems.FM_RECEIVER, Models.GENERATED)
+        modelGenderator.register(PirateRadioItems.STORAGE_CARD, Models.GENERATED)
+        modelGenderator.register(PirateRadioItems.DISPOSABLE_TRANSMITTER, Models.GENERATED)
+    }
+
+}
+
+class PirateRadioRecipeGenerator(
+    output: FabricDataOutput?,
+    registriesFuture: CompletableFuture<RegistryWrapper.WrapperLookup>?
+) : FabricRecipeProvider(output, registriesFuture) {
+    override fun generate(exporter: RecipeExporter) {
+        ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, PirateRadioItems.DISPOSABLE_TRANSMITTER)
+            .input(PirateRadioItems.SBC).input(PirateRadioItems.WIRE).input(PirateRadioItems.POWERBANK)
+            .input(PirateRadioItems.STORAGE_CARD)
+            .criterion("has_sbc", RecipeProvider.conditionsFromItem(PirateRadioItems.SBC)).offerTo(exporter)
+    }
+
+}
+
+object PirateRadioDataGenerator : DataGeneratorEntrypoint {
+    override fun onInitializeDataGenerator(fabricDataGenerator: FabricDataGenerator) {
+        val pack = fabricDataGenerator.createPack()
+
+        pack.addProvider(::PirateRadioItemModelGenerator)
+        pack.addProvider(::PirateRadioRecipeGenerator)
+    }
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/PirateRadioEntityModelLayers.kt b/src/client/kotlin/space/autistic/radio/client/PirateRadioEntityModelLayers.kt
new file mode 100644
index 0000000..765912d
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/PirateRadioEntityModelLayers.kt
@@ -0,0 +1,18 @@
+package space.autistic.radio.client
+
+import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry
+import net.minecraft.client.model.TexturedModelData
+import net.minecraft.client.render.entity.model.EntityModelLayer
+import net.minecraft.client.render.entity.model.VillagerResemblingModel
+import net.minecraft.util.Identifier
+import space.autistic.radio.PirateRadio
+
+object PirateRadioEntityModelLayers {
+    val ELECTRONICS_TRADER = EntityModelLayer(Identifier.of(PirateRadio.MOD_ID, "electronics-trader"), "main")
+
+    fun initialize() {
+        EntityModelLayerRegistry.registerModelLayer(ELECTRONICS_TRADER) {
+            TexturedModelData.of(VillagerResemblingModel.getModelData(), 64, 64)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/antenna/AntennaModel.kt b/src/client/kotlin/space/autistic/radio/client/antenna/AntennaModel.kt
new file mode 100644
index 0000000..74a7c96
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/antenna/AntennaModel.kt
@@ -0,0 +1,19 @@
+package space.autistic.radio.client.antenna
+
+import org.joml.Vector3d
+
+interface AntennaModel {
+    /**
+     * Returns the linear power level/gain to apply for a receiver at the given position. The receiver is assumed to be
+     * vertically oriented.
+     *
+     * Note: 1.0f = 0dB, 0.5f = -3dB (approx.), 0.1f = -10dB.
+     */
+    fun apply(position: Vector3d): Float
+
+    /**
+     * Returns whether to process block/material attenuation. Useful for "global" antennas (i.e. those that return a
+     * constant for all positions given to [apply]).
+     */
+    fun shouldAttenuate(): Boolean
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/antenna/AntennaModelFactory.kt b/src/client/kotlin/space/autistic/radio/client/antenna/AntennaModelFactory.kt
new file mode 100644
index 0000000..33a7087
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/antenna/AntennaModelFactory.kt
@@ -0,0 +1,7 @@
+package space.autistic.radio.client.antenna
+
+import org.joml.Quaterniond
+
+interface AntennaModelFactory {
+    fun create(orientation: Quaterniond): AntennaModel
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/antenna/NullModel.kt b/src/client/kotlin/space/autistic/radio/client/antenna/NullModel.kt
new file mode 100644
index 0000000..3c188b6
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/antenna/NullModel.kt
@@ -0,0 +1,13 @@
+package space.autistic.radio.client.antenna
+
+import org.joml.Vector3d
+
+class NullModel : AntennaModel {
+    override fun apply(position: Vector3d): Float {
+        return 0f
+    }
+
+    override fun shouldAttenuate(): Boolean {
+        return false
+    }
+}
diff --git a/src/client/kotlin/space/autistic/radio/client/antenna/WasmAntennaFactory.kt b/src/client/kotlin/space/autistic/radio/client/antenna/WasmAntennaFactory.kt
new file mode 100644
index 0000000..7181e95
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/antenna/WasmAntennaFactory.kt
@@ -0,0 +1,97 @@
+package space.autistic.radio.client.antenna
+
+import com.dylibso.chicory.experimental.aot.AotMachineFactory
+import com.dylibso.chicory.runtime.ExportFunction
+import com.dylibso.chicory.runtime.ImportValues
+import com.dylibso.chicory.runtime.Instance
+import com.dylibso.chicory.wasm.ChicoryException
+import com.dylibso.chicory.wasm.InvalidException
+import com.dylibso.chicory.wasm.Parser
+import com.dylibso.chicory.wasm.types.MemoryLimits
+import com.dylibso.chicory.wasm.types.Value
+import com.dylibso.chicory.wasm.types.ValueType
+import org.joml.Quaterniond
+import org.joml.Vector3d
+import space.autistic.radio.PirateRadio
+import java.util.logging.Level
+import java.util.logging.Logger
+
+class WasmAntennaFactory(moduleBytes: ByteArray) : AntennaModelFactory {
+    var failing = false
+    private val instanceBuilder = run {
+        try {
+            val module = Parser.parse(moduleBytes)
+            Instance.builder(module).withMachineFactory(AotMachineFactory(module)).withImportValues(defaultImports)
+                // capped at 1MB per antenna
+                .withMemoryLimits(MemoryLimits(0, 16))
+        } catch (e: ChicoryException) {
+            logger.log(Level.SEVERE, "Error while trying to parse antenna model.", e)
+            failing = true
+            null
+        }
+    }
+
+    override fun create(orientation: Quaterniond): AntennaModel {
+        if (failing) {
+            return NullModel()
+        }
+        try {
+            val instance = instanceBuilder!!.build()
+            // see basic module abi convention: https://github.com/WebAssembly/tool-conventions/blob/4487bbc2f5a0ad6b5ca76e233bdfa5ed4513dd8c/BasicModuleABI.md
+            var initialize: ExportFunction? = null
+            try {
+                initialize = instance.export("_initialize")
+            } catch (_: InvalidException) {
+                // export may not exist, it's fine
+            }
+            initialize?.apply()
+            // initialize antenna orientation
+            instance.export("set-orientation").apply(
+                orientation.x.toRawBits(),
+                orientation.y.toRawBits(),
+                orientation.z.toRawBits(),
+                orientation.w.toRawBits()
+            )
+            if (instance.exports().global("should-attenuate").type != ValueType.I32) {
+                logger.log(
+                    Level.SEVERE, "Error while trying to initialize antenna model: missing 'should-attenuate'"
+                )
+                failing = true
+                return NullModel()
+            }
+            val shouldAttenuate = instance.exports().global("should-attenuate").value != 0L
+            val apply = instance.export("apply")
+            return object : AntennaModel {
+                override fun apply(position: Vector3d): Float {
+                    if (failing) {
+                        return 0f
+                    }
+                    try {
+                        return Value.longToFloat(
+                            apply.apply(
+                                position.x.toRawBits(), position.y.toRawBits(), position.z.toRawBits()
+                            )[0]
+                        )
+                    } catch (e: ChicoryException) {
+                        logger.log(Level.SEVERE, "Error while trying to evaluate antenna model.", e)
+                        failing = true
+                        return 0f
+                    }
+                }
+
+                override fun shouldAttenuate(): Boolean {
+                    return shouldAttenuate
+                }
+            }
+        } catch (e: ChicoryException) {
+            logger.log(Level.SEVERE, "Error while trying to initialize antenna model.", e)
+            failing = true
+            return NullModel()
+        }
+    }
+
+    companion object {
+        private val defaultImports = ImportValues.builder().build()
+        private val logger = Logger.getLogger(PirateRadio.MOD_ID)
+    }
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/entity/ElectronicsTraderEntityRenderer.kt b/src/client/kotlin/space/autistic/radio/client/entity/ElectronicsTraderEntityRenderer.kt
new file mode 100644
index 0000000..91c29db
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/entity/ElectronicsTraderEntityRenderer.kt
@@ -0,0 +1,23 @@
+package space.autistic.radio.client.entity
+
+import net.minecraft.client.render.entity.EntityRendererFactory
+import net.minecraft.client.render.entity.MobEntityRenderer
+import net.minecraft.client.render.entity.model.VillagerResemblingModel
+import net.minecraft.util.Identifier
+import space.autistic.radio.PirateRadio
+import space.autistic.radio.client.PirateRadioEntityModelLayers
+import space.autistic.radio.entity.ElectronicsTraderEntity
+
+class ElectronicsTraderEntityRenderer(context: EntityRendererFactory.Context) :
+    MobEntityRenderer<ElectronicsTraderEntity, VillagerResemblingModel<ElectronicsTraderEntity>>(
+        context,
+        VillagerResemblingModel(context.getPart(PirateRadioEntityModelLayers.ELECTRONICS_TRADER)),
+        0.5f
+    ) {
+
+    companion object {
+        val TEXTURE = Identifier.of(PirateRadio.MOD_ID, "electronics-trader")
+    }
+
+    override fun getTexture(entity: ElectronicsTraderEntity?): Identifier = TEXTURE
+}
\ No newline at end of file
diff --git a/src/client/kotlin/space/autistic/radio/client/gui/FmReceiverScreen.kt b/src/client/kotlin/space/autistic/radio/client/gui/FmReceiverScreen.kt
new file mode 100644
index 0000000..4bd4db2
--- /dev/null
+++ b/src/client/kotlin/space/autistic/radio/client/gui/FmReceiverScreen.kt
@@ -0,0 +1,11 @@
+package space.autistic.radio.client.gui
+
+import net.minecraft.client.gui.screen.Screen
+import net.minecraft.text.Text
+
+class FmReceiverScreen : Screen(Text.translatable("pirate-radio.fm-receiver")) {
+
+    override fun init() {
+        // TODO
+    }
+}
\ No newline at end of file