This commit is contained in:
2026-04-22 17:57:09 +02:00
25 changed files with 574 additions and 113 deletions

View File

@@ -19,6 +19,7 @@ import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.item.Items
import org.neoflock.neocomputers.item.Tabs
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.utils.FontProvider
import org.neoflock.neocomputers.utils.GenericContainerScreen
import org.slf4j.Logger
@@ -40,6 +41,7 @@ object NeoComputers {
BlockEntities.registerPowerBlocks()
Menus.MENUS.register()
Tabs.TABS.register()
Sounds.SOUNDS.register()
ComponentRoles.mapDefaultTextures()
// i dont know why architectury wants two lambdas but whatever
EnvExecutor.runInEnv(Env.CLIENT) {{
@@ -70,6 +72,15 @@ object NeoComputers {
player ->
NodeSynchronizer.playerScreenClosed(player)
}
NetworkManager.registerReceiver(NetworkManager.c2s(),NodeSynchronizer.ScreenDataPayload.TYPE, NodeSynchronizer.ScreenDataPayload.CODEC, {
packet, ctx ->
val player = ctx.player
if(player is ServerPlayer) {
NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer)
}
})
// we have to do this because the datagen task runs in the physical server
EnvExecutor.runInEnv(Env.CLIENT) {{
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC, {
@@ -93,7 +104,6 @@ object NeoComputers {
// https://github.com/architectury/architectury-api/issues/518
NetworkManager.registerS2CPayloadType(NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC)
NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC)
}}
LOGGER.info("Registered!")

View File

@@ -2,28 +2,89 @@ package org.neoflock.neocomputers.block;
import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.util.RandomSource
import net.minecraft.world.Containers
import net.minecraft.world.InteractionResult
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.CombustionGeneratorBlock.Companion.COMBUSTGEN_ACTIVE
import org.neoflock.neocomputers.entity.BlockEntities
import org.neoflock.neocomputers.entity.ScreenEntity
import org.neoflock.neocomputers.gui.menu.Menus
import org.neoflock.neocomputers.gui.menu.ScreenMenu
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.entity.CaseBlockEntity
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.sounds.Sounds
class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance)) { // placeholder stuff
companion object {
val COMPUTER_RUNNING = BooleanProperty.create("running")!!
fun getLuminance(blockState: BlockState): Int {
return if(blockState.getValue(COMPUTER_RUNNING)) 3 else 0
}
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState)
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
builder.add(COMPUTER_RUNNING)
}
fun getMachine(level: BlockGetter, blockPos: BlockPos): CaseBlockEntity {
return level.getBlockEntity(blockPos) as CaseBlockEntity
}
override fun getSignal(
blockState: BlockState,
blockGetter: BlockGetter,
blockPos: BlockPos,
direction: Direction
): Int {
return getMachine(blockGetter, blockPos).redstoneOut[dirToIdx(direction.opposite)]
}
override fun onPlace(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
blockState2: BlockState,
bl: Boolean
) {
if(!level.isClientSide) {
level.updateNeighborsAt(blockPos, this)
getMachine(level, blockPos).refetchAllRedstone()
}
super.onPlace(blockState, level, blockPos, blockState2, bl)
}
override fun neighborChanged(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
block: Block,
blockPos2: BlockPos,
bl: Boolean
) {
super.neighborChanged(blockState, level, blockPos, block, blockPos2, bl)
if(!level.isClientSide) {
val dir = Direction.getNearest(blockPos2.center.subtract(blockPos.center))
getMachine(level, blockPos).refetchRedstone(dir)
}
}
class CaseBlock() : BaseBlock() { // placeholder stuff
override fun useWithoutItem(
blockState: BlockState,
level: Level,
@@ -32,13 +93,21 @@ class CaseBlock() : BaseBlock() { // placeholder stuff
blockHitResult: BlockHitResult
): InteractionResult {
if(!level.isClientSide) {
MenuRegistry.openMenu(player as ServerPlayer, object : MenuProvider {
override fun getDisplayName(): Component = Component.literal("Computer")
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
return Menus.CASE_MENU.get().create(i, inventory);
}
})
val ent = level.getBlockEntity(blockPos, BlockEntities.CASE_ENTITY.get()).get()
MenuRegistry.openMenu(player as ServerPlayer, ent)
NodeSynchronizer.registerPlayerScreen(player, ent)
}
return InteractionResult.SUCCESS
}
override fun onRemove(
blockState: BlockState,
level: Level,
blockPos: BlockPos,
blockState2: BlockState,
bl: Boolean
) {
Containers.dropContentsOnDestroy(blockState, blockState2, level, blockPos)
super.onRemove(blockState, level, blockPos, blockState2, bl)
}
}

View File

@@ -13,6 +13,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.FurnaceBlock
import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
@@ -30,15 +31,15 @@ class SolarGeneratorBlock : NodeBlock(), EntityBlock {
// TODO: make it glow when burning
class CombustionGeneratorBlock : NodeBlock, EntityBlock {
companion object {
val ACTIVE = BooleanProperty.create("active")
val COMBUSTGEN_ACTIVE = BooleanProperty.create("active")
fun getLuminance(blockState: BlockState): Int {
return if(blockState.getValue(ACTIVE)) 5 else 0
return if(blockState.getValue(COMBUSTGEN_ACTIVE)) 5 else 0
}
}
constructor(): super(Properties.of().sound(SoundType.STONE).lightLevel(CombustionGeneratorBlock::getLuminance)) {
registerDefaultState(defaultBlockState().setValue(ACTIVE, false))
registerDefaultState(defaultBlockState().setValue(COMBUSTGEN_ACTIVE, false))
}
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = CombustionGeneratorBlockEntity(blockPos, blockState).initNetworking()
@@ -60,12 +61,12 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock {
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
builder.add(ACTIVE)
builder.add(COMBUSTGEN_ACTIVE)
}
override fun animateTick(blockState: BlockState, level: Level, blockPos: BlockPos, randomSource: RandomSource) {
if(blockState.getValue(ACTIVE)) {
if(randomSource.nextDouble() < 0.1) level.playSound(null, blockPos, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.AMBIENT)
if(blockState.getValue(COMBUSTGEN_ACTIVE)) {
if(randomSource.nextDouble() < 0.1) level.playLocalSound(blockPos, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.AMBIENT, 1F, 1F, false)
val x = blockPos.x.toDouble()
val y = blockPos.y.toDouble()

View File

@@ -68,6 +68,27 @@ object NodeSynchronizer {
override fun type() = TYPE
}
class ScreenDataPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
companion object {
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_SYNC_ID)
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload {
val id = buf.readByteArray().decodeToString()
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
return ScreenDataPayload(id, buffer)
}
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenDataPayload) {
buf.writeByteArray(payload.entityTypeWireID.encodeToByteArray())
buf.writeBytes(payload.buffer)
}
}
}
override fun type() = TYPE
}
val screenMap = mutableMapOf<ServerPlayer, NodeBlockEntity>()
fun playerScreenClosed(player: ServerPlayer) {
@@ -87,6 +108,10 @@ object NodeSynchronizer {
NetworkManager.sendToPlayer(player, ScreenPayload(nodeTypeToWireID(ent.type), buf))
}
}
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
NetworkManager.sendToServer(ScreenDataPayload("", friendlyByteBuf))
}
}
abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) {
@@ -119,6 +144,8 @@ abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: Bl
// Encodes data meant for the associated screen of a player
open fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {}
open fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) {}
private var stateIsDirty = true
open fun getNeighbourEntities(): List<BlockEntity> {

View File

@@ -75,6 +75,11 @@ object BlockEntities {
::CombustionGeneratorBlockEntity, mutableSetOf(Blocks.REDSTONEIO_BLOCK.get()), BullshitFix()
)
}
val CASE_ENTITY: RegistrySupplier<BlockEntityType<CaseBlockEntity>> = BLOCKENTITIES.register("case") {
BlockEntityType(
::CaseBlockEntity, mutableSetOf(Blocks.CASE_BLOCK.get()), BullshitFix()
)
}
fun registerPowerBlocks() {
PowerManager.registerPowerBlockEntity(CAPACITOR_ENTITY.get())

View File

@@ -0,0 +1,210 @@
package org.neoflock.neocomputers.entity
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.BiomeAmbientSoundsHandler
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
import net.minecraft.client.resources.sounds.SoundInstance
import net.minecraft.client.sounds.LoopingAudioStream
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundSource
import net.minecraft.world.Container
import net.minecraft.world.ContainerHelper
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.CaseBlock
import org.neoflock.neocomputers.block.NodeBlockEntity
import org.neoflock.neocomputers.block.dirToIdx
import org.neoflock.neocomputers.gui.menu.CaseMenu
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.network.PowerRole
import org.neoflock.neocomputers.sounds.ComputerRunningSoundInstance
import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.utils.GenericContainer
import java.time.Duration
import kotlin.math.min
class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider {
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY)
var isOn = false
var soundInstance: SoundInstance? = null
override val node = object : Networking.Node() {
override var powerRole = PowerRole.STORAGE
override var energyCapacity: Long = 500
}
override fun encodeDownstreamData(packet: FriendlyByteBuf) {
super.encodeDownstreamData(packet)
packet.writeBoolean(isOn)
}
override fun syncWithUpstream(packet: FriendlyByteBuf) {
super.syncWithUpstream(packet)
setRunning(packet.readBoolean())
}
override fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) {
val c = packet.readByte().toInt()
if(c == 0x01) {
start()
}
if(c == 0x02) {
stop()
}
}
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
super.encodeScreenData(player, packet)
packet.writeBoolean(isOn)
}
val redstoneIn = Array(Direction.entries.size) {0}
val redstoneOut = Array(Direction.entries.size) {0}
fun refetchRedstone(dir: Direction) {
val src = blockPos.offset(dir.stepX, dir.stepY, dir.stepZ)
val cur = level?.getSignal(src, dir) ?: 0
val idx = dirToIdx(dir)
if(redstoneIn[idx] != cur) {
onRedstoneSignalChanged(dir, redstoneIn[idx], cur)
}
redstoneIn[idx] = cur
}
fun refetchAllRedstone() {
Direction.entries.forEach { refetchRedstone(it) }
}
fun sendMachineEvent(event: MachineEvent) {
stacks.forEach {
val item = it.item
if(item is ComponentItem) {
item.onMachineEvent(it, this, event)
}
}
}
fun onRedstoneSignalChanged(dir: Direction, oldValue: Int, newValue: Int) {
sendMachineEvent(MachineRedstoneEvent(this, dir, oldValue, newValue))
Networking.emitMessage(node, Networking.ComputerUncheckedSignal(node, "redstone_changed", arrayOf(node.address.toString(), dirToIdx(dir), oldValue, newValue)))
NeoComputers.LOGGER.info("redstone in direction ${dir.name} changed from $oldValue to $newValue")
if(oldValue == 0) {
// Rising edge
start()
}
setChanged()
}
override fun getMachineBlockPosition(): BlockPos = blockPos
override fun getMachineLevel(): Level = level!!
override fun isRunning(): Boolean = isOn
fun setRunning(value: Boolean) {
if(isOn == value) return
NeoComputers.LOGGER.info("[${node.address}] Going from $isOn to $value")
isOn = value
val world = level ?: return
blockState?.setValue(CaseBlock.COMPUTER_RUNNING, isOn)
if(world.isClientSide) {
if(value) {
soundInstance = ComputerRunningSoundInstance(this, Sounds.COMPUTER_RUNNING.get(), SoundSource.AMBIENT)
Minecraft.getInstance().soundManager.play(soundInstance!!)
} else {
Minecraft.getInstance().soundManager.stop(soundInstance!!)
soundInstance = null
}
return
}
// Server-side stuff!!
world.onBlockStateChange(blockPos, blockState, blockState)
sendMachineEvent(MachinePowerEvent(this, isOn))
}
override fun start(): Boolean {
setRunning(true)
return isOn
}
override fun stop(): Boolean {
setRunning(false)
return isOn
}
override fun crash(error: String): Boolean {
NeoComputers.LOGGER.warn("Crashing cases is not implemented yet lol")
return false
}
override fun getLastError(): String? = null
override fun getMachineNode(): Networking.Node = node
override fun getRedstoneInput(direction: Direction): Int = redstoneIn[dirToIdx(direction)]
override fun getRedstoneOutput(direction: Direction): Int = redstoneOut[dirToIdx(direction)]
override fun setRedstoneOutput(direction: Direction, newValue: Int): Int {
val idx = dirToIdx(direction)
val old = redstoneOut[idx]
redstoneOut[idx] = newValue
return old
}
override fun beepAsync(frequency: Int, duration: Duration, volume: Double): Boolean {
NeoComputers.LOGGER.warn("beep not yet implemented")
return true
}
override fun getMachineMemoryTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getMemoryCapacity(it) }.sum().toLong()
override fun getMachineMemoryUsed(): Long = 0
override fun getMachineComponentsUsed(): Long = node.connections.size.toLong()
override fun getMachineComponentsTotal(): Long = stacks.mapNotNull { (it.item as? ComponentItem)?.getComponentCapacity(it) }.sum().toLong()
override fun getItems(): NonNullList<ItemStack> = stacks
override fun stillValid(player: Player): Boolean = true
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.loadAdditional(compoundTag, provider)
node.energy = min(node.energyCapacity, compoundTag.getLong("energy"))
//isOn = compoundTag.getBoolean("powerOn")
ContainerHelper.loadAllItems(compoundTag, getItems(), provider)
}
override fun saveAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
super.saveAdditional(compoundTag, provider)
compoundTag.putLong("energy", node.energy)
//compoundTag.putBoolean("powerOn", isOn)
ContainerHelper.saveAllItems(compoundTag, getItems(), provider)
}
override fun getDisplayName(): Component? = Component.literal("Computer")
override fun createMenu(i: Int, inventory: Inventory, player: Player) = CaseMenu(i, inventory, this)
override fun canPlaceItem(i: Int, itemStack: ItemStack): Boolean = false
override fun canTakeItem(container: Container, i: Int, itemStack: ItemStack): Boolean = false
override fun setChanged() {
super.setChanged()
}
override fun setRemoved() {
setRunning(false)
super.setRemoved()
}
}

