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

View File

@@ -2,28 +2,89 @@ package org.neoflock.neocomputers.block;
import dev.architectury.registry.menu.MenuRegistry import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.core.BlockPos 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.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.InteractionResult
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.level.BlockGetter
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level 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.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.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 net.minecraft.world.phys.BlockHitResult
import org.neoflock.neocomputers.NeoComputers 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.BlockEntities
import org.neoflock.neocomputers.entity.ScreenEntity import org.neoflock.neocomputers.entity.CaseBlockEntity
import org.neoflock.neocomputers.gui.menu.Menus import org.neoflock.neocomputers.entity.MachineEntity
import org.neoflock.neocomputers.gui.menu.ScreenMenu import org.neoflock.neocomputers.sounds.Sounds
import org.neoflock.neocomputers.network.Networking
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( override fun useWithoutItem(
blockState: BlockState, blockState: BlockState,
level: Level, level: Level,
@@ -32,13 +93,21 @@ class CaseBlock() : BaseBlock() { // placeholder stuff
blockHitResult: BlockHitResult blockHitResult: BlockHitResult
): InteractionResult { ): InteractionResult {
if(!level.isClientSide) { if(!level.isClientSide) {
MenuRegistry.openMenu(player as ServerPlayer, object : MenuProvider { val ent = level.getBlockEntity(blockPos, BlockEntities.CASE_ENTITY.get()).get()
override fun getDisplayName(): Component = Component.literal("Computer") MenuRegistry.openMenu(player as ServerPlayer, ent)
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu { NodeSynchronizer.registerPlayerScreen(player, ent)
return Menus.CASE_MENU.get().create(i, inventory);
}
})
} }
return InteractionResult.SUCCESS 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.Level
import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.EntityBlock 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.SoundType
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
@@ -30,15 +31,15 @@ class SolarGeneratorBlock : NodeBlock(), EntityBlock {
// TODO: make it glow when burning // TODO: make it glow when burning
class CombustionGeneratorBlock : NodeBlock, EntityBlock { class CombustionGeneratorBlock : NodeBlock, EntityBlock {
companion object { companion object {
val ACTIVE = BooleanProperty.create("active") val COMBUSTGEN_ACTIVE = BooleanProperty.create("active")
fun getLuminance(blockState: BlockState): Int { 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)) { 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() 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?>) { 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) { override fun animateTick(blockState: BlockState, level: Level, blockPos: BlockPos, randomSource: RandomSource) {
if(blockState.getValue(ACTIVE)) { if(blockState.getValue(COMBUSTGEN_ACTIVE)) {
if(randomSource.nextDouble() < 0.1) level.playSound(null, blockPos, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.AMBIENT) if(randomSource.nextDouble() < 0.1) level.playLocalSound(blockPos, SoundEvents.FURNACE_FIRE_CRACKLE, SoundSource.AMBIENT, 1F, 1F, false)
val x = blockPos.x.toDouble() val x = blockPos.x.toDouble()
val y = blockPos.y.toDouble() val y = blockPos.y.toDouble()

View File

@@ -68,6 +68,27 @@ object NodeSynchronizer {
override fun type() = TYPE 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>() val screenMap = mutableMapOf<ServerPlayer, NodeBlockEntity>()
fun playerScreenClosed(player: ServerPlayer) { fun playerScreenClosed(player: ServerPlayer) {
@@ -87,6 +108,10 @@ object NodeSynchronizer {
NetworkManager.sendToPlayer(player, ScreenPayload(nodeTypeToWireID(ent.type), buf)) 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) { 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 // Encodes data meant for the associated screen of a player
open fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {} open fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {}
open fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) {}
private var stateIsDirty = true private var stateIsDirty = true
open fun getNeighbourEntities(): List<BlockEntity> { open fun getNeighbourEntities(): List<BlockEntity> {

View File

@@ -75,6 +75,11 @@ object BlockEntities {
::CombustionGeneratorBlockEntity, mutableSetOf(Blocks.REDSTONEIO_BLOCK.get()), BullshitFix() ::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() { fun registerPowerBlocks() {
PowerManager.registerPowerBlockEntity(CAPACITOR_ENTITY.get()) 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.core.NonNullList
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer 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.ContainerHelper
import net.minecraft.world.MenuProvider import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level 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 net.minecraft.world.level.block.state.BlockState
import org.neoflock.neocomputers.block.CombustionGeneratorBlock import org.neoflock.neocomputers.block.CombustionGeneratorBlock
import org.neoflock.neocomputers.block.NodeBlockEntity import org.neoflock.neocomputers.block.NodeBlockEntity
@@ -81,7 +75,7 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
override fun setChanged() { override fun setChanged() {
super.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) { 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.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.world.level.Level
import org.neoflock.neocomputers.item.ComponentItem import org.neoflock.neocomputers.item.ComponentItem
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import java.time.Duration
abstract class MachineEvent { abstract class MachineEvent {
abstract val machine: MachineEntity 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 { interface MachineEntity {
// Block position of machine, for wireless tech // 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 isRunning(): Boolean
fun start(): Boolean fun start(): Boolean
fun stop(): Boolean fun stop(): Boolean
fun crash(error: String): Boolean fun crash(error: String): Boolean
fun getLastError(): String?
fun getMachineNode(): Networking.Node fun getMachineNode(): Networking.Node
// Some metadata
fun getMachineMemoryTotal(): Long
fun getMachineMemoryUsed(): Long
fun getMachineComponentsUsed(): Long
fun getMachineComponentsTotal(): Long
// Redstone signals // Redstone signals
fun getRedstoneInput(direction: Direction): Int fun getRedstoneInput(direction: Direction): Int
fun getRedstoneOutput(direction: Direction): Int fun getRedstoneOutput(direction: Direction): Int

View File

@@ -1,49 +1,15 @@
package org.neoflock.neocomputers.gui.menu; 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.Container
import net.minecraft.world.SimpleContainer import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.gui.menu.Menus; import org.neoflock.neocomputers.entity.CaseBlockEntity
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.gui.widget.ComponentRoles import org.neoflock.neocomputers.gui.widget.ComponentRoles
import org.neoflock.neocomputers.gui.widget.ComponentSlot import org.neoflock.neocomputers.gui.widget.ComponentSlot
import org.neoflock.neocomputers.gui.widget.ComponentSlotRequirement 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 import org.neoflock.neocomputers.utils.GenericContainerMenu
// lowest IQ machine to exist open class CaseMenu : GenericContainerMenu {
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 val eepromRequirement = ComponentSlotRequirement(1, ComponentRoles.FIRMWARE) open val eepromRequirement = ComponentSlotRequirement(1, ComponentRoles.FIRMWARE)
open val slotRequirements = listOf( open val slotRequirements = listOf(
listOf(ComponentSlotRequirement(1, ComponentRoles.CARD), ComponentSlotRequirement(1, ComponentRoles.CARD)), listOf(ComponentSlotRequirement(1, ComponentRoles.CARD), ComponentSlotRequirement(1, ComponentRoles.CARD)),
@@ -51,18 +17,23 @@ class CaseMenu : GenericContainerMenu {
listOf(ComponentSlotRequirement(1, ComponentRoles.STORAGE)), 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) { constructor(i: Int, inv: Inventory, container: Container) : super(Menus.CASE_MENU.get(), i, container) {
this.addInventorySlots(inv, 8, 84) 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 var i = 1
for ((col, slotCol) in slotRequirements.withIndex()) { for ((col, slotCol) in slotRequirements.withIndex()) {
for ((row, slotReq) in slotCol.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++ i++
} }
} }
// for (int col=1; col<4; col++) { // for (int col=1; col<4; col++) {
// for (int row=1; row<4; row++) { // for (int row=1; row<4; row++) {
// int i = (row-1)*3+(col-1); // int i = (row-1)*3+(col-1);

View File

@@ -1,14 +1,13 @@
package org.neoflock.neocomputers.gui.menu; package org.neoflock.neocomputers.gui.menu;
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.gui.menu.Menus; import org.neoflock.neocomputers.gui.menu.Menus;
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.MenuType import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.utils.GenericContainerMenu
class ScreenMenu(i: Int, inv: Inventory) : AbstractContainerMenu(Menus.SCREEN_MENU.get(), i) { class ScreenMenu(i: Int, inv: Inventory) : GenericContainerMenu(Menus.SCREEN_MENU.get(), i, SimpleContainer(0)) {
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
} }

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+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 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()) val linew = ceil(frac*(width-2).toFloat())
guiGraphics.fill( guiGraphics.fill(
RenderType.gui(), 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.DefaultVertexFormat
import com.mojang.blaze3d.vertex.Tesselator import com.mojang.blaze3d.vertex.Tesselator
import com.mojang.blaze3d.vertex.VertexFormat import com.mojang.blaze3d.vertex.VertexFormat
import io.netty.buffer.Unpooled
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.components.ImageButton import net.minecraft.client.gui.components.ImageButton
import net.minecraft.client.gui.components.WidgetSprites import net.minecraft.client.gui.components.WidgetSprites
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import org.neoflock.neocomputers.block.NodeSynchronizer
import org.neoflock.neocomputers.gui.menu.CaseMenu import org.neoflock.neocomputers.gui.menu.CaseMenu
import org.neoflock.neocomputers.gui.widget.ButtonSprites import org.neoflock.neocomputers.gui.widget.ButtonSprites
import org.neoflock.neocomputers.gui.widget.ImagerButton 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_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 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 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) { constructor(abstractContainerMenu: CaseMenu, inventory: Inventory, component: Component) : super(abstractContainerMenu, inventory, component) {
btn = ImagerButton( btn = ImagerButton(
15, 15, 15, 15,
18, 18, 18, 18,
ButtonSprites(BTN, 18, 18, 36, 36) ButtonSprites(BTN, 18, 18, 36, 36)
) { ) {
var btn = it as ImagerButton val buf = FriendlyByteBuf(Unpooled.buffer())
btn.pressed = !btn.pressed buf.writeByte(if(isOn) 0x02 else 0x01)
NodeSynchronizer.sendScreenInteraction(buf)
} }
// addRenderableWidget(btn!!) // addRenderableWidget(btn!!)
} }
@@ -47,7 +58,6 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
val relX = (this.width - this.imageWidth) / 2 val relX = (this.width - this.imageWidth) / 2
val relY = (this.height - this.imageHeight) / 2 val relY = (this.height - this.imageHeight) / 2
btn!!.x = relX+70 btn!!.x = relX+70
btn!!.y = relY+33 btn!!.y = relY+33
btn!!.render(guiGraphics, i, j, f) // minecraft SUCKSSS btn!!.render(guiGraphics, i, j, f) // minecraft SUCKSSS
@@ -64,5 +74,4 @@ class CaseScreen : GenericContainerScreen<CaseMenu> {
} else return false } 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. // Tier 0 allows ALL tiers, making it completely untiered.
// Role determines what the role is. // 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) { override fun draw(graphics: GuiGraphics, relX: Int, relY: Int, mouseX: Int, mouseY: Int) {
super.draw(graphics, relX, relY, mouseX, mouseY) super.draw(graphics, relX, relY, mouseX, mouseY)
if(!hasItem()) { if(!hasItem()) {
@@ -115,17 +115,21 @@ class ComponentSlot(container: Container, slot: Int, x: Int, y: Int, val machine
override fun set(itemStack: ItemStack) { override fun set(itemStack: ItemStack) {
super.set(itemStack) super.set(itemStack)
if(machine != null) {
val item = itemStack.item val item = itemStack.item
if (item is ComponentItem) { if (item is ComponentItem) {
item.whenComponentPlaced(itemStack, machine, requirement.role) item.whenComponentPlaced(itemStack, machine, requirement.role)
} }
} }
}
override fun onTake(player: Player, itemStack: ItemStack) { override fun onTake(player: Player, itemStack: ItemStack) {
if(machine != null) {
val item = itemStack.item val item = itemStack.item
if (item is ComponentItem) { if (item is ComponentItem) {
item.whenComponentTaken(itemStack, machine, requirement.role) item.whenComponentTaken(itemStack, machine, requirement.role)
} }
}
super.onTake(player, itemStack) 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? if(oldNode != null) Networking.removeNode(oldNode) // did a mod forget to call whenComponentTaken?
val node = toComponentNode(itemStack, machine) ?: return val node = toComponentNode(itemStack, machine) ?: return
Networking.addNode(node) Networking.addNode(node)
machine.getMachineNode().connectTo(node)
} }
// Component taken, and thus removed // Component taken, and thus removed
fun whenComponentTaken(itemStack: ItemStack, machine: MachineEntity, previousRole: String) { fun whenComponentTaken(itemStack: ItemStack, machine: MachineEntity, previousRole: String) {
val node = getComponentNode(itemStack) ?: return val node = getComponentNode(itemStack) ?: return
node.disconnectFrom(machine.getMachineNode())
Networking.removeNode(node) Networking.removeNode(node)
} }

View File

@@ -6,6 +6,7 @@ import net.minecraft.core.component.DataComponentType
import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import java.nio.ByteBuffer
object DataComponents { object DataComponents {
val ADDRESS = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "address"), 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"), val READONLY = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "readonly"),
DataComponentType.builder<Boolean>().persistent(Codec.BOOL).build()) DataComponentType.builder<Boolean>().persistent(Codec.BOOL).build())
val EEPROM_CODE = Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "eeprom_code"), 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"), 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.gui.widget.ComponentRoles
import org.neoflock.neocomputers.network.Networking import org.neoflock.neocomputers.network.Networking
import org.neoflock.neocomputers.utils.Formatting import org.neoflock.neocomputers.utils.Formatting
import java.util.UUID import java.nio.ByteBuffer
fun getEEPROMProperties(): Item.Properties = Item.Properties() fun getEEPROMProperties(codeCap: Int, dataCap: Int): Item.Properties = Item.Properties()
.component(DataComponents.EEPROM_CODE, "") .component(DataComponents.EEPROM_CODE, ByteBuffer.allocate(codeCap))
.component(DataComponents.EEPROM_DATA, "") .component(DataComponents.EEPROM_DATA, ByteBuffer.allocate(dataCap))
.component(DataComponents.LABEL, "") .component(DataComponents.LABEL, "")
.component(DataComponents.READONLY, false) .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 getComponentRoles(itemStack: ItemStack): Set<String> = setOf(ComponentRoles.FIRMWARE)
override fun getComponentTier(itemStack: ItemStack): Int = tier 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 tooltipFlag: TooltipFlag
) { ) {
if(tooltipFlag.isAdvanced) { if(tooltipFlag.isAdvanced) {
val code = itemStack.get(DataComponents.EEPROM_CODE) ?: "" val codeSize = itemStack.get(DataComponents.EEPROM_CODE)?.position() ?: 0
val data = itemStack.get(DataComponents.EEPROM_DATA) ?: "" val dataSize = itemStack.get(DataComponents.EEPROM_DATA)?.position() ?: 0
val addr = itemStack.get(DataComponents.ADDRESS) val addr = itemStack.get(DataComponents.ADDRESS)
val readonly = itemStack.get(DataComponents.READONLY) ?: false 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) val addrComp = if(addr == null) Component.translatable("neocomputers.noaddr") else Component.literal(addr)
list.addLast(addrComp) list.addLast(addrComp)
list.addLast(Component.translatable("neocomputers.eeprom.codeused", Formatting.formatMemory(codeSize.toLong()), 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) super.whenComponentPlaced(itemStack, machine, newRole)
} }
// TODO: Modem Component // TODO: GPU Component
override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null override fun toComponentNode(itemStack: ItemStack, machine: MachineEntity): Networking.Node? = null
override fun appendHoverText( override fun appendHoverText(

View File

@@ -2,11 +2,14 @@ package org.neoflock.neocomputers.item
import dev.architectury.registry.CreativeTabRegistry import dev.architectury.registry.CreativeTabRegistry
import dev.architectury.registry.registries.DeferredRegister import dev.architectury.registry.registries.DeferredRegister
import net.minecraft.client.Minecraft
import net.minecraft.core.registries.Registries import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.CreativeModeTab import net.minecraft.world.item.CreativeModeTab
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import org.neoflock.neocomputers.NeoComputers import org.neoflock.neocomputers.NeoComputers
import java.nio.ByteBuffer
object Tabs { object Tabs {
val TABS: DeferredRegister<CreativeModeTab> = val TABS: DeferredRegister<CreativeModeTab> =
@@ -53,11 +56,24 @@ object Tabs {
output.accept(ItemStack(Items.EE0.get())) output.accept(ItemStack(Items.EE0.get()))
// Criminal black magic to put LuaBIOS EEPROM in the tabs
do {
val luaBios = ItemStack(Items.EE0.get()) 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.LABEL, "Lua BIOS")
luaBios.set(DataComponents.EEPROM_CODE, "error('hi')") luaBios.set(DataComponents.EEPROM_CODE, codeBuf)
luaBios.set(DataComponents.EEPROM_DATA, "random garbage")
output.accept(luaBios) 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) { fun addInventorySlots(inventory: Inventory, x: Int, y: Int) {
// Based off the code in ChestMenu // Based off the code in ChestMenu
for (i in 0..2) { for (i in 0..2) {
@@ -88,7 +88,7 @@ abstract class GenericContainerMenu(menuType: MenuType<*>, id: Int, var containe
val stack = slot.item val stack = slot.item
val copied = stack.copy() val copied = stack.copy()
val contSize = container!!.containerSize val contSize = container.containerSize
if(i < contSize) { if(i < contSize) {
if(!this.moveItemStackTo(stack, contSize, slots.size, true)) { 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 { 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) { override fun render(graphics: GuiGraphics, mouseX: Int, mouseY: Int, something: Float) {
super.render(graphics, mouseX, mouseY, something) super.render(graphics, mouseX, mouseY, something)
renderCustomOverlay(graphics, mouseX, mouseY, something)
if(shouldRenderTooltip()) super.renderTooltip(graphics, mouseX, mouseY) if(shouldRenderTooltip()) super.renderTooltip(graphics, mouseX, mouseY)
} }
} }

View File

@@ -46,5 +46,6 @@
"neocomputers.noaddr": "No address assigned", "neocomputers.noaddr": "No address assigned",
"neocomputers.memory.capacity": "Capacity: %1$s", "neocomputers.memory.capacity": "Capacity: %1$s",
"neocomputers.eeprom.codeused": "Code Storage: %1$s / %2$s", "neocomputers.eeprom.codeused": "Code Storage: %1$s / %2$s",
"neocomputers.eeprom.dataused": "Data Storage: %1$s / %2$s" "neocomputers.eeprom.dataused": "Data Storage: %1$s / %2$s",
"sounds.neocomputers.computer_running": "Computer Fans"
} }

View File

@@ -0,0 +1,64 @@
local init
do
local component_invoke = component.invoke
local function boot_invoke(address, method, ...)
local result = table.pack(pcall(component_invoke, address, method, ...))
if not result[1] then
return nil, result[2]
else
return table.unpack(result, 2, result.n)
end
end
-- backwards compatibility, may remove later
local eeprom = component.list("eeprom")()
computer.getBootAddress = function()
return boot_invoke(eeprom, "getData")
end
computer.setBootAddress = function(address)
return boot_invoke(eeprom, "setData", address)
end
do
local screen = component.list("screen")()
local gpu = component.list("gpu")()
if gpu and screen then
boot_invoke(gpu, "bind", screen)
end
end
local function tryLoadFrom(address)
local handle, reason = boot_invoke(address, "open", "/init.lua")
if not handle then
return nil, reason
end
local buffer = ""
repeat
local data, reason = boot_invoke(address, "read", handle, math.maxinteger or math.huge)
if not data and reason then
return nil, reason
end
buffer = buffer .. (data or "")
until not data
boot_invoke(address, "close", handle)
return load(buffer, "=init")
end
local reason
if computer.getBootAddress() then
init, reason = tryLoadFrom(computer.getBootAddress())
end
if not init then
computer.setBootAddress()
for address in component.list("filesystem") do
init, reason = tryLoadFrom(address)
if init then
computer.setBootAddress(address)
break
end
end
end
if not init then
error("no bootable medium found" .. (reason and (": " .. tostring(reason)) or ""), 0)
end
computer.beep(1000, 0.2)
end
return init()

View File

@@ -0,0 +1,9 @@
{
"computer_running": {
"subtitle": "sounds.neocomputers.computer_running",
"category": "ambient",
"sounds": [
{"name": "neocomputers:computer_running", "stream": true}
]
}
}