terminal screen no worky
parent
44b600a835
commit
b9be0549c9
@ -0,0 +1,93 @@
|
||||
package net.brokenmoon.redcontrol
|
||||
|
||||
import net.brokenmoon.redcontrol.RedControl.modloc
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.ResourceReloader.Synchronizer
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.profiler.Profiler
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.lwjgl.opengl.GL30
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Executor
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
object Shaders {
|
||||
|
||||
private var screen = 0
|
||||
|
||||
fun screen() = screen
|
||||
|
||||
fun init() {
|
||||
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(object : IdentifiableResourceReloadListener {
|
||||
|
||||
override fun reload(s: Synchronizer, rm: ResourceManager, profiler: Profiler, profiler1: Profiler, executor: Executor, executor1: Executor): CompletableFuture<Void> {
|
||||
return CompletableFuture.runAsync({
|
||||
if (screen != 0) GL30.glDeleteProgram(screen)
|
||||
|
||||
screen = loadShader(rm, "screen")
|
||||
}, executor1).thenCompose { s.whenPrepared(null) }
|
||||
}
|
||||
|
||||
override fun getFabricId(): Identifier = modloc("shaders")
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadShader(rm: ResourceManager, id: String): Int {
|
||||
val vshs = rm.getResource(modloc("shaders/$id.vert")).getOrNull()?.inputStream?.bufferedReader()?.readText()
|
||||
val fshs = rm.getResource(modloc("shaders/$id.frag")).getOrNull()?.inputStream?.bufferedReader()?.readText()
|
||||
|
||||
val vsh = GL30.glCreateShader(GL30.GL_VERTEX_SHADER)
|
||||
val fsh = GL30.glCreateShader(GL30.GL_FRAGMENT_SHADER)
|
||||
val prog = GL30.glCreateProgram()
|
||||
|
||||
// No goto? I'll make my own.
|
||||
run {
|
||||
GL30.glShaderSource(vsh, vshs)
|
||||
GL30.glShaderSource(fsh, fshs)
|
||||
|
||||
GL30.glCompileShader(vsh)
|
||||
if (GL30.glGetShaderi(vsh, GL30.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
|
||||
// TODO use logger
|
||||
val log = GL30.glGetShaderInfoLog(vsh, 32768)
|
||||
println("Failed to compile vertex shader '$id'")
|
||||
for (line in log.lineSequence()) println(line)
|
||||
return@run
|
||||
}
|
||||
|
||||
GL30.glCompileShader(fsh)
|
||||
if (GL30.glGetShaderi(fsh, GL30.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
|
||||
// TODO use logger
|
||||
val log = GL30.glGetShaderInfoLog(fsh, 32768)
|
||||
println("Failed to compile fragment shader '$id'")
|
||||
for (line in log.lineSequence()) println(line)
|
||||
return@run
|
||||
}
|
||||
|
||||
GL30.glAttachShader(prog, vsh)
|
||||
GL30.glAttachShader(prog, fsh)
|
||||
GL30.glLinkProgram(prog)
|
||||
|
||||
if (GL30.glGetProgrami(prog, GL30.GL_LINK_STATUS) == GL11.GL_FALSE) {
|
||||
// TODO use logger
|
||||
val log = GL30.glGetProgramInfoLog(prog, 32768)
|
||||
println("Failed to link program '$id'")
|
||||
for (line in log.lineSequence()) println(line)
|
||||
return@run
|
||||
}
|
||||
|
||||
GL30.glDeleteShader(vsh)
|
||||
GL30.glDeleteShader(fsh)
|
||||
return prog
|
||||
}
|
||||
|
||||
GL30.glDeleteShader(vsh)
|
||||
GL30.glDeleteShader(fsh)
|
||||
GL30.glDeleteProgram(prog)
|
||||
return 0
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package net.brokenmoon.redcontrol.screen;
|
||||
|
||||
import net.brokenmoon.redcontrol.RedControl;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class MonitorScreen extends Screen {
|
||||
Identifier screenTexture = new Identifier("redcontrol", "gui/display.png");
|
||||
public MonitorScreen(Text title) {
|
||||
super(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
RedControl.LOGGER.info("Opened Monitor Screen");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
context.drawTexture(screenTexture,0,0,0,0,350,230);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPause() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
package net.brokenmoon.redcontrol.screen
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager
|
||||
import com.mojang.blaze3d.platform.TextureUtil
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import io.netty.buffer.Unpooled
|
||||
import net.brokenmoon.redcontrol.RedControlNetworking
|
||||
import net.brokenmoon.redcontrol.Shaders
|
||||
import net.brokenmoon.redcontrol.blocks.TerminalEntity
|
||||
import net.dblsaiko.qcommon.croco.Mat4
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gl.Framebuffer
|
||||
import net.minecraft.client.gl.SimpleFramebuffer
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.render.VertexFormat.DrawMode
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import org.lwjgl.BufferUtils
|
||||
import org.lwjgl.glfw.GLFW
|
||||
import org.lwjgl.opengl.*
|
||||
import org.lwjgl.opengl.GL11.GL_FLOAT
|
||||
import org.lwjgl.opengl.GL11.GL_TRIANGLES
|
||||
import kotlin.experimental.xor
|
||||
import kotlin.math.round
|
||||
|
||||
private val buf = BufferUtils.createByteBuffer(16384)
|
||||
|
||||
private val vbo = GL30.glGenBuffers()
|
||||
private val vao = GL30.glGenVertexArrays()
|
||||
private val screenTex = createTexture()
|
||||
private val charsetTex = createTexture()
|
||||
|
||||
class TerminalScreen(val te: TerminalEntity) : Screen(Text.translatable("block.redcontrol.terminal")) {
|
||||
|
||||
private var uMvp = 0
|
||||
private var uCharset = 0
|
||||
private var uScreen = 0
|
||||
private var aXyz = 0
|
||||
private var aUv = 0
|
||||
|
||||
private var fb: Framebuffer? = null
|
||||
|
||||
override fun tick() {
|
||||
val minecraft = client ?: return
|
||||
val dist = minecraft.player?.getCameraPosVec(1f)?.squaredDistanceTo(Vec3d.ofCenter(te.pos))
|
||||
?: Double.POSITIVE_INFINITY
|
||||
if (dist > 10 * 10) minecraft.setScreen(null)
|
||||
}
|
||||
|
||||
override fun render(ctx: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
renderBackground(ctx,mouseX,mouseY,delta)
|
||||
val matrices = ctx.matrices
|
||||
val sh = Shaders.screen()
|
||||
val fb = fb ?: return
|
||||
val mc = client ?: return
|
||||
|
||||
fb.setTexFilter(if ((mc.window.scaleFactor.toInt() % 2) == 0) GL11.GL_NEAREST else GL11.GL_LINEAR)
|
||||
|
||||
fb.beginWrite(true)
|
||||
val mat = Mat4.ortho(0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f)
|
||||
|
||||
GL30.glUseProgram(sh)
|
||||
GL30.glBindVertexArray(vao)
|
||||
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo)
|
||||
|
||||
GL20.glEnableVertexAttribArray(aXyz)
|
||||
GL20.glEnableVertexAttribArray(aUv)
|
||||
|
||||
RenderSystem.activeTexture(GL13.GL_TEXTURE0)
|
||||
//RenderSystem.enableTexture() //TODO: figure out why this does not exists
|
||||
RenderSystem.bindTexture(screenTex)
|
||||
|
||||
buf.clear()
|
||||
val fbuf = buf.asFloatBuffer()
|
||||
mat.intoBuffer(fbuf)
|
||||
fbuf.flip()
|
||||
GL30.glUniformMatrix4fv(uMvp, false, fbuf)
|
||||
|
||||
GL30.glUniform1i(uScreen, 0)
|
||||
|
||||
buf.clear()
|
||||
buf.put(te.screen)
|
||||
|
||||
if (te.cm == 1 || (te.cm == 2 && (System.currentTimeMillis() / 500) % 2 == 0L)) {
|
||||
val ci = te.cx + te.cy * 80
|
||||
buf.put(ci, te.screen[ci] xor 0x80.toByte())
|
||||
}
|
||||
|
||||
buf.rewind()
|
||||
GlStateManager._texImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_R16I, 80, 60, 0, GL30.GL_RED_INTEGER, GL11.GL_UNSIGNED_BYTE, buf.asIntBuffer())
|
||||
|
||||
RenderSystem.activeTexture(GL13.GL_TEXTURE2)
|
||||
//RenderSystem.enableTexture() //TODO: figure out why this does not exists
|
||||
RenderSystem.bindTexture(charsetTex)
|
||||
GL30.glUniform1i(uCharset, 2)
|
||||
|
||||
buf.clear()
|
||||
buf.put(te.charset)
|
||||
buf.rewind()
|
||||
GlStateManager._texImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_R16I, 8, 256, 0, GL30.GL_RED_INTEGER, GL11.GL_UNSIGNED_BYTE, buf.asIntBuffer())
|
||||
|
||||
GL11.glDrawArrays(GL_TRIANGLES, 0, 6)
|
||||
|
||||
GL20.glDisableVertexAttribArray(aXyz)
|
||||
GL20.glDisableVertexAttribArray(aUv)
|
||||
|
||||
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0)
|
||||
GL30.glBindVertexArray(0)
|
||||
GL30.glUseProgram(0)
|
||||
|
||||
RenderSystem.bindTexture(0)
|
||||
//RenderSystem.disableTexture() //TODO: figure out why this does not exists
|
||||
RenderSystem.activeTexture(GL13.GL_TEXTURE0)
|
||||
RenderSystem.bindTexture(0)
|
||||
|
||||
mc.framebuffer.beginWrite(true)
|
||||
|
||||
val swidth = 8 * 80 * 0.5
|
||||
val sheight = 8 * 50 * 0.5
|
||||
val x1 = round(width / 2.0 - swidth / 2.0)
|
||||
val y1 = round(height / 2.0 - sheight / 2.0)
|
||||
|
||||
matrices.push()
|
||||
matrices.translate(x1, y1, -2000.0) // why the -2000? not sure
|
||||
|
||||
val shader = mc.gameRenderer.blitScreenProgram
|
||||
shader.addSampler("DiffuseSampler", fb.colorAttachment)
|
||||
shader.modelViewMat?.set(matrices.peek().positionMatrix)
|
||||
shader.projectionMat?.set(RenderSystem.getProjectionMatrix())
|
||||
shader.bind()
|
||||
|
||||
val t = RenderSystem.renderThreadTesselator()
|
||||
val buf = t.buffer
|
||||
buf.begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR)
|
||||
buf.vertex(0.0, 0.0, 0.0).texture(0f, 1f).color(255, 255, 255, 255).next()
|
||||
buf.vertex(0.0, sheight, 0.0).texture(0f, 0f).color(255, 255, 255, 255).next()
|
||||
buf.vertex(swidth, sheight, 0.0).texture(1f, 0f).color(255, 255, 255, 255).next()
|
||||
buf.vertex(swidth, 0.0, 0.0).texture(1f, 1f).color(255, 255, 255, 255).next()
|
||||
buf.end()
|
||||
//BufferRenderer.postDraw(buf) //TODO: figure out why this is gone
|
||||
|
||||
shader.unbind()
|
||||
|
||||
matrices.pop()
|
||||
}
|
||||
|
||||
override fun keyPressed(key: Int, scancode: Int, modifiers: Int): Boolean {
|
||||
if (super.keyPressed(key, scancode, modifiers)) return true
|
||||
|
||||
val result: Byte? = when (key) {
|
||||
GLFW.GLFW_KEY_BACKSPACE -> 0x08
|
||||
GLFW.GLFW_KEY_ENTER -> 0x0D
|
||||
GLFW.GLFW_KEY_HOME -> 0x80
|
||||
GLFW.GLFW_KEY_END -> 0x81
|
||||
GLFW.GLFW_KEY_UP -> 0x82
|
||||
GLFW.GLFW_KEY_DOWN -> 0x83
|
||||
GLFW.GLFW_KEY_LEFT -> 0x84
|
||||
GLFW.GLFW_KEY_RIGHT -> 0x85
|
||||
else -> null
|
||||
}?.toByte()
|
||||
|
||||
if (result != null) pushKey(result)
|
||||
|
||||
return result != null
|
||||
}
|
||||
|
||||
override fun charTyped(c: Char, modifiers: Int): Boolean {
|
||||
if (super.charTyped(c, modifiers)) return true
|
||||
|
||||
val result: Byte? = when (c) {
|
||||
in '\u0001'..'\u007F' -> c.code.toByte()
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (result != null) pushKey(result)
|
||||
|
||||
return result != null
|
||||
}
|
||||
|
||||
private fun pushKey(c: Byte) {
|
||||
val buffer = PacketByteBuf(Unpooled.buffer())
|
||||
buffer.writeBlockPos(te.pos)
|
||||
buffer.writeByte(c.toInt())
|
||||
ClientPlayNetworking.send(RedControlNetworking.KEY_PRESS, buffer)
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
//client!!.keyboard.setRepeatEvents(true) //TODO: figure out why this does not exists
|
||||
|
||||
initDrawData()
|
||||
initFb()
|
||||
}
|
||||
|
||||
private fun initDrawData() {
|
||||
val sh = Shaders.screen()
|
||||
|
||||
GL30.glUseProgram(sh)
|
||||
GL30.glBindVertexArray(vao)
|
||||
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, vbo)
|
||||
|
||||
uMvp = GL30.glGetUniformLocation(sh, "mvp")
|
||||
uCharset = GL30.glGetUniformLocation(sh, "charset")
|
||||
uScreen = GL30.glGetUniformLocation(sh, "screen")
|
||||
|
||||
aXyz = GL30.glGetAttribLocation(sh, "xyz")
|
||||
aUv = GL30.glGetAttribLocation(sh, "uv")
|
||||
|
||||
GL20.glVertexAttribPointer(aXyz, 3, GL_FLOAT, false, 20, 0)
|
||||
GL20.glVertexAttribPointer(aUv, 2, GL_FLOAT, false, 20, 12)
|
||||
|
||||
buf.clear()
|
||||
|
||||
floatArrayOf(
|
||||
0f, 0f, 0f, 0f, 0f,
|
||||
1f, 1f, 0f, 1f, 1f,
|
||||
1f, 0f, 0f, 1f, 0f,
|
||||
|
||||
0f, 0f, 0f, 0f, 0f,
|
||||
0f, 1f, 0f, 0f, 1f,
|
||||
1f, 1f, 0f, 1f, 1f
|
||||
).forEach { buf.putFloat(it) }
|
||||
|
||||
buf.rewind()
|
||||
|
||||
GL30.glBufferData(GL30.GL_ARRAY_BUFFER, buf, GL15.GL_STATIC_DRAW)
|
||||
|
||||
GL30.glBindBuffer(GL30.GL_ARRAY_BUFFER, 0)
|
||||
GL30.glBindVertexArray(0)
|
||||
GL30.glUseProgram(0)
|
||||
}
|
||||
|
||||
private fun initFb() {
|
||||
fb?.delete()
|
||||
val scale = 4
|
||||
fb = SimpleFramebuffer(80 * 8 * scale, 50 * 8 * scale, false, MinecraftClient.IS_SYSTEM_MAC)
|
||||
}
|
||||
|
||||
override fun removed() {
|
||||
//client!!.keyboard.setRepeatEvents(false) //TODO: check if this is still okay. or if we need to change this
|
||||
fb?.delete()
|
||||
fb = null
|
||||
}
|
||||
|
||||
override fun shouldPause() = false
|
||||
|
||||
}
|
||||
|
||||
private fun createTexture(): Int {
|
||||
val tex = TextureUtil.generateTextureId()
|
||||
RenderSystem.bindTexture(tex)
|
||||
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST)
|
||||
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST)
|
||||
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT)
|
||||
RenderSystem.texParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT)
|
||||
RenderSystem.bindTexture(0)
|
||||
return tex
|
||||
}
|
@ -1,276 +0,0 @@
|
||||
package net.brokenmoon.redcontrol.blockentities;
|
||||
|
||||
import net.brokenmoon.redcontrol.RedControl;
|
||||
import net.minecraft.block.BlockState;
|
||||
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.util.math.BlockPos;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MonitorEntity extends Peripheral {
|
||||
|
||||
public static final int WIDTH = 80;
|
||||
public static final int HEIGHT = 50;
|
||||
|
||||
private int accessRow;
|
||||
private int cursorX;
|
||||
private int cursorY;
|
||||
private int cursorMode = 2; // (0: hidden, 1: solid, 2: blink)
|
||||
private int keyBufferStart;
|
||||
private int keyBufferPos;
|
||||
private int blitMode; // (1: fill, 2: invert; 3: shift)
|
||||
private int blitXStartOrFill;
|
||||
private int blitYStart;
|
||||
private int blitXOffset;
|
||||
private int blitYOffset;
|
||||
private int blitWidth;
|
||||
private int blitHeight;
|
||||
|
||||
private byte[] keyBuffer = new byte[0x10];
|
||||
private byte[][] windowData = new byte[HEIGHT][WIDTH];
|
||||
|
||||
public String[] getText(){
|
||||
String[] l = new String[HEIGHT];
|
||||
for(int y = 0; y < HEIGHT; y++) {
|
||||
String temp = "";
|
||||
for (int x = 0; x < WIDTH; x++) {
|
||||
temp += (char) windowData[y][x];
|
||||
}
|
||||
l[y] = temp;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
private boolean isDisplayDirty;
|
||||
private boolean isCursorDirty;
|
||||
|
||||
public MonitorEntity(BlockPos pos, BlockState state) {
|
||||
super(RedControl.MONITOR_BLOCK_ENTITY, pos, state, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int address, int data) {
|
||||
switch (address) {
|
||||
case 0x00:
|
||||
this.accessRow = data;
|
||||
break;
|
||||
case 0x01:
|
||||
this.isCursorDirty = this.cursorX != data;
|
||||
this.cursorX = data;
|
||||
break;
|
||||
case 0x02:
|
||||
this.isCursorDirty = this.cursorY != data;
|
||||
this.cursorY = data;
|
||||
break;
|
||||
case 0x03:
|
||||
this.isCursorDirty = this.cursorMode != data;
|
||||
this.cursorMode = data;
|
||||
break;
|
||||
case 0x04:
|
||||
this.keyBufferStart = data & 0x0f;
|
||||
break;
|
||||
case 0x05:
|
||||
this.keyBufferPos = data & 0x0f;
|
||||
break;
|
||||
case 0x06:
|
||||
break;
|
||||
case 0x07:
|
||||
this.blitMode = data;
|
||||
break;
|
||||
case 0x08:
|
||||
this.blitXStartOrFill = data;
|
||||
break;
|
||||
case 0x09:
|
||||
this.blitYStart = data;
|
||||
break;
|
||||
case 0x0A:
|
||||
this.blitXOffset = data;
|
||||
break;
|
||||
case 0x0B:
|
||||
this.blitYOffset = data;
|
||||
break;
|
||||
case 0x0C:
|
||||
this.blitWidth = data;
|
||||
break;
|
||||
case 0x0D:
|
||||
this.blitHeight = data;
|
||||
break;
|
||||
default:
|
||||
if (address >= 0x10 && address < 0x60) {
|
||||
this.isDisplayDirty = true;
|
||||
this.windowData[this.accessRow][address - 0x10] = (byte) data;
|
||||
}
|
||||
if (this.isDisplayDirty) {
|
||||
this.isDisplayDirty = false;
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(int address) {
|
||||
switch (address) {
|
||||
case 0x00:
|
||||
return this.accessRow;
|
||||
case 0x01:
|
||||
return this.cursorX;
|
||||
case 0x02:
|
||||
return this.cursorY;
|
||||
case 0x03:
|
||||
return this.cursorMode;
|
||||
case 0x04:
|
||||
return this.keyBufferStart;
|
||||
case 0x05:
|
||||
return this.keyBufferPos;
|
||||
case 0x06:
|
||||
return this.keyBuffer[this.keyBufferStart] & 0xff;
|
||||
case 0x07:
|
||||
return this.blitMode;
|
||||
case 0x08:
|
||||
return this.blitXStartOrFill;
|
||||
case 0x09:
|
||||
return this.blitYStart;
|
||||
case 0x0A:
|
||||
return this.blitXOffset;
|
||||
case 0x0B:
|
||||
return this.blitYOffset;
|
||||
case 0x0C:
|
||||
return this.blitWidth;
|
||||
case 0x0D:
|
||||
return this.blitHeight;
|
||||
default:
|
||||
if (address >= 0x10 && address < 0x60) {
|
||||
return this.windowData[this.accessRow][address - 0x10] & 0xff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
int maxWidth = Math.min(WIDTH, this.blitWidth + this.blitXOffset);
|
||||
int maxHeight = Math.min(HEIGHT, this.blitHeight + this.blitYOffset);
|
||||
|
||||
int row = this.blitYOffset;
|
||||
int col;
|
||||
|
||||
this.isDisplayDirty |= this.blitMode != 0;
|
||||
|
||||
switch (this.blitMode) {
|
||||
case 1: // fill
|
||||
for (; row < maxHeight; row++) {
|
||||
for (col = this.blitXOffset; col < maxWidth; col++) {
|
||||
this.windowData[row][col] = (byte) this.blitXStartOrFill;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: // invert
|
||||
for (; row < maxHeight; row++) {
|
||||
for (col = this.blitXOffset; col < maxWidth; col++) {
|
||||
this.windowData[row][col] ^= 0x80;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: // shift
|
||||
int shiftX = this.blitXStartOrFill - this.blitXOffset;
|
||||
int shiftY = this.blitYStart - this.blitYOffset;
|
||||
for (; row < maxHeight; row++) {
|
||||
int srcRow = row + shiftY;
|
||||
if (srcRow >= 0 & srcRow < HEIGHT) {
|
||||
for (col = this.blitXOffset; col < maxWidth; col++) {
|
||||
int srcCol = col + shiftX;
|
||||
if (srcCol >= 0 && srcCol < WIDTH) {
|
||||
this.windowData[row][col] = this.windowData[srcRow][srcCol];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.blitMode = 0;
|
||||
|
||||
if (this.isCursorDirty) {
|
||||
this.isCursorDirty = false;
|
||||
this.updateCursor(this.cursorX, this.cursorY, this.cursorMode);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCursor(int cursorX, int cursorY, int cursorMode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a key code to the key buffer.
|
||||
*
|
||||
* @param key The key code
|
||||
*/
|
||||
public void onKey(byte key) {
|
||||
int nextPos = (this.keyBufferPos + 1) & 0x0f;
|
||||
if (nextPos != this.keyBufferStart) {
|
||||
this.keyBuffer[this.keyBufferPos] = key;
|
||||
this.keyBufferPos = nextPos;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeNbt(NbtCompound nbt) {
|
||||
nbt.putByteArray("keyBuffer", this.keyBuffer);
|
||||
for(int i = 0; i < windowData.length; i++){
|
||||
nbt.putByteArray("winData" + i, windowData[i]);
|
||||
}
|
||||
|
||||
nbt.putInt("acessRow", accessRow);
|
||||
nbt.putInt("cursorX", cursorX);
|
||||
nbt.putInt("cursorY", cursorY);
|
||||
nbt.putInt("cursorMode", cursorMode);
|
||||
nbt.putInt("keyBufferStart", keyBufferStart);
|
||||
nbt.putInt("keyBufferPos", keyBufferPos);
|
||||
nbt.putInt("blitMode", blitMode);
|
||||
nbt.putInt("blitXStartOrFill", blitXStartOrFill);
|
||||
nbt.putInt("blitYStart", blitYStart);
|
||||
nbt.putInt("blitXOffset", blitXOffset);
|
||||
nbt.putInt("blitYOffset", blitYOffset);
|
||||
nbt.putInt("blitWidth", blitWidth);
|
||||
nbt.putInt("blitHeight", blitHeight);
|
||||
|
||||
super.writeNbt(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNbt(NbtCompound nbt) {
|
||||
this.keyBuffer = nbt.getByteArray("keyBuffer");
|
||||
|
||||
for(int i = 0; i < windowData.length; i++){
|
||||
windowData[i] = nbt.getByteArray("winData" + i);
|
||||
}
|
||||
|
||||
accessRow = nbt.getInt("accessRow");
|
||||
cursorX = nbt.getInt("cursorX");
|
||||
cursorY = nbt.getInt("cursorY");
|
||||
cursorMode = nbt.getInt("cursorMode");
|
||||
keyBufferStart = nbt.getInt("keyBufferStart");
|
||||
keyBufferPos = nbt.getInt("keyBufferPos");
|
||||
blitMode = nbt.getInt("blitMode");
|
||||
blitXStartOrFill = nbt.getInt("blitXStartOrFill");
|
||||
blitYStart = nbt.getInt("blitYStart");
|
||||
blitXOffset = nbt.getInt("blitXOffset");
|
||||
blitYOffset = nbt.getInt("blitYOffset");
|
||||
blitWidth = nbt.getInt("blitWidth");
|
||||
blitHeight = nbt.getInt("blitHeight");
|
||||
|
||||
super.readNbt(nbt);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||
return BlockEntityUpdateS2CPacket.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound toInitialChunkDataNbt() {
|
||||
return createNbt();
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
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.MonitorEntity;
|
||||
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.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.BlockWithEntity;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MonitorBlock extends NetworkCarrier {
|
||||
public MonitorBlock(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BlockWithEntity> getCodec() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new MonitorEntity(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (!world.isClient) {
|
||||
if (player.getStackInHand(hand).getItem() == RedControl.SQUEAKY_HAMMER) {
|
||||
super.onUse(state,world,pos,player,hand,hit);
|
||||
MonitorEntity monitor = (MonitorEntity) world.getBlockEntity(pos);
|
||||
player.sendMessage(Text.literal(String.valueOf(monitor.getBus().hashCode())), false);
|
||||
String[] text = monitor.getText();
|
||||
for (String s : text) {
|
||||
player.sendMessage(Text.literal(s), false);
|
||||
}
|
||||
}
|
||||
PacketByteBuf byteBuf = PacketByteBufs.create();
|
||||
byteBuf.writeBlockPos(pos);
|
||||
ServerPlayNetworking.send((ServerPlayerEntity) player, RedControlNetworking.MONITOR_PACKET_ID,byteBuf);
|
||||
}
|
||||
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
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.BlockState
|
||||
import net.minecraft.block.BlockWithEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
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 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 {
|
||||
if (world.getBlockEntity(pos) !is TerminalEntity) return FAIL
|
||||
if (!world.isClient) {
|
||||
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 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) world?.updateListeners(pos, cachedState, cachedState, 3)
|
||||
|
||||
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)
|
||||
if (needsClientUpdate)
|
||||
getWorld()?.updateListeners(getPos(), cachedState, cachedState, 3)
|
||||
markDirty()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package net.brokenmoon.redcontrol.util
|
||||
|
||||
val Byte.unsigned: Int
|
||||
get() = this.toInt() and 0xFF
|
||||
|
||||
val Short.unsigned: Int
|
||||
get() = this.toInt() and 0xFFFF
|
||||
|
||||
val Int.unsigned: Long
|
||||
get() = this.toLong() and 0xFFFFFFFF
|
||||
|
||||
infix fun Int.pmod(i: Int): Int = (this % i).let { if (it < 0) it + i else it }
|
||||
infix fun Long.pmod(i: Int): Int = (this % i).let { if (it < 0) (it + i).toInt() else it.toInt() }
|
||||
infix fun Long.pmod(l: Long): Long = (this % l).let { if (it < 0) it + l else it }
|
||||
|
||||
infix fun Byte.pmod(i: Int): Byte = (this % i).let { if (it < 0) (it + i).toByte() else it.toByte() }
|
||||
|
||||
infix fun Byte.shr(i: Int): Byte = (this.toInt() shr i).toByte()
|
||||
infix fun Byte.ushr(i: Int): Byte = (this.unsigned ushr i).toByte()
|
||||
infix fun Byte.shl(i: Int): Byte = (this.toInt() shl i).toByte()
|
||||
|
||||
infix fun Short.shr(i: Int): Short = (this.toInt() shr i).toShort()
|
||||
infix fun Short.ushr(i: Int): Short = (this.unsigned ushr i).toShort()
|
||||
infix fun Short.shl(i: Int): Short = (this.toInt() shl i).toShort()
|
@ -0,0 +1,64 @@
|
||||
#version 330 core
|
||||
|
||||
#define SCREEN_WIDTH 80
|
||||
#define SCREEN_HEIGHT 50
|
||||
|
||||
#define BGCOLOR vec3(0.09, 0.07, 0)
|
||||
#define FGCOLOR vec3(0.78, 0.57, 0.01)
|
||||
|
||||
uniform usampler2D charset;
|
||||
uniform usampler2D screen;
|
||||
|
||||
in vec2 f_uv;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
float get_pixel(in ivec2 px) {
|
||||
// where is this character on the screen? (0,0) - (SCREEN_WIDTH,SCREEN_HEIGHT)
|
||||
ivec2 char = px / 8;
|
||||
|
||||
// which pixel of this character is this? (0,0)-(8,8)
|
||||
ivec2 chPixel = px % 8;
|
||||
|
||||
// which character is this? 0-255
|
||||
int chIndex = int(texelFetch(screen, char, 0).x);
|
||||
|
||||
// the bitmap of the currently drawing line of the character
|
||||
int lineData = int(texelFetch(charset, ivec2(chPixel.y, chIndex), 0).x);
|
||||
|
||||
return float((lineData >> (7 - chPixel.x)) & 1);
|
||||
}
|
||||
|
||||
float get_pixel_with_fx(in vec2 screenPos) {
|
||||
ivec2 px = ivec2(screenPos);
|
||||
vec2 partial = fract(screenPos);
|
||||
|
||||
float strength = get_pixel(px);
|
||||
|
||||
float x = 0;
|
||||
for (int dist = 1; dist < 6; dist++) {
|
||||
float f = get_pixel(px - ivec2(dist, 0)) * 0.3;
|
||||
if (f > 0) {
|
||||
x = f * pow(0.5, dist + partial.x - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
strength = min(1, strength + x);
|
||||
|
||||
if (px.y % 2 == 0) strength *= 0.9;
|
||||
strength *= mix(0.25, 1, sin(partial.y * 3.141592654));
|
||||
|
||||
strength *= dot(vec3(0, 0, 1), normalize(vec3(px - 0.5 * vec2(SCREEN_WIDTH * 8, SCREEN_HEIGHT * 8), 300)));
|
||||
|
||||
return strength;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 screenPos = vec2(f_uv.x * SCREEN_WIDTH * 8, f_uv.y * SCREEN_HEIGHT * 8);
|
||||
|
||||
float strength = get_pixel_with_fx(screenPos);
|
||||
|
||||
vec3 color = mix(BGCOLOR, FGCOLOR, strength);
|
||||
|
||||
fragColor = vec4(color, 1);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#version 330 core
|
||||
|
||||
in vec3 xyz;
|
||||
in vec2 uv;
|
||||
|
||||
out vec2 f_uv;
|
||||
|
||||
uniform mat4 mvp;
|
||||
|
||||
void main() {
|
||||
f_uv = uv;
|
||||
gl_Position = mvp * vec4(xyz, 1);
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue