NodeBlockEntity is dead, long live SingleDeviceBlockEntity + cables function
@@ -1,7 +1,5 @@
|
||||
package org.neoflock.neocomputers
|
||||
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat
|
||||
import com.mojang.blaze3d.vertex.VertexFormat
|
||||
import dev.architectury.event.events.client.ClientLifecycleEvent
|
||||
import dev.architectury.event.events.common.LifecycleEvent
|
||||
import dev.architectury.event.events.common.PlayerEvent
|
||||
@@ -15,24 +13,20 @@ import org.neoflock.neocomputers.gui.menu.Menus
|
||||
import dev.architectury.utils.Env
|
||||
import dev.architectury.utils.EnvExecutor
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.RenderStateShard
|
||||
import net.minecraft.client.renderer.RenderType
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.block.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.gui.buffer.BufferRenderer
|
||||
import org.neoflock.neocomputers.block.DeviceBlockEntity
|
||||
import org.neoflock.neocomputers.gui.render.ScreenRenderer
|
||||
import org.neoflock.neocomputers.gui.widget.ComponentRoles
|
||||
import org.neoflock.neocomputers.item.GPUCard
|
||||
import org.neoflock.neocomputers.item.Items
|
||||
import org.neoflock.neocomputers.item.Tabs
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.sounds.Sounds
|
||||
import org.neoflock.neocomputers.utils.FontProvider
|
||||
import org.neoflock.neocomputers.utils.GenericContainerScreen
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.nio.Buffer
|
||||
|
||||
object NeoComputers {
|
||||
const val MODID: String = "neocomputers"
|
||||
@@ -58,7 +52,7 @@ object NeoComputers {
|
||||
Tabs.TABS.register()
|
||||
Sounds.SOUNDS.register()
|
||||
ComponentRoles.mapDefaultTextures()
|
||||
// i dont know why architectury wants two lambdas but whatever
|
||||
// I don't know why architectury wants two lambdas but whatever
|
||||
EnvExecutor.runInEnv(Env.CLIENT) {{
|
||||
ClientLifecycleEvent.CLIENT_SETUP.register {
|
||||
Menus.registerScreens()
|
||||
@@ -87,7 +81,7 @@ object NeoComputers {
|
||||
Networking.channels.remove()
|
||||
}
|
||||
|
||||
ClientLifecycleEvent.CLIENT_STARTED.register {
|
||||
ClientLifecycleEvent.CLIENT_SETUP.register {
|
||||
Networking.allNodes.remove()
|
||||
Networking.wirelessNodes.remove()
|
||||
Networking.channels.remove()
|
||||
@@ -111,19 +105,22 @@ object NeoComputers {
|
||||
packet, ctx ->
|
||||
val player = ctx.player
|
||||
if(player is ServerPlayer) {
|
||||
NodeSynchronizer.screenMap[player]?.processScreenInteraction(player, packet.buffer)
|
||||
val ent = NodeSynchronizer.screenMap[player]
|
||||
if(ent is DeviceNode) {
|
||||
ent.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, {
|
||||
NetworkManager.registerReceiver(NetworkManager.s2c(),NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC, {
|
||||
packet, ctx ->
|
||||
val level = ctx.player.level()
|
||||
val ent = level.getBlockEntity(packet.blockPos)
|
||||
if(ent is NodeBlockEntity) {
|
||||
ent.syncWithUpstream(packet.buffer)
|
||||
if(ent is DeviceBlockEntity) {
|
||||
ent.processCommits(packet.buffers)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -143,7 +140,7 @@ object NeoComputers {
|
||||
}}
|
||||
EnvExecutor.runInEnv(Env.SERVER) {{
|
||||
// https://github.com/architectury/architectury-api/issues/518
|
||||
NetworkManager.registerS2CPayloadType(NodeSynchronizer.StatePayload.TYPE, NodeSynchronizer.StatePayload.CODEC)
|
||||
NetworkManager.registerS2CPayloadType(NodeSynchronizer.DeviceBlockStatePayload.TYPE, NodeSynchronizer.DeviceBlockStatePayload.CODEC)
|
||||
NetworkManager.registerS2CPayloadType(NodeSynchronizer.ScreenPayload.TYPE, NodeSynchronizer.ScreenPayload.CODEC)
|
||||
NetworkManager.registerS2CPayloadType(NodeSynchronizer.BeepDataPayload.TYPE, NodeSynchronizer.BeepDataPayload.CODEC)
|
||||
}}
|
||||
|
||||
@@ -27,7 +27,7 @@ import net.minecraft.world.phys.shapes.VoxelShape
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.entity.CableEntity
|
||||
|
||||
class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
|
||||
class CableBlock() : DeviceBlock(Properties.of()), EntityBlock {
|
||||
companion object {
|
||||
val NORTH = BooleanProperty.create("north")
|
||||
val EAST = BooleanProperty.create("east")
|
||||
@@ -96,12 +96,20 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
|
||||
fun shouldConnect(pos: BlockPos, npos: BlockPos, level: Level): Boolean {
|
||||
val ent = level.getBlockEntity(npos)
|
||||
val blockState = level.getBlockState(pos)
|
||||
val theirState = level.getBlockState(npos)
|
||||
|
||||
return ent is NodeBlockEntity || (ent is CableEntity &&
|
||||
(level.getBlockState(npos).getValue(COLOR).equals(blockState.getValue(COLOR)) ||
|
||||
blockState.getValue(COLOR).equals(DyeColor.LIGHT_GRAY) ||
|
||||
level.getBlockState(npos).getValue(COLOR).equals(DyeColor.LIGHT_GRAY)))
|
||||
// val state: BlockState? = (ent is CableEntity && (level.getBlockState(neighborPos).getValue(COLOR).equals(state.getValue(COLOR)) || state.getValue(COLOR).equals(DyeColor.LIGHT_GRAY))
|
||||
val universal = DyeColor.LIGHT_GRAY
|
||||
if(ent is CableEntity) {
|
||||
val ourColor = blockState.getValue(COLOR)
|
||||
val theirColor = theirState.getValue(COLOR)
|
||||
|
||||
if(ourColor.equals(universal)) return true
|
||||
if(theirColor.equals(universal)) return true
|
||||
if(ourColor.equals(theirColor)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
return ent is DeviceBlockEntity
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,9 +138,7 @@ class CableBlock() : BaseBlock(Properties.of()), EntityBlock {
|
||||
.add(COLOR))
|
||||
}
|
||||
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
|
||||
return CableEntity(pos, state)
|
||||
}
|
||||
override fun newBlockEntity(pos: BlockPos, state: BlockState) = CableEntity(pos, state)
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape? {
|
||||
val idx = calcIdx(state.getValue(NORTH), state.getValue(SOUTH), state.getValue(EAST), state.getValue(WEST), state.getValue(UP), state.getValue(DOWN))
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.chat.OutgoingChatMessage
|
||||
import net.minecraft.network.chat.PlayerChatMessage
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
@@ -25,6 +26,16 @@ open class CapacitorEntity(val capacity: Long, type: BlockEntityType<*>, pos: Bl
|
||||
val deviceNode = object : DeviceNode() {
|
||||
override var powerRole = PowerRole.STORAGE
|
||||
override var energyCapacity: Long = capacity
|
||||
|
||||
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
|
||||
super.writeFullStateCommit(buf)
|
||||
buf.writeVarLong(energy)
|
||||
}
|
||||
|
||||
override fun processCommit(buf: FriendlyByteBuf) {
|
||||
super.processCommit(buf)
|
||||
energy = buf.readVarLong()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: cache list
|
||||
@@ -46,7 +57,7 @@ class CapacitorEntityTier1(pos: BlockPos, state: BlockState): CapacitorEntity(20
|
||||
class CapacitorEntityTier2(pos: BlockPos, state: BlockState): CapacitorEntity(50000, BlockEntities.CAPACITOR2_ENTITY.get(), pos, state)
|
||||
class CapacitorEntityTier3(pos: BlockPos, state: BlockState): CapacitorEntity(100000, BlockEntities.CAPACITOR3_ENTITY.get(), pos, state)
|
||||
|
||||
class CapacitorBlock(val tier: Int) : NodeBlock() {
|
||||
class CapacitorBlock(val tier: Int) : DeviceBlock() {
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
|
||||
val cap: CapacitorEntity = when(tier) {
|
||||
1 -> CapacitorEntityTier1(blockPos, blockState)
|
||||
@@ -54,7 +65,7 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
|
||||
3 -> CapacitorEntityTier3(blockPos, blockState)
|
||||
else -> throw UnsupportedOperationException("unsupported tier: $tier")
|
||||
}
|
||||
return cap.initNetworking()
|
||||
return cap
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
@@ -64,13 +75,12 @@ class CapacitorBlock(val tier: Int) : NodeBlock() {
|
||||
player: Player,
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
if(!level.isClientSide()) {
|
||||
val p = player as ServerPlayer
|
||||
if(level.isClientSide()) {
|
||||
val ent = level.getBlockEntity(blockPos)
|
||||
if(ent is CapacitorEntity) {
|
||||
if(p.isCrouching) ent.deviceNode.giveEnergy(1)
|
||||
val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.deviceNode.connections.size} connections, ${ent.deviceNode.getReachable().size} connected)")
|
||||
p.sendSystemMessage(OutgoingChatMessage.create(msg).content())
|
||||
if(player.isCrouching) ent.deviceNode.giveEnergy(1)
|
||||
val msg = PlayerChatMessage.system("energy: ${ent.deviceNode.energy} / ${ent.capacity} (${ent.deviceNode.connections.size} connections, ${ent.deviceNode.getReachable().size} reachable)")
|
||||
player.sendSystemMessage(OutgoingChatMessage.create(msg).content())
|
||||
}
|
||||
}
|
||||
return InteractionResult.SUCCESS
|
||||
|
||||
@@ -31,9 +31,10 @@ import org.neoflock.neocomputers.block.CombustionGeneratorBlock.Companion.COMBUS
|
||||
import org.neoflock.neocomputers.entity.BlockEntities
|
||||
import org.neoflock.neocomputers.entity.CaseBlockEntity
|
||||
import org.neoflock.neocomputers.entity.MachineEntity
|
||||
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.sounds.Sounds
|
||||
|
||||
class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance)) { // placeholder stuff
|
||||
class CaseBlock() : DeviceBlock(Properties.of().sound(SoundType.METAL).lightLevel(CaseBlock::getLuminance)) { // placeholder stuff
|
||||
companion object {
|
||||
val FACING: EnumProperty<Direction> = EnumProperty.create<Direction>("facing", Direction::class.java)
|
||||
val COMPUTER_RUNNING = BooleanProperty.create("running")!!
|
||||
@@ -47,7 +48,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
|
||||
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(COMPUTER_RUNNING, false))
|
||||
}
|
||||
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState).initNetworking()
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = CaseBlockEntity(blockPos, blockState)
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block?, BlockState?>) {
|
||||
builder.add(COMPUTER_RUNNING)
|
||||
@@ -115,7 +116,7 @@ class CaseBlock() : NodeBlock(Properties.of().sound(SoundType.METAL).lightLevel(
|
||||
} else {
|
||||
// Open menu
|
||||
MenuRegistry.openMenu(player as ServerPlayer, ent)
|
||||
NodeSynchronizer.registerPlayerScreen(player, ent)
|
||||
NodeSynchronizer.registerPlayerScreen(player, ent.deviceNode)
|
||||
}
|
||||
}
|
||||
return InteractionResult.SUCCESS
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package org.neoflock.neocomputers.block
|
||||
|
||||
import dev.architectury.networking.NetworkManager
|
||||
import io.netty.buffer.Unpooled
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.EntityBlock
|
||||
@@ -11,9 +15,18 @@ import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||
|
||||
abstract class SingleDeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): DeviceBlockEntity(type, pos, state) {
|
||||
abstract val deviceNode: DeviceNode
|
||||
|
||||
override fun getDeviceNodes() = listOf(deviceNode)
|
||||
override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? = deviceNode
|
||||
}
|
||||
|
||||
abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState): BlockEntity(type, pos, state) {
|
||||
val connetionsInDir = MutableList(Direction.entries.size) { HashSet<DeviceNode>() }
|
||||
val connetionsInDir = MutableList<DeviceNode?>(Direction.entries.size) { null }
|
||||
var alreadySetup = false
|
||||
|
||||
abstract fun getDeviceNodes(): List<DeviceNode>
|
||||
|
||||
@@ -22,57 +35,85 @@ abstract class DeviceBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state:
|
||||
// so it is Direction.UP if we asked from the one on the top side.
|
||||
abstract fun getNodeFromSide(directionToRequester: Direction): DeviceNode?
|
||||
|
||||
open fun processCommits(commits: Iterable<FriendlyByteBuf>) {
|
||||
val devs = getDeviceNodes()
|
||||
for (buf in commits) {
|
||||
val idx = buf.readVarInt()
|
||||
if(idx >= 0 && idx < devs.size) {
|
||||
devs[idx].processCommit(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun initNetworking(): DeviceBlockEntity {
|
||||
getDeviceNodes().forEach { Networking.addNode(it) }
|
||||
if(hasLevel()) {
|
||||
alreadySetup = true
|
||||
Networking.addNodes(getDeviceNodes())
|
||||
Direction.entries.forEach { handleConnectionsFor(it) }
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
open fun getCurrentlyConnectedNodesIn(direction: Direction): HashSet<DeviceNode> {
|
||||
// Cables are 1 node
|
||||
open fun getCurrentlyConnectedNodeIn(direction: Direction): DeviceNode? {
|
||||
val ent = level?.getBlockEntity(blockPos.relative(direction))
|
||||
val connected = HashSet<DeviceNode>()
|
||||
if(ent is DeviceBlockEntity) {
|
||||
val node = ent.getNodeFromSide(direction.opposite)
|
||||
if(node != null) connected.add(node)
|
||||
return ent.getNodeFromSide(direction.opposite)
|
||||
}
|
||||
return connected
|
||||
return null
|
||||
}
|
||||
|
||||
// TODO: rethink this shi so sharing a node on 2 different sides doesn't make connections require mutually exclusive conditions
|
||||
// TODO: actually like, rethink the whole class so far
|
||||
|
||||
open fun handleConnectionsFor(direction: Direction) {
|
||||
// refuse connections on no node to reduce CPU load
|
||||
val node = getNodeFromSide(direction.opposite) ?: return
|
||||
val old = connetionsInDir[direction.ordinal]
|
||||
val now = getCurrentlyConnectedNodesIn(direction)
|
||||
val now = getCurrentlyConnectedNodeIn(direction)
|
||||
|
||||
// TODO: optimize this hellscape
|
||||
|
||||
val toKill = HashSet<DeviceNode>()
|
||||
old.forEach {
|
||||
if(it !in now) toKill.add(it)
|
||||
}
|
||||
toKill.forEach { node.disconnectFrom(it) }
|
||||
now.forEach {
|
||||
if(it !in old) node.connectTo(it)
|
||||
if(old?.address != now?.address) {
|
||||
if(old != null) node.disconnectFrom(old)
|
||||
if(now != null) node.connectTo(now)
|
||||
}
|
||||
connetionsInDir[direction.ordinal] = now
|
||||
}
|
||||
|
||||
// TODO: optimize this sometime before our test computers melt
|
||||
open fun tickDevice() {
|
||||
open fun tickDevice(level: Level) {
|
||||
// Handles device connections and sync here
|
||||
|
||||
// Process connections
|
||||
Direction.entries.forEach {
|
||||
handleConnectionsFor(it)
|
||||
// we do it like this because stinky MC will call stuff before world is fully setup
|
||||
// and then not notify us of neighbour changes
|
||||
// this is because MC is considered shit
|
||||
if(!alreadySetup) {
|
||||
initNetworking()
|
||||
}
|
||||
}
|
||||
|
||||
open fun sendCommitsToClient(level: Level) {
|
||||
if(level is ServerLevel) {
|
||||
// synchronization!
|
||||
val commits = mutableListOf<FriendlyByteBuf>()
|
||||
val devs = getDeviceNodes()
|
||||
for((i, dev) in devs.withIndex()) {
|
||||
// TODO: use dev.outOfSync to only set commits if something changed, and allow client to request the commits (securely)
|
||||
dev.outOfSync = false
|
||||
val buf = FriendlyByteBuf(Unpooled.buffer())
|
||||
buf.writeVarInt(i)
|
||||
dev.writeFullStateCommit(buf)
|
||||
commits.addLast(buf)
|
||||
}
|
||||
if(commits.isNotEmpty()) {
|
||||
level.players().forEach {
|
||||
val dist = it.position().distanceTo(blockPos.center)
|
||||
if(dist < 100) NetworkManager.sendToPlayer(it, NodeSynchronizer.DeviceBlockStatePayload(blockPos, commits))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setRemoved() {
|
||||
super.setRemoved()
|
||||
getDeviceNodes().forEach { Networking.removeNode(it) }
|
||||
alreadySetup = false
|
||||
Networking.removeNodes(getDeviceNodes())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +126,8 @@ abstract class DeviceBlock(properties: Properties = Properties.of()): BaseBlock(
|
||||
return object : BlockEntityTicker<T> {
|
||||
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T & Any) {
|
||||
if(blockEntity !is DeviceBlockEntity) return
|
||||
blockEntity.tickDevice()
|
||||
blockEntity.tickDevice(level)
|
||||
blockEntity.sendCommitsToClient(level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,14 @@ import net.minecraft.world.phys.BlockHitResult
|
||||
import org.neoflock.neocomputers.entity.BlockEntities
|
||||
import org.neoflock.neocomputers.entity.SolarGeneratorBlockEntity
|
||||
import org.neoflock.neocomputers.entity.CombustionGeneratorBlockEntity
|
||||
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||
|
||||
class SolarGeneratorBlock : NodeBlock(), EntityBlock {
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState).initNetworking()
|
||||
class SolarGeneratorBlock : DeviceBlock(), EntityBlock {
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = SolarGeneratorBlockEntity(blockPos, blockState)
|
||||
}
|
||||
|
||||
// TODO: make it glow when burning
|
||||
class CombustionGeneratorBlock : NodeBlock, EntityBlock {
|
||||
class CombustionGeneratorBlock : DeviceBlock, EntityBlock {
|
||||
companion object {
|
||||
val COMBUSTGEN_ACTIVE = BooleanProperty.create("active")
|
||||
|
||||
@@ -42,7 +43,7 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock {
|
||||
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)
|
||||
|
||||
override fun useWithoutItem(
|
||||
blockState: BlockState,
|
||||
@@ -54,7 +55,7 @@ class CombustionGeneratorBlock : NodeBlock, EntityBlock {
|
||||
if(!level.isClientSide()) {
|
||||
val sp = player as ServerPlayer
|
||||
val ent = level.getBlockEntity(blockPos, BlockEntities.COMBUSTGEN_ENTITY.get()).get()
|
||||
NodeSynchronizer.registerPlayerScreen(sp, ent)
|
||||
NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
|
||||
MenuRegistry.openMenu(sp, ent)
|
||||
}
|
||||
return InteractionResult.SUCCESS
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
package org.neoflock.neocomputers.block
|
||||
|
||||
import dev.architectury.networking.NetworkManager
|
||||
import io.netty.buffer.Unpooled
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
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.entity.BlockEntityTicker
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import java.time.Duration
|
||||
|
||||
object NodeSynchronizer {
|
||||
class StatePayload(var blockPos: BlockPos, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val NODE_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "node_sync")
|
||||
val TYPE = CustomPacketPayload.Type<StatePayload>(NODE_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, StatePayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): StatePayload {
|
||||
val blockPos = buf.readBlockPos()
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return StatePayload(blockPos, buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: StatePayload) {
|
||||
buf.writeBlockPos(payload.blockPos)
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class ScreenPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenPayload>(SCREEN_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): ScreenPayload {
|
||||
val id = buf.readByteArray().decodeToString()
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return ScreenPayload(id, buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenPayload) {
|
||||
buf.writeByteArray(payload.entityTypeWireID.encodeToByteArray())
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class ScreenDataPayload(var entityTypeWireID: String, var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_DATA_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
|
||||
}
|
||||
|
||||
class BeepDataPayload(val pos: BlockPos, val pattern: String, val freq: Int, val duration: Duration, val volume: Double): CustomPacketPayload {
|
||||
companion object {
|
||||
val BEEP_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "beep_data")
|
||||
val TYPE = CustomPacketPayload.Type<BeepDataPayload>(BEEP_DATA_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, BeepDataPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): BeepDataPayload {
|
||||
val pos = buf.readBlockPos()
|
||||
val pattern = buf.readUtf()
|
||||
val freq = buf.readVarInt()
|
||||
val duration = buf.readVarLong()
|
||||
val volume = buf.readDouble()
|
||||
return BeepDataPayload(pos, pattern, freq, Duration.ofMillis(duration), volume)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: BeepDataPayload) {
|
||||
buf.writeBlockPos(payload.pos)
|
||||
buf.writeUtf(payload.pattern)
|
||||
buf.writeVarInt(payload.freq)
|
||||
buf.writeVarLong(payload.duration.toMillis())
|
||||
buf.writeDouble(payload.volume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
val screenMap = HashMap<ServerPlayer, NodeBlockEntity>()
|
||||
|
||||
fun playerScreenClosed(player: ServerPlayer) {
|
||||
screenMap.remove(player)
|
||||
}
|
||||
|
||||
fun nodeTypeToWireID(nodeType: BlockEntityType<*>): String = nodeType.javaClass.canonicalName
|
||||
|
||||
fun registerPlayerScreen(player: ServerPlayer, entity: NodeBlockEntity) {
|
||||
screenMap[player] = entity
|
||||
}
|
||||
|
||||
fun syncScreens() {
|
||||
for((player, ent) in screenMap) {
|
||||
val buf = FriendlyByteBuf(Unpooled.buffer())
|
||||
ent.encodeScreenData(player, buf)
|
||||
NetworkManager.sendToPlayer(player, ScreenPayload(nodeTypeToWireID(ent.type), buf))
|
||||
}
|
||||
}
|
||||
|
||||
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
|
||||
NetworkManager.sendToServer(ScreenDataPayload("", friendlyByteBuf))
|
||||
}
|
||||
|
||||
fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) {
|
||||
if(level is ServerLevel) {
|
||||
level.players().forEach {
|
||||
NetworkManager.sendToPlayer(it, beepDataPayload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class NodeBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) : BlockEntity(blockEntityType, blockPos, blockState) {
|
||||
abstract val deviceNode: DeviceNode
|
||||
|
||||
fun initNetworking(): NodeBlockEntity {
|
||||
Networking.addNode(deviceNode)
|
||||
invalidateNodeState()
|
||||
return this
|
||||
}
|
||||
|
||||
// runs on the server, meant to encode state to send to all players
|
||||
open fun encodeDownstreamData(packet: FriendlyByteBuf) {
|
||||
packet.writeUUID(deviceNode.address)
|
||||
packet.writeLong(deviceNode.energy)
|
||||
packet.writeLong(deviceNode.energyCapacity)
|
||||
packet.writeEnum(deviceNode.reachability)
|
||||
packet.writeEnum(deviceNode.powerRole)
|
||||
}
|
||||
|
||||
// runs on the client, meant to decode server state packets to synchronize client state
|
||||
open fun syncWithUpstream(packet: FriendlyByteBuf) {
|
||||
Networking.changeNodeAddress(deviceNode, packet.readUUID())
|
||||
deviceNode.energy = packet.readLong()
|
||||
deviceNode.energyCapacity = packet.readLong()
|
||||
deviceNode.reachability = packet.readEnum(deviceNode.reachability.javaClass)
|
||||
deviceNode.powerRole = packet.readEnum(deviceNode.powerRole.javaClass)
|
||||
}
|
||||
|
||||
// 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> {
|
||||
val subpos = listOf(
|
||||
blockPos.offset(0, 0, 1),
|
||||
blockPos.offset(0, 0, -1),
|
||||
blockPos.offset(0, 1, 0),
|
||||
blockPos.offset(0, -1, 0),
|
||||
blockPos.offset(1, 0, 0),
|
||||
blockPos.offset(-1, 0, 0),
|
||||
)
|
||||
|
||||
return subpos.mapNotNull { pos -> level?.getBlockEntity(pos) }
|
||||
}
|
||||
|
||||
open fun computeEdges(): Set<NodeBlockEntity> {
|
||||
val s = mutableSetOf<NodeBlockEntity>()
|
||||
val neighbours = getNeighbourEntities()
|
||||
for(neighbour in neighbours) {
|
||||
if(neighbour is NodeBlockEntity) s.add(neighbour);
|
||||
// TODO: handle cable entities
|
||||
}
|
||||
s.remove(this)
|
||||
return s
|
||||
}
|
||||
|
||||
open fun invalidateNodeState() {
|
||||
stateIsDirty = true
|
||||
}
|
||||
|
||||
open fun needsSynchronization() = stateIsDirty
|
||||
|
||||
open fun tickNode(level: Level) {
|
||||
if(!level.isClientSide) {
|
||||
val l = level as ServerLevel
|
||||
val packetBuf = FriendlyByteBuf(Unpooled.buffer())
|
||||
encodeDownstreamData(packetBuf)
|
||||
l.players().forEach {
|
||||
if(it.level().isLoaded(blockPos)) NetworkManager.sendToPlayer(it, NodeSynchronizer.StatePayload(blockPos, packetBuf))
|
||||
}
|
||||
}
|
||||
if(!stateIsDirty) return
|
||||
stateIsDirty = false
|
||||
computeEdges().forEach {
|
||||
deviceNode.connectTo(it.deviceNode)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
invalidateNodeState()
|
||||
computeEdges().forEach { it.invalidateNodeState() }
|
||||
super.setChanged()
|
||||
}
|
||||
|
||||
override fun setRemoved() {
|
||||
super.setRemoved()
|
||||
Networking.removeNode(deviceNode)
|
||||
}
|
||||
|
||||
override fun clearRemoved() {
|
||||
super.clearRemoved()
|
||||
initNetworking()
|
||||
}
|
||||
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
super.loadAdditional(compoundTag, provider)
|
||||
invalidateNodeState()
|
||||
computeEdges().forEach { it.invalidateNodeState() }
|
||||
}
|
||||
}
|
||||
|
||||
abstract class NodeBlock(properties: Properties = Properties.of()): BaseBlock(properties), EntityBlock {
|
||||
override fun <T : BlockEntity> getTicker(
|
||||
level: Level,
|
||||
blockState: BlockState,
|
||||
blockEntityType: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
return object : BlockEntityTicker<T> {
|
||||
override fun tick(level: Level, blockPos: BlockPos, blockState: BlockState, blockEntity: T) {
|
||||
if(blockEntity !is NodeBlockEntity) return
|
||||
if(Networking.getNode(blockEntity.deviceNode.address) == null) blockEntity.initNetworking()
|
||||
blockEntity.tickNode(level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlace(
|
||||
blockState: BlockState,
|
||||
level: Level,
|
||||
blockPos: BlockPos,
|
||||
blockState2: BlockState,
|
||||
bl: Boolean
|
||||
) {
|
||||
super.onPlace(blockState, level, blockPos, blockState2, bl)
|
||||
if(!level.isClientSide) {
|
||||
val ent = level.getBlockEntity(blockPos)
|
||||
if(ent is NodeBlockEntity) {
|
||||
ent.invalidateNodeState()
|
||||
ent.computeEdges().forEach { it.invalidateNodeState() }
|
||||
}
|
||||
level.updateNeighborsAt(blockPos, this)
|
||||
}
|
||||
}
|
||||
|
||||
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 ent = level.getBlockEntity(blockPos)
|
||||
if(ent is NodeBlockEntity) {
|
||||
ent.invalidateNodeState()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import org.neoflock.neocomputers.entity.BlockEntities
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
|
||||
class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEntity(BlockEntities.REDSTONEIO_ENTITY.get(), blockPos, blockState) {
|
||||
class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.REDSTONEIO_ENTITY.get(), blockPos, blockState) {
|
||||
val redstoneIn = Array<Int>(Direction.entries.size) {0}
|
||||
val redstoneOut = Array<Int>(Direction.entries.size) {0}
|
||||
|
||||
@@ -44,7 +44,7 @@ class RedstoneIOEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnt
|
||||
}
|
||||
}
|
||||
|
||||
class RedstoneIOBlock(): NodeBlock(Properties.of().isRedstoneConductor { state, getter, pos -> true }) {
|
||||
class RedstoneIOBlock(): DeviceBlock(Properties.of().isRedstoneConductor { state, getter, pos -> true }) {
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity = RedstoneIOEntity(blockPos, blockState)
|
||||
|
||||
fun getRedstoneIO(level: BlockGetter, blockPos: BlockPos): RedstoneIOEntity? {
|
||||
|
||||
@@ -26,23 +26,19 @@ import org.neoflock.neocomputers.entity.ScreenEntity
|
||||
import org.neoflock.neocomputers.gui.menu.ScreenMenu
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||
|
||||
class ScreenBlock() : NodeBlock() {
|
||||
class ScreenBlock() : DeviceBlock() {
|
||||
companion object {
|
||||
val FACING_HORIZ: EnumProperty<Direction> = EnumProperty.create<Direction>("facing_horiz", Direction::class.java)
|
||||
val FACING_VERTI: IntegerProperty = IntegerProperty.create("facing_verti", 0, 2) // "Integer" property doesnt accept values below 0, also death to enums, long live magic numbers
|
||||
val ENERGY: Long = 5
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any().setValue(FACING_HORIZ, Direction.NORTH).setValue(FACING_VERTI, 1))
|
||||
}
|
||||
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity? {
|
||||
val scr = ScreenEntity(blockPos, blockState)
|
||||
scr.initNetworking()
|
||||
return scr
|
||||
}
|
||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState) = ScreenEntity(blockPos, blockState)
|
||||
|
||||
override fun useWithoutItem(
|
||||
blockState: BlockState,
|
||||
@@ -52,14 +48,9 @@ class ScreenBlock() : NodeBlock() {
|
||||
blockHitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
if(!level.isClientSide) {
|
||||
val screenState = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
|
||||
if(!screenState.deviceNode.consumeEnergy(ENERGY)) {
|
||||
player.sendSystemMessage(Component.literal("Not enough power."))
|
||||
return InteractionResult.SUCCESS
|
||||
};
|
||||
val sp = player as ServerPlayer
|
||||
val ent = level.getBlockEntity(blockPos, BlockEntities.SCREEN_ENTITY.get()).get()
|
||||
NodeSynchronizer.registerPlayerScreen(sp, ent)
|
||||
NodeSynchronizer.registerPlayerScreen(sp, ent.deviceNode)
|
||||
MenuRegistry.openExtendedMenu(sp, object : ExtendedMenuProvider {
|
||||
override fun getDisplayName(): Component = Component.literal("SCREEEEEN!")
|
||||
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
|
||||
|
||||
@@ -88,12 +88,11 @@ object BlockEntities {
|
||||
}
|
||||
|
||||
fun registerPowerBlocks() {
|
||||
// TODO: function for the new DeviceBlockEntity
|
||||
//PowerManager.registerPowerBlockEntity(CAPACITOR_ENTITY.get())
|
||||
//PowerManager.registerPowerBlockEntity(CAPACITOR2_ENTITY.get())
|
||||
//PowerManager.registerPowerBlockEntity(CAPACITOR3_ENTITY.get())
|
||||
PowerManager.registerPowerBlockEntity(SOLARGEN_ENTITY.get())
|
||||
PowerManager.registerPowerBlockEntity(COMBUSTGEN_ENTITY.get())
|
||||
PowerManager.registerPowerBlockEntity(CASE_ENTITY.get())
|
||||
PowerManager.registerPowerDevice(CAPACITOR_ENTITY.get())
|
||||
PowerManager.registerPowerDevice(CAPACITOR2_ENTITY.get())
|
||||
PowerManager.registerPowerDevice(CAPACITOR3_ENTITY.get())
|
||||
PowerManager.registerPowerDevice(SOLARGEN_ENTITY.get())
|
||||
PowerManager.registerPowerDevice(COMBUSTGEN_ENTITY.get())
|
||||
PowerManager.registerPowerDevice(CASE_ENTITY.get())
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,31 @@ package org.neoflock.neocomputers.entity
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.block.CableBlock
|
||||
import org.neoflock.neocomputers.block.CableBlock.Companion.COLOR
|
||||
import org.neoflock.neocomputers.block.CableBlock.Companion.getPropByDirection
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
|
||||
class CableEntity(pos: BlockPos, state: BlockState) : BlockEntity(BlockEntities.CABLE_ENTITY.get(), pos, state) {
|
||||
class CableEntity(pos: BlockPos, state: BlockState) : SingleDeviceBlockEntity(BlockEntities.CABLE_ENTITY.get(), pos, state) {
|
||||
override val deviceNode = object : DeviceNode(){}
|
||||
|
||||
override fun sendCommitsToClient(level: Level) {
|
||||
// we have nothing to commit lol
|
||||
return
|
||||
}
|
||||
|
||||
override fun getNodeFromSide(directionToRequester: Direction): DeviceNode? {
|
||||
if(CableBlock.shouldConnect(blockPos, blockPos.relative(directionToRequester), level!!)) {
|
||||
return deviceNode
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun setChanged() {
|
||||
super.setChanged()
|
||||
|
||||
@@ -20,8 +20,7 @@ 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.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
|
||||
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
||||
import org.neoflock.neocomputers.item.ComponentItem
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
@@ -33,8 +32,10 @@ import org.neoflock.neocomputers.utils.GenericContainer
|
||||
import java.time.Duration
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||
import kotlin.text.ifEmpty
|
||||
|
||||
class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider {
|
||||
class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): SingleDeviceBlockEntity(BlockEntities.CASE_ENTITY.get(), blockPos, blockState), MachineEntity, GenericContainer, MenuProvider {
|
||||
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(7, ItemStack.EMPTY)
|
||||
|
||||
var isOn = false
|
||||
@@ -47,26 +48,28 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
override val deviceNode = object : DeviceNode() {
|
||||
override var powerRole = PowerRole.CONSUMER
|
||||
override var energyCapacity: Long = 500
|
||||
|
||||
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
|
||||
super.writeFullStateCommit(buf)
|
||||
buf.writeUUID(address)
|
||||
buf.writeBoolean(isOn)
|
||||
buf.writeVarInt(diskActivityTime)
|
||||
buf.writeVarInt(networkActivityTime)
|
||||
buf.writeUtf(err ?: "")
|
||||
}
|
||||
|
||||
override fun encodeDownstreamData(packet: FriendlyByteBuf) {
|
||||
super.encodeDownstreamData(packet)
|
||||
packet.writeBoolean(isOn)
|
||||
packet.writeVarInt(diskActivityTime)
|
||||
packet.writeVarInt(networkActivityTime)
|
||||
packet.writeUtf(err ?: "")
|
||||
override fun processCommit(buf: FriendlyByteBuf) {
|
||||
super.processCommit(buf)
|
||||
Networking.changeNodeAddress(this, buf.readUUID())
|
||||
setRunning(buf.readBoolean())
|
||||
diskActivityTime = buf.readVarInt()
|
||||
networkActivityTime = buf.readVarInt()
|
||||
err = buf.readUtf().ifEmpty { null }
|
||||
}
|
||||
|
||||
override fun syncWithUpstream(packet: FriendlyByteBuf) {
|
||||
super.syncWithUpstream(packet)
|
||||
setRunning(packet.readBoolean())
|
||||
diskActivityTime = packet.readVarInt()
|
||||
networkActivityTime = packet.readVarInt()
|
||||
err = packet.readUtf().ifEmpty { null }
|
||||
}
|
||||
|
||||
override fun processScreenInteraction(player: ServerPlayer, packet: FriendlyByteBuf) {
|
||||
val c = packet.readByte().toInt()
|
||||
override fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) {
|
||||
super.processScreenInteraction(player, buf)
|
||||
val c = buf.readByte().toInt()
|
||||
if(c == 0x01) {
|
||||
start()
|
||||
}
|
||||
@@ -75,17 +78,33 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
}
|
||||
}
|
||||
|
||||
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
|
||||
super.encodeScreenData(player, packet)
|
||||
packet.writeBoolean(isOn)
|
||||
packet.writeByteArray((err ?: "").encodeToByteArray())
|
||||
packet.writeLong(deviceNode.energy)
|
||||
packet.writeLong(deviceNode.energyCapacity)
|
||||
packet.writeLong(getMachineMemoryUsed())
|
||||
packet.writeLong(getMachineMemoryTotal())
|
||||
packet.writeLong(getMachineComponentsUsed())
|
||||
packet.writeLong(getMachineComponentsTotal())
|
||||
packet.writeUtf(arch)
|
||||
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
|
||||
super.encodeScreenData(player, buf)
|
||||
buf.writeBoolean(isOn)
|
||||
buf.writeByteArray((err ?: "").encodeToByteArray())
|
||||
buf.writeLong(energy)
|
||||
buf.writeLong(energyCapacity)
|
||||
buf.writeLong(getMachineMemoryUsed())
|
||||
buf.writeLong(getMachineMemoryTotal())
|
||||
buf.writeLong(getMachineComponentsUsed())
|
||||
buf.writeLong(getMachineComponentsTotal())
|
||||
buf.writeUtf(arch)
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
if (isRunning()) {
|
||||
if(diskActivityTime > 0) diskActivityTime--
|
||||
if(networkActivityTime > 0) networkActivityTime--
|
||||
if(getMachineArchitectures().isEmpty()) {
|
||||
crash("@neocomputers.errors.ENOCPU")
|
||||
} else if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
|
||||
crash("@neocomputers.errors.E2BIG")
|
||||
} else if (!consumeEnergy(1)) {
|
||||
crash("@neocomputers.errors.ENOENJ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val redstoneIn = Array(Direction.entries.size) {0}
|
||||
@@ -154,9 +173,9 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
override fun start(): Boolean {
|
||||
if(isOn) return true
|
||||
err = null
|
||||
val archs = getMachineArchitectures()
|
||||
val architectures = getMachineArchitectures()
|
||||
// Beep patterns taken from https://github.com/MightyPirates/OpenComputers/blob/571482db88080d56329e8f8cf0db2a90825bf1d7/src/main/scala/li/cil/oc/server/machine/Machine.scala
|
||||
if(archs.isEmpty()) {
|
||||
if(architectures.isEmpty()) {
|
||||
crash("@neocomputers.errors.ENOCPU")
|
||||
beepAsync("-..")
|
||||
return false
|
||||
@@ -178,9 +197,9 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
beepAsync("-.")
|
||||
return false
|
||||
}
|
||||
if(arch !in archs) {
|
||||
if(arch !in architectures) {
|
||||
// Just pick one! TODO: consult EEPROM first
|
||||
arch = archs.first()
|
||||
arch = architectures.first()
|
||||
}
|
||||
beepAsync(".")
|
||||
setRunning(true)
|
||||
@@ -194,10 +213,8 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
}
|
||||
|
||||
override fun crash(error: String): Boolean {
|
||||
if(isOn) {
|
||||
beepAsync("--")
|
||||
sendMachineEvent(MachineCrashEvent(this, error))
|
||||
}
|
||||
setRunning(false)
|
||||
err = error
|
||||
return true
|
||||
@@ -271,21 +288,4 @@ class CaseBlockEntity(blockPos: BlockPos, blockState: BlockState): NodeBlockEnti
|
||||
setRunning(false)
|
||||
super.setRemoved()
|
||||
}
|
||||
|
||||
override fun tickNode(level: Level) {
|
||||
super.tickNode(level)
|
||||
if(!level.isClientSide) {
|
||||
if (isRunning()) {
|
||||
if(diskActivityTime > 0) diskActivityTime--
|
||||
if(networkActivityTime > 0) networkActivityTime--
|
||||
if(getMachineArchitectures().isEmpty()) {
|
||||
crash("@neocomputers.errors.ENOCPU")
|
||||
} else if(getMachineComponentsUsed() > getMachineComponentsTotal()) {
|
||||
crash("@neocomputers.errors.E2BIG")
|
||||
} else if (!deviceNode.consumeEnergy(1)) {
|
||||
crash("@neocomputers.errors.ENOENJ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.neoflock.neocomputers.entity
|
||||
|
||||
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
|
||||
@@ -15,16 +16,15 @@ import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.block.CombustionGeneratorBlock
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
|
||||
import org.neoflock.neocomputers.gui.menu.CombustionGeneratorMenu
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.PowerRole
|
||||
import org.neoflock.neocomputers.utils.GenericContainer
|
||||
import org.neoflock.neocomputers.utils.ContainerUtils
|
||||
import kotlin.math.min
|
||||
|
||||
class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider {
|
||||
class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : SingleDeviceBlockEntity(BlockEntities.COMBUSTGEN_ENTITY.get(), blockPos, blockState), GenericContainer, MenuProvider {
|
||||
val energyPerTick: Long = 50
|
||||
|
||||
var burningTimeRemaining: Int = 0
|
||||
@@ -32,6 +32,11 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
override val deviceNode = object : DeviceNode() {
|
||||
override var powerRole = PowerRole.GENERATOR
|
||||
override var energyCapacity: Long = 100000
|
||||
|
||||
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
|
||||
buf.writeLong(energy)
|
||||
buf.writeLong(energyCapacity)
|
||||
}
|
||||
}
|
||||
|
||||
val stacks: NonNullList<ItemStack> = NonNullList<ItemStack>.withSize(1, ItemStack.EMPTY)
|
||||
@@ -46,8 +51,8 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
return !this.isRemoved
|
||||
}
|
||||
|
||||
override fun tickNode(level: Level) {
|
||||
super.tickNode(level)
|
||||
override fun tickDevice(level: Level) {
|
||||
super.tickDevice(level)
|
||||
// TODO: give us a block state tag for active
|
||||
|
||||
// keep combusting and shi
|
||||
@@ -79,11 +84,6 @@ class CombustionGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState)
|
||||
level?.setBlockAndUpdate(blockPos, blockState.setValue(CombustionGeneratorBlock.COMBUSTGEN_ACTIVE, burningTimeRemaining > 0))
|
||||
}
|
||||
|
||||
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
|
||||
packet.writeLong(deviceNode.energy)
|
||||
packet.writeLong(deviceNode.energyCapacity)
|
||||
}
|
||||
|
||||
override fun loadAdditional(compoundTag: CompoundTag, provider: HolderLookup.Provider) {
|
||||
super.loadAdditional(compoundTag, provider)
|
||||
deviceNode.energy = min(deviceNode.energyCapacity, compoundTag.getLong("energy"))
|
||||
|
||||
@@ -1,38 +1,66 @@
|
||||
package org.neoflock.neocomputers.entity;
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.locale.Language
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
|
||||
import org.neoflock.neocomputers.gui.buffer.BufferRenderer
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.utils.GPUChar
|
||||
import org.neoflock.neocomputers.utils.TextBuffer
|
||||
import kotlin.text.ifEmpty
|
||||
|
||||
class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
NodeBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) {
|
||||
SingleDeviceBlockEntity(BlockEntities.SCREEN_ENTITY.get(), blockPos, blockState) {
|
||||
|
||||
var lastError: String? = null
|
||||
var isOn: Boolean = false
|
||||
|
||||
override val deviceNode = object : DeviceNode() {
|
||||
override fun received(message: Networking.Message) {
|
||||
super.received(message)
|
||||
if(message is Networking.ComputerEvent) {
|
||||
// return if not directly connected
|
||||
if(message.sender !in this.connections) return
|
||||
val mEnv = message.machineEvent
|
||||
NeoComputers.LOGGER.info("Got message $mEnv!")
|
||||
if(mEnv is MachinePowerEvent) {
|
||||
if(mEnv.nowRunning) {
|
||||
textBuf.set(0, 0, address.toString())
|
||||
} else {
|
||||
lastError = null
|
||||
textBuf.fill(0, 0, textBuf.width, textBuf.height, GPUChar(' '))
|
||||
textBuf.set(0, 0, address.toString())
|
||||
}
|
||||
isOn = mEnv.nowRunning
|
||||
}
|
||||
if(mEnv is MachineCrashEvent) {
|
||||
lastError = mEnv.error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {
|
||||
super.encodeScreenData(player, buf)
|
||||
textBuf.encodeContents(buf)
|
||||
}
|
||||
|
||||
override fun writeFullStateCommit(buf: FriendlyByteBuf) {
|
||||
super.writeFullStateCommit(buf)
|
||||
buf.writeUUID(address)
|
||||
buf.writeBoolean(isOn)
|
||||
buf.writeUtf(lastError ?: "")
|
||||
textBuf.encodeContents(buf)
|
||||
}
|
||||
|
||||
override fun processCommit(buf: FriendlyByteBuf) {
|
||||
super.processCommit(buf)
|
||||
if(Networking.changeNodeAddress(this, buf.readUUID())) createscreenstuffs()
|
||||
isOn = buf.readBoolean()
|
||||
lastError = buf.readUtf().ifEmpty { null }
|
||||
textBuf.decodeContents(buf)
|
||||
}
|
||||
}
|
||||
var bound = "screen/unbound"
|
||||
@@ -41,23 +69,8 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
|
||||
private var cleanrenderer: () -> Unit = { }; // TODO: THIS SUCKS, FIND A BETTER WAY
|
||||
|
||||
override fun encodeDownstreamData(packet: FriendlyByteBuf) {
|
||||
super.encodeDownstreamData(packet)
|
||||
textBuf.encodeContents(packet)
|
||||
}
|
||||
|
||||
override fun syncWithUpstream(packet: FriendlyByteBuf) {
|
||||
super.syncWithUpstream(packet)
|
||||
textBuf.decodeContents(packet)
|
||||
}
|
||||
|
||||
override fun encodeScreenData(player: ServerPlayer, packet: FriendlyByteBuf) {
|
||||
super.encodeScreenData(player, packet)
|
||||
textBuf.encodeContents(packet)
|
||||
}
|
||||
|
||||
override fun tickNode(level: Level) {
|
||||
super.tickNode(level)
|
||||
override fun tickDevice(level: Level) {
|
||||
super.tickDevice(level)
|
||||
cleanrenderer()
|
||||
createscreenstuffs()
|
||||
}
|
||||
@@ -71,9 +84,29 @@ class ScreenEntity(blockPos: BlockPos, blockState: BlockState) :
|
||||
private fun createscreenstuffs() {
|
||||
bound = "screen/"+deviceNode.address.toString().replace("-", "_")
|
||||
if (level!!.isClientSide) {
|
||||
if(lastError == null) {
|
||||
if(!isOn) {
|
||||
textBuf.fill(0, 0, textBuf.width, textBuf.height)
|
||||
}
|
||||
var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), textBuf)
|
||||
renderer.drawBuffer()
|
||||
cleanrenderer = { renderer.clean() }
|
||||
} else {
|
||||
var trueError = lastError!!
|
||||
if(trueError.startsWith("@")) {
|
||||
val trans = trueError.substring(1)
|
||||
val lang = Language.getInstance()
|
||||
trueError = lang.getOrDefault("neocomputers.computer.errorNoMsg", "Error: ") + lang.getOrDefault(trans)
|
||||
}
|
||||
val throwAwayBuf = TextBuffer(50, 16)
|
||||
val fg = 0xFFFFFF
|
||||
val bg = 0x2B68A6
|
||||
throwAwayBuf.fill(0, 0, throwAwayBuf.width, throwAwayBuf.height, GPUChar(' ', fg, bg))
|
||||
throwAwayBuf.set((throwAwayBuf.width - trueError.length) / 2, throwAwayBuf.height/2, trueError, fg, bg)
|
||||
var renderer = BufferRenderer(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, bound), throwAwayBuf)
|
||||
renderer.drawBuffer()
|
||||
cleanrenderer = { renderer.clean() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package org.neoflock.neocomputers.entity
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import org.neoflock.neocomputers.block.SingleDeviceBlockEntity
|
||||
import org.neoflock.neocomputers.network.DeviceNode
|
||||
import org.neoflock.neocomputers.network.Networking
|
||||
import org.neoflock.neocomputers.network.PowerRole
|
||||
|
||||
class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : NodeBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) {
|
||||
class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : SingleDeviceBlockEntity(BlockEntities.SOLARGEN_ENTITY.get(), blockPos, blockState) {
|
||||
val energyPerTick: Long = 50
|
||||
|
||||
override val deviceNode = object : DeviceNode() {
|
||||
@@ -18,8 +18,8 @@ class SolarGeneratorBlockEntity(blockPos: BlockPos, blockState: BlockState) : No
|
||||
override var energyCapacity: Long = 50000
|
||||
}
|
||||
|
||||
override fun tickNode(level: Level) {
|
||||
super.tickNode(level)
|
||||
override fun tickDevice(level: Level) {
|
||||
super.tickDevice(level)
|
||||
val l = level
|
||||
if(l.isDay) {
|
||||
deviceNode.giveEnergy(energyPerTick)
|
||||
|
||||
@@ -27,6 +27,7 @@ class ScreenEntityRenderer(val context: BlockEntityRendererProvider.Context?) :
|
||||
.createCompositeState(false))
|
||||
}
|
||||
override fun render(entity: ScreenEntity, partialTick: Float, mat: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) {
|
||||
if(!entity.isOn && entity.lastError == null) return
|
||||
val facing = entity.blockState.getValue(ScreenBlock.FACING_HORIZ)
|
||||
val vert = entity.blockState.getValue(ScreenBlock.FACING_VERTI)-1
|
||||
|
||||
|
||||
@@ -26,15 +26,11 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
|
||||
Minecraft.getInstance().textureManager.register(this.id, tex)
|
||||
}
|
||||
|
||||
fun dump(path: String) {
|
||||
image.writeToFile(File(path))
|
||||
NeoComputers.LOGGER.info("DUMPED!!!")
|
||||
fun toRGBA(color: Int): Int {
|
||||
// Minecaft lies, its AGBR
|
||||
return java.lang.Integer.reverseBytes((color.toLong() * 256 + 0xFF).toInt())
|
||||
}
|
||||
|
||||
// fun toRGBA(color: Int): Int {
|
||||
// return color.shl(8).or(0xFF)
|
||||
// }
|
||||
|
||||
fun drawGlyph(x: Int, y: Int, c: Char, fg: Int) {
|
||||
var glyph: ArrayList<Byte> = FontProvider.map[c]!!
|
||||
|
||||
@@ -42,7 +38,7 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
|
||||
for (i in 0..<CHARW) {
|
||||
// var pixel = ((glyph[j] and ((1 shl (CHARW - i - 1)).toByte())).toInt()) ushr (CHARW - i - 1) // retardation
|
||||
var pixel = (glyph[j] and (0b10000000 ushr i).toByte()).toInt()
|
||||
if (pixel > 0) image.setPixelRGBA(x+i, y+j, 0xFF000000.toInt()+fg)
|
||||
if (pixel > 0) image.setPixelRGBA(x+i, y+j, toRGBA(fg))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +49,7 @@ class BufferRenderer(private var id: ResourceLocation, private var buffer: TextB
|
||||
var char: GPUChar = buffer.get(i, j)
|
||||
var x = i*CHARW
|
||||
var y = j*CHARH
|
||||
image.fillRect(x, y, CHARW, CHARH, (0xFF000000+char.bg).toInt())
|
||||
image.fillRect(x, y, CHARW, CHARH, toRGBA(char.bg))
|
||||
if (char.c != ' ' && char.c != '\u0000') drawGlyph(x, y, char.c, char.fg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import net.minecraft.world.entity.player.Inventory
|
||||
import net.minecraft.world.inventory.tooltip.TooltipComponent
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.block.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.network.NodeSynchronizer
|
||||
import org.neoflock.neocomputers.gui.menu.CaseMenu
|
||||
import org.neoflock.neocomputers.gui.widget.ButtonSprites
|
||||
import org.neoflock.neocomputers.gui.widget.ImagerButton
|
||||
|
||||
@@ -24,6 +24,8 @@ class ScreenScreen : GenericContainerScreen<ScreenMenu>{
|
||||
|
||||
var textBuf = TextBuffer(0, 0)
|
||||
|
||||
override fun shouldCenterTitle(): Boolean = false
|
||||
|
||||
override fun processScreenStatePacket(buf: FriendlyByteBuf) {
|
||||
super.processScreenStatePacket(buf)
|
||||
textBuf.decodeContents(buf)
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.neoflock.neocomputers.network
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import org.neoflock.neocomputers.network.Networking.Message
|
||||
import org.neoflock.neocomputers.network.Networking.Visibility
|
||||
@@ -215,7 +216,8 @@ open class DeviceNode(_address: UUID? = null) {
|
||||
outOfSync = true
|
||||
}
|
||||
|
||||
open fun encodeScreenData(buf: FriendlyByteBuf) {}
|
||||
open fun encodeScreenData(player: ServerPlayer, buf: FriendlyByteBuf) {}
|
||||
open fun processScreenInteraction(player: ServerPlayer, buf: FriendlyByteBuf) {}
|
||||
|
||||
// Meant to write the entire state as a single commit
|
||||
// for clients which say they have no fucking idea what the server is storing
|
||||
|
||||
@@ -85,12 +85,14 @@ object Networking {
|
||||
fun getNode(address: UUID): DeviceNode? = allNodes.get()[address]
|
||||
|
||||
// TODO: use setter, more convenient
|
||||
fun changeNodeAddress(deviceNode: DeviceNode, address: UUID) {
|
||||
if(deviceNode.address.equals(address)) return
|
||||
if(deviceNode.address !in allNodes.get()) return
|
||||
fun changeNodeAddress(deviceNode: DeviceNode, address: UUID): Boolean {
|
||||
if(deviceNode.address.equals(address)) return false
|
||||
if(deviceNode.address !in allNodes.get()) return false
|
||||
if(address in allNodes.get()) return false
|
||||
allNodes.get().remove(deviceNode.address)
|
||||
deviceNode.address = address
|
||||
allNodes.get()[address] = deviceNode
|
||||
return true
|
||||
}
|
||||
|
||||
fun addNode(deviceNode: DeviceNode) {
|
||||
@@ -103,12 +105,17 @@ object Networking {
|
||||
allNodes.get().forEach { it.value.onNodeAdded(deviceNode) }
|
||||
}
|
||||
|
||||
fun addNodes(vararg deviceNodes: DeviceNode) {
|
||||
fun addNodes(deviceNodes: Iterable<DeviceNode>) {
|
||||
deviceNodes.forEach { addNode(it) }
|
||||
}
|
||||
|
||||
fun addNodes(vararg deviceNodes: DeviceNode) {
|
||||
addNodes(deviceNodes.asIterable())
|
||||
}
|
||||
|
||||
fun removeNode(deviceNode: DeviceNode) {
|
||||
if(deviceNode.address !in allNodes.get()) return
|
||||
NodeSynchronizer.nodeErased(deviceNode)
|
||||
allNodes.get().forEach { it.value.onNodeRemoved(deviceNode) }
|
||||
// toList() in order to copy it
|
||||
deviceNode.connections.toList().forEach {
|
||||
@@ -121,10 +128,14 @@ object Networking {
|
||||
}
|
||||
}
|
||||
|
||||
fun removeNodes(vararg deviceNodes: DeviceNode) {
|
||||
fun removeNodes(deviceNodes: Iterable<DeviceNode>) {
|
||||
deviceNodes.forEach { removeNode(it) }
|
||||
}
|
||||
|
||||
fun removeNodes(vararg deviceNodes: DeviceNode) {
|
||||
removeNodes(deviceNodes.asIterable())
|
||||
}
|
||||
|
||||
val channels = ThreadLocal.withInitial { HashMap<String, MutableSet<DeviceNode>>() }
|
||||
|
||||
fun addToChannel(channel: String, deviceNode: DeviceNode) {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package org.neoflock.neocomputers.network
|
||||
|
||||
import dev.architectury.networking.NetworkManager
|
||||
import io.netty.buffer.Unpooled
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.FriendlyByteBuf
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf
|
||||
import net.minecraft.network.codec.StreamCodec
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.level.Level
|
||||
import org.neoflock.neocomputers.NeoComputers
|
||||
import java.time.Duration
|
||||
|
||||
object NodeSynchronizer {
|
||||
class DeviceBlockStatePayload(var blockPos: BlockPos, var buffers: List<FriendlyByteBuf>): CustomPacketPayload {
|
||||
companion object {
|
||||
val NODE_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "node_sync")
|
||||
val TYPE = CustomPacketPayload.Type<DeviceBlockStatePayload>(NODE_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, DeviceBlockStatePayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): DeviceBlockStatePayload {
|
||||
val blockPos = buf.readBlockPos()
|
||||
val bufferCount = buf.readVarInt()
|
||||
val buffers = List(bufferCount) {
|
||||
val bytes = buf.readByteArray()
|
||||
val rawBuf = Unpooled.buffer(bytes.size)
|
||||
rawBuf.writeBytes(bytes)
|
||||
FriendlyByteBuf(rawBuf)
|
||||
}
|
||||
return DeviceBlockStatePayload(blockPos, buffers)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: DeviceBlockStatePayload) {
|
||||
buf.writeBlockPos(payload.blockPos)
|
||||
buf.writeVarInt(payload.buffers.size)
|
||||
payload.buffers.forEach {
|
||||
buf.writeByteArray(it.array())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class ScreenPayload(var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val SCREEN_SYNC_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_sync")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenPayload>(SCREEN_SYNC_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): ScreenPayload {
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return ScreenPayload(buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenPayload) {
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class ScreenDataPayload(var buffer: FriendlyByteBuf): CustomPacketPayload {
|
||||
companion object {
|
||||
val SCREEN_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "screen_data")
|
||||
val TYPE = CustomPacketPayload.Type<ScreenDataPayload>(SCREEN_DATA_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, ScreenDataPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): ScreenDataPayload {
|
||||
val buffer = FriendlyByteBuf(buf.copy(buf.readerIndex(), buf.readableBytes()))
|
||||
return ScreenDataPayload(buffer)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: ScreenDataPayload) {
|
||||
buf.writeBytes(payload.buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
class BeepDataPayload(val pos: BlockPos, val pattern: String, val freq: Int, val duration: Duration, val volume: Double): CustomPacketPayload {
|
||||
companion object {
|
||||
val BEEP_DATA_ID = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "beep_data")
|
||||
val TYPE = CustomPacketPayload.Type<BeepDataPayload>(BEEP_DATA_ID)
|
||||
val CODEC = object : StreamCodec<RegistryFriendlyByteBuf, BeepDataPayload> {
|
||||
override fun decode(buf: RegistryFriendlyByteBuf): BeepDataPayload {
|
||||
val pos = buf.readBlockPos()
|
||||
val pattern = buf.readUtf()
|
||||
val freq = buf.readVarInt()
|
||||
val duration = buf.readVarLong()
|
||||
val volume = buf.readDouble()
|
||||
return BeepDataPayload(pos, pattern, freq, Duration.ofMillis(duration), volume)
|
||||
}
|
||||
|
||||
override fun encode(buf: RegistryFriendlyByteBuf, payload: BeepDataPayload) {
|
||||
buf.writeBlockPos(payload.pos)
|
||||
buf.writeUtf(payload.pattern)
|
||||
buf.writeVarInt(payload.freq)
|
||||
buf.writeVarLong(payload.duration.toMillis())
|
||||
buf.writeDouble(payload.volume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun type() = TYPE
|
||||
}
|
||||
|
||||
val screenMap = HashMap<ServerPlayer, DeviceNode>()
|
||||
|
||||
fun playerScreenClosed(player: ServerPlayer) {
|
||||
screenMap.remove(player)
|
||||
}
|
||||
|
||||
fun registerPlayerScreen(player: ServerPlayer, devNode: DeviceNode) {
|
||||
screenMap[player] = devNode
|
||||
}
|
||||
|
||||
fun nodeErased(node: DeviceNode) {
|
||||
var player: ServerPlayer? = null
|
||||
for((p, n) in screenMap) {
|
||||
if(n == node) player = p
|
||||
}
|
||||
if(player != null) screenMap.remove(player)
|
||||
}
|
||||
|
||||
fun syncScreens() {
|
||||
for((player, ent) in screenMap) {
|
||||
val buf = FriendlyByteBuf(Unpooled.buffer())
|
||||
ent.encodeScreenData(player, buf)
|
||||
NetworkManager.sendToPlayer(player, ScreenPayload(buf))
|
||||
}
|
||||
}
|
||||
|
||||
fun sendScreenInteraction(friendlyByteBuf: FriendlyByteBuf) {
|
||||
NetworkManager.sendToServer(ScreenDataPayload(friendlyByteBuf))
|
||||
}
|
||||
|
||||
fun emitBeep(level: Level, beepDataPayload: BeepDataPayload) {
|
||||
if(level is ServerLevel) {
|
||||
level.players().forEach {
|
||||
NetworkManager.sendToPlayer(it, beepDataPayload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package org.neoflock.neocomputers.network
|
||||
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import org.neoflock.neocomputers.block.DeviceBlockEntity
|
||||
//? if fabric {
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext
|
||||
import org.neoflock.neocomputers.block.NodeBlockEntity
|
||||
import net.minecraft.core.Direction
|
||||
import team.reborn.energy.api.EnergyStorage;
|
||||
//?}
|
||||
|
||||
@@ -11,27 +12,30 @@ import team.reborn.energy.api.EnergyStorage;
|
||||
// the NodeBlockEntity and Node given us a way to get power from a block, we just
|
||||
// need to tell mods how to do it as well
|
||||
object PowerManager {
|
||||
fun<T: NodeBlockEntity> registerPowerBlockEntity(blockEntityType: BlockEntityType<T>) {
|
||||
fun<T: DeviceBlockEntity> registerPowerDevice(blockEntityType: BlockEntityType<T>) {
|
||||
//? if fabric {
|
||||
EnergyStorage.SIDED.registerForBlockEntity({
|
||||
entity, dir -> object : EnergyStorage {
|
||||
override fun getAmount() = entity.deviceNode.energy
|
||||
override fun getCapacity() = entity.deviceNode.energyCapacity
|
||||
override fun supportsExtraction() = entity.deviceNode.powerRole != PowerRole.CONSUMER && entity.deviceNode.energyCapacity > 0
|
||||
override fun supportsInsertion() = entity.deviceNode.powerRole != PowerRole.GENERATOR
|
||||
// TODO: as this is currently written, if the node instance changes and the mod cached the conversion, we're boned. Consider fixing it.
|
||||
entity, dir ->
|
||||
val node = entity.getNodeFromSide(dir ?: Direction.UP)
|
||||
if(node == null) null else object : EnergyStorage {
|
||||
override fun getAmount() = node.energy
|
||||
override fun getCapacity() = node.energyCapacity
|
||||
override fun supportsExtraction() = node.powerRole != PowerRole.CONSUMER && node.energyCapacity > 0
|
||||
override fun supportsInsertion() = node.powerRole != PowerRole.GENERATOR
|
||||
override fun extract(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(entity.deviceNode.powerRole == PowerRole.CONSUMER) return 0
|
||||
val taken = entity.deviceNode.withdrawEnergy(maxAmount)
|
||||
if(node.powerRole == PowerRole.CONSUMER) return 0
|
||||
val taken = node.withdrawEnergy(maxAmount)
|
||||
transaction?.addCloseCallback {
|
||||
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) entity.deviceNode.giveEnergy(taken)
|
||||
ctx, res -> if(res.wasAborted() || !res.wasCommitted()) node.giveEnergy(taken)
|
||||
}
|
||||
return taken
|
||||
}
|
||||
override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {
|
||||
if(entity.deviceNode.powerRole == PowerRole.GENERATOR) return 0
|
||||
val given = entity.deviceNode.giveEnergy(maxAmount)
|
||||
if(node.powerRole == PowerRole.GENERATOR) return 0
|
||||
val given = node.giveEnergy(maxAmount)
|
||||
transaction?.addCloseCallback { ctx, res ->
|
||||
if (res.wasAborted() || !res.wasCommitted()) entity.deviceNode.withdrawEnergy(given)
|
||||
if (res.wasAborted() || !res.wasCommitted()) node.withdrawEnergy(given)
|
||||
}
|
||||
return given
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 618 B |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 651 B After Width: | Height: | Size: 651 B |
|
Before Width: | Height: | Size: 389 B After Width: | Height: | Size: 389 B |