NodeBlockEntity is dead, long live SingleDeviceBlockEntity + cables function
This commit is contained in:
@@ -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) }
|
||||
Direction.entries.forEach { handleConnectionsFor(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 {
|
||||
|
||||
Reference in New Issue
Block a user