From 5729218e22141096d61f85830019754375a0c1eb Mon Sep 17 00:00:00 2001 From: zyxkad Date: Mon, 22 Jul 2024 12:42:49 -0600 Subject: [PATCH 1/5] add ultimate monitor --- .../peripheral/UltimateMonitorPeripheral.java | 89 +++ .../terminal/UltimateNetworkedTerminal.java | 114 ++++ .../terminal/UltimateServerMonitor.java | 72 ++ .../blockentities/UltimateMonitorEntity.java | 632 ++++++++++++++++++ .../configuration/PeripheralsConfig.java | 7 + .../common/setup/APBlockEntityTypes.java | 1 + .../common/setup/APBlocks.java | 3 +- 7 files changed, 917 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java new file mode 100644 index 000000000..71261ee88 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java @@ -0,0 +1,89 @@ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral; + +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.core.apis.TableHelper; +import dan200.computercraft.shared.peripheral.monitor.MonitorPeripheral; +import de.srendi.advancedperipherals.common.addons.computercraft.owner.BlockEntityPeripheralOwner; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal.MonitorSide; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateServerMonitor; +import de.srendi.advancedperipherals.common.blocks.blockentities.UltimateMonitorEntity; + +import java.util.Map; +import java.util.HashMap; + +public class UltimateMonitorPeripheral extends MonitorPeripheral { + private final UltimateMonitorEntity monitor; + + public UltimateMonitorPeripheral(UltimateMonitorEntity monitor) { + super(monitor); + this.monitor = monitor; + } + + private UltimateServerMonitor getMonitor() throws LuaException { + UltimateServerMonitor monitor = this.monitor.getCachedServerMonitor(); + if (monitor == null) { + throw new LuaException("Monitor has been detached"); + } + return monitor; + } + + @Override + public UltimateNetworkedTerminal getTerminal() throws LuaException { + UltimateNetworkedTerminal term = getMonitor().getTerminal(); + if (term == null) { + throw new LuaException("Monitor has been detached"); + } + return term; + } + + @LuaFunction + public double getTransparency() { + return (double)(this.getTerminal().getTransparency()) / 0xff; + } + + @LuaFunction + public void setTransparency(double transparency) { + this.getTerminal().setTransparency((int)(transparency * 0xff)); + } + + @LuaFunction + public Map getSideColor(IArguments args) { + if (args.count() < 1) { + throw new LuaException("getSideColor need one argument"); + } + MonitorSide side = MonitorSide.fromString(args.getString(0)); + if (side == null) { + throw new LuaException(String.format("Invalid monitor side %s", side)); + } + int[] color = this.getTerminal().getSideColor(side); + int a = this.getTerminal().getSideTransparency(side); + Map obj = new HashMap<>(); + obj.put("r", color[0]); + obj.put("g", color[1]); + obj.put("b", color[2]); + obj.put("a", a); + return obj; + } + + @LuaFunction + public void setSideColor(IArguments args) { + if (args.count() < 2) { + throw new LuaException("setSideColor need two arguments"); + } + MonitorSide side = MonitorSide.fromString(args.getString(0)); + if (side == null) { + throw new LuaException(String.format("Invalid monitor side %s", side)); + } + if (!(args.get(1) instanceof Map obj)) { + throw new LuaException("The second argument should be an rgba table"); + } + int r = (int) TableHelper.getNumberField(obj, "r"); + int g = (int) TableHelper.getNumberField(obj, "g"); + int b = (int) TableHelper.getNumberField(obj, "b"); + int a = (int) TableHelper.getNumberField(obj, "a"); + this.getTerminal().setSideColor(side, new int[]{r, g, b}); + this.getTerminal().setSideTransparency(side, a); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java new file mode 100644 index 000000000..ff9b6f805 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java @@ -0,0 +1,114 @@ +package de.srendi.advancedperipherals.common.addons.computercraft.terminal; + +import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; +import net.minecraft.network.FriendlyByteBuf; + +import java.util.Arrays; + +public class UltimateNetworkedTerminal extends NetworkedTerminal { + private final int[] transparencies = new int[]{0xff, 0, 0, 0}; + private final int[][] sideColors = new int[3][3]; + + public UltimateNetworkedTerminal(int width, int height, int[] transparencies) { + this(width, height, transparencies, null); + } + + public UltimateNetworkedTerminal(int width, int height, int[] transparencies, Runnable changedCallback) { + super(width, height, true, changedCallback); + if (transparencies != null) { + System.arraycopy(transparencies, 0, this.transparencies, 0, 4); + } + } + + public int getTransparency() { + return this.transparencies[0]; + } + + public void setTransparency(int transparency) { + if (this.transparencies[0] == transparency) { + return; + } + this.transparencies[0] = transparency; + setChanged(); + } + + public int getSideTransparency(MonitorSide side) { + return this.transparencies[side.getIndex()]; + } + + public void setSideTransparency(MonitorSide side, int transparency) { + if (this.transparencies[side.getIndex()] == transparency) { + return; + } + this.transparencies[side.getIndex()] = transparency; + setChanged(); + } + + public int[] getSideColor(MonitorSide side) { + return this.sideColors[side.getIndex() - 1]; + } + + public void setSideColor(MonitorSide side, int[] color) { + if (color.length != 3) { + throw new IllegalArgumentException("color.length must be 3"); + } + if (Arrays.equals(this.sideColors[side.getIndex() - 1], color)) { + return; + } + this.sideColors[side.getIndex() - 1] = color; + setChanged(); + } + + @Override + public synchronized void reset() { + super.reset(); + this.transparencies[0] = 0xff; + this.transparencies[1] = 0; + this.transparencies[2] = 0; + this.transparencies[3] = 0; + } + + @Override + public synchronized void write(FriendlyByteBuf buffer) { + super.write(buffer); + buffer.writeByte(this.transparencies[0]); + buffer.writeByte(this.transparencies[1]); + buffer.writeByte(this.transparencies[2]); + buffer.writeByte(this.transparencies[3]); + } + + @Override + public synchronized void read(FriendlyByteBuf buffer) { + super.read(buffer); + this.transparencies[0] = buffer.readByte(); + this.transparencies[1] = buffer.readByte(); + this.transparencies[2] = buffer.readByte(); + this.transparencies[3] = buffer.readByte(); + } + + public static enum MonitorSide { + PANEL(0), + FRONT(1), + LEFT(2), + TOP(3); + + private final int index; + + private MonitorSide(int index){ + this.index = index; + } + + public int getIndex() { + return index; + } + + public static MonitorSide fromString(String s) { + return switch (s.toLowerCase()) { + case "front", "back", "z" -> FRONT; + case "left", "right", "x" -> LEFT; + case "top", "bottom", "y" -> TOP; + default -> null; + }; + } + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java new file mode 100644 index 000000000..cf9f8574b --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java @@ -0,0 +1,72 @@ +// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. +// +// SPDX-License-Identifier: LicenseRef-CCPL + +package de.srendi.advancedperipherals.common.addons.computercraft.terminal; + +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.peripheral.monitor.ServerMonitor; +import dan200.computercraft.shared.util.TickScheduler; +import de.srendi.advancedperipherals.common.blocks.blockentities.UltimateMonitorEntity; + +import org.jetbrains.annotations.Nullable; +import java.util.concurrent.atomic.AtomicBoolean; + +public class UltimateServerMonitor extends ServerMonitor { + private final UltimateMonitorEntity origin; + + private @Nullable UltimateNetworkedTerminal terminal; + private final AtomicBoolean resized = new AtomicBoolean(false); + + public UltimateServerMonitor(UltimateMonitorEntity origin) { + super(true, origin); + } + + @Override + synchronized void rebuild() { + Terminal oldTerm = getTerminal(); + var oldWidth = oldTerm == null ? -1 : oldTerm.getWidth(); + var oldHeight = oldTerm == null ? -1 : oldTerm.getHeight(); + + var textScale = this.getTextScale() * 0.5; + var termWidth = (int) Math.max( + (double) Math.round((origin.getWidth() - 2.0 * (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN)) / (textScale * 6.0 * UltimateMonitorEntity.RENDER_PIXEL_SCALE)), + 1.0 + ); + var termHeight = (int) Math.max( + (double) Math.round((origin.getHeight() - 2.0 * (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN)) / (textScale * 9.0 * UltimateMonitorEntity.RENDER_PIXEL_SCALE)), + 1.0 + ); + + if (terminal == null) { + terminal = new UltimateNetworkedTerminal(termWidth, termHeight, null, this::markChanged); + markChanged(); + } else { + terminal.resize(termWidth, termHeight); + } + + if (oldWidth != termWidth || oldHeight != termHeight) { + terminal.clear(); + resized.set(true); + markChanged(); + } + } + + @Override + synchronized void reset() { + if (terminal == null) return; + terminal = null; + markChanged(); + } + + @Override + boolean pollResized() { + return resized.getAndSet(false); + } + + @Nullable + @Override + public UltimateNetworkedTerminal getTerminal() { + return terminal; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java b/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java new file mode 100644 index 000000000..c9f66a154 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java @@ -0,0 +1,632 @@ +// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. +// +// SPDX-License-Identifier: LicenseRef-CCPL + +package de.srendi.advancedperipherals.common.blocks.blockentities; + +import com.google.common.annotations.VisibleForTesting; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.UltimateMonitorPeripheral; +import de.srendi.advancedperipherals.common.blocks.base.PeripheralBlockEntity; +import de.srendi.advancedperipherals.common.setup.APBlockEntityTypes; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateServerMonitor; +import dan200.computercraft.annotations.ForgeOverride; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; +import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity; +import dan200.computercraft.shared.computer.terminal.TerminalState; +import dan200.computercraft.shared.config.Config; +import dan200.computercraft.shared.util.BlockEntityHelpers; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class UltimateMonitorEntity extends MonitorBlockEntity { + private static final Logger LOG = LoggerFactory.getLogger(UltimateMonitorEntity.class); + + private static final String NBT_X = "XIndex"; + private static final String NBT_Y = "YIndex"; + private static final String NBT_WIDTH = "Width"; + private static final String NBT_HEIGHT = "Height"; + + private @Nullable UltimateServerMonitor serverMonitor; + + /** + * The monitor's state on the client. This is defined iff we're the origin monitor + * ({@code xIndex == 0 && yIndex == 0}). + */ + private @Nullable ClientMonitor clientMonitor; + + private @Nullable MonitorPeripheral peripheral; + private final Set computers = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + private boolean needsUpdate = false; + private boolean needsValidating = false; + + private int width = 1; + private int height = 1; + private int xIndex = 0; + private int yIndex = 0; + + private @Nullable BlockPos bbPos; + private @Nullable BlockState bbState; + private int bbX, bbY, bbWidth, bbHeight; + private @Nullable AABB boundingBox; + + public UltimateMonitorEntity(BlockPos pos, BlockState state) { + super(APBlockEntityTypes.ULTIMATE_MONITOR.get(), pos, state); + } + + @Override + public void clearRemoved() { + super.clearRemoved(); + needsValidating = true; + } + + @Override + void destroy() { + // TODO: Call this before using the block + if (!getLevel().isClientSide) contractNeighbours(); + } + + @Override + public void setRemoved() { + super.setRemoved(); + if (clientMonitor != null) clientMonitor.destroy(); + } + + @Override + public void saveAdditional(CompoundTag tag) { + super.saveAdditional(tag); + tag.putInt(NBT_X, xIndex); + tag.putInt(NBT_Y, yIndex); + tag.putInt(NBT_WIDTH, width); + tag.putInt(NBT_HEIGHT, height); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + + var oldXIndex = xIndex; + var oldYIndex = yIndex; + + xIndex = nbt.getInt(NBT_X); + yIndex = nbt.getInt(NBT_Y); + width = nbt.getInt(NBT_WIDTH); + height = nbt.getInt(NBT_HEIGHT); + + if (level != null && level.isClientSide) onClientLoad(oldXIndex, oldYIndex); + } + + @Override + void blockTick() { + if (needsValidating) { + needsValidating = false; + validate(); + } + + if (needsUpdate) { + needsUpdate = false; + expand(); + } + + if (xIndex != 0 || yIndex != 0 || serverMonitor == null) return; + + if (serverMonitor.pollResized()) eachComputer(c -> c.queueEvent("monitor_resize", c.getAttachmentName())); + if (serverMonitor.pollTerminalChanged()) MonitorWatcher.enqueue(this); + } + + @Override + @Nullable + @VisibleForTesting + public UltimateServerMonitor getCachedServerMonitor() { + return serverMonitor; + } + + @Nullable + private UltimateServerMonitor getServerMonitor() { + if (serverMonitor != null) return serverMonitor; + + var origin = getOrigin(); + if (origin == null) return null; + + return serverMonitor = origin.serverMonitor; + } + + @Nullable + private UltimateServerMonitor createServerMonitor() { + if (serverMonitor != null) return serverMonitor; + + if (xIndex == 0 && yIndex == 0) { + // If we're the origin, set up the new monitor + serverMonitor = new UltimateServerMonitor(this); + + // And propagate it to child monitors + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + var monitor = getLoadedMonitor(x, y).getMonitor(); + if (monitor != null) monitor.serverMonitor = serverMonitor; + } + } + + return serverMonitor; + } else { + // Otherwise fetch the origin and attempt to get its monitor + // Note this may load chunks, but we don't really have a choice here. + var te = getLevel().getBlockEntity(toWorldPos(0, 0)); + if (!(te instanceof UltimateMonitorEntity monitor)) return null; + + return serverMonitor = monitor.createServerMonitor(); + } + } + + private void createServerTerminal() { + var monitor = createServerMonitor(); + if (monitor != null && monitor.getTerminal() == null) monitor.rebuild(); + } + + @Override + @Nullable + public ClientMonitor getOriginClientMonitor() { + if (clientMonitor != null) return clientMonitor; + + var origin = getOrigin(); + return origin == null ? null : origin.clientMonitor; + } + + // Networking stuff + + @Override + public final ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public final CompoundTag getUpdateTag() { + var nbt = super.getUpdateTag(); + nbt.putInt(NBT_X, xIndex); + nbt.putInt(NBT_Y, yIndex); + nbt.putInt(NBT_WIDTH, width); + nbt.putInt(NBT_HEIGHT, height); + return nbt; + } + + private void onClientLoad(int oldXIndex, int oldYIndex) { + if ((oldXIndex != xIndex || oldYIndex != yIndex) && clientMonitor != null) { + // If our index has changed, and we were the origin, then destroy the current monitor. + clientMonitor.destroy(); + clientMonitor = null; + } + + // If we're the origin terminal then create it. + if (xIndex == 0 && yIndex == 0 && clientMonitor == null) clientMonitor = new ClientMonitor(this); + } + + @Override + public final void read(@Nullable TerminalState state) { + if (xIndex != 0 || yIndex != 0) { + LOG.warn("Receiving monitor state for non-origin terminal at {}", getBlockPos()); + return; + } + + if (clientMonitor == null) clientMonitor = new ClientMonitor(this); + clientMonitor.read(state); + } + + // Sizing and placement stuff + + private void updateBlockState() { + getLevel().setBlock(getBlockPos(), getBlockState() + .setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections( + yIndex < height - 1, yIndex > 0, + xIndex > 0, xIndex < width - 1)), 2); + } + + // region Sizing and placement stuff + @Override + public Direction getDirection() { + // Ensure we're actually a monitor block. This _should_ always be the case, but sometimes there's + // fun problems with the block being missing on the client. + var state = getBlockState(); + return state.hasProperty(MonitorBlock.FACING) ? state.getValue(MonitorBlock.FACING) : Direction.NORTH; + } + + @Override + public Direction getOrientation() { + var state = getBlockState(); + return state.hasProperty(MonitorBlock.ORIENTATION) ? state.getValue(MonitorBlock.ORIENTATION) : Direction.NORTH; + } + + @Override + public Direction getFront() { + var orientation = getOrientation(); + return orientation == Direction.NORTH ? getDirection() : orientation; + } + + @Override + public Direction getRight() { + return getDirection().getCounterClockWise(); + } + + @Override + public Direction getDown() { + var orientation = getOrientation(); + if (orientation == Direction.NORTH) return Direction.UP; + return orientation == Direction.DOWN ? getDirection() : getDirection().getOpposite(); + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getXIndex() { + return xIndex; + } + + @Override + public int getYIndex() { + return yIndex; + } + + @Override + boolean isCompatible(MonitorEntity other) { + return other instanceof UltimateMonitorEntity && getOrientation() == other.getOrientation() && getDirection() == other.getDirection(); + } + + /** + * Get a tile within the current monitor only if it is loaded and compatible. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The located monitor + */ + private MonitorState getLoadedMonitor(int x, int y) { + if (x == xIndex && y == yIndex) return MonitorState.present(this); + var pos = toWorldPos(x, y); + + var world = getLevel(); + if (world == null || !world.isLoaded(pos)) return MonitorState.UNLOADED; + + var tile = world.getBlockEntity(pos); + if (!(tile instanceof UltimateMonitorEntity monitor)) return MonitorState.MISSING; + + return isCompatible(monitor) ? MonitorState.present(monitor) : MonitorState.MISSING; + } + + private @Nullable UltimateMonitorEntity getOrigin() { + return getLoadedMonitor(0, 0).getMonitor(); + } + + /** + * Convert monitor coordinates to world coordinates. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The monitor's position. + */ + @Override + BlockPos toWorldPos(int x, int y) { + if (xIndex == x && yIndex == y) return getBlockPos(); + return getBlockPos().relative(getRight(), -xIndex + x).relative(getDown(), -yIndex + y); + } + + @Override + void resize(int width, int height) { + // If we're not already the origin then we'll need to generate a new terminal. + if (xIndex != 0 || yIndex != 0) serverMonitor = null; + + xIndex = 0; + yIndex = 0; + this.width = width; + this.height = height; + + // Determine if we actually need a monitor. In order to do this, simply check if + // any component monitor been wrapped as a peripheral. Whilst this flag may be + // out of date, + var needsTerminal = false; + terminalCheck: + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + var monitor = getLoadedMonitor(x, y).getMonitor(); + if (monitor != null && monitor.peripheral != null) { + needsTerminal = true; + break terminalCheck; + } + } + } + + // Either delete the current monitor or sync a new one. + if (needsTerminal) { + if (serverMonitor == null) serverMonitor = new UltimateServerMonitor(this); + + // Update the terminal's width and height and rebuild it. This ensures the monitor + // is consistent when syncing it to other monitors. + serverMonitor.rebuild(); + } else { + // Remove the terminal from the serverMonitor, but keep it around - this ensures that we sync + // the (now blank) monitor to the client. + if (serverMonitor != null) serverMonitor.reset(); + } + + // Update the other monitors, setting coordinates, dimensions and the server terminal + var pos = getBlockPos(); + Direction down = getDown(), right = getRight(); + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + var other = getLevel().getBlockEntity(pos.relative(right, x).relative(down, y)); + if (!(other instanceof UltimateMonitorEntity monitor) || !isCompatible(monitor)) continue; + + monitor.xIndex = x; + monitor.yIndex = y; + monitor.width = width; + monitor.height = height; + monitor.serverMonitor = serverMonitor; + monitor.needsUpdate = monitor.needsValidating = false; + monitor.updateBlockState(); + BlockEntityHelpers.updateBlock(monitor); + } + } + + assertInvariant(); + } + + @Override + void updateNeighborsDeferred() { + needsUpdate = true; + } + + @Override + void expand() { + var monitor = getOrigin(); + if (monitor != null && monitor.xIndex == 0 && monitor.yIndex == 0) new Expander(monitor).expand(); + } + + private void contractNeighbours() { + if (width == 1 && height == 1) return; + + var pos = getBlockPos(); + Direction down = getDown(), right = getRight(); + var origin = toWorldPos(0, 0); + + UltimateMonitorEntity toLeft = null, toAbove = null, toRight = null, toBelow = null; + if (xIndex > 0) toLeft = tryResizeAt(pos.relative(right, -xIndex), xIndex, 1); + if (yIndex > 0) toAbove = tryResizeAt(origin, width, yIndex); + if (xIndex < width - 1) toRight = tryResizeAt(pos.relative(right, 1), width - xIndex - 1, 1); + if (yIndex < height - 1) { + toBelow = tryResizeAt(origin.relative(down, yIndex + 1), width, height - yIndex - 1); + } + + if (toLeft != null) toLeft.expand(); + if (toAbove != null) toAbove.expand(); + if (toRight != null) toRight.expand(); + if (toBelow != null) toBelow.expand(); + } + + @Nullable + private UltimateMonitorEntity tryResizeAt(BlockPos pos, int width, int height) { + var tile = getLevel().getBlockEntity(pos); + if (tile instanceof UltimateMonitorEntity monitor && isCompatible(monitor)) { + monitor.resize(width, height); + return monitor; + } + + return null; + } + + + private boolean checkMonitorAt(int xIndex, int yIndex) { + var state = getLoadedMonitor(xIndex, yIndex); + if (state.isMissing()) return false; + + var monitor = state.getMonitor(); + if (monitor == null) return true; + + return monitor.xIndex == xIndex && monitor.yIndex == yIndex && monitor.width == width && monitor.height == height; + } + + private void validate() { + if (xIndex == 0 && yIndex == 0 && width == 1 && height == 1) return; + + if (xIndex >= 0 && xIndex <= width && width > 0 && width <= Config.monitorWidth && + yIndex >= 0 && yIndex <= height && height > 0 && height <= Config.monitorHeight && + checkMonitorAt(0, 0) && checkMonitorAt(0, height - 1) && + checkMonitorAt(width - 1, 0) && checkMonitorAt(width - 1, height - 1)) { + return; + } + + // Something in our monitor is invalid. For now, let's just reset ourselves and then try to integrate ourselves + // later. + LOG.warn("Monitor is malformed, resetting to 1x1."); + resize(1, 1); + needsUpdate = true; + } + // endregion + + @Override + void monitorTouched(float xPos, float yPos, float zPos) { + this.monitorTouched(xPos, yPos, zPos, null); + } + + void monitorTouched(float xPos, float yPos, float zPos, @Nullable Player player) { + var pair = XYPair + .of(xPos, yPos, zPos, getDirection(), getOrientation()) + .add(xIndex, height - yIndex - 1); + + if (pair.x() > width - RENDER_BORDER || pair.y() > height - RENDER_BORDER || pair.x() < RENDER_BORDER || pair.y() < RENDER_BORDER) { + return; + } + + var serverTerminal = getServerMonitor(); + if (serverTerminal == null) return; + + Terminal originTerminal = serverTerminal.getTerminal(); + if (originTerminal == null) return; + + var xCharWidth = (width - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getWidth(); + var yCharHeight = (height - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getHeight(); + + var xCharPos = (int) Math.min(originTerminal.getWidth(), Math.max((pair.x() - RENDER_BORDER - RENDER_MARGIN) / xCharWidth + 1.0, 1.0)); + var yCharPos = (int) Math.min(originTerminal.getHeight(), Math.max((pair.y() - RENDER_BORDER - RENDER_MARGIN) / yCharHeight + 1.0, 1.0)); + + eachComputer(c -> c.queueEvent("monitor_touch", c.getAttachmentName(), xCharPos, yCharPos, player == null ? null : player.getName())); + } + + private void eachComputer(Consumer fun) { + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + var monitor = getLoadedMonitor(x, y).getMonitor(); + if (monitor == null) continue; + + for (var computer : monitor.computers) fun.accept(computer); + } + } + } + + @Override + public IPeripheral peripheral() { + createServerTerminal(); + var peripheral = this.peripheral != null ? this.peripheral : (this.peripheral = new MonitorPeripheral(this)); + assertInvariant(); + return peripheral; + } + + @Override + void addComputer(IComputerAccess computer) { + computers.add(computer); + } + + @Override + void removeComputer(IComputerAccess computer) { + computers.remove(computer); + } + + @Override + @ForgeOverride + public AABB getRenderBoundingBox() { + // We attempt to cache the bounding box to save having to do property lookups (and allocations!) on every frame. + // Unfortunately the AABB does depend on quite a lot of state, so we need to add a bunch of extra fields - + // ideally these'd be a single object, but I don't think worth doing until Java has value types. + if (boundingBox != null && getBlockState().equals(bbState) && getBlockPos().equals(bbPos) && + xIndex == bbX && yIndex == bbY && width == bbWidth && height == bbHeight) { + return boundingBox; + } + + bbState = getBlockState(); + bbPos = getBlockPos(); + bbX = xIndex; + bbY = yIndex; + bbWidth = width; + bbHeight = height; + + var startPos = toWorldPos(0, 0); + var endPos = toWorldPos(width, height); + return boundingBox = new AABB( + Math.min(startPos.getX(), endPos.getX()), + Math.min(startPos.getY(), endPos.getY()), + Math.min(startPos.getZ(), endPos.getZ()), + Math.max(startPos.getX(), endPos.getX()) + 1, + Math.max(startPos.getY(), endPos.getY()) + 1, + Math.max(startPos.getZ(), endPos.getZ()) + 1 + ); + } + + /** + * Assert all {@linkplain #checkInvariants() monitor invariants} hold. + */ + private void assertInvariant() { + assert checkInvariants() : "Monitor invariants failed. See logs."; + } + + /** + * Check various invariants about this monitor multiblock. This is only called when assertions are enabled, so + * will be skipped outside of tests. + * + * @return Whether all invariants passed. + */ + private boolean checkInvariants() { + LOG.debug("Checking monitor invariants at {}", getBlockPos()); + + var okay = true; + + if (width <= 0 || height <= 0) { + okay = false; + LOG.error("Monitor {} has non-positive of {}x{}", getBlockPos(), width, height); + } + + var hasPeripheral = false; + var origin = getOrigin(); + var serverMonitor = origin != null ? origin.serverMonitor : this.serverMonitor; + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + var monitor = getLoadedMonitor(x, y).getMonitor(); + if (monitor == null) continue; + + hasPeripheral |= monitor.peripheral != null; + + if (monitor.serverMonitor != null && monitor.serverMonitor != serverMonitor) { + okay = false; + LOG.error( + "Monitor {} expected to be have serverMonitor={}, but was {}", + monitor.getBlockPos(), serverMonitor, monitor.serverMonitor + ); + } + + if (monitor.xIndex != x || monitor.yIndex != y) { + okay = false; + LOG.error( + "Monitor {} expected to be at {},{}, but believes it is {},{}", + monitor.getBlockPos(), x, y, monitor.xIndex, monitor.yIndex + ); + } + + if (monitor.width != width || monitor.height != height) { + okay = false; + LOG.error( + "Monitor {} expected to be size {},{}, but believes it is {},{}", + monitor.getBlockPos(), width, height, monitor.width, monitor.height + ); + } + + var expectedState = getBlockState().setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections( + y < height - 1, y > 0, x > 0, x < width - 1 + )); + if (monitor.getBlockState() != expectedState) { + okay = false; + LOG.error( + "Monitor {} expected to have state {}, but has state {}", + monitor.getBlockState(), expectedState, monitor.getBlockState() + ); + } + } + } + + if (hasPeripheral != (serverMonitor != null && serverMonitor.getTerminal() != null)) { + okay = false; + LOG.error( + "Peripheral is {}, but serverMonitor={} and serverMonitor.terminal={}", + hasPeripheral, serverMonitor, serverMonitor == null ? null : serverMonitor.getTerminal() + ); + } + + return okay; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java b/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java index 8ed5cd8ba..79ec89f4a 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java +++ b/src/main/java/de/srendi/advancedperipherals/common/configuration/PeripheralsConfig.java @@ -107,6 +107,9 @@ public class PeripheralsConfig implements IAPConfig { public final ForgeConfigSpec.BooleanValue enableSaddleTurtle; public final ForgeConfigSpec.BooleanValue allowSaddleTurtleCapturePlayer; + // Ultimate monitor + public final ForgeConfigSpec.BooleanValue enableUltimateMonitor; + private static final List chatBoxDefaultBannedCommands = Arrays.asList( "/execute", "/op", @@ -242,6 +245,10 @@ public PeripheralsConfig() { enableSaddleTurtle = builder.comment("Enable saddle turtle").define("enableSaddleTurtle", true); allowSaddleTurtleCapturePlayer = builder.comment("Allow saddle turtle to capture player").define("allowSaddleTurtleCapturePlayer", true); + + pop("Ultimate_Monitor", builder); + + enableUltimateMonitor = builder.comment("Enable ultimate monitor").define("enableUltimateMonitor", true); pop("Operations", builder); diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java index ab760488c..f2c00760f 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java @@ -26,5 +26,6 @@ protected static void register() { public static final RegistryObject> COLONY_INTEGRATOR = APRegistration.TILE_ENTITIES.register("colony_integrator", () -> new BlockEntityType<>(ColonyIntegratorEntity::new, Sets.newHashSet(APBlocks.COLONY_INTEGRATOR.get()), null)); public static final RegistryObject> NBT_STORAGE = APRegistration.TILE_ENTITIES.register("nbt_storage", () -> new BlockEntityType<>(NBTStorageEntity::new, Sets.newHashSet(APBlocks.NBT_STORAGE.get()), null)); public static final RegistryObject> DISTANCE_DETECTOR = APRegistration.TILE_ENTITIES.register("distance_detector", () -> new BlockEntityType<>(DistanceDetectorEntity::new, Sets.newHashSet(APBlocks.DISTANCE_DETECTOR.get()), null)); + public static final RegistryObject> ULTIMATE_MONITOR = APRegistration.TILE_ENTITIES.register("ultimate_monitor", () -> new BlockEntityType(UltimateMonitorEntity::new, Sets.newHashSet(APBlocks.ULTIMATE_MONITOR.get()), null)); } diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java index aab4cdd0b..842e6dd15 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java @@ -39,7 +39,8 @@ protected static void register() { public static final RegistryObject GEO_SCANNER = register("geo_scanner", () -> new APBlockEntityBlock<>(APBlockEntityTypes.GEO_SCANNER, false), () -> new APBlockItem(APBlocks.GEO_SCANNER.get(), APConfig.PERIPHERALS_CONFIG.enableGeoScanner)); public static final RegistryObject COLONY_INTEGRATOR = register("colony_integrator", () -> new APBlockEntityBlock<>(APBlockEntityTypes.COLONY_INTEGRATOR, false), () -> new APBlockItem(APBlocks.COLONY_INTEGRATOR.get(), APConfig.PERIPHERALS_CONFIG.enableColonyIntegrator)); public static final RegistryObject NBT_STORAGE = register("nbt_storage", () -> new APBlockEntityBlock<>(APBlockEntityTypes.NBT_STORAGE, false), () -> new APBlockItem(APBlocks.NBT_STORAGE.get(), APConfig.PERIPHERALS_CONFIG.enableNBTStorage)); - public static final RegistryObject DISTANCE_DETECTOR = register("distance_detector", () -> new APBlockEntityBlock<>(APBlockEntityTypes.DISTANCE_DETECTOR, BlockBehaviour.Properties.of(Material.METAL).noOcclusion(), true), () -> new APBlockItem(APBlocks.DISTANCE_DETECTOR.get(), APConfig.PERIPHERALS_CONFIG.enableNBTStorage)); + public static final RegistryObject DISTANCE_DETECTOR = register("distance_detector", () -> new APBlockEntityBlock<>(APBlockEntityTypes.DISTANCE_DETECTOR, BlockBehaviour.Properties.of(Material.METAL).noOcclusion(), true), () -> new APBlockItem(APBlocks.DISTANCE_DETECTOR.get(), APConfig.PERIPHERALS_CONFIG.enableDistanceDetector)); + public static final RegistryObject ULTIMATE_MONITOR = register("ultimate_monitor", () -> new APBlockEntityBlock<>(APBlockEntityTypes.ULTIMATE_MONITOR, true), () -> new APBlockItem(APBlocks.ULTIMATE_MONITOR.get(), APConfig.PERIPHERALS_CONFIG.enableUltimateMonitor)); private static RegistryObject registerNoItem(String name, Supplier block) { return APRegistration.BLOCKS.register(name, block); From 5c87dd2949bf08b15e556cc50585f203c4c09bce Mon Sep 17 00:00:00 2001 From: zyxkad Date: Mon, 22 Jul 2024 15:39:10 -0600 Subject: [PATCH 2/5] add ultimate monitor and make it work only can load and have events so far --- .../client/ClientRegistry.java | 2 + .../client/renderer/RenderTypes.java | 104 +++ .../renderer/UltimateMonitorRenderer.java | 283 ++++++++ .../peripheral/UltimateMonitorPeripheral.java | 89 --- .../monitor/UltimateBlockMonitor.java | 117 ++++ .../monitor/UltimateClientMonitor.java | 201 ++++++ .../peripheral/monitor/UltimateExpander.java | 111 +++ .../monitor/UltimateMonitorEntity.java | 649 ++++++++++++++++++ .../monitor/UltimateMonitorPeripheral.java | 161 +++++ .../monitor/UltimateMonitorState.java | 52 ++ .../monitor/UltimateMonitorWatcher.java | 106 +++ .../monitor/UltimateServerMonitor.java | 97 +++ .../terminal/UltimateNetworkedTerminal.java | 9 +- .../terminal/UltimateServerMonitor.java | 72 -- .../blockentities/UltimateMonitorEntity.java | 632 ----------------- .../common/data/BlockTagsProvider.java | 3 + .../common/setup/APBlockEntityTypes.java | 1 + .../common/setup/APBlocks.java | 3 +- 18 files changed, 1892 insertions(+), 800 deletions(-) create mode 100644 src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java create mode 100644 src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java delete mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateExpander.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorState.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java delete mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java delete mode 100644 src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java diff --git a/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java b/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java index 9eaf6025f..f9d49d6e0 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java +++ b/src/main/java/de/srendi/advancedperipherals/client/ClientRegistry.java @@ -4,6 +4,7 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import de.srendi.advancedperipherals.AdvancedPeripherals; import de.srendi.advancedperipherals.client.renderer.DistanceDetectorRenderer; +import de.srendi.advancedperipherals.client.renderer.UltimateMonitorRenderer; import de.srendi.advancedperipherals.client.screens.InventoryManagerScreen; import de.srendi.advancedperipherals.client.screens.SaddleTurtleScreen; import de.srendi.advancedperipherals.client.screens.SmartGlassesScreen; @@ -66,6 +67,7 @@ public static void registeringKeymappings(RegisterKeyMappingsEvent event) { @SubscribeEvent public static void registeringRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerBlockEntityRenderer(APBlockEntityTypes.DISTANCE_DETECTOR.get(), DistanceDetectorRenderer::new); + event.registerBlockEntityRenderer(APBlockEntityTypes.ULTIMATE_MONITOR.get(), UltimateMonitorRenderer::new); } @SubscribeEvent diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java new file mode 100644 index 000000000..940950cdb --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java @@ -0,0 +1,104 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.client.renderer; + +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.render.MonitorTextureBufferShader; +import dan200.computercraft.client.render.text.FixedWidthFontRenderer; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RegisterShadersEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import javax.annotation.Nonnull; +import java.io.IOException; + +@Mod.EventBusSubscriber( modid = AdvancedPeripherals.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD ) +public class RenderTypes +{ + public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20); + + private static MonitorTextureBufferShader monitorTboShader; + + /** + * Renders a fullbright terminal. + */ + public static final RenderType TERMINAL = RenderType.text( FixedWidthFontRenderer.FONT ); + + /** + * Renders a monitor with the TBO shader. + * + * @see MonitorTextureBufferShader + */ + public static final RenderType MONITOR_TBO = Types.MONITOR_TBO; + + /** + * A variant of {@link #TERMINAL} which uses the lightmap rather than rendering fullbright. + */ + public static final RenderType PRINTOUT_TEXT = RenderType.text( FixedWidthFontRenderer.FONT ); + + /** + * Printout's background texture. {@link RenderType#text(ResourceLocation)} is a little questionable, but + * it is what maps use, so should behave the same as vanilla in both item frames and in-hand. + */ + public static final RenderType PRINTOUT_BACKGROUND = RenderType.text( new ResourceLocation( "computercraft", "textures/gui/printout.png" ) ); + + @Nonnull + static MonitorTextureBufferShader getMonitorTextureBufferShader() + { + if( monitorTboShader == null ) throw new NullPointerException( "MonitorTboShader has not been registered" ); + return monitorTboShader; + } + + @Nonnull + static ShaderInstance getTerminalShader() + { + return GameRenderer.getRendertypeTextShader(); + } + + @SubscribeEvent + public static void registerShaders( RegisterShadersEvent event ) throws IOException + { + event.registerShader( + new MonitorTextureBufferShader( + event.getResourceManager(), + new ResourceLocation( ComputerCraft.MOD_ID, "monitor_tbo" ), + MONITOR_TBO.format() + ), + x -> monitorTboShader = (MonitorTextureBufferShader) x + ); + } + + private static final class Types extends RenderStateShard + { + private static final RenderStateShard.TextureStateShard TERM_FONT_TEXTURE = new TextureStateShard( + FixedWidthFontRenderer.FONT, + false, false // blur, minimap + ); + + static final RenderType MONITOR_TBO = RenderType.create( + "monitor_tbo", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.TRIANGLE_STRIP, 128, + false, false, // useDelegate, needsSorting + RenderType.CompositeState.builder() + .setTextureState( TERM_FONT_TEXTURE ) + .setShaderState( new ShaderStateShard( RenderTypes::getMonitorTextureBufferShader ) ) + .createCompositeState( false ) + ); + + private Types( String name, Runnable setup, Runnable destroy ) + { + super( name, setup, destroy ); + } + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java new file mode 100644 index 000000000..44aa29230 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java @@ -0,0 +1,283 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.client.renderer; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.MemoryTracker; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import com.mojang.math.Matrix3f; +import com.mojang.math.Matrix4f; +import com.mojang.math.Vector3f; +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.FrameInfo; +import dan200.computercraft.client.render.MonitorTextureBufferShader; +import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; +import dan200.computercraft.client.render.text.FixedWidthFontRenderer; +import dan200.computercraft.client.util.DirectBuffers; +import dan200.computercraft.client.util.DirectVertexBuffer; +import dan200.computercraft.shared.integration.ShaderMod; +import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; +import dan200.computercraft.shared.util.DirectionUtil; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateClientMonitor; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateMonitorEntity; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; +import net.minecraft.Util; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL31; + +import javax.annotation.Nonnull; +import java.nio.ByteBuffer; +import java.util.function.Consumer; + +import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; +import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; + +public class UltimateMonitorRenderer implements BlockEntityRenderer +{ + + static final int TEXTURE_INDEX = GL13.GL_TEXTURE3; // export from dan200.computercraft.client.render.MonitorTextureBufferShader + + /** + * {@link UltimateMonitorEntity#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between + * the monitor frame and contents. + */ + private static final float MARGIN = (float) (UltimateMonitorEntity.RENDER_MARGIN * 1.1); + + private static final Matrix3f IDENTITY_NORMAL = Util.make( new Matrix3f(), Matrix3f::setIdentity ); + + private static ByteBuffer backingBuffer; + + public UltimateMonitorRenderer( BlockEntityRendererProvider.Context context ) + { + } + + @Override + public void render( @Nonnull UltimateMonitorEntity monitor, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource bufferSource, int lightmapCoord, int overlayLight ) + { + // Render from the origin monitor + UltimateClientMonitor originTerminal = monitor.getClientMonitor(); + + if( originTerminal == null ) return; + UltimateMonitorEntity origin = originTerminal.getOrigin(); + BlockPos monitorPos = monitor.getBlockPos(); + + // Ensure each monitor terminal is rendered only once. We allow rendering a specific tile + // multiple times in a single frame to ensure compatibility with shaders which may run a + // pass multiple times. + long renderFrame = FrameInfo.getRenderFrame(); + if( originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals( originTerminal.lastRenderPos ) ) + { + return; + } + + originTerminal.lastRenderFrame = renderFrame; + originTerminal.lastRenderPos = monitorPos; + + BlockPos originPos = origin.getBlockPos(); + + // Determine orientation + Direction dir = origin.getDirection(); + Direction front = origin.getFront(); + float yaw = dir.toYRot(); + float pitch = DirectionUtil.toPitchAngle( front ); + + // Setup initial transform + transform.pushPose(); + transform.translate( + originPos.getX() - monitorPos.getX() + 0.5, + originPos.getY() - monitorPos.getY() + 0.5, + originPos.getZ() - monitorPos.getZ() + 0.5 + ); + + transform.mulPose( Vector3f.YN.rotationDegrees( yaw ) ); + transform.mulPose( Vector3f.XP.rotationDegrees( pitch ) ); + transform.translate( + -0.5 + UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN, + origin.getHeight() - 0.5 - (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN) + 0, + 0.5 + ); + double xSize = origin.getWidth() - 2.0 * (UltimateMonitorEntity.RENDER_MARGIN + UltimateMonitorEntity.RENDER_BORDER); + double ySize = origin.getHeight() - 2.0 * (UltimateMonitorEntity.RENDER_MARGIN + UltimateMonitorEntity.RENDER_BORDER); + + // Draw the contents + UltimateNetworkedTerminal terminal = originTerminal.getTerminal(); + if( terminal != null && !ShaderMod.INSTANCE.isRenderingShadowPass() ) + { + // Draw a terminal + int width = terminal.getWidth(), height = terminal.getHeight(); + int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; + double xScale = xSize / pixelWidth; + double yScale = ySize / pixelHeight; + transform.pushPose(); + transform.scale( (float) xScale, (float) -yScale, 1.0f ); + + Matrix4f matrix = transform.last().pose(); + + renderTerminal( matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) ); + + transform.popPose(); + } + else + { + FixedWidthFontRenderer.drawEmptyTerminal( + FixedWidthFontRenderer.toVertexConsumer( transform, bufferSource.getBuffer( RenderTypes.TERMINAL ) ), + -MARGIN, MARGIN, + (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) + ); + } + + transform.popPose(); + } + + private static void renderTerminal( Matrix4f matrix, UltimateClientMonitor monitor, float xMargin, float yMargin ) + { + UltimateNetworkedTerminal terminal = monitor.getTerminal(); + int width = terminal.getWidth(), height = terminal.getHeight(); + int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; + + MonitorRenderer renderType = MonitorRenderer.current(); + boolean redraw = monitor.pollTerminalChanged(); + if( monitor.createBuffer( renderType ) ) redraw = true; + + switch( renderType ) + { + case TBO: + { + if( redraw ) + { + var terminalBuffer = getBuffer( width * height * 3 ); + MonitorTextureBufferShader.setTerminalData( terminalBuffer, terminal ); + DirectBuffers.setBufferData( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer, terminalBuffer, GL20.GL_STATIC_DRAW ); + + var uniformBuffer = getBuffer( MonitorTextureBufferShader.UNIFORM_SIZE ); + MonitorTextureBufferShader.setUniformData( uniformBuffer, terminal ); + DirectBuffers.setBufferData( GL31.GL_UNIFORM_BUFFER, monitor.tboUniform, uniformBuffer, GL20.GL_STATIC_DRAW ); + } + + // Nobody knows what they're doing! + int active = GlStateManager._getActiveTexture(); + RenderSystem.activeTexture( TEXTURE_INDEX ); + GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, monitor.tboTexture ); + RenderSystem.activeTexture( active ); + + MonitorTextureBufferShader shader = RenderTypes.getMonitorTextureBufferShader(); + shader.setupUniform( monitor.tboUniform ); + + BufferBuilder buffer = Tesselator.getInstance().getBuilder(); + buffer.begin( RenderTypes.MONITOR_TBO.mode(), RenderTypes.MONITOR_TBO.format() ); + tboVertex( buffer, matrix, -xMargin, -yMargin ); + tboVertex( buffer, matrix, -xMargin, pixelHeight + yMargin ); + tboVertex( buffer, matrix, pixelWidth + xMargin, -yMargin ); + tboVertex( buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin ); + RenderTypes.MONITOR_TBO.end( buffer, 0, 0, 0 ); + + break; + } + + case VBO: + { + var backgroundBuffer = monitor.backgroundBuffer; + var foregroundBuffer = monitor.foregroundBuffer; + if( redraw ) + { + int size = DirectFixedWidthFontRenderer.getVertexCount( terminal ); + + // In an ideal world we could upload these both into one buffer. However, we can't render VBOs with + // and starting and ending offset, and so need to use two buffers instead. + + renderToBuffer( backgroundBuffer, size, sink -> + DirectFixedWidthFontRenderer.drawTerminalBackground( sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin ) ); + + renderToBuffer( foregroundBuffer, size, sink -> { + DirectFixedWidthFontRenderer.drawTerminalForeground( sink, 0, 0, terminal ); + // If the cursor is visible, we append it to the end of our buffer. When rendering, we can either + // render n or n+1 quads and so toggle the cursor on and off. + DirectFixedWidthFontRenderer.drawCursor( sink, 0, 0, terminal ); + } ); + } + + // Our VBO doesn't transform its vertices with the provided pose stack, which means that the inverse view + // rotation matrix gives entirely wrong numbers for fog distances. We just set it to the identity which + // gives a good enough approximation. + Matrix3f oldInverseRotation = RenderSystem.getInverseViewRotationMatrix(); + RenderSystem.setInverseViewRotationMatrix( IDENTITY_NORMAL ); + + RenderTypes.TERMINAL.setupRenderState(); + + // Render background geometry + backgroundBuffer.bind(); + backgroundBuffer.drawWithShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader() ); + + // Render foreground geometry with glPolygonOffset enabled. + GL11.glPolygonOffset( -1.0f, -10.0f ); + GL11.glEnable( GL11.GL_POLYGON_OFFSET_FILL ); + + foregroundBuffer.bind(); + foregroundBuffer.drawWithShader( + matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader(), + // As mentioned in the above comment, render the extra cursor quad if it is visible this frame. Each + // // quad has an index count of 6. + FixedWidthFontRenderer.isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() + ? foregroundBuffer.getIndexCount() + 6 : foregroundBuffer.getIndexCount() + ); + + // Clear state + GL11.glPolygonOffset( 0.0f, -0.0f ); + GL11.glDisable( GL11.GL_POLYGON_OFFSET_FILL ); + RenderTypes.TERMINAL.clearRenderState(); + VertexBuffer.unbind(); + + RenderSystem.setInverseViewRotationMatrix( oldInverseRotation ); + + break; + } + } + } + + private static void renderToBuffer( DirectVertexBuffer vbo, int size, Consumer draw ) + { + var sink = ShaderMod.INSTANCE.getQuadEmitter( size, UltimateMonitorRenderer::getBuffer ); + var buffer = sink.buffer(); + + draw.accept( sink ); + buffer.flip(); + vbo.upload( buffer.limit() / sink.format().getVertexSize(), RenderTypes.TERMINAL.mode(), sink.format(), buffer ); + } + + private static void tboVertex( VertexConsumer builder, Matrix4f matrix, float x, float y ) + { + // We encode position in the UV, as that's not transformed by the matrix. + builder.vertex( matrix, x, y, 0 ).uv( x, y ).endVertex(); + } + + @Nonnull + private static ByteBuffer getBuffer( int capacity ) + { + + ByteBuffer buffer = backingBuffer; + if( buffer == null || buffer.capacity() < capacity ) + { + buffer = backingBuffer = buffer == null ? MemoryTracker.create( capacity ) : MemoryTracker.resize( buffer, capacity ); + } + + buffer.clear(); + return buffer; + } + + @Override + public int getViewDistance() + { + return ComputerCraft.monitorDistance; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java deleted file mode 100644 index 71261ee88..000000000 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/UltimateMonitorPeripheral.java +++ /dev/null @@ -1,89 +0,0 @@ -package de.srendi.advancedperipherals.common.addons.computercraft.peripheral; - -import dan200.computercraft.api.lua.LuaException; -import dan200.computercraft.api.lua.LuaFunction; -import dan200.computercraft.core.apis.TableHelper; -import dan200.computercraft.shared.peripheral.monitor.MonitorPeripheral; -import de.srendi.advancedperipherals.common.addons.computercraft.owner.BlockEntityPeripheralOwner; -import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; -import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal.MonitorSide; -import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateServerMonitor; -import de.srendi.advancedperipherals.common.blocks.blockentities.UltimateMonitorEntity; - -import java.util.Map; -import java.util.HashMap; - -public class UltimateMonitorPeripheral extends MonitorPeripheral { - private final UltimateMonitorEntity monitor; - - public UltimateMonitorPeripheral(UltimateMonitorEntity monitor) { - super(monitor); - this.monitor = monitor; - } - - private UltimateServerMonitor getMonitor() throws LuaException { - UltimateServerMonitor monitor = this.monitor.getCachedServerMonitor(); - if (monitor == null) { - throw new LuaException("Monitor has been detached"); - } - return monitor; - } - - @Override - public UltimateNetworkedTerminal getTerminal() throws LuaException { - UltimateNetworkedTerminal term = getMonitor().getTerminal(); - if (term == null) { - throw new LuaException("Monitor has been detached"); - } - return term; - } - - @LuaFunction - public double getTransparency() { - return (double)(this.getTerminal().getTransparency()) / 0xff; - } - - @LuaFunction - public void setTransparency(double transparency) { - this.getTerminal().setTransparency((int)(transparency * 0xff)); - } - - @LuaFunction - public Map getSideColor(IArguments args) { - if (args.count() < 1) { - throw new LuaException("getSideColor need one argument"); - } - MonitorSide side = MonitorSide.fromString(args.getString(0)); - if (side == null) { - throw new LuaException(String.format("Invalid monitor side %s", side)); - } - int[] color = this.getTerminal().getSideColor(side); - int a = this.getTerminal().getSideTransparency(side); - Map obj = new HashMap<>(); - obj.put("r", color[0]); - obj.put("g", color[1]); - obj.put("b", color[2]); - obj.put("a", a); - return obj; - } - - @LuaFunction - public void setSideColor(IArguments args) { - if (args.count() < 2) { - throw new LuaException("setSideColor need two arguments"); - } - MonitorSide side = MonitorSide.fromString(args.getString(0)); - if (side == null) { - throw new LuaException(String.format("Invalid monitor side %s", side)); - } - if (!(args.get(1) instanceof Map obj)) { - throw new LuaException("The second argument should be an rgba table"); - } - int r = (int) TableHelper.getNumberField(obj, "r"); - int g = (int) TableHelper.getNumberField(obj, "g"); - int b = (int) TableHelper.getNumberField(obj, "b"); - int a = (int) TableHelper.getNumberField(obj, "a"); - this.getTerminal().setSideColor(side, new int[]{r, g, b}); - this.getTerminal().setSideTransparency(side, a); - } -} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java new file mode 100644 index 000000000..546fa902f --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java @@ -0,0 +1,117 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import dan200.computercraft.shared.common.BlockGeneric; +import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.registries.RegistryObject; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class UltimateBlockMonitor extends BlockGeneric +{ + public static final DirectionProperty ORIENTATION = DirectionProperty.create( "orientation", + Direction.UP, Direction.DOWN, Direction.NORTH ); + + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + + public static final EnumProperty STATE = EnumProperty.create( "state", MonitorEdgeState.class ); + + public UltimateBlockMonitor( Properties settings, RegistryObject> type ) + { + super( settings, type ); + // TODO: Test underwater - do we need isSolid at all? + registerDefaultState( getStateDefinition().any() + .setValue( ORIENTATION, Direction.NORTH ) + .setValue( FACING, Direction.NORTH ) + .setValue( STATE, MonitorEdgeState.NONE ) ); + } + + @Override + protected void createBlockStateDefinition( StateDefinition.Builder builder ) + { + builder.add( ORIENTATION, FACING, STATE ); + } + + @Nonnull + @Override + @Deprecated + public BlockState mirror( BlockState state, Mirror mirrorIn ) + { + return state.rotate( mirrorIn.getRotation( state.getValue( FACING ) ) ); + } + + @Nonnull + @Override + @Deprecated + public BlockState rotate( BlockState state, Rotation rot ) + { + return state.setValue( FACING, rot.rotate( state.getValue( FACING ) ) ); + } + + @Override + @Nullable + public BlockState getStateForPlacement( BlockPlaceContext context ) + { + float pitch = context.getPlayer() == null ? 0 : context.getPlayer().getXRot(); + Direction orientation; + if( pitch > 66.5f ) + { + // If the player is looking down, place it facing upwards + orientation = Direction.UP; + } + else if( pitch < -66.5f ) + { + // If they're looking up, place it down. + orientation = Direction.DOWN; + } + else + { + orientation = Direction.NORTH; + } + + return defaultBlockState() + .setValue( FACING, context.getHorizontalDirection().getOpposite() ) + .setValue( ORIENTATION, orientation ); + } + + @Override + public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState blockState, @Nullable LivingEntity livingEntity, @Nonnull ItemStack itemStack ) + { + super.setPlacedBy( world, pos, blockState, livingEntity, itemStack ); + + BlockEntity entity = world.getBlockEntity( pos ); + if( entity instanceof UltimateMonitorEntity monitor && !world.isClientSide ) + { + // Defer the block update if we're being placed by another TE. See #691 + if( livingEntity == null || livingEntity instanceof FakePlayer ) + { + monitor.updateNeighborsDeferred(); + return; + } + + monitor.expand(); + } + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java new file mode 100644 index 000000000..c609ba1e8 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java @@ -0,0 +1,201 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import com.mojang.blaze3d.platform.GlStateManager; +import dan200.computercraft.client.util.DirectBuffers; +import dan200.computercraft.client.util.DirectVertexBuffer; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.computer.terminal.TerminalState; +import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; +import dan200.computercraft.shared.peripheral.monitor.XYPair; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; +import net.minecraft.core.BlockPos; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL31; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public final class UltimateClientMonitor +{ + private static final Set allMonitors = new HashSet<>(); + + private final UltimateMonitorEntity origin; + + public long lastRenderFrame = -1; + public BlockPos lastRenderPos = null; + + public int tboBuffer; + public int tboTexture; + public int tboUniform; + public DirectVertexBuffer backgroundBuffer; + public DirectVertexBuffer foregroundBuffer; + private UltimateNetworkedTerminal terminal; + private boolean terminalChanged; + + public UltimateClientMonitor( UltimateMonitorEntity origin ) + { + this.origin = origin; + } + + public UltimateMonitorEntity getOrigin() + { + return origin; + } + + /** + * Create the appropriate buffer if needed. + * + * @param renderer The renderer to use. This can be fetched from {@link MonitorRenderer#current()}. + * @return If a buffer was created. This will return {@code false} if we already have an appropriate buffer, + * or this mode does not require one. + */ + @OnlyIn( Dist.CLIENT ) + public boolean createBuffer( MonitorRenderer renderer ) + { + switch( renderer ) + { + case TBO: + { + if( tboBuffer != 0 ) return false; + + deleteBuffers(); + + tboBuffer = DirectBuffers.createBuffer(); + DirectBuffers.setEmptyBufferData( GL31.GL_TEXTURE_BUFFER, tboBuffer, GL15.GL_STATIC_DRAW ); + tboTexture = GlStateManager._genTexture(); + GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, tboTexture ); + GL31.glTexBuffer( GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, tboBuffer ); + GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, 0 ); + + tboUniform = DirectBuffers.createBuffer(); + DirectBuffers.setEmptyBufferData( GL31.GL_UNIFORM_BUFFER, tboUniform, GL15.GL_STATIC_DRAW ); + + addMonitor(); + return true; + } + + case VBO: + if( backgroundBuffer != null ) return false; + + deleteBuffers(); + backgroundBuffer = new DirectVertexBuffer(); + foregroundBuffer = new DirectVertexBuffer(); + addMonitor(); + return true; + + default: + return false; + } + } + + private void addMonitor() + { + synchronized( allMonitors ) + { + allMonitors.add( this ); + } + } + + private void deleteBuffers() + { + + if( tboBuffer != 0 ) + { + DirectBuffers.deleteBuffer( GL31.GL_TEXTURE_BUFFER, tboBuffer ); + tboBuffer = 0; + } + + if( tboTexture != 0 ) + { + GlStateManager._deleteTexture( tboTexture ); + tboTexture = 0; + } + + if( tboUniform != 0 ) + { + DirectBuffers.deleteBuffer( GL31.GL_UNIFORM_BUFFER, tboUniform ); + tboUniform = 0; + } + + if( backgroundBuffer != null ) + { + backgroundBuffer.close(); + backgroundBuffer = null; + } + + if( foregroundBuffer != null ) + { + foregroundBuffer.close(); + foregroundBuffer = null; + } + } + + @OnlyIn( Dist.CLIENT ) + public void destroy() + { + if( tboBuffer != 0 || backgroundBuffer != null ) + { + synchronized( allMonitors ) + { + allMonitors.remove( this ); + } + + deleteBuffers(); + } + } + + @OnlyIn( Dist.CLIENT ) + public static void destroyAll() + { + synchronized( allMonitors ) + { + for( Iterator iterator = allMonitors.iterator(); iterator.hasNext(); ) + { + UltimateClientMonitor monitor = iterator.next(); + monitor.deleteBuffers(); + + iterator.remove(); + } + } + } + + public boolean pollTerminalChanged() + { + boolean changed = terminalChanged; + terminalChanged = false; + return changed; + } + + public UltimateNetworkedTerminal getTerminal() + { + return terminal; + } + + void read( TerminalState state ) + { + if( state.hasTerminal() ) + { + if( terminal == null ) terminal = new UltimateNetworkedTerminal( state.width, state.height ); + state.apply( terminal ); + terminalChanged = true; + } + else + { + if( terminal != null ) + { + terminal = null; + terminalChanged = true; + } + } + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateExpander.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateExpander.java new file mode 100644 index 000000000..0907a3c2d --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateExpander.java @@ -0,0 +1,111 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import dan200.computercraft.ComputerCraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.Objects; + +/** + * Expands a monitor into available space. This tries to expand in each direction until a fixed point is reached. + */ +class UltimateExpander +{ + private final Level level; + private final Direction down; + private final Direction right; + + private UltimateMonitorEntity origin; + private int width; + private int height; + + UltimateExpander( UltimateMonitorEntity origin ) + { + this.origin = origin; + width = origin.getWidth(); + height = origin.getHeight(); + + level = Objects.requireNonNull( origin.getLevel(), "level cannot be null" ); + down = origin.getDown(); + right = origin.getRight(); + } + + void expand() + { + int changedCount = 0; + + // Impose a limit on the number of resizes we can attempt. There's a risk of getting into an infinite loop + // if we merge right/down and the next monitor has a width/height of 0. This /should/ never happen - validation + // will catch it - but I also have a complete lack of faith in the code. + // As an aside, I think the actual limit is width+height resizes, but again - complete lack of faith. + int changeLimit = ComputerCraft.monitorWidth * ComputerCraft.monitorHeight + 1; + while( expandIn( true, false ) || expandIn( true, true ) || + expandIn( false, false ) || expandIn( false, true ) + ) + { + changedCount++; + if( changedCount > changeLimit ) + { + ComputerCraft.log.error( "Monitor has grown too much. This suggests there's an empty monitor in the world." ); + break; + } + } + + if( changedCount > 0 ) origin.resize( width, height ); + } + + /** + * Attempt to expand a monitor in a particular direction as much as possible. + * + * @param useXAxis {@literal true} if we're expanding on the X Axis, {@literal false} if on the Y. + * @param isPositive {@literal true} if we're expanding in the positive direction, {@literal false} if negative. + * @return If the monitor changed. + */ + private boolean expandIn( boolean useXAxis, boolean isPositive ) + { + BlockPos pos = origin.getBlockPos(); + int height = this.height, width = this.width; + + int otherOffset = isPositive ? (useXAxis ? width : height) : -1; + BlockPos otherPos = useXAxis ? pos.relative( right, otherOffset ) : pos.relative( down, otherOffset ); + BlockEntity other = level.getBlockEntity( otherPos ); + if( !(other instanceof UltimateMonitorEntity otherMonitor) || !origin.isCompatible( otherMonitor ) ) return false; + + if( useXAxis ) + { + if( otherMonitor.getYIndex() != 0 || otherMonitor.getHeight() != height ) return false; + width += otherMonitor.getWidth(); + if( width > ComputerCraft.monitorWidth ) return false; + } + else + { + if( otherMonitor.getXIndex() != 0 || otherMonitor.getWidth() != width ) return false; + height += otherMonitor.getHeight(); + if( height > ComputerCraft.monitorHeight ) return false; + } + + if( !isPositive ) + { + BlockEntity otherOrigin = level.getBlockEntity( otherMonitor.toWorldPos( 0, 0 ) ); + if( !(otherOrigin instanceof UltimateMonitorEntity originMonitor) || !origin.isCompatible( originMonitor ) ) + { + return false; + } + + origin = originMonitor; + } + + this.width = width; + this.height = height; + + return true; + } + +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java new file mode 100644 index 000000000..991e60dd0 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java @@ -0,0 +1,649 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import com.google.common.annotations.VisibleForTesting; +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.common.TileGeneric; +import dan200.computercraft.shared.computer.terminal.TerminalState; +import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState; +import dan200.computercraft.shared.peripheral.monitor.XYPair; +import dan200.computercraft.shared.util.CapabilityUtil; +import dan200.computercraft.shared.util.TickScheduler; +import de.srendi.advancedperipherals.common.setup.APBlockEntityTypes; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL; + +public class UltimateMonitorEntity extends TileGeneric { + public static final double RENDER_BORDER = 2.0 / 16.0; + public static final double RENDER_MARGIN = 0.5 / 16.0; + public static final double RENDER_PIXEL_SCALE = 1.0 / 64.0; + + private static final String NBT_X = "XIndex"; + private static final String NBT_Y = "YIndex"; + private static final String NBT_WIDTH = "Width"; + private static final String NBT_HEIGHT = "Height"; + + private UltimateServerMonitor serverMonitor; + private UltimateClientMonitor clientMonitor; + private UltimateMonitorPeripheral peripheral; + private LazyOptional peripheralCap; + private final Set computers = new HashSet<>(); + + private boolean needsUpdate = false; + private boolean needsValidating = false; + private boolean destroyed = false; + + // MonitorWatcher state. + boolean enqueued; + TerminalState cached; + + private int width = 1; + private int height = 1; + private int xIndex = 0; + private int yIndex = 0; + + private BlockPos bbPos; + private BlockState bbState; + private int bbX, bbY, bbWidth, bbHeight; + private AABB boundingBox; + + TickScheduler.Token tickToken = new TickScheduler.Token( this ); + + public UltimateMonitorEntity( BlockPos pos, BlockState state ) + { + super( APBlockEntityTypes.ULTIMATE_MONITOR.get(), pos, state ); + } + + @Override + public void clearRemoved() // TODO: Switch back to onLood + { + super.clearRemoved(); + needsValidating = true; // Same, tbh + TickScheduler.schedule( tickToken ); + } + + @Override + public void destroy() + { + // TODO: Call this before using the block + if( destroyed ) return; + destroyed = true; + if( !getLevel().isClientSide ) contractNeighbours(); + } + + @Override + public void setRemoved() + { + super.setRemoved(); + if( clientMonitor != null && xIndex == 0 && yIndex == 0 ) clientMonitor.destroy(); + } + + @Override + public void onChunkUnloaded() + { + super.onChunkUnloaded(); + if( clientMonitor != null && xIndex == 0 && yIndex == 0 ) clientMonitor.destroy(); + } + + @Nonnull + @Override + public InteractionResult onActivate( Player player, InteractionHand hand, BlockHitResult hit ) + { + if( !player.isCrouching() && getFront() == hit.getDirection() ) + { + if( !getLevel().isClientSide ) + { + monitorTouched( + (float) (hit.getLocation().x - hit.getBlockPos().getX()), + (float) (hit.getLocation().y - hit.getBlockPos().getY()), + (float) (hit.getLocation().z - hit.getBlockPos().getZ()), + player + ); + } + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } + + @Override + public void saveAdditional( CompoundTag tag ) + { + tag.putInt( NBT_X, xIndex ); + tag.putInt( NBT_Y, yIndex ); + tag.putInt( NBT_WIDTH, width ); + tag.putInt( NBT_HEIGHT, height ); + super.saveAdditional( tag ); + } + + @Override + public void load( @Nonnull CompoundTag nbt ) + { + super.load( nbt ); + + xIndex = nbt.getInt( NBT_X ); + yIndex = nbt.getInt( NBT_Y ); + width = nbt.getInt( NBT_WIDTH ); + height = nbt.getInt( NBT_HEIGHT ); + } + + @Override + public void blockTick() + { + if( needsValidating ) + { + needsValidating = false; + validate(); + } + + if( needsUpdate ) + { + needsUpdate = false; + expand(); + } + + if( xIndex != 0 || yIndex != 0 || serverMonitor == null ) return; + + if( serverMonitor.pollResized() ) eachComputer( c -> c.queueEvent( "monitor_resize", c.getAttachmentName() ) ); + if( serverMonitor.pollTerminalChanged() ) UltimateMonitorWatcher.enqueue( this ); + } + + @Override + public void invalidateCaps() + { + super.invalidateCaps(); + peripheralCap = CapabilityUtil.invalidate( peripheralCap ); + } + + @Nonnull + @Override + public LazyOptional getCapability( @Nonnull Capability cap, @Nullable Direction side ) + { + if( cap == CAPABILITY_PERIPHERAL ) + { + createServerMonitor(); // Ensure the monitor is created before doing anything else. + if( peripheral == null ) peripheral = new UltimateMonitorPeripheral( this ); + if( peripheralCap == null ) peripheralCap = LazyOptional.of( () -> peripheral ); + return peripheralCap.cast(); + } + return super.getCapability( cap, side ); + } + + @Nullable + @VisibleForTesting + public UltimateServerMonitor getCachedServerMonitor() + { + return serverMonitor; + } + + @Nullable + private UltimateServerMonitor getServerMonitor() + { + if( serverMonitor != null ) return serverMonitor; + + UltimateMonitorEntity origin = getOrigin().getMonitor(); + if( origin == null ) return null; + + return serverMonitor = origin.serverMonitor; + } + + @Nullable + private UltimateServerMonitor createServerMonitor() + { + if( serverMonitor != null ) return serverMonitor; + + if( xIndex == 0 && yIndex == 0 ) + { + // If we're the origin, set up the new monitor + serverMonitor = new UltimateServerMonitor( this ); + serverMonitor.rebuild(); + + // And propagate it to child monitors + for( int x = 0; x < width; x++ ) + { + for( int y = 0; y < height; y++ ) + { + UltimateMonitorEntity monitor = getLoadedMonitor( x, y ).getMonitor(); + if( monitor != null ) monitor.serverMonitor = serverMonitor; + } + } + + return serverMonitor; + } + else + { + // Otherwise fetch the origin and attempt to get its monitor + // Note this may load chunks, but we don't really have a choice here. + BlockEntity te = level.getBlockEntity( toWorldPos( 0, 0 ) ); + if( !(te instanceof UltimateMonitorEntity monitor) ) return null; + + return serverMonitor = monitor.createServerMonitor(); + } + } + + @Nullable + public UltimateClientMonitor getClientMonitor() + { + if( clientMonitor != null ) return clientMonitor; + + BlockEntity te = level.getBlockEntity( toWorldPos( 0, 0 ) ); + if( !(te instanceof UltimateMonitorEntity monitor) ) return null; + + return clientMonitor = monitor.clientMonitor; + } + + // Networking stuff + + @Nonnull + @Override + public final CompoundTag getUpdateTag() + { + CompoundTag nbt = super.getUpdateTag(); + nbt.putInt( NBT_X, xIndex ); + nbt.putInt( NBT_Y, yIndex ); + nbt.putInt( NBT_WIDTH, width ); + nbt.putInt( NBT_HEIGHT, height ); + return nbt; + } + + @Override + public final void handleUpdateTag( @Nonnull CompoundTag nbt ) + { + super.handleUpdateTag( nbt ); + + int oldXIndex = xIndex; + int oldYIndex = yIndex; + + xIndex = nbt.getInt( NBT_X ); + yIndex = nbt.getInt( NBT_Y ); + width = nbt.getInt( NBT_WIDTH ); + height = nbt.getInt( NBT_HEIGHT ); + + if( oldXIndex != xIndex || oldYIndex != yIndex ) + { + // If our index has changed then it's possible the origin monitor has changed. Thus + // we'll clear our cache. If we're the origin then we'll need to remove the glList as well. + if( oldXIndex == 0 && oldYIndex == 0 && clientMonitor != null ) clientMonitor.destroy(); + clientMonitor = null; + } + + if( xIndex == 0 && yIndex == 0 ) + { + // If we're the origin terminal then create it. + if( clientMonitor == null ) clientMonitor = new UltimateClientMonitor( this ); + } + } + + public final void read( TerminalState state ) + { + if( xIndex != 0 || yIndex != 0 ) + { + ComputerCraft.log.warn( "Receiving monitor state for non-origin terminal at {}", getBlockPos() ); + return; + } + + if( clientMonitor == null ) clientMonitor = new UltimateClientMonitor( this ); + clientMonitor.read( state ); + } + + // Sizing and placement stuff + + private void updateBlockState() + { + getLevel().setBlock( getBlockPos(), getBlockState() + .setValue( UltimateBlockMonitor.STATE, MonitorEdgeState.fromConnections( + yIndex < height - 1, yIndex > 0, + xIndex > 0, xIndex < width - 1 ) ), 2 ); + } + + // region Sizing and placement stuff + public Direction getDirection() + { + // Ensure we're actually a monitor block. This _should_ always be the case, but sometimes there's + // fun problems with the block being missing on the client. + BlockState state = getBlockState(); + return state.hasProperty( UltimateBlockMonitor.FACING ) ? state.getValue( UltimateBlockMonitor.FACING ) : Direction.NORTH; + } + + public Direction getOrientation() + { + BlockState state = getBlockState(); + return state.hasProperty( UltimateBlockMonitor.ORIENTATION ) ? state.getValue( UltimateBlockMonitor.ORIENTATION ) : Direction.NORTH; + } + + public Direction getFront() + { + Direction orientation = getOrientation(); + return orientation == Direction.NORTH ? getDirection() : orientation; + } + + public Direction getRight() + { + return getDirection().getCounterClockWise(); + } + + public Direction getDown() + { + Direction orientation = getOrientation(); + if( orientation == Direction.NORTH ) return Direction.UP; + return orientation == Direction.DOWN ? getDirection() : getDirection().getOpposite(); + } + + public int getWidth() + { + return width; + } + + public int getHeight() + { + return height; + } + + public int getXIndex() + { + return xIndex; + } + + public int getYIndex() + { + return yIndex; + } + + boolean isCompatible( UltimateMonitorEntity other ) + { + return other instanceof UltimateMonitorEntity && getOrientation() == other.getOrientation() && getDirection() == other.getDirection(); + } + + /** + * Get a tile within the current monitor only if it is loaded and compatible. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The located monitor + */ + @Nonnull + private UltimateMonitorState getLoadedMonitor( int x, int y ) + { + if( x == xIndex && y == yIndex ) return UltimateMonitorState.present( this ); + BlockPos pos = toWorldPos( x, y ); + + Level world = getLevel(); + if( world == null || !world.isLoaded( pos ) ) return UltimateMonitorState.UNLOADED; + + BlockEntity tile = world.getBlockEntity( pos ); + if( !(tile instanceof UltimateMonitorEntity monitor) ) return UltimateMonitorState.MISSING; + + return isCompatible( monitor ) ? UltimateMonitorState.present( monitor ) : UltimateMonitorState.MISSING; + } + + private UltimateMonitorState getOrigin() + { + return getLoadedMonitor( 0, 0 ); + } + + /** + * Convert monitor coordinates to world coordinates. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The monitor's position. + */ + BlockPos toWorldPos( int x, int y ) + { + if( xIndex == x && yIndex == y ) return getBlockPos(); + return getBlockPos().relative( getRight(), -xIndex + x ).relative( getDown(), -yIndex + y ); + } + + void resize( int width, int height ) + { + // If we're not already the origin then we'll need to generate a new terminal. + if( xIndex != 0 || yIndex != 0 ) serverMonitor = null; + + xIndex = 0; + yIndex = 0; + this.width = width; + this.height = height; + + // Determine if we actually need a monitor. In order to do this, simply check if + // any component monitor been wrapped as a peripheral. Whilst this flag may be + // out of date, + boolean needsTerminal = false; + terminalCheck: + for( int x = 0; x < width; x++ ) + { + for( int y = 0; y < height; y++ ) + { + UltimateMonitorEntity monitor = getLoadedMonitor( x, y ).getMonitor(); + if( monitor != null && monitor.peripheral != null ) + { + needsTerminal = true; + break terminalCheck; + } + } + } + + // Either delete the current monitor or sync a new one. + if( needsTerminal ) + { + if( serverMonitor == null ) serverMonitor = new UltimateServerMonitor( this ); + } + else + { + serverMonitor = null; + } + + // Update the terminal's width and height and rebuild it. This ensures the monitor + // is consistent when syncing it to other monitors. + if( serverMonitor != null ) serverMonitor.rebuild(); + + // Update the other monitors, setting coordinates, dimensions and the server terminal + BlockPos pos = getBlockPos(); + Direction down = getDown(), right = getRight(); + for( int x = 0; x < width; x++ ) + { + for( int y = 0; y < height; y++ ) + { + BlockEntity other = getLevel().getBlockEntity( pos.relative( right, x ).relative( down, y ) ); + if( !(other instanceof UltimateMonitorEntity monitor) || !isCompatible( monitor ) ) continue; + + monitor.xIndex = x; + monitor.yIndex = y; + monitor.width = width; + monitor.height = height; + monitor.serverMonitor = serverMonitor; + monitor.needsUpdate = monitor.needsValidating = false; + monitor.updateBlockState(); + monitor.updateBlock(); + } + } + } + + void updateNeighborsDeferred() + { + needsUpdate = true; + } + + void expand() + { + UltimateMonitorEntity monitor = getOrigin().getMonitor(); + if( monitor != null && monitor.xIndex == 0 && monitor.yIndex == 0 ) new UltimateExpander( monitor ).expand(); + } + + private void contractNeighbours() + { + if( width == 1 && height == 1 ) return; + + BlockPos pos = getBlockPos(); + Direction down = getDown(), right = getRight(); + BlockPos origin = toWorldPos( 0, 0 ); + + UltimateMonitorEntity toLeft = null, toAbove = null, toRight = null, toBelow = null; + if( xIndex > 0 ) toLeft = tryResizeAt( pos.relative( right, -xIndex ), xIndex, 1 ); + if( yIndex > 0 ) toAbove = tryResizeAt( origin, width, yIndex ); + if( xIndex < width - 1 ) toRight = tryResizeAt( pos.relative( right, 1 ), width - xIndex - 1, 1 ); + if( yIndex < height - 1 ) + { + toBelow = tryResizeAt( origin.relative( down, yIndex + 1 ), width, height - yIndex - 1 ); + } + + if( toLeft != null ) toLeft.expand(); + if( toAbove != null ) toAbove.expand(); + if( toRight != null ) toRight.expand(); + if( toBelow != null ) toBelow.expand(); + } + + @Nullable + private UltimateMonitorEntity tryResizeAt( BlockPos pos, int width, int height ) + { + BlockEntity tile = level.getBlockEntity( pos ); + if( tile instanceof UltimateMonitorEntity monitor && isCompatible( monitor ) ) + { + monitor.resize( width, height ); + return monitor; + } + + return null; + } + + + private boolean checkMonitorAt( int xIndex, int yIndex ) + { + UltimateMonitorState state = getLoadedMonitor( xIndex, yIndex ); + if( state.isMissing() ) return false; + + UltimateMonitorEntity monitor = state.getMonitor(); + if( monitor == null ) return true; + + return monitor.xIndex == xIndex && monitor.yIndex == yIndex && monitor.width == width && monitor.height == height; + } + + private void validate() + { + if( xIndex == 0 && yIndex == 0 && width == 1 && height == 1 ) return; + + if( xIndex >= 0 && xIndex <= width && width > 0 && width <= ComputerCraft.monitorWidth && + yIndex >= 0 && yIndex <= height && height > 0 && height <= ComputerCraft.monitorHeight && + checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) && + checkMonitorAt( width - 1, 0 ) && checkMonitorAt( width - 1, height - 1 ) ) + { + return; + } + + // Something in our monitor is invalid. For now, let's just reset ourselves and then try to integrate ourselves + // later. + ComputerCraft.log.warn( "Monitor is malformed, resetting to 1x1." ); + resize( 1, 1 ); + needsUpdate = true; + } + // endregion + + private void monitorTouched( float xPos, float yPos, float zPos, @Nullable Player player ) + { + XYPair pair = XYPair + .of( xPos, yPos, zPos, getDirection(), getOrientation() ) + .add( xIndex, height - yIndex - 1 ); + + if( pair.x() > width - RENDER_BORDER || pair.y() > height - RENDER_BORDER || pair.x() < RENDER_BORDER || pair.y() < RENDER_BORDER ) + { + return; + } + + UltimateServerMonitor serverTerminal = getServerMonitor(); + if( serverTerminal == null ) return; + + Terminal originTerminal = serverTerminal.getTerminal(); + if( originTerminal == null ) return; + + double xCharWidth = (width - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getWidth(); + double yCharHeight = (height - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getHeight(); + + int xCharPos = (int) Math.min( originTerminal.getWidth(), Math.max( (pair.x() - RENDER_BORDER - RENDER_MARGIN) / xCharWidth + 1.0, 1.0 ) ); + int yCharPos = (int) Math.min( originTerminal.getHeight(), Math.max( (pair.y() - RENDER_BORDER - RENDER_MARGIN) / yCharHeight + 1.0, 1.0 ) ); + + eachComputer( c -> c.queueEvent( "monitor_touch", c.getAttachmentName(), xCharPos, yCharPos, player == null ? null : player.getName().getString() ) ); + } + + private void eachComputer( Consumer fun ) + { + for( int x = 0; x < width; x++ ) + { + for( int y = 0; y < height; y++ ) + { + UltimateMonitorEntity monitor = getLoadedMonitor( x, y ).getMonitor(); + if( monitor == null ) continue; + + for( IComputerAccess computer : monitor.computers ) fun.accept( computer ); + } + } + } + + void addComputer( IComputerAccess computer ) + { + computers.add( computer ); + } + + void removeComputer( IComputerAccess computer ) + { + computers.remove( computer ); + } + + @Nonnull + @Override + public AABB getRenderBoundingBox() + { + // We attempt to cache the bounding box to save having to do property lookups (and allocations!) on every frame. + // Unfortunately the AABB does depend on quite a lot of state, so we need to add a bunch of extra fields - + // ideally these'd be a single object, but I don't think worth doing until Java has value types. + if( boundingBox != null && getBlockState().equals( bbState ) && getBlockPos().equals( bbPos ) && + xIndex == bbX && yIndex == bbY && width == bbWidth && height == bbHeight ) + { + return boundingBox; + } + + bbState = getBlockState(); + bbPos = getBlockPos(); + bbX = xIndex; + bbY = yIndex; + bbWidth = width; + bbHeight = height; + + BlockPos startPos = toWorldPos( 0, 0 ); + BlockPos endPos = toWorldPos( width, height ); + return boundingBox = new AABB( + Math.min( startPos.getX(), endPos.getX() ), + Math.min( startPos.getY(), endPos.getY() ), + Math.min( startPos.getZ(), endPos.getZ() ), + Math.max( startPos.getX(), endPos.getX() ) + 1, + Math.max( startPos.getY(), endPos.getY() ) + 1, + Math.max( startPos.getZ(), endPos.getZ() ) + 1 + ); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java new file mode 100644 index 000000000..f3d7264e3 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java @@ -0,0 +1,161 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import dan200.computercraft.api.lua.IArguments; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.LuaValues; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.apis.TableHelper; +import dan200.computercraft.core.apis.TermMethods; +import de.srendi.advancedperipherals.common.addons.computercraft.owner.BlockEntityPeripheralOwner; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal.MonitorSide; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; +import java.util.HashMap; + +public class UltimateMonitorPeripheral extends TermMethods implements IPeripheral { + private final UltimateMonitorEntity monitor; + + public UltimateMonitorPeripheral(UltimateMonitorEntity monitor) { + this.monitor = monitor; + } + + @Override + public String getType() { + return "monitor"; + } + + @LuaFunction + public boolean isUltimate() { + return true; + } + + @LuaFunction + public double getTransparency() throws LuaException { + return (double)(this.getTerminal().getTransparency()) / 0xff; + } + + @LuaFunction + public void setTransparency(double transparency) throws LuaException { + this.getTerminal().setTransparency((int)(transparency * 0xff)); + } + + @LuaFunction + public Map getSideColor(IArguments args) throws LuaException { + if (args.count() < 1) { + throw new LuaException("getSideColor need one argument"); + } + MonitorSide side = MonitorSide.fromString(args.getString(0)); + if (side == null) { + throw new LuaException(String.format("Invalid monitor side %s", side)); + } + int[] color = this.getTerminal().getSideColor(side); + int a = this.getTerminal().getSideTransparency(side); + Map obj = new HashMap<>(); + obj.put("r", color[0]); + obj.put("g", color[1]); + obj.put("b", color[2]); + obj.put("a", a); + return obj; + } + + @LuaFunction + public void setSideColor(IArguments args) throws LuaException { + if (args.count() < 2) { + throw new LuaException("setSideColor need two arguments"); + } + MonitorSide side = MonitorSide.fromString(args.getString(0)); + if (side == null) { + throw new LuaException(String.format("Invalid monitor side %s", side)); + } + if (!(args.get(1) instanceof Map obj)) { + throw new LuaException("The second argument should be an rgba table"); + } + int r = (int) TableHelper.getNumberField(obj, "r"); + int g = (int) TableHelper.getNumberField(obj, "g"); + int b = (int) TableHelper.getNumberField(obj, "b"); + int a = (int) TableHelper.getNumberField(obj, "a"); + this.getTerminal().setSideColor(side, new int[]{r, g, b}); + this.getTerminal().setSideTransparency(side, a); + } + + /** + * Set the scale of this monitor. A larger scale will result in the monitor having a lower resolution, but display + * text much larger. + * + * @param scaleArg The monitor's scale. This must be a multiple of 0.5 between 0.5 and 5. + * @throws LuaException If the scale is out of range. + * @see #getTextScale() + */ + @LuaFunction + public final void setTextScale( double scaleArg ) throws LuaException + { + int scale = (int) (LuaValues.checkFinite( 0, scaleArg ) * 2.0); + if( scale < 1 || scale > 10 ) throw new LuaException( "Expected number in range 0.5-5" ); + getMonitor().setTextScale( scale ); + } + + /** + * Get the monitor's current text scale. + * + * @return The monitor's current scale. + * @throws LuaException If the monitor cannot be found. + * @cc.since 1.81.0 + */ + @LuaFunction + public final double getTextScale() throws LuaException + { + return getMonitor().getTextScale() / 2.0; + } + + @Override + public void attach( @Nonnull IComputerAccess computer ) + { + monitor.addComputer( computer ); + } + + @Override + public void detach( @Nonnull IComputerAccess computer ) + { + monitor.removeComputer( computer ); + } + + @Override + public boolean equals( IPeripheral other ) + { + return other instanceof UltimateMonitorPeripheral && monitor == ((UltimateMonitorPeripheral) other).monitor; + } + + @Nonnull + private UltimateServerMonitor getMonitor() throws LuaException + { + UltimateServerMonitor monitor = this.monitor.getCachedServerMonitor(); + if( monitor == null ) throw new LuaException( "Monitor has been detached" ); + return monitor; + } + + @Nonnull + @Override + public UltimateNetworkedTerminal getTerminal() throws LuaException + { + UltimateNetworkedTerminal terminal = getMonitor().getTerminal(); + if( terminal == null ) throw new LuaException( "Monitor has been detached" ); + return terminal; + } + + @Nullable + @Override + public Object getTarget() + { + return monitor; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorState.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorState.java new file mode 100644 index 000000000..fb54affe7 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorState.java @@ -0,0 +1,52 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +final class UltimateMonitorState +{ + public static final UltimateMonitorState UNLOADED = new UltimateMonitorState( State.UNLOADED, null ); + public static final UltimateMonitorState MISSING = new UltimateMonitorState( State.MISSING, null ); + + private final State state; + private final UltimateMonitorEntity monitor; + + private UltimateMonitorState( @Nonnull State state, @Nullable UltimateMonitorEntity monitor ) + { + this.state = state; + this.monitor = monitor; + } + + public static UltimateMonitorState present( @Nonnull UltimateMonitorEntity monitor ) + { + return new UltimateMonitorState( State.PRESENT, monitor ); + } + + public boolean isPresent() + { + return state == State.PRESENT; + } + + public boolean isMissing() + { + return state == State.MISSING; + } + + @Nullable + public UltimateMonitorEntity getMonitor() + { + return monitor; + } + + enum State + { + UNLOADED, + MISSING, + PRESENT, + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java new file mode 100644 index 000000000..44aac02a5 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java @@ -0,0 +1,106 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.client.MonitorClientMessage; +import dan200.computercraft.shared.computer.terminal.TerminalState; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.level.ChunkWatchEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.util.ArrayDeque; +import java.util.Queue; + +@Mod.EventBusSubscriber( modid = AdvancedPeripherals.MOD_ID ) +public final class UltimateMonitorWatcher +{ + private static final Queue watching = new ArrayDeque<>(); + + private UltimateMonitorWatcher() + { + } + + static void enqueue( UltimateMonitorEntity monitor ) + { + if( monitor.enqueued ) return; + + monitor.enqueued = true; + monitor.cached = null; + watching.add( monitor ); + } + + @SubscribeEvent + public static void onWatch( ChunkWatchEvent.Watch event ) + { + // Find all origin monitors who are not already on the queue and send the + // monitor data to the player. + for( BlockEntity te : event.getChunk().getBlockEntities().values() ) + { + if( !(te instanceof UltimateMonitorEntity monitor) ) continue; + + UltimateServerMonitor serverMonitor = getMonitor( monitor ); + if( serverMonitor == null || monitor.enqueued ) continue; + + TerminalState state = getState( monitor, serverMonitor ); + NetworkHandler.sendToPlayer( event.getPlayer(), new MonitorClientMessage( monitor.getBlockPos(), state ) ); + } + } + + @SubscribeEvent + public static void onTick( TickEvent.ServerTickEvent event ) + { + // Find all enqueued monitors and send their contents to all nearby players. + + if( event.phase != TickEvent.Phase.END ) return; + + long limit = ComputerCraft.monitorBandwidth; + boolean obeyLimit = limit > 0; + + UltimateMonitorEntity tile; + while( (!obeyLimit || limit > 0) && (tile = watching.poll()) != null ) + { + tile.enqueued = false; + UltimateServerMonitor monitor = getMonitor( tile ); + if( monitor == null ) continue; + + BlockPos pos = tile.getBlockPos(); + Level world = tile.getLevel(); + if( !(world instanceof ServerLevel) ) continue; + + LevelChunk chunk = world.getChunkAt( pos ); + if( ((ServerLevel) world).getChunkSource().chunkMap.getPlayers( chunk.getPos(), false ).isEmpty() ) + { + continue; + } + + TerminalState state = getState( tile, monitor ); + NetworkHandler.sendToAllTracking( new MonitorClientMessage( pos, state ), chunk ); + + limit -= state.size(); + } + } + + private static UltimateServerMonitor getMonitor( UltimateMonitorEntity monitor ) + { + return !monitor.isRemoved() && monitor.getXIndex() == 0 && monitor.getYIndex() == 0 ? monitor.getCachedServerMonitor() : null; + } + + private static TerminalState getState( UltimateMonitorEntity tile, UltimateServerMonitor monitor ) + { + TerminalState state = tile.cached; + if( state == null ) state = tile.cached = new TerminalState( monitor.getTerminal() ); + return state; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java new file mode 100644 index 000000000..792091195 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java @@ -0,0 +1,97 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; + +import com.google.common.annotations.VisibleForTesting; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.util.TickScheduler; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; + +import javax.annotation.Nullable; +import java.util.concurrent.atomic.AtomicBoolean; + +public class UltimateServerMonitor +{ + private final UltimateMonitorEntity origin; + + private int textScale = 2; + private @Nullable UltimateNetworkedTerminal terminal; + private final AtomicBoolean resized = new AtomicBoolean( false ); + private final AtomicBoolean changed = new AtomicBoolean( false ); + + UltimateServerMonitor( UltimateMonitorEntity origin ) + { + this.origin = origin; + } + + synchronized void rebuild() + { + Terminal oldTerm = getTerminal(); + int oldWidth = oldTerm == null ? -1 : oldTerm.getWidth(); + int oldHeight = oldTerm == null ? -1 : oldTerm.getHeight(); + + double textScale = this.textScale * 0.5; + int termWidth = (int) Math.max( + Math.round( (origin.getWidth() - 2.0 * (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN)) / (textScale * 6.0 * UltimateMonitorEntity.RENDER_PIXEL_SCALE) ), + 1.0 + ); + int termHeight = (int) Math.max( + Math.round( (origin.getHeight() - 2.0 * (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN)) / (textScale * 9.0 * UltimateMonitorEntity.RENDER_PIXEL_SCALE) ), + 1.0 + ); + + if( terminal == null ) + { + terminal = new UltimateNetworkedTerminal( termWidth, termHeight, this::markChanged ); + markChanged(); + } + else + { + terminal.resize( termWidth, termHeight ); + } + + if( oldWidth != termWidth || oldHeight != termHeight ) + { + terminal.clear(); + resized.set( true ); + markChanged(); + } + } + + private void markChanged() + { + if( !changed.getAndSet( true ) ) TickScheduler.schedule( origin.tickToken ); + } + + int getTextScale() + { + return textScale; + } + + synchronized void setTextScale( int textScale ) + { + if( this.textScale == textScale ) return; + this.textScale = textScale; + rebuild(); + } + + boolean pollResized() + { + return resized.getAndSet( false ); + } + + boolean pollTerminalChanged() + { + return changed.getAndSet( false ); + } + + @Nullable + @VisibleForTesting + public UltimateNetworkedTerminal getTerminal() + { + return terminal; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java index ff9b6f805..19a2d73d6 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java @@ -9,15 +9,12 @@ public class UltimateNetworkedTerminal extends NetworkedTerminal { private final int[] transparencies = new int[]{0xff, 0, 0, 0}; private final int[][] sideColors = new int[3][3]; - public UltimateNetworkedTerminal(int width, int height, int[] transparencies) { - this(width, height, transparencies, null); + public UltimateNetworkedTerminal(int width, int height) { + this(width, height, null); } - public UltimateNetworkedTerminal(int width, int height, int[] transparencies, Runnable changedCallback) { + public UltimateNetworkedTerminal(int width, int height, Runnable changedCallback) { super(width, height, true, changedCallback); - if (transparencies != null) { - System.arraycopy(transparencies, 0, this.transparencies, 0, 4); - } } public int getTransparency() { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java deleted file mode 100644 index cf9f8574b..000000000 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateServerMonitor.java +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package de.srendi.advancedperipherals.common.addons.computercraft.terminal; - -import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.peripheral.monitor.ServerMonitor; -import dan200.computercraft.shared.util.TickScheduler; -import de.srendi.advancedperipherals.common.blocks.blockentities.UltimateMonitorEntity; - -import org.jetbrains.annotations.Nullable; -import java.util.concurrent.atomic.AtomicBoolean; - -public class UltimateServerMonitor extends ServerMonitor { - private final UltimateMonitorEntity origin; - - private @Nullable UltimateNetworkedTerminal terminal; - private final AtomicBoolean resized = new AtomicBoolean(false); - - public UltimateServerMonitor(UltimateMonitorEntity origin) { - super(true, origin); - } - - @Override - synchronized void rebuild() { - Terminal oldTerm = getTerminal(); - var oldWidth = oldTerm == null ? -1 : oldTerm.getWidth(); - var oldHeight = oldTerm == null ? -1 : oldTerm.getHeight(); - - var textScale = this.getTextScale() * 0.5; - var termWidth = (int) Math.max( - (double) Math.round((origin.getWidth() - 2.0 * (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN)) / (textScale * 6.0 * UltimateMonitorEntity.RENDER_PIXEL_SCALE)), - 1.0 - ); - var termHeight = (int) Math.max( - (double) Math.round((origin.getHeight() - 2.0 * (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN)) / (textScale * 9.0 * UltimateMonitorEntity.RENDER_PIXEL_SCALE)), - 1.0 - ); - - if (terminal == null) { - terminal = new UltimateNetworkedTerminal(termWidth, termHeight, null, this::markChanged); - markChanged(); - } else { - terminal.resize(termWidth, termHeight); - } - - if (oldWidth != termWidth || oldHeight != termHeight) { - terminal.clear(); - resized.set(true); - markChanged(); - } - } - - @Override - synchronized void reset() { - if (terminal == null) return; - terminal = null; - markChanged(); - } - - @Override - boolean pollResized() { - return resized.getAndSet(false); - } - - @Nullable - @Override - public UltimateNetworkedTerminal getTerminal() { - return terminal; - } -} diff --git a/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java b/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java deleted file mode 100644 index c9f66a154..000000000 --- a/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/UltimateMonitorEntity.java +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. -// -// SPDX-License-Identifier: LicenseRef-CCPL - -package de.srendi.advancedperipherals.common.blocks.blockentities; - -import com.google.common.annotations.VisibleForTesting; -import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.UltimateMonitorPeripheral; -import de.srendi.advancedperipherals.common.blocks.base.PeripheralBlockEntity; -import de.srendi.advancedperipherals.common.setup.APBlockEntityTypes; -import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateServerMonitor; -import dan200.computercraft.annotations.ForgeOverride; -import dan200.computercraft.api.peripheral.IComputerAccess; -import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; -import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity; -import dan200.computercraft.shared.computer.terminal.TerminalState; -import dan200.computercraft.shared.config.Config; -import dan200.computercraft.shared.util.BlockEntityHelpers; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class UltimateMonitorEntity extends MonitorBlockEntity { - private static final Logger LOG = LoggerFactory.getLogger(UltimateMonitorEntity.class); - - private static final String NBT_X = "XIndex"; - private static final String NBT_Y = "YIndex"; - private static final String NBT_WIDTH = "Width"; - private static final String NBT_HEIGHT = "Height"; - - private @Nullable UltimateServerMonitor serverMonitor; - - /** - * The monitor's state on the client. This is defined iff we're the origin monitor - * ({@code xIndex == 0 && yIndex == 0}). - */ - private @Nullable ClientMonitor clientMonitor; - - private @Nullable MonitorPeripheral peripheral; - private final Set computers = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - private boolean needsUpdate = false; - private boolean needsValidating = false; - - private int width = 1; - private int height = 1; - private int xIndex = 0; - private int yIndex = 0; - - private @Nullable BlockPos bbPos; - private @Nullable BlockState bbState; - private int bbX, bbY, bbWidth, bbHeight; - private @Nullable AABB boundingBox; - - public UltimateMonitorEntity(BlockPos pos, BlockState state) { - super(APBlockEntityTypes.ULTIMATE_MONITOR.get(), pos, state); - } - - @Override - public void clearRemoved() { - super.clearRemoved(); - needsValidating = true; - } - - @Override - void destroy() { - // TODO: Call this before using the block - if (!getLevel().isClientSide) contractNeighbours(); - } - - @Override - public void setRemoved() { - super.setRemoved(); - if (clientMonitor != null) clientMonitor.destroy(); - } - - @Override - public void saveAdditional(CompoundTag tag) { - super.saveAdditional(tag); - tag.putInt(NBT_X, xIndex); - tag.putInt(NBT_Y, yIndex); - tag.putInt(NBT_WIDTH, width); - tag.putInt(NBT_HEIGHT, height); - } - - @Override - public void load(CompoundTag nbt) { - super.load(nbt); - - var oldXIndex = xIndex; - var oldYIndex = yIndex; - - xIndex = nbt.getInt(NBT_X); - yIndex = nbt.getInt(NBT_Y); - width = nbt.getInt(NBT_WIDTH); - height = nbt.getInt(NBT_HEIGHT); - - if (level != null && level.isClientSide) onClientLoad(oldXIndex, oldYIndex); - } - - @Override - void blockTick() { - if (needsValidating) { - needsValidating = false; - validate(); - } - - if (needsUpdate) { - needsUpdate = false; - expand(); - } - - if (xIndex != 0 || yIndex != 0 || serverMonitor == null) return; - - if (serverMonitor.pollResized()) eachComputer(c -> c.queueEvent("monitor_resize", c.getAttachmentName())); - if (serverMonitor.pollTerminalChanged()) MonitorWatcher.enqueue(this); - } - - @Override - @Nullable - @VisibleForTesting - public UltimateServerMonitor getCachedServerMonitor() { - return serverMonitor; - } - - @Nullable - private UltimateServerMonitor getServerMonitor() { - if (serverMonitor != null) return serverMonitor; - - var origin = getOrigin(); - if (origin == null) return null; - - return serverMonitor = origin.serverMonitor; - } - - @Nullable - private UltimateServerMonitor createServerMonitor() { - if (serverMonitor != null) return serverMonitor; - - if (xIndex == 0 && yIndex == 0) { - // If we're the origin, set up the new monitor - serverMonitor = new UltimateServerMonitor(this); - - // And propagate it to child monitors - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - var monitor = getLoadedMonitor(x, y).getMonitor(); - if (monitor != null) monitor.serverMonitor = serverMonitor; - } - } - - return serverMonitor; - } else { - // Otherwise fetch the origin and attempt to get its monitor - // Note this may load chunks, but we don't really have a choice here. - var te = getLevel().getBlockEntity(toWorldPos(0, 0)); - if (!(te instanceof UltimateMonitorEntity monitor)) return null; - - return serverMonitor = monitor.createServerMonitor(); - } - } - - private void createServerTerminal() { - var monitor = createServerMonitor(); - if (monitor != null && monitor.getTerminal() == null) monitor.rebuild(); - } - - @Override - @Nullable - public ClientMonitor getOriginClientMonitor() { - if (clientMonitor != null) return clientMonitor; - - var origin = getOrigin(); - return origin == null ? null : origin.clientMonitor; - } - - // Networking stuff - - @Override - public final ClientboundBlockEntityDataPacket getUpdatePacket() { - return ClientboundBlockEntityDataPacket.create(this); - } - - @Override - public final CompoundTag getUpdateTag() { - var nbt = super.getUpdateTag(); - nbt.putInt(NBT_X, xIndex); - nbt.putInt(NBT_Y, yIndex); - nbt.putInt(NBT_WIDTH, width); - nbt.putInt(NBT_HEIGHT, height); - return nbt; - } - - private void onClientLoad(int oldXIndex, int oldYIndex) { - if ((oldXIndex != xIndex || oldYIndex != yIndex) && clientMonitor != null) { - // If our index has changed, and we were the origin, then destroy the current monitor. - clientMonitor.destroy(); - clientMonitor = null; - } - - // If we're the origin terminal then create it. - if (xIndex == 0 && yIndex == 0 && clientMonitor == null) clientMonitor = new ClientMonitor(this); - } - - @Override - public final void read(@Nullable TerminalState state) { - if (xIndex != 0 || yIndex != 0) { - LOG.warn("Receiving monitor state for non-origin terminal at {}", getBlockPos()); - return; - } - - if (clientMonitor == null) clientMonitor = new ClientMonitor(this); - clientMonitor.read(state); - } - - // Sizing and placement stuff - - private void updateBlockState() { - getLevel().setBlock(getBlockPos(), getBlockState() - .setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections( - yIndex < height - 1, yIndex > 0, - xIndex > 0, xIndex < width - 1)), 2); - } - - // region Sizing and placement stuff - @Override - public Direction getDirection() { - // Ensure we're actually a monitor block. This _should_ always be the case, but sometimes there's - // fun problems with the block being missing on the client. - var state = getBlockState(); - return state.hasProperty(MonitorBlock.FACING) ? state.getValue(MonitorBlock.FACING) : Direction.NORTH; - } - - @Override - public Direction getOrientation() { - var state = getBlockState(); - return state.hasProperty(MonitorBlock.ORIENTATION) ? state.getValue(MonitorBlock.ORIENTATION) : Direction.NORTH; - } - - @Override - public Direction getFront() { - var orientation = getOrientation(); - return orientation == Direction.NORTH ? getDirection() : orientation; - } - - @Override - public Direction getRight() { - return getDirection().getCounterClockWise(); - } - - @Override - public Direction getDown() { - var orientation = getOrientation(); - if (orientation == Direction.NORTH) return Direction.UP; - return orientation == Direction.DOWN ? getDirection() : getDirection().getOpposite(); - } - - @Override - public int getWidth() { - return width; - } - - @Override - public int getHeight() { - return height; - } - - @Override - public int getXIndex() { - return xIndex; - } - - @Override - public int getYIndex() { - return yIndex; - } - - @Override - boolean isCompatible(MonitorEntity other) { - return other instanceof UltimateMonitorEntity && getOrientation() == other.getOrientation() && getDirection() == other.getDirection(); - } - - /** - * Get a tile within the current monitor only if it is loaded and compatible. - * - * @param x Absolute X position in monitor coordinates - * @param y Absolute Y position in monitor coordinates - * @return The located monitor - */ - private MonitorState getLoadedMonitor(int x, int y) { - if (x == xIndex && y == yIndex) return MonitorState.present(this); - var pos = toWorldPos(x, y); - - var world = getLevel(); - if (world == null || !world.isLoaded(pos)) return MonitorState.UNLOADED; - - var tile = world.getBlockEntity(pos); - if (!(tile instanceof UltimateMonitorEntity monitor)) return MonitorState.MISSING; - - return isCompatible(monitor) ? MonitorState.present(monitor) : MonitorState.MISSING; - } - - private @Nullable UltimateMonitorEntity getOrigin() { - return getLoadedMonitor(0, 0).getMonitor(); - } - - /** - * Convert monitor coordinates to world coordinates. - * - * @param x Absolute X position in monitor coordinates - * @param y Absolute Y position in monitor coordinates - * @return The monitor's position. - */ - @Override - BlockPos toWorldPos(int x, int y) { - if (xIndex == x && yIndex == y) return getBlockPos(); - return getBlockPos().relative(getRight(), -xIndex + x).relative(getDown(), -yIndex + y); - } - - @Override - void resize(int width, int height) { - // If we're not already the origin then we'll need to generate a new terminal. - if (xIndex != 0 || yIndex != 0) serverMonitor = null; - - xIndex = 0; - yIndex = 0; - this.width = width; - this.height = height; - - // Determine if we actually need a monitor. In order to do this, simply check if - // any component monitor been wrapped as a peripheral. Whilst this flag may be - // out of date, - var needsTerminal = false; - terminalCheck: - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - var monitor = getLoadedMonitor(x, y).getMonitor(); - if (monitor != null && monitor.peripheral != null) { - needsTerminal = true; - break terminalCheck; - } - } - } - - // Either delete the current monitor or sync a new one. - if (needsTerminal) { - if (serverMonitor == null) serverMonitor = new UltimateServerMonitor(this); - - // Update the terminal's width and height and rebuild it. This ensures the monitor - // is consistent when syncing it to other monitors. - serverMonitor.rebuild(); - } else { - // Remove the terminal from the serverMonitor, but keep it around - this ensures that we sync - // the (now blank) monitor to the client. - if (serverMonitor != null) serverMonitor.reset(); - } - - // Update the other monitors, setting coordinates, dimensions and the server terminal - var pos = getBlockPos(); - Direction down = getDown(), right = getRight(); - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - var other = getLevel().getBlockEntity(pos.relative(right, x).relative(down, y)); - if (!(other instanceof UltimateMonitorEntity monitor) || !isCompatible(monitor)) continue; - - monitor.xIndex = x; - monitor.yIndex = y; - monitor.width = width; - monitor.height = height; - monitor.serverMonitor = serverMonitor; - monitor.needsUpdate = monitor.needsValidating = false; - monitor.updateBlockState(); - BlockEntityHelpers.updateBlock(monitor); - } - } - - assertInvariant(); - } - - @Override - void updateNeighborsDeferred() { - needsUpdate = true; - } - - @Override - void expand() { - var monitor = getOrigin(); - if (monitor != null && monitor.xIndex == 0 && monitor.yIndex == 0) new Expander(monitor).expand(); - } - - private void contractNeighbours() { - if (width == 1 && height == 1) return; - - var pos = getBlockPos(); - Direction down = getDown(), right = getRight(); - var origin = toWorldPos(0, 0); - - UltimateMonitorEntity toLeft = null, toAbove = null, toRight = null, toBelow = null; - if (xIndex > 0) toLeft = tryResizeAt(pos.relative(right, -xIndex), xIndex, 1); - if (yIndex > 0) toAbove = tryResizeAt(origin, width, yIndex); - if (xIndex < width - 1) toRight = tryResizeAt(pos.relative(right, 1), width - xIndex - 1, 1); - if (yIndex < height - 1) { - toBelow = tryResizeAt(origin.relative(down, yIndex + 1), width, height - yIndex - 1); - } - - if (toLeft != null) toLeft.expand(); - if (toAbove != null) toAbove.expand(); - if (toRight != null) toRight.expand(); - if (toBelow != null) toBelow.expand(); - } - - @Nullable - private UltimateMonitorEntity tryResizeAt(BlockPos pos, int width, int height) { - var tile = getLevel().getBlockEntity(pos); - if (tile instanceof UltimateMonitorEntity monitor && isCompatible(monitor)) { - monitor.resize(width, height); - return monitor; - } - - return null; - } - - - private boolean checkMonitorAt(int xIndex, int yIndex) { - var state = getLoadedMonitor(xIndex, yIndex); - if (state.isMissing()) return false; - - var monitor = state.getMonitor(); - if (monitor == null) return true; - - return monitor.xIndex == xIndex && monitor.yIndex == yIndex && monitor.width == width && monitor.height == height; - } - - private void validate() { - if (xIndex == 0 && yIndex == 0 && width == 1 && height == 1) return; - - if (xIndex >= 0 && xIndex <= width && width > 0 && width <= Config.monitorWidth && - yIndex >= 0 && yIndex <= height && height > 0 && height <= Config.monitorHeight && - checkMonitorAt(0, 0) && checkMonitorAt(0, height - 1) && - checkMonitorAt(width - 1, 0) && checkMonitorAt(width - 1, height - 1)) { - return; - } - - // Something in our monitor is invalid. For now, let's just reset ourselves and then try to integrate ourselves - // later. - LOG.warn("Monitor is malformed, resetting to 1x1."); - resize(1, 1); - needsUpdate = true; - } - // endregion - - @Override - void monitorTouched(float xPos, float yPos, float zPos) { - this.monitorTouched(xPos, yPos, zPos, null); - } - - void monitorTouched(float xPos, float yPos, float zPos, @Nullable Player player) { - var pair = XYPair - .of(xPos, yPos, zPos, getDirection(), getOrientation()) - .add(xIndex, height - yIndex - 1); - - if (pair.x() > width - RENDER_BORDER || pair.y() > height - RENDER_BORDER || pair.x() < RENDER_BORDER || pair.y() < RENDER_BORDER) { - return; - } - - var serverTerminal = getServerMonitor(); - if (serverTerminal == null) return; - - Terminal originTerminal = serverTerminal.getTerminal(); - if (originTerminal == null) return; - - var xCharWidth = (width - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getWidth(); - var yCharHeight = (height - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getHeight(); - - var xCharPos = (int) Math.min(originTerminal.getWidth(), Math.max((pair.x() - RENDER_BORDER - RENDER_MARGIN) / xCharWidth + 1.0, 1.0)); - var yCharPos = (int) Math.min(originTerminal.getHeight(), Math.max((pair.y() - RENDER_BORDER - RENDER_MARGIN) / yCharHeight + 1.0, 1.0)); - - eachComputer(c -> c.queueEvent("monitor_touch", c.getAttachmentName(), xCharPos, yCharPos, player == null ? null : player.getName())); - } - - private void eachComputer(Consumer fun) { - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - var monitor = getLoadedMonitor(x, y).getMonitor(); - if (monitor == null) continue; - - for (var computer : monitor.computers) fun.accept(computer); - } - } - } - - @Override - public IPeripheral peripheral() { - createServerTerminal(); - var peripheral = this.peripheral != null ? this.peripheral : (this.peripheral = new MonitorPeripheral(this)); - assertInvariant(); - return peripheral; - } - - @Override - void addComputer(IComputerAccess computer) { - computers.add(computer); - } - - @Override - void removeComputer(IComputerAccess computer) { - computers.remove(computer); - } - - @Override - @ForgeOverride - public AABB getRenderBoundingBox() { - // We attempt to cache the bounding box to save having to do property lookups (and allocations!) on every frame. - // Unfortunately the AABB does depend on quite a lot of state, so we need to add a bunch of extra fields - - // ideally these'd be a single object, but I don't think worth doing until Java has value types. - if (boundingBox != null && getBlockState().equals(bbState) && getBlockPos().equals(bbPos) && - xIndex == bbX && yIndex == bbY && width == bbWidth && height == bbHeight) { - return boundingBox; - } - - bbState = getBlockState(); - bbPos = getBlockPos(); - bbX = xIndex; - bbY = yIndex; - bbWidth = width; - bbHeight = height; - - var startPos = toWorldPos(0, 0); - var endPos = toWorldPos(width, height); - return boundingBox = new AABB( - Math.min(startPos.getX(), endPos.getX()), - Math.min(startPos.getY(), endPos.getY()), - Math.min(startPos.getZ(), endPos.getZ()), - Math.max(startPos.getX(), endPos.getX()) + 1, - Math.max(startPos.getY(), endPos.getY()) + 1, - Math.max(startPos.getZ(), endPos.getZ()) + 1 - ); - } - - /** - * Assert all {@linkplain #checkInvariants() monitor invariants} hold. - */ - private void assertInvariant() { - assert checkInvariants() : "Monitor invariants failed. See logs."; - } - - /** - * Check various invariants about this monitor multiblock. This is only called when assertions are enabled, so - * will be skipped outside of tests. - * - * @return Whether all invariants passed. - */ - private boolean checkInvariants() { - LOG.debug("Checking monitor invariants at {}", getBlockPos()); - - var okay = true; - - if (width <= 0 || height <= 0) { - okay = false; - LOG.error("Monitor {} has non-positive of {}x{}", getBlockPos(), width, height); - } - - var hasPeripheral = false; - var origin = getOrigin(); - var serverMonitor = origin != null ? origin.serverMonitor : this.serverMonitor; - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - var monitor = getLoadedMonitor(x, y).getMonitor(); - if (monitor == null) continue; - - hasPeripheral |= monitor.peripheral != null; - - if (monitor.serverMonitor != null && monitor.serverMonitor != serverMonitor) { - okay = false; - LOG.error( - "Monitor {} expected to be have serverMonitor={}, but was {}", - monitor.getBlockPos(), serverMonitor, monitor.serverMonitor - ); - } - - if (monitor.xIndex != x || monitor.yIndex != y) { - okay = false; - LOG.error( - "Monitor {} expected to be at {},{}, but believes it is {},{}", - monitor.getBlockPos(), x, y, monitor.xIndex, monitor.yIndex - ); - } - - if (monitor.width != width || monitor.height != height) { - okay = false; - LOG.error( - "Monitor {} expected to be size {},{}, but believes it is {},{}", - monitor.getBlockPos(), width, height, monitor.width, monitor.height - ); - } - - var expectedState = getBlockState().setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections( - y < height - 1, y > 0, x > 0, x < width - 1 - )); - if (monitor.getBlockState() != expectedState) { - okay = false; - LOG.error( - "Monitor {} expected to have state {}, but has state {}", - monitor.getBlockState(), expectedState, monitor.getBlockState() - ); - } - } - } - - if (hasPeripheral != (serverMonitor != null && serverMonitor.getTerminal() != null)) { - okay = false; - LOG.error( - "Peripheral is {}, but serverMonitor={} and serverMonitor.terminal={}", - hasPeripheral, serverMonitor, serverMonitor == null ? null : serverMonitor.getTerminal() - ); - } - - return okay; - } -} diff --git a/src/main/java/de/srendi/advancedperipherals/common/data/BlockTagsProvider.java b/src/main/java/de/srendi/advancedperipherals/common/data/BlockTagsProvider.java index 0ccd4f85e..0273f2c74 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/data/BlockTagsProvider.java +++ b/src/main/java/de/srendi/advancedperipherals/common/data/BlockTagsProvider.java @@ -1,7 +1,9 @@ package de.srendi.advancedperipherals.common.data; +import dan200.computercraft.api.ComputerCraftTags; import de.srendi.advancedperipherals.AdvancedPeripherals; import de.srendi.advancedperipherals.common.blocks.base.IHarvestableBlock; +import de.srendi.advancedperipherals.common.setup.APBlocks; import net.minecraft.core.Registry; import net.minecraft.data.DataGenerator; import net.minecraft.data.tags.TagsProvider; @@ -33,6 +35,7 @@ public BlockTagsProvider(DataGenerator generator, @Nullable ExistingFileHelper e @Override protected void addTags() { + tag(ComputerCraftTags.Blocks.MONITOR).add(APBlocks.ULTIMATE_MONITOR.get()); blockRegistry.getEntries().stream().map(RegistryObject::get).forEach(block -> { if (!(block instanceof IHarvestableBlock harvesterBlock)) throw new IllegalArgumentException("For any block you should define harvester logic!"); diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java index f2c00760f..4256a4035 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlockEntityTypes.java @@ -2,6 +2,7 @@ import com.google.common.collect.Sets; import de.srendi.advancedperipherals.common.addons.APAddons; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateMonitorEntity; import de.srendi.advancedperipherals.common.blocks.blockentities.*; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraftforge.registries.RegistryObject; diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java index 842e6dd15..b3af9d58d 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java @@ -1,5 +1,6 @@ package de.srendi.advancedperipherals.common.setup; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateBlockMonitor; import de.srendi.advancedperipherals.common.blocks.PlayerDetectorBlock; import de.srendi.advancedperipherals.common.blocks.RedstoneIntegratorBlock; import de.srendi.advancedperipherals.common.blocks.base.APBlockEntityBlock; @@ -40,7 +41,7 @@ protected static void register() { public static final RegistryObject COLONY_INTEGRATOR = register("colony_integrator", () -> new APBlockEntityBlock<>(APBlockEntityTypes.COLONY_INTEGRATOR, false), () -> new APBlockItem(APBlocks.COLONY_INTEGRATOR.get(), APConfig.PERIPHERALS_CONFIG.enableColonyIntegrator)); public static final RegistryObject NBT_STORAGE = register("nbt_storage", () -> new APBlockEntityBlock<>(APBlockEntityTypes.NBT_STORAGE, false), () -> new APBlockItem(APBlocks.NBT_STORAGE.get(), APConfig.PERIPHERALS_CONFIG.enableNBTStorage)); public static final RegistryObject DISTANCE_DETECTOR = register("distance_detector", () -> new APBlockEntityBlock<>(APBlockEntityTypes.DISTANCE_DETECTOR, BlockBehaviour.Properties.of(Material.METAL).noOcclusion(), true), () -> new APBlockItem(APBlocks.DISTANCE_DETECTOR.get(), APConfig.PERIPHERALS_CONFIG.enableDistanceDetector)); - public static final RegistryObject ULTIMATE_MONITOR = register("ultimate_monitor", () -> new APBlockEntityBlock<>(APBlockEntityTypes.ULTIMATE_MONITOR, true), () -> new APBlockItem(APBlocks.ULTIMATE_MONITOR.get(), APConfig.PERIPHERALS_CONFIG.enableUltimateMonitor)); + public static final RegistryObject ULTIMATE_MONITOR = register("ultimate_monitor", () -> new UltimateBlockMonitor(BlockBehaviour.Properties.of(Material.BUILDABLE_GLASS).strength(4, 2).noOcclusion(), APBlockEntityTypes.ULTIMATE_MONITOR), () -> new APBlockItem(APBlocks.ULTIMATE_MONITOR.get(), APConfig.PERIPHERALS_CONFIG.enableUltimateMonitor)); private static RegistryObject registerNoItem(String name, Supplier block) { return APRegistration.BLOCKS.register(name, block); From 4251a8409fb331af4b9602de9d3689cfd4a7fd9f Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 25 Jul 2024 21:01:02 -0600 Subject: [PATCH 3/5] add ultimate monitor which support render transparency texts but it still have some bugs when rendering --- build.gradle | 14 +- .../67cce32b1c3cbbcb1f646605f4914e3f196986c2 | 35 +- .../b8526e444ae7356037f3a813274f6835d1f3dd16 | 21 +- .../c622617f6fabf890a00b9275cd5f643584a8a2c8 | 4 +- .../f95c7003282837dabaa33e3ffceec4e6865b5218 | 6 +- .../blockstates/ultimate_monitor.json | 964 ++++++++++++++++++ .../advancedperipherals/lang/en_us.json | 2 + .../models/block/ultimate_monitor.json | 9 + .../models/block/ultimate_monitor_d.json | 9 + .../models/block/ultimate_monitor_item.json | 9 + .../models/block/ultimate_monitor_l.json | 9 + .../models/block/ultimate_monitor_ld.json | 9 + .../models/block/ultimate_monitor_lr.json | 9 + .../models/block/ultimate_monitor_lrd.json | 9 + .../models/block/ultimate_monitor_lru.json | 9 + .../models/block/ultimate_monitor_lrud.json | 9 + .../models/block/ultimate_monitor_lu.json | 9 + .../models/block/ultimate_monitor_lud.json | 9 + .../models/block/ultimate_monitor_r.json | 9 + .../models/block/ultimate_monitor_rd.json | 9 + .../models/block/ultimate_monitor_ru.json | 9 + .../models/block/ultimate_monitor_rud.json | 9 + .../models/block/ultimate_monitor_u.json | 9 + .../models/block/ultimate_monitor_ud.json | 9 + .../models/item/ultimate_monitor.json | 3 + .../loot_tables/blocks/distance_detector.json | 6 + .../loot_tables/blocks/fluid_detector.json | 6 + .../loot_tables/blocks/gas_detector.json | 6 + .../loot_tables/blocks/ultimate_monitor.json | 26 + .../computercraft/tags/blocks/monitor.json | 5 + .../forge/tags/blocks/needs_gold_tool.json | 5 + .../tags/blocks/mineable/pickaxe.json | 3 +- .../client/ClientEventSubscriber.java | 56 +- .../client/renderer/RenderTypes.java | 55 +- .../renderer/UltimateMonitorRenderer.java | 142 ++- .../UltimateMonitorTextureBufferShader.java | 125 +++ .../text/DirectFixedWidthFontRenderer.java | 289 ++++++ .../monitor/UltimateBlockMonitor.java | 12 +- .../monitor/UltimateClientMonitor.java | 4 +- .../monitor/UltimateMonitorEntity.java | 20 +- .../monitor/UltimateMonitorPeripheral.java | 28 +- .../monitor/UltimateMonitorWatcher.java | 29 +- .../monitor/UltimateServerMonitor.java | 20 +- .../terminal/UltimateNetworkedTerminal.java | 111 +- .../terminal/UltimateTerminalState.java | 187 ++++ .../data/BlockStatesAndModelsProvider.java | 211 ++++ .../common/data/EnUsLanguageProvider.java | 3 +- .../common/network/APNetworking.java | 2 + .../toclient/UltimateMonitorClientPacket.java | 55 + .../common/setup/APBlocks.java | 8 +- .../models/block/monitor_base.json | 27 + .../shaders/core/monitor_tbo.fsh | 69 ++ .../shaders/core/monitor_tbo.json | 19 + .../shaders/core/monitor_tbo.vsh | 21 + .../textures/block/transparent.png | Bin 0 -> 423 bytes .../textures/block/ultimate_monitor_0.png | Bin 0 -> 195 bytes .../textures/block/ultimate_monitor_1.png | Bin 0 -> 186 bytes .../textures/block/ultimate_monitor_15.png | Bin 0 -> 182 bytes .../textures/block/ultimate_monitor_16.png | Bin 0 -> 193 bytes .../textures/block/ultimate_monitor_17.png | Bin 0 -> 238 bytes .../textures/block/ultimate_monitor_18.png | Bin 0 -> 178 bytes .../textures/block/ultimate_monitor_19.png | Bin 0 -> 233 bytes .../textures/block/ultimate_monitor_2.png | Bin 0 -> 224 bytes .../textures/block/ultimate_monitor_20.png | Bin 0 -> 175 bytes .../textures/block/ultimate_monitor_21.png | Bin 0 -> 208 bytes .../textures/block/ultimate_monitor_22.png | Bin 0 -> 242 bytes .../textures/block/ultimate_monitor_23.png | Bin 0 -> 191 bytes .../textures/block/ultimate_monitor_24.png | Bin 0 -> 117 bytes .../textures/block/ultimate_monitor_25.png | Bin 0 -> 191 bytes .../textures/block/ultimate_monitor_26.png | Bin 0 -> 155 bytes .../textures/block/ultimate_monitor_27.png | Bin 0 -> 75 bytes .../textures/block/ultimate_monitor_28.png | Bin 0 -> 155 bytes .../textures/block/ultimate_monitor_29.png | Bin 0 -> 204 bytes .../textures/block/ultimate_monitor_3.png | Bin 0 -> 186 bytes .../textures/block/ultimate_monitor_30.png | Bin 0 -> 145 bytes .../textures/block/ultimate_monitor_31.png | Bin 0 -> 198 bytes .../textures/block/ultimate_monitor_32.png | Bin 0 -> 217 bytes .../textures/block/ultimate_monitor_33.png | Bin 0 -> 212 bytes .../textures/block/ultimate_monitor_34.png | Bin 0 -> 202 bytes .../textures/block/ultimate_monitor_35.png | Bin 0 -> 210 bytes .../textures/block/ultimate_monitor_36.png | Bin 0 -> 199 bytes .../textures/block/ultimate_monitor_37.png | Bin 0 -> 188 bytes .../textures/block/ultimate_monitor_38.png | Bin 0 -> 212 bytes .../textures/block/ultimate_monitor_39.png | Bin 0 -> 203 bytes .../textures/block/ultimate_monitor_4.png | Bin 0 -> 217 bytes .../textures/block/ultimate_monitor_40.png | Bin 0 -> 234 bytes .../textures/block/ultimate_monitor_41.png | Bin 0 -> 202 bytes .../textures/block/ultimate_monitor_42.png | Bin 0 -> 235 bytes .../textures/block/ultimate_monitor_43.png | Bin 0 -> 179 bytes .../textures/block/ultimate_monitor_44.png | Bin 0 -> 221 bytes .../textures/block/ultimate_monitor_45.png | Bin 0 -> 189 bytes .../textures/block/ultimate_monitor_46.png | Bin 0 -> 224 bytes .../textures/block/ultimate_monitor_47.png | Bin 0 -> 186 bytes .../textures/block/ultimate_monitor_5.png | Bin 0 -> 212 bytes .../textures/block/ultimate_monitor_6.png | Bin 0 -> 188 bytes .../textures/block/ultimate_monitor_7.png | Bin 0 -> 199 bytes 96 files changed, 2560 insertions(+), 192 deletions(-) create mode 100644 src/generated/resources/assets/advancedperipherals/blockstates/ultimate_monitor.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_d.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_item.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_l.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ld.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lr.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrd.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lru.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrud.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lu.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lud.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_r.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rd.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ru.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rud.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_u.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ud.json create mode 100644 src/generated/resources/assets/advancedperipherals/models/item/ultimate_monitor.json create mode 100644 src/generated/resources/data/advancedperipherals/loot_tables/blocks/ultimate_monitor.json create mode 100644 src/generated/resources/data/computercraft/tags/blocks/monitor.json create mode 100644 src/generated/resources/data/forge/tags/blocks/needs_gold_tool.json create mode 100644 src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java create mode 100644 src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateTerminalState.java create mode 100644 src/main/java/de/srendi/advancedperipherals/common/network/toclient/UltimateMonitorClientPacket.java create mode 100644 src/main/resources/assets/advancedperipherals/models/block/monitor_base.json create mode 100644 src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh create mode 100644 src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.json create mode 100644 src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.vsh create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/transparent.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_0.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_1.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_15.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_16.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_17.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_18.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_19.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_2.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_20.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_21.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_22.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_23.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_24.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_25.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_26.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_27.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_28.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_29.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_3.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_30.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_31.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_32.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_33.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_34.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_35.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_36.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_37.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_38.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_39.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_4.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_40.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_41.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_42.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_43.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_44.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_45.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_46.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_47.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_5.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_6.png create mode 100644 src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_7.png diff --git a/build.gradle b/build.gradle index b176a7695..5829e0134 100644 --- a/build.gradle +++ b/build.gradle @@ -314,11 +314,11 @@ dependencies { compileOnly fg.deobf("com.ldtteam:domum_ornamentum:${domumornamentum_version}:universal") compileOnly fg.deobf("com.ldtteam:blockui:${blockui_version}") // IMPORTANT. This should be removed/commented when running `runData` - runtimeOnly fg.deobf("com.ldtteam:minecolonies:${minecolonies_version}") - runtimeOnly fg.deobf("com.ldtteam:structurize:${structurize_version}") - runtimeOnly fg.deobf("com.ldtteam:multipiston:${multipiston_version}") - runtimeOnly fg.deobf("com.ldtteam:domum_ornamentum:${domumornamentum_version}:universal") - runtimeOnly fg.deobf("com.ldtteam:blockui:${blockui_version}") + // runtimeOnly fg.deobf("com.ldtteam:minecolonies:${minecolonies_version}") + // runtimeOnly fg.deobf("com.ldtteam:structurize:${structurize_version}") + // runtimeOnly fg.deobf("com.ldtteam:multipiston:${multipiston_version}") + // runtimeOnly fg.deobf("com.ldtteam:domum_ornamentum:${domumornamentum_version}:universal") + // runtimeOnly fg.deobf("com.ldtteam:blockui:${blockui_version}") // Patchouli compileOnly fg.deobf("vazkii.patchouli:Patchouli:${patchouli_version}") @@ -331,8 +331,8 @@ dependencies { // DimStorage compileOnly fg.deobf("curse.maven:dimstorage-353882:${dimstorage_version}") compileOnly fg.deobf("curse.maven:edivadlib-638508:${edivadlib_version}") - runtimeOnly fg.deobf("curse.maven:dimstorage-353882:${dimstorage_version}") - runtimeOnly fg.deobf("curse.maven:edivadlib-638508:${edivadlib_version}") + // runtimeOnly fg.deobf("curse.maven:dimstorage-353882:${dimstorage_version}") + // runtimeOnly fg.deobf("curse.maven:edivadlib-638508:${edivadlib_version}") //Powah compileOnly fg.deobf("curse.maven:powah-633483:${powah_version}") diff --git a/src/generated/resources/.cache/67cce32b1c3cbbcb1f646605f4914e3f196986c2 b/src/generated/resources/.cache/67cce32b1c3cbbcb1f646605f4914e3f196986c2 index fe9b9c502..315f2e75d 100644 --- a/src/generated/resources/.cache/67cce32b1c3cbbcb1f646605f4914e3f196986c2 +++ b/src/generated/resources/.cache/67cce32b1c3cbbcb1f646605f4914e3f196986c2 @@ -1,17 +1,18 @@ -// 1.19.2 2024-05-28T14:53:16.656615 LootTables -d865e8ac35302c486faf5c7122569c554186186d data/advancedperipherals/loot_tables/blocks/block_reader.json -a6f896cc3dbd8da12737825ec71e32970f54025c data/advancedperipherals/loot_tables/blocks/chat_box.json -f50f506ae1987537f76be4c05a81689b25798f91 data/advancedperipherals/loot_tables/blocks/colony_integrator.json -4dcad851450e4ddd8d28a0017a417bf6e5579bda data/advancedperipherals/loot_tables/blocks/distance_detector.json -1d1b858d09538dc66bab2c33a2f9c58361a9c472 data/advancedperipherals/loot_tables/blocks/energy_detector.json -317004b1358ef9bf83957d2d6796529a226161c7 data/advancedperipherals/loot_tables/blocks/environment_detector.json -df6186990887f77b7db37ebb8ee50a7e7f096b0a data/advancedperipherals/loot_tables/blocks/fluid_detector.json -cea5a036d4ccdcc6ef026d1a226b5e13f1060676 data/advancedperipherals/loot_tables/blocks/gas_detector.json -d5a3964f518b138cbd7305c819a36af3d9340e4a data/advancedperipherals/loot_tables/blocks/geo_scanner.json -8aa1deea908fd02f049e047c8ca16679bbef68a2 data/advancedperipherals/loot_tables/blocks/inventory_manager.json -9a2898a63e2e0c087ce8eba211c63d1c8b9fd4aa data/advancedperipherals/loot_tables/blocks/me_bridge.json -973770040bacb61482adb9739fd1d977f16c107f data/advancedperipherals/loot_tables/blocks/nbt_storage.json -b4b80e8c9d62b53d9252cdfc5918c077d3a01f6e data/advancedperipherals/loot_tables/blocks/peripheral_casing.json -58b6adbea5d4ae43ed9af0b627df2b8a4907acde data/advancedperipherals/loot_tables/blocks/player_detector.json -a58aebcc52684302968e7af4f50a12aff93aaf2d data/advancedperipherals/loot_tables/blocks/redstone_integrator.json -0ef1678f88b8fcb744bfd95261f66745340be8f4 data/advancedperipherals/loot_tables/blocks/rs_bridge.json +// 1.19.2 2024-07-22T18:12:14.617845 LootTables +618b63c020ab64890c8a2d2506dd61cd30259a44 data/advancedperipherals/loot_tables/blocks/block_reader.json +0923665563d05307a7fa7d711a2d7a994a31eb6e data/advancedperipherals/loot_tables/blocks/chat_box.json +bf2a80256cfba0bd8c0283d493882e5816882f1f data/advancedperipherals/loot_tables/blocks/colony_integrator.json +cff4b81aa381bc0d1172b0a4c8bb35c1b954a018 data/advancedperipherals/loot_tables/blocks/distance_detector.json +1c5dbe1a8e07e040a3c2893a002cd535ee41dc20 data/advancedperipherals/loot_tables/blocks/energy_detector.json +8cc03eca1d191f725bc558836f26a85a466cb127 data/advancedperipherals/loot_tables/blocks/environment_detector.json +12589e7642b383029457d97a64637494ea8507a3 data/advancedperipherals/loot_tables/blocks/fluid_detector.json +421a3ece56485bd46374844639bfd606ce4665ec data/advancedperipherals/loot_tables/blocks/gas_detector.json +a2ae352dce564b878daecf47026f268dd432fbcf data/advancedperipherals/loot_tables/blocks/geo_scanner.json +807d449d02c0af701f84d3e806ebac20d3a1f91c data/advancedperipherals/loot_tables/blocks/inventory_manager.json +bb9b442dfad1d5f31397585c8394c85243fa169a data/advancedperipherals/loot_tables/blocks/me_bridge.json +52b1fe2e265be456ebf48d7e0d9ebcc8c994176f data/advancedperipherals/loot_tables/blocks/nbt_storage.json +2651e6053857a31f5bccc360969924848a197ad5 data/advancedperipherals/loot_tables/blocks/peripheral_casing.json +6b5058b6bc49689bf2b858b89f3981a4bb681750 data/advancedperipherals/loot_tables/blocks/player_detector.json +6af6542c7b64aa22dbfcdb01299daf4911827a49 data/advancedperipherals/loot_tables/blocks/redstone_integrator.json +b221c6a82da0875264a112b54f847ac6a1f36797 data/advancedperipherals/loot_tables/blocks/rs_bridge.json +e4e12e83f3fe31e9d7e751ebed1d68787af737c9 data/advancedperipherals/loot_tables/blocks/ultimate_monitor.json diff --git a/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 b/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 index 01ccc33ab..e07994eb6 100644 --- a/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 +++ b/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 @@ -1,4 +1,4 @@ -// 1.19.2 2024-05-28T14:53:16.658228 Block States: advancedperipherals +// 1.19.2 2024-07-22T22:10:50.227118 Block States: advancedperipherals 5e28ce1be9a6996d982641e5df1fa7162090b8cc assets/advancedperipherals/blockstates/block_reader.json f42bdde60f84fdb312f7cf3b2be461d9c11ebdc8 assets/advancedperipherals/blockstates/chat_box.json 1227aa092fcf1327547ace6ccc9db230e45891b0 assets/advancedperipherals/blockstates/colony_integrator.json @@ -15,6 +15,7 @@ d1fe6188b0b0ce8779cb9795a746177858cbaa41 assets/advancedperipherals/blockstates/ ff12c7217911184266589813a2c8f9b0d46cfd65 assets/advancedperipherals/blockstates/player_detector.json 726cf2599b0c765bcfacda88a1943be74f985877 assets/advancedperipherals/blockstates/redstone_integrator.json 6b176e8fdb048f7b6678bfbc1c4baf2bcfa67a1f assets/advancedperipherals/blockstates/rs_bridge.json +4b72ba2f7dfee76e138eea29f0b76c856456afca assets/advancedperipherals/blockstates/ultimate_monitor.json 544ff1ecb58622350b58940036b4b1908e1146da assets/advancedperipherals/models/block/block_reader.json fbaa69d6c98549d3f2d4a1c7bebd9b6b80d56621 assets/advancedperipherals/models/block/chat_box.json 68f9d37bd85649937150ba0bb8f4496bb2ef218d assets/advancedperipherals/models/block/colony_integrator.json @@ -31,3 +32,21 @@ f6cb0dda1ce8217563903d2dfaf5ef0297939750 assets/advancedperipherals/models/block 5a1679b4dcc8da2d8c67674216d242456bb51366 assets/advancedperipherals/models/block/player_detector.json d08b8946e1eb01cc9c8af4fa297b582614d1034b assets/advancedperipherals/models/block/redstone_integrator.json 41cf7d22016a995aeda9df9d9cbf1d4069b99f9e assets/advancedperipherals/models/block/rs_bridge.json +4d1d80c69bca9055b4cbb887f43ee6fce944d46f assets/advancedperipherals/models/block/ultimate_monitor.json +cce2709f323a71a029523533d7225dd6062b1243 assets/advancedperipherals/models/block/ultimate_monitor_d.json +db96f3c5231c785b10b37457ca104337c34acf7a assets/advancedperipherals/models/block/ultimate_monitor_item.json +1d704566dcc73c0a471a60c17e1139859f6b4087 assets/advancedperipherals/models/block/ultimate_monitor_l.json +03a49d3bebc2538c1bdbb21fc5bd3b96fe4e5be7 assets/advancedperipherals/models/block/ultimate_monitor_ld.json +987050c372fc907ae6dc9d759e693572da0d919a assets/advancedperipherals/models/block/ultimate_monitor_lr.json +1a13dca8e31d9b27f9391ed34fefa4dd1493d4cb assets/advancedperipherals/models/block/ultimate_monitor_lrd.json +9001072a7c861839313cf3e43b2628aad9a475ed assets/advancedperipherals/models/block/ultimate_monitor_lru.json +4d864eefd1a7522f5d0a73bcc35cf6557b2c421c assets/advancedperipherals/models/block/ultimate_monitor_lrud.json +82c5b1847ced8a2a2b9dd431e7a604fc03861bad assets/advancedperipherals/models/block/ultimate_monitor_lu.json +6ff46ed73c890105f2b24de9011e130658753ee1 assets/advancedperipherals/models/block/ultimate_monitor_lud.json +dbcfbec0493edfd68c64f7b0cf49157b1fded7e9 assets/advancedperipherals/models/block/ultimate_monitor_r.json +c0d0a974d391e336691058e09cb0f49c29a48c5f assets/advancedperipherals/models/block/ultimate_monitor_rd.json +63686a2dc96294e37d3129f0bb196caa77079712 assets/advancedperipherals/models/block/ultimate_monitor_ru.json +9116783ed78d59bccbf1333194ce1d633abe85d7 assets/advancedperipherals/models/block/ultimate_monitor_rud.json +c4752b33dfed1cce0d20c10ddd8250dd01fd7b3e assets/advancedperipherals/models/block/ultimate_monitor_u.json +14433cf178dc4e4c2e706f11704ef7aae46f3ae4 assets/advancedperipherals/models/block/ultimate_monitor_ud.json +fc1b1b273f7c7f9280402df88b0f8e31963f8978 assets/advancedperipherals/models/item/ultimate_monitor.json diff --git a/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 b/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 index ea5891f84..21fc917d5 100644 --- a/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 +++ b/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 @@ -1,2 +1,2 @@ -// 1.19.2 2024-05-28T14:53:16.658014 Languages: en_us -2580077212426421e05822910bde2edff88e2346 assets/advancedperipherals/lang/en_us.json +// 1.19.2 2024-07-22T18:16:05.152428 Languages: en_us +b6442b87d0f767417402f3a7c3e32438286f9ed4 assets/advancedperipherals/lang/en_us.json diff --git a/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 b/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 index 25f180a23..096763328 100644 --- a/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 +++ b/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 @@ -1,4 +1,6 @@ -// 1.19.2 2024-05-28T14:53:16.656337 Block tags +// 1.19.2 2024-07-22T18:12:14.620084 Block tags +cf893e68a6390f614a4c2c2e22f421f452754a81 data/computercraft/tags/blocks/monitor.json +cf893e68a6390f614a4c2c2e22f421f452754a81 data/forge/tags/blocks/needs_gold_tool.json e1f71dcb4f9e7e36e29b0ad09d6520dc3adfa4a6 data/forge/tags/blocks/needs_wood_tool.json -ef4684e10e5054e8cfd515dffa4a98169d281078 data/minecraft/tags/blocks/mineable/pickaxe.json +3816f0e77b104dc7cb9a3f5ac6e5c58ae645f13f data/minecraft/tags/blocks/mineable/pickaxe.json 8de9358ffeaa8d5f015774f70244a93b915427b8 data/minecraft/tags/blocks/needs_iron_tool.json diff --git a/src/generated/resources/assets/advancedperipherals/blockstates/ultimate_monitor.json b/src/generated/resources/assets/advancedperipherals/blockstates/ultimate_monitor.json new file mode 100644 index 000000000..3cbefee83 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/blockstates/ultimate_monitor.json @@ -0,0 +1,964 @@ +{ + "variants": { + "facing=east,orientation=down,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 90, + "y": 90 + }, + "facing=east,orientation=down,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 90, + "y": 90 + }, + "facing=east,orientation=north,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 0, + "y": 90 + }, + "facing=east,orientation=north,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 0, + "y": 90 + }, + "facing=east,orientation=up,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 270, + "y": 90 + }, + "facing=east,orientation=up,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 270, + "y": 90 + }, + "facing=north,orientation=down,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 90, + "y": 0 + }, + "facing=north,orientation=down,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 90, + "y": 0 + }, + "facing=north,orientation=north,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 0, + "y": 0 + }, + "facing=north,orientation=north,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 0, + "y": 0 + }, + "facing=north,orientation=up,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 270, + "y": 0 + }, + "facing=north,orientation=up,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 270, + "y": 0 + }, + "facing=south,orientation=down,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 90, + "y": 180 + }, + "facing=south,orientation=down,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 90, + "y": 180 + }, + "facing=south,orientation=north,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 0, + "y": 180 + }, + "facing=south,orientation=north,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 0, + "y": 180 + }, + "facing=south,orientation=up,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 270, + "y": 180 + }, + "facing=south,orientation=up,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 270, + "y": 180 + }, + "facing=west,orientation=down,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 90, + "y": 270 + }, + "facing=west,orientation=down,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 90, + "y": 270 + }, + "facing=west,orientation=north,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 0, + "y": 270 + }, + "facing=west,orientation=north,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 0, + "y": 270 + }, + "facing=west,orientation=up,state=d": { + "model": "advancedperipherals:block/ultimate_monitor_d", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=l": { + "model": "advancedperipherals:block/ultimate_monitor_l", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=ld": { + "model": "advancedperipherals:block/ultimate_monitor_ld", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=lr": { + "model": "advancedperipherals:block/ultimate_monitor_lr", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=lrd": { + "model": "advancedperipherals:block/ultimate_monitor_lrd", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=lru": { + "model": "advancedperipherals:block/ultimate_monitor_lru", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=lrud": { + "model": "advancedperipherals:block/ultimate_monitor_lrud", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=lu": { + "model": "advancedperipherals:block/ultimate_monitor_lu", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=lud": { + "model": "advancedperipherals:block/ultimate_monitor_lud", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=none": { + "model": "advancedperipherals:block/ultimate_monitor", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=r": { + "model": "advancedperipherals:block/ultimate_monitor_r", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=rd": { + "model": "advancedperipherals:block/ultimate_monitor_rd", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=ru": { + "model": "advancedperipherals:block/ultimate_monitor_ru", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=rud": { + "model": "advancedperipherals:block/ultimate_monitor_rud", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=u": { + "model": "advancedperipherals:block/ultimate_monitor_u", + "x": 270, + "y": 270 + }, + "facing=west,orientation=up,state=ud": { + "model": "advancedperipherals:block/ultimate_monitor_ud", + "x": 270, + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/lang/en_us.json b/src/generated/resources/assets/advancedperipherals/lang/en_us.json index 4533665e0..ada534ca4 100644 --- a/src/generated/resources/assets/advancedperipherals/lang/en_us.json +++ b/src/generated/resources/assets/advancedperipherals/lang/en_us.json @@ -32,6 +32,7 @@ "block.advancedperipherals.player_detector": "Player Detector", "block.advancedperipherals.redstone_integrator": "Redstone Integrator", "block.advancedperipherals.rs_bridge": "RS Bridge", + "block.advancedperipherals.ultimate_monitor": "Ultimate Monitor", "curios.identifier.glasses": "Glasses", "entity.minecraft.villager.advancedperipherals.advancedperipherals:computer_scientist": "Computer Scientist", "item.advancedperipherals.chunk_controller": "Chunk Controller", @@ -72,6 +73,7 @@ "item.advancedperipherals.tooltip.redstone_integrator": "&7This block is able to interact with redstone. Works exactly like the redstone api of an computer.", "item.advancedperipherals.tooltip.rs_bridge": "&7The RS Bridge interacts with Refined Storage to manage your items.", "item.advancedperipherals.tooltip.show_desc": "&b[&7%s&b] &7For Description", + "item.advancedperipherals.tooltip.ultimate_monitor": "&7A more advanced monitor. Can identify the operator and allow lights to pass through.", "item.advancedperipherals.tooltip.weak_automata_core": "&7Upgrade for turtles, which makes turtles more useful.", "item.advancedperipherals.weak_automata_core": "Weak Automata Core", "itemGroup.advancedperipheralstab": "Advanced Peripherals", diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor.json new file mode 100644 index 000000000..8123d1396 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_16", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_d.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_d.json new file mode 100644 index 000000000..4d8a6fb2f --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_d.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_20", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_item.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_item.json new file mode 100644 index 000000000..f0ffbc99b --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_item.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_15", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_l.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_l.json new file mode 100644 index 000000000..6cd80236d --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_l.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_19", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ld.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ld.json new file mode 100644 index 000000000..cd0537521 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ld.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_31", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lr.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lr.json new file mode 100644 index 000000000..5147808d9 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lr.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_18", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrd.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrd.json new file mode 100644 index 000000000..503301bc0 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrd.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_30", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lru.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lru.json new file mode 100644 index 000000000..04e1dc282 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lru.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_24", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrud.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrud.json new file mode 100644 index 000000000..b9a951475 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lrud.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_27", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lu.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lu.json new file mode 100644 index 000000000..63f57a3e1 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lu.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_25", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lud.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lud.json new file mode 100644 index 000000000..3e3d84a08 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_lud.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_28", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_r.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_r.json new file mode 100644 index 000000000..869fe696f --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_r.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_17", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rd.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rd.json new file mode 100644 index 000000000..4155d4d7e --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rd.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_29", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ru.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ru.json new file mode 100644 index 000000000..487d97be3 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ru.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_23", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rud.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rud.json new file mode 100644 index 000000000..0139f2e13 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_rud.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_26", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_u.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_u.json new file mode 100644 index 000000000..8befdeb4c --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_u.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_22", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ud.json b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ud.json new file mode 100644 index 000000000..524f5fa48 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/ultimate_monitor_ud.json @@ -0,0 +1,9 @@ +{ + "parent": "advancedperipherals:block/monitor_base", + "textures": { + "back": "advancedperipherals:block/transparent", + "front": "advancedperipherals:block/ultimate_monitor_21", + "side": "advancedperipherals:block/transparent", + "top": "advancedperipherals:block/transparent" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/models/item/ultimate_monitor.json b/src/generated/resources/assets/advancedperipherals/models/item/ultimate_monitor.json new file mode 100644 index 000000000..3b78001fa --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/item/ultimate_monitor.json @@ -0,0 +1,3 @@ +{ + "parent": "advancedperipherals:block/ultimate_monitor_item" +} \ No newline at end of file diff --git a/src/generated/resources/data/advancedperipherals/loot_tables/blocks/distance_detector.json b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/distance_detector.json index 21855bf71..4da55fb18 100644 --- a/src/generated/resources/data/advancedperipherals/loot_tables/blocks/distance_detector.json +++ b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/distance_detector.json @@ -11,6 +11,12 @@ "entries": [ { "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:copy_name", + "source": "block_entity" + } + ], "name": "advancedperipherals:distance_detector" } ], diff --git a/src/generated/resources/data/advancedperipherals/loot_tables/blocks/fluid_detector.json b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/fluid_detector.json index 731bc1f02..a57de0eab 100644 --- a/src/generated/resources/data/advancedperipherals/loot_tables/blocks/fluid_detector.json +++ b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/fluid_detector.json @@ -11,6 +11,12 @@ "entries": [ { "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:copy_name", + "source": "block_entity" + } + ], "name": "advancedperipherals:fluid_detector" } ], diff --git a/src/generated/resources/data/advancedperipherals/loot_tables/blocks/gas_detector.json b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/gas_detector.json index dadeb8bc5..be3a1af68 100644 --- a/src/generated/resources/data/advancedperipherals/loot_tables/blocks/gas_detector.json +++ b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/gas_detector.json @@ -11,6 +11,12 @@ "entries": [ { "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:copy_name", + "source": "block_entity" + } + ], "name": "advancedperipherals:gas_detector" } ], diff --git a/src/generated/resources/data/advancedperipherals/loot_tables/blocks/ultimate_monitor.json b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/ultimate_monitor.json new file mode 100644 index 000000000..70101b63b --- /dev/null +++ b/src/generated/resources/data/advancedperipherals/loot_tables/blocks/ultimate_monitor.json @@ -0,0 +1,26 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:copy_name", + "source": "block_entity" + } + ], + "name": "advancedperipherals:ultimate_monitor" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/computercraft/tags/blocks/monitor.json b/src/generated/resources/data/computercraft/tags/blocks/monitor.json new file mode 100644 index 000000000..d8b92db14 --- /dev/null +++ b/src/generated/resources/data/computercraft/tags/blocks/monitor.json @@ -0,0 +1,5 @@ +{ + "values": [ + "advancedperipherals:ultimate_monitor" + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/forge/tags/blocks/needs_gold_tool.json b/src/generated/resources/data/forge/tags/blocks/needs_gold_tool.json new file mode 100644 index 000000000..d8b92db14 --- /dev/null +++ b/src/generated/resources/data/forge/tags/blocks/needs_gold_tool.json @@ -0,0 +1,5 @@ +{ + "values": [ + "advancedperipherals:ultimate_monitor" + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json index 8f9cf40b9..5bc92ba71 100644 --- a/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json @@ -15,6 +15,7 @@ "advancedperipherals:geo_scanner", "advancedperipherals:colony_integrator", "advancedperipherals:nbt_storage", - "advancedperipherals:distance_detector" + "advancedperipherals:distance_detector", + "advancedperipherals:ultimate_monitor" ] } \ No newline at end of file diff --git a/src/main/java/de/srendi/advancedperipherals/client/ClientEventSubscriber.java b/src/main/java/de/srendi/advancedperipherals/client/ClientEventSubscriber.java index 8b3d01dac..5c18365ae 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/ClientEventSubscriber.java +++ b/src/main/java/de/srendi/advancedperipherals/client/ClientEventSubscriber.java @@ -1,23 +1,35 @@ package de.srendi.advancedperipherals.client; +import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.systems.RenderSystem; import de.srendi.advancedperipherals.AdvancedPeripherals; import de.srendi.advancedperipherals.common.entity.TurtleSeatEntity; import de.srendi.advancedperipherals.common.network.APNetworking; import de.srendi.advancedperipherals.common.network.toserver.SaddleTurtleControlPacket; import net.minecraft.client.Minecraft; +import net.minecraft.client.Camera; import net.minecraft.client.player.Input; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.InputEvent; import net.minecraftforge.client.event.MovementInputUpdateEvent; import net.minecraftforge.client.event.RenderGuiOverlayEvent; +import net.minecraftforge.client.event.RenderLevelStageEvent; import net.minecraftforge.client.gui.overlay.VanillaGuiOverlay; import net.minecraftforge.event.entity.EntityMountEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; +import java.util.ArrayList; +import java.util.List; + @Mod.EventBusSubscriber(modid = AdvancedPeripherals.MOD_ID, value = Dist.CLIENT) public class ClientEventSubscriber { + public static final Minecraft MINECRAFT = Minecraft.getInstance(); + @SubscribeEvent public static void renderingHuds(RenderGuiOverlayEvent.Pre event) { if (ClientRegistry.SADDLE_TURTLE_OVERLAY.shouldRenderFuelBar() && event.getOverlay().id().equals(VanillaGuiOverlay.EXPERIENCE_BAR.id())) { @@ -30,15 +42,14 @@ public static void renderingHuds(RenderGuiOverlayEvent.Pre event) { @SubscribeEvent public static void playerTryDismount(InputEvent.Key event) { - Minecraft minecraft = Minecraft.getInstance(); - if (!minecraft.options.keyShift.matches(event.getKey(), event.getScanCode())) { + if (!MINECRAFT.options.keyShift.matches(event.getKey(), event.getScanCode())) { return; } switch (event.getAction()) { case InputConstants.PRESS: sneaking = true; if (ClientRegistry.SADDLE_TURTLE_OVERLAY.isPlayerMountedOnTurtle()) { - minecraft.options.keyShift.setDown(false); + MINECRAFT.options.keyShift.setDown(false); } break; case InputConstants.RELEASE: @@ -52,7 +63,7 @@ public static void playerTryDismount(InputEvent.Key event) { @SubscribeEvent public static void playerMounting(EntityMountEvent event) { - if (event.isMounting() && event.getEntityMounting() == Minecraft.getInstance().player && event.getEntityBeingMounted() instanceof TurtleSeatEntity) { + if (event.isMounting() && event.getEntityMounting() == MINECRAFT.player && event.getEntityBeingMounted() instanceof TurtleSeatEntity) { // clear last key records lastInput.up = false; lastInput.down = false; @@ -81,4 +92,41 @@ public static void playerMove(MovementInputUpdateEvent event) { APNetworking.sendToServer(new SaddleTurtleControlPacket(input.up, input.down, input.left, input.right, input.jumping, sneaking)); } } + + private static final List renderers = new ArrayList<>(); + + public static void addRenderer(LazyRenderer r) { + renderers.add(r); + } + + // Reference https://github.com/mekanism/Mekanism/blob/1.19.x/src/main/java/mekanism/client/render/RenderTickHandler.java#L152 + @SubscribeEvent + public static void renderWorld(RenderLevelStageEvent event) { + if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) { + if (renderers.isEmpty()) { + return; + } + Camera camera = event.getCamera(); + PoseStack matrix = event.getPoseStack(); + MultiBufferSource buffer = MINECRAFT.renderBuffers().bufferSource(); + float partialTicks = event.getPartialTick(); + + matrix.pushPose(); + // here we translate based on the inverse position of the client viewing camera to get back to 0, 0, 0 + Vec3 camVec = camera.getPosition(); + matrix.translate(-camVec.x, -camVec.y, -camVec.z); + + renderers.forEach((r) -> { + r.render(partialTicks, matrix, buffer); + }); + renderers.clear(); + + matrix.popPose(); + } + } + + public static interface LazyRenderer { + void render(float partialTicks, PoseStack transform, MultiBufferSource bufferSource); + Vec3 getCenterPos(float partialTicks); + } } diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java index 940950cdb..0716ec02e 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/RenderTypes.java @@ -8,7 +8,6 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormat; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.client.render.MonitorTextureBufferShader; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import de.srendi.advancedperipherals.AdvancedPeripherals; import net.minecraft.client.renderer.GameRenderer; @@ -24,22 +23,21 @@ import javax.annotation.Nonnull; import java.io.IOException; -@Mod.EventBusSubscriber( modid = AdvancedPeripherals.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD ) -public class RenderTypes -{ +@Mod.EventBusSubscriber(modid = AdvancedPeripherals.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) +public class RenderTypes { public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20); - private static MonitorTextureBufferShader monitorTboShader; + private static UltimateMonitorTextureBufferShader monitorTboShader; /** * Renders a fullbright terminal. */ - public static final RenderType TERMINAL = RenderType.text( FixedWidthFontRenderer.FONT ); + public static final RenderType TERMINAL = Types.TERMINAL; /** * Renders a monitor with the TBO shader. * - * @see MonitorTextureBufferShader + * @see UltimateMonitorTextureBufferShader */ public static final RenderType MONITOR_TBO = Types.MONITOR_TBO; @@ -52,10 +50,10 @@ public class RenderTypes * Printout's background texture. {@link RenderType#text(ResourceLocation)} is a little questionable, but * it is what maps use, so should behave the same as vanilla in both item frames and in-hand. */ - public static final RenderType PRINTOUT_BACKGROUND = RenderType.text( new ResourceLocation( "computercraft", "textures/gui/printout.png" ) ); + public static final RenderType PRINTOUT_BACKGROUND = RenderType.text( new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/printout.png" ) ); @Nonnull - static MonitorTextureBufferShader getMonitorTextureBufferShader() + static UltimateMonitorTextureBufferShader getMonitorTextureBufferShader() { if( monitorTboShader == null ) throw new NullPointerException( "MonitorTboShader has not been registered" ); return monitorTboShader; @@ -71,34 +69,49 @@ static ShaderInstance getTerminalShader() public static void registerShaders( RegisterShadersEvent event ) throws IOException { event.registerShader( - new MonitorTextureBufferShader( + new UltimateMonitorTextureBufferShader( event.getResourceManager(), - new ResourceLocation( ComputerCraft.MOD_ID, "monitor_tbo" ), + new ResourceLocation( AdvancedPeripherals.MOD_ID, "monitor_tbo" ), MONITOR_TBO.format() ), - x -> monitorTboShader = (MonitorTextureBufferShader) x + x -> monitorTboShader = (UltimateMonitorTextureBufferShader) x ); } - private static final class Types extends RenderStateShard - { + private static final class Types extends RenderStateShard { private static final RenderStateShard.TextureStateShard TERM_FONT_TEXTURE = new TextureStateShard( FixedWidthFontRenderer.FONT, false, false // blur, minimap ); + public static final RenderType TERMINAL = RenderType.create( + "text", + DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, + VertexFormat.Mode.QUADS, + 256, + false, + true, + RenderType.CompositeState.builder() + .setShaderState(RENDERTYPE_TEXT_SHADER) + .setTextureState(TERM_FONT_TEXTURE) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setLightmapState(LIGHTMAP) + .createCompositeState(true) + ); + static final RenderType MONITOR_TBO = RenderType.create( "monitor_tbo", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.TRIANGLE_STRIP, 128, - false, false, // useDelegate, needsSorting + true, true, // useDelegate, needsSorting RenderType.CompositeState.builder() - .setTextureState( TERM_FONT_TEXTURE ) - .setShaderState( new ShaderStateShard( RenderTypes::getMonitorTextureBufferShader ) ) - .createCompositeState( false ) + .setTextureState(TERM_FONT_TEXTURE) + .setShaderState(new ShaderStateShard(RenderTypes::getMonitorTextureBufferShader)) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setOutputState(TRANSLUCENT_TARGET) + .createCompositeState(true) ); - private Types( String name, Runnable setup, Runnable destroy ) - { - super( name, setup, destroy ); + private Types(String name, Runnable setup, Runnable destroy) { + super(name, setup, destroy); } } } diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java index 44aa29230..8f3836285 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java @@ -14,14 +14,14 @@ import com.mojang.math.Vector3f; import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.FrameInfo; -import dan200.computercraft.client.render.MonitorTextureBufferShader; -import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import dan200.computercraft.client.util.DirectBuffers; import dan200.computercraft.client.util.DirectVertexBuffer; import dan200.computercraft.shared.integration.ShaderMod; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.util.DirectionUtil; +import de.srendi.advancedperipherals.client.ClientEventSubscriber; +import de.srendi.advancedperipherals.client.renderer.text.DirectFixedWidthFontRenderer; import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateClientMonitor; import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateMonitorEntity; import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; @@ -31,6 +31,7 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.phys.Vec3; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL20; @@ -39,14 +40,12 @@ import javax.annotation.Nonnull; import java.nio.ByteBuffer; import java.util.function.Consumer; +import java.util.function.IntFunction; import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; -public class UltimateMonitorRenderer implements BlockEntityRenderer -{ - - static final int TEXTURE_INDEX = GL13.GL_TEXTURE3; // export from dan200.computercraft.client.render.MonitorTextureBufferShader +public class UltimateMonitorRenderer implements BlockEntityRenderer { /** * {@link UltimateMonitorEntity#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between @@ -58,26 +57,25 @@ public class UltimateMonitorRenderer implements BlockEntityRenderer - DirectFixedWidthFontRenderer.drawTerminalBackground( sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin ) ); + DirectFixedWidthFontRenderer.drawTerminalBackground( sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin, + terminal.getBackgroundTransparency() ) ); renderToBuffer( foregroundBuffer, size, sink -> { - DirectFixedWidthFontRenderer.drawTerminalForeground( sink, 0, 0, terminal ); + DirectFixedWidthFontRenderer.drawTerminalForeground( sink, 0, 0, terminal, terminal.getTextTransparency() ); // If the cursor is visible, we append it to the end of our buffer. When rendering, we can either // render n or n+1 quads and so toggle the cursor on and off. - DirectFixedWidthFontRenderer.drawCursor( sink, 0, 0, terminal ); + DirectFixedWidthFontRenderer.drawCursor( sink, 0, 0, terminal, terminal.getTextTransparency() ); } ); } @@ -245,39 +242,38 @@ private static void renderTerminal( Matrix4f matrix, UltimateClientMonitor monit } } - private static void renderToBuffer( DirectVertexBuffer vbo, int size, Consumer draw ) - { - var sink = ShaderMod.INSTANCE.getQuadEmitter( size, UltimateMonitorRenderer::getBuffer ); + private static DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(int vertexCount, IntFunction makeBuffer) { + return new DirectFixedWidthFontRenderer.ByteBufferEmitter( + makeBuffer.apply(RenderTypes.TERMINAL.format().getVertexSize() * vertexCount * 4) + ); + } + + private static void renderToBuffer(DirectVertexBuffer vbo, int size, Consumer draw) { + var sink = getQuadEmitter(size, UltimateMonitorRenderer::getBuffer); var buffer = sink.buffer(); - draw.accept( sink ); + draw.accept(sink); buffer.flip(); - vbo.upload( buffer.limit() / sink.format().getVertexSize(), RenderTypes.TERMINAL.mode(), sink.format(), buffer ); + vbo.upload(buffer.limit() / sink.format().getVertexSize(), RenderTypes.TERMINAL.mode(), sink.format(), buffer); } - private static void tboVertex( VertexConsumer builder, Matrix4f matrix, float x, float y ) - { + private static void tboVertex(VertexConsumer builder, Matrix4f matrix, float x, float y) { // We encode position in the UV, as that's not transformed by the matrix. - builder.vertex( matrix, x, y, 0 ).uv( x, y ).endVertex(); + builder.vertex(matrix, x, y, 0).uv(x, y).endVertex(); } @Nonnull - private static ByteBuffer getBuffer( int capacity ) - { - + private static ByteBuffer getBuffer(int capacity) { ByteBuffer buffer = backingBuffer; - if( buffer == null || buffer.capacity() < capacity ) - { - buffer = backingBuffer = buffer == null ? MemoryTracker.create( capacity ) : MemoryTracker.resize( buffer, capacity ); + if (buffer == null || buffer.capacity() < capacity) { + buffer = backingBuffer = buffer == null ? MemoryTracker.create(capacity) : MemoryTracker.resize(buffer, capacity); } - buffer.clear(); return buffer; } @Override - public int getViewDistance() - { + public int getViewDistance() { return ComputerCraft.monitorDistance; } } diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java new file mode 100644 index 000000000..016d22432 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java @@ -0,0 +1,125 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.client.renderer; + +import com.mojang.blaze3d.shaders.Uniform; +import com.mojang.blaze3d.vertex.VertexFormat; +import dan200.computercraft.client.FrameInfo; +import dan200.computercraft.client.render.text.FixedWidthFontRenderer; +import dan200.computercraft.core.terminal.TextBuffer; +import dan200.computercraft.shared.util.Colour; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceProvider; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL31; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.nio.ByteBuffer; + +import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour; + +public class UltimateMonitorTextureBufferShader extends ShaderInstance +{ + public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4 + 4 + 4; + + static final int TEXTURE_INDEX = GL13.GL_TEXTURE3; + + private static final Logger LOGGER = LogManager.getLogger(); + + private final int monitorData; + private int uniformBuffer = 0; + + private final Uniform cursorBlink; + + public UltimateMonitorTextureBufferShader( ResourceProvider provider, ResourceLocation location, VertexFormat format ) throws IOException + { + super( provider, location, format ); + monitorData = GL31.glGetUniformBlockIndex( getId(), "MonitorData" ); + if( monitorData == -1 ) throw new IllegalStateException( "Could not find MonitorData uniform." ); + + cursorBlink = getUniformChecked( "CursorBlink" ); + + Uniform tbo = getUniformChecked( "Tbo" ); + if( tbo != null ) tbo.set( TEXTURE_INDEX - GL13.GL_TEXTURE0 ); + } + + public void setupUniform( int buffer ) + { + uniformBuffer = buffer; + + int cursorAlpha = FrameInfo.getGlobalCursorBlink() ? 1 : 0; + if( cursorBlink != null && cursorBlink.getIntBuffer().get( 0 ) != cursorAlpha ) cursorBlink.set( cursorAlpha ); + } + + @Override + public void apply() + { + super.apply(); + GL31.glBindBufferBase( GL31.GL_UNIFORM_BUFFER, monitorData, uniformBuffer ); + } + + @Nullable + private Uniform getUniformChecked( String name ) + { + Uniform uniform = getUniform( name ); + if( uniform == null ) + { + LOGGER.warn( "Monitor shader {} should have uniform {}, but it was not present.", getName(), name ); + } + + return uniform; + } + + public static void setTerminalData( ByteBuffer buffer, UltimateNetworkedTerminal terminal ) + { + int width = terminal.getWidth(), height = terminal.getHeight(); + + int pos = 0; + for( int y = 0; y < height; y++ ) + { + TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y ); + for( int x = 0; x < width; x++ ) + { + buffer.put( pos, (byte) (text.charAt( x ) & 0xFF) ); + buffer.put( pos + 1, (byte) getColour( textColour.charAt( x ), Colour.WHITE ) ); + buffer.put( pos + 2, (byte) getColour( background.charAt( x ), Colour.BLACK ) ); + pos += 3; + } + } + + buffer.limit( pos ); + } + + public static void setUniformData( ByteBuffer buffer, UltimateNetworkedTerminal terminal ) + { + int pos = 0; + var palette = terminal.getPalette(); + for( int i = 0; i < 16; i++ ) + { + double[] colour = palette.getColour( i ); + buffer.putFloat( pos, (float) colour[0] ).putFloat( pos + 4, (float) colour[1] ).putFloat( pos + 8, (float) colour[2] ) + .putFloat(pos + 12, 0.5f); + + pos += 4 * 4; // std140 requires these are 4-wide + } + + boolean showCursor = FixedWidthFontRenderer.isCursorVisible( terminal ); + buffer + .putInt( pos, terminal.getWidth() ).putInt( pos + 4, terminal.getHeight() ) + .putInt( pos + 8, showCursor ? terminal.getCursorX() : -2 ) + .putInt( pos + 12, showCursor ? terminal.getCursorY() : -2 ) + .putInt( pos + 16, 15 - terminal.getTextColour() ) + .putFloat(pos + 20, terminal.getTextTransparency() / 255.0f) + .putFloat(pos + 24, terminal.getBackgroundTransparency() / 255.0f); + + buffer.limit( UNIFORM_SIZE ); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java new file mode 100644 index 000000000..2762589c1 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java @@ -0,0 +1,289 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.client.renderer.text; + +import com.mojang.blaze3d.platform.MemoryTracker; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; +import dan200.computercraft.client.render.RenderTypes; +import dan200.computercraft.client.render.text.FixedWidthFontRenderer; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.core.terminal.TextBuffer; +import dan200.computercraft.shared.util.Colour; +import dan200.computercraft.shared.util.Palette; +import org.lwjgl.system.MemoryUtil; + +import javax.annotation.Nonnull; +import java.nio.ByteBuffer; + +import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*; +import static org.lwjgl.system.MemoryUtil.*; + +/** + * An optimised copy of {@link FixedWidthFontRenderer} emitter emits directly to a {@link QuadEmitter} rather than + * emitting to {@link VertexConsumer}. This allows us to emit vertices very quickly, when using the VBO renderer. + *

+ * There are some limitations here: + *

    + *
  • No transformation matrix (not needed for VBOs).
  • + *
  • Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX_LIGHTMAP}.
  • + *
  • The buffer MUST be allocated with {@link MemoryTracker}, and not through any other means.
  • + *
+ *

+ * Note this is almost an exact copy of {@link FixedWidthFontRenderer}. While the code duplication is unfortunate, + * it is measurably faster than introducing polymorphism into {@link FixedWidthFontRenderer}. + * + * IMPORTANT: When making changes to this class, please check if you need to make the same changes to + * {@link FixedWidthFontRenderer}. + */ +public final class DirectFixedWidthFontRenderer +{ + static final float WIDTH = 256.0f; + static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; + static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; + + private DirectFixedWidthFontRenderer() + { + } + + private static void drawChar( QuadEmitter emitter, float x, float y, int index, byte[] colour, int alpha ) + { + // Short circuit to avoid the common case - the texture should be blank here after all. + if( index == '\0' || index == ' ' ) return; + + int column = index % 16; + int row = index / 16; + + int xStart = 1 + column * (FONT_WIDTH + 2); + int yStart = 1 + row * (FONT_HEIGHT + 2); + + quad( + emitter, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour, + xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, + (byte) alpha + ); + } + + private static void drawQuad( QuadEmitter emitter, float x, float y, float width, float height, Palette palette, char colourIndex, int alpha ) + { + byte[] colour = palette.getRenderColours( getColour( colourIndex, Colour.BLACK ) ); + quad( emitter, x, y, x + width, y + height, 0f, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, alpha ); + } + + private static void drawBackground( + @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, + float leftMarginSize, float rightMarginSize, float height, int alpha + ) + { + if( leftMarginSize > 0 ) + { + drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, backgroundColour.charAt( 0 ), alpha ); + } + + if( rightMarginSize > 0 ) + { + drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, backgroundColour.charAt( backgroundColour.length() - 1 ), alpha ); + } + + // Batch together runs of identical background cells. + int blockStart = 0; + char blockColour = '\0'; + for( int i = 0; i < backgroundColour.length(); i++ ) + { + char colourIndex = backgroundColour.charAt( i ); + if( colourIndex == blockColour ) continue; + + if( blockColour != '\0' ) + { + drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, blockColour, alpha ); + } + + blockColour = colourIndex; + blockStart = i; + } + + if( blockColour != '\0' ) + { + drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, blockColour, alpha ); + } + } + + public static void drawString( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, int alpha ) + { + for( int i = 0; i < text.length(); i++ ) + { + byte[] colour = palette.getRenderColours( getColour( textColour.charAt( i ), Colour.BLACK ) ); + + int index = text.charAt( i ); + if( index > 255 ) index = '?'; + drawChar( emitter, x + i * FONT_WIDTH, y, index, colour, alpha ); + } + + } + + public static void drawTerminalForeground( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, int alpha ) + { + Palette palette = terminal.getPalette(); + int height = terminal.getHeight(); + + // The main text + for( int i = 0; i < height; i++ ) + { + float rowY = y + FONT_HEIGHT * i; + drawString( + emitter, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ), + palette, alpha + ); + } + } + + public static void drawTerminalBackground( + @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize, + int alpha + ) + { + Palette palette = terminal.getPalette(); + int height = terminal.getHeight(); + + // Top and bottom margins + drawBackground( + emitter, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, + leftMarginSize, rightMarginSize, topMarginSize, alpha + ); + + drawBackground( + emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, + leftMarginSize, rightMarginSize, bottomMarginSize, alpha + ); + + // The main text + for( int i = 0; i < height; i++ ) + { + float rowY = y + FONT_HEIGHT * i; + drawBackground( + emitter, x, rowY, terminal.getBackgroundColourLine( i ), palette, + leftMarginSize, rightMarginSize, FONT_HEIGHT, alpha + ); + } + } + + public static void drawCursor( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, int alpha ) + { + if( isCursorVisible( terminal ) ) + { + byte[] colour = terminal.getPalette().getRenderColours( 15 - terminal.getTextColour() ); + drawChar( emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, alpha ); + } + } + + public static int getVertexCount( Terminal terminal ) + { + return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2; + } + + private static void quad( QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ) + { + buffer.quad( x1, y1, x2, y2, z, rgba, u1, v1, u2, v2, alpha ); + } + + public interface QuadEmitter + { + VertexFormat format(); + + ByteBuffer buffer(); + + void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ); + } + + public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter + { + @Override + public VertexFormat format() + { + return RenderTypes.TERMINAL.format(); + } + + @Override + public void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ) + { + DirectFixedWidthFontRenderer.quad( buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2, alpha ); + } + } + + static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ) + { + // Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the + // underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write. + // This provides significant performance gains, at the cost of well, using Unsafe. + // Each vertex is 28 bytes, giving 112 bytes in total. Vertices are of the form (xyz:FFF)(rgba:BBBB)(uv1:FF)(uv2:SS), + // which matches the POSITION_COLOR_TEX_LIGHTMAP vertex format. + + int position = buffer.position(); + long addr = MemoryUtil.memAddress( buffer ); + + // We're doing terrible unsafe hacks below, so let's be really sure that what we're doing is reasonable. + if( position < 0 || 112 > buffer.limit() - position ) throw new IndexOutOfBoundsException(); + // Require the pointer to be aligned to a 32-bit boundary. + if( (addr & 3) != 0 ) throw new IllegalStateException( "Memory is not aligned" ); + // Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances. + if( rgba.length != 4 ) throw new IllegalStateException(); + + memPutFloat( addr + 0, x1 ); + memPutFloat( addr + 4, y1 ); + memPutFloat( addr + 8, z ); + memPutByte( addr + 12, rgba[0] ); + memPutByte( addr + 13, rgba[1] ); + memPutByte( addr + 14, rgba[2] ); + memPutByte( addr + 15, (byte) alpha ); + memPutFloat( addr + 16, u1 ); + memPutFloat( addr + 20, v1 ); + memPutShort( addr + 24, (short) 0xF0 ); + memPutShort( addr + 26, (short) 0xF0 ); + + memPutFloat( addr + 28, x1 ); + memPutFloat( addr + 32, y2 ); + memPutFloat( addr + 36, z ); + memPutByte( addr + 40, rgba[0] ); + memPutByte( addr + 41, rgba[1] ); + memPutByte( addr + 42, rgba[2] ); + memPutByte( addr + 43, (byte) alpha ); + memPutFloat( addr + 44, u1 ); + memPutFloat( addr + 48, v2 ); + memPutShort( addr + 52, (short) 0xF0 ); + memPutShort( addr + 54, (short) 0xF0 ); + + memPutFloat( addr + 56, x2 ); + memPutFloat( addr + 60, y2 ); + memPutFloat( addr + 64, z ); + memPutByte( addr + 68, rgba[0] ); + memPutByte( addr + 69, rgba[1] ); + memPutByte( addr + 70, rgba[2] ); + memPutByte( addr + 71, (byte) alpha ); + memPutFloat( addr + 72, u2 ); + memPutFloat( addr + 76, v2 ); + memPutShort( addr + 80, (short) 0xF0 ); + memPutShort( addr + 82, (short) 0xF0 ); + + memPutFloat( addr + 84, x2 ); + memPutFloat( addr + 88, y1 ); + memPutFloat( addr + 92, z ); + memPutByte( addr + 96, rgba[0] ); + memPutByte( addr + 97, rgba[1] ); + memPutByte( addr + 98, rgba[2] ); + memPutByte( addr + 99, (byte) alpha ); + memPutFloat( addr + 100, u2 ); + memPutFloat( addr + 104, v1 ); + memPutShort( addr + 108, (short) 0xF0 ); + memPutShort( addr + 110, (short) 0xF0 ); + + // Finally increment the position. + buffer.position( position + 112 ); + + // Well done for getting to the end of this method. I recommend you take a break and go look at cute puppies. + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java index 546fa902f..db2707709 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateBlockMonitor.java @@ -7,14 +7,17 @@ import dan200.computercraft.shared.common.BlockGeneric; import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState; +import de.srendi.advancedperipherals.common.blocks.base.IHarvestableBlock; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.tags.TagKey; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -23,14 +26,14 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraftforge.common.Tags; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.registries.RegistryObject; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public class UltimateBlockMonitor extends BlockGeneric -{ +public class UltimateBlockMonitor extends BlockGeneric implements IHarvestableBlock { public static final DirectionProperty ORIENTATION = DirectionProperty.create( "orientation", Direction.UP, Direction.DOWN, Direction.NORTH ); @@ -48,6 +51,11 @@ public UltimateBlockMonitor( Properties settings, RegistryObject getHarvestTag() { + return Tags.Blocks.NEEDS_GOLD_TOOL; + } + @Override protected void createBlockStateDefinition( StateDefinition.Builder builder ) { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java index c609ba1e8..65c8ac52c 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateClientMonitor.java @@ -9,10 +9,10 @@ import dan200.computercraft.client.util.DirectBuffers; import dan200.computercraft.client.util.DirectVertexBuffer; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.peripheral.monitor.XYPair; import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateTerminalState; import net.minecraft.core.BlockPos; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -181,7 +181,7 @@ public UltimateNetworkedTerminal getTerminal() return terminal; } - void read( TerminalState state ) + void read( UltimateTerminalState state ) { if( state.hasTerminal() ) { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java index 991e60dd0..b82994421 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java @@ -9,13 +9,13 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.common.TileGeneric; -import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState; import dan200.computercraft.shared.peripheral.monitor.XYPair; import dan200.computercraft.shared.util.CapabilityUtil; import dan200.computercraft.shared.util.TickScheduler; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateTerminalState; import de.srendi.advancedperipherals.common.setup.APBlockEntityTypes; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -63,7 +63,7 @@ public class UltimateMonitorEntity extends TileGeneric { // MonitorWatcher state. boolean enqueued; - TerminalState cached; + UltimateTerminalState cached; private int width = 1; private int height = 1; @@ -262,6 +262,13 @@ public UltimateClientMonitor getClientMonitor() // Networking stuff + @Nonnull + @Override + public final ClientboundBlockEntityDataPacket getUpdatePacket() + { + return ClientboundBlockEntityDataPacket.create( this ); + } + @Nonnull @Override public final CompoundTag getUpdateTag() @@ -302,7 +309,7 @@ public final void handleUpdateTag( @Nonnull CompoundTag nbt ) } } - public final void read( TerminalState state ) + public final void read( UltimateTerminalState state ) { if( xIndex != 0 || yIndex != 0 ) { @@ -379,7 +386,7 @@ public int getYIndex() boolean isCompatible( UltimateMonitorEntity other ) { - return other instanceof UltimateMonitorEntity && getOrientation() == other.getOrientation() && getDirection() == other.getDirection(); + return !other.destroyed && getOrientation() == other.getOrientation() && getDirection() == other.getDirection(); } /** @@ -429,6 +436,7 @@ void resize( int width, int height ) xIndex = 0; yIndex = 0; + System.out.println("resizing: " + this + " " + width + ", " + height); this.width = width; this.height = height; @@ -579,7 +587,7 @@ private void monitorTouched( float xPos, float yPos, float zPos, @Nullable Playe UltimateServerMonitor serverTerminal = getServerMonitor(); if( serverTerminal == null ) return; - Terminal originTerminal = serverTerminal.getTerminal(); + UltimateNetworkedTerminal originTerminal = serverTerminal.getTerminal(); if( originTerminal == null ) return; double xCharWidth = (width - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getWidth(); diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java index f3d7264e3..6b4847233 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java @@ -40,13 +40,33 @@ public boolean isUltimate() { } @LuaFunction - public double getTransparency() throws LuaException { - return (double)(this.getTerminal().getTransparency()) / 0xff; + public double getPanelDepth() throws LuaException { + return (double)(this.getTerminal().getPanelDepth()); } @LuaFunction - public void setTransparency(double transparency) throws LuaException { - this.getTerminal().setTransparency((int)(transparency * 0xff)); + public void setPanelDepth(double panelDepth) throws LuaException { + this.getTerminal().setPanelDepth((float)(panelDepth)); + } + + @LuaFunction + public double getTextTransparency() throws LuaException { + return (double)(this.getTerminal().getTextTransparency()) / 0xff; + } + + @LuaFunction + public void setTextTransparency(double transparency) throws LuaException { + this.getTerminal().setTextTransparency((int)(transparency * 0xff)); + } + + @LuaFunction + public double getBackgroundTransparency() throws LuaException { + return (double)(this.getTerminal().getBackgroundTransparency()) / 0xff; + } + + @LuaFunction + public void setBackgroundTransparency(double transparency) throws LuaException { + this.getTerminal().setBackgroundTransparency((int)(transparency * 0xff)); } @LuaFunction diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java index 44aac02a5..d00ca869e 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorWatcher.java @@ -6,9 +6,9 @@ package de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.shared.network.NetworkHandler; -import dan200.computercraft.shared.network.client.MonitorClientMessage; -import dan200.computercraft.shared.computer.terminal.TerminalState; +import de.srendi.advancedperipherals.common.network.APNetworking; +import de.srendi.advancedperipherals.common.network.toclient.UltimateMonitorClientPacket; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateTerminalState; import de.srendi.advancedperipherals.AdvancedPeripherals; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; @@ -53,8 +53,8 @@ public static void onWatch( ChunkWatchEvent.Watch event ) UltimateServerMonitor serverMonitor = getMonitor( monitor ); if( serverMonitor == null || monitor.enqueued ) continue; - TerminalState state = getState( monitor, serverMonitor ); - NetworkHandler.sendToPlayer( event.getPlayer(), new MonitorClientMessage( monitor.getBlockPos(), state ) ); + UltimateTerminalState state = getState( monitor, serverMonitor ); + APNetworking.sendToClient( new UltimateMonitorClientPacket( monitor.getBlockPos(), state ), event.getPlayer() ); } } @@ -79,14 +79,8 @@ public static void onTick( TickEvent.ServerTickEvent event ) Level world = tile.getLevel(); if( !(world instanceof ServerLevel) ) continue; - LevelChunk chunk = world.getChunkAt( pos ); - if( ((ServerLevel) world).getChunkSource().chunkMap.getPlayers( chunk.getPos(), false ).isEmpty() ) - { - continue; - } - - TerminalState state = getState( tile, monitor ); - NetworkHandler.sendToAllTracking( new MonitorClientMessage( pos, state ), chunk ); + UltimateTerminalState state = getState( tile, monitor ); + APNetworking.sendToAllTracking( new UltimateMonitorClientPacket( pos, state ), world, pos ); limit -= state.size(); } @@ -97,10 +91,11 @@ private static UltimateServerMonitor getMonitor( UltimateMonitorEntity monitor ) return !monitor.isRemoved() && monitor.getXIndex() == 0 && monitor.getYIndex() == 0 ? monitor.getCachedServerMonitor() : null; } - private static TerminalState getState( UltimateMonitorEntity tile, UltimateServerMonitor monitor ) - { - TerminalState state = tile.cached; - if( state == null ) state = tile.cached = new TerminalState( monitor.getTerminal() ); + private static UltimateTerminalState getState( UltimateMonitorEntity tile, UltimateServerMonitor monitor ) { + UltimateTerminalState state = tile.cached; + if (state == null) { + state = tile.cached = new UltimateTerminalState(monitor.getTerminal()); + } return state; } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java index 792091195..6efe8fe0e 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java @@ -61,13 +61,13 @@ synchronized void rebuild() } } - private void markChanged() - { - if( !changed.getAndSet( true ) ) TickScheduler.schedule( origin.tickToken ); + private void markChanged() { + if (!changed.getAndSet(true)) { + TickScheduler.schedule(origin.tickToken); + } } - int getTextScale() - { + int getTextScale() { return textScale; } @@ -78,14 +78,12 @@ synchronized void setTextScale( int textScale ) rebuild(); } - boolean pollResized() - { - return resized.getAndSet( false ); + boolean pollResized() { + return resized.getAndSet(false); } - boolean pollTerminalChanged() - { - return changed.getAndSet( false ); + boolean pollTerminalChanged() { + return changed.getAndSet(false); } @Nullable diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java index 19a2d73d6..573471b53 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java @@ -1,12 +1,16 @@ package de.srendi.advancedperipherals.common.addons.computercraft.terminal; import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import java.util.Arrays; public class UltimateNetworkedTerminal extends NetworkedTerminal { - private final int[] transparencies = new int[]{0xff, 0, 0, 0}; + private float panelDepth = 0; + private int textTransparency = 0xff; + private int backgroundTransparency = 0xff; + private final int[] transparencies = new int[]{0, 0, 0}; private final int[][] sideColors = new int[3][3]; public UltimateNetworkedTerminal(int width, int height) { @@ -17,15 +21,42 @@ public UltimateNetworkedTerminal(int width, int height, Runnable changedCallback super(width, height, true, changedCallback); } - public int getTransparency() { - return this.transparencies[0]; + public float getPanelDepth() { + return this.panelDepth; } - public void setTransparency(int transparency) { - if (this.transparencies[0] == transparency) { + public void setPanelDepth(float z) { + z = Math.min(Math.max(z, 0), 1); + if (this.panelDepth == z) { return; } - this.transparencies[0] = transparency; + this.panelDepth = z; + setChanged(); + } + + public int getTextTransparency() { + return this.textTransparency; + } + + public void setTextTransparency(int transparency) { + transparency = Math.min(Math.max(transparency, 0), 0xff); + if (this.textTransparency == transparency) { + return; + } + this.textTransparency = transparency; + setChanged(); + } + + public int getBackgroundTransparency() { + return this.backgroundTransparency; + } + + public void setBackgroundTransparency(int transparency) { + transparency = Math.min(Math.max(transparency, 0), 0xff); + if (this.backgroundTransparency == transparency) { + return; + } + this.backgroundTransparency = transparency; setChanged(); } @@ -34,6 +65,7 @@ public int getSideTransparency(MonitorSide side) { } public void setSideTransparency(MonitorSide side, int transparency) { + transparency = Math.min(Math.max(transparency, 0), 0xff); if (this.transparencies[side.getIndex()] == transparency) { return; } @@ -42,52 +74,91 @@ public void setSideTransparency(MonitorSide side, int transparency) { } public int[] getSideColor(MonitorSide side) { - return this.sideColors[side.getIndex() - 1]; + return this.sideColors[side.getIndex()]; } public void setSideColor(MonitorSide side, int[] color) { if (color.length != 3) { throw new IllegalArgumentException("color.length must be 3"); } - if (Arrays.equals(this.sideColors[side.getIndex() - 1], color)) { + color[0] = Math.min(Math.max(color[0], 0), 0xff); + color[1] = Math.min(Math.max(color[1], 0), 0xff); + color[2] = Math.min(Math.max(color[2], 0), 0xff); + if (Arrays.equals(this.sideColors[side.getIndex()], color)) { return; } - this.sideColors[side.getIndex() - 1] = color; + this.sideColors[side.getIndex()] = color; setChanged(); } @Override public synchronized void reset() { super.reset(); - this.transparencies[0] = 0xff; + this.panelDepth = 0; + this.textTransparency = 0xff; + this.backgroundTransparency = 0xff; + this.transparencies[0] = 0; this.transparencies[1] = 0; this.transparencies[2] = 0; - this.transparencies[3] = 0; + setChanged(); } @Override public synchronized void write(FriendlyByteBuf buffer) { super.write(buffer); + buffer.writeFloat(this.panelDepth); + buffer.writeByte(this.textTransparency); + buffer.writeByte(this.backgroundTransparency); buffer.writeByte(this.transparencies[0]); buffer.writeByte(this.transparencies[1]); buffer.writeByte(this.transparencies[2]); - buffer.writeByte(this.transparencies[3]); } @Override public synchronized void read(FriendlyByteBuf buffer) { super.read(buffer); - this.transparencies[0] = buffer.readByte(); - this.transparencies[1] = buffer.readByte(); - this.transparencies[2] = buffer.readByte(); - this.transparencies[3] = buffer.readByte(); + this.panelDepth = buffer.readFloat(); + this.textTransparency = (int)(buffer.readByte()) & 0xff; + this.backgroundTransparency = (int)(buffer.readByte()) & 0xff; + this.transparencies[0] = (int)(buffer.readByte()) & 0xff; + this.transparencies[1] = (int)(buffer.readByte()) & 0xff; + this.transparencies[2] = (int)(buffer.readByte()) & 0xff; + setChanged(); + } + + @Override + public synchronized CompoundTag writeToNBT(CompoundTag nbt) { + super.writeToNBT(nbt); + nbt.putFloat("term_panelDepth", this.panelDepth); + nbt.putByte("term_textTransparency", (byte) this.textTransparency); + nbt.putByte("term_backgroundTransparency", (byte) this.backgroundTransparency); + nbt.putByteArray("term_sideTransparencies", new byte[]{ + (byte) this.transparencies[0], + (byte) this.transparencies[1], + (byte) this.transparencies[2], + }); + return nbt; + } + + @Override + public synchronized void readFromNBT(CompoundTag nbt) { + super.readFromNBT(nbt); + this.panelDepth = nbt.getFloat("term_panelDepth"); + this.textTransparency = (int)(nbt.getByte("term_textTransparency")) & 0xff; + this.backgroundTransparency = (int)(nbt.getByte("term_backgroundTransparency")) & 0xff; + byte[] transparencies = nbt.getByteArray("term_sideTransparencies"); + if (transparencies.length == 3) { + this.transparencies[0] = (int)(transparencies[0]) & 0xff; + this.transparencies[1] = (int)(transparencies[1]) & 0xff; + this.transparencies[2] = (int)(transparencies[2]) & 0xff; + } + setChanged(); } public static enum MonitorSide { - PANEL(0), - FRONT(1), - LEFT(2), - TOP(3); + FRONT(0), + LEFT(1), + TOP(2); private final int index; diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateTerminalState.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateTerminalState.java new file mode 100644 index 000000000..ae6d584bc --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateTerminalState.java @@ -0,0 +1,187 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.addons.computercraft.terminal; + +import dan200.computercraft.shared.util.IoUtil; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + * A snapshot of a terminal's state. + *

+ * This is somewhat memory inefficient (we build a buffer, only to write it elsewhere), however it means we get a + * complete and accurate description of a terminal, which avoids a lot of complexities with resizing terminals, dirty + * states, etc... + */ +public class UltimateTerminalState +{ + public final int width; + public final int height; + + private final boolean compress; + + @Nullable + private final ByteBuf buffer; + + private ByteBuf compressed; + + public UltimateTerminalState( @Nullable UltimateNetworkedTerminal terminal ) + { + this( terminal, true ); + } + + public UltimateTerminalState( @Nullable UltimateNetworkedTerminal terminal, boolean compress ) + { + this.compress = compress; + + if( terminal == null ) + { + width = height = 0; + buffer = null; + } + else + { + width = terminal.getWidth(); + height = terminal.getHeight(); + + ByteBuf buf = buffer = Unpooled.buffer(); + terminal.write( new FriendlyByteBuf( buf ) ); + } + } + + public UltimateTerminalState( FriendlyByteBuf buf ) + { + buf.readBoolean(); + compress = buf.readBoolean(); + + if( buf.readBoolean() ) + { + width = buf.readVarInt(); + height = buf.readVarInt(); + + int length = buf.readVarInt(); + buffer = readCompressed( buf, length, compress ); + } + else + { + width = height = 0; + buffer = null; + } + } + + public void write( FriendlyByteBuf buf ) + { + buf.writeBoolean( true ); + buf.writeBoolean( compress ); + + buf.writeBoolean( buffer != null ); + if( buffer != null ) + { + buf.writeVarInt( width ); + buf.writeVarInt( height ); + + ByteBuf sendBuffer = getCompressed(); + buf.writeVarInt( sendBuffer.readableBytes() ); + buf.writeBytes( sendBuffer, sendBuffer.readerIndex(), sendBuffer.readableBytes() ); + } + } + + public boolean hasTerminal() + { + return buffer != null; + } + + public int size() + { + return buffer == null ? 0 : buffer.readableBytes(); + } + + public void apply( UltimateNetworkedTerminal terminal ) + { + if( buffer == null ) throw new NullPointerException( "buffer" ); + terminal.resize( width, height ); + terminal.read( new FriendlyByteBuf( buffer ) ); + } + + public UltimateNetworkedTerminal create() + { + if( buffer == null ) throw new NullPointerException( "Terminal does not exist" ); + UltimateNetworkedTerminal terminal = new UltimateNetworkedTerminal( width, height ); + terminal.read( new FriendlyByteBuf( buffer ) ); + return terminal; + } + + private ByteBuf getCompressed() + { + if( buffer == null ) throw new NullPointerException( "buffer" ); + if( !compress ) return buffer; + if( compressed != null ) return compressed; + + ByteBuf compressed = Unpooled.buffer(); + OutputStream stream = null; + try + { + stream = new GZIPOutputStream( new ByteBufOutputStream( compressed ) ); + stream.write( buffer.array(), buffer.arrayOffset(), buffer.readableBytes() ); + } + catch( IOException e ) + { + throw new UncheckedIOException( e ); + } + finally + { + IoUtil.closeQuietly( stream ); + } + + return this.compressed = compressed; + } + + private static ByteBuf readCompressed( ByteBuf buf, int length, boolean compress ) + { + if( compress ) + { + ByteBuf buffer = Unpooled.buffer(); + InputStream stream = null; + try + { + stream = new GZIPInputStream( new ByteBufInputStream( buf, length ) ); + byte[] swap = new byte[8192]; + while( true ) + { + int bytes = stream.read( swap ); + if( bytes == -1 ) break; + buffer.writeBytes( swap, 0, bytes ); + } + } + catch( IOException e ) + { + throw new UncheckedIOException( e ); + } + finally + { + IoUtil.closeQuietly( stream ); + } + return buffer; + } + else + { + ByteBuf buffer = Unpooled.buffer( length ); + buf.readBytes( buffer, length ); + return buffer; + } + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java b/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java index ae80747a0..71f03891c 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java +++ b/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java @@ -1,13 +1,26 @@ package de.srendi.advancedperipherals.common.data; +import com.google.gson.JsonObject; +import com.google.gson.JsonElement; +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState; import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateBlockMonitor; import de.srendi.advancedperipherals.common.blocks.base.BaseBlock; import de.srendi.advancedperipherals.common.setup.APBlocks; import net.minecraft.core.Direction; import net.minecraft.core.FrontAndTop; +import net.minecraft.data.CachedOutput; import net.minecraft.data.DataGenerator; +import net.minecraft.data.DataProvider; +import net.minecraft.data.models.BlockModelGenerators; +import net.minecraft.data.models.blockstates.*; +import net.minecraft.data.models.model.*; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.Property; import net.minecraftforge.client.model.generators.BlockModelBuilder; import net.minecraftforge.client.model.generators.BlockStateProvider; import net.minecraftforge.client.model.generators.ConfiguredModel; @@ -16,12 +29,31 @@ import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.registries.ForgeRegistries; +import java.io.IOException; +import java.nio.file.Path; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import static net.minecraft.data.models.model.ModelLocationUtils.getModelLocation; +import static net.minecraft.data.models.model.TextureMapping.getBlockTexture; public class BlockStatesAndModelsProvider extends BlockStateProvider { + private final DataGenerator generator; + private Consumer addBlockState = null; + private BiConsumer> modelOutput = null; public BlockStatesAndModelsProvider(DataGenerator packOutput, ExistingFileHelper exFileHelper) { super(packOutput, AdvancedPeripherals.MOD_ID, exFileHelper); + this.generator = packOutput; } @Override @@ -141,4 +173,183 @@ private ResourceLocation key(Block block) { private String name(Block block) { return key(block).getPath(); } + + @Override + public void run(CachedOutput cache) throws IOException { + doMonitors(this.generator, cache); + super.run(cache); + } + + //// from dan200.computercraft.data.BlockModelGenerator //// + + private static final ModelTemplate MONITOR_BASE = new ModelTemplate( + Optional.of( new ResourceLocation( AdvancedPeripherals.MOD_ID, "block/monitor_base" ) ), + Optional.empty(), + TextureSlot.FRONT, TextureSlot.SIDE, TextureSlot.TOP, TextureSlot.BACK + ); + + private void doMonitors(DataGenerator generator, CachedOutput output) { + DataGenerator.PathProvider blockStatePath = generator.createPathProvider( DataGenerator.Target.RESOURCE_PACK, "blockstates" ); + DataGenerator.PathProvider modelPath = generator.createPathProvider( DataGenerator.Target.RESOURCE_PACK, "models" ); + Map blockStates = new HashMap<>(); + this.addBlockState = gen -> { + Block block = gen.getBlock(); + if( blockStates.containsKey( block ) ) + { + throw new IllegalStateException( "Duplicate blockstate definition for " + block ); + } + blockStates.put( block, gen ); + }; + + Map> models = new HashMap<>(); + this.modelOutput = (id, contents) -> { + if(models.containsKey(id)) { + throw new IllegalStateException("Duplicate model definition for " + id); + } + models.put(id, contents); + }; + Set explicitItems = new HashSet<>(); + BlockModelGenerators blockGen = new BlockModelGenerators(addBlockState, modelOutput, explicitItems::add); + + registerMonitor(blockGen, (UltimateBlockMonitor) APBlocks.ULTIMATE_MONITOR.get()); + + for( Block block : ForgeRegistries.BLOCKS ) + { + if( !blockStates.containsKey( block ) ) continue; + + Item item = Item.BY_BLOCK.get( block ); + if( item == null || explicitItems.contains( item ) ) continue; + + ResourceLocation model = ModelLocationUtils.getModelLocation( item ); + if( !models.containsKey( model ) ) + { + models.put( model, new DelegatedModel( ModelLocationUtils.getModelLocation( block ) ) ); + } + } + + saveCollection( output, blockStates, x -> blockStatePath.json( ForgeRegistries.BLOCKS.getKey( x ) ) ); + saveCollection( output, models, modelPath::json ); + } + + private static void saveCollection( CachedOutput output, Map> items, Function getLocation ) + { + for( Map.Entry> entry : items.entrySet() ) + { + Path path = getLocation.apply( entry.getKey() ); + try + { + DataProvider.saveStable( output, entry.getValue().get(), path ); + } + catch( Exception exception ) + { + ComputerCraft.log.error( "Couldn't save {}", path, exception ); + } + } + } + + private void registerMonitor(BlockModelGenerators generators, UltimateBlockMonitor block) + { + monitorModel( generators, block, "", 16, 4, 0, 32 ); + monitorModel( generators, block, "_d", 20, 7, 0, 36 ); + monitorModel( generators, block, "_l", 19, 4, 1, 33 ); + monitorModel( generators, block, "_ld", 31, 7, 1, 45 ); + monitorModel( generators, block, "_lr", 18, 4, 2, 34 ); + monitorModel( generators, block, "_lrd", 30, 7, 2, 46 ); + monitorModel( generators, block, "_lru", 24, 5, 2, 40 ); + monitorModel( generators, block, "_lrud", 27, 6, 2, 43 ); + monitorModel( generators, block, "_lu", 25, 5, 1, 39 ); + monitorModel( generators, block, "_lud", 28, 6, 1, 42 ); + monitorModel( generators, block, "_r", 17, 4, 3, 35 ); + monitorModel( generators, block, "_rd", 29, 7, 3, 47 ); + monitorModel( generators, block, "_ru", 23, 5, 3, 41 ); + monitorModel( generators, block, "_rud", 26, 6, 3, 44 ); + monitorModel( generators, block, "_u", 22, 5, 0, 38 ); + monitorModel( generators, block, "_ud", 21, 6, 0, 37 ); + + addBlockState.accept( MultiVariantGenerator.multiVariant( block ) + .with( createHorizontalFacingDispatch() ) + .with( createVerticalFacingDispatch( UltimateBlockMonitor.ORIENTATION ) ) + .with( createModelDispatch( UltimateBlockMonitor.STATE, edge -> getModelLocation( block, edge == MonitorEdgeState.NONE ? "" : "_" + edge.getSerializedName() ) ) ) + ); + modelOutput.accept( ModelLocationUtils.getModelLocation(block.asItem()), new DelegatedModel(monitorModel( generators, block, "_item", 15, 4, 0, 32 ) )); + } + + private static final ResourceLocation TRANSPARENT_TEXTURE = new ResourceLocation(AdvancedPeripherals.MOD_ID, "block/transparent"); + + private ResourceLocation monitorModel( BlockModelGenerators generators, UltimateBlockMonitor block, String corners, int front, int side, int top, int back ) + { + TextureMapping textureMap = new TextureMapping(); + textureMap.put(TextureSlot.FRONT, getBlockTexture(block, "_" + front)); + // textureMap.put(TextureSlot.SIDE, getBlockTexture(block, "_" + side)); + // textureMap.put(TextureSlot.TOP, getBlockTexture(block, "_" + top)); + // textureMap.put(TextureSlot.BACK, getBlockTexture(block, "_" + back)); + textureMap.put(TextureSlot.SIDE, TRANSPARENT_TEXTURE); + textureMap.put(TextureSlot.TOP, TRANSPARENT_TEXTURE); + textureMap.put(TextureSlot.BACK, TRANSPARENT_TEXTURE); + return MONITOR_BASE.create( + getModelLocation( block, corners ), + textureMap, + modelOutput + ); + } + + private static PropertyDispatch createHorizontalFacingDispatch() + { + var dispatch = PropertyDispatch.property( BlockStateProperties.HORIZONTAL_FACING ); + for( Direction direction : BlockStateProperties.HORIZONTAL_FACING.getPossibleValues() ) + { + dispatch.select( direction, Variant.variant().with( VariantProperties.Y_ROT, toYAngle( direction ) ) ); + } + return dispatch; + } + + private static PropertyDispatch createVerticalFacingDispatch( Property property ) + { + var dispatch = PropertyDispatch.property( property ); + for( Direction direction : property.getPossibleValues() ) + { + dispatch.select( direction, Variant.variant().with( VariantProperties.X_ROT, toXAngle( direction ) ) ); + } + return dispatch; + } + + private static > PropertyDispatch createModelDispatch( Property property, Function makeModel ) + { + var variant = PropertyDispatch.property( property ); + for( T value : property.getPossibleValues() ) + { + variant.select( value, Variant.variant().with( VariantProperties.MODEL, makeModel.apply( value ) ) ); + } + return variant; + } + + private static VariantProperties.Rotation toXAngle( Direction direction ) + { + switch( direction ) + { + default: + return VariantProperties.Rotation.R0; + case UP: + return VariantProperties.Rotation.R270; + case DOWN: + return VariantProperties.Rotation.R90; + } + } + + private static VariantProperties.Rotation toYAngle( Direction direction ) + { + switch( direction ) + { + default: + return VariantProperties.Rotation.R0; + case NORTH: + return VariantProperties.Rotation.R0; + case SOUTH: + return VariantProperties.Rotation.R180; + case EAST: + return VariantProperties.Rotation.R90; + case WEST: + return VariantProperties.Rotation.R270; + } + } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java b/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java index 53cb3a4bc..497d13ef5 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java +++ b/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java @@ -75,6 +75,7 @@ private void addBlocks() { addBlock(APBlocks.PLAYER_DETECTOR, "Player Detector"); addBlock(APBlocks.REDSTONE_INTEGRATOR, "Redstone Integrator"); addBlock(APBlocks.DISTANCE_DETECTOR, "Distance Detector"); + addBlock(APBlocks.ULTIMATE_MONITOR, "Ultimate Monitor"); } private void addTurtles() { @@ -85,7 +86,6 @@ private void addTurtles() { addTurtle(CCRegistration.ID.ENVIRONMENT_TURTLE, "Environment"); addTurtle(CCRegistration.ID.PLAYER_TURTLE, "Player Detector"); addTurtle(CCRegistration.ID.GEOSCANNER_TURTLE, "Geo"); - addTurtle(CCRegistration.ID.COMPASS_TURTLE, "Compass"); addTurtle(CCRegistration.ID.WEAK_AUTOMATA, "Weak automata"); addTurtle(CCRegistration.ID.HUSBANDRY_AUTOMATA, "Husbandry automata"); addTurtle(CCRegistration.ID.END_AUTOMATA, "End automata"); @@ -139,6 +139,7 @@ private void addTooltips() { addTooltip(APItems.OVERPOWERED_HUSBANDRY_AUTOMATA_CORE.get(), "&7Improved version of the husbandry automata core, that provides some overpowered uses! Be careful, the upgrade is very fragile."); addTooltip(APItems.END_AUTOMATA_CORE.get(), "&7Upgrade for turtles, that allows basic interaction with the world and teleportation in one dimension."); addTooltip(APItems.OVERPOWERED_END_AUTOMATA_CORE.get(), "&7Improved version of the end automata core, that provides some overpowered uses! Be careful, the upgrade is very fragile."); + addTooltip(APBlocks.ULTIMATE_MONITOR.get(), "&7A more advanced monitor. Can identify the operator and allow lights to pass through."); } private void addTexts() { diff --git a/src/main/java/de/srendi/advancedperipherals/common/network/APNetworking.java b/src/main/java/de/srendi/advancedperipherals/common/network/APNetworking.java index 1a6751ff4..a68b925cc 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/network/APNetworking.java +++ b/src/main/java/de/srendi/advancedperipherals/common/network/APNetworking.java @@ -5,6 +5,7 @@ import de.srendi.advancedperipherals.common.network.toclient.DistanceDetectorSyncPacket; import de.srendi.advancedperipherals.common.network.toclient.SaddleTurtleInfoPacket; import de.srendi.advancedperipherals.common.network.toclient.ToastToClientPacket; +import de.srendi.advancedperipherals.common.network.toclient.UltimateMonitorClientPacket; import de.srendi.advancedperipherals.common.network.toserver.GlassesHotkeyPacket; import de.srendi.advancedperipherals.common.network.toserver.SaddleTurtleControlPacket; import net.minecraft.core.BlockPos; @@ -38,6 +39,7 @@ public static void init() { registerServerToClient(DistanceDetectorSyncPacket.class, DistanceDetectorSyncPacket::decode); registerServerToClient(SaddleTurtleInfoPacket.class, SaddleTurtleInfoPacket::decode); registerServerToClient(ToastToClientPacket.class, ToastToClientPacket::decode); + registerServerToClient(UltimateMonitorClientPacket.class, UltimateMonitorClientPacket::decode); registerClientToServer(GlassesHotkeyPacket.class, GlassesHotkeyPacket::decode); registerClientToServer(SaddleTurtleControlPacket.class, SaddleTurtleControlPacket::decode); } diff --git a/src/main/java/de/srendi/advancedperipherals/common/network/toclient/UltimateMonitorClientPacket.java b/src/main/java/de/srendi/advancedperipherals/common/network/toclient/UltimateMonitorClientPacket.java new file mode 100644 index 000000000..11eaa82a8 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/network/toclient/UltimateMonitorClientPacket.java @@ -0,0 +1,55 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package de.srendi.advancedperipherals.common.network.toclient; + +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.monitor.UltimateMonitorEntity; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateTerminalState; +import de.srendi.advancedperipherals.common.network.base.IPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.network.NetworkEvent; + +import javax.annotation.Nonnull; + +public class UltimateMonitorClientPacket implements IPacket +{ + private final BlockPos pos; + private final UltimateTerminalState state; + + public UltimateMonitorClientPacket( BlockPos pos, UltimateTerminalState state ) + { + this.pos = pos; + this.state = state; + } + + @Override + public void encode( @Nonnull FriendlyByteBuf buf ) + { + buf.writeBlockPos( pos ); + state.write( buf ); + } + + @Override + public void handle( NetworkEvent.Context context ) + { + LocalPlayer player = Minecraft.getInstance().player; + if( player == null || player.level == null ) return; + + BlockEntity te = player.level.getBlockEntity( pos ); + if( !(te instanceof UltimateMonitorEntity monitor) ) return; + + monitor.read( state ); + } + + public static UltimateMonitorClientPacket decode(FriendlyByteBuf buffer) { + return new UltimateMonitorClientPacket(buffer.readBlockPos(), new UltimateTerminalState(buffer)); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java index b3af9d58d..1a2598874 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/APBlocks.java @@ -41,7 +41,9 @@ protected static void register() { public static final RegistryObject COLONY_INTEGRATOR = register("colony_integrator", () -> new APBlockEntityBlock<>(APBlockEntityTypes.COLONY_INTEGRATOR, false), () -> new APBlockItem(APBlocks.COLONY_INTEGRATOR.get(), APConfig.PERIPHERALS_CONFIG.enableColonyIntegrator)); public static final RegistryObject NBT_STORAGE = register("nbt_storage", () -> new APBlockEntityBlock<>(APBlockEntityTypes.NBT_STORAGE, false), () -> new APBlockItem(APBlocks.NBT_STORAGE.get(), APConfig.PERIPHERALS_CONFIG.enableNBTStorage)); public static final RegistryObject DISTANCE_DETECTOR = register("distance_detector", () -> new APBlockEntityBlock<>(APBlockEntityTypes.DISTANCE_DETECTOR, BlockBehaviour.Properties.of(Material.METAL).noOcclusion(), true), () -> new APBlockItem(APBlocks.DISTANCE_DETECTOR.get(), APConfig.PERIPHERALS_CONFIG.enableDistanceDetector)); - public static final RegistryObject ULTIMATE_MONITOR = register("ultimate_monitor", () -> new UltimateBlockMonitor(BlockBehaviour.Properties.of(Material.BUILDABLE_GLASS).strength(4, 2).noOcclusion(), APBlockEntityTypes.ULTIMATE_MONITOR), () -> new APBlockItem(APBlocks.ULTIMATE_MONITOR.get(), APConfig.PERIPHERALS_CONFIG.enableUltimateMonitor)); + public static final RegistryObject ULTIMATE_MONITOR = register("ultimate_monitor", + () -> new UltimateBlockMonitor(BlockBehaviour.Properties.of(Material.BUILDABLE_GLASS).strength(4, 2).noOcclusion().isValidSpawn(APBlocks::never).isRedstoneConductor(APBlocks::never).isSuffocating(APBlocks::never).isViewBlocking(APBlocks::never).lightLevel((state) -> state.getLightEmission()), APBlockEntityTypes.ULTIMATE_MONITOR), + () -> new APBlockItem(APBlocks.ULTIMATE_MONITOR.get(), APConfig.PERIPHERALS_CONFIG.enableUltimateMonitor)); private static RegistryObject registerNoItem(String name, Supplier block) { return APRegistration.BLOCKS.register(name, block); @@ -53,6 +55,10 @@ private static RegistryObject register(String name, Supplie return registryObject; } + public static boolean never(BlockState state, BlockGetter level, BlockPos pos, net.minecraft.world.entity.EntityType entity) { + return false; + } + public static boolean never(BlockState state, BlockGetter level, BlockPos pos) { return false; } diff --git a/src/main/resources/assets/advancedperipherals/models/block/monitor_base.json b/src/main/resources/assets/advancedperipherals/models/block/monitor_base.json new file mode 100644 index 000000000..d3bbbc150 --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/models/block/monitor_base.json @@ -0,0 +1,27 @@ +{ + "parent": "block/block", + "render_type": "minecraft:cutout", + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "down": { "texture": "#down", "emissivity": 15, "ambientocclusion": false }, + "up": { "texture": "#up", "emissivity": 15, "ambientocclusion": false }, + "north": { "texture": "#north", "emissivity": 15, "ambientocclusion": false }, + "south": { "texture": "#south", "emissivity": 15, "ambientocclusion": false }, + "west": { "texture": "#west", "emissivity": 15, "ambientocclusion": false }, + "east": { "texture": "#east", "emissivity": 15, "ambientocclusion": false } + } + } + ], + "textures": { + "particle": "#front", + "down": "#top", + "up": "#top", + "north": "#front", + "east": "#side", + "south": "#back", + "west": "#side" + } +} diff --git a/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh new file mode 100644 index 000000000..3d709dfed --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh @@ -0,0 +1,69 @@ +#version 150 + +#moj_import + +#define FONT_WIDTH 6.0 +#define FONT_HEIGHT 9.0 + +uniform sampler2D Sampler0; // Font +uniform usamplerBuffer Tbo; + +layout(std140) uniform MonitorData { + vec3 Palette[16]; + int Width; + int Height; + ivec2 CursorPos; + int CursorColour; + float TextTransparency; + float BackgroundTransparency; +}; +uniform int CursorBlink; + +uniform vec4 ColorModulator; +uniform float FogStart; +uniform float FogEnd; +uniform vec4 FogColor; + +in float vertexDistance; +in vec2 fontPos; + +out vec4 fragColor; + +vec2 texture_corner(int index) { + float x = 1.0 + float(index % 16) * (FONT_WIDTH + 2.0); + float y = 1.0 + float(index / 16) * (FONT_HEIGHT + 2.0); + return vec2(x, y); +} + +vec4 recolour(vec4 texture, int colour) { + return vec4(texture.rgb * Palette[colour], texture.rgba); +} + +void main() { + vec2 term_pos = vec2(fontPos.x / FONT_WIDTH, fontPos.y / FONT_HEIGHT); + vec2 corner = floor(term_pos); + + ivec2 cell = ivec2(corner); + int index = 3 * (clamp(cell.x, 0, Width - 1) + clamp(cell.y, 0, Height - 1) * Width); + + // 1 if 0 <= x, y < Width, Height, 0 otherwise + vec2 outside = step(vec2(0.0, 0.0), vec2(cell)) * step(vec2(cell), vec2(float(Width) - 1.0, float(Height) - 1.0)); + float mult = outside.x * outside.y; + + int character = int(texelFetch(Tbo, index).r); + int fg = int(texelFetch(Tbo, index + 1).r); + int bg = int(texelFetch(Tbo, index + 2).r); + + vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT); + vec4 charTex = recolour(texture(Sampler0, (texture_corner(character) + pos) / 256.0), fg); + + // Applies the cursor on top of the current character if we're blinking and in the current cursor's cell. We do it + // this funky way to avoid branches. + vec4 cursorTex = recolour(texture(Sampler0, (texture_corner(95) + pos) / 256.0), CursorColour); // 95 = '_' + vec4 img = mix(charTex, cursorTex, cursorTex.a * float(CursorBlink) * (CursorPos == cell ? 1.0 : 0.0)); + float textAlpha = img.a * mult * TextTransparency; + + vec4 colour = (img.a * mult > 0 ? vec4(img.rgb, textAlpha) : vec4(Palette[bg], BackgroundTransparency)) * ColorModulator; + + fragColor = colour;//linear_fog(colour, vertexDistance, FogStart, FogEnd, FogColor); +} diff --git a/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.json b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.json new file mode 100644 index 000000000..04973275e --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.json @@ -0,0 +1,19 @@ +{ + "vertex": "advancedperipherals:monitor_tbo", + "fragment": "advancedperipherals:monitor_tbo", + "attributes": [ "Position" ], + "samplers": [ { "name": "Sampler0" } ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "IViewRotMat", "type": "matrix3x3", "count": 9, "values": [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, + { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, + { "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] }, + + { "name": "Tbo", "type": "int", "count": 1, "values": [ 3 ] }, + { "name": "CursorBlink", "type": "int", "count": 1, "values": [ 0 ] } + ] +} diff --git a/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.vsh b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.vsh new file mode 100644 index 000000000..885b20df5 --- /dev/null +++ b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.vsh @@ -0,0 +1,21 @@ +#version 150 + +#moj_import + +in vec3 Position; +in vec2 UV0; + +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; +uniform mat3 IViewRotMat; +uniform int FogShape; + +out float vertexDistance; +out vec2 fontPos; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1); + + vertexDistance = fog_distance(ModelViewMat, IViewRotMat * Position, FogShape); + fontPos = UV0; +} diff --git a/src/main/resources/assets/advancedperipherals/textures/block/transparent.png b/src/main/resources/assets/advancedperipherals/textures/block/transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..432862776a601b91dfc3fd96c7e7fdc7b77c6f04 GIT binary patch literal 423 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt?{ikZ&N0iMpz3I#>^X_+~x3=A3* z=T6w@b;Lo$)jzDzbxV-Q5vA5#2bSQQYq%T(Itp3CSLaMRdQ_!N<@6)@AL_phqdA)H zC#+3)J2xXSQHove`-h76_hvTs&2@`3-On5uH$CoxbPV6RH}9ll%^G6o7_91=_kR9` zD!cfNJ^L(^hh z#v8UstIj%`v$@kI|9N!hOK5b3OmSEL*$JG&^-4RR{G?nozB!ln^ z+;f(tUGcZEV{qfI3ckm|zqGur#(v(nf8A$`ZQu9w_rDJm4(+{VkoA7aWC@3LRxDDA zo|~>;S+io-)$|4x6P3MJC&Vvec(wW*yPrCzeQB0{^Q!l^uGjAE2$!9aVWsuPw9%Ep td-ta+H?t(f?=!}m%Ij*JT=D<&e})Z8IfbXS53d8c&ePS;Wt~$(69A01O|bv~ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_1.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9b8e541b534b9575d55d32be8dca5930c08d158b GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UA8a-VcLnJQ8_B(PhIr6Z4mT(l4 z3=0cm`xCi9bD`b4#4kzl&ZXg5^PWH6*I!t9wm4?KyKjhTmUGtYC6gr_Rw!QJ@D$3* z3JZC=i`B!)`O+1|{6!2|*Z;L%_GH?cy}otot!uTRE?EhSUaoNPO%OaV>u+SuQNG8I jtQVNppVQ#{`u`vM`>KMUJ9jtAfgI)O>gTe~DWM4fWlcy+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_15.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_15.png new file mode 100644 index 0000000000000000000000000000000000000000..d26ff56b9d14b19a30aee1b15bfdc14d59d6b8d5 GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAYCT;XLnOjuyA8Px8}PXP7p-cO z^3piucJ!!;^S8FR6A30i9Ne1UUssv@hF$2<#}<*t+7(LMmwEJ?D|_zMIG!SSY*IcO z=YlNP$O&wLvh$91y*ZFv*e e;vEZ$4gWJTZ!~c-O+0)Jubamy>f0sPKf#kpVFp`LENJ#(y literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_17.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_17.png new file mode 100644 index 0000000000000000000000000000000000000000..e7f90c3c95389af146ef0871ad669cd95c670c64 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`TRmMILn>|^oqUk1RYAb@uY{tC zlcvzZ*56Cyxt6rbtLXmPS9zzBV{gXNZuL2>#*a(a86C28|7>G?*<_+&+m>r4Jj}{V zm!w9nxT1KiR5mg@Q^sA$L*}@n=F;$_9{b<5_jk%Es80!!5!uSE>$o$DL2MSwuYLck zR>oGGT*mhywc?^$<1#)cjw!`?!WQR0pJ(zFyH>jN^{!W}+j5h+8Rpt(TE;NFE6urE meLk7f;8cD>+`NrN&zQeu?Q4%cy6_Fq0}P(7elF{r5}E)nUtH4w literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_18.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_18.png new file mode 100644 index 0000000000000000000000000000000000000000..1425de96546c9b37e2163366b7cdaf43c8fb11d3 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`m7Xq+Ar-gYPPXQ1QRHc{?MYDW zTEroC&s%_-Uqx(Q{&cvhmb)~U z3M97_^DX+$m(=soBl~Uk`mIg%CxpYp!}Hf}J#fbIe?tEI+8<{w>Ue1`pT-jUlXKOF c_r3LTg*&_JUaWcG0CWU{r>mdKI;Vst0R4qVu>b%7 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_19.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_19.png new file mode 100644 index 0000000000000000000000000000000000000000..59ff8bedbf212aed2161cf90c2fcf67ae1b150ea GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`8$4YcLn>}9o$SbURDs8(TD$Gd7s>wdnugwbb zGI>>CcHX%1{YgvrV9nMAQLmR5ovZ)-*(UQ#2}3y}!}c78Il8A0u|Kdp(a!52roY?Hry}ZKB}^uYRkt%e(zoSKacGm%8=o!%}wb3$t|; zW*$14IqTEf8#9Xzvad0m?k*(yB`oHX)wOx2v)=U`4`FOvSGXmq{0YmdKI;Vst0GMo1a{vGU literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_20.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_20.png new file mode 100644 index 0000000000000000000000000000000000000000..e65d6f6b78edda79b7ea0e9a92500905e9f17702 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAm`Z~Df*BafCZDwc@-jSK9780g z#`YL;H5dr6eAj1ldE#Nw$r5}+yG}!3-Hra+&o(_Z*sNhGui4cSwQ0@rMG~Hq=B5}2 z=kOlZakOaKx6tB1!`}V{tSijLT#A>AFEZDP`+iV*z5gYa6~UKS`PCn9KDi?{=@)aJ Wm0b3>R~AP=F7b5rb6Mw<&;$U0%{(yx literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_21.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_21.png new file mode 100644 index 0000000000000000000000000000000000000000..953663a9c8db556c263d90e5680d52c879bc1dba GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`(>+}rLn>~~oovYGDk$ReJ}$$h zWkrjVe~yO|7x#Ys>!<(!mHsPZoE+fQc|FQUzCR~~on*+>Y9Qe9UGG+l z0-LsBYi*M_SBr>cz=jVszh{a*R5puDlsdcT%o^|4yNb@uT=}^%JyJ}K!AtWlLj_Ah z&%-H`xE{<&p0%tqd+jQ&9Wh!N3?F(PpGa97$}6~)wZSn%nCI{Tlk=N@>F3-oW8T8K z=ev)gTi)7I=5?`xXI{1! q|L3S2P;qQBn4mt7+H4^M(ChvJ1fpJgTlMF}ccRA#S#DDWb3 q`=aXioc)|e$7;C#KegTd{m!X(ZIR#n^NJW3ssI20 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_25.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_25.png new file mode 100644 index 0000000000000000000000000000000000000000..ffea7b8e0dc1cd068f5429b4ee409454e482bd5d GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ZJsWUAr-gQPBP?TP~>6x+ifP+ z6p{6CKg-4sj!6%HD^)AV6onQYGE=k8ahj5}FG{;rpyPG;T2spd)#uq0Sh98HFI1~1 z^T-EyO=O(lkgXe?)Dt*OVd)X}HCr{FpJ6{^nOfe!vdCK|c|!5Kc9t1WIj%%6XJh{I ou39KzvVvp8sp%Ig{>z?c3)gQ66bpQYW literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_26.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_26.png new file mode 100644 index 0000000000000000000000000000000000000000..9d047e8d0a6100f76befc1e03d5d71eea9cfbb57 GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`NuDl_Ar-gQPCUrTV93F8xz9u3 z)2He6N7|Sa`$gU*a=(i_u))|Yw71xA%PZ9f_oCSa0^jU?-EgmZ&vye>jss^Rju^6S zb8y+9ufh~qooLAAqR8W7+oPbg zNTVwuaealNqmE6*v>iDgABLwf2hV76PfO8M}f4wpNbFFs3)fK$a7d_Pt8`iWsD64qt zwWfJp4dk5A(ze81Gk+1otCbcHN+x~K*^=J8YQ5DTNx|z(!oKdU-lhhe4#&k$8m5aJ jJ6ZlHPu6{1-oD!M zL(uuae9okYhrb7Gu-L&KGqF)~({4p$=e1uWyd2L-YN{TQ=s(kQP3m1R3|U|Y|{^^%X8G9`xqWuE5Ky={6v)Zu^GCWs#l|4-Z?xo zs4K|p$=>DZ=R7Y&y$&FA1-ls22WQ%mvv4FO#o^-PNDz+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_32.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_32.png new file mode 100644 index 0000000000000000000000000000000000000000..55a1c633f56c30e86f3fd81ae47a7cdd0f39331e GIT binary patch literal 217 zcmV;~04D#5P)NklGX_;fRKa|D1eNdc1dZ<{<_w0z0CBLpa%EDcV5r7 zc1DFGXJ=lfIFFpsl)rqIW$$9ZnYW72#5uJkpT^uChIue}3*qTfGywN zL7azX{G`Y-lpf4^O=k2I!z4v&G&JRm$1qleswH9^xzDJ*3B&9v-g(W3{kgrC-&$U z_QqVCaw0skPfu;m&lMcLG2XY8$N&HU literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_34.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_34.png new file mode 100644 index 0000000000000000000000000000000000000000..068a59ba687d4365397f49c740790cecc180cb8b GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UACVIL!hDb=V9^B~5;K;*ZF!}sk z7uS<=Avp)ibi6-m%;&!Gv+~SO>F>WS_kX?iQ`Ir|UFs#{b4S;0*rdF7TSZtIkLo0y z>mt(c=5!exxqK{g=hxHo*NV@!p6y(yryAzX4a!!c?sX|M*2$753aZ`bH4sLQhvemvv4FO#pPZOnCqR literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_37.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_37.png new file mode 100644 index 0000000000000000000000000000000000000000..a970f8175e161c24ed3fd6678c2cc063cc7bdfb7 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAnmt_{LnI_w4?21=GYT*q{Mo&e zg_E;7>dS=P-SScyQ7`(VP1PH=au%G)3=Lc>R~#RqaHZy~a;Y!lj`NWW4x!tBy0FHy z?mauPK4{g|<2IrUVh7e&UTcdAy%o>x;TePs*P%@B=x^)78&qol`;+0OjvPQ2+n{ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_38.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_38.png new file mode 100644 index 0000000000000000000000000000000000000000..91f52201b24796f519eff24ce01f6e8ab3795ea3 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAW_!9ghDb=V9^B}A#6f`Jz{jfE z6N?yxycRGRDS4?aUU{8e|62L)Gdc6`cYLu(f2{7>6k8?p+{C!8=08KB?5vNol}-m# z89djWc`=x0vdGGd7p7>>I~*anYFbC(6T8LV)Oa?(zP0b2)Ee1`^6%=s8b5j!^nsk~ M>FVdQ&MBb@0PA*H#Q*>R literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_39.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_39.png new file mode 100644 index 0000000000000000000000000000000000000000..c0dc753b6c1f1945c6976611456034b20e952638 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UACV9FzhDcmC^>gGpY{2um{FsKU z<=1V$PuYnWNIkJ`e9-*g{N!Dir09t$N$tWCi}|mluy;P#BU`Y$v|LV}FM(m(#s%45 z;#=NvPkmE1J#>;-Mf=u9{uNHzPj1vdyydg~?XRZzO$pDnj`Sp)-e5e*_Ik=m4%>i7 zy3;KG3(nNaS|3yS^!cQt_gup!PI!Ft*5VB-w|ryGJ<-0^tR$EjNklGX_;fRKa|D1eNdc1dZ<{<_w0z0CBLpa%EDcV5r7 zc1DFGXJ=lfIFFpsl)rqIW$$9ZnYW72#5uJkpT^uChIue}3*qTfGywN zL7azX{G`Y-lpf4^O=k2I!z4v&G&JRm$1qleswH9^xzDJ*3B&9W@ zJxh^U;6H6mdKI;Vst011d(O8@`> literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_41.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_41.png new file mode 100644 index 0000000000000000000000000000000000000000..79997fd0d43dfc365c0ef09832a0d71985b2c1e9 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UACVIL!hDcmC^>gGpY{2t5{@Q82 z)(OewFZMIJG1o5-s5$Xp&2n`|>hWoFB<46A=~%U^QT(7Jb3*ct&lMIH&c;FpM~Z&T z?)}hOl=o~>*82dd*{%<`{TieG8~r)E^=4s2?ZJ=QrKgKy2!|}|!9d8JN;uyjq%X+Yp_pkvEv*WQB47G~PE^F8}GQYDAet#vz&iAC}yEWGiAN?5d zIGtyyrNgGy*~fLBUf;O+X`6n;=6>}~sVvj=HSda_Zk=}eV~(kS#@W5IPEVAcqj&Im b>0|pl4FYdNG?_O7tzz(W^>bP0l+XkK6}d&W literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_44.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_44.png new file mode 100644 index 0000000000000000000000000000000000000000..1b92701120f25ab631e8bcf94de3b5dd8759545e GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPHF3h)VWRaRD3QBl#<)YR40H83zR zGBUEVva+_ewzs#hs;a83t*x)GZ)j*}Zf?H5$?pPCSDvSfV+e;V>p?@lrT_ugi`)O7 znwz{ic)I$ztaD0e0sz3;PIdqQ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_45.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_45.png new file mode 100644 index 0000000000000000000000000000000000000000..845e7fa93f0ac398bd5a9f8f25edafebb8a72d3c GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAT0C7GLnI_w4?21=GYT*qtn5)x z@>UUw{P4iGTV5(7>P3IF>GQ`Qe->KzO@IHna8KRc=UY~-2%EXRmDS1jxkqd;!<@|w z4XeuT1u}*w&aM9ExiZY!oV9`V!p^I0QK4s98P+jbPIufG@+w9vYvtBrMh4b~&ply{ muGw{>47vw@GHwfD{LAh-zf9|4zRf(4yF6X}T-G@yGywn|aYZBm literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_46.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_46.png new file mode 100644 index 0000000000000000000000000000000000000000..d4cc702af24ba79910a51d1755538c931844c19a GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDURZ&q1gVfM;~eX35A&K31-yZ*WG_Pcz`lM}mdKI;Vst0Nn>q!~g&Q literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_47.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_47.png new file mode 100644 index 0000000000000000000000000000000000000000..e2e08d5ef843644bf7f479c2c3047a218e10c722 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UA8a-VcLnI_w4?21s0m>fy+0Fe( zQ9^0rQQIHu8<(~I+Y$PC&-?YCKU>JHe_p$%K7Mhi>(;7XZtj*@&u8pf#!%D4$`JBZ zB7iYGQ9k&e=gP4B#Y_j7ei&bCiwa#Y$PmN&OP055>80W@SKY1a1Q|3K`V6C3#okJZ hGUz^(XwVHk@LE2&tF$z?eRn;`QJ$`TF6*2UngH?>LvH{8 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_5.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_5.png new file mode 100644 index 0000000000000000000000000000000000000000..91f52201b24796f519eff24ce01f6e8ab3795ea3 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAW_!9ghDb=V9^B}A#6f`Jz{jfE z6N?yxycRGRDS4?aUU{8e|62L)Gdc6`cYLu(f2{7>6k8?p+{C!8=08KB?5vNol}-m# z89djWc`=x0vdGGd7p7>>I~*anYFbC(6T8LV)Oa?(zP0b2)Ee1`^6%=s8b5j!^nsk~ M>FVdQ&MBb@0PA*H#Q*>R literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_6.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_6.png new file mode 100644 index 0000000000000000000000000000000000000000..a970f8175e161c24ed3fd6678c2cc063cc7bdfb7 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UAnmt_{LnI_w4?21=GYT*q{Mo&e zg_E;7>dS=P-SScyQ7`(VP1PH=au%G)3=Lc>R~#RqaHZy~a;Y!lj`NWW4x!tBy0FHy z?mauPK4{g|<2IrUVh7e&UTcdAy%o>x;TePs*P%@B=x^)78&qol`;+0OjvPQ2+n{ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_7.png b/src/main/resources/assets/advancedperipherals/textures/block/ultimate_monitor_7.png new file mode 100644 index 0000000000000000000000000000000000000000..938501b403e3e2ef098e7ee5c1b2a48399515e93 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UA`aE46LnI_w4?21=2Z}HpEEi`M zO7W2BX4a!!c?sX|M*2$753aZ`bH4sLQhvemvv4FO#pPZOnCqR literal 0 HcmV?d00001 From 2b4e3c28c3ba0d45852af6615cac8167ec3d57f0 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Sat, 27 Jul 2024 11:19:31 -0600 Subject: [PATCH 4/5] add alpha channel to palette --- .../renderer/UltimateMonitorRenderer.java | 15 +- .../UltimateMonitorTextureBufferShader.java | 37 +- .../text/DirectFixedWidthFontRenderer.java | 141 +++-- .../monitor/UltimateMonitorPeripheral.java | 499 +++++++++++++++--- .../monitor/UltimateServerMonitor.java | 39 +- .../terminal/UltimateNetworkedTerminal.java | 131 +---- .../shaders/core/monitor_tbo.fsh | 13 +- .../lua/rom/autorun/api_window_patch.lua | 112 ++++ 8 files changed, 667 insertions(+), 320 deletions(-) create mode 100644 src/main/resources/data/computercraft/lua/rom/autorun/api_window_patch.lua diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java index 8f3836285..f91e43919 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorRenderer.java @@ -187,21 +187,20 @@ private static void renderTerminal(Matrix4f matrix, UltimateClientMonitor monito var backgroundBuffer = monitor.backgroundBuffer; var foregroundBuffer = monitor.foregroundBuffer; if (redraw) { - int size = DirectFixedWidthFontRenderer.getVertexCount( terminal ); + int size = DirectFixedWidthFontRenderer.getVertexCount(terminal); // In an ideal world we could upload these both into one buffer. However, we can't render VBOs with // and starting and ending offset, and so need to use two buffers instead. - renderToBuffer( backgroundBuffer, size, sink -> - DirectFixedWidthFontRenderer.drawTerminalBackground( sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin, - terminal.getBackgroundTransparency() ) ); + renderToBuffer(backgroundBuffer, size, sink -> + DirectFixedWidthFontRenderer.drawTerminalBackground(sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin)); - renderToBuffer( foregroundBuffer, size, sink -> { - DirectFixedWidthFontRenderer.drawTerminalForeground( sink, 0, 0, terminal, terminal.getTextTransparency() ); + renderToBuffer(foregroundBuffer, size, sink -> { + DirectFixedWidthFontRenderer.drawTerminalForeground(sink, 0, 0, terminal); // If the cursor is visible, we append it to the end of our buffer. When rendering, we can either // render n or n+1 quads and so toggle the cursor on and off. - DirectFixedWidthFontRenderer.drawCursor( sink, 0, 0, terminal, terminal.getTextTransparency() ); - } ); + DirectFixedWidthFontRenderer.drawCursor(sink, 0, 0, terminal); + }); } // Our VBO doesn't transform its vertices with the provided pose stack, which means that the inverse view diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java index 016d22432..04519bd5f 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/UltimateMonitorTextureBufferShader.java @@ -26,9 +26,8 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour; -public class UltimateMonitorTextureBufferShader extends ShaderInstance -{ - public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4 + 4 + 4; +public class UltimateMonitorTextureBufferShader extends ShaderInstance { + public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4; static final int TEXTURE_INDEX = GL13.GL_TEXTURE3; @@ -78,8 +77,7 @@ private Uniform getUniformChecked( String name ) return uniform; } - public static void setTerminalData( ByteBuffer buffer, UltimateNetworkedTerminal terminal ) - { + public static void setTerminalData(ByteBuffer buffer, UltimateNetworkedTerminal terminal) { int width = terminal.getWidth(), height = terminal.getHeight(); int pos = 0; @@ -98,27 +96,28 @@ public static void setTerminalData( ByteBuffer buffer, UltimateNetworkedTerminal buffer.limit( pos ); } - public static void setUniformData( ByteBuffer buffer, UltimateNetworkedTerminal terminal ) - { + public static void setUniformData(ByteBuffer buffer, UltimateNetworkedTerminal terminal) { int pos = 0; var palette = terminal.getPalette(); - for( int i = 0; i < 16; i++ ) - { - double[] colour = palette.getColour( i ); - buffer.putFloat( pos, (float) colour[0] ).putFloat( pos + 4, (float) colour[1] ).putFloat( pos + 8, (float) colour[2] ) - .putFloat(pos + 12, 0.5f); + for (int i = 0; i < 16; i++) { + double[] colour = palette.getColour(i); + float alpha = terminal.getPaletteTransparency(i); + buffer + .putFloat(pos, (float) colour[0]) + .putFloat(pos + 4, (float) colour[1]) + .putFloat(pos + 8, (float) colour[2]) + .putFloat(pos + 12, alpha); pos += 4 * 4; // std140 requires these are 4-wide } - boolean showCursor = FixedWidthFontRenderer.isCursorVisible( terminal ); + boolean showCursor = FixedWidthFontRenderer.isCursorVisible(terminal); buffer - .putInt( pos, terminal.getWidth() ).putInt( pos + 4, terminal.getHeight() ) - .putInt( pos + 8, showCursor ? terminal.getCursorX() : -2 ) - .putInt( pos + 12, showCursor ? terminal.getCursorY() : -2 ) - .putInt( pos + 16, 15 - terminal.getTextColour() ) - .putFloat(pos + 20, terminal.getTextTransparency() / 255.0f) - .putFloat(pos + 24, terminal.getBackgroundTransparency() / 255.0f); + .putInt(pos, terminal.getWidth()) + .putInt(pos + 4, terminal.getHeight()) + .putInt(pos + 8, showCursor ? terminal.getCursorX() : -2) + .putInt(pos + 12, showCursor ? terminal.getCursorY() : -2) + .putInt(pos + 16, 15 - terminal.getTextColour()); buffer.limit( UNIFORM_SIZE ); } diff --git a/src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java b/src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java index 2762589c1..f05703a90 100644 --- a/src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java +++ b/src/main/java/de/srendi/advancedperipherals/client/renderer/text/DirectFixedWidthFontRenderer.java @@ -11,10 +11,10 @@ import com.mojang.blaze3d.vertex.VertexFormat; import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; -import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Palette; +import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; import org.lwjgl.system.MemoryUtil; import javax.annotation.Nonnull; @@ -64,140 +64,126 @@ private static void drawChar( QuadEmitter emitter, float x, float y, int index, quad( emitter, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour, xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, - (byte) alpha + alpha ); } - private static void drawQuad( QuadEmitter emitter, float x, float y, float width, float height, Palette palette, char colourIndex, int alpha ) - { - byte[] colour = palette.getRenderColours( getColour( colourIndex, Colour.BLACK ) ); - quad( emitter, x, y, x + width, y + height, 0f, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, alpha ); + private static void drawQuad(QuadEmitter emitter, float x, float y, float width, float height, UltimateNetworkedTerminal terminal, char colourIndex) { + int color = getColour(colourIndex, Colour.BLACK); + byte[] colour = terminal.getPalette().getRenderColours(color); + int alpha = terminal.getPaletteTransparencyByte(color); + quad(emitter, x, y, x + width, y + height, 0f, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, alpha); } private static void drawBackground( - @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, - float leftMarginSize, float rightMarginSize, float height, int alpha - ) - { - if( leftMarginSize > 0 ) - { - drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, backgroundColour.charAt( 0 ), alpha ); + @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull UltimateNetworkedTerminal terminal, + float leftMarginSize, float rightMarginSize, float height + ) { + if (leftMarginSize > 0) { + drawQuad(emitter, x - leftMarginSize, y, leftMarginSize, height, terminal, backgroundColour.charAt(0)); } - if( rightMarginSize > 0 ) - { - drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, backgroundColour.charAt( backgroundColour.length() - 1 ), alpha ); + if (rightMarginSize > 0) { + drawQuad(emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, terminal, backgroundColour.charAt(backgroundColour.length() - 1)); } // Batch together runs of identical background cells. int blockStart = 0; char blockColour = '\0'; - for( int i = 0; i < backgroundColour.length(); i++ ) - { - char colourIndex = backgroundColour.charAt( i ); - if( colourIndex == blockColour ) continue; + for (int i = 0; i < backgroundColour.length(); i++) { + char colourIndex = backgroundColour.charAt(i); + if (colourIndex == blockColour) { + continue; + } - if( blockColour != '\0' ) - { - drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, blockColour, alpha ); + if (blockColour != '\0') { + drawQuad(emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, terminal, blockColour); } blockColour = colourIndex; blockStart = i; } - if( blockColour != '\0' ) - { - drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, blockColour, alpha ); + if (blockColour != '\0') { + drawQuad(emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, terminal, blockColour); } } - public static void drawString( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, int alpha ) - { - for( int i = 0; i < text.length(); i++ ) - { - byte[] colour = palette.getRenderColours( getColour( textColour.charAt( i ), Colour.BLACK ) ); + public static void drawString(@Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull UltimateNetworkedTerminal terminal) { + for (int i = 0; i < text.length(); i++) { + int color = getColour(textColour.charAt(i), Colour.BLACK); + byte[] colour = terminal.getPalette().getRenderColours(color); + int alpha = terminal.getPaletteTransparencyByte(color); - int index = text.charAt( i ); - if( index > 255 ) index = '?'; - drawChar( emitter, x + i * FONT_WIDTH, y, index, colour, alpha ); + int index = text.charAt(i); + if (index > 255) { + index = '?'; + } + drawChar(emitter, x + i * FONT_WIDTH, y, index, colour, alpha); } } - public static void drawTerminalForeground( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, int alpha ) - { - Palette palette = terminal.getPalette(); + public static void drawTerminalForeground(@Nonnull QuadEmitter emitter, float x, float y, @Nonnull UltimateNetworkedTerminal terminal) { int height = terminal.getHeight(); // The main text - for( int i = 0; i < height; i++ ) - { + for (int i = 0; i < height; i++) { float rowY = y + FONT_HEIGHT * i; - drawString( - emitter, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ), - palette, alpha - ); + drawString(emitter, x, rowY, terminal.getLine(i), terminal.getTextColourLine(i), terminal); } } public static void drawTerminalBackground( - @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, - float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize, - int alpha - ) - { - Palette palette = terminal.getPalette(); + @Nonnull QuadEmitter emitter, float x, float y, @Nonnull UltimateNetworkedTerminal terminal, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize + ) { int height = terminal.getHeight(); // Top and bottom margins drawBackground( - emitter, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, - leftMarginSize, rightMarginSize, topMarginSize, alpha + emitter, x, y - topMarginSize, terminal.getBackgroundColourLine(0), terminal, + leftMarginSize, rightMarginSize, topMarginSize ); drawBackground( - emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, - leftMarginSize, rightMarginSize, bottomMarginSize, alpha + emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine(height - 1), terminal, + leftMarginSize, rightMarginSize, bottomMarginSize ); // The main text - for( int i = 0; i < height; i++ ) - { + for (int i = 0; i < height; i++) { float rowY = y + FONT_HEIGHT * i; drawBackground( - emitter, x, rowY, terminal.getBackgroundColourLine( i ), palette, - leftMarginSize, rightMarginSize, FONT_HEIGHT, alpha + emitter, x, rowY, terminal.getBackgroundColourLine(i), terminal, + leftMarginSize, rightMarginSize, FONT_HEIGHT ); } } - public static void drawCursor( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, int alpha ) - { - if( isCursorVisible( terminal ) ) - { - byte[] colour = terminal.getPalette().getRenderColours( 15 - terminal.getTextColour() ); - drawChar( emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, alpha ); + public static void drawCursor(@Nonnull QuadEmitter emitter, float x, float y, @Nonnull UltimateNetworkedTerminal terminal) { + if (isCursorVisible(terminal)) { + int color = 15 - terminal.getTextColour(); + byte[] colour = terminal.getPalette().getRenderColours(color); + int alpha = terminal.getPaletteTransparencyByte(color); + drawChar(emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, alpha); } } - public static int getVertexCount( Terminal terminal ) - { + public static int getVertexCount(UltimateNetworkedTerminal terminal) { return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2; } - private static void quad( QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ) - { - buffer.quad( x1, y1, x2, y2, z, rgba, u1, v1, u2, v2, alpha ); + private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha) { + buffer.quad(x1, y1, x2, y2, z, rgba, u1, v1, u2, v2, alpha); } - public interface QuadEmitter - { + public interface QuadEmitter { VertexFormat format(); ByteBuffer buffer(); - void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ); + void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha); } public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter @@ -209,14 +195,13 @@ public VertexFormat format() } @Override - public void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ) + public void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha) { DirectFixedWidthFontRenderer.quad( buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2, alpha ); } } - static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha ) - { + static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int alpha) { // Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the // underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write. // This provides significant performance gains, at the cost of well, using Unsafe. @@ -239,7 +224,7 @@ static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, flo memPutByte( addr + 12, rgba[0] ); memPutByte( addr + 13, rgba[1] ); memPutByte( addr + 14, rgba[2] ); - memPutByte( addr + 15, (byte) alpha ); + memPutByte( addr + 15, (byte)(alpha) ); memPutFloat( addr + 16, u1 ); memPutFloat( addr + 20, v1 ); memPutShort( addr + 24, (short) 0xF0 ); @@ -251,7 +236,7 @@ static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, flo memPutByte( addr + 40, rgba[0] ); memPutByte( addr + 41, rgba[1] ); memPutByte( addr + 42, rgba[2] ); - memPutByte( addr + 43, (byte) alpha ); + memPutByte( addr + 43, (byte)(alpha) ); memPutFloat( addr + 44, u1 ); memPutFloat( addr + 48, v2 ); memPutShort( addr + 52, (short) 0xF0 ); @@ -263,7 +248,7 @@ static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, flo memPutByte( addr + 68, rgba[0] ); memPutByte( addr + 69, rgba[1] ); memPutByte( addr + 70, rgba[2] ); - memPutByte( addr + 71, (byte) alpha ); + memPutByte( addr + 71, (byte)(alpha) ); memPutFloat( addr + 72, u2 ); memPutFloat( addr + 76, v2 ); memPutShort( addr + 80, (short) 0xF0 ); @@ -275,7 +260,7 @@ static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, flo memPutByte( addr + 96, rgba[0] ); memPutByte( addr + 97, rgba[1] ); memPutByte( addr + 98, rgba[2] ); - memPutByte( addr + 99, (byte) alpha ); + memPutByte( addr + 99, (byte)(alpha) ); memPutFloat( addr + 100, u2 ); memPutFloat( addr + 104, v1 ); memPutShort( addr + 108, (short) 0xF0 ); diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java index 6b4847233..471a78fec 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java @@ -13,16 +13,19 @@ import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.apis.TableHelper; import dan200.computercraft.core.apis.TermMethods; +import dan200.computercraft.shared.util.Palette; +import dan200.computercraft.shared.util.StringUtil; import de.srendi.advancedperipherals.common.addons.computercraft.owner.BlockEntityPeripheralOwner; import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal; -import de.srendi.advancedperipherals.common.addons.computercraft.terminal.UltimateNetworkedTerminal.MonitorSide; +import org.apache.commons.lang3.ArrayUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Map; +import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.Map; -public class UltimateMonitorPeripheral extends TermMethods implements IPeripheral { +public class UltimateMonitorPeripheral implements IPeripheral { private final UltimateMonitorEntity monitor; public UltimateMonitorPeripheral(UltimateMonitorEntity monitor) { @@ -49,79 +52,21 @@ public void setPanelDepth(double panelDepth) throws LuaException { this.getTerminal().setPanelDepth((float)(panelDepth)); } - @LuaFunction - public double getTextTransparency() throws LuaException { - return (double)(this.getTerminal().getTextTransparency()) / 0xff; - } - - @LuaFunction - public void setTextTransparency(double transparency) throws LuaException { - this.getTerminal().setTextTransparency((int)(transparency * 0xff)); - } - - @LuaFunction - public double getBackgroundTransparency() throws LuaException { - return (double)(this.getTerminal().getBackgroundTransparency()) / 0xff; - } - - @LuaFunction - public void setBackgroundTransparency(double transparency) throws LuaException { - this.getTerminal().setBackgroundTransparency((int)(transparency * 0xff)); - } - - @LuaFunction - public Map getSideColor(IArguments args) throws LuaException { - if (args.count() < 1) { - throw new LuaException("getSideColor need one argument"); - } - MonitorSide side = MonitorSide.fromString(args.getString(0)); - if (side == null) { - throw new LuaException(String.format("Invalid monitor side %s", side)); - } - int[] color = this.getTerminal().getSideColor(side); - int a = this.getTerminal().getSideTransparency(side); - Map obj = new HashMap<>(); - obj.put("r", color[0]); - obj.put("g", color[1]); - obj.put("b", color[2]); - obj.put("a", a); - return obj; - } - - @LuaFunction - public void setSideColor(IArguments args) throws LuaException { - if (args.count() < 2) { - throw new LuaException("setSideColor need two arguments"); - } - MonitorSide side = MonitorSide.fromString(args.getString(0)); - if (side == null) { - throw new LuaException(String.format("Invalid monitor side %s", side)); - } - if (!(args.get(1) instanceof Map obj)) { - throw new LuaException("The second argument should be an rgba table"); - } - int r = (int) TableHelper.getNumberField(obj, "r"); - int g = (int) TableHelper.getNumberField(obj, "g"); - int b = (int) TableHelper.getNumberField(obj, "b"); - int a = (int) TableHelper.getNumberField(obj, "a"); - this.getTerminal().setSideColor(side, new int[]{r, g, b}); - this.getTerminal().setSideTransparency(side, a); - } - /** * Set the scale of this monitor. A larger scale will result in the monitor having a lower resolution, but display * text much larger. * - * @param scaleArg The monitor's scale. This must be a multiple of 0.5 between 0.5 and 5. + * @param scaleArg The monitor's scale. This must be a multiple of 0.1 between 0.1 and 5. * @throws LuaException If the scale is out of range. * @see #getTextScale() */ @LuaFunction - public final void setTextScale( double scaleArg ) throws LuaException - { - int scale = (int) (LuaValues.checkFinite( 0, scaleArg ) * 2.0); - if( scale < 1 || scale > 10 ) throw new LuaException( "Expected number in range 0.5-5" ); - getMonitor().setTextScale( scale ); + public final void setTextScale(double scaleArg) throws LuaException { + int scale = (int) (LuaValues.checkFinite(0, scaleArg) * 10.0); + if (scale < 1 || scale > 10 * 5) { + throw new LuaException( "Expected number in range 0.1-10" ); + } + getMonitor().setTextScale(scale); } /** @@ -134,7 +79,7 @@ public final void setTextScale( double scaleArg ) throws LuaException @LuaFunction public final double getTextScale() throws LuaException { - return getMonitor().getTextScale() / 2.0; + return getMonitor().getTextScale() / 10.0; } @Override @@ -156,26 +101,424 @@ public boolean equals( IPeripheral other ) } @Nonnull - private UltimateServerMonitor getMonitor() throws LuaException - { + private UltimateServerMonitor getMonitor() throws LuaException { UltimateServerMonitor monitor = this.monitor.getCachedServerMonitor(); - if( monitor == null ) throw new LuaException( "Monitor has been detached" ); + if (monitor == null) { + throw new LuaException("Monitor has been detached"); + } return monitor; } @Nonnull - @Override - public UltimateNetworkedTerminal getTerminal() throws LuaException - { + public UltimateNetworkedTerminal getTerminal() throws LuaException { UltimateNetworkedTerminal terminal = getMonitor().getTerminal(); - if( terminal == null ) throw new LuaException( "Monitor has been detached" ); + if (terminal == null) { + throw new LuaException("Monitor has been detached"); + } return terminal; } @Nullable @Override - public Object getTarget() - { + public Object getTarget() { return monitor; } + + //// From dan200.computercraft.core.apis.TermMethods /// + + private static int getHighestBit( int group ) + { + int bit = 0; + while( group > 0 ) + { + group >>= 1; + bit++; + } + return bit; + } + + /** + * Write {@code text} at the current cursor position, moving the cursor to the end of the text. + *

+ * Unlike functions like {@code write} and {@code print}, this does not wrap the text - it simply copies the + * text to the current terminal line. + * + * @param arguments The text to write. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.param text The text to write. + */ + @LuaFunction + public final void write( IArguments arguments ) throws LuaException + { + String text = StringUtil.toString( arguments.get( 0 ) ); + UltimateNetworkedTerminal terminal = getTerminal(); + synchronized( terminal ) + { + terminal.write( text ); + terminal.setCursorPos( terminal.getCursorX() + text.length(), terminal.getCursorY() ); + } + } + + /** + * Move all positions up (or down) by {@code y} pixels. + *

+ * Every pixel in the terminal will be replaced by the line {@code y} pixels below it. If {@code y} is negative, it + * will copy pixels from above instead. + * + * @param y The number of lines to move up by. This may be a negative number. + * @throws LuaException (hidden) If the terminal cannot be found. + */ + @LuaFunction + public final void scroll( int y ) throws LuaException + { + getTerminal().scroll( y ); + } + + /** + * Get the position of the cursor. + * + * @return The cursor's position. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.treturn number The x position of the cursor. + * @cc.treturn number The y position of the cursor. + */ + @LuaFunction + public final Object[] getCursorPos() throws LuaException + { + UltimateNetworkedTerminal terminal = getTerminal(); + return new Object[] { terminal.getCursorX() + 1, terminal.getCursorY() + 1 }; + } + + /** + * Set the position of the cursor. {@link #write(IArguments) terminal writes} will begin from this position. + * + * @param x The new x position of the cursor. + * @param y The new y position of the cursor. + * @throws LuaException (hidden) If the terminal cannot be found. + */ + @LuaFunction + public final void setCursorPos( int x, int y ) throws LuaException + { + UltimateNetworkedTerminal terminal = getTerminal(); + synchronized( terminal ) + { + terminal.setCursorPos( x - 1, y - 1 ); + } + } + + /** + * Checks if the cursor is currently blinking. + * + * @return If the cursor is blinking. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.since 1.80pr1.9 + */ + @LuaFunction + public final boolean getCursorBlink() throws LuaException + { + return getTerminal().getCursorBlink(); + } + + /** + * Sets whether the cursor should be visible (and blinking) at the current {@link #getCursorPos() cursor position}. + * + * @param blink Whether the cursor should blink. + * @throws LuaException (hidden) If the terminal cannot be found. + */ + @LuaFunction + public final void setCursorBlink( boolean blink ) throws LuaException + { + UltimateNetworkedTerminal terminal = getTerminal(); + synchronized( terminal ) + { + terminal.setCursorBlink( blink ); + } + } + + /** + * Get the size of the terminal. + * + * @return The terminal's size. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.treturn number The terminal's width. + * @cc.treturn number The terminal's height. + */ + @LuaFunction + public final Object[] getSize() throws LuaException + { + UltimateNetworkedTerminal terminal = getTerminal(); + return new Object[] { terminal.getWidth(), terminal.getHeight() }; + } + + /** + * Clears the terminal, filling it with the {@link #getBackgroundColour() current background colour}. + * + * @throws LuaException (hidden) If the terminal cannot be found. + */ + @LuaFunction + public final void clear() throws LuaException + { + getTerminal().clear(); + } + + /** + * Clears the line the cursor is currently on, filling it with the {@link #getBackgroundColour() current background + * colour}. + * + * @throws LuaException (hidden) If the terminal cannot be found. + */ + @LuaFunction + public final void clearLine() throws LuaException + { + getTerminal().clearLine(); + } + + /** + * Return the colour that new text will be written as. + * + * @return The current text colour. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.see colors For a list of colour constants, returned by this function. + * @cc.since 1.74 + */ + @LuaFunction( { "getTextColour", "getTextColor" } ) + public final int getTextColour() throws LuaException + { + return encodeColour( getTerminal().getTextColour() ); + } + + /** + * Set the colour that new text will be written as. + * + * @param colourArg The new text colour. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.see colors For a list of colour constants. + * @cc.since 1.45 + * @cc.changed 1.80pr1 Standard computers can now use all 16 colors, being changed to grayscale on screen. + */ + @LuaFunction( { "setTextColour", "setTextColor" } ) + public final void setTextColour( int colourArg ) throws LuaException + { + int colour = parseColour( colourArg ); + UltimateNetworkedTerminal terminal = getTerminal(); + synchronized( terminal ) + { + terminal.setTextColour( colour ); + } + } + + /** + * Return the current background colour. This is used when {@link #write writing text} and {@link #clear clearing} + * the terminal. + * + * @return The current background colour. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.see colors For a list of colour constants, returned by this function. + * @cc.since 1.74 + */ + @LuaFunction( { "getBackgroundColour", "getBackgroundColor" } ) + public final int getBackgroundColour() throws LuaException + { + return encodeColour( getTerminal().getBackgroundColour() ); + } + + /** + * Set the current background colour. This is used when {@link #write writing text} and {@link #clear clearing} the + * terminal. + * + * @param colourArg The new background colour. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.see colors For a list of colour constants. + * @cc.since 1.45 + * @cc.changed 1.80pr1 Standard computers can now use all 16 colors, being changed to grayscale on screen. + */ + @LuaFunction( { "setBackgroundColour", "setBackgroundColor" } ) + public final void setBackgroundColour( int colourArg ) throws LuaException + { + int colour = parseColour( colourArg ); + UltimateNetworkedTerminal terminal = getTerminal(); + synchronized( terminal ) + { + terminal.setBackgroundColour( colour ); + } + } + + /** + * Determine if this terminal supports colour. + *

+ * Terminals which do not support colour will still allow writing coloured text/backgrounds, but it will be + * displayed in greyscale. + * + * @return Whether this terminal supports colour. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.since 1.45 + */ + @LuaFunction( { "isColour", "isColor" } ) + public final boolean getIsColour() throws LuaException + { + return true; + } + + /** + * Writes {@code text} to the terminal with the specific foreground and background characters. + *

+ * As with {@link #write(IArguments)}, the text will be written at the current cursor location, with the cursor + * moving to the end of the text. + *

+ * {@code textColour} and {@code backgroundColour} must both be strings the same length as {@code text}. All + * characters represent a single hexadecimal digit, which is converted to one of CC's colours. For instance, + * {@code "a"} corresponds to purple. + * + * @param text The text to write. + * @param textColour The corresponding text colours. + * @param backgroundColour The corresponding background colours. + * @throws LuaException If the three inputs are not the same length. + * @cc.see colors For a list of colour constants, and their hexadecimal values. + * @cc.since 1.74 + * @cc.changed 1.80pr1 Standard computers can now use all 16 colors, being changed to grayscale on screen. + * @cc.usage Prints "Hello, world!" in rainbow text. + *

{@code
+     * term.blit("Hello, world!","01234456789ab","0000000000000")
+     * }
+ */ + @LuaFunction + public final void blit( ByteBuffer text, ByteBuffer textColour, ByteBuffer backgroundColour ) throws LuaException + { + if( textColour.remaining() != text.remaining() || backgroundColour.remaining() != text.remaining() ) + { + throw new LuaException( "Arguments must be the same length" ); + } + + UltimateNetworkedTerminal terminal = getTerminal(); + synchronized( terminal ) + { + terminal.blit( text, textColour, backgroundColour ); + terminal.setCursorPos( terminal.getCursorX() + text.remaining(), terminal.getCursorY() ); + } + } + + /** + * Set the palette for a specific colour. + *

+ * ComputerCraft's palette system allows you to change how a specific colour should be displayed. For instance, you + * can make @{colors.red} more red by setting its palette to #FF0000. This does now allow you to draw more + * colours - you are still limited to 16 on the screen at one time - but you can change which colours are + * used. + * + * @param args The new palette values. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.tparam [1] number index The colour whose palette should be changed. + * @cc.tparam number colour A 24-bit integer representing the RGB value of the colour. For instance the integer + * `0xFF0000` corresponds to the colour #FF0000. + * @cc.tparam [2] number index The colour whose palette should be changed. + * @cc.tparam number r The intensity of the red channel, between 0 and 1. + * @cc.tparam number g The intensity of the green channel, between 0 and 1. + * @cc.tparam number b The intensity of the blue channel, between 0 and 1. + * @cc.usage Change the @{colors.red|red colour} from the default #CC4C4C to #FF0000. + *

{@code
+     * term.setPaletteColour(colors.red, 0xFF0000)
+     * term.setTextColour(colors.red)
+     * print("Hello, world!")
+     * }
+ * @cc.usage As above, but specifying each colour channel separately. + *
{@code
+     * term.setPaletteColour(colors.red, 1, 0, 0)
+     * term.setTextColour(colors.red)
+     * print("Hello, world!")
+     * }
+ * @cc.see colors.unpackRGB To convert from the 24-bit format to three separate channels. + * @cc.see colors.packRGB To convert from three separate channels to the 24-bit format. + * @cc.since 1.80pr1 + */ + @LuaFunction( { "setPaletteColour", "setPaletteColor" } ) + public final void setPaletteColour(IArguments args) throws LuaException { + int colour = 15 - parseColour(args.getInt(0)); + switch (args.count()) { + case 2: + { + int hex = args.getInt(1); + double[] rgb = Palette.decodeRGB8(hex); + setColour(getTerminal(), colour, rgb[0], rgb[1], rgb[2]); + } + break; + case 4: + { + double r = args.getFiniteDouble(1); + double g = args.getFiniteDouble(2); + double b = args.getFiniteDouble(3); + setColour(getTerminal(), colour, r, g, b); + } + break; + case 3: + { + int hex = args.getInt(1); + double a = args.getFiniteDouble(2); + double[] rgb = Palette.decodeRGB8(hex); + setColour(getTerminal(), colour, rgb[0], rgb[1], rgb[2], a); + } + break; + case 5: + { + double r = args.getFiniteDouble(1); + double g = args.getFiniteDouble(2); + double b = args.getFiniteDouble(3); + double a = args.getFiniteDouble(3); + setColour(getTerminal(), colour, r, g, b, a); + } + break; + } + } + + /** + * Get the current palette for a specific colour. + * + * @param colourArg The colour whose palette should be fetched. + * @return The resulting colour. + * @throws LuaException (hidden) If the terminal cannot be found. + * @cc.treturn number The red channel, will be between 0 and 1. + * @cc.treturn number The green channel, will be between 0 and 1. + * @cc.treturn number The blue channel, will be between 0 and 1. + * @cc.since 1.80pr1 + */ + @LuaFunction( { "getPaletteColour", "getPaletteColor" } ) + public final Object[] getPaletteColour(int colourArg) throws LuaException { + int colour = 15 - parseColour(colourArg); + UltimateNetworkedTerminal terminal = getTerminal(); + synchronized(terminal) { + double[] rgb = terminal.getPalette().getColour(colour); + if (rgb == null) { + return null; + } + double a = terminal.getPaletteTransparency(colour); + return ArrayUtils.toObject(new double[]{ + rgb[0], + rgb[1], + rgb[2], + a, + }); + } + } + + public static int parseColour( int colour ) throws LuaException + { + if( colour <= 0 ) throw new LuaException( "Colour out of range" ); + colour = getHighestBit( colour ) - 1; + if( colour < 0 || colour > 15 ) throw new LuaException( "Colour out of range" ); + return colour; + } + + + public static int encodeColour(int colour) { + return 1 << colour; + } + + public static void setColour(UltimateNetworkedTerminal terminal, int colour, double r, double g, double b) { + terminal.getPalette().setColour(colour, r, g, b); + terminal.setChanged(); + } + + public static void setColour(UltimateNetworkedTerminal terminal, int colour, double r, double g, double b, double a) { + terminal.setPaletteTransparency(colour, (float)(a)); + setColour(terminal, colour, r, g, b); + } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java index 6efe8fe0e..983de37c7 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateServerMonitor.java @@ -13,27 +13,24 @@ import javax.annotation.Nullable; import java.util.concurrent.atomic.AtomicBoolean; -public class UltimateServerMonitor -{ +public class UltimateServerMonitor { private final UltimateMonitorEntity origin; - private int textScale = 2; + private int textScale = 10; private @Nullable UltimateNetworkedTerminal terminal; private final AtomicBoolean resized = new AtomicBoolean( false ); private final AtomicBoolean changed = new AtomicBoolean( false ); - UltimateServerMonitor( UltimateMonitorEntity origin ) - { + UltimateServerMonitor(UltimateMonitorEntity origin) { this.origin = origin; } - synchronized void rebuild() - { + synchronized void rebuild() { Terminal oldTerm = getTerminal(); int oldWidth = oldTerm == null ? -1 : oldTerm.getWidth(); int oldHeight = oldTerm == null ? -1 : oldTerm.getHeight(); - double textScale = this.textScale * 0.5; + double textScale = this.textScale * 0.1; int termWidth = (int) Math.max( Math.round( (origin.getWidth() - 2.0 * (UltimateMonitorEntity.RENDER_BORDER + UltimateMonitorEntity.RENDER_MARGIN)) / (textScale * 6.0 * UltimateMonitorEntity.RENDER_PIXEL_SCALE) ), 1.0 @@ -43,20 +40,16 @@ synchronized void rebuild() 1.0 ); - if( terminal == null ) - { - terminal = new UltimateNetworkedTerminal( termWidth, termHeight, this::markChanged ); + if (terminal == null) { + terminal = new UltimateNetworkedTerminal(termWidth, termHeight, this::markChanged); markChanged(); - } - else - { - terminal.resize( termWidth, termHeight ); + } else { + terminal.resize(termWidth, termHeight); } - if( oldWidth != termWidth || oldHeight != termHeight ) - { + if (oldWidth != termWidth || oldHeight != termHeight) { terminal.clear(); - resized.set( true ); + resized.set(true); markChanged(); } } @@ -71,9 +64,10 @@ int getTextScale() { return textScale; } - synchronized void setTextScale( int textScale ) - { - if( this.textScale == textScale ) return; + synchronized void setTextScale(int textScale) { + if (this.textScale == textScale) { + return; + } this.textScale = textScale; rebuild(); } @@ -88,8 +82,7 @@ boolean pollTerminalChanged() { @Nullable @VisibleForTesting - public UltimateNetworkedTerminal getTerminal() - { + public UltimateNetworkedTerminal getTerminal() { return terminal; } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java index 573471b53..2292678b2 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/terminal/UltimateNetworkedTerminal.java @@ -7,11 +7,10 @@ import java.util.Arrays; public class UltimateNetworkedTerminal extends NetworkedTerminal { + private static final int PALETTE_SIZE = 16; // sync with dan200.computercraft.shared.util.Palette + private float panelDepth = 0; - private int textTransparency = 0xff; - private int backgroundTransparency = 0xff; - private final int[] transparencies = new int[]{0, 0, 0}; - private final int[][] sideColors = new int[3][3]; + private final byte[] transparencies = new byte[PALETTE_SIZE]; public UltimateNetworkedTerminal(int width, int height) { this(width, height, null); @@ -19,6 +18,9 @@ public UltimateNetworkedTerminal(int width, int height) { public UltimateNetworkedTerminal(int width, int height, Runnable changedCallback) { super(width, height, true, changedCallback); + for (int i = 0; i < this.transparencies.length; i++) { + this.transparencies[i] = (byte)(0xff); + } } public float getPanelDepth() { @@ -34,72 +36,32 @@ public void setPanelDepth(float z) { setChanged(); } - public int getTextTransparency() { - return this.textTransparency; + public int getPaletteTransparencyByte(int color) { + return (int)(this.transparencies[color]) & 0xff; } - public void setTextTransparency(int transparency) { - transparency = Math.min(Math.max(transparency, 0), 0xff); - if (this.textTransparency == transparency) { - return; + public float getPaletteTransparency(int color) { + if (color < 0 || color >= this.transparencies.length) { + return 1; } - this.textTransparency = transparency; - setChanged(); + return (float)((int)(this.transparencies[color]) & 0xff) / 255.0f; } - public int getBackgroundTransparency() { - return this.backgroundTransparency; - } - - public void setBackgroundTransparency(int transparency) { - transparency = Math.min(Math.max(transparency, 0), 0xff); - if (this.backgroundTransparency == transparency) { + public void setPaletteTransparency(int color, float a) { + if (color < 0 || color >= this.transparencies.length) { return; } - this.backgroundTransparency = transparency; - setChanged(); - } - - public int getSideTransparency(MonitorSide side) { - return this.transparencies[side.getIndex()]; - } - - public void setSideTransparency(MonitorSide side, int transparency) { - transparency = Math.min(Math.max(transparency, 0), 0xff); - if (this.transparencies[side.getIndex()] == transparency) { - return; - } - this.transparencies[side.getIndex()] = transparency; - setChanged(); - } - - public int[] getSideColor(MonitorSide side) { - return this.sideColors[side.getIndex()]; - } - - public void setSideColor(MonitorSide side, int[] color) { - if (color.length != 3) { - throw new IllegalArgumentException("color.length must be 3"); - } - color[0] = Math.min(Math.max(color[0], 0), 0xff); - color[1] = Math.min(Math.max(color[1], 0), 0xff); - color[2] = Math.min(Math.max(color[2], 0), 0xff); - if (Arrays.equals(this.sideColors[side.getIndex()], color)) { - return; - } - this.sideColors[side.getIndex()] = color; - setChanged(); + a = Math.min(Math.max(a, 0), 1); + this.transparencies[color] = (byte)((int)(a * 0xff)); } @Override public synchronized void reset() { super.reset(); this.panelDepth = 0; - this.textTransparency = 0xff; - this.backgroundTransparency = 0xff; - this.transparencies[0] = 0; - this.transparencies[1] = 0; - this.transparencies[2] = 0; + for (int i = 0; i < this.transparencies.length; i++) { + this.transparencies[i] = (byte)(0xff); + } setChanged(); } @@ -107,22 +69,14 @@ public synchronized void reset() { public synchronized void write(FriendlyByteBuf buffer) { super.write(buffer); buffer.writeFloat(this.panelDepth); - buffer.writeByte(this.textTransparency); - buffer.writeByte(this.backgroundTransparency); - buffer.writeByte(this.transparencies[0]); - buffer.writeByte(this.transparencies[1]); - buffer.writeByte(this.transparencies[2]); + buffer.writeBytes(this.transparencies); } @Override public synchronized void read(FriendlyByteBuf buffer) { super.read(buffer); this.panelDepth = buffer.readFloat(); - this.textTransparency = (int)(buffer.readByte()) & 0xff; - this.backgroundTransparency = (int)(buffer.readByte()) & 0xff; - this.transparencies[0] = (int)(buffer.readByte()) & 0xff; - this.transparencies[1] = (int)(buffer.readByte()) & 0xff; - this.transparencies[2] = (int)(buffer.readByte()) & 0xff; + buffer.readBytes(this.transparencies); setChanged(); } @@ -130,13 +84,7 @@ public synchronized void read(FriendlyByteBuf buffer) { public synchronized CompoundTag writeToNBT(CompoundTag nbt) { super.writeToNBT(nbt); nbt.putFloat("term_panelDepth", this.panelDepth); - nbt.putByte("term_textTransparency", (byte) this.textTransparency); - nbt.putByte("term_backgroundTransparency", (byte) this.backgroundTransparency); - nbt.putByteArray("term_sideTransparencies", new byte[]{ - (byte) this.transparencies[0], - (byte) this.transparencies[1], - (byte) this.transparencies[2], - }); + nbt.putByteArray("term_paletteTransparencies", this.transparencies); return nbt; } @@ -144,39 +92,8 @@ public synchronized CompoundTag writeToNBT(CompoundTag nbt) { public synchronized void readFromNBT(CompoundTag nbt) { super.readFromNBT(nbt); this.panelDepth = nbt.getFloat("term_panelDepth"); - this.textTransparency = (int)(nbt.getByte("term_textTransparency")) & 0xff; - this.backgroundTransparency = (int)(nbt.getByte("term_backgroundTransparency")) & 0xff; - byte[] transparencies = nbt.getByteArray("term_sideTransparencies"); - if (transparencies.length == 3) { - this.transparencies[0] = (int)(transparencies[0]) & 0xff; - this.transparencies[1] = (int)(transparencies[1]) & 0xff; - this.transparencies[2] = (int)(transparencies[2]) & 0xff; - } + byte[] transparencies = nbt.getByteArray("term_paletteTransparencies"); + System.arraycopy(transparencies, 0, this.transparencies, 0, Math.min(transparencies.length, this.transparencies.length)); setChanged(); } - - public static enum MonitorSide { - FRONT(0), - LEFT(1), - TOP(2); - - private final int index; - - private MonitorSide(int index){ - this.index = index; - } - - public int getIndex() { - return index; - } - - public static MonitorSide fromString(String s) { - return switch (s.toLowerCase()) { - case "front", "back", "z" -> FRONT; - case "left", "right", "x" -> LEFT; - case "top", "bottom", "y" -> TOP; - default -> null; - }; - } - } } diff --git a/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh index 3d709dfed..78f65482c 100644 --- a/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh +++ b/src/main/resources/assets/advancedperipherals/shaders/core/monitor_tbo.fsh @@ -9,13 +9,11 @@ uniform sampler2D Sampler0; // Font uniform usamplerBuffer Tbo; layout(std140) uniform MonitorData { - vec3 Palette[16]; + vec4 Palette[16]; int Width; int Height; ivec2 CursorPos; int CursorColour; - float TextTransparency; - float BackgroundTransparency; }; uniform int CursorBlink; @@ -36,7 +34,7 @@ vec2 texture_corner(int index) { } vec4 recolour(vec4 texture, int colour) { - return vec4(texture.rgb * Palette[colour], texture.rgba); + return vec4(texture.rgb * Palette[colour].rgb, texture.rgba); } void main() { @@ -56,14 +54,15 @@ void main() { vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT); vec4 charTex = recolour(texture(Sampler0, (texture_corner(character) + pos) / 256.0), fg); + float textTransparency = Palette[fg].a; // Applies the cursor on top of the current character if we're blinking and in the current cursor's cell. We do it // this funky way to avoid branches. vec4 cursorTex = recolour(texture(Sampler0, (texture_corner(95) + pos) / 256.0), CursorColour); // 95 = '_' vec4 img = mix(charTex, cursorTex, cursorTex.a * float(CursorBlink) * (CursorPos == cell ? 1.0 : 0.0)); - float textAlpha = img.a * mult * TextTransparency; + float textAlpha = img.a * mult * textTransparency; - vec4 colour = (img.a * mult > 0 ? vec4(img.rgb, textAlpha) : vec4(Palette[bg], BackgroundTransparency)) * ColorModulator; + vec4 colour = (img.a * mult > 0 ? vec4(img.rgb, textAlpha) : Palette[bg]) * ColorModulator; - fragColor = colour;//linear_fog(colour, vertexDistance, FogStart, FogEnd, FogColor); + fragColor = linear_fog(colour, vertexDistance, FogStart, FogEnd, FogColor); } diff --git a/src/main/resources/data/computercraft/lua/rom/autorun/api_window_patch.lua b/src/main/resources/data/computercraft/lua/rom/autorun/api_window_patch.lua new file mode 100644 index 000000000..71935ba14 --- /dev/null +++ b/src/main/resources/data/computercraft/lua/rom/autorun/api_window_patch.lua @@ -0,0 +1,112 @@ + +local window_create = window.create + +local expect = dofile("rom/modules/main/cc/expect.lua").expect + +local tHex = { + [colors.white] = "0", + [colors.orange] = "1", + [colors.magenta] = "2", + [colors.lightBlue] = "3", + [colors.yellow] = "4", + [colors.lime] = "5", + [colors.pink] = "6", + [colors.gray] = "7", + [colors.lightGray] = "8", + [colors.cyan] = "9", + [colors.purple] = "a", + [colors.blue] = "b", + [colors.brown] = "c", + [colors.green] = "d", + [colors.red] = "e", + [colors.black] = "f", +} + +local string_rep = string.rep + +--- patch window.create +function window.create(parent, nX, nY, nWidth, nHeight, bStartVisible) + expect(1, parent, "table") + expect(2, nX, "number") + expect(3, nY, "number") + expect(4, nWidth, "number") + expect(5, nHeight, "number") + expect(6, bStartVisible, "boolean", "nil") + + if parent == term then + error("term is not a recommended window parent, try term.current() instead", 2) + end + + local tPalette = {} + do + for i = 0, 15 do + local c = 2 ^ i + tPalette[c] = { parent.getPaletteColour(c) } + end + end + + local function updatePalette() + for k, v in pairs(tPalette) do + parent.setPaletteColour(k, v[1], v[2], v[3], v[4]) + end + end + + local w = window_create(parent, nX, nY, nWidth, nHeight, bStartVisible) + + --- patch window.setPaletteColour + function w.setPaletteColour(colour, r, g, b, a) + expect(1, colour, "number") + + if tHex[colour] == nil then + error("Invalid color (got " .. colour .. ")" , 2) + end + + expect(2, r, "number", "nil") + expect(3, g, "number", "nil") + expect(4, b, "number", "nil") + expect(5, a, "number", "nil") + local tCol + if g == nil then + tCol = { colours.unpackRGB(r), 1 } + tPalette[colour] = tCol + elseif b == nil then + tCol = { colours.unpackRGB(r), g } + tPalette[colour] = tCol + else + tCol = tPalette[colour] + tCol[1] = r + tCol[2] = g + tCol[3] = b + tCol[4] = a == nil and 1 or a + end + + if bVisible then + return parent.setPaletteColour(colour, tCol[1], tCol[2], tCol[3], tCol[4]) + end + end + + w.setPaletteColor = w.setPaletteColour + + --- patch window.getPaletteColour + function w.getPaletteColour(colour) + expect(1, colour, "number") + if tHex[colour] == nil then + error("Invalid color (got " .. colour .. ")" , 2) + end + local tCol = tPalette[colour] + return tCol[1], tCol[2], tCol[3], tCol[4] + end + + w.getPaletteColor = w.getPaletteColour + + local window_redraw = w.redraw + --- patch window.redraw + function w.redraw() + if w.isVisible() then + window_redraw() + updatePalette() + end + end + + return w +end From 514dffa0f0ec8828ac4e1482e2d8a5efcab90300 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Sat, 27 Jul 2024 18:09:31 -0600 Subject: [PATCH 5/5] make window API patch optional --- .../monitor/UltimateMonitorEntity.java | 45 ++++++++++--------- .../monitor/UltimateMonitorPeripheral.java | 2 +- .../lua/rom/autorun/00_advanceperipherals.lua | 16 +++++++ .../advancedperipherals/window.lua} | 7 ++- .../computercraft/lua/rom/programs/flat.lua | 27 ++++++++++- 5 files changed, 73 insertions(+), 24 deletions(-) create mode 100644 src/main/resources/data/computercraft/lua/rom/autorun/00_advanceperipherals.lua rename src/main/resources/data/computercraft/lua/rom/{autorun/api_window_patch.lua => patches/advancedperipherals/window.lua} (96%) diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java index b82994421..b9a555fb3 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorEntity.java @@ -573,42 +573,47 @@ private void validate() } // endregion - private void monitorTouched( float xPos, float yPos, float zPos, @Nullable Player player ) - { + private void monitorTouched(float xPos, float yPos, float zPos, @Nullable Player player) { XYPair pair = XYPair - .of( xPos, yPos, zPos, getDirection(), getOrientation() ) - .add( xIndex, height - yIndex - 1 ); + .of(xPos, yPos, zPos, getDirection(), getOrientation()) + .add(xIndex, height - yIndex - 1); - if( pair.x() > width - RENDER_BORDER || pair.y() > height - RENDER_BORDER || pair.x() < RENDER_BORDER || pair.y() < RENDER_BORDER ) - { + if (pair.x() > width - RENDER_BORDER || pair.y() > height - RENDER_BORDER || pair.x() < RENDER_BORDER || pair.y() < RENDER_BORDER) { return; } UltimateServerMonitor serverTerminal = getServerMonitor(); - if( serverTerminal == null ) return; + if (serverTerminal == null) { + return; + } UltimateNetworkedTerminal originTerminal = serverTerminal.getTerminal(); - if( originTerminal == null ) return; + if (originTerminal == null) { + return; + } double xCharWidth = (width - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getWidth(); double yCharHeight = (height - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getHeight(); - int xCharPos = (int) Math.min( originTerminal.getWidth(), Math.max( (pair.x() - RENDER_BORDER - RENDER_MARGIN) / xCharWidth + 1.0, 1.0 ) ); - int yCharPos = (int) Math.min( originTerminal.getHeight(), Math.max( (pair.y() - RENDER_BORDER - RENDER_MARGIN) / yCharHeight + 1.0, 1.0 ) ); + // TODO: sight check for depth panel + int xCharPos = (int) Math.min(originTerminal.getWidth(), Math.max((pair.x() - RENDER_BORDER - RENDER_MARGIN) / xCharWidth + 1.0, 1.0)); + int yCharPos = (int) Math.min(originTerminal.getHeight(), Math.max((pair.y() - RENDER_BORDER - RENDER_MARGIN) / yCharHeight + 1.0, 1.0)); - eachComputer( c -> c.queueEvent( "monitor_touch", c.getAttachmentName(), xCharPos, yCharPos, player == null ? null : player.getName().getString() ) ); + eachComputer(c -> c.queueEvent("monitor_touch", c.getAttachmentName(), xCharPos, yCharPos, + player == null ? null : player.getName().getString())); } - private void eachComputer( Consumer fun ) - { - for( int x = 0; x < width; x++ ) - { - for( int y = 0; y < height; y++ ) - { - UltimateMonitorEntity monitor = getLoadedMonitor( x, y ).getMonitor(); - if( monitor == null ) continue; + private void eachComputer(Consumer fun) { + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + UltimateMonitorEntity monitor = getLoadedMonitor(x, y).getMonitor(); + if (monitor == null) { + continue; + } - for( IComputerAccess computer : monitor.computers ) fun.accept( computer ); + for (IComputerAccess computer : monitor.computers) { + fun.accept(computer); + } } } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java index 471a78fec..aa9ba4cf2 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/monitor/UltimateMonitorPeripheral.java @@ -462,7 +462,7 @@ public final void setPaletteColour(IArguments args) throws LuaException { double r = args.getFiniteDouble(1); double g = args.getFiniteDouble(2); double b = args.getFiniteDouble(3); - double a = args.getFiniteDouble(3); + double a = args.getFiniteDouble(4); setColour(getTerminal(), colour, r, g, b, a); } break; diff --git a/src/main/resources/data/computercraft/lua/rom/autorun/00_advanceperipherals.lua b/src/main/resources/data/computercraft/lua/rom/autorun/00_advanceperipherals.lua new file mode 100644 index 000000000..5bfaa6af2 --- /dev/null +++ b/src/main/resources/data/computercraft/lua/rom/autorun/00_advanceperipherals.lua @@ -0,0 +1,16 @@ + +settings.define("ap.patch.window", { + description = "Enable window API patch for alpha palette support", + type = "boolean", + default = true, +}) + +-- Not an easter egg +settings.define("ap.power.to.turn.earth.flat", { + type = "number", + default = 0, +}) + +if settings.get("ap.patch.window") then + dofile("rom/patches/advancedperipherals/window.lua") +end diff --git a/src/main/resources/data/computercraft/lua/rom/autorun/api_window_patch.lua b/src/main/resources/data/computercraft/lua/rom/patches/advancedperipherals/window.lua similarity index 96% rename from src/main/resources/data/computercraft/lua/rom/autorun/api_window_patch.lua rename to src/main/resources/data/computercraft/lua/rom/patches/advancedperipherals/window.lua index 71935ba14..b2b11db01 100644 --- a/src/main/resources/data/computercraft/lua/rom/autorun/api_window_patch.lua +++ b/src/main/resources/data/computercraft/lua/rom/patches/advancedperipherals/window.lua @@ -1,6 +1,4 @@ -local window_create = window.create - local expect = dofile("rom/modules/main/cc/expect.lua").expect local tHex = { @@ -23,6 +21,7 @@ local tHex = { } local string_rep = string.rep +local window_create = window.create --- patch window.create function window.create(parent, nX, nY, nWidth, nHeight, bStartVisible) @@ -53,6 +52,10 @@ function window.create(parent, nX, nY, nWidth, nHeight, bStartVisible) local w = window_create(parent, nX, nY, nWidth, nHeight, bStartVisible) + function w.isUltimate() + return parent.isUltimate and parent.isUltimate() + end + --- patch window.setPaletteColour function w.setPaletteColour(colour, r, g, b, a) expect(1, colour, "number") diff --git a/src/main/resources/data/computercraft/lua/rom/programs/flat.lua b/src/main/resources/data/computercraft/lua/rom/programs/flat.lua index 05956edff..3d640be82 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/flat.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/flat.lua @@ -1 +1,26 @@ -print("The earth is flat, at least in minecraft!") \ No newline at end of file + +print("The earth is flat, at least in minecraft!") + +local power = settings.get("ap.power.to.turn.earth.flat") +settings.set("ap.power.to.turn.earth.flat", power + 1) +settings.save() + +if power >= 100 and power % 10 == 0 then + term.setTextColor(colors.yellow) + term.write('> ') + term.setTextColor(colors.white) + term.setCursorBlink(true) + sleep(1) + term.setCursorBlink(false) + printError('\nERR: power supply is low, entering power saving mode ...') + peripheral.find('monitor', function(_, monitor) + if monitor.isUltimate then + for i = 0, 15 do + local c = 2 ^ i + local r, g, b = monitor.getPaletteColor(c) + monitor.setPaletteColor(c, r, g, b, 0.11) + sleep() + end + end + end) +end