View File

@@ -5,20 +5,14 @@ import net.minecraft.core.HolderLookup
import net.minecraft.core.NonNullList
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvents
import net.minecraft.sounds.SoundSource
import net.minecraft.world.ContainerHelper
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.ChestBlock
import net.minecraft.world.level.block.FurnaceBlock
import net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.CombustionGeneratorBlock
import org.neoflock.neocomputers.block.NodeBlockEntity
@@ -81,7 +75,7 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
override fun setChanged() {
super.setChanged()
level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.ACTIVE, burningTimeRemaining > 0))
level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.COMBUSTGEN_ACTIVE, burningTimeRemaining > 0))
}
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {

View File

@@ -2,26 +2,39 @@ package org.neoflock.neocomputers.entity
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.level.Level
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.Networking
import java.time.Duration
abstract class MachineEvent {
abstract val machine: MachineEntity
}
data class MachineRedstoneEvent(override val machine: MachineEntity, val side: Direction): MachineEvent()
data class MachineRedstoneEvent(override val machine: MachineEntity, val side: Direction, val oldValue: Int, val newValue: Int): MachineEvent()
data class MachinePowerEvent(override val machine: MachineEntity, val nowRunning: Boolean): MachineEvent()
interface MachineEntity {
// Block position of machine, for wireless tech
fun getBlockPosition(): BlockPos
fun getMachineBlockPosition(): BlockPos
fun getMachineLevel(): Level
fun beepAsync(frequency: Int, duration: Duration, volume: Double): Boolean
fun isRunning(): Boolean
fun start(): Boolean
fun stop(): Boolean
fun crash(error: String): Boolean
fun getLastError(): String?
fun getMachineNode(): Networking.Node
// Some metadata
fun getMachineMemoryTotal(): Long
fun getMachineMemoryUsed(): Long
fun getMachineComponentsUsed(): Long
fun getMachineComponentsTotal(): Long
// Redstone signals
fun getRedstoneInput(direction: Direction): Int
fun getRedstoneOutput(direction: Direction): Int

View File

@@ -1,49 +1,15 @@
package org.neoflock.neocomputers.gui.menu;
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.Container
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.gui.menu.Menus;
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.entity.CaseBlockEntity
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.gui.widget.ComponentSlot
import org.neoflock.neocomputers.gui.widget.ComponentSlotRequirement
import org.neoflock.neocomputers.gui.widget.DynamicSlot
import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.GenericContainerMenu
// lowest IQ machine to exist
class DumbMachine: MachineEntity {
override fun getBlockPosition() = BlockPos.ZERO!!
override fun isRunning() = false
override fun start() = false
override fun stop() = false
override fun crash(error: String) = false
override fun getMachineNode() = Networking.Node()
override fun getRedstoneInput(direction: Direction): Int = 0
override fun getRedstoneOutput(direction: Direction): Int = 0
override fun setRedstoneOutput(direction: Direction, newValue: Int): Int = 0
}
class CaseMenu : GenericContainerMenu {
constructor(i: Int, inv: Inventory) : this(i, inv, SimpleContainer(7))
open class CaseMenu : GenericContainerMenu {
open val eepromRequirement = ComponentSlotRequirement(1, ComponentRoles.FIRMWARE)
open val slotRequirements = listOf(
listOf(ComponentSlotRequirement(1, ComponentRoles.CARD), ComponentSlotRequirement(1, ComponentRoles.CARD)),
@@ -51,18 +17,23 @@ class CaseMenu : GenericContainerMenu {
listOf(ComponentSlotRequirement(1, ComponentRoles.STORAGE)),
)
constructor(i: Int, inv: Inventory) : this(i, inv, SimpleContainer(7))
constructor(i: Int, inv: Inventory, container: Container) : super(Menus.CASE_MENU.get(), i, container) {
this.addInventorySlots(inv, 8, 84)
this.addSlot(ComponentSlot(this.container!!, 0, 20, 34, DumbMachine(), eepromRequirement))
val machine = container as? CaseBlockEntity
this.addSlot(ComponentSlot(this.container, 0, 20, 34, machine, eepromRequirement))
var i = 1
for ((col, slotCol) in slotRequirements.withIndex()) {
for ((row, slotReq) in slotCol.withIndex()) {
this.addSlot(ComponentSlot(this.container!!, i, 98+(col*22), 18*(row+1)-2, DumbMachine(), slotReq))
this.addSlot(ComponentSlot(this.container, i, 98+(col*22), 18*(row+1)-2, machine, slotReq))
i++
}
}
// for (int col=1; col<4; col++) {
// for (int row=1; row<4; row++) {
// int i = (row-1)*3+(col-1);

View File

@@ -1,14 +1,13 @@
package org.neoflock.neocomputers.gui.menu;
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.gui.menu.Menus;
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.utils.GenericContainerMenu
class ScreenMenu(i: Int, inv: Inventory) : AbstractContainerMenu(Menus.SCREEN_MENU.get(), i) {
override fun stillValid(player: Player) = true // TODO: implement this properly
override fun quickMoveStack(player: Player, i: Int): ItemStack = ItemStack.EMPTY // there's no container here anyways
class ScreenMenu(i: Int, inv: Inventory) : GenericContainerMenu(Menus.SCREEN_MENU.get(), i, SimpleContainer(0)) {
}

View File

@@ -38,7 +38,7 @@ object ProgressBar {
guiGraphics.fill(x+1, y+height-1, x+width, y+height, 0xFFFFFFFF.toInt()) // bottom right corner + bottom edge
guiGraphics.fill(x+width-1, y+height-1, x+width, y+1, 0xFFFFFFFF.toInt()) // right edge
val frac = value.toFloat() / max.toFloat()
val frac = if(max == 0L) 0.0f else value.toFloat() / max.toFloat()
val linew = ceil(frac*(width-2).toFloat())
guiGraphics.fill(
RenderType.gui(),

View File

@@ -4,16 +4,19 @@ import com.mojang.blaze3d.vertex.BufferBuilder
import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.Tesselator
import com.mojang.blaze3d.vertex.VertexFormat
import io.netty.buffer.Unpooled
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.ImageButton
import net.minecraft.client.gui.components.WidgetSprites
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.NodeSynchronizer
import org.neoflock.neocomputers.gui.menu.CaseMenu
import org.neoflock.neocomputers.gui.widget.ButtonSprites
import org.neoflock.neocomputers.gui.widget.ImagerButton
@@ -27,18 +30,26 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
// private val BTN_ENABLED_HOVER: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/button/power/enabled_hover.png")
// private val BTN_DISABLED_HOVER: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "textures/gui/power/disabled_hover.png")
private var btn: ImagerButton? = null;
private var btn: ImagerButton? = null
override fun shouldCenterTitle(): Boolean = false
var isOn = false
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
super.processScreenStatePacket(buf)
isOn = buf.readBoolean()
btn?.pressed = isOn
}
constructor(abstractContainerMenu: CaseMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
btn = ImagerButton(
15, 15,
18, 18,
ButtonSprites(BTN, 18, 18, 36, 36)
) {
var btn = it as ImagerButton
btn.pressed = !btn.pressed
val buf = FriendlyByteBuf(Unpooled.buffer())
buf.writeByte(if(isOn) 0x02 else 0x01)
NodeSynchronizer.sendScreenInteraction(buf)
}
// addRenderableWidget(btn!!)
}
@@ -47,7 +58,6 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
val relX = (this.width - this.imageWidth) / 2
val relY = (this.height - this.imageHeight) / 2
btn!!.x = relX+70
btn!!.y = relY+33
btn!!.render(guiGraphics, i, j, f) // minecraft SUCKSSS
@@ -64,5 +74,4 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
} else return false
}
}

View File

@@ -85,7 +85,7 @@ data class ComponentSlotRequirement(val tier: Int, val role: String) {
// Tier 0 allows ALL tiers, making it completely untiered.
// Role determines what the role is.
class ComponentSlot(container: Container, slot: Int, x: Int, y: Int, val machine: MachineEntity, val requirement: ComponentSlotRequirement): DynamicSlot(container, slot, x, y) {
class ComponentSlot(container: Container, slot: Int, x: Int, y: Int, val machine: MachineEntity?, val requirement: ComponentSlotRequirement): DynamicSlot(container, slot, x, y) {
override fun draw(graphics: GuiGraphics, relX: Int, relY: Int, mouseX: Int, mouseY: Int) {
super.draw(graphics, relX, relY, mouseX, mouseY)
if(!hasItem()) {
@@ -115,16 +115,20 @@ class ComponentSlot(container: Container, slot: Int, x: Int, y: Int, val machine
override fun set(itemStack: ItemStack) {
super.set(itemStack)
val item = itemStack.item
if(item is ComponentItem) {
item.whenComponentPlaced(itemStack, machine, requirement.role)
if(machine != null) {
val item = itemStack.item
if (item is ComponentItem) {
item.whenComponentPlaced(itemStack, machine, requirement.role)
}
}
}
override fun onTake(player: Player, itemStack: ItemStack) {
val item = itemStack.item
if(item is ComponentItem) {
item.whenComponentTaken(itemStack, machine, requirement.role)
if(machine != null) {
val item = itemStack.item
if (item is ComponentItem) {
item.whenComponentTaken(itemStack, machine, requirement.role)
}
}
super.onTake(player, itemStack)
}

View File

@@ -21,11 +21,13 @@ interface ComponentItem {
if(oldNode != null) Networking.removeNode(oldNode) // did a mod forget to call whenComponentTaken?
val node = toComponentNode(itemStack, machine) ?: return
Networking.addNode(node)
machine.getMachineNode().connectTo(node)
}
// Component taken, and thus removed
fun whenComponentTaken(itemStack: ItemStack, machine: MachineEntity, previousRole: String) {
val node = getComponentNode(itemStack) ?: return
node.disconnectFrom(machine.getMachineNode())
Networking.removeNode(node)
}

View File

@@ -6,6 +6,7 @@ import net.minecraft.core.component.DataComponentType
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.NeoComputers
import java.nio.ByteBuffer
object DataComponents {
val ADDRESS = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "address"),
@@ -15,7 +16,7 @@ object DataComponents {
val READONLY = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "readonly"),
DataComponentType.builder<Boolean>().persistent(Codec.BOOL).build())
val EEPROM_CODE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_code"),
DataComponentType.builder<String>().persistent(Codec.STRING).build())
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
val EEPROM_DATA = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_data"),
DataComponentType.builder<String>().persistent(Codec.STRING).build())
DataComponentType.builder<ByteBuffer>().persistent(Codec.BYTE_BUFFER).build())
}

View File

@@ -8,15 +8,15 @@ import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting
import java.util.UUID
import java.nio.ByteBuffer
fun getEEPROMProperties(): Item.Properties = Item.Properties()
.component(DataComponents.EEPROM_CODE, "")
.component(DataComponents.EEPROM_DATA, "")
fun getEEPROMProperties(codeCap: Int, dataCap: Int): Item.Properties = Item.Properties()
.component(DataComponents.EEPROM_CODE, ByteBuffer.allocate(codeCap))
.component(DataComponents.EEPROM_DATA, ByteBuffer.allocate(dataCap))
.component(DataComponents.LABEL, "")
.component(DataComponents.READONLY, false)
open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: Int): Item(getEEPROMProperties()), ComponentItem {
open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: Int): Item(getEEPROMProperties(codeCapacity, dataCapacity)), ComponentItem {
override fun getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.FIRMWARE)
override fun getComponentTier(itemStack: ItemStack): Int = tier
@@ -39,12 +39,10 @@ open class EEPROMItem(val tier: Int, val codeCapacity: Int, val dataCapacity: In
tooltipFlag: TooltipFlag
) {
if(tooltipFlag.isAdvanced) {
val code = itemStack.get(DataComponents.EEPROM_CODE) ?: ""
val data = itemStack.get(DataComponents.EEPROM_DATA) ?: ""
val codeSize = itemStack.get(DataComponents.EEPROM_CODE)?.position() ?: 0
val dataSize = itemStack.get(DataComponents.EEPROM_DATA)?.position() ?: 0
val addr = itemStack.get(DataComponents.ADDRESS)
val readonly = itemStack.get(DataComponents.READONLY) ?: false
val codeSize = code.encodeToByteArray().size
val dataSize = data.encodeToByteArray().size
val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
list.addLast(addrComp)
list.addLast(Component.translatable("neocomputers.eeprom.codeused", Formatting.formatMemory(codeSize.toLong()),

View File

@@ -20,7 +20,7 @@ open class GPUCard(val tier: Int, val vram: Long): Item(Properties()), Component
super.whenComponentPlaced(itemStack, machine, newRole)
}
// TODO: Modem Component
// TODO: GPU Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun appendHoverText(

View File

@@ -2,11 +2,14 @@ package org.neoflock.neocomputers.item
import dev.architectury.registry.CreativeTabRegistry
import dev.architectury.registry.registries.DeferredRegister
import net.minecraft.client.Minecraft
import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.CreativeModeTab
import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.NeoComputers
import java.nio.ByteBuffer
object Tabs {
val TABS: DeferredRegister<CreativeModeTab> =
@@ -53,11 +56,24 @@ object Tabs {
output.accept(ItemStack(Items.EE0.get()))
val luaBios = ItemStack(Items.EE0.get())
luaBios.set(DataComponents.LABEL, "Lua BIOS")
luaBios.set(DataComponents.EEPROM_CODE, "error('hi')")
luaBios.set(DataComponents.EEPROM_DATA, "random garbage")
output.accept(luaBios)
// Criminal black magic to put LuaBIOS EEPROM in the tabs
do {
val luaBios = ItemStack(Items.EE0.get())
val res = Minecraft.getInstance().resourceManager.getResourceOrThrow(
ResourceLocation.fromNamespaceAndPath(
NeoComputers.MODID,
"lua/oc_bios.lua"
)
)
val stream = res.openAsReader()
val code = stream.readText().encodeToByteArray()
stream.close()
val codeBuf = ByteBuffer.allocate(code.size)
codeBuf.put(code)
luaBios.set(DataComponents.LABEL, "Lua BIOS")
luaBios.set(DataComponents.EEPROM_CODE, codeBuf)
output.accept(luaBios)
} while(false)
}
}
}

View File

@@ -0,0 +1,37 @@
package org.neoflock.neocomputers.sounds
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
import net.minecraft.client.resources.sounds.MinecartSoundInstance
import net.minecraft.client.resources.sounds.SoundInstance
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import org.neoflock.neocomputers.entity.MachineEntity
class ComputerRunningSoundInstance: AbstractTickableSoundInstance {
val machine: MachineEntity
fun updatePosition() {
val pos = machine.getMachineBlockPosition()
this.x = pos.x.toDouble() + 0.5
this.y = pos.y.toDouble() + 0.5
this.z = pos.z.toDouble() + 0.5
}
constructor(machine: MachineEntity, soundEvent: SoundEvent, soundSource: SoundSource): super(soundEvent, soundSource, SoundInstance.createUnseededRandom()) {
this.machine = machine
this.looping = true
this.delay = 0
this.volume = 1.0F
this.pitch = 1.0F
updatePosition()
}
override fun tick() {
if(!machine.isRunning()) {
this.stop()
} else {
updatePosition()
}
}
}

View File

@@ -0,0 +1,17 @@
package org.neoflock.neocomputers.sounds
import dev.architectury.registry.registries.DeferredRegister
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import org.neoflock.neocomputers.NeoComputers
object Sounds {
val SOUNDS = DeferredRegister.create(NeoComputers.MODID, Registries.SOUND_EVENT)!!
val COMPUTER_RUNNING = registerSound("computer_running")
fun registerSound(name: String) = SOUNDS.register(name) {
SoundEvent.createVariableRangeEvent(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, name))
}!!
}

View File

@@ -62,7 +62,7 @@ interface GenericContainer : Container {
}
}
abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var container: Container?): AbstractContainerMenu(menuType, id) {
abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var container: Container): AbstractContainerMenu(menuType, id) {
fun addInventorySlots(inventory: Inventory, x: Int, y: Int) {
// Based off the code in ChestMenu
for (i in 0..2) {
@@ -88,7 +88,7 @@ abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var containe
val stack = slot.item
val copied = stack.copy()
val contSize = container!!.containerSize
val contSize = container.containerSize
if(i < contSize) {
if(!this.moveItemStackTo(stack, contSize, slots.size, true)) {
@@ -108,7 +108,7 @@ abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var containe
}
override fun stillValid(player: Player): Boolean {
return container!!.stillValid(player)
return container.stillValid(player)
}
}
@@ -148,9 +148,13 @@ abstract class GenericContainerScreen<T: GenericContainerMenu>(menu: T, inventor
}
}
open fun renderCustomOverlay(graphics: GuiGraphics, mouseX: Int, mouseY: Int, blend: Float) {
}
override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
super.render(graphics, mouseX, mouseY, something)
renderCustomOverlay(graphics, mouseX, mouseY, something)
if(shouldRenderTooltip()) super.renderTooltip(graphics, mouseX, mouseY)
}
}