Merge branch '1.18-forge' into rus
This commit is contained in:
58
.github/workflows/publish.yml
vendored
58
.github/workflows/publish.yml
vendored
@@ -2,11 +2,10 @@ name: publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [ published ]
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: publish
|
||||
publish-github:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -54,10 +53,49 @@ jobs:
|
||||
GITHUB_MAVEN_URL: 'https://maven.pkg.github.com/${{ github.repository }}'
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - name: Publish to Curseforge
|
||||
# run: ./gradlew -Psemver='${{ steps.split_tag.outputs._1 }}' curseforge
|
||||
# env:
|
||||
# GPR_USER: ${{ secrets.GPR_USER }}
|
||||
# GPR_KEY: ${{ secrets.GPR_KEY }}
|
||||
# CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
|
||||
# CHANGELOG: ${{ github.event.release.body }}
|
||||
publish-curse:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Extract Version from Tag
|
||||
uses: rishabhgupta/split-by@v1
|
||||
id: split_tag
|
||||
with:
|
||||
string: ${{ github.event.release.tag_name }}
|
||||
split-by: '/'
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 17
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ubuntu-latest-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: ubuntu-latest-gradle-
|
||||
|
||||
# Set Curseforge release type based on pre-release flag.
|
||||
- name: Set release type to 'release'
|
||||
run: |
|
||||
echo "CURSEFORGE_RELEASE_TYPE=release" >> $GITHUB_ENV
|
||||
if: github.event.release.prerelease == false
|
||||
- name: Set release type to 'alpha'
|
||||
run: |
|
||||
echo "CURSEFORGE_RELEASE_TYPE=alpha" >> $GITHUB_ENV
|
||||
if: github.event.release.prerelease == true
|
||||
|
||||
- name: Publish to Curseforge
|
||||
run: ./gradlew -Psemver='${{ steps.split_tag.outputs._1 }}' curseforge
|
||||
env:
|
||||
GPR_USER: ${{ secrets.GPR_USER }}
|
||||
GPR_KEY: ${{ secrets.GPR_KEY }}
|
||||
CURSEFORGE_API_KEY: ${{ secrets.CURSEFORGE_API_KEY }}
|
||||
CURSEFORGE_RELEASE_TYPE: ${{ env.CURSEFORGE_RELEASE_TYPE }}
|
||||
CHANGELOG: ${{ github.event.release.body }}
|
||||
|
||||
@@ -58,7 +58,7 @@ repositories {
|
||||
dependencies {
|
||||
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
||||
|
||||
implementation "li.cil.sedna:sedna-${minecraft_version}-forge:1.0.3"
|
||||
implementation "li.cil.sedna:sedna-${minecraft_version}-forge:1.0.5"
|
||||
|
||||
implementation fg.deobf("curse.maven:markdownmanual-502485:3565800")
|
||||
|
||||
@@ -184,7 +184,7 @@ curseforge {
|
||||
apiKey = System.getenv('CURSEFORGE_API_KEY') ?: ""
|
||||
project {
|
||||
id = curse_project_id
|
||||
releaseType = curse_project_releaseType
|
||||
releaseType = System.getenv('CURSEFORGE_RELEASE_TYPE') ?: "alpha"
|
||||
changelogType = 'markdown'
|
||||
changelog = System.getenv("CHANGELOG") ?: "Changelog not available."
|
||||
addGameVersion 'Forge'
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=false
|
||||
|
||||
forge_version=39.0.4
|
||||
forge_version=39.0.13
|
||||
|
||||
semver=0.0.0
|
||||
|
||||
curse_project_id=437654
|
||||
curse_project_releaseType=alpha
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.UUID;
|
||||
* connected to this element.
|
||||
* <p>
|
||||
* This interface is relevant when implementing means to extend the bus, e.g.
|
||||
* to provide a custom cable implementation or some kind of a device container.
|
||||
* to provide a custom cable implementation or some kind of device container.
|
||||
* <p>
|
||||
* Implementations <em>must</em> call {@link #scheduleScan()} when they become
|
||||
* invalid, e.g. due to being in a chunk that is being unloaded or the block
|
||||
@@ -31,7 +31,7 @@ public interface DeviceBusElement extends DeviceBus {
|
||||
* <p>
|
||||
* This will be called by {@link DeviceBusController}s when scanning.
|
||||
* <p>
|
||||
* Bus elements can be have multiple controllers at the same time. This is used
|
||||
* Bus elements can have multiple controllers at the same time. This is used
|
||||
* by controllers to detect each other on the bus.
|
||||
* <p>
|
||||
* When {@link #scheduleScan()} is called, {@link DeviceBusController#scheduleBusScan()}
|
||||
@@ -89,7 +89,7 @@ public interface DeviceBusElement extends DeviceBus {
|
||||
* track of the device. Note that some device types (e.g. {@link RPCDevice}s)
|
||||
* require for an ID to be provided for them to work at all.
|
||||
* <p>
|
||||
* It is possible for multiple devices to have the same identifier. Typically
|
||||
* It is possible for multiple devices to have the same identifier. Typically,
|
||||
* this means they represent a view on the same underlying object. How this is
|
||||
* handled depends on the device type and may or may not be supported.
|
||||
* <p>
|
||||
|
||||
@@ -31,7 +31,7 @@ public interface Firmware extends IForgeRegistryEntry<Firmware> {
|
||||
* <p>
|
||||
* This will usually load machine code into memory at the specified start address.
|
||||
* <p>
|
||||
* Typically only returns {@code false} when there was not enough memory to fit the firmware.
|
||||
* Typically, only returns {@code false} when there was not enough memory to fit the firmware.
|
||||
*
|
||||
* @param memory access to the memory map of the machine.
|
||||
* @param startAddress the memory address where execution will commence.
|
||||
|
||||
@@ -28,7 +28,7 @@ public interface BlockDeviceQuery {
|
||||
BlockPos getQueryPosition();
|
||||
|
||||
/**
|
||||
* The side of the block this query is performed on, if any.
|
||||
* The world-space side of the block this query is performed on, if any.
|
||||
* <p>
|
||||
* May be {@code null} just as when requesting a capability from a {@link BlockEntity}.
|
||||
*
|
||||
|
||||
@@ -97,10 +97,6 @@ public interface VMContext {
|
||||
/**
|
||||
* Waits for the executor thread of the virtual machine to finish running.
|
||||
* <p>
|
||||
* Events subscribers can only be registered inside {@link VMDevice#mount(VMContext)}.
|
||||
* Trying to register subscribers after that method has returned will result in an
|
||||
* exception.
|
||||
* <p>
|
||||
* Note that this may trigger a {@link li.cil.oc2.api.bus.device.vm.event.VMPausingEvent}
|
||||
* if the virtual machine has not been paused before. Calling this on a paused virtual
|
||||
* machine is a no-op.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package li.cil.oc2.api.capabilities;
|
||||
|
||||
import li.cil.oc2.api.bus.device.provider.BlockDeviceQuery;
|
||||
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
/**
|
||||
* This interface provides access to a list of {@link Player}s that are currently
|
||||
@@ -8,6 +12,10 @@ import net.minecraft.world.entity.player.Player;
|
||||
* <p>
|
||||
* For example, for computers and robots this is the list of players that currently have
|
||||
* the terminal UI opened.
|
||||
* <p>
|
||||
* Must be implemented by the {@link BlockEntity} or {@link Entity} that serves as the
|
||||
* context for device creation via {@link BlockDeviceQuery}s or {@link ItemDeviceQuery}s,
|
||||
* respectively.
|
||||
*/
|
||||
public interface TerminalUserProvider {
|
||||
/**
|
||||
|
||||
@@ -32,14 +32,17 @@ public enum RobotOperationSide {
|
||||
this(parent.direction);
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public static Direction getAdjustedDirection(@Nullable final RobotOperationSide side, final Entity entity) {
|
||||
/**
|
||||
* Gets the world-space direction for the specified side relative to the specified entity.
|
||||
*
|
||||
* @param entity the entity to which the side is relative.
|
||||
* @param side the side to convert to a world-space direction.
|
||||
* @return a world-space direction.
|
||||
*/
|
||||
public static Direction toGlobal(final Entity entity, @Nullable final RobotOperationSide side) {
|
||||
Direction direction = side == null
|
||||
? RobotOperationSide.FRONT.getDirection()
|
||||
: side.getDirection();
|
||||
? RobotOperationSide.FRONT.direction
|
||||
: side.direction;
|
||||
if (direction.getAxis().isHorizontal()) {
|
||||
final int horizontalIndex = entity.getDirection().get2DDataValue();
|
||||
for (int i = 0; i < horizontalIndex; i++) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import javax.annotation.Nullable;
|
||||
* This enum indicates a side of a block device.
|
||||
* <p>
|
||||
* It is intended to be used by {@link li.cil.oc2.api.bus.device.rpc.RPCDevice} APIs,
|
||||
* providing both convenience for the called by providing a range of aliases, and also
|
||||
* providing both convenience for the caller by providing a range of aliases, and also
|
||||
* stability, in case Mojang decide to rename the enum fields of the {@link Direction}
|
||||
* enum at some time in the future.
|
||||
*/
|
||||
@@ -67,14 +67,6 @@ public enum Side {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public int get2DDataValue() {
|
||||
return direction.get2DDataValue();
|
||||
}
|
||||
|
||||
public int get3DDataValue() {
|
||||
return direction.get3DDataValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return base != null ? base.toString() : super.toString();
|
||||
|
||||
@@ -9,9 +9,11 @@ import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.container.AbstractMachineTerminalContainer;
|
||||
import li.cil.oc2.common.util.TooltipUtils;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.FormattedText;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
@@ -103,6 +105,10 @@ public abstract class AbstractMachineTerminalScreen<T extends AbstractMachineTer
|
||||
super.init();
|
||||
terminalWidget.init();
|
||||
|
||||
final EditBox focusIndicatorEditBox = new EditBox(font, 0, 0, 0, 0, TextComponent.EMPTY);
|
||||
focusIndicatorEditBox.setFocus(true);
|
||||
setFocusIndicatorEditBox(focusIndicatorEditBox);
|
||||
|
||||
addRenderableWidget(new ToggleImageButton(
|
||||
this, leftPos - Sprites.SIDEBAR_3.width + 4, topPos + CONTROLS_TOP + 4,
|
||||
12, 12,
|
||||
@@ -168,6 +174,11 @@ public abstract class AbstractMachineTerminalScreen<T extends AbstractMachineTer
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
// We use this text box to indicate to Forge that we want all input, and event handlers should not be allowed
|
||||
// to steal input from us (e.g. via custom key bindings). Since Forge is lazy and just uses getDeclaredFields
|
||||
// to get private fields, which completely skips fields in base classes, we require subclasses to hold the field...
|
||||
protected abstract void setFocusIndicatorEditBox(final EditBox editBox);
|
||||
|
||||
@Override
|
||||
protected void renderBg(final PoseStack stack, final float partialTicks, final int mouseX, final int mouseY) {
|
||||
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.client.gui;
|
||||
|
||||
import li.cil.oc2.common.container.ComputerTerminalContainer;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
@@ -8,7 +9,18 @@ import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public final class ComputerTerminalScreen extends AbstractMachineTerminalScreen<ComputerTerminalContainer> {
|
||||
@SuppressWarnings("all") private EditBox focusIndicatorEditBox;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public ComputerTerminalScreen(final ComputerTerminalContainer container, final Inventory playerInventory, final Component title) {
|
||||
super(container, playerInventory, title);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void setFocusIndicatorEditBox(final EditBox editBox) {
|
||||
focusIndicatorEditBox = editBox;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
@@ -17,6 +18,7 @@ import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static li.cil.oc2.common.util.TranslationUtils.text;
|
||||
@@ -181,7 +183,7 @@ public final class FileChooserScreen extends Screen {
|
||||
|
||||
final FileList.FileEntry selected = fileList.getSelected();
|
||||
if (selected != null) {
|
||||
return selected.file.equals(directory.getParent());
|
||||
return Objects.equals(selected.file, directory.getParent());
|
||||
}
|
||||
|
||||
final String selectedFileEntry = fileNameTextField.getValue();
|
||||
@@ -191,7 +193,7 @@ public final class FileChooserScreen extends Screen {
|
||||
private Optional<Path> getPath() {
|
||||
final FileList.FileEntry selected = fileList.getSelected();
|
||||
if (selected != null) {
|
||||
return Optional.of(selected.file);
|
||||
return Optional.ofNullable(selected.file);
|
||||
}
|
||||
|
||||
if (directory == null) {
|
||||
@@ -277,7 +279,7 @@ public final class FileChooserScreen extends Screen {
|
||||
super(FileChooserScreen.this.getMinecraft(), FileChooserScreen.this.width, FileChooserScreen.this.height, y, y + height, slotHeight);
|
||||
}
|
||||
|
||||
public void refreshFiles(@Nullable Path directory) {
|
||||
public void refreshFiles(@Nullable final Path directory) {
|
||||
FileChooserScreen.directory = directory;
|
||||
|
||||
setScrollAmount(0);
|
||||
@@ -328,7 +330,7 @@ public final class FileChooserScreen extends Screen {
|
||||
refreshFiles(path);
|
||||
} else {
|
||||
refreshFiles(path.getParent());
|
||||
children().stream().filter(entry -> entry.file.equals(path))
|
||||
children().stream().filter(entry -> path.equals(entry.file))
|
||||
.findFirst().ifPresent(entry -> {
|
||||
entry.select();
|
||||
centerScrollOn(entry);
|
||||
@@ -350,18 +352,21 @@ public final class FileChooserScreen extends Screen {
|
||||
return createDirectoryEntry(path, path.getFileName().toString() + path.getFileSystem().getSeparator());
|
||||
}
|
||||
|
||||
private FileList.FileEntry createDirectoryEntry(final Path path, final String displayName) {
|
||||
private FileList.FileEntry createDirectoryEntry(@Nullable final Path path, final String displayName) {
|
||||
final TextColor color = path != null && Files.exists(path)
|
||||
? TextColor.fromRgb(0xA0A0FF)
|
||||
: TextColor.fromLegacyFormat(ChatFormatting.GRAY);
|
||||
return new FileList.FileEntry(path, new TextComponent(displayName)
|
||||
.withStyle(s -> s.withColor(TextColor.fromRgb(0xA0A0FF))));
|
||||
.withStyle(s -> s.withColor(color)));
|
||||
}
|
||||
|
||||
private final class FileEntry extends ObjectSelectionList.Entry<FileEntry> {
|
||||
private final Path file;
|
||||
@Nullable private final Path file;
|
||||
private final Component displayName;
|
||||
|
||||
private long lastEntryClickTime = 0;
|
||||
|
||||
public FileEntry(final Path file, final Component displayName) {
|
||||
public FileEntry(@Nullable final Path file, final Component displayName) {
|
||||
this.file = file;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
@@ -379,7 +384,7 @@ public final class FileChooserScreen extends Screen {
|
||||
select();
|
||||
|
||||
final boolean isDoubleClick = System.currentTimeMillis() - lastEntryClickTime < 250;
|
||||
if (isDoubleClick) {
|
||||
if (isDoubleClick && okButton.active) {
|
||||
confirm();
|
||||
}
|
||||
|
||||
@@ -390,11 +395,13 @@ public final class FileChooserScreen extends Screen {
|
||||
}
|
||||
|
||||
public void select() {
|
||||
if (directory != null && file.equals(directory.getParent())) {
|
||||
if (directory != null && Objects.equals(directory.getParent(), file)) {
|
||||
fileNameTextField.setValue("..");
|
||||
} else {
|
||||
} else if (file != null) {
|
||||
final Path fileName = file.getFileName();
|
||||
fileNameTextField.setValue(fileName != null ? fileName.toString() : file.toString());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
fileNameTextField.moveCursorToStart();
|
||||
fileNameTextField.setHighlightPos(0);
|
||||
|
||||
@@ -90,7 +90,9 @@ public final class MachineTerminalWidget extends GuiComponent {
|
||||
}
|
||||
|
||||
public boolean charTyped(final char ch, final int modifier) {
|
||||
terminal.putInput((byte) ch);
|
||||
if (modifier == 0 || modifier == GLFW.GLFW_MOD_SHIFT) {
|
||||
terminal.putInput((byte) ch);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
package li.cil.oc2.client.gui;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Quaternion;
|
||||
import com.mojang.math.Vector3f;
|
||||
import li.cil.oc2.client.gui.widget.Texture;
|
||||
import li.cil.oc2.client.renderer.ModRenderType;
|
||||
import li.cil.oc2.common.item.Items;
|
||||
import li.cil.oc2.common.item.NetworkInterfaceCardItem;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.network.message.NetworkInterfaceCardConfigurationMessage;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static li.cil.oc2.common.util.TranslationUtils.key;
|
||||
import static li.cil.oc2.common.util.TranslationUtils.text;
|
||||
|
||||
public final class NetworkInterfaceCardScreen extends Screen {
|
||||
private static final String SIDE_STATE_TEXT = key("gui.{mod}.network_interface_card.side_state");
|
||||
private static final Component CONNECTIVITY_ENABLED_TEXT = text("gui.{mod}.network_interface_card.connectivity.enabled");
|
||||
private static final Component CONNECTIVITY_DISABLED_TEXT = text("gui.{mod}.network_interface_card.connectivity.disabled");
|
||||
private static final Component INFO_TEXT = text("gui.{mod}.network_interface_card.info");
|
||||
|
||||
public static final int UI_WIDTH = Sprites.NETWORK_INTERFACE_CARD_SCREEN.width;
|
||||
public static final int UI_HEIGHT = Sprites.NETWORK_INTERFACE_CARD_SCREEN.height;
|
||||
public static final int BLOCK_LEFT = UI_WIDTH / 2;
|
||||
public static final int BLOCK_TOP = 53;
|
||||
public static final int INFO_TEXT_LEFT = 8;
|
||||
public static final int INFO_TEXT_TOP = 104;
|
||||
public static final int INFO_TEXT_WIDTH = UI_WIDTH - 16;
|
||||
public static final int MAX_BLOCK_PITCH = 30;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final Player player;
|
||||
private final InteractionHand hand;
|
||||
|
||||
private final ComputerBlockItemRenderer computerBlockItemRenderer = new ComputerBlockItemRenderer();
|
||||
|
||||
private Vector3f blockRotation = new Vector3f(-30, 45, 0);
|
||||
private int left, top;
|
||||
@Nullable private Direction focusedSide;
|
||||
private boolean isDraggingBlock, hasDraggedBlock;
|
||||
private double dragStartX, dragStartY;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public NetworkInterfaceCardScreen(final Player player, final InteractionHand hand) {
|
||||
super(Items.NETWORK_INTERFACE_CARD.get().getDescription());
|
||||
this.player = player;
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
|
||||
left = (width - UI_WIDTH) / 2;
|
||||
top = (height - UI_HEIGHT) / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
final ItemStack heldItem = player.getItemInHand(hand);
|
||||
if (!heldItem.is(Items.NETWORK_INTERFACE_CARD.get())) {
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(final double mouseX, final double mouseY, final int button) {
|
||||
final boolean result = super.mouseClicked(mouseX, mouseY, button);
|
||||
|
||||
if (!result && isMouseInBlockArea(mouseX, mouseY) && button == 0) {
|
||||
isDraggingBlock = true;
|
||||
hasDraggedBlock = false;
|
||||
dragStartX = mouseX;
|
||||
dragStartY = mouseY;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(final double mouseX, final double mouseY, final int button) {
|
||||
if (isDraggingBlock && button == 0) {
|
||||
isDraggingBlock = false;
|
||||
if (!hasDraggedBlock && focusedSide != null) {
|
||||
final NetworkInterfaceCardConfigurationMessage message = new NetworkInterfaceCardConfigurationMessage(hand, focusedSide, !getConfiguration(focusedSide));
|
||||
Network.INSTANCE.sendToServer(message);
|
||||
Minecraft.getInstance().getSoundManager()
|
||||
.play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1));
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseReleased(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(final double mouseX, final double mouseY, final int activeButton, final double deltaX, final double deltaY) {
|
||||
if (isDraggingBlock && activeButton == 0) {
|
||||
if (!hasDraggedBlock) {
|
||||
final double dx = mouseX - dragStartX;
|
||||
final double dy = mouseY - dragStartY;
|
||||
final double delta = Math.sqrt(dx * dx + dy * dy);
|
||||
hasDraggedBlock = delta > 3;
|
||||
}
|
||||
if (hasDraggedBlock) {
|
||||
blockRotation = new Vector3f(
|
||||
Mth.clamp(blockRotation.x() - (float) deltaY, -MAX_BLOCK_PITCH, MAX_BLOCK_PITCH),
|
||||
Mth.wrapDegrees(blockRotation.y() + (float) deltaX),
|
||||
blockRotation.z()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final PoseStack stack, final int mouseX, final int mouseY, final float partialTicks) {
|
||||
renderBackground(stack);
|
||||
Sprites.NETWORK_INTERFACE_CARD_SCREEN.draw(stack, left, top);
|
||||
|
||||
super.render(stack, mouseX, mouseY, partialTicks);
|
||||
|
||||
final int blockX = left + BLOCK_LEFT;
|
||||
final int blockY = top + BLOCK_TOP;
|
||||
focusedSide = computerBlockItemRenderer.getFocusedSide(blockX - mouseX, blockY - mouseY, blockRotation);
|
||||
computerBlockItemRenderer.render(blockX, blockY, blockRotation);
|
||||
|
||||
if (focusedSide != null) {
|
||||
final Component enabledComponent = getConfiguration(focusedSide) ? CONNECTIVITY_ENABLED_TEXT : CONNECTIVITY_DISABLED_TEXT;
|
||||
final TranslatableComponent tooltip = new TranslatableComponent(SIDE_STATE_TEXT, enabledComponent);
|
||||
renderTooltip(stack, tooltip, mouseX, mouseY);
|
||||
}
|
||||
|
||||
font.drawWordWrap(INFO_TEXT, left + INFO_TEXT_LEFT, top + INFO_TEXT_TOP, INFO_TEXT_WIDTH, 0xAAAAAA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPauseScreen() {
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean isMouseInBlockArea(final double mouseX, final double mouseY) {
|
||||
return mouseX >= left + 37 && mouseX <= left + (37 + 102) &&
|
||||
mouseY >= top + 10 && mouseY <= top + (10 + 102);
|
||||
}
|
||||
|
||||
private boolean getConfiguration(@Nullable final Direction side) {
|
||||
return side != null && NetworkInterfaceCardItem.getSideConfiguration(player.getItemInHand(hand), side);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final class ComputerBlockItemRenderer {
|
||||
public static final int BLOCK_RENDER_SIZE = 48;
|
||||
|
||||
private final ItemStack computerItemStack = new ItemStack(Items.COMPUTER.get());
|
||||
private final ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
|
||||
private final BakedModel model = itemRenderer.getModel(computerItemStack, null, null, 0);
|
||||
|
||||
@Nullable
|
||||
private Direction getFocusedSide(final float mouseX, final float mouseY, final Vector3f rotation) {
|
||||
// Rotate ray inversely around block to represent visual block rotation.
|
||||
final Quaternion quaternion = Quaternion.fromXYZDegrees(rotation);
|
||||
quaternion.conj();
|
||||
|
||||
// Move ray in screen space to mouse position.
|
||||
final float relMouseX = -mouseX / (float) BLOCK_RENDER_SIZE;
|
||||
final float relMouseY = -mouseY / (float) BLOCK_RENDER_SIZE;
|
||||
|
||||
final Vector3f source = new Vector3f();
|
||||
source.add(relMouseX, relMouseY, 1);
|
||||
source.transform(quaternion);
|
||||
|
||||
final Vector3f target = new Vector3f();
|
||||
target.add(relMouseX, relMouseY, -1);
|
||||
target.transform(quaternion);
|
||||
|
||||
// Intersect rotated ray with bounding box representing block.
|
||||
final AABB aabb = new AABB(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5);
|
||||
return aabb.clip(new Vec3(source), new Vec3(target))
|
||||
.map(hit -> Direction.getNearest(hit.x, -hit.y(), hit.z()))
|
||||
.filter(side -> side != Direction.SOUTH)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public void render(final int x, final int y, final Vector3f rotation) {
|
||||
RenderSystem.setShaderTexture(0, InventoryMenu.BLOCK_ATLAS);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
RenderSystem.setShaderColor(1, 1, 1, 1);
|
||||
|
||||
final Vector3f renderRotation = rotation.copy();
|
||||
renderRotation.add(0, 180, 0);
|
||||
|
||||
final PoseStack stack = RenderSystem.getModelViewStack();
|
||||
stack.pushPose();
|
||||
stack.translate(x, y, 0);
|
||||
stack.mulPose(Quaternion.fromXYZDegrees(renderRotation));
|
||||
stack.scale(BLOCK_RENDER_SIZE, -BLOCK_RENDER_SIZE, BLOCK_RENDER_SIZE);
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
|
||||
final MultiBufferSource.BufferSource bufferSource = Minecraft.getInstance().renderBuffers().bufferSource();
|
||||
renderBlock(bufferSource);
|
||||
renderOverlays(stack, bufferSource);
|
||||
bufferSource.endBatch();
|
||||
|
||||
stack.popPose();
|
||||
RenderSystem.applyModelViewMatrix();
|
||||
}
|
||||
|
||||
private void renderBlock(final MultiBufferSource.BufferSource bufferSource) {
|
||||
itemRenderer.render(computerItemStack, ItemTransforms.TransformType.NONE, false, new PoseStack(), bufferSource, 0xF000F0, OverlayTexture.NO_OVERLAY, model);
|
||||
}
|
||||
|
||||
private void renderOverlays(final PoseStack poseStack, final MultiBufferSource.BufferSource bufferSource) {
|
||||
for (final Direction side : Direction.values()) {
|
||||
// South face of computers is the front face (screen) and there's no connectivity allowed there.
|
||||
if (side == Direction.SOUTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
poseStack.pushPose();
|
||||
poseStack.setIdentity();
|
||||
|
||||
poseStack.translate(-side.getStepX() * 0.51, side.getStepY() * 0.51, -side.getStepZ() * 0.51);
|
||||
|
||||
final Vector3f sideRotation = switch (side) {
|
||||
case DOWN -> new Vector3f(-90, 0, 0);
|
||||
case UP -> new Vector3f(90, 0, 0);
|
||||
case NORTH -> new Vector3f(0, 180, 0);
|
||||
case WEST -> new Vector3f(0, -90, 0);
|
||||
case EAST -> new Vector3f(0, 90, 0);
|
||||
default -> throw new IllegalStateException("Unexpected value: " + side);
|
||||
};
|
||||
poseStack.mulPose(Quaternion.fromXYZDegrees(sideRotation));
|
||||
|
||||
poseStack.translate(-0.5, -0.5, 0);
|
||||
|
||||
if (getConfiguration(side)) {
|
||||
renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_ENABLED_TEXTURE);
|
||||
} else {
|
||||
renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_DISABLED_TEXTURE);
|
||||
}
|
||||
|
||||
if (side == focusedSide) {
|
||||
renderOverlay(poseStack, bufferSource, Textures.BLOCK_FACE_FOCUSED_TEXTURE);
|
||||
}
|
||||
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
|
||||
private void renderOverlay(final PoseStack poseStack, final MultiBufferSource.BufferSource bufferSource, final Texture texture) {
|
||||
final VertexConsumer buffer = bufferSource.getBuffer(ModRenderType.getOverlay(texture.location));
|
||||
|
||||
buffer.vertex(poseStack.last().pose(), 0, 0, 0).uv(0, 0).endVertex();
|
||||
buffer.vertex(poseStack.last().pose(), 0, 1, 0).uv(0, 1).endVertex();
|
||||
buffer.vertex(poseStack.last().pose(), 1, 1, 0).uv(1, 1).endVertex();
|
||||
buffer.vertex(poseStack.last().pose(), 1, 0, 0).uv(1, 0).endVertex();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package li.cil.oc2.client.gui;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import li.cil.oc2.common.container.RobotTerminalContainer;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
@@ -16,6 +17,10 @@ public final class RobotTerminalScreen extends AbstractMachineTerminalScreen<Rob
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@SuppressWarnings("all") private EditBox focusIndicatorEditBox;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotTerminalScreen(final RobotTerminalContainer container, final Inventory playerInventory, final Component title) {
|
||||
super(container, playerInventory, title);
|
||||
}
|
||||
@@ -36,4 +41,11 @@ public final class RobotTerminalScreen extends AbstractMachineTerminalScreen<Rob
|
||||
RobotContainerScreen.renderSelection(stack, menu.getRobot().getSelectedSlot(), leftPos + SLOTS_X + 4, topPos + SLOTS_Y + 4, 12);
|
||||
renderTooltip(stack, mouseX, mouseY);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void setFocusIndicatorEditBox(final EditBox editBox) {
|
||||
focusIndicatorEditBox = editBox;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public final class Sprites {
|
||||
public static final Sprite ROBOT_CONTAINER = new Sprite(ROBOT_CONTAINER_TEXTURE);
|
||||
public static final Sprite TERMINAL_SCREEN = new Sprite(TERMINAL_SCREEN_TEXTURE);
|
||||
public static final Sprite BUS_INTERFACE_SCREEN = new Sprite(BUS_INTERFACE_SCREEN_TEXTURE);
|
||||
public static final Sprite NETWORK_INTERFACE_CARD_SCREEN = new Sprite(NETWORK_INTERFACE_CARD_SCREEN_TEXTURE);
|
||||
|
||||
public static final Sprite TERMINAL_FOCUSED = new Sprite(TERMINAL_FOCUSED_TEXTURE);
|
||||
public static final Sprite SLOT_SELECTION = new Sprite(SLOT_SELECTION_TEXTURE, 18, 18, 0, 0);
|
||||
|
||||
@@ -7,11 +7,15 @@ public final class Textures {
|
||||
public static final Texture ROBOT_CONTAINER_TEXTURE = new Texture("textures/gui/widget/robot_container.png", 176, 197);
|
||||
public static final Texture TERMINAL_SCREEN_TEXTURE = new Texture("textures/gui/widget/terminal_screen.png", 336, 208);
|
||||
public static final Texture BUS_INTERFACE_SCREEN_TEXTURE = new Texture("textures/gui/widget/bus_interface_screen.png", 240, 30);
|
||||
public static final Texture NETWORK_INTERFACE_CARD_SCREEN_TEXTURE = new Texture("textures/gui/widget/network_interface_card_screen.png", 176, 130);
|
||||
|
||||
public static final Texture TERMINAL_FOCUSED_TEXTURE = new Texture("textures/gui/overlay/terminal_focused.png", 336, 208);
|
||||
public static final Texture SLOT_SELECTION_TEXTURE = new Texture("textures/gui/overlay/slot_selection.png", 18, 270);
|
||||
public static final Texture INFO_ICON_TEXTURE = new Texture("textures/gui/overlay/slot_info.png", 28, 28);
|
||||
public static final Texture WARN_ICON_TEXTURE = new Texture("textures/gui/overlay/slot_warn.png", 28, 28);
|
||||
public static final Texture BLOCK_FACE_FOCUSED_TEXTURE = new Texture("textures/gui/overlay/block_face_focused.png", 16, 16);
|
||||
public static final Texture BLOCK_FACE_ENABLED_TEXTURE = new Texture("textures/gui/overlay/block_face_enabled.png", 16, 16);
|
||||
public static final Texture BLOCK_FACE_DISABLED_TEXTURE = new Texture("textures/gui/overlay/block_face_disabled.png", 16, 16);
|
||||
|
||||
public static final Texture HOTBAR_TEXTURE = new Texture("textures/gui/widget/hotbar.png", 224, 26);
|
||||
public static final Texture SIDEBAR_2_TEXTURE = new Texture("textures/gui/widget/sidebar_2.png", 19, 34);
|
||||
|
||||
@@ -9,7 +9,7 @@ public final class TerminalInput {
|
||||
private static final Int2ObjectArrayMap<Int2ObjectArrayMap<byte[]>> KEYCODE_SEQUENCES = new Int2ObjectArrayMap<>();
|
||||
|
||||
static {
|
||||
addSequence(GLFW.GLFW_KEY_ENTER, '\n');
|
||||
addSequence(GLFW.GLFW_KEY_ENTER, '\r');
|
||||
addSequence(GLFW.GLFW_KEY_TAB, '\t');
|
||||
addSequence(GLFW.GLFW_KEY_BACKSPACE, '\b');
|
||||
|
||||
@@ -21,26 +21,54 @@ public final class TerminalInput {
|
||||
addSequence(GLFW.GLFW_KEY_PAGE_UP, "\033[5~");
|
||||
addSequence(GLFW.GLFW_KEY_PAGE_DOWN, "\033[6~");
|
||||
|
||||
addSequence(GLFW.GLFW_KEY_F1, "\033[11~");
|
||||
addSequence(GLFW.GLFW_KEY_F2, "\033[12~");
|
||||
addSequence(GLFW.GLFW_KEY_F3, "\033[13~");
|
||||
addSequence(GLFW.GLFW_KEY_F4, "\033[14~");
|
||||
addSequence(GLFW.GLFW_KEY_F5, "\033[15~");
|
||||
addSequence(GLFW.GLFW_KEY_F6, "\033[17~");
|
||||
addSequence(GLFW.GLFW_KEY_F7, "\033[18~");
|
||||
addSequence(GLFW.GLFW_KEY_F8, "\033[19~");
|
||||
addSequence(GLFW.GLFW_KEY_F9, "\033[20~");
|
||||
addSequence(GLFW.GLFW_KEY_F10, "\033[21~");
|
||||
addSequence(GLFW.GLFW_KEY_F11, "\033[23~");
|
||||
addSequence(GLFW.GLFW_KEY_F12, "\033[24~");
|
||||
|
||||
addSequence(GLFW.GLFW_KEY_UP, "\033[A");
|
||||
addSequence(GLFW.GLFW_KEY_DOWN, "\033[B");
|
||||
addSequence(GLFW.GLFW_KEY_RIGHT, "\033[C");
|
||||
addSequence(GLFW.GLFW_KEY_LEFT, "\033[D");
|
||||
|
||||
for (int i = 'A'; i <= 'Z'; i++) {
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL,
|
||||
addSequence(
|
||||
GLFW.GLFW_MOD_CONTROL,
|
||||
GLFW.GLFW_KEY_A + (i - 'A'),
|
||||
(byte) (1 + i - 'A'));
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_ALT,
|
||||
(byte) (1 + i - 'A')
|
||||
);
|
||||
addSequence(
|
||||
GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT,
|
||||
GLFW.GLFW_KEY_A + (i - 'A'),
|
||||
(byte) 27, (byte) ('a' + i - 'A'));
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_ALT | GLFW.GLFW_MOD_SHIFT,
|
||||
(byte) (1 + i - 'A')
|
||||
);
|
||||
|
||||
addSequence(
|
||||
GLFW.GLFW_MOD_ALT,
|
||||
GLFW.GLFW_KEY_A + (i - 'A'),
|
||||
(byte) 27, (byte) i);
|
||||
(byte) '\033', (byte) ('a' + i - 'A')
|
||||
);
|
||||
addSequence(
|
||||
GLFW.GLFW_MOD_ALT | GLFW.GLFW_MOD_SHIFT,
|
||||
GLFW.GLFW_KEY_A + (i - 'A'),
|
||||
(byte) '\033', (byte) (i + 128)
|
||||
);
|
||||
}
|
||||
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_LEFT_BRACKET, (byte) 27);
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_BACKSLASH, (byte) 28);
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_RIGHT_BRACKET, (byte) 29);
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_LEFT_BRACKET, (byte) '\033');
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT, GLFW.GLFW_KEY_LEFT_BRACKET, (byte) '\033');
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_BACKSLASH, (byte) '\034');
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT, GLFW.GLFW_KEY_BACKSLASH, (byte) '\034');
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL, GLFW.GLFW_KEY_RIGHT_BRACKET, (byte) '\035');
|
||||
addSequence(GLFW.GLFW_MOD_CONTROL | GLFW.GLFW_MOD_SHIFT, GLFW.GLFW_KEY_RIGHT_BRACKET, (byte) '\035');
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -47,6 +47,24 @@ public abstract class ModRenderType extends RenderType {
|
||||
state);
|
||||
}
|
||||
|
||||
public static RenderType getOverlay(final ResourceLocation location) {
|
||||
final TextureStateShard texture = new TextureStateShard(location, false, true);
|
||||
final RenderType.CompositeState state = RenderType.CompositeState.builder()
|
||||
.setShaderState(RenderStateShard.POSITION_TEX_SHADER)
|
||||
.setTextureState(texture)
|
||||
.setOutputState(TRANSLUCENT_TARGET)
|
||||
.setTransparencyState(ADDITIVE_TRANSPARENCY)
|
||||
.createCompositeState(false);
|
||||
return create(
|
||||
API.MOD_ID + "/overlay",
|
||||
DefaultVertexFormat.POSITION_TEX,
|
||||
VertexFormat.Mode.QUADS,
|
||||
256,
|
||||
false,
|
||||
true,
|
||||
state);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private ModRenderType(final String name, final VertexFormat format, final VertexFormat.Mode drawMode, final int bufferSize, final boolean useDelegate, final boolean needsSorting, final Runnable setupTask, final Runnable clearTask) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Vector3f;
|
||||
import li.cil.oc2.client.renderer.entity.model.RobotModel;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
@@ -12,7 +12,7 @@ import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
public final class RobotRenderer extends EntityRenderer<RobotEntity> {
|
||||
public final class RobotRenderer extends EntityRenderer<Robot> {
|
||||
private final RobotModel model;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -25,13 +25,13 @@ public final class RobotRenderer extends EntityRenderer<RobotEntity> {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureLocation(final RobotEntity entity) {
|
||||
public ResourceLocation getTextureLocation(final Robot entity) {
|
||||
return RobotModel.ROBOT_ENTITY_TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final RobotEntity entity, final float entityYaw, final float partialTicks, final PoseStack stack, final MultiBufferSource bufferSource, final int packedLight) {
|
||||
final RobotEntity.AnimationState state = entity.getAnimationState();
|
||||
public void render(final Robot entity, final float entityYaw, final float partialTicks, final PoseStack stack, final MultiBufferSource bufferSource, final int packedLight) {
|
||||
final Robot.AnimationState state = entity.getAnimationState();
|
||||
state.update(partialTicks, entity.level.random);
|
||||
|
||||
stack.pushPose();
|
||||
|
||||
@@ -3,7 +3,7 @@ package li.cil.oc2.client.renderer.entity.model;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.client.model.EntityModel;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
@@ -16,7 +16,7 @@ import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.common.model.TransformationHelper;
|
||||
|
||||
public final class RobotModel extends EntityModel<RobotEntity> {
|
||||
public final class RobotModel extends EntityModel<Robot> {
|
||||
public static final ModelLayerLocation ROBOT_MODEL_LAYER = new ModelLayerLocation(new ResourceLocation(API.MOD_ID, "robot"), "main");
|
||||
public static final ResourceLocation ROBOT_ENTITY_TEXTURE = new ResourceLocation(API.MOD_ID, "textures/entity/robot/robot.png");
|
||||
|
||||
@@ -57,8 +57,8 @@ public final class RobotModel extends EntityModel<RobotEntity> {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void setupAnim(final RobotEntity entity, final float limbSwing, final float limbSwingAmount, final float ageInTicks, final float netHeadYaw, final float headPitch) {
|
||||
final RobotEntity.AnimationState state = entity.getAnimationState();
|
||||
public void setupAnim(final Robot entity, final float limbSwing, final float limbSwingAmount, final float ageInTicks, final float netHeadYaw, final float headPitch) {
|
||||
final Robot.AnimationState state = entity.getAnimationState();
|
||||
baseY = state.baseRenderOffsetY;
|
||||
topY = state.topRenderOffsetY;
|
||||
topRotation[1] = state.topRenderRotationY;
|
||||
|
||||
@@ -86,20 +86,22 @@ public final class RedstoneInterfaceBlockEntity extends BlockEntity implements N
|
||||
@Callback(name = GET_REDSTONE_OUTPUT, synchronize = false)
|
||||
public int getRedstoneOutput(@Parameter(SIDE) @Nullable final Side side) {
|
||||
if (side == null) throw new IllegalArgumentException();
|
||||
final int index = side.getDirection().get3DDataValue();
|
||||
|
||||
return output[side.get3DDataValue()];
|
||||
return output[index];
|
||||
}
|
||||
|
||||
@Callback(name = SET_REDSTONE_OUTPUT)
|
||||
public void setRedstoneOutput(@Parameter(SIDE) @Nullable final Side side, @Parameter(VALUE) final int value) {
|
||||
if (side == null) throw new IllegalArgumentException();
|
||||
final int index = side.getDirection().get3DDataValue();
|
||||
|
||||
final byte clampedValue = (byte) Mth.clamp(value, 0, 15);
|
||||
if (clampedValue == output[side.get3DDataValue()]) {
|
||||
if (clampedValue == output[index]) {
|
||||
return;
|
||||
}
|
||||
|
||||
output[side.get3DDataValue()] = clampedValue;
|
||||
output[index] = clampedValue;
|
||||
|
||||
final Direction direction = HorizontalBlockUtils.toGlobal(getBlockState(), side);
|
||||
if (direction != null) {
|
||||
|
||||
@@ -78,6 +78,16 @@ public abstract class AbstractGroupingDeviceBusElement<T extends AbstractGroupin
|
||||
if (sideTag.contains(GROUP_DATA_TAG_NAME, NBTTagIds.TAG_COMPOUND)) {
|
||||
groupData[i] = sideTag.getCompound(GROUP_DATA_TAG_NAME);
|
||||
}
|
||||
|
||||
// Immediately load data into devices, if we already have some.
|
||||
for (final T entry : groups.get(i)) {
|
||||
final CompoundTag devicesTag = groupData[i];
|
||||
entry.getDeviceDataKey().ifPresent(key -> {
|
||||
if (devicesTag.contains(key, NBTTagIds.TAG_COMPOUND)) {
|
||||
entry.getDevice().deserializeNBT(devicesTag.getCompound(key));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,19 +89,11 @@ public abstract class AbstractBlockDeviceVMDevice<TBlock extends BlockDevice, TI
|
||||
|
||||
@Override
|
||||
public void suspend() {
|
||||
if (device != null) {
|
||||
try {
|
||||
device.close();
|
||||
} catch (final IOException e) {
|
||||
LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
closeBlockDevice();
|
||||
|
||||
if (blobHandle != null) {
|
||||
BlobStorage.close(blobHandle);
|
||||
}
|
||||
|
||||
device = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,6 +155,20 @@ public abstract class AbstractBlockDeviceVMDevice<TBlock extends BlockDevice, TI
|
||||
|
||||
protected abstract TBlock createBlockDevice() throws IOException;
|
||||
|
||||
protected void closeBlockDevice() {
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
device.close();
|
||||
} catch (final IOException e) {
|
||||
LOGGER.error(e);
|
||||
}
|
||||
|
||||
device = null;
|
||||
}
|
||||
|
||||
protected void handleDataAccess() {
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public final class BlockOperationsModuleDevice extends AbstractItemRPCDevice {
|
||||
|
||||
final List<ItemEntity> oldItems = getItemsInRange();
|
||||
|
||||
final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity);
|
||||
final Direction direction = RobotOperationSide.toGlobal(entity, side);
|
||||
if (!tryHarvestBlock(serverLevel, entity.blockPosition().relative(direction))) {
|
||||
return false;
|
||||
}
|
||||
@@ -129,7 +129,7 @@ public final class BlockOperationsModuleDevice extends AbstractItemRPCDevice {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity);
|
||||
final Direction direction = RobotOperationSide.toGlobal(entity, side);
|
||||
final BlockPos blockPos = entity.blockPosition().relative(direction);
|
||||
final Direction oppositeDirection = direction.getOpposite();
|
||||
final BlockHitResult hit = new BlockHitResult(
|
||||
|
||||
@@ -2,7 +2,7 @@ package li.cil.oc2.common.bus.device.item;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent;
|
||||
import li.cil.oc2.common.util.Location;
|
||||
import li.cil.sedna.api.device.BlockDevice;
|
||||
import li.cil.sedna.device.block.ByteBufferBlockDevice;
|
||||
@@ -39,16 +39,8 @@ public final class HardDriveVMDeviceWithInitialData extends HardDriveVMDevice {
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleResumingRunningEvent(final VMResumingRunningEvent event) {
|
||||
if (copyJob != null) {
|
||||
try {
|
||||
copyJob.get();
|
||||
} catch (final Throwable e) {
|
||||
LOGGER.error(e);
|
||||
} finally {
|
||||
copyJob = null;
|
||||
}
|
||||
}
|
||||
public void handleResumedRunningEvent(final VMResumedRunningEvent event) {
|
||||
joinCopyJob();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -71,4 +63,28 @@ public final class HardDriveVMDeviceWithInitialData extends HardDriveVMDevice {
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeBlockDevice() {
|
||||
// Join the copy job before releasing the device to avoid writes from thread to closed device.
|
||||
// Since we use memory mapped memory, closing the device leads to it holding a dead pointer, meaning
|
||||
// further access to it will hard-crash the JVM.
|
||||
joinCopyJob();
|
||||
|
||||
super.closeBlockDevice();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private void joinCopyJob() {
|
||||
if (copyJob != null) {
|
||||
try {
|
||||
copyJob.get();
|
||||
} catch (final Throwable e) {
|
||||
LOGGER.error(e);
|
||||
} finally {
|
||||
copyJob = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice
|
||||
}
|
||||
|
||||
final int originalStackSize = stack.getCount();
|
||||
final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity);
|
||||
final Direction direction = RobotOperationSide.toGlobal(entity, side);
|
||||
final List<IItemHandler> itemHandlers = getItemStackHandlersInDirection(direction).toList();
|
||||
for (final IItemHandler handler : itemHandlers) {
|
||||
stack = ItemHandlerHelper.insertItemStacked(handler, stack, false);
|
||||
@@ -122,7 +122,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice
|
||||
}
|
||||
|
||||
final int originalStackSize = stack.getCount();
|
||||
final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity);
|
||||
final Direction direction = RobotOperationSide.toGlobal(entity, side);
|
||||
final Optional<IItemHandler> optional = getItemStackHandlersInDirection(direction).findFirst();
|
||||
if (optional.isPresent()) {
|
||||
stack = optional.get().insertItem(intoSlot, stack, false);
|
||||
@@ -150,7 +150,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice
|
||||
return 0;
|
||||
}
|
||||
|
||||
final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity);
|
||||
final Direction direction = RobotOperationSide.toGlobal(entity, side);
|
||||
final List<IItemHandler> handlers = getItemStackHandlersInDirection(direction).collect(Collectors.toList());
|
||||
if (handlers.isEmpty()) {
|
||||
return takeFromWorld(count);
|
||||
@@ -167,7 +167,7 @@ public final class InventoryOperationsModuleDevice extends AbstractItemRPCDevice
|
||||
return 0;
|
||||
}
|
||||
|
||||
final Direction direction = RobotOperationSide.getAdjustedDirection(side, entity);
|
||||
final Direction direction = RobotOperationSide.toGlobal(entity, side);
|
||||
return getItemStackHandlersInDirection(direction).findFirst().map(handler ->
|
||||
takeFromInventory(count, handler, fromSlot)).orElse(0);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import li.cil.oc2.common.bus.device.util.IdentityProxy;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalAddress;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalInterrupt;
|
||||
import li.cil.oc2.common.capabilities.Capabilities;
|
||||
import li.cil.oc2.common.item.NetworkInterfaceCardItem;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.sedna.device.virtio.VirtIONetworkDevice;
|
||||
@@ -51,7 +52,7 @@ public final class NetworkInterfaceCardItemDevice extends IdentityProxy<ItemStac
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> LazyOptional<T> getCapability(final Capability<T> cap, @Nullable final Direction side) {
|
||||
if (cap == Capabilities.NETWORK_INTERFACE && side != null) {
|
||||
if (cap == Capabilities.NETWORK_INTERFACE && NetworkInterfaceCardItem.getSideConfiguration(identity, side)) {
|
||||
return LazyOptional.of(() -> networkInterface).cast();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ public final class RedstoneInterfaceCardItemDevice extends AbstractItemRPCDevice
|
||||
@Override
|
||||
public <T> LazyOptional<T> getCapability(@Nonnull final Capability<T> capability, @Nullable final Direction side) {
|
||||
if (capability == Capabilities.REDSTONE_EMITTER && side != null) {
|
||||
return LazyOptional.of(() -> capabilities[side.get3DDataValue()]).cast();
|
||||
final int index = side.get3DDataValue();
|
||||
return LazyOptional.of(() -> capabilities[index]).cast();
|
||||
}
|
||||
|
||||
return LazyOptional.empty();
|
||||
@@ -101,20 +102,22 @@ public final class RedstoneInterfaceCardItemDevice extends AbstractItemRPCDevice
|
||||
@Callback(name = GET_REDSTONE_OUTPUT, synchronize = false)
|
||||
public int getRedstoneOutput(@Parameter(SIDE) @Nullable final Side side) {
|
||||
if (side == null) throw new IllegalArgumentException();
|
||||
final int index = side.getDirection().get3DDataValue();
|
||||
|
||||
return output[side.get3DDataValue()];
|
||||
return output[index];
|
||||
}
|
||||
|
||||
@Callback(name = SET_REDSTONE_OUTPUT)
|
||||
public void setRedstoneOutput(@Parameter(SIDE) @Nullable final Side side, @Parameter(VALUE) final int value) {
|
||||
if (side == null) throw new IllegalArgumentException();
|
||||
final int index = side.getDirection().get3DDataValue();
|
||||
|
||||
final byte clampedValue = (byte) Mth.clamp(value, 0, 15);
|
||||
if (clampedValue == output[side.get3DDataValue()]) {
|
||||
if (clampedValue == output[index]) {
|
||||
return;
|
||||
}
|
||||
|
||||
output[side.get3DDataValue()] = clampedValue;
|
||||
output[index] = clampedValue;
|
||||
|
||||
final Direction direction = HorizontalBlockUtils.toGlobal(blockEntity.getBlockState(), side);
|
||||
if (direction != null) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package li.cil.oc2.common.container;
|
||||
|
||||
import li.cil.oc2.common.bus.CommonDeviceBusController;
|
||||
import li.cil.oc2.common.energy.FixedEnergyStorage;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import li.cil.oc2.common.network.message.OpenRobotInventoryMessage;
|
||||
import li.cil.oc2.common.network.message.OpenRobotTerminalMessage;
|
||||
@@ -16,13 +16,15 @@ import net.minecraft.world.inventory.MenuType;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public abstract class AbstractRobotContainer extends AbstractMachineTerminalContainer {
|
||||
private final RobotEntity robot;
|
||||
private final Robot robot;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public AbstractRobotContainer(final MenuType<?> type, final int id, final RobotEntity robot, final IntPrecisionContainerData energyInfo) {
|
||||
public AbstractRobotContainer(final MenuType<?> type, final int id, final Player player, final Robot robot, final IntPrecisionContainerData energyInfo) {
|
||||
super(type, id, energyInfo);
|
||||
this.robot = robot;
|
||||
|
||||
this.robot.addTerminalUser(player);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -37,7 +39,7 @@ public abstract class AbstractRobotContainer extends AbstractMachineTerminalCont
|
||||
Network.INSTANCE.sendToServer(new OpenRobotTerminalMessage(robot));
|
||||
}
|
||||
|
||||
public RobotEntity getRobot() {
|
||||
public Robot getRobot() {
|
||||
return robot;
|
||||
}
|
||||
|
||||
@@ -66,6 +68,13 @@ public abstract class AbstractRobotContainer extends AbstractMachineTerminalCont
|
||||
return robot.isAlive() && robot.closerThan(player, 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(final Player player) {
|
||||
super.removed(player);
|
||||
|
||||
this.robot.removeTerminalUser(player);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
protected static IntPrecisionContainerData createEnergyInfo(final FixedEnergyStorage energy, final CommonDeviceBusController busController) {
|
||||
|
||||
@@ -30,11 +30,11 @@ public final class ComputerTerminalContainer extends AbstractComputerContainer {
|
||||
}, computer.getBlockPos());
|
||||
}
|
||||
|
||||
public static ComputerTerminalContainer createClient(final int id, final Inventory playerInventory, final FriendlyByteBuf data) {
|
||||
public static ComputerTerminalContainer createClient(final int id, final Inventory inventory, final FriendlyByteBuf data) {
|
||||
final BlockPos pos = data.readBlockPos();
|
||||
final BlockEntity blockEntity = playerInventory.player.level.getBlockEntity(pos);
|
||||
final BlockEntity blockEntity = inventory.player.level.getBlockEntity(pos);
|
||||
if (blockEntity instanceof final ComputerBlockEntity computer) {
|
||||
return new ComputerTerminalContainer(id, playerInventory.player, computer, createEnergyInfo());
|
||||
return new ComputerTerminalContainer(id, inventory.player, computer, createEnergyInfo());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
@@ -55,13 +55,13 @@ public class DeviceItemStackHandler extends FixedSizeItemStackHandler {
|
||||
|
||||
public void loadItems(final CompoundTag tag) {
|
||||
super.deserializeNBT(tag);
|
||||
for (int slot = 0; slot < getSlots(); slot++) {
|
||||
busElement.updateDevices(slot, getStackInSlot(slot));
|
||||
}
|
||||
}
|
||||
|
||||
public void loadDevices(final CompoundTag tag) {
|
||||
busElement.load(tag);
|
||||
for (int slot = 0; slot < getSlots(); slot++) {
|
||||
busElement.updateDevices(slot, getStackInSlot(slot));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,7 @@ package li.cil.oc2.common.container;
|
||||
import li.cil.oc2.api.bus.device.DeviceTypes;
|
||||
import li.cil.oc2.common.bus.CommonDeviceBusController;
|
||||
import li.cil.oc2.common.energy.FixedEnergyStorage;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.vm.VMItemStackHandlers;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -18,7 +18,7 @@ import net.minecraftforge.items.SlotItemHandler;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
|
||||
public final class RobotInventoryContainer extends AbstractRobotContainer {
|
||||
public static void createServer(final RobotEntity robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) {
|
||||
public static void createServer(final Robot robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) {
|
||||
NetworkHooks.openGui(player, new MenuProvider() {
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
@@ -35,7 +35,7 @@ public final class RobotInventoryContainer extends AbstractRobotContainer {
|
||||
public static RobotInventoryContainer createClient(final int id, final Inventory inventory, final FriendlyByteBuf data) {
|
||||
final int entityId = data.readVarInt();
|
||||
final Entity entity = inventory.player.level.getEntity(entityId);
|
||||
if (entity instanceof final RobotEntity robot) {
|
||||
if (entity instanceof final Robot robot) {
|
||||
return new RobotInventoryContainer(id, robot, inventory.player, createEnergyInfo());
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ public final class RobotInventoryContainer extends AbstractRobotContainer {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private RobotInventoryContainer(final int id, final RobotEntity robot, final Player player, final IntPrecisionContainerData energyInfo) {
|
||||
super(Containers.ROBOT.get(), id, robot, energyInfo);
|
||||
private RobotInventoryContainer(final int id, final Robot robot, final Player player, final IntPrecisionContainerData energyInfo) {
|
||||
super(Containers.ROBOT.get(), id, player, robot, energyInfo);
|
||||
|
||||
final VMItemStackHandlers handlers = robot.getItemStackHandlers();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package li.cil.oc2.common.container;
|
||||
import li.cil.oc2.client.gui.Sprites;
|
||||
import li.cil.oc2.common.bus.CommonDeviceBusController;
|
||||
import li.cil.oc2.common.energy.FixedEnergyStorage;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -17,7 +17,7 @@ import net.minecraftforge.items.SlotItemHandler;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
|
||||
public final class RobotTerminalContainer extends AbstractRobotContainer {
|
||||
public static void createServer(final RobotEntity robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) {
|
||||
public static void createServer(final Robot robot, final FixedEnergyStorage energy, final CommonDeviceBusController busController, final ServerPlayer player) {
|
||||
NetworkHooks.openGui(player, new MenuProvider() {
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
@@ -26,7 +26,7 @@ public final class RobotTerminalContainer extends AbstractRobotContainer {
|
||||
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(final int id, final Inventory inventory, final Player player) {
|
||||
return new RobotTerminalContainer(id, robot, createEnergyInfo(energy, busController));
|
||||
return new RobotTerminalContainer(id, player, robot, createEnergyInfo(energy, busController));
|
||||
}
|
||||
}, b -> b.writeVarInt(robot.getId()));
|
||||
}
|
||||
@@ -34,8 +34,8 @@ public final class RobotTerminalContainer extends AbstractRobotContainer {
|
||||
public static RobotTerminalContainer createClient(final int id, final Inventory inventory, final FriendlyByteBuf data) {
|
||||
final int entityId = data.readVarInt();
|
||||
final Entity entity = inventory.player.level.getEntity(entityId);
|
||||
if (entity instanceof final RobotEntity robot) {
|
||||
return new RobotTerminalContainer(id, robot, createEnergyInfo());
|
||||
if (entity instanceof final Robot robot) {
|
||||
return new RobotTerminalContainer(id, inventory.player, robot, createEnergyInfo());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException();
|
||||
@@ -43,8 +43,8 @@ public final class RobotTerminalContainer extends AbstractRobotContainer {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private RobotTerminalContainer(final int id, final RobotEntity robot, final IntPrecisionContainerData energyInfo) {
|
||||
super(Containers.ROBOT_TERMINAL.get(), id, robot, energyInfo);
|
||||
private RobotTerminalContainer(final int id, final Player player, final Robot robot, final IntPrecisionContainerData energyInfo) {
|
||||
super(Containers.ROBOT_TERMINAL.get(), id, player, robot, energyInfo);
|
||||
|
||||
// It's kinda dumb we need to access technically-client-side stuff here, but that's the nature of containers
|
||||
// needing to specify display positions for some reason.
|
||||
|
||||
@@ -15,7 +15,7 @@ public final class Entities {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static final RegistryObject<EntityType<RobotEntity>> ROBOT = register("robot", RobotEntity::new, MobCategory.MISC, b -> b.sized(14f / 16f, 14f / 16f).fireImmune().noSummon());
|
||||
public static final RegistryObject<EntityType<Robot>> ROBOT = register("robot", Robot::new, MobCategory.MISC, b -> b.sized(14f / 16f, 14f / 16f).fireImmune().noSummon());
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import li.cil.oc2.api.bus.device.object.Callback;
|
||||
import li.cil.oc2.api.bus.device.object.ObjectDevice;
|
||||
import li.cil.oc2.api.bus.device.object.Parameter;
|
||||
import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
|
||||
import li.cil.oc2.api.capabilities.Robot;
|
||||
import li.cil.oc2.api.capabilities.TerminalUserProvider;
|
||||
import li.cil.oc2.common.Config;
|
||||
import li.cil.oc2.common.bus.AbstractDeviceBusElement;
|
||||
import li.cil.oc2.common.bus.CommonDeviceBusController;
|
||||
@@ -82,10 +82,10 @@ import java.util.function.Consumer;
|
||||
import static java.util.Collections.singleton;
|
||||
import static li.cil.oc2.common.Constants.*;
|
||||
|
||||
public final class RobotEntity extends Entity implements Robot {
|
||||
public static final EntityDataAccessor<BlockPos> TARGET_POSITION = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.BLOCK_POS);
|
||||
public static final EntityDataAccessor<Direction> TARGET_DIRECTION = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.DIRECTION);
|
||||
public static final EntityDataAccessor<Byte> SELECTED_SLOT = SynchedEntityData.defineId(RobotEntity.class, EntityDataSerializers.BYTE);
|
||||
public final class Robot extends Entity implements li.cil.oc2.api.capabilities.Robot, TerminalUserProvider {
|
||||
public static final EntityDataAccessor<BlockPos> TARGET_POSITION = SynchedEntityData.defineId(Robot.class, EntityDataSerializers.BLOCK_POS);
|
||||
public static final EntityDataAccessor<Direction> TARGET_DIRECTION = SynchedEntityData.defineId(Robot.class, EntityDataSerializers.DIRECTION);
|
||||
public static final EntityDataAccessor<Byte> SELECTED_SLOT = SynchedEntityData.defineId(Robot.class, EntityDataSerializers.BYTE);
|
||||
|
||||
private static final String TERMINAL_TAG_NAME = "terminal";
|
||||
private static final String STATE_TAG_NAME = "state";
|
||||
@@ -118,11 +118,12 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
private final RobotItemStackHandlers deviceItems = new RobotItemStackHandlers();
|
||||
private final FixedEnergyStorage energy = new FixedEnergyStorage(Config.robotEnergyStorage);
|
||||
private final ItemStackHandler inventory = new FixedSizeItemStackHandler(INVENTORY_SIZE);
|
||||
private final Set<Player> terminalUsers = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
private long lastPistonMovement;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotEntity(final EntityType<?> type, final Level world) {
|
||||
public Robot(final EntityType<?> type, final Level world) {
|
||||
super(type, world);
|
||||
this.blocksBuilding = true;
|
||||
setNoGravity(true);
|
||||
@@ -224,6 +225,19 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
RobotInventoryContainer.createServer(this, energy, virtualMachine.busController, player);
|
||||
}
|
||||
|
||||
public void addTerminalUser(final Player player) {
|
||||
terminalUsers.add(player);
|
||||
}
|
||||
|
||||
public void removeTerminalUser(final Player player) {
|
||||
terminalUsers.remove(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Player> getTerminalUsers() {
|
||||
return terminalUsers;
|
||||
}
|
||||
|
||||
public void dropSelf() {
|
||||
if (!isAlive()) {
|
||||
return;
|
||||
@@ -599,10 +613,10 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
|
||||
public void tick() {
|
||||
if (level.isClientSide()) {
|
||||
RobotActions.performClient(RobotEntity.this);
|
||||
RobotActions.performClient(Robot.this);
|
||||
} else {
|
||||
if (action != null) {
|
||||
final RobotActionResult result = action.perform(RobotEntity.this);
|
||||
final RobotActionResult result = action.perform(Robot.this);
|
||||
if (result != RobotActionResult.INCOMPLETE) {
|
||||
synchronized (results) {
|
||||
if (results.size() == MAX_QUEUED_RESULTS) {
|
||||
@@ -618,10 +632,10 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
if (action == null) {
|
||||
action = queue.poll();
|
||||
if (action != null) {
|
||||
action.initialize(RobotEntity.this);
|
||||
action.initialize(Robot.this);
|
||||
}
|
||||
}
|
||||
RobotActions.performServer(RobotEntity.this, action);
|
||||
RobotActions.performServer(Robot.this, action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +728,7 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
|
||||
@Override
|
||||
protected ItemDeviceQuery getDeviceQuery(final ItemStack stack) {
|
||||
return Devices.makeQuery(RobotEntity.this, stack);
|
||||
return Devices.makeQuery(Robot.this, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -767,7 +781,7 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
|
||||
@Override
|
||||
protected void sendTerminalUpdateToClient(final ByteBuffer output) {
|
||||
Network.sendToClientsTrackingEntity(new RobotTerminalOutputMessage(RobotEntity.this, output), RobotEntity.this);
|
||||
Network.sendToClientsTrackingEntity(new RobotTerminalOutputMessage(Robot.this, output), Robot.this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,7 +810,7 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
super.stopRunnerAndReset();
|
||||
|
||||
TerminalUtils.resetTerminal(terminal, output -> Network.sendToClientsTrackingEntity(
|
||||
new RobotTerminalOutputMessage(RobotEntity.this, output), RobotEntity.this));
|
||||
new RobotTerminalOutputMessage(Robot.this, output), Robot.this));
|
||||
|
||||
actionProcessor.clear();
|
||||
}
|
||||
@@ -808,17 +822,17 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
|
||||
@Override
|
||||
protected void handleBusStateChanged(final CommonDeviceBusController.BusState value) {
|
||||
Network.sendToClientsTrackingEntity(new RobotBusStateMessage(RobotEntity.this), RobotEntity.this);
|
||||
Network.sendToClientsTrackingEntity(new RobotBusStateMessage(Robot.this), Robot.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRunStateChanged(final VMRunState value) {
|
||||
Network.sendToClientsTrackingEntity(new RobotRunStateMessage(RobotEntity.this), RobotEntity.this);
|
||||
Network.sendToClientsTrackingEntity(new RobotRunStateMessage(Robot.this), Robot.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleBootErrorChanged(@Nullable final Component value) {
|
||||
Network.sendToClientsTrackingEntity(new RobotBootErrorMessage(RobotEntity.this), RobotEntity.this);
|
||||
Network.sendToClientsTrackingEntity(new RobotBootErrorMessage(Robot.this), Robot.this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,12 +849,12 @@ public final class RobotEntity extends Entity implements Robot {
|
||||
|
||||
@Callback(synchronize = false)
|
||||
public int getSelectedSlot() {
|
||||
return RobotEntity.this.getSelectedSlot();
|
||||
return Robot.this.getSelectedSlot();
|
||||
}
|
||||
|
||||
@Callback(synchronize = false)
|
||||
public void setSelectedSlot(@Parameter("slot") final int slot) {
|
||||
RobotEntity.this.setSelectedSlot(slot);
|
||||
Robot.this.setSelectedSlot(slot);
|
||||
}
|
||||
|
||||
@Callback
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
public abstract class AbstractRobotAction {
|
||||
@@ -36,10 +36,10 @@ public abstract class AbstractRobotAction {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public void initialize(final RobotEntity robot) {
|
||||
public void initialize(final Robot robot) {
|
||||
}
|
||||
|
||||
public abstract RobotActionResult perform(RobotEntity robot);
|
||||
public abstract RobotActionResult perform(Robot robot);
|
||||
|
||||
public CompoundTag serialize() {
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
public abstract class AbstractRobotActionType {
|
||||
@@ -18,13 +18,13 @@ public abstract class AbstractRobotActionType {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void initializeData(final RobotEntity robot) {
|
||||
public void initializeData(final Robot robot) {
|
||||
}
|
||||
|
||||
public void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) {
|
||||
public void performServer(final Robot robot, final AbstractRobotAction currentAction) {
|
||||
}
|
||||
|
||||
public void performClient(final RobotEntity robot) {
|
||||
public void performClient(final Robot robot) {
|
||||
}
|
||||
|
||||
public abstract AbstractRobotAction deserialize(final CompoundTag tag);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -19,19 +19,19 @@ public final class RobotActions {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void initializeData(final RobotEntity robot) {
|
||||
public static void initializeData(final Robot robot) {
|
||||
for (final AbstractRobotActionType type : ACTIONS) {
|
||||
type.initializeData(robot);
|
||||
}
|
||||
}
|
||||
|
||||
public static void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) {
|
||||
public static void performServer(final Robot robot, final AbstractRobotAction currentAction) {
|
||||
for (final AbstractRobotActionType type : ACTIONS) {
|
||||
type.performServer(robot, currentAction);
|
||||
}
|
||||
}
|
||||
|
||||
public static void performClient(final RobotEntity robot) {
|
||||
public static void performClient(final Robot robot) {
|
||||
for (final AbstractRobotActionType type : ACTIONS) {
|
||||
type.performClient(robot);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.entity.Entities;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.NBTUtils;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -53,7 +53,7 @@ public final class RobotMovementAction extends AbstractRobotAction {
|
||||
return Vec3.atBottomCenterOf(position).add(0, 0.5f * (1 - Entities.ROBOT.get().getHeight()), 0);
|
||||
}
|
||||
|
||||
public static void moveTowards(final RobotEntity robot, final Vec3 targetPosition) {
|
||||
public static void moveTowards(final Robot robot, final Vec3 targetPosition) {
|
||||
Vec3 delta = targetPosition.subtract(robot.position());
|
||||
if (delta.lengthSqr() > MOVEMENT_SPEED * MOVEMENT_SPEED) {
|
||||
delta = delta.normalize().scale(MOVEMENT_SPEED);
|
||||
@@ -65,7 +65,7 @@ public final class RobotMovementAction extends AbstractRobotAction {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void initialize(final RobotEntity robot) {
|
||||
public void initialize(final Robot robot) {
|
||||
if (origin == null || start == null || target == null) {
|
||||
origin = robot.blockPosition();
|
||||
start = origin;
|
||||
@@ -81,11 +81,11 @@ public final class RobotMovementAction extends AbstractRobotAction {
|
||||
}
|
||||
|
||||
targetPos = getTargetPositionInBlock(target);
|
||||
robot.getEntityData().set(RobotEntity.TARGET_POSITION, target);
|
||||
robot.getEntityData().set(Robot.TARGET_POSITION, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RobotActionResult perform(final RobotEntity robot) {
|
||||
public RobotActionResult perform(final Robot robot) {
|
||||
if (targetPos == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
@@ -142,7 +142,7 @@ public final class RobotMovementAction extends AbstractRobotAction {
|
||||
}
|
||||
}
|
||||
|
||||
private void moveAndResolveCollisions(final RobotEntity robot) {
|
||||
private void moveAndResolveCollisions(final Robot robot) {
|
||||
if (start == null || target == null || targetPos == null) {
|
||||
return;
|
||||
}
|
||||
@@ -157,11 +157,11 @@ public final class RobotMovementAction extends AbstractRobotAction {
|
||||
target = start;
|
||||
start = newStart;
|
||||
targetPos = getTargetPositionInBlock(target);
|
||||
robot.getEntityData().set(RobotEntity.TARGET_POSITION, target);
|
||||
robot.getEntityData().set(Robot.TARGET_POSITION, target);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateTarget(final RobotEntity robot) {
|
||||
private void validateTarget(final Robot robot) {
|
||||
final BlockPos currentPosition = robot.blockPosition();
|
||||
if (start == null || Objects.equals(currentPosition, start) ||
|
||||
target == null || Objects.equals(currentPosition, target)) {
|
||||
@@ -184,6 +184,6 @@ public final class RobotMovementAction extends AbstractRobotAction {
|
||||
}
|
||||
|
||||
targetPos = getTargetPositionInBlock(target);
|
||||
robot.getEntityData().set(RobotEntity.TARGET_POSITION, target);
|
||||
robot.getEntityData().set(Robot.TARGET_POSITION, target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
@@ -12,20 +12,20 @@ public final class RobotMovementActionType extends AbstractRobotActionType {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void initializeData(final RobotEntity robot) {
|
||||
robot.getEntityData().set(RobotEntity.TARGET_POSITION, robot.blockPosition());
|
||||
public void initializeData(final Robot robot) {
|
||||
robot.getEntityData().set(Robot.TARGET_POSITION, robot.blockPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) {
|
||||
public void performServer(final Robot robot, final AbstractRobotAction currentAction) {
|
||||
if (!(currentAction instanceof RobotMovementAction)) {
|
||||
robot.getEntityData().set(RobotEntity.TARGET_POSITION, robot.blockPosition());
|
||||
robot.getEntityData().set(Robot.TARGET_POSITION, robot.blockPosition());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performClient(final RobotEntity robot) {
|
||||
final Vec3 target = RobotMovementAction.getTargetPositionInBlock(robot.getEntityData().get(RobotEntity.TARGET_POSITION));
|
||||
public void performClient(final Robot robot) {
|
||||
final Vec3 target = RobotMovementAction.getTargetPositionInBlock(robot.getEntityData().get(Robot.TARGET_POSITION));
|
||||
if (robot.position().distanceToSqr(target) > RobotMovementAction.TARGET_EPSILON) {
|
||||
RobotMovementAction.moveTowards(robot, target);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.util.NBTUtils;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -39,14 +39,14 @@ public final class RobotRotationAction extends AbstractRobotAction {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void rotateTowards(final RobotEntity robot, final Direction targetRotation) {
|
||||
public static void rotateTowards(final Robot robot, final Direction targetRotation) {
|
||||
robot.setYRot(Mth.approachDegrees(robot.getYRot(), targetRotation.toYRot(), ROTATION_SPEED));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void initialize(final RobotEntity robot) {
|
||||
public void initialize(final Robot robot) {
|
||||
if (target == null) {
|
||||
target = robot.getDirection();
|
||||
if (direction != null) {
|
||||
@@ -57,11 +57,11 @@ public final class RobotRotationAction extends AbstractRobotAction {
|
||||
}
|
||||
}
|
||||
|
||||
robot.getEntityData().set(RobotEntity.TARGET_DIRECTION, target);
|
||||
robot.getEntityData().set(Robot.TARGET_DIRECTION, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RobotActionResult perform(final RobotEntity robot) {
|
||||
public RobotActionResult perform(final Robot robot) {
|
||||
if (target == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.entity.robot;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.util.Mth;
|
||||
@@ -13,20 +13,20 @@ public final class RobotRotationActionType extends AbstractRobotActionType {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void initializeData(final RobotEntity robot) {
|
||||
robot.getEntityData().set(RobotEntity.TARGET_DIRECTION, robot.getDirection());
|
||||
public void initializeData(final Robot robot) {
|
||||
robot.getEntityData().set(Robot.TARGET_DIRECTION, robot.getDirection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performServer(final RobotEntity robot, final AbstractRobotAction currentAction) {
|
||||
public void performServer(final Robot robot, final AbstractRobotAction currentAction) {
|
||||
if (!(currentAction instanceof RobotRotationAction)) {
|
||||
robot.getEntityData().set(RobotEntity.TARGET_DIRECTION, robot.getDirection());
|
||||
robot.getEntityData().set(Robot.TARGET_DIRECTION, robot.getDirection());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performClient(final RobotEntity robot) {
|
||||
final Direction target = robot.getEntityData().get(RobotEntity.TARGET_DIRECTION);
|
||||
public void performClient(final Robot robot) {
|
||||
final Direction target = robot.getEntityData().get(Robot.TARGET_DIRECTION);
|
||||
if (Mth.degreesDifferenceAbs(robot.getYRot(), target.toYRot()) > RobotRotationAction.TARGET_EPSILON) {
|
||||
RobotRotationAction.rotateTowards(robot, target);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public final class Items {
|
||||
new FloppyItem(512 * Constants.KILOBYTE));
|
||||
|
||||
public static final RegistryObject<Item> REDSTONE_INTERFACE_CARD = register("redstone_interface_card");
|
||||
public static final RegistryObject<Item> NETWORK_INTERFACE_CARD = register("network_interface_card");
|
||||
public static final RegistryObject<Item> NETWORK_INTERFACE_CARD = register("network_interface_card", NetworkInterfaceCardItem::new);
|
||||
public static final RegistryObject<Item> FILE_IMPORT_EXPORT_CARD = register("file_import_export_card");
|
||||
public static final RegistryObject<Item> SOUND_CARD = register("sound_card");
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package li.cil.oc2.common.item;
|
||||
|
||||
import li.cil.oc2.client.gui.NetworkInterfaceCardScreen;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.util.ItemStackUtils;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static li.cil.oc2.common.util.TooltipUtils.withColor;
|
||||
import static li.cil.oc2.common.util.TranslationUtils.text;
|
||||
|
||||
public final class NetworkInterfaceCardItem extends ModItem {
|
||||
private static final String SIDE_CONFIGURATION_TAG_NAME = "sides";
|
||||
private static final Component IS_CONFIGURED_TEXT = withColor(text("item.{mod}.network_interface_card.is_configured"), ChatFormatting.GREEN);
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void setSideConfiguration(final ItemStack stack, final Direction side, final boolean enabled) {
|
||||
final int index = side.get3DDataValue();
|
||||
|
||||
final CompoundTag tag = ItemStackUtils.getOrCreateModDataTag(stack);
|
||||
final byte[] values;
|
||||
if (tag.contains(SIDE_CONFIGURATION_TAG_NAME, NBTTagIds.TAG_BYTE_ARRAY) &&
|
||||
tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME).length == Constants.BLOCK_FACE_COUNT) {
|
||||
values = tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME);
|
||||
} else {
|
||||
values = new byte[Constants.BLOCK_FACE_COUNT];
|
||||
Arrays.fill(values, (byte) 1);
|
||||
}
|
||||
|
||||
values[index] = (byte) (enabled ? 1 : 0);
|
||||
|
||||
tag.putByteArray(SIDE_CONFIGURATION_TAG_NAME, values);
|
||||
}
|
||||
|
||||
public static boolean getSideConfiguration(final ItemStack stack, @Nullable final Direction side) {
|
||||
if (side == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int index = side.get3DDataValue();
|
||||
|
||||
final CompoundTag tag = ItemStackUtils.getModDataTag(stack);
|
||||
if (tag.contains(SIDE_CONFIGURATION_TAG_NAME, NBTTagIds.TAG_BYTE_ARRAY)) {
|
||||
final byte[] values = tag.getByteArray(SIDE_CONFIGURATION_TAG_NAME);
|
||||
if (index < values.length) {
|
||||
return values[index] != 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean hasConfiguration(final ItemStack stack) {
|
||||
final byte[] values = ItemStackUtils.getModDataTag(stack).getByteArray(SIDE_CONFIGURATION_TAG_NAME);
|
||||
for (final byte value : values) {
|
||||
if (value == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@Override
|
||||
public void appendHoverText(final ItemStack stack, @Nullable final Level level, final List<Component> tooltip, final TooltipFlag flag) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
if (NetworkInterfaceCardItem.hasConfiguration(stack)) {
|
||||
tooltip.add(IS_CONFIGURED_TEXT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(final Level level, final Player player, final InteractionHand hand) {
|
||||
final ItemStack itemStack = player.getItemInHand(hand);
|
||||
|
||||
if (player.getLevel().isClientSide()) {
|
||||
if (itemStack.is(Items.NETWORK_INTERFACE_CARD.get())) {
|
||||
openConfigurationScreen(player, hand);
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResultHolder.sidedSuccess(itemStack, player.getLevel().isClientSide());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void openConfigurationScreen(final Player player, final InteractionHand hand) {
|
||||
Minecraft.getInstance().setScreen(new NetworkInterfaceCardScreen(player, hand));
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import li.cil.oc2.client.renderer.entity.RobotWithoutLevelRenderer;
|
||||
import li.cil.oc2.common.Config;
|
||||
import li.cil.oc2.common.energy.EnergyStorageItemStack;
|
||||
import li.cil.oc2.common.entity.Entities;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.entity.robot.RobotActions;
|
||||
import li.cil.oc2.common.util.LevelUtils;
|
||||
import li.cil.oc2.common.util.NBTUtils;
|
||||
@@ -77,7 +77,7 @@ public final class RobotItem extends ModItem {
|
||||
position = Vec3.atCenterOf(pos.relative(context.getClickedFace()));
|
||||
}
|
||||
|
||||
final RobotEntity robot = Entities.ROBOT.get().create(context.getLevel());
|
||||
final Robot robot = Entities.ROBOT.get().create(context.getLevel());
|
||||
if (robot == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ public final class Network {
|
||||
registerMessage(ClientCanceledImportFileMessage.class, ClientCanceledImportFileMessage::new, NetworkDirection.PLAY_TO_SERVER);
|
||||
|
||||
registerMessage(BusCableFacadeMessage.class, BusCableFacadeMessage::new, NetworkDirection.PLAY_TO_CLIENT);
|
||||
|
||||
registerMessage(NetworkInterfaceCardConfigurationMessage.class, NetworkInterfaceCardConfigurationMessage::new, NetworkDirection.PLAY_TO_SERVER);
|
||||
}
|
||||
|
||||
public static <T> void sendToClientsTrackingChunk(final T message, final LevelChunk chunk) {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.item.Items;
|
||||
import li.cil.oc2.common.item.NetworkInterfaceCardItem;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
public final class NetworkInterfaceCardConfigurationMessage extends AbstractMessage {
|
||||
private InteractionHand hand;
|
||||
private Direction side;
|
||||
private boolean value;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public NetworkInterfaceCardConfigurationMessage(final InteractionHand hand, final Direction side, final boolean value) {
|
||||
this.hand = hand;
|
||||
this.side = side;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public NetworkInterfaceCardConfigurationMessage(final FriendlyByteBuf buffer) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void fromBytes(final FriendlyByteBuf buffer) {
|
||||
hand = buffer.readEnum(InteractionHand.class);
|
||||
side = buffer.readEnum(Direction.class);
|
||||
value = buffer.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(final FriendlyByteBuf buffer) {
|
||||
buffer.writeEnum(hand);
|
||||
buffer.writeEnum(side);
|
||||
buffer.writeBoolean(value);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
final ServerPlayer player = context.getSender();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ItemStack itemStack = player.getItemInHand(hand);
|
||||
if (!itemStack.is(Items.NETWORK_INTERFACE_CARD.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkInterfaceCardItem.setSideConfiguration(itemStack, side, value);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -11,7 +11,7 @@ public final class OpenRobotInventoryMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public OpenRobotInventoryMessage(final RobotEntity robot) {
|
||||
public OpenRobotInventoryMessage(final Robot robot) {
|
||||
this.entityId = robot.getId();
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class OpenRobotInventoryMessage extends AbstractMessage {
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
final ServerPlayer player = context.getSender();
|
||||
if (player != null) {
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class,
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, Robot.class,
|
||||
robot -> robot.openInventoryScreen(player));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -11,7 +11,7 @@ public final class OpenRobotTerminalMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public OpenRobotTerminalMessage(final RobotEntity robot) {
|
||||
public OpenRobotTerminalMessage(final Robot robot) {
|
||||
this.entityId = robot.getId();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public final class OpenRobotTerminalMessage extends AbstractMessage {
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
final ServerPlayer player = context.getSender();
|
||||
if (player != null) {
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class,
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, Robot.class,
|
||||
robot -> robot.openTerminalScreen(player));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -12,7 +12,7 @@ public final class RobotBootErrorMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotBootErrorMessage(final RobotEntity robot) {
|
||||
public RobotBootErrorMessage(final Robot robot) {
|
||||
this.entityId = robot.getId();
|
||||
this.value = robot.getVirtualMachine().getBootError();
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public final class RobotBootErrorMessage extends AbstractMessage {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withClientEntity(entityId, RobotEntity.class,
|
||||
MessageUtils.withClientEntity(entityId, Robot.class,
|
||||
robot -> robot.getVirtualMachine().setBootErrorClient(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.bus.CommonDeviceBusController;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
@@ -12,7 +12,7 @@ public final class RobotBusStateMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotBusStateMessage(final RobotEntity robot) {
|
||||
public RobotBusStateMessage(final Robot robot) {
|
||||
this.entityId = robot.getId();
|
||||
this.value = robot.getVirtualMachine().getBusState();
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public final class RobotBusStateMessage extends AbstractMessage {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withClientEntity(entityId, RobotEntity.class,
|
||||
MessageUtils.withClientEntity(entityId, Robot.class,
|
||||
robot -> robot.getVirtualMachine().setBusStateClient(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.bus.CommonDeviceBusController;
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
import li.cil.oc2.common.vm.VMRunState;
|
||||
@@ -19,7 +19,7 @@ public final class RobotInitializationMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotInitializationMessage(final RobotEntity robot) {
|
||||
public RobotInitializationMessage(final Robot robot) {
|
||||
this.entityId = robot.getId();
|
||||
this.busState = robot.getVirtualMachine().getBusState();
|
||||
this.runState = robot.getVirtualMachine().getRunState();
|
||||
@@ -55,7 +55,7 @@ public final class RobotInitializationMessage extends AbstractMessage {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withClientEntity(entityId, RobotEntity.class,
|
||||
MessageUtils.withClientEntity(entityId, Robot.class,
|
||||
robot -> {
|
||||
robot.getVirtualMachine().setBusStateClient(busState);
|
||||
robot.getVirtualMachine().setRunStateClient(runState);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import li.cil.oc2.common.network.Network;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -11,7 +11,7 @@ public final class RobotInitializationRequestMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotInitializationRequestMessage(final RobotEntity robot) {
|
||||
public RobotInitializationRequestMessage(final Robot robot) {
|
||||
this.entityId = robot.getId();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public final class RobotInitializationRequestMessage extends AbstractMessage {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withServerEntity(context, entityId, RobotEntity.class,
|
||||
MessageUtils.withServerEntity(context, entityId, Robot.class,
|
||||
robot -> Network.INSTANCE.reply(new RobotInitializationMessage(robot), context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
@@ -11,7 +11,7 @@ public final class RobotPowerMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotPowerMessage(final RobotEntity robot, final boolean power) {
|
||||
public RobotPowerMessage(final Robot robot, final boolean power) {
|
||||
this.entityId = robot.getId();
|
||||
this.power = power;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public final class RobotPowerMessage extends AbstractMessage {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class,
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, Robot.class,
|
||||
robot -> {
|
||||
if (power) {
|
||||
robot.start();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import li.cil.oc2.common.vm.VMRunState;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
@@ -12,7 +12,7 @@ public final class RobotRunStateMessage extends AbstractMessage {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public RobotRunStateMessage(final RobotEntity robot) {
|
||||
public RobotRunStateMessage(final Robot robot) {
|
||||
this.entityId = robot.getId();
|
||||
this.value = robot.getVirtualMachine().getRunState();
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public final class RobotRunStateMessage extends AbstractMessage {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withClientEntity(entityId, RobotEntity.class,
|
||||
MessageUtils.withClientEntity(entityId, Robot.class,
|
||||
robot -> robot.getVirtualMachine().setRunStateClient(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
@@ -8,7 +8,7 @@ import net.minecraftforge.network.NetworkEvent;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class RobotTerminalInputMessage extends AbstractTerminalEntityMessage {
|
||||
public RobotTerminalInputMessage(final RobotEntity robot, final ByteBuffer data) {
|
||||
public RobotTerminalInputMessage(final Robot robot, final ByteBuffer data) {
|
||||
super(robot, data);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public final class RobotTerminalInputMessage extends AbstractTerminalEntityMessa
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, RobotEntity.class,
|
||||
MessageUtils.withNearbyServerEntity(context, entityId, Robot.class,
|
||||
robot -> robot.getTerminal().putInput(ByteBuffer.wrap(data)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package li.cil.oc2.common.network.message;
|
||||
|
||||
import li.cil.oc2.common.entity.RobotEntity;
|
||||
import li.cil.oc2.common.entity.Robot;
|
||||
import li.cil.oc2.common.network.MessageUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
@@ -8,7 +8,7 @@ import net.minecraftforge.network.NetworkEvent;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class RobotTerminalOutputMessage extends AbstractTerminalEntityMessage {
|
||||
public RobotTerminalOutputMessage(final RobotEntity robot, final ByteBuffer data) {
|
||||
public RobotTerminalOutputMessage(final Robot robot, final ByteBuffer data) {
|
||||
super(robot, data);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public final class RobotTerminalOutputMessage extends AbstractTerminalEntityMess
|
||||
|
||||
@Override
|
||||
protected void handleMessage(final NetworkEvent.Context context) {
|
||||
MessageUtils.withClientEntity(entityId, RobotEntity.class,
|
||||
MessageUtils.withClientEntity(entityId, Robot.class,
|
||||
robot -> robot.getTerminal().putOutput(ByteBuffer.wrap(data)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,17 @@ public final class HorizontalBlockUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int index = direction.get2DDataValue();
|
||||
if (index < 0) {
|
||||
if (direction.getAxis().isVertical()) {
|
||||
return direction;
|
||||
}
|
||||
|
||||
if (!blockState.hasProperty(HorizontalDirectionalBlock.FACING)) {
|
||||
return direction;
|
||||
}
|
||||
|
||||
final Direction facing = blockState.getValue(HorizontalDirectionalBlock.FACING);
|
||||
final int toLocal = HORIZONTAL_DIRECTION_COUNT - facing.get2DDataValue();
|
||||
final int rotatedIndex = (index + toLocal) % HORIZONTAL_DIRECTION_COUNT;
|
||||
final int index = direction.get2DDataValue();
|
||||
final int toLocal = -facing.get2DDataValue();
|
||||
final int rotatedIndex = (index + toLocal + HORIZONTAL_DIRECTION_COUNT) % HORIZONTAL_DIRECTION_COUNT;
|
||||
return Direction.from2DDataValue(rotatedIndex);
|
||||
}
|
||||
|
||||
@@ -39,16 +38,16 @@ public final class HorizontalBlockUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int index = side.get2DDataValue();
|
||||
if (index < 0) {
|
||||
return side.getDirection();
|
||||
final Direction direction = side.getDirection();
|
||||
if (direction.getAxis().isVertical()) {
|
||||
return direction;
|
||||
}
|
||||
|
||||
if (!blockState.hasProperty(HorizontalDirectionalBlock.FACING)) {
|
||||
return side.getDirection();
|
||||
return direction;
|
||||
}
|
||||
|
||||
final Direction facing = blockState.getValue(HorizontalDirectionalBlock.FACING);
|
||||
final int index = direction.get2DDataValue();
|
||||
final int toGlobal = facing.get2DDataValue();
|
||||
final int rotatedIndex = (index + toGlobal) % HORIZONTAL_DIRECTION_COUNT;
|
||||
return Direction.from2DDataValue(rotatedIndex);
|
||||
|
||||
@@ -9,10 +9,8 @@ public final class TerminalUtils {
|
||||
private static final ByteBuffer TERMINAL_RESET_SEQUENCE = ByteBuffer.wrap(new byte[]{
|
||||
// Make sure we're in normal mode.
|
||||
'J',
|
||||
// Reset color and style.
|
||||
'\033', '[', '0', 'm',
|
||||
// Clear screen.
|
||||
'\033', '[', '2', 'J'
|
||||
// Reset.
|
||||
'\033', 'c',
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -167,12 +167,11 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
|
||||
private void joinWorkerThread() {
|
||||
if (runner != null) {
|
||||
try {
|
||||
state.context.postEvent(new VMPausingEvent());
|
||||
runner.join();
|
||||
runner.scheduleResumeEvent();
|
||||
} catch (final Throwable e) {
|
||||
LOGGER.error(e);
|
||||
runner = null;
|
||||
setRunState(VMRunState.STOPPED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
// Implements a couple of control sequences from here: https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
|
||||
// VT100 emulation: https://vt100.net/docs/vt100-ug/chapter3.html
|
||||
@Serialized
|
||||
public final class Terminal {
|
||||
public static final int WIDTH = 80, HEIGHT = 24;
|
||||
@@ -32,14 +32,31 @@ public final class Terminal {
|
||||
|
||||
private static final int TAB_WIDTH = 4;
|
||||
|
||||
private static final int COLOR_BLACK = 0;
|
||||
private static final int COLOR_RED = 1;
|
||||
private static final int COLOR_GREEN = 2;
|
||||
private static final int COLOR_YELLOW = 3;
|
||||
private static final int COLOR_BLUE = 4;
|
||||
private static final int COLOR_MAGENTA = 5;
|
||||
private static final int COLOR_CYAN = 6;
|
||||
private static final int COLOR_WHITE = 7;
|
||||
@SuppressWarnings("unused")
|
||||
private static final class Color {
|
||||
static final int BLACK = 0;
|
||||
static final int RED = 1;
|
||||
static final int GREEN = 2;
|
||||
static final int YELLOW = 3;
|
||||
static final int BLUE = 4;
|
||||
static final int MAGENTA = 5;
|
||||
static final int CYAN = 6;
|
||||
static final int WHITE = 7;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final class Mode {
|
||||
static final int LNM = 20; // Line Feed/New Line Mode
|
||||
static final int DECCKM = 1; // Cursor key
|
||||
static final int DECANM = 2; // ANSI/VT52
|
||||
static final int DECCOLM = 3; // Column
|
||||
static final int DECSCLM = 4; // Scrolling
|
||||
static final int DECSCNM = 5; // Screen
|
||||
static final int DECOM = 6; // Origin
|
||||
static final int DECAWM = 7; // Auto wrap
|
||||
static final int DECARM = 8; // Auto repeating
|
||||
static final int DECINLM = 9; // Interlace
|
||||
}
|
||||
|
||||
private static final int COLOR_MASK = 0b111;
|
||||
private static final int COLOR_FOREGROUND_SHIFT = 3;
|
||||
@@ -52,7 +69,7 @@ public final class Terminal {
|
||||
private static final int STYLE_HIDDEN_MASK = 1 << 5;
|
||||
|
||||
// Default style: no modifiers, white foreground, black background.
|
||||
private static final byte DEFAULT_COLORS = COLOR_WHITE << COLOR_FOREGROUND_SHIFT;
|
||||
private static final byte DEFAULT_COLORS = Color.WHITE << COLOR_FOREGROUND_SHIFT;
|
||||
private static final byte DEFAULT_STYLE = 0;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -60,7 +77,10 @@ public final class Terminal {
|
||||
public enum State { // Must be public for serialization.
|
||||
NORMAL, // Currently reading characters normally.
|
||||
ESCAPE, // Last character was ESC, figure out what kind next.
|
||||
SEQUENCE, // Know what sequence we have, now parsing it.
|
||||
SHIFT_IN_CHARACTER_SET, // Shift in character set.
|
||||
SHIFT_OUT_CHARACTER_SET, // Shift out character set.
|
||||
HASH, // Escape sequence with # intermediate.
|
||||
CONTROL_SEQUENCE, // Know what sequence we have, now parsing it.
|
||||
}
|
||||
|
||||
public interface RendererView {
|
||||
@@ -73,18 +93,21 @@ public final class Terminal {
|
||||
private final byte[] buffer = new byte[WIDTH * HEIGHT];
|
||||
private final byte[] colors = new byte[WIDTH * HEIGHT];
|
||||
private final byte[] styles = new byte[WIDTH * HEIGHT];
|
||||
private final boolean[] tabs = new boolean[WIDTH];
|
||||
private State state = State.NORMAL;
|
||||
private final int[] args = new int[4];
|
||||
private int argCount = 0;
|
||||
private int modes;
|
||||
private int scrollFirst = 0, scrollLast = HEIGHT - 1;
|
||||
private int x, y;
|
||||
private int savedX, savedY;
|
||||
|
||||
// Color info packed into one byte for compact storage
|
||||
// 0-2: background color (index)
|
||||
// 3-5: foreground color (index)
|
||||
private byte color = DEFAULT_COLORS;
|
||||
private byte color;
|
||||
// Style info packed into one byte for compact storage
|
||||
private byte style = DEFAULT_STYLE;
|
||||
private byte style;
|
||||
|
||||
// Rendering data for client
|
||||
private final transient Set<RendererModel> renderers = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
|
||||
@@ -94,7 +117,7 @@ public final class Terminal {
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public Terminal() {
|
||||
clear();
|
||||
RIS();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -179,36 +202,58 @@ public final class Terminal {
|
||||
switch (state) {
|
||||
case NORMAL -> {
|
||||
switch (value) {
|
||||
case (byte) '\r' -> setCursorPos(0, y);
|
||||
case (byte) '\n' -> putNewLine();
|
||||
case (byte) '\t' -> {
|
||||
if (x + TAB_WIDTH > WIDTH) {
|
||||
setCursorPos(0, y);
|
||||
putNewLine();
|
||||
case '\007' -> hasPendingBell = true;
|
||||
case '\033' -> state = State.ESCAPE;
|
||||
case '\016' -> { } // SO
|
||||
case '\017' -> { } // SI
|
||||
|
||||
case (byte) '\r' /* 015 */ -> setCursorPos(0, y);
|
||||
case (byte) '\n' /* 012 */, '\013', '\014' -> {
|
||||
if (getMode(Mode.LNM)) {
|
||||
NEL();
|
||||
} else {
|
||||
setCursorPos(x + TAB_WIDTH - (x % TAB_WIDTH), y);
|
||||
IND();
|
||||
}
|
||||
}
|
||||
case (byte) '\b' -> setCursorPos(x - 1, y);
|
||||
case 7 -> hasPendingBell = true;
|
||||
case 27 -> state = State.ESCAPE;
|
||||
default -> {
|
||||
if (!Character.isISOControl(ch)) {
|
||||
putChar(ch);
|
||||
case (byte) '\t' /* 011 */ -> {
|
||||
if (x < WIDTH) {
|
||||
do {
|
||||
x++;
|
||||
} while (x < WIDTH && !tabs[x]);
|
||||
}
|
||||
}
|
||||
case (byte) '\b' /* 010 */ -> setCursorPos(Math.min(x, WIDTH - 1) - 1, y);
|
||||
|
||||
default -> putChar(ch);
|
||||
}
|
||||
}
|
||||
case ESCAPE -> {
|
||||
if (ch == '[') {
|
||||
if (ch == '[') { // Control Sequence Indicator
|
||||
Arrays.fill(args, (byte) 0);
|
||||
argCount = 0;
|
||||
state = State.SEQUENCE;
|
||||
state = State.CONTROL_SEQUENCE;
|
||||
} else if (ch == '(') { // SCS – Select Character Set
|
||||
state = State.SHIFT_IN_CHARACTER_SET;
|
||||
} else if (ch == ')') { // SCS – Select Character Set
|
||||
state = State.SHIFT_OUT_CHARACTER_SET;
|
||||
} else if (ch == '#') { // # Intermediate
|
||||
state = State.HASH;
|
||||
} else {
|
||||
state = State.NORMAL;
|
||||
switch (ch) {
|
||||
case 'D' -> IND(); // IND – Index
|
||||
case 'E' -> NEL(); // NEL – Next Line
|
||||
case 'M' -> RI(); // RI – Reverse Index
|
||||
case '7' -> DECSC(); // DECSC – Save Cursor (DEC Private)
|
||||
case '8' -> DECRC(); // DECRC – Restore Cursor (DEC Private)
|
||||
case 'H' -> HTS(); // HTS – Horizontal Tabulation Set
|
||||
case 'c' -> RIS(); // RIS – Reset To Initial State
|
||||
case '=' -> { } // DECKPAM – Keypad Application Mode (DEC Private)
|
||||
case '>' -> { } // DECKPNM – Keypad Numeric Mode (DEC Private)
|
||||
}
|
||||
}
|
||||
}
|
||||
case SEQUENCE -> {
|
||||
case CONTROL_SEQUENCE -> {
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
if (argCount < args.length) {
|
||||
final int digit = ch - '0';
|
||||
@@ -219,101 +264,272 @@ public final class Terminal {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ch == '?') {
|
||||
break; // Ignore ? intermediate character.
|
||||
}
|
||||
|
||||
if (argCount < args.length) {
|
||||
argCount++;
|
||||
}
|
||||
|
||||
if (ch == ';' || ch == '?') {
|
||||
break;
|
||||
if (ch == ';') {
|
||||
break; // Keep going, we have another argument.
|
||||
}
|
||||
|
||||
state = State.NORMAL;
|
||||
|
||||
switch (ch) {
|
||||
case 'A' -> // Cursor Up
|
||||
setCursorPos(x, y - Math.max(1, args[0]));
|
||||
case 'B' -> // Cursor Down
|
||||
setCursorPos(x, y + Math.max(1, args[0]));
|
||||
case 'C' -> // Cursor Forward
|
||||
setCursorPos(x + Math.max(1, args[0]), y);
|
||||
case 'D' -> // Cursor Back
|
||||
setCursorPos(x - Math.max(1, args[0]), y);
|
||||
case 'E' -> // Cursor Next Line
|
||||
setCursorPos(0, y + Math.min(1, args[0]));
|
||||
case 'F' -> // Cursor Previous Line
|
||||
setCursorPos(0, y - Math.min(1, args[0]));
|
||||
case 'G' -> // Cursor Horizontal Absolute
|
||||
setCursorPos(args[0] - 1, y);
|
||||
// Don't care about terminal mode fanciness so just alias.
|
||||
case 'f', 'H' -> // Cursor Position
|
||||
setCursorPos(args[1] - 1, args[0] - 1);
|
||||
case 'J' -> { // Erase in Display
|
||||
if (args[0] == 0) { // Cursor and down
|
||||
clearLine(y, x, WIDTH);
|
||||
for (int iy = y + 1; iy < HEIGHT; iy++) {
|
||||
clearLine(iy);
|
||||
}
|
||||
} else if (args[0] == 1) { // Cursor and up
|
||||
clearLine(y, 0, x + 1);
|
||||
for (int iy = 0; iy < y; iy++) {
|
||||
clearLine(iy);
|
||||
}
|
||||
} else if (args[0] == 2) { // Everything
|
||||
clear();
|
||||
}
|
||||
}
|
||||
case 'K' -> { // Erase in Line
|
||||
if (args[0] == 0) { // Cursor and right
|
||||
clearLine(y, x, WIDTH);
|
||||
} else if (args[0] == 1) { // Cursor and left
|
||||
clearLine(y, 0, x + 1);
|
||||
} else if (args[0] == 2) { // ...entirely
|
||||
clearLine(y);
|
||||
}
|
||||
}
|
||||
|
||||
// S, T: Scroll Up/Down. We don't have scrollback.
|
||||
case 'm' -> { // Select Graphic Rendition
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
final int arg = args[i];
|
||||
selectStyle(arg);
|
||||
}
|
||||
}
|
||||
case 'n' -> { // Device Status Report
|
||||
switch (args[0]) {
|
||||
case 5 -> { // Report console status
|
||||
if (!displayOnly) {
|
||||
putInput((byte) 27);
|
||||
for (final char i : "[0n".toCharArray()) {
|
||||
putInput((byte) i);
|
||||
}
|
||||
}
|
||||
}
|
||||
case 6 -> { // Report cursor position
|
||||
if (!displayOnly) {
|
||||
putInput((byte) 27);
|
||||
for (final char i : String.format("[%d;%dR", (y % HEIGHT) + 1, x + 1).toCharArray()) {
|
||||
putInput((byte) i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case 's' -> { // Save Current Cursor Position
|
||||
savedX = x;
|
||||
savedY = y;
|
||||
}
|
||||
case 'u' -> { // Restore Saved Cursor Position
|
||||
x = savedX;
|
||||
y = savedY;
|
||||
}
|
||||
case 'A' -> CUU(); // CUU - Cursor Up
|
||||
case 'B' -> CUD(); // CUD – Cursor Down
|
||||
case 'C' -> CUF(); // CUF – Cursor Forward
|
||||
case 'D' -> CUB(); // CUB – Cursor Backward
|
||||
case 'H' -> CUP(); // CUP - Cursor Position
|
||||
case 'f' -> HVP(); // HVP – Horizontal and Vertical Position
|
||||
case 'm' -> SGR(); // SGR – Select Graphic Rendition
|
||||
case 'K' -> EL(); // EL – Erase In Line
|
||||
case 'J' -> ED(); // ED – Erase In Display
|
||||
case 'r' -> DECSTBM(); // DECSTBM – Set Top and Bottom Margins (DEC Private)
|
||||
case 'g' -> TBC(); // TBC – Tabulation Clear
|
||||
case 'h' -> SM(); // SM – Set Mode
|
||||
case 'l' -> RM(); // RM – Reset Mode
|
||||
case 'n' -> DSR(); // DSR – Device Status Report
|
||||
case 'c' -> DA(); // DA – Device Attributes
|
||||
}
|
||||
}
|
||||
}
|
||||
case SHIFT_IN_CHARACTER_SET, SHIFT_OUT_CHARACTER_SET -> {
|
||||
state = State.NORMAL;
|
||||
switch (ch) {
|
||||
case 'A' -> { } // United Kingdom Set
|
||||
case 'B' -> { } // ASCII Set
|
||||
case '0' -> { } // Special Graphics
|
||||
case '1' -> { } // Alternate Character ROM Standard Character Set
|
||||
case '2' -> { } // Alternate Character ROM Special Graphics
|
||||
}
|
||||
}
|
||||
case HASH -> {
|
||||
state = State.NORMAL;
|
||||
switch (ch) {
|
||||
case '3' -> { } // Change this line to double-height top half (DECDHL)
|
||||
case '4' -> { } // Change this line to double-height bottom half (DECDHL)
|
||||
case '5' -> { } // Change this line to single-width single-height (DECSWL)
|
||||
case '6' -> { } // Change this line to double-width single-height (DECDWL)
|
||||
case '8' -> { // Fill Screen with Es (DECALN)
|
||||
Arrays.fill(buffer, (byte) 'E');
|
||||
renderers.forEach(model -> model.getDirtyMask().set(-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
private void IND() {
|
||||
if (y >= scrollLast) {
|
||||
shiftUpOne();
|
||||
} else {
|
||||
setCursorPos(x, y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void NEL() {
|
||||
if (y >= scrollLast) {
|
||||
shiftUpOne();
|
||||
setCursorPos(0, y);
|
||||
} else {
|
||||
setCursorPos(0, y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void RI() {
|
||||
if (y <= scrollFirst) {
|
||||
shiftDownOne();
|
||||
} else {
|
||||
setCursorPos(0, y - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void DECSC() {
|
||||
savedX = x;
|
||||
savedY = y;
|
||||
}
|
||||
|
||||
private void DECRC() {
|
||||
x = savedX;
|
||||
y = savedY;
|
||||
}
|
||||
|
||||
private void HTS() {
|
||||
if (x >= 0 && x < WIDTH) {
|
||||
tabs[x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void RIS() {
|
||||
color = DEFAULT_COLORS;
|
||||
style = DEFAULT_STYLE;
|
||||
clear();
|
||||
Arrays.fill(tabs, false);
|
||||
for (int i = 1; i < WIDTH; i++) {
|
||||
if (i % TAB_WIDTH == 0) {
|
||||
tabs[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CUU() {
|
||||
setClampedCursorPos(x, y - Math.max(1, args[0]));
|
||||
}
|
||||
|
||||
private void CUD() {
|
||||
setClampedCursorPos(x, y + Math.max(1, args[0]));
|
||||
}
|
||||
|
||||
private void CUF() {
|
||||
setClampedCursorPos(x + Math.max(1, args[0]), y);
|
||||
}
|
||||
|
||||
private void CUB() {
|
||||
setClampedCursorPos(x - Math.max(1, args[0]), y);
|
||||
}
|
||||
|
||||
private void CUP() {
|
||||
setRelativeCursorPos(args[1] - 1, args[0] - 1);
|
||||
}
|
||||
|
||||
private void HVP() {
|
||||
CUP();
|
||||
}
|
||||
|
||||
private void SGR() {
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
selectStyle(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void EL() {
|
||||
switch (args[0]) {
|
||||
case 0 -> // From cursor to end of line
|
||||
clearLine(y, x, WIDTH);
|
||||
case 1 -> // From beginning of line to cursor
|
||||
clearLine(y, 0, x + 1);
|
||||
case 2 -> // Entire line containing cursor
|
||||
clearLine(y);
|
||||
}
|
||||
}
|
||||
|
||||
private void ED() {
|
||||
switch (args[0]) {
|
||||
case 0 -> { // From cursor to end of screen
|
||||
clearLine(y, x, WIDTH);
|
||||
for (int iy = y + 1; iy < HEIGHT; iy++) {
|
||||
clearLine(iy);
|
||||
}
|
||||
}
|
||||
case 1 -> { // From beginning of screen to cursor
|
||||
for (int iy = 0; iy < y; iy++) {
|
||||
clearLine(iy);
|
||||
}
|
||||
clearLine(y, 0, x + 1);
|
||||
}
|
||||
case 2 -> // Entire screen
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void DECSTBM() {
|
||||
final int first, last;
|
||||
if (argCount == 2) {
|
||||
first = args[0] - 1;
|
||||
last = args[1] - 1;
|
||||
} else {
|
||||
first = 0;
|
||||
last = HEIGHT - 1;
|
||||
}
|
||||
if (first < 0 || last > HEIGHT - 1 || last - first <= 0) {
|
||||
return;
|
||||
}
|
||||
scrollFirst = first; // to index
|
||||
scrollLast = last; // to index
|
||||
setRelativeCursorPos(0, 0); // send cursor home
|
||||
}
|
||||
|
||||
private void TBC() {
|
||||
switch (args[0]) {
|
||||
case 0 -> { // Clear tab at current column
|
||||
if (x >= 0 && x < WIDTH) {
|
||||
tabs[x] = false;
|
||||
}
|
||||
}
|
||||
case 3 -> // Clear all tabs
|
||||
Arrays.fill(tabs, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SM() {
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
final int mode = args[i];
|
||||
if (mode != 0) {
|
||||
setMode(mode);
|
||||
}
|
||||
if (mode == Mode.DECOM) {
|
||||
setRelativeCursorPos(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RM() {
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
final int mode = args[i];
|
||||
if (mode != 0) {
|
||||
resetMode(mode);
|
||||
}
|
||||
if (mode == Mode.DECOM) {
|
||||
setRelativeCursorPos(0, 0);
|
||||
clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DSR() {
|
||||
switch (args[0]) {
|
||||
case 5 -> // Report console status
|
||||
putResponse("\033[0n"); // Ready, No malfunctions detected
|
||||
case 6 -> { // Report cursor position
|
||||
if (getMode(Mode.DECOM)) {
|
||||
putResponse(String.format("\033[%d;%dR", (y - scrollFirst) + 1, x + 1));
|
||||
} else {
|
||||
putResponse(String.format("\033[%d;%dR", (y % HEIGHT) + 1, x + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DA() {
|
||||
putResponse("\033[?1;0c"); // No options.
|
||||
}
|
||||
|
||||
private void setMode(final int mode) {
|
||||
modes |= 1 << mode;
|
||||
}
|
||||
|
||||
private void resetMode(final int mode) {
|
||||
modes &= ~(1 << mode);
|
||||
}
|
||||
|
||||
private boolean getMode(final int mode) {
|
||||
return (modes & (1 << mode)) != 0;
|
||||
}
|
||||
|
||||
private void putResponse(final String value) {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
putResponse((byte) value.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void putResponse(final byte value) {
|
||||
if (!displayOnly) {
|
||||
putInput(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void selectStyle(final int sgr) {
|
||||
switch (sgr) {
|
||||
@@ -325,11 +541,11 @@ public final class Terminal {
|
||||
style |= STYLE_BOLD_MASK;
|
||||
case 2 -> // Faint or decreased intensity
|
||||
style |= STYLE_DIM_MASK;
|
||||
case 4 -> // Underline
|
||||
case 4 -> // Underscore
|
||||
style |= STYLE_UNDERLINE_MASK;
|
||||
case 5 -> // Slow Blink
|
||||
case 5 -> // Blink
|
||||
style |= STYLE_BLINK_MASK;
|
||||
case 7 -> // Reverse video
|
||||
case 7 -> // Negative (reverse) image
|
||||
style |= STYLE_INVERT_MASK;
|
||||
case 8 -> // Conceal aka Hide
|
||||
style |= STYLE_HIDDEN_MASK;
|
||||
@@ -354,15 +570,33 @@ public final class Terminal {
|
||||
}
|
||||
}
|
||||
|
||||
private void setRelativeCursorPos(final int x, final int y) {
|
||||
if (getMode(Mode.DECOM)) {
|
||||
setCursorPos(x, Math.min(scrollFirst + y, scrollLast));
|
||||
} else {
|
||||
setCursorPos(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private void setClampedCursorPos(final int x, final int y) {
|
||||
setCursorPos(x, Math.max(scrollFirst, Math.min(scrollLast, y)));
|
||||
}
|
||||
|
||||
private void setCursorPos(final int x, final int y) {
|
||||
this.x = Math.max(0, Math.min(WIDTH - 1, x));
|
||||
this.y = Math.max(0, Math.min(HEIGHT - 1, y));
|
||||
}
|
||||
|
||||
private void putChar(final char ch) {
|
||||
if (Character.isISOControl(ch))
|
||||
return;
|
||||
|
||||
if (x >= WIDTH) {
|
||||
setCursorPos(0, y);
|
||||
putNewLine();
|
||||
if (getMode(Mode.DECAWM)) {
|
||||
NEL();
|
||||
} else {
|
||||
setCursorPos(WIDTH - 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
setChar(x, y, ch);
|
||||
@@ -387,7 +621,7 @@ public final class Terminal {
|
||||
Arrays.fill(buffer, (byte) ' ');
|
||||
Arrays.fill(colors, DEFAULT_COLORS);
|
||||
Arrays.fill(styles, DEFAULT_STYLE);
|
||||
renderers.forEach(model -> model.getDirtyMask().set((1 << HEIGHT) - 1));
|
||||
renderers.forEach(model -> model.getDirtyMask().set(-1));
|
||||
}
|
||||
|
||||
private void clearLine(final int y) {
|
||||
@@ -401,24 +635,41 @@ public final class Terminal {
|
||||
renderers.forEach(model -> model.getDirtyMask().accumulateAndGet(1 << y, (prev, next) -> prev | next));
|
||||
}
|
||||
|
||||
private void putNewLine() {
|
||||
y++;
|
||||
if (y >= HEIGHT) {
|
||||
y = HEIGHT - 1;
|
||||
shiftUpOne();
|
||||
}
|
||||
private void shiftUpOne() {
|
||||
shiftLines(scrollFirst + 1, scrollLast, -1);
|
||||
}
|
||||
|
||||
private void shiftUpOne() {
|
||||
System.arraycopy(buffer, WIDTH, buffer, 0, buffer.length - WIDTH);
|
||||
System.arraycopy(colors, WIDTH, colors, 0, colors.length - WIDTH);
|
||||
System.arraycopy(styles, WIDTH, styles, 0, styles.length - WIDTH);
|
||||
Arrays.fill(buffer, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, (byte) ' ');
|
||||
Arrays.fill(colors, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, DEFAULT_COLORS);
|
||||
Arrays.fill(styles, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, DEFAULT_STYLE);
|
||||
private void shiftDownOne() {
|
||||
shiftLines(scrollFirst, scrollLast - 1, 1);
|
||||
}
|
||||
|
||||
// Offset is baked into buffers so we must rebuild them all.
|
||||
renderers.forEach(model -> model.getDirtyMask().set(-1));
|
||||
private void shiftLines(final int firstLine, final int lastLine, final int count) {
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
final int srcIndex = firstLine * WIDTH;
|
||||
final int charCount = (lastLine + 1) * WIDTH - srcIndex;
|
||||
final int dstIndex = srcIndex + count * WIDTH;
|
||||
|
||||
System.arraycopy(buffer, srcIndex, buffer, dstIndex, charCount);
|
||||
System.arraycopy(colors, srcIndex, colors, dstIndex, charCount);
|
||||
System.arraycopy(styles, srcIndex, styles, dstIndex, charCount);
|
||||
|
||||
final int clearIndex = count > 0 ? srcIndex : (dstIndex + charCount);
|
||||
final int clearCount = Math.abs(count * WIDTH);
|
||||
Arrays.fill(buffer, clearIndex, clearIndex + clearCount, (byte) ' ');
|
||||
// TODO Copy color and style from last line.
|
||||
Arrays.fill(colors, clearIndex, clearIndex + clearCount, DEFAULT_COLORS);
|
||||
Arrays.fill(styles, clearIndex, clearIndex + clearCount, DEFAULT_STYLE);
|
||||
|
||||
int dirtyLinesMask = 0;
|
||||
final int dirtyStart = Math.min(firstLine, firstLine + count);
|
||||
final int dirtyEnd = Math.max(lastLine, lastLine + count);
|
||||
for (int i = dirtyStart; i <= dirtyEnd; i++) {
|
||||
dirtyLinesMask |= 1 << i;
|
||||
}
|
||||
final int finalDirtyLinesMask = dirtyLinesMask;
|
||||
renderers.forEach(model -> model.getDirtyMask().accumulateAndGet(finalDirtyLinesMask, (left, right) -> left | right));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -671,7 +922,7 @@ public final class Terminal {
|
||||
final BufferBuilder buffer = Tesselator.getInstance().getBuilder();
|
||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||
|
||||
final int foreground = COLORS[COLOR_WHITE];
|
||||
final int foreground = COLORS[Color.WHITE];
|
||||
final float r = ((foreground >> 16) & 0xFF) / 255f;
|
||||
final float g = ((foreground >> 8) & 0xFF) / 255f;
|
||||
final float b = ((foreground) & 0xFF) / 255f;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import li.cil.ceres.api.Serialized;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMInitializationException;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMInitializingEvent;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent;
|
||||
import li.cil.oc2.api.bus.device.vm.event.*;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.bus.RPCDeviceBusAdapter;
|
||||
import li.cil.oc2.common.vm.context.global.GlobalVMContext;
|
||||
@@ -40,7 +37,7 @@ public class VMRunner implements Runnable {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean firedResumeEvent;
|
||||
private boolean firedResumedRunningEvent;
|
||||
@Serialized private boolean firedInitializationEvent;
|
||||
@Serialized private Component runtimeError;
|
||||
|
||||
@@ -57,10 +54,6 @@ public class VMRunner implements Runnable {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public void scheduleResumeEvent() {
|
||||
firedResumeEvent = false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Component getRuntimeError() {
|
||||
return runtimeError;
|
||||
@@ -79,14 +72,20 @@ public class VMRunner implements Runnable {
|
||||
}
|
||||
|
||||
public void join() throws Throwable {
|
||||
if (lastSchedule != null) {
|
||||
try {
|
||||
lastSchedule.get();
|
||||
} catch (final InterruptedException e) {
|
||||
// We do not mind this.
|
||||
} catch (final ExecutionException e) {
|
||||
throw e.getCause();
|
||||
context.postEvent(new VMPausingEvent());
|
||||
try {
|
||||
if (lastSchedule != null) {
|
||||
try {
|
||||
lastSchedule.get();
|
||||
} catch (final InterruptedException e) {
|
||||
// We do not mind this.
|
||||
} catch (final ExecutionException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
context.postEvent(new VMResumingRunningEvent());
|
||||
firedResumedRunningEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,9 +135,8 @@ public class VMRunner implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
if (!firedResumeEvent) {
|
||||
firedResumeEvent = true;
|
||||
context.postEvent(new VMResumingRunningEvent());
|
||||
if (!firedResumedRunningEvent) {
|
||||
firedResumedRunningEvent = true;
|
||||
context.postEvent(new VMResumedRunningEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ authors = "Sangar"
|
||||
description = '''
|
||||
Modern computers for the modern player.
|
||||
|
||||
This mod uses the Terminus Font under the Open Font License.
|
||||
This mod uses the Terminus Font under the Open Font License. The full license can be found in the JAR of this mod.
|
||||
'''
|
||||
|
||||
[[dependencies.oc2]]
|
||||
@@ -29,7 +29,7 @@ side = "BOTH"
|
||||
[[dependencies.oc2]]
|
||||
modId = "sedna"
|
||||
mandatory = true
|
||||
versionRange = "[1.0.0,)"
|
||||
versionRange = "[1.0.5,)"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
||||
[[dependencies.oc2]]
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
Bus interfaces connect external devices to [computers](computer.md). This includes explicit device blocks, such as the [redstone interface](redstone_interface.md). Some generic functionality blocks of blocks is also available, such as information on energy storage.
|
||||
|
||||
It is possible to configure an explicit name for a bus interface using a [wrench](../item/wrench.md). This is useful when attaching multiple devices of the same type to a computer: when searching devices by name, these custom names will also work.
|
||||
It is possible to configure an explicit name for a bus interface using a [wrench](../item/wrench.md). This is useful when attaching multiple devices of the same type to a computer: when searching devices by type name (`devices:find(typeName)`), these custom names will also work.
|
||||
|
||||
Note that [computers](computer.md) must also be explicitly connected to a [bus](bus_cable.md) with a bus connector.
|
||||
Note that [computers](computer.md) must also be explicitly connected to a [bus](bus_cable.md) with a bus connector.
|
||||
|
||||
@@ -5,6 +5,8 @@ The network interface card (NIC) allows [computers](../block/computer.md) to sen
|
||||
|
||||
Computers *have to be shut down* before installing or removing this component. Installing it while the computer is running will have no effect, removing it may lead to system errors.
|
||||
|
||||
These cards can be configured to only connect to selected sides (use while holding). This allows using multiple cards to build a custom router, for example.
|
||||
|
||||
When using the default Linux distribution, this device will provide a regular ethernet device. Network setup can either be performed manually, or using the convenience script `setup-network.lua`. This script provides the option to either use a fixed-address setup, or a DHCP setup. For a DHCP setup, exactly one computer in the network must act as a DHCP server.
|
||||
|
||||
After initial setup, use the command `ifconfig` to see the currently used IP address.
|
||||
After initial setup, use the command `ifconfig` to see the currently used IP address.
|
||||
|
||||
@@ -14,7 +14,7 @@ To use the `devices` library, import it using `require("devices")`.
|
||||
- `id` is the unique identifier of the device.
|
||||
- Returns a wrapper for the specified device.
|
||||
|
||||
`find(typeName):Device` returns a wrapper for a device of the specified type. If there are many devices of this type, it is undefined which one will be returned.
|
||||
`find(typeName):Device` returns a wrapper for a device of the specified type. If there are many devices of this type, it is undefined which one will be returned. Aliases set in [Bus Interfaces](block/bus_interface.md) may also be used.
|
||||
- `typeName` is the device type for which to find a device.
|
||||
- Returns a wrapper for a device of the specified device.
|
||||
|
||||
@@ -72,4 +72,4 @@ The lamp should now light up!
|
||||
|
||||

|
||||
|
||||
With this, you have the tools to learn the names of connected devices, which methods they offer and how to obtain their documentation. Go ahead and experiment with the other methods of the redstone interface to read incoming redstone signals, or try out other devices!
|
||||
With this, you have the tools to learn the names of connected devices, which methods they offer and how to obtain their documentation. Go ahead and experiment with the other methods of the redstone interface to read incoming redstone signals, or try out other devices!
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
"item.oc2.redstone_interface_card": "Redstone Interface Card",
|
||||
"item.oc2.redstone_interface_card.desc": "This card allows the computer to emit a redstone signal from all directions. A useful thing.",
|
||||
"item.oc2.network_interface_card": "Network Interface Card",
|
||||
"item.oc2.network_interface_card.desc": "With this card, the computer can communicate with a network connected to it through a Network Connector.",
|
||||
"item.oc2.network_interface_card.desc": "Allows sending and receiving messages via attached Network Connector.",
|
||||
"item.oc2.network_interface_card.is_configured": "Has connectivity configuration.",
|
||||
"item.oc2.file_import_export_card": "File Import/Export Card",
|
||||
"item.oc2.file_import_export_card.desc": "Provides an API to import and export files into and out of a virtual computer from and into your real file system.",
|
||||
"item.oc2.robot": "Robot",
|
||||
@@ -85,6 +86,11 @@
|
||||
"gui.oc2.file_chooser.confirm_button.overwrite": "Overwrite",
|
||||
"gui.oc2.file_chooser.cancel_button": "Cancel",
|
||||
|
||||
"gui.oc2.network_interface_card.side_state": "Connectivity: %s",
|
||||
"gui.oc2.network_interface_card.connectivity.enabled": "Enabled",
|
||||
"gui.oc2.network_interface_card.connectivity.disabled": "Disabled",
|
||||
"gui.oc2.network_interface_card.info": "Drag to rotate. Click faces to toggle connectivity.",
|
||||
|
||||
"manual.oc2.home": "Home",
|
||||
"manual.oc2.blocks": "Blocks",
|
||||
"manual.oc2.items": "Items",
|
||||
@@ -109,4 +115,4 @@
|
||||
"subtitles.oc2.floppy_eject": "Floppy ejected",
|
||||
"subtitles.oc2.floppy_insert": "Floppy inserted",
|
||||
"subtitles.oc2.hdd": "Hard drive access"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
27
src/main/scripts/init.d/S10export_terminfo
Normal file
27
src/main/scripts/init.d/S10export_terminfo
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
TERMINFO_PROFILE="/etc/profile.d/02_init_terminfo.sh"
|
||||
|
||||
# Registers terminfo environment variables in global profile config if they don't exist yet
|
||||
start() {
|
||||
if [ ! -f $TERMINFO_PROFILE ]
|
||||
then
|
||||
echo "TERMINFO=/usr/lib/terminfo/v/vt100" > $TERMINFO_PROFILE
|
||||
echo "COLUMNS=80" >> $TERMINFO_PROFILE
|
||||
echo "LINES=24" >> $TERMINFO_PROFILE
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit $?
|
||||
Reference in New Issue
Block a user