You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
8.8 KiB
Kotlin

package net.brokenmoon.redcontrol.blocks
import com.mojang.serialization.MapCodec
import net.brokenmoon.redcontrol.RedControl
import net.brokenmoon.redcontrol.RedControlNetworking
import net.brokenmoon.redcontrol.blockentities.Peripheral
import net.brokenmoon.redcontrol.util.unsigned
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.listener.ClientPlayPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.ActionResult.FAIL
import net.minecraft.util.ActionResult.SUCCESS
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
import java.io.FileNotFoundException
import java.net.URI
import kotlin.experimental.xor
class TerminalBlock(settings: Settings) : NetworkCarrier(settings) {
@Suppress("OVERRIDE_DEPRECATION")
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
val monitor = world.getBlockEntity(pos) as? TerminalEntity?: return FAIL
if (!world.isClient) {
if (player.getStackInHand(hand).item === RedControl.SQUEAKY_HAMMER) {
super.onUse(state, world, pos, player, hand, hit)
//val text: Array<String> = monitor.getText()
//for (s in text) {
// player.sendMessage(Text.literal(s), false)
//}
}
val bbuf = PacketByteBufs.create()
bbuf.writeBlockPos(pos)
ServerPlayNetworking.send(player as ServerPlayerEntity,RedControlNetworking.MONITOR_PACKET_ID,bbuf)
}
return SUCCESS
}
override fun getCodec(): MapCodec<out BlockWithEntity?>? {
return null
}
override fun createBlockEntity(pos: BlockPos, state: BlockState) = TerminalEntity(pos, state)
}
class TerminalEntity(pos: BlockPos, state: BlockState) : Peripheral(RedControl.TERMINAL_BLOCK_ENTITY, pos, state,1) {
val screen = ByteArray(80 * 50) { 0x20 }
val charset = RedControl.images["charset.bin"]?.clone()?: throw FileNotFoundException("expected a charset.bin in images datapack")
val kb = ByteArray(16)
var command: Byte = 0
var row = 0
var cx = 0
var cy = 0
var cm = 2
var kbs = 0
var kbp = 0
var bx1 = 0
var by1 = 0
var bx2 = 0
var by2 = 0
var bw = 0
var bh = 0
var char = 0
fun getText(): Array<String> {
val l = Array(80) { _ -> "" }
for (y in 0 until 50) {
var temp = ""
for (x in 0 until 80) {
temp += Char(screen[y*80+x].toUShort())
}
l[y] = temp
}
return l
}
fun pushKey(byte: Byte): Boolean {
return if ((kbp + 1) % 16 != kbs) {
kb[kbp] = byte
kbp = (kbp + 1) % 16
true
} else false
}
fun getIndices(x1: Int, y1: Int, w: Int, h: Int): Sequence<Int> = sequence {
for (i in 0 until h) for (j in 0 until w) {
val x = j + x1
val y = i + y1
if (x in 0 until 80 && y in 0 until 60)
yield(x + 80 * y)
}
}
override fun read(address: Int): Int = readData(address.toByte()).toInt()
override fun update() {
val data = this
var error = false
when (data.command.unsigned) {
1 -> data.getIndices(data.bx2, data.by2, data.bw, data.bh).forEach { data.screen[it] = data.bx1.toByte() }
2 -> data.getIndices(data.bx2, data.by2, data.bw, data.bh).forEach { data.screen[it] = data.screen[it] xor 0x80.toByte() }
3 -> data.getIndices(data.bx2, data.by2, data.bw, data.bh).zip(data.getIndices(data.bx1, data.by1, data.bw, data.bh)).forEach { (dest, src) -> data.screen[dest] = data.screen[src] }
4 -> RedControl.images["charset.bin"]!!.copyInto(data.charset)
255 -> Unit
else -> error = true
}
if (data.command in 1..4) {
markDirty()
world?.updateListeners(pos, cachedState, cachedState, Block.NOTIFY_LISTENERS)
}
data.command = if (error) -1 else 0
}
override fun write(address: Int, data: Int) = storeData(address.toByte(),data.toByte())
private fun readData(at: Byte): Byte {
return when (val at = at.unsigned) {
0x00 -> row.toByte()
0x01 -> cx.toByte()
0x02 -> cy.toByte()
0x03 -> cm.toByte()
0x04 -> kbs.toByte()
0x05 -> kbp.toByte()
0x06 -> kb[kbs]
0x07 -> command
0x08 -> bx1.toByte()
0x09 -> by1.toByte()
0x0A -> bx2.toByte()
0x0B -> by2.toByte()
0x0C -> bw.toByte()
0x0D -> bh.toByte()
0x0E -> char.toByte()
in 0x10..0x5F -> screen[row * 80 + at - 0x10]
in 0x60..0x67 -> charset[char * 8 + at - 0x60]
else -> 0
}
}
private fun storeData(at: Byte, data: Byte) {
when (val at = at.unsigned) {
0x00 -> row = data.unsigned % 50
0x01 -> cx = data.unsigned % 80
0x02 -> cy = data.unsigned % 50
0x03 -> cm = data.unsigned % 3
0x04 -> kbs = data.unsigned % 16
0x05 -> kbp = data.unsigned % 16
0x06 -> kb[kbs] = data
0x07 -> command = data
0x08 -> bx1 = data.unsigned % 80
0x09 -> by1 = data.unsigned % 50
0x0A -> bx2 = data.unsigned % 80
0x0B -> by2 = data.unsigned % 50
0x0C -> bw = data.unsigned
0x0D -> bh = data.unsigned
0x0E -> char = data.unsigned
in 0x10..0x5F -> screen[row * 80 + at - 0x10] = data
in 0x60..0x67 -> charset[char * 8 + at - 0x60] = data
}
val needsClientUpdate = at.unsigned in setOf(0x01, 0x02, 0x03) + (0x10..0x67)
markDirty()
if (needsClientUpdate) {
getWorld()?.updateListeners(getPos(), cachedState, cachedState, Block.NOTIFY_LISTENERS)
}
}
override fun toInitialChunkDataNbt(): NbtCompound {
val tag = super.toInitialChunkDataNbt()
// these are big, TODO: only send changed data
tag.putByteArray("screen", screen)
tag.putByteArray("charset", charset)
tag.putByte("cx", cx.toByte())
tag.putByte("cy", cy.toByte())
tag.putByte("cm", cm.toByte())
return tag
}
override fun writeNbt(tag: NbtCompound) {
super.writeNbt(tag)
tag.putByteArray("screen", screen)
tag.putByteArray("charset", charset)
tag.putByteArray("kb", kb)
tag.putByte("command", command)
tag.putByte("row", row.toByte())
tag.putByte("cx", cx.toByte())
tag.putByte("cy", cy.toByte())
tag.putByte("cm", cm.toByte())
tag.putByte("kbs", kbs.toByte())
tag.putByte("kbp", kbp.toByte())
tag.putByte("bx1", bx1.toByte())
tag.putByte("by1", by1.toByte())
tag.putByte("bx2", bx2.toByte())
tag.putByte("by2", by2.toByte())
tag.putByte("bw", bw.toByte())
tag.putByte("bh", bh.toByte())
tag.putByte("char", char.toByte())
}
override fun readNbt(tag: NbtCompound) {
super.readNbt(tag)
val world = getWorld()
tag.getByteArray("screen").copyInto(screen)
tag.getByteArray("charset").copyInto(charset)
cx = tag.getByte("cx").unsigned
cy = tag.getByte("cy").unsigned
cm = tag.getByte("cm").unsigned
if (world == null || !world.isClient) {
tag.getByteArray("screen").copyInto(screen)
tag.getByteArray("charset").copyInto(charset)
tag.getByteArray("kb").copyInto(kb)
command = tag.getByte("command")
row = tag.getByte("row").unsigned
kbs = tag.getByte("kbs").unsigned
kbp = tag.getByte("kbp").unsigned
bx1 = tag.getByte("bx1").unsigned
by1 = tag.getByte("by1").unsigned
bx2 = tag.getByte("bx2").unsigned
by2 = tag.getByte("by2").unsigned
bw = tag.getByte("bw").unsigned
bh = tag.getByte("bh").unsigned
char = tag.getByte("char").unsigned
}
}
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? = BlockEntityUpdateS2CPacket.create(this);
}