mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-04 12:58:07 +00:00
Merge branch 'mc-1.20.x' into mc-1.21.x
This commit is contained in:
commit
9bb62b047a
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -30,3 +30,5 @@ body:
|
|||||||
Description of the bug. Please include the following:
|
Description of the bug. Please include the following:
|
||||||
- Logs: These will be located in the `logs/` directory of your Minecraft instance. This is always useful, even if it doesn't include errors, so please upload this!
|
- Logs: These will be located in the `logs/` directory of your Minecraft instance. This is always useful, even if it doesn't include errors, so please upload this!
|
||||||
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
||||||
|
|
||||||
|

|
||||||
|
@ -10,8 +10,9 @@ kotlin.jvm.target.validation.mode=error
|
|||||||
|
|
||||||
neogradle.subsystems.conventions.runs.enabled=false
|
neogradle.subsystems.conventions.runs.enabled=false
|
||||||
|
|
||||||
|
# Mod properties
|
||||||
isUnstable=true
|
isUnstable=true
|
||||||
modVersion=1.114.3
|
modVersion=1.114.4
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.21.1
|
mcVersion=1.21.1
|
||||||
|
@ -31,6 +31,7 @@ import dan200.computercraft.shared.computer.blocks.CommandComputerBlock;
|
|||||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||||
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||||
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
|
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
|
||||||
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
||||||
@ -90,7 +91,6 @@ import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable;
|
|||||||
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
||||||
import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker;
|
import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker;
|
||||||
import dan200.computercraft.shared.turtle.upgrades.TurtleTool;
|
import dan200.computercraft.shared.turtle.upgrades.TurtleTool;
|
||||||
import dan200.computercraft.shared.util.ComponentMap;
|
|
||||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||||
import dan200.computercraft.shared.util.NonNegativeId;
|
import dan200.computercraft.shared.util.NonNegativeId;
|
||||||
import dan200.computercraft.shared.util.StorageCapacity;
|
import dan200.computercraft.shared.util.StorageCapacity;
|
||||||
@ -615,7 +615,7 @@ public final class ModRegistry {
|
|||||||
|
|
||||||
ComputerCraftAPI.registerAPIFactory(computer -> {
|
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||||
var turtle = computer.getComponent(ComputerComponents.TURTLE);
|
var turtle = computer.getComponent(ComputerComponents.TURTLE);
|
||||||
var metrics = Objects.requireNonNull(computer.getComponent(ComponentMap.METRICS));
|
var metrics = Objects.requireNonNull(computer.getComponent(ServerComputer.METRICS));
|
||||||
return turtle == null ? null : new TurtleAPI(metrics, (TurtleAccessInternal) turtle);
|
return turtle == null ? null : new TurtleAPI(metrics, (TurtleAccessInternal) turtle);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,15 +24,13 @@ import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
|||||||
import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver;
|
import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.ComputerMetrics;
|
import dan200.computercraft.shared.computer.metrics.basic.ComputerMetrics;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.entity.RelativeMovement;
|
import net.minecraft.world.entity.RelativeMovement;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
@ -259,18 +257,11 @@ public final class CommandComputerCraft {
|
|||||||
* @return The constant {@code 1}.
|
* @return The constant {@code 1}.
|
||||||
*/
|
*/
|
||||||
private static int view(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
|
private static int view(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
|
||||||
var player = source.getPlayerOrException();
|
PlatformHelper.get().openMenu(
|
||||||
new ComputerContainerData(computer, new ItemStack(ModRegistry.Items.COMPUTER_NORMAL.get())).open(player, new MenuProvider() {
|
source.getPlayerOrException(), Component.translatable("gui.computercraft.view_computer"),
|
||||||
@Override
|
(id, player, entity) -> new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, player, p -> true, computer),
|
||||||
public Component getDisplayName() {
|
new ComputerContainerData(computer, new ItemStack(ModRegistry.Items.COMPUTER_NORMAL.get()))
|
||||||
return Component.translatable("gui.computercraft.view_computer");
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) {
|
|
||||||
return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, player, p -> true, computer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.computer.blocks;
|
|||||||
import dan200.computercraft.annotations.ForgeOverride;
|
import dan200.computercraft.annotations.ForgeOverride;
|
||||||
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
|
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@ -121,7 +122,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
var serverComputer = computer.createServerComputer();
|
var serverComputer = computer.createServerComputer();
|
||||||
serverComputer.turnOn();
|
serverComputer.turnOn();
|
||||||
|
|
||||||
new ComputerContainerData(serverComputer, getItem(computer)).open(player, computer);
|
PlatformHelper.get().openMenu(player, computer.getName(), computer, new ComputerContainerData(serverComputer, getItem(computer)));
|
||||||
}
|
}
|
||||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,10 @@ import net.minecraft.network.chat.Component;
|
|||||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.LockCode;
|
import net.minecraft.world.LockCode;
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.Nameable;
|
import net.minecraft.world.Nameable;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.inventory.MenuConstructor;
|
||||||
|
import net.minecraft.world.level.block.GameMasterBlock;
|
||||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
|
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
@ -40,7 +41,7 @@ import javax.annotation.Nullable;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class AbstractComputerBlockEntity extends BlockEntity implements Nameable, MenuProvider {
|
public abstract class AbstractComputerBlockEntity extends BlockEntity implements Nameable, MenuConstructor {
|
||||||
private static final String NBT_ID = "ComputerId";
|
private static final String NBT_ID = "ComputerId";
|
||||||
private static final String NBT_LABEL = "Label";
|
private static final String NBT_LABEL = "Label";
|
||||||
private static final String NBT_ON = "On";
|
private static final String NBT_ON = "On";
|
||||||
@ -344,6 +345,10 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isAdminOnly() {
|
||||||
|
return getBlockState().getBlock() instanceof GameMasterBlock;
|
||||||
|
}
|
||||||
|
|
||||||
public final void setComputerID(int id) {
|
public final void setComputerID(int id) {
|
||||||
if (getLevel().isClientSide || computerID == id) return;
|
if (getLevel().isClientSide || computerID == id) return;
|
||||||
|
|
||||||
@ -454,4 +459,9 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
public Component getDisplayName() {
|
public Component getDisplayName() {
|
||||||
return Nameable.super.getDisplayName();
|
return Nameable.super.getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onlyOpCanSetNbt() {
|
||||||
|
return isAdminOnly();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
|||||||
import dan200.computercraft.core.apis.ComputerAccess;
|
import dan200.computercraft.core.apis.ComputerAccess;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
import dan200.computercraft.core.computer.ApiLifecycle;
|
import dan200.computercraft.core.computer.ApiLifecycle;
|
||||||
import dan200.computercraft.shared.util.ComponentMap;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
|
||||||
@ -26,11 +25,11 @@ import java.util.Map;
|
|||||||
final class ComputerSystem extends ComputerAccess implements IComputerSystem, ApiLifecycle {
|
final class ComputerSystem extends ComputerAccess implements IComputerSystem, ApiLifecycle {
|
||||||
private final ServerComputer computer;
|
private final ServerComputer computer;
|
||||||
private final IAPIEnvironment environment;
|
private final IAPIEnvironment environment;
|
||||||
private final ComponentMap components;
|
private final Map<ComputerComponent<?>, Object> components;
|
||||||
|
|
||||||
private boolean active;
|
private boolean active;
|
||||||
|
|
||||||
ComputerSystem(ServerComputer computer, IAPIEnvironment environment, ComponentMap components) {
|
ComputerSystem(ServerComputer computer, IAPIEnvironment environment, Map<ComputerComponent<?>, Object> components) {
|
||||||
super(environment);
|
super(environment);
|
||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
@ -95,7 +94,8 @@ final class ComputerSystem extends ComputerAccess implements IComputerSystem, Ap
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public <T> @Nullable T getComponent(ComputerComponent<T> component) {
|
public <T> @Nullable T getComponent(ComputerComponent<T> component) {
|
||||||
return components.get(component);
|
return (T) components.get(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,18 +26,21 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
|||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage;
|
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage;
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||||
import dan200.computercraft.shared.util.ComponentMap;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ServerComputer implements ComputerEnvironment, ComputerEvents.Receiver {
|
public class ServerComputer implements ComputerEnvironment, ComputerEvents.Receiver {
|
||||||
|
public static final ComputerComponent<MetricsObserver> METRICS = ComputerComponent.create("computercraft", "metrics");
|
||||||
|
|
||||||
private final UUID instanceUUID = UUID.randomUUID();
|
private final UUID instanceUUID = UUID.randomUUID();
|
||||||
|
|
||||||
private ServerLevel level;
|
private ServerLevel level;
|
||||||
@ -65,14 +68,12 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
|
|||||||
|
|
||||||
storageCapacity = properties.storageCapacity;
|
storageCapacity = properties.storageCapacity;
|
||||||
|
|
||||||
var componentBuilder = ComponentMap.builder();
|
properties.addComponent(METRICS, metrics);
|
||||||
componentBuilder.add(ComponentMap.METRICS, metrics);
|
|
||||||
if (family == ComputerFamily.COMMAND) {
|
if (family == ComputerFamily.COMMAND) {
|
||||||
componentBuilder.add(ComputerComponents.ADMIN_COMPUTER, new AdminComputer() {
|
properties.addComponent(ComputerComponents.ADMIN_COMPUTER, new AdminComputer() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
componentBuilder.add(properties.components.build());
|
var components = Map.copyOf(properties.components);
|
||||||
var components = componentBuilder.build();
|
|
||||||
|
|
||||||
computer = new Computer(context.computerContext(), this, terminal, properties.computerID);
|
computer = new Computer(context.computerContext(), this, terminal, properties.computerID);
|
||||||
computer.setLabel(properties.label);
|
computer.setLabel(properties.label);
|
||||||
@ -282,7 +283,7 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
|
|||||||
private int terminalWidth = Config.DEFAULT_COMPUTER_TERM_WIDTH;
|
private int terminalWidth = Config.DEFAULT_COMPUTER_TERM_WIDTH;
|
||||||
private int terminalHeight = Config.DEFAULT_COMPUTER_TERM_HEIGHT;
|
private int terminalHeight = Config.DEFAULT_COMPUTER_TERM_HEIGHT;
|
||||||
private long storageCapacity = -1;
|
private long storageCapacity = -1;
|
||||||
private final ComponentMap.Builder components = ComponentMap.builder();
|
private final Map<ComputerComponent<?>, Object> components = new HashMap<>();
|
||||||
|
|
||||||
private Properties(int computerID, ComputerFamily family) {
|
private Properties(int computerID, ComputerFamily family) {
|
||||||
this.computerID = computerID;
|
this.computerID = computerID;
|
||||||
@ -313,7 +314,8 @@ public class ServerComputer implements ComputerEnvironment, ComputerEvents.Recei
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T> Properties addComponent(ComputerComponent<T> component, T value) {
|
public <T> Properties addComponent(ComputerComponent<T> component, T value) {
|
||||||
components.add(component, value);
|
if (components.containsKey(component)) throw new IllegalArgumentException(component + " is already set");
|
||||||
|
components.put(component, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,7 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
|||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
|
|
||||||
@ -20,16 +18,6 @@ import net.minecraft.world.inventory.MenuType;
|
|||||||
public interface ContainerData {
|
public interface ContainerData {
|
||||||
void toBytes(RegistryFriendlyByteBuf buf);
|
void toBytes(RegistryFriendlyByteBuf buf);
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a menu for a specific player using this data.
|
|
||||||
*
|
|
||||||
* @param player The player to open the menu for.
|
|
||||||
* @param menu The underlying menu provider.
|
|
||||||
*/
|
|
||||||
default void open(Player player, MenuProvider menu) {
|
|
||||||
PlatformHelper.get().openMenu(player, menu, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static <C extends AbstractContainerMenu, T extends ContainerData> MenuType<C> toType(StreamCodec<RegistryFriendlyByteBuf, T> codec, Factory<C, T> factory) {
|
static <C extends AbstractContainerMenu, T extends ContainerData> MenuType<C> toType(StreamCodec<RegistryFriendlyByteBuf, T> codec, Factory<C, T> factory) {
|
||||||
return PlatformHelper.get().createMenuType(codec, factory);
|
return PlatformHelper.get().createMenuType(codec, factory);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import net.minecraft.core.Direction;
|
|||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
@ -237,7 +238,7 @@ public class MonitorBlockEntity extends BlockEntity {
|
|||||||
getLevel().setBlock(getBlockPos(), getBlockState()
|
getLevel().setBlock(getBlockPos(), getBlockState()
|
||||||
.setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections(
|
.setValue(MonitorBlock.STATE, MonitorEdgeState.fromConnections(
|
||||||
yIndex < height - 1, yIndex > 0,
|
yIndex < height - 1, yIndex > 0,
|
||||||
xIndex > 0, xIndex < width - 1)), 2);
|
xIndex > 0, xIndex < width - 1)), Block.UPDATE_CLIENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Sizing and placement stuff
|
// region Sizing and placement stuff
|
||||||
|
@ -17,6 +17,7 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
@ -24,10 +25,14 @@ import net.minecraft.server.level.ServerPlayer;
|
|||||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.*;
|
import net.minecraft.world.Container;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.WorldlyContainer;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.MenuConstructor;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.CreativeModeTab;
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
import net.minecraft.world.item.DyeColor;
|
import net.minecraft.world.item.DyeColor;
|
||||||
@ -109,10 +114,11 @@ public interface PlatformHelper {
|
|||||||
* Open a container using a specific {@link ContainerData}.
|
* Open a container using a specific {@link ContainerData}.
|
||||||
*
|
*
|
||||||
* @param player The player to open the menu for.
|
* @param player The player to open the menu for.
|
||||||
* @param owner The underlying menu provider.
|
* @param title The title for this menu.
|
||||||
* @param menu The menu data.
|
* @param menu The underlying menu constructor.
|
||||||
|
* @param data The menu data.
|
||||||
*/
|
*/
|
||||||
void openMenu(Player player, MenuProvider owner, ContainerData menu);
|
void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate components on a block enitty.
|
* Invalidate components on a block enitty.
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.pocket.inventory;
|
|
||||||
|
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
|
||||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.world.InteractionHand;
|
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
|
||||||
|
|
||||||
public class PocketComputerMenuProvider implements MenuProvider {
|
|
||||||
private final ServerComputer computer;
|
|
||||||
private final Component name;
|
|
||||||
private final PocketComputerItem item;
|
|
||||||
private final InteractionHand hand;
|
|
||||||
private final boolean isTypingOnly;
|
|
||||||
|
|
||||||
public PocketComputerMenuProvider(ServerComputer computer, ItemStack stack, PocketComputerItem item, InteractionHand hand, boolean isTypingOnly) {
|
|
||||||
this.computer = computer;
|
|
||||||
name = stack.getHoverName();
|
|
||||||
this.item = item;
|
|
||||||
this.hand = hand;
|
|
||||||
this.isTypingOnly = isTypingOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getDisplayName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player entity) {
|
|
||||||
return new ComputerMenuWithoutInventory(
|
|
||||||
isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.COMPUTER.get(), id, inventory,
|
|
||||||
p -> {
|
|
||||||
var stack = p.getItemInHand(hand);
|
|
||||||
return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level().getServer()), stack) == computer;
|
|
||||||
},
|
|
||||||
computer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,13 +17,14 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
|
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||||
import dan200.computercraft.shared.computer.items.ServerComputerReference;
|
import dan200.computercraft.shared.computer.items.ServerComputerReference;
|
||||||
import dan200.computercraft.shared.config.ConfigSpec;
|
import dan200.computercraft.shared.config.ConfigSpec;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketBrain;
|
import dan200.computercraft.shared.pocket.core.PocketBrain;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketHolder;
|
import dan200.computercraft.shared.pocket.core.PocketHolder;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
|
|
||||||
import dan200.computercraft.shared.util.*;
|
import dan200.computercraft.shared.util.*;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
@ -147,8 +148,13 @@ public class PocketComputerItem extends Item implements IMedia {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!stop) {
|
if (!stop) {
|
||||||
var isTypingOnly = hand == InteractionHand.OFF_HAND;
|
PlatformHelper.get().openMenu(
|
||||||
new ComputerContainerData(computer, stack).open(player, new PocketComputerMenuProvider(computer, stack, this, hand, isTypingOnly));
|
player, stack.getHoverName(),
|
||||||
|
(id, inventory, entity) -> new ComputerMenuWithoutInventory(
|
||||||
|
hand == InteractionHand.OFF_HAND ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.COMPUTER.get(),
|
||||||
|
id, inventory, p -> isServerComputer(computer, p.getItemInHand(hand)), computer
|
||||||
|
),
|
||||||
|
new ComputerContainerData(computer, stack));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack);
|
return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack);
|
||||||
|
@ -40,6 +40,7 @@ import net.minecraft.world.entity.Entity;
|
|||||||
import net.minecraft.world.entity.MoverType;
|
import net.minecraft.world.entity.MoverType;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.material.PushReaction;
|
import net.minecraft.world.level.material.PushReaction;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
@ -256,7 +257,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
|||||||
try {
|
try {
|
||||||
// We use Block.UPDATE_CLIENTS here to ensure that neighbour updates caused in Block.updateNeighbourShapes
|
// We use Block.UPDATE_CLIENTS here to ensure that neighbour updates caused in Block.updateNeighbourShapes
|
||||||
// are sent to the client. We want to avoid doing a full block update until the turtle state is copied over.
|
// are sent to the client. We want to avoid doing a full block update until the turtle state is copied over.
|
||||||
if (world.setBlock(pos, newState, 2)) {
|
if (world.setBlock(pos, newState, Block.UPDATE_CLIENTS)) {
|
||||||
var block = world.getBlockState(pos).getBlock();
|
var block = world.getBlockState(pos).getBlock();
|
||||||
if (block == oldBlock.getBlock()) {
|
if (block == oldBlock.getBlock()) {
|
||||||
var newTile = world.getBlockEntity(pos);
|
var newTile = world.getBlockEntity(pos);
|
||||||
@ -669,7 +670,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var aabb = new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
var aabb = new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
var list = world.getEntitiesOfClass(Entity.class, aabb, TurtleBrain::canPush);
|
var list = world.getEntities((Entity) null, aabb, TurtleBrain::canPush);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
double pushStep = 1.0f / ANIM_DURATION;
|
double pushStep = 1.0f / ANIM_DURATION;
|
||||||
var pushStepX = moveDir.getStepX() * pushStep;
|
var pushStepX = moveDir.getStepX() * pushStep;
|
||||||
|
@ -22,11 +22,10 @@ public class TurtleDetectCommand implements TurtleCommand {
|
|||||||
var direction = this.direction.toWorldDir(turtle);
|
var direction = this.direction.toWorldDir(turtle);
|
||||||
|
|
||||||
// Check if thing in front is air or not
|
// Check if thing in front is air or not
|
||||||
var world = turtle.getLevel();
|
var level = turtle.getLevel();
|
||||||
var oldPosition = turtle.getPosition();
|
var pos = turtle.getPosition().relative(direction);
|
||||||
var newPosition = oldPosition.relative(direction);
|
|
||||||
|
|
||||||
return !WorldUtil.isLiquidBlock(world, newPosition) && !world.isEmptyBlock(newPosition)
|
return !WorldUtil.isEmptyBlock(level.getBlockState(pos))
|
||||||
? TurtleCommandResult.success()
|
? TurtleCommandResult.success()
|
||||||
: TurtleCommandResult.failure();
|
: TurtleCommandResult.failure();
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ import dan200.computercraft.shared.util.WorldUtil;
|
|||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.material.PushReaction;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.shapes.Shapes;
|
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
||||||
|
|
||||||
public class TurtleMoveCommand implements TurtleCommand {
|
public class TurtleMoveCommand implements TurtleCommand {
|
||||||
private final MoveDirection direction;
|
private final MoveDirection direction;
|
||||||
@ -30,57 +30,32 @@ public class TurtleMoveCommand implements TurtleCommand {
|
|||||||
var direction = this.direction.toWorldDir(turtle);
|
var direction = this.direction.toWorldDir(turtle);
|
||||||
|
|
||||||
// Check if we can move
|
// Check if we can move
|
||||||
var oldWorld = (ServerLevel) turtle.getLevel();
|
var level = (ServerLevel) turtle.getLevel();
|
||||||
var oldPosition = turtle.getPosition();
|
var oldPosition = turtle.getPosition();
|
||||||
var newPosition = oldPosition.relative(direction);
|
var newPosition = oldPosition.relative(direction);
|
||||||
|
|
||||||
var turtlePlayer = TurtlePlayer.getWithPosition(turtle, oldPosition, direction);
|
var turtlePlayer = TurtlePlayer.getWithPosition(turtle, oldPosition, direction);
|
||||||
var canEnterResult = canEnter(turtlePlayer, oldWorld, newPosition);
|
var canEnterResult = canEnter(turtlePlayer, level, newPosition);
|
||||||
if (!canEnterResult.isSuccess()) {
|
if (!canEnterResult.isSuccess()) return canEnterResult;
|
||||||
return canEnterResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check existing block is air or replaceable
|
// Check existing block is air or replaceable.
|
||||||
var state = oldWorld.getBlockState(newPosition);
|
var existingState = level.getBlockState(newPosition);
|
||||||
if (!oldWorld.isEmptyBlock(newPosition) &&
|
if (!(WorldUtil.isEmptyBlock(existingState) || existingState.canBeReplaced())) {
|
||||||
!WorldUtil.isLiquidBlock(oldWorld, newPosition) &&
|
|
||||||
!state.canBeReplaced()) {
|
|
||||||
return TurtleCommandResult.failure("Movement obstructed");
|
return TurtleCommandResult.failure("Movement obstructed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check there isn't anything in the way
|
// Check there isn't an entity in the way.
|
||||||
var collision = state.getCollisionShape(oldWorld, oldPosition).move(
|
var turtleShape = level.getBlockState(oldPosition).getCollisionShape(level, oldPosition)
|
||||||
newPosition.getX(),
|
.move(newPosition.getX(), newPosition.getY(), newPosition.getZ());
|
||||||
newPosition.getY(),
|
if (!level.isUnobstructed(null, turtleShape) && !canPushEntities(level, turtleShape.bounds())) {
|
||||||
newPosition.getZ()
|
return TurtleCommandResult.failure("Movement obstructed");
|
||||||
);
|
|
||||||
|
|
||||||
if (!oldWorld.isUnobstructed(null, collision)) {
|
|
||||||
if (!Config.turtlesCanPush || this.direction == MoveDirection.UP || this.direction == MoveDirection.DOWN) {
|
|
||||||
return TurtleCommandResult.failure("Movement obstructed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check there is space for all the pushable entities to be pushed
|
|
||||||
var list = oldWorld.getEntitiesOfClass(Entity.class, getBox(collision), x -> x != null && x.isAlive() && x.blocksBuilding);
|
|
||||||
for (var entity : list) {
|
|
||||||
var pushedBB = entity.getBoundingBox().move(
|
|
||||||
direction.getStepX(),
|
|
||||||
direction.getStepY(),
|
|
||||||
direction.getStepZ()
|
|
||||||
);
|
|
||||||
if (!oldWorld.isUnobstructed(null, Shapes.create(pushedBB))) {
|
|
||||||
return TurtleCommandResult.failure("Movement obstructed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check fuel level
|
// Check fuel level
|
||||||
if (turtle.isFuelNeeded() && turtle.getFuelLevel() < 1) {
|
if (turtle.isFuelNeeded() && turtle.getFuelLevel() < 1) return TurtleCommandResult.failure("Out of fuel");
|
||||||
return TurtleCommandResult.failure("Out of fuel");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
if (!turtle.teleportTo(oldWorld, newPosition)) return TurtleCommandResult.failure("Movement failed");
|
if (!turtle.teleportTo(level, newPosition)) return TurtleCommandResult.failure("Movement failed");
|
||||||
|
|
||||||
// Consume fuel
|
// Consume fuel
|
||||||
turtle.consumeFuel(1);
|
turtle.consumeFuel(1);
|
||||||
@ -114,9 +89,20 @@ public class TurtleMoveCommand implements TurtleCommand {
|
|||||||
return TurtleCommandResult.success();
|
return TurtleCommandResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AABB getBox(VoxelShape shape) {
|
|
||||||
return shape.isEmpty() ? EMPTY_BOX : shape.bounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final AABB EMPTY_BOX = new AABB(0, 0, 0, 0, 0, 0);
|
/**
|
||||||
|
* Determine if all entities in the given bounds can be pushed by the turtle.
|
||||||
|
*
|
||||||
|
* @param level The current level.
|
||||||
|
* @param bounds The bounding box.
|
||||||
|
* @return Whether all entities can be pushed.
|
||||||
|
*/
|
||||||
|
private boolean canPushEntities(Level level, AABB bounds) {
|
||||||
|
if (!Config.turtlesCanPush) return false;
|
||||||
|
|
||||||
|
// Check there is space for all the pushable entities to be pushed
|
||||||
|
return level.getEntities((Entity) null, bounds, e -> e.isAlive()
|
||||||
|
&& !e.isSpectator() && e.blocksBuilding && e.getPistonPushReaction() == PushReaction.IGNORE
|
||||||
|
).isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.util;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.component.ComputerComponent;
|
|
||||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An immutable map of components.
|
|
||||||
*/
|
|
||||||
public final class ComponentMap {
|
|
||||||
public static final ComputerComponent<MetricsObserver> METRICS = ComputerComponent.create("computercraft", "metrics");
|
|
||||||
|
|
||||||
private static final ComponentMap EMPTY = new ComponentMap(Map.of());
|
|
||||||
|
|
||||||
private final Map<ComputerComponent<?>, Object> components;
|
|
||||||
|
|
||||||
private ComponentMap(Map<ComputerComponent<?>, Object> components) {
|
|
||||||
this.components = components;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> @Nullable T get(ComputerComponent<T> component) {
|
|
||||||
return (T) components.get(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ComponentMap empty() {
|
|
||||||
return EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Builder builder() {
|
|
||||||
return new Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Builder {
|
|
||||||
private final Map<ComputerComponent<?>, Object> components = new HashMap<>();
|
|
||||||
|
|
||||||
private Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> Builder add(ComputerComponent<T> component, T value) {
|
|
||||||
addImpl(component, value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder add(ComponentMap components) {
|
|
||||||
for (var component : components.components.entrySet()) addImpl(component.getKey(), component.getValue());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addImpl(ComputerComponent<?> component, Object value) {
|
|
||||||
if (components.containsKey(component)) throw new IllegalArgumentException(component + " is already set");
|
|
||||||
components.put(component, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComponentMap build() {
|
|
||||||
return new ComponentMap(Map.copyOf(components));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,9 +28,14 @@ import net.minecraft.world.phys.shapes.VoxelShape;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class WorldUtil {
|
public final class WorldUtil {
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public static boolean isLiquidBlock(Level world, BlockPos pos) {
|
public static boolean isLiquidBlock(Level world, BlockPos pos) {
|
||||||
if (!world.isInWorldBounds(pos)) return false;
|
return world.isInWorldBounds(pos) && world.getBlockState(pos).liquid();
|
||||||
return world.getBlockState(pos).liquid();
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static boolean isEmptyBlock(BlockState state) {
|
||||||
|
return state.isAir() || state.liquid();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isVecInside(VoxelShape shape, Vec3 vec) {
|
public static boolean isVecInside(VoxelShape shape, Vec3 vec) {
|
||||||
|
@ -18,6 +18,7 @@ import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
|||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
@ -26,10 +27,10 @@ import net.minecraft.server.level.ServerPlayer;
|
|||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.MenuConstructor;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.CreativeModeTab;
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
@ -77,7 +78,7 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
|
public void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data) {
|
||||||
throw new UnsupportedOperationException("Cannot open menu inside tests");
|
throw new UnsupportedOperationException("Cannot open menu inside tests");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.gametest
|
||||||
|
|
||||||
|
import dan200.computercraft.gametest.api.assertNoPeripheral
|
||||||
|
import dan200.computercraft.gametest.api.assertPeripheral
|
||||||
|
import dan200.computercraft.gametest.api.immediate
|
||||||
|
import dan200.computercraft.shared.ModRegistry
|
||||||
|
import dan200.computercraft.shared.platform.ComponentAccess
|
||||||
|
import net.minecraft.core.BlockPos
|
||||||
|
import net.minecraft.core.Direction
|
||||||
|
import net.minecraft.gametest.framework.GameTest
|
||||||
|
import net.minecraft.gametest.framework.GameTestHelper
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that we expose [ComponentAccess] for various blocks/block entities
|
||||||
|
*/
|
||||||
|
class Component_Test {
|
||||||
|
@GameTest(template = "default")
|
||||||
|
fun Peripheral(context: GameTestHelper) = context.immediate {
|
||||||
|
val pos = BlockPos(2, 2, 2)
|
||||||
|
// We fetch peripherals from the NORTH, as that is the default direction for modems. This is a bit of a hack,
|
||||||
|
// but avoids having to override the block state.
|
||||||
|
val side = Direction.NORTH
|
||||||
|
|
||||||
|
for ((block, type) in mapOf(
|
||||||
|
// Computers
|
||||||
|
ModRegistry.Blocks.COMPUTER_NORMAL to Optional.of("computer"),
|
||||||
|
ModRegistry.Blocks.COMPUTER_ADVANCED to Optional.of("computer"),
|
||||||
|
ModRegistry.Blocks.COMPUTER_COMMAND to Optional.empty(),
|
||||||
|
// Turtles
|
||||||
|
ModRegistry.Blocks.TURTLE_NORMAL to Optional.of("turtle"),
|
||||||
|
ModRegistry.Blocks.TURTLE_ADVANCED to Optional.of("turtle"),
|
||||||
|
// Peripherals
|
||||||
|
ModRegistry.Blocks.SPEAKER to Optional.of("speaker"),
|
||||||
|
ModRegistry.Blocks.DISK_DRIVE to Optional.of("drive"),
|
||||||
|
ModRegistry.Blocks.PRINTER to Optional.of("printer"),
|
||||||
|
ModRegistry.Blocks.MONITOR_NORMAL to Optional.of("monitor"),
|
||||||
|
ModRegistry.Blocks.MONITOR_ADVANCED to Optional.of("monitor"),
|
||||||
|
ModRegistry.Blocks.WIRELESS_MODEM_NORMAL to Optional.of("modem"),
|
||||||
|
ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED to Optional.of("modem"),
|
||||||
|
ModRegistry.Blocks.WIRED_MODEM_FULL to Optional.of("modem"),
|
||||||
|
ModRegistry.Blocks.REDSTONE_RELAY to Optional.of("redstone_relay"),
|
||||||
|
)) {
|
||||||
|
context.setBlock(pos, block.get())
|
||||||
|
if (type.isPresent) {
|
||||||
|
context.assertPeripheral(pos, side, type.get())
|
||||||
|
} else {
|
||||||
|
context.assertNoPeripheral(pos, side)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -95,17 +95,6 @@ class Computer_Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check computers and turtles expose peripherals.
|
|
||||||
*/
|
|
||||||
@GameTest
|
|
||||||
fun Computer_peripheral(context: GameTestHelper) = context.sequence {
|
|
||||||
thenExecute {
|
|
||||||
context.assertPeripheral(BlockPos(3, 2, 2), type = "computer")
|
|
||||||
context.assertPeripheral(BlockPos(1, 2, 2), type = "turtle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check chest peripherals are reattached with a new size.
|
* Check chest peripherals are reattached with a new size.
|
||||||
*/
|
*/
|
||||||
|
@ -658,6 +658,27 @@ class Turtle_Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test turtles can push entities.
|
||||||
|
*/
|
||||||
|
@GameTest
|
||||||
|
fun Move_push_entity(helper: GameTestHelper) = helper.sequence {
|
||||||
|
thenOnComputer { turtle.up().await().assertArrayEquals(true) }
|
||||||
|
thenIdle(9)
|
||||||
|
thenExecute {
|
||||||
|
// The turtle has moved up
|
||||||
|
helper.assertBlockPresent(ModRegistry.Blocks.TURTLE_NORMAL.get(), BlockPos(2, 3, 2))
|
||||||
|
|
||||||
|
// As has the villager
|
||||||
|
val pos = BlockPos(2, 4, 2)
|
||||||
|
helper.assertEntityPresent(EntityType.VILLAGER, pos)
|
||||||
|
|
||||||
|
val villager = helper.getEntity(EntityType.VILLAGER)
|
||||||
|
val expectedY = helper.absolutePos(pos).y - 0.125
|
||||||
|
if (villager.y < expectedY) helper.fail("Expected villager at y>=$expectedY, but at ${villager.y}", pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test a turtle can attack an entity and capture its drops.
|
* Test a turtle can attack an entity and capture its drops.
|
||||||
*/
|
*/
|
||||||
|
@ -128,6 +128,14 @@ fun GameTestHelper.sequence(run: GameTestSequence.() -> Unit) {
|
|||||||
sequence.thenSucceed()
|
sequence.thenSucceed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a function immediately, and then succeed.
|
||||||
|
*/
|
||||||
|
fun GameTestHelper.immediate(run: () -> Unit) {
|
||||||
|
run()
|
||||||
|
succeed()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom instance of [GameTestAssertPosException] which allows for longer error messages.
|
* A custom instance of [GameTestAssertPosException] which allows for longer error messages.
|
||||||
*/
|
*/
|
||||||
@ -237,15 +245,17 @@ private fun GameTestHelper.getPeripheralAt(pos: BlockPos, direction: Direction):
|
|||||||
|
|
||||||
fun GameTestHelper.assertPeripheral(pos: BlockPos, direction: Direction = Direction.UP, type: String) {
|
fun GameTestHelper.assertPeripheral(pos: BlockPos, direction: Direction = Direction.UP, type: String) {
|
||||||
val peripheral = getPeripheralAt(pos, direction)
|
val peripheral = getPeripheralAt(pos, direction)
|
||||||
|
val block = getBlockState(pos).block.name.string
|
||||||
when {
|
when {
|
||||||
peripheral == null -> fail("No peripheral at position", pos)
|
peripheral == null -> fail("No peripheral for '$block'", pos)
|
||||||
peripheral.type != type -> fail("Peripheral is of type ${peripheral.type}, expected $type", pos)
|
peripheral.type != type -> fail("Peripheral for '$block' is of type ${peripheral.type}, expected $type", pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun GameTestHelper.assertNoPeripheral(pos: BlockPos, direction: Direction = Direction.UP) {
|
fun GameTestHelper.assertNoPeripheral(pos: BlockPos, direction: Direction = Direction.UP) {
|
||||||
val peripheral = getPeripheralAt(pos, direction)
|
val peripheral = getPeripheralAt(pos, direction)
|
||||||
if (peripheral != null) fail("Expected no peripheral, got a ${peripheral.type}", pos)
|
val block = getBlockState(pos).block.name
|
||||||
|
if (peripheral != null) fail("Expected no peripheral for '$block', got a ${peripheral.type}", pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: String? = null) {
|
fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: String? = null) {
|
||||||
|
@ -36,6 +36,10 @@ object ManagedComputers : ILuaMachine.Factory {
|
|||||||
private val LOGGER = LoggerFactory.getLogger(ManagedComputers::class.java)
|
private val LOGGER = LoggerFactory.getLogger(ManagedComputers::class.java)
|
||||||
private val computers: MutableMap<String, Queue<suspend LuaTaskContext.() -> Unit>> = mutableMapOf()
|
private val computers: MutableMap<String, Queue<suspend LuaTaskContext.() -> Unit>> = mutableMapOf()
|
||||||
|
|
||||||
|
internal fun reset() {
|
||||||
|
computers.clear()
|
||||||
|
}
|
||||||
|
|
||||||
internal fun enqueue(test: GameTestInfo, label: String, task: suspend LuaTaskContext.() -> Unit): Monitor {
|
internal fun enqueue(test: GameTestInfo, label: String, task: suspend LuaTaskContext.() -> Unit): Monitor {
|
||||||
val monitor = Monitor(test, label)
|
val monitor = Monitor(test, label)
|
||||||
computers.computeIfAbsent(label) { ConcurrentLinkedDeque() }.add {
|
computers.computeIfAbsent(label) { ConcurrentLinkedDeque() }.add {
|
||||||
|
@ -84,6 +84,8 @@ object TestHooks {
|
|||||||
StructureUtils.clearSpaceForStructure(StructureUtils.getStructureBoundingBox(structure), level)
|
StructureUtils.clearSpaceForStructure(StructureUtils.getStructureBoundingBox(structure), level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ManagedComputers.reset()
|
||||||
|
|
||||||
// Delete server context and add one with a mutable machine factory. This allows us to set the factory for
|
// Delete server context and add one with a mutable machine factory. This allows us to set the factory for
|
||||||
// specific test batches without having to reset all computers.
|
// specific test batches without having to reset all computers.
|
||||||
for (computer in ServerContext.get(server).registry().computers) {
|
for (computer in ServerContext.get(server).registry().computers) {
|
||||||
@ -99,6 +101,7 @@ object TestHooks {
|
|||||||
fun areComputersIdle(server: MinecraftServer) = ComputerThreadReflection.isFullyIdle(ServerContext.get(server))
|
fun areComputersIdle(server: MinecraftServer) = ComputerThreadReflection.isFullyIdle(ServerContext.get(server))
|
||||||
|
|
||||||
private val testClasses = listOf(
|
private val testClasses = listOf(
|
||||||
|
Component_Test::class.java,
|
||||||
Computer_Test::class.java,
|
Computer_Test::class.java,
|
||||||
CraftOs_Test::class.java,
|
CraftOs_Test::class.java,
|
||||||
Details_Test::class.java,
|
Details_Test::class.java,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
DataVersion: 2975,
|
DataVersion: 3465,
|
||||||
size: [5, 5, 5],
|
size: [5, 5, 5],
|
||||||
data: [
|
data: [
|
||||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
@ -33,19 +33,19 @@
|
|||||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
{pos: [1, 1, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 1, 2], state: "computercraft:turtle_advanced{facing:south,waterlogged:false}", nbt: {Fuel: 0, Items: [], On: 0b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_advanced"}},
|
{pos: [1, 1, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
{pos: [1, 1, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
{pos: [2, 1, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 1, 2], state: "minecraft:air"},
|
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:west,waterlogged:false}", nbt: {ComputerId: 1, Label: "turtle_test.move_push_entity", Fuel: 80, Items: [], On: 1b, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
{pos: [2, 1, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
{pos: [3, 1, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 1, 2], state: "computercraft:computer_advanced{facing:north,state:off}", nbt: {On: 0b, id: "computercraft:computer_advanced"}},
|
{pos: [3, 1, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
{pos: [3, 1, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||||
@ -58,19 +58,19 @@
|
|||||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
{pos: [1, 2, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
{pos: [1, 2, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
{pos: [1, 2, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
{pos: [2, 2, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
{pos: [2, 2, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
{pos: [3, 2, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
{pos: [3, 2, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
{pos: [3, 2, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||||
@ -83,19 +83,19 @@
|
|||||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
{pos: [1, 3, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
{pos: [1, 3, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
{pos: [1, 3, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
{pos: [2, 3, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
{pos: [2, 3, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
{pos: [3, 3, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
{pos: [3, 3, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
{pos: [3, 3, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||||
@ -108,19 +108,19 @@
|
|||||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
{pos: [1, 4, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
{pos: [1, 4, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
{pos: [1, 4, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
{pos: [2, 4, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
{pos: [2, 4, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
{pos: [3, 4, 1], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
{pos: [3, 4, 2], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
{pos: [3, 4, 3], state: "minecraft:white_stained_glass"},
|
||||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||||
@ -128,11 +128,13 @@
|
|||||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||||
],
|
],
|
||||||
entities: [],
|
entities: [
|
||||||
|
{blockPos: [2, 1, 2], pos: [2.5d, 1.875d, 2.5d], nbt: {AbsorptionAmount: 0.0f, Age: 0, Air: 300s, ArmorDropChances: [0.085f, 0.085f, 0.085f, 0.085f], ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.5d, Name: "minecraft:generic.movement_speed"}, {Base: 48.0d, Modifiers: [{Amount: -0.01165046535152748d, Name: "Random spawn bonus", Operation: 1, UUID: [I; 1412502412, 1522745411, -1211155694, 2103054347]}], Name: "minecraft:generic.follow_range"}], Brain: {memories: {}}, CanPickUpLoot: 1b, DeathTime: 0s, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, FoodLevel: 0b, ForcedAge: 0, Gossips: [], HandDropChances: [0.085f, 0.085f], HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Inventory: [], Invulnerable: 0b, LastGossipDecay: 52357L, LastRestock: 0L, LeftHanded: 0b, Motion: [0.0d, -0.0784000015258789d, 0.0d], OnGround: 1b, PersistenceRequired: 0b, PortalCooldown: 0, Pos: [-33.5d, 58.875d, -21.5d], RestocksToday: 0, Rotation: [-102.704926f, 0.0f], UUID: [I; 164071932, -867285780, -1817215456, -2129864016], VillagerData: {level: 1, profession: "minecraft:none", type: "minecraft:desert"}, Xp: 0, id: "minecraft:villager"}}
|
||||||
|
],
|
||||||
palette: [
|
palette: [
|
||||||
"minecraft:polished_andesite",
|
"minecraft:polished_andesite",
|
||||||
|
"minecraft:white_stained_glass",
|
||||||
"minecraft:air",
|
"minecraft:air",
|
||||||
"computercraft:turtle_advanced{facing:south,waterlogged:false}",
|
"computercraft:turtle_normal{facing:west,waterlogged:false}"
|
||||||
"computercraft:computer_advanced{facing:north,state:off}"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -47,12 +47,22 @@ local function sortCoords(startX, startY, endX, endY)
|
|||||||
return minX, maxX, minY, maxY
|
return minX, maxX, minY, maxY
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Parses an image from a multi-line string
|
--[=[- Parses an image from a multi-line string
|
||||||
--
|
|
||||||
-- @tparam string image The string containing the raw-image data.
|
@tparam string image The string containing the raw-image data.
|
||||||
-- @treturn table The parsed image data, suitable for use with
|
@treturn table The parsed image data, suitable for use with [`paintutils.drawImage`].
|
||||||
-- [`paintutils.drawImage`].
|
@usage Parse an image from a string, and draw it.
|
||||||
-- @since 1.80pr1
|
|
||||||
|
local image = paintutils.parseImage([[
|
||||||
|
e e
|
||||||
|
|
||||||
|
e e
|
||||||
|
eeee
|
||||||
|
]])
|
||||||
|
paintutils.drawImage(image, term.getCursorPos())
|
||||||
|
|
||||||
|
@since 1.80pr1
|
||||||
|
]=]
|
||||||
function parseImage(image)
|
function parseImage(image)
|
||||||
expect(1, image, "string")
|
expect(1, image, "string")
|
||||||
local tImage = {}
|
local tImage = {}
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
# New features in CC: Tweaked 1.114.4
|
||||||
|
|
||||||
|
* Allow typing/pasting any character in the CC charset.
|
||||||
|
* Add a new `computercraft:storage_capacity` to override a disk or computer's capacity.
|
||||||
|
|
||||||
|
Several bug fixes:
|
||||||
|
* Fix command computers having NBT set when placed in a Create contraption.
|
||||||
|
* Use correct bounding box when checking for entities in turtle movement.
|
||||||
|
|
||||||
# New features in CC: Tweaked 1.114.3
|
# New features in CC: Tweaked 1.114.3
|
||||||
|
|
||||||
* `wget` now prints the error that occurred, rather than a generic "Failed" (tizu69).
|
* `wget` now prints the error that occurred, rather than a generic "Failed" (tizu69).
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
New features in CC: Tweaked 1.114.3
|
New features in CC: Tweaked 1.114.4
|
||||||
|
|
||||||
* `wget` now prints the error that occurred, rather than a generic "Failed" (tizu69).
|
* Allow typing/pasting any character in the CC charset.
|
||||||
* Update several translations.
|
* Add a new `computercraft:storage_capacity` to override a disk or computer's capacity.
|
||||||
|
|
||||||
Several bug fixes:
|
Several bug fixes:
|
||||||
* Fix `fs.isDriveRoot` returning true for non-existent files.
|
* Fix command computers having NBT set when placed in a Create contraption.
|
||||||
* Fix possible memory leak when sending terminal contents.
|
* Use correct bounding box when checking for entities in turtle movement.
|
||||||
|
|
||||||
Type "help changelog" to see the full version history.
|
Type "help changelog" to see the full version history.
|
||||||
|
@ -47,11 +47,11 @@ import net.minecraft.tags.TagKey;
|
|||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.MenuConstructor;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.*;
|
import net.minecraft.world.item.*;
|
||||||
import net.minecraft.world.item.crafting.Ingredient;
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
@ -105,8 +105,8 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
|
public void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data) {
|
||||||
player.openMenu(new WrappedMenuProvider<>(owner, menu));
|
player.openMenu(new WrappedMenuProvider<>(title, menu, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -277,22 +277,22 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private record WrappedMenuProvider<T extends ContainerData>(
|
private record WrappedMenuProvider<T extends ContainerData>(
|
||||||
MenuProvider owner, T menu
|
Component title, MenuConstructor menu, T data
|
||||||
) implements ExtendedScreenHandlerFactory<T> {
|
) implements ExtendedScreenHandlerFactory<T> {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {
|
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {
|
||||||
return owner.createMenu(id, inventory, player);
|
return menu.createMenu(id, inventory, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getDisplayName() {
|
public Component getDisplayName() {
|
||||||
return owner.getDisplayName();
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T getScreenOpeningData(ServerPlayer player) {
|
public T getScreenOpeningData(ServerPlayer player) {
|
||||||
return menu;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,19 +23,18 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.*;
|
||||||
import net.minecraft.world.InteractionHand;
|
|
||||||
import net.minecraft.world.InteractionResult;
|
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.MenuConstructor;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.minecraft.world.item.CreativeModeTab;
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
@ -95,8 +94,8 @@ public class PlatformHelperImpl implements PlatformHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openMenu(Player player, MenuProvider owner, ContainerData menu) {
|
public void openMenu(Player player, Component title, MenuConstructor menu, ContainerData data) {
|
||||||
player.openMenu(owner, menu::toBytes);
|
player.openMenu(new SimpleMenuProvider(menu, title), data::toBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user