mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-11-13 05:19:59 +00:00
Merge branch 'mc-1.20.x' into mc-1.20.x-speaker-sound
This commit is contained in:
commit
f9c9b6b119
12
.github/workflows/main-ci.yml
vendored
12
.github/workflows/main-ci.yml
vendored
@ -9,16 +9,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 📥 Clone repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 📥 Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: 📥 Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
@ -82,16 +82,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
|
6
.github/workflows/make-doc.yml
vendored
6
.github/workflows/make-doc.yml
vendored
@ -13,16 +13,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
|
@ -40,10 +40,6 @@ repositories {
|
||||
|
||||
val mainMaven = maven("https://squiddev.cc/maven") {
|
||||
name = "SquidDev"
|
||||
content {
|
||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
||||
includeModule("org.spongepowered", "mixin")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
@ -113,6 +109,8 @@ sourceSets.all {
|
||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||
option("NullAway:CheckOptionalEmptiness")
|
||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||
|
||||
excludedPaths = ".*/jmh_generated/.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<property name="tabWidth" value="4"/>
|
||||
<property name="charset" value="UTF-8" />
|
||||
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||
</module>
|
||||
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
|
@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.109.7
|
||||
modVersion=1.110.1
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.20.1
|
||||
|
@ -26,7 +26,7 @@ slf4j = "2.0.1"
|
||||
asm = "9.6"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.42.0"
|
||||
cobalt = "0.9.1"
|
||||
cobalt = "0.9.2"
|
||||
commonsCli = "1.6.0"
|
||||
jetbrainsAnnotations = "24.1.0"
|
||||
jsr305 = "3.0.2"
|
||||
@ -55,7 +55,7 @@ jmh = "1.37"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.8.2"
|
||||
checkstyle = "10.12.6"
|
||||
checkstyle = "10.14.1"
|
||||
curseForgeGradle = "1.0.14"
|
||||
errorProne-core = "2.23.0"
|
||||
errorProne-plugin = "3.1.0"
|
||||
@ -64,16 +64,15 @@ forgeGradle = "6.0.20"
|
||||
githubRelease = "2.5.2"
|
||||
gradleVersions = "0.50.0"
|
||||
ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-69-gf294ab2"
|
||||
illuaminate = "0.1.0-71-g378d86e"
|
||||
librarian = "1.+"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.+"
|
||||
mixinGradle = "0.7.38"
|
||||
nullAway = "0.9.9"
|
||||
spotless = "6.23.3"
|
||||
taskTree = "2.1.1"
|
||||
teavm = "0.10.0-SQUID.3"
|
||||
vanillaExtract = "0.1.1"
|
||||
vanillaExtract = "0.1.2"
|
||||
versionCatalogUpdate = "0.8.1"
|
||||
|
||||
[libraries]
|
||||
@ -171,7 +170,6 @@ githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "g
|
||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
@ -29,7 +29,7 @@ public final class Services {
|
||||
* @throws IllegalStateException When the service cannot be loaded.
|
||||
*/
|
||||
public static <T> T load(Class<T> klass) {
|
||||
var services = ServiceLoader.load(klass).stream().toList();
|
||||
var services = ServiceLoader.load(klass, klass.getClassLoader()).stream().toList();
|
||||
return switch (services.size()) {
|
||||
case 1 -> services.get(0).get();
|
||||
case 0 -> throw new IllegalStateException("Cannot find service for " + klass.getName());
|
||||
|
@ -22,6 +22,7 @@ import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||
@ -77,8 +78,10 @@ public final class ClientRegistry {
|
||||
|
||||
/**
|
||||
* Register any client-side objects which must be done on the main thread.
|
||||
*
|
||||
* @param itemProperties Callback to register item properties.
|
||||
*/
|
||||
public static void registerMainThread() {
|
||||
public static void registerMainThread(RegisterItemProperty itemProperties) {
|
||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
||||
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
||||
@ -90,11 +93,14 @@ public final class ClientRegistry {
|
||||
|
||||
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
||||
|
||||
registerItemProperty("state",
|
||||
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
|
||||
registerItemProperty(itemProperties, "state",
|
||||
new UnclampedPropertyFunction((stack, world, player, random) -> {
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
return (computer == null ? ComputerState.OFF : computer.getState()).ordinal();
|
||||
}),
|
||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
registerItemProperty("coloured",
|
||||
registerItemProperty(itemProperties, "coloured",
|
||||
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
@ -115,9 +121,17 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
private static void registerItemProperty(RegisterItemProperty itemProperties, String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
||||
for (var item : items) itemProperties.register(item.get(), id, getter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an item property via {@link ItemProperties#register}. Forge and Fabric expose different methods, so we
|
||||
* supply this via mod-loader-specific code.
|
||||
*/
|
||||
public interface RegisterItemProperty {
|
||||
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
|
||||
}
|
||||
|
||||
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
||||
@ -155,17 +169,14 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
private static int getPocketColour(ItemStack stack, int layer) {
|
||||
switch (layer) {
|
||||
case 0:
|
||||
default:
|
||||
return 0xFFFFFF;
|
||||
case 1: // Frame colour
|
||||
return IColouredItem.getColourBasic(stack);
|
||||
case 2: { // Light colour
|
||||
var light = ClientPocketComputers.get(stack).getLightState();
|
||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
||||
return switch (layer) {
|
||||
default -> 0xFFFFFF;
|
||||
case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
|
||||
case 2 -> { // Light colour
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static int getTurtleColour(ItemStack stack, int layer) {
|
||||
|
@ -17,6 +17,7 @@ import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@ -27,7 +28,6 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -67,19 +67,17 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
||||
computer.setState(state, lightState);
|
||||
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
||||
public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||
ClientPocketComputers.setState(instanceId, state, lightState, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePocketComputerDeleted(int instanceId) {
|
||||
public void handlePocketComputerDeleted(UUID instanceId) {
|
||||
ClientPocketComputers.remove(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
|
||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio buffer) {
|
||||
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
|
||||
}
|
||||
|
||||
|
@ -4,21 +4,25 @@
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Maps {@link ServerComputer#getInstanceID()} to locals {@link PocketComputerData}.
|
||||
* Maps {@link ServerComputer#getInstanceUUID()} to locals {@link PocketComputerData}.
|
||||
* <p>
|
||||
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
|
||||
*/
|
||||
public final class ClientPocketComputers {
|
||||
private static final Int2ObjectMap<PocketComputerData> instances = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<UUID, PocketComputerData> instances = new HashMap<>();
|
||||
|
||||
private ClientPocketComputers() {
|
||||
}
|
||||
@ -27,25 +31,29 @@ public final class ClientPocketComputers {
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
public static void remove(int id) {
|
||||
public static void remove(UUID id) {
|
||||
instances.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a pocket computer.
|
||||
* Set the state of a pocket computer.
|
||||
*
|
||||
* @param instanceId The instance ID of the pocket computer.
|
||||
* @param advanced Whether this computer has an advanced terminal.
|
||||
* @return The pocket computer data.
|
||||
* @param instanceId The instance ID of the pocket computer.
|
||||
* @param state The computer state of the pocket computer.
|
||||
* @param lightColour The current colour of the modem light.
|
||||
* @param terminalData The current terminal contents.
|
||||
*/
|
||||
public static PocketComputerData get(int instanceId, boolean advanced) {
|
||||
public static void setState(UUID instanceId, ComputerState state, int lightColour, TerminalState terminalData) {
|
||||
var computer = instances.get(instanceId);
|
||||
if (computer == null) instances.put(instanceId, computer = new PocketComputerData(advanced));
|
||||
return computer;
|
||||
if (computer == null) {
|
||||
instances.put(instanceId, new PocketComputerData(state, lightColour, terminalData));
|
||||
} else {
|
||||
computer.setState(state, lightColour, terminalData);
|
||||
}
|
||||
}
|
||||
|
||||
public static PocketComputerData get(ItemStack stack) {
|
||||
var family = stack.getItem() instanceof PocketComputerItem computer ? computer.getFamily() : ComputerFamily.NORMAL;
|
||||
return get(PocketComputerItem.getInstanceID(stack), family != ComputerFamily.NORMAL);
|
||||
public static @Nullable PocketComputerData get(ItemStack stack) {
|
||||
var id = PocketComputerItem.getInstanceID(stack);
|
||||
return id == null ? null : instances.get(id);
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Clientside data about a pocket computer.
|
||||
* <p>
|
||||
@ -21,20 +21,22 @@ import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
* @see ClientPocketComputers The registry which holds pocket computers.
|
||||
* @see PocketServerComputer The server-side pocket computer.
|
||||
*/
|
||||
public class PocketComputerData {
|
||||
private final NetworkedTerminal terminal;
|
||||
private ComputerState state = ComputerState.OFF;
|
||||
private int lightColour = -1;
|
||||
public final class PocketComputerData {
|
||||
private @Nullable NetworkedTerminal terminal;
|
||||
private ComputerState state;
|
||||
private int lightColour;
|
||||
|
||||
public PocketComputerData(boolean colour) {
|
||||
terminal = new NetworkedTerminal(Config.pocketTermWidth, Config.pocketTermHeight, colour);
|
||||
PocketComputerData(ComputerState state, int lightColour, TerminalState terminalData) {
|
||||
this.state = state;
|
||||
this.lightColour = lightColour;
|
||||
if (terminalData.hasTerminal()) terminal = terminalData.create();
|
||||
}
|
||||
|
||||
public int getLightState() {
|
||||
return state != ComputerState.OFF ? lightColour : -1;
|
||||
}
|
||||
|
||||
public Terminal getTerminal() {
|
||||
public @Nullable NetworkedTerminal getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
@ -42,12 +44,16 @@ public class PocketComputerData {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(ComputerState state, int lightColour) {
|
||||
void setState(ComputerState state, int lightColour, TerminalState terminalData) {
|
||||
this.state = state;
|
||||
this.lightColour = lightColour;
|
||||
}
|
||||
|
||||
public void setTerminal(TerminalState state) {
|
||||
state.apply(terminal);
|
||||
if (terminalData.hasTerminal()) {
|
||||
if (terminal == null) {
|
||||
terminal = terminalData.create();
|
||||
} else {
|
||||
terminalData.apply(terminal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@ -32,10 +33,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
@Override
|
||||
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
var terminal = computer.getTerminal();
|
||||
var terminal = computer == null ? null : computer.getTerminal();
|
||||
|
||||
var termWidth = terminal.getWidth();
|
||||
var termHeight = terminal.getHeight();
|
||||
int termWidth, termHeight;
|
||||
if (terminal == null) {
|
||||
termWidth = Config.pocketTermWidth;
|
||||
termHeight = Config.pocketTermHeight;
|
||||
} else {
|
||||
termWidth = terminal.getWidth();
|
||||
termHeight = terminal.getHeight();
|
||||
}
|
||||
|
||||
var width = termWidth * FONT_WIDTH + MARGIN * 2;
|
||||
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
||||
@ -60,14 +67,15 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
||||
|
||||
// Render the light
|
||||
var lightColour = ClientPocketComputers.get(stack).getLightState();
|
||||
if (lightColour == -1) lightColour = Colour.BLACK.getHex();
|
||||
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||
renderLight(transform, bufferSource, lightColour, width, height);
|
||||
|
||||
FixedWidthFontRenderer.drawTerminal(
|
||||
FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL)),
|
||||
MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN
|
||||
);
|
||||
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||
if (terminal == null) {
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
|
||||
} else {
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||
}
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.client.sound;
|
||||
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.sounds.AudioStream;
|
||||
@ -36,7 +37,7 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The {@link Channel} which this sound is playing on.
|
||||
*
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||
*/
|
||||
@Nullable
|
||||
Channel channel;
|
||||
@ -44,21 +45,23 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The underlying {@link SoundEngine} executor.
|
||||
*
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||
* @see SoundEngine#executor
|
||||
*/
|
||||
@Nullable
|
||||
Executor executor;
|
||||
|
||||
private int charge = 0; // q
|
||||
private int strength = 0; // s
|
||||
private int lowPassCharge;
|
||||
private boolean previousBit = false;
|
||||
|
||||
DfpwmStream() {
|
||||
}
|
||||
|
||||
void push(ByteBuffer input) {
|
||||
void push(EncodedAudio audio) {
|
||||
var charge = audio.charge();
|
||||
var strength = audio.strength();
|
||||
var previousBit = audio.previousBit();
|
||||
var input = audio.audio();
|
||||
|
||||
var readable = input.remaining();
|
||||
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
||||
|
||||
|
@ -6,12 +6,12 @@ package dan200.computercraft.client.sound;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
|
||||
@ -25,7 +25,7 @@ public class SpeakerInstance {
|
||||
SpeakerInstance() {
|
||||
}
|
||||
|
||||
private void pushAudio(ByteBuffer buffer) {
|
||||
private void pushAudio(EncodedAudio buffer) {
|
||||
var sound = this.sound;
|
||||
|
||||
var stream = currentStream;
|
||||
@ -43,7 +43,7 @@ public class SpeakerInstance {
|
||||
}
|
||||
}
|
||||
|
||||
public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) {
|
||||
public void playAudio(SpeakerPosition position, float volume, EncodedAudio buffer) {
|
||||
pushAudio(buffer);
|
||||
|
||||
var soundManager = Minecraft.getInstance().getSoundManager();
|
||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.impl.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.wired.WiredNetworkChange;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.util.PeripheralHelpers;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -52,7 +53,7 @@ final class WiredNetworkChangeImpl implements WiredNetworkChange {
|
||||
var oldValue = entry.getValue();
|
||||
if (newPeripherals.containsKey(oldKey)) {
|
||||
var rightValue = added.get(oldKey);
|
||||
if (oldValue.equals(rightValue)) {
|
||||
if (PeripheralHelpers.equals(oldValue, rightValue)) {
|
||||
added.remove(oldKey);
|
||||
} else {
|
||||
removed.put(oldKey, oldValue);
|
||||
|
@ -134,7 +134,7 @@ public final class CommandComputerCraft {
|
||||
} else if (b.getLevel() == world) {
|
||||
return 1;
|
||||
} else {
|
||||
return Integer.compare(a.getInstanceID(), b.getInstanceID());
|
||||
return a.getInstanceUUID().compareTo(b.getInstanceUUID());
|
||||
}
|
||||
});
|
||||
|
||||
@ -159,7 +159,8 @@ public final class CommandComputerCraft {
|
||||
*/
|
||||
private static int dumpComputer(CommandSourceStack source, ServerComputer computer) {
|
||||
var table = new TableBuilder("Dump");
|
||||
table.row(header("Instance"), text(Integer.toString(computer.getInstanceID())));
|
||||
table.row(header("Instance ID"), text(Integer.toString(computer.getInstanceID())));
|
||||
table.row(header("Instance UUID"), text(computer.getInstanceUUID().toString()));
|
||||
table.row(header("Id"), text(Integer.toString(computer.getID())));
|
||||
table.row(header("Label"), text(computer.getLabel()));
|
||||
table.row(header("On"), bool(computer.isOn()));
|
||||
@ -338,11 +339,7 @@ public final class CommandComputerCraft {
|
||||
if (computer == null) {
|
||||
out.append("#" + computerId + " ").append(coloured("(unloaded)", ChatFormatting.GRAY));
|
||||
} else {
|
||||
out.append(link(
|
||||
text("#" + computerId + " ").append(coloured("(instance " + computer.getInstanceID() + ")", ChatFormatting.GRAY)),
|
||||
makeComputerCommand("dump", computer),
|
||||
Component.translatable("commands.computercraft.dump.action")
|
||||
));
|
||||
out.append(makeComputerDumpCommand(computer));
|
||||
}
|
||||
|
||||
// And, if we're a player, some useful links
|
||||
@ -372,10 +369,6 @@ public final class CommandComputerCraft {
|
||||
return out;
|
||||
}
|
||||
|
||||
private static String makeComputerCommand(String command, ServerComputer computer) {
|
||||
return String.format("/computercraft %s @c[instance=%d]", command, computer.getInstanceID());
|
||||
}
|
||||
|
||||
private static Component linkPosition(CommandSourceStack context, ServerComputer computer) {
|
||||
if (ModRegistry.Permissions.PERMISSION_TP.test(context)) {
|
||||
return link(
|
||||
@ -392,7 +385,7 @@ public final class CommandComputerCraft {
|
||||
var file = new File(ServerContext.get(source.getServer()).storageDir().toFile(), "computer/" + id);
|
||||
if (!file.isDirectory()) return null;
|
||||
|
||||
return link(
|
||||
return clientLink(
|
||||
text("\u270E"),
|
||||
"/" + CLIENT_OPEN_FOLDER + " " + id,
|
||||
Component.translatable("commands.computercraft.dump.open_path")
|
||||
|
@ -16,7 +16,10 @@ import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import net.minecraft.advancements.critereon.MinMaxBounds;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.commands.arguments.UuidArgument;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.ComponentContents;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
@ -30,17 +33,21 @@ import java.util.stream.Stream;
|
||||
import static dan200.computercraft.shared.command.CommandUtils.suggestOnServer;
|
||||
import static dan200.computercraft.shared.command.Exceptions.*;
|
||||
import static dan200.computercraft.shared.command.arguments.ArgumentParserUtils.consume;
|
||||
import static dan200.computercraft.shared.command.text.ChatHelpers.makeComputerDumpCommand;
|
||||
|
||||
public record ComputerSelector(
|
||||
String selector,
|
||||
OptionalInt instanceId,
|
||||
@Nullable UUID instanceUuid,
|
||||
OptionalInt computerId,
|
||||
@Nullable String label,
|
||||
@Nullable ComputerFamily family,
|
||||
@Nullable AABB bounds,
|
||||
@Nullable MinMaxBounds.Doubles range
|
||||
) {
|
||||
private static final ComputerSelector all = new ComputerSelector("@c[]", OptionalInt.empty(), OptionalInt.empty(), null, null, null, null);
|
||||
private static final ComputerSelector all = new ComputerSelector("@c[]", OptionalInt.empty(), null, OptionalInt.empty(), null, null, null, null);
|
||||
|
||||
private static UuidArgument uuidArgument = UuidArgument.uuid();
|
||||
|
||||
/**
|
||||
* A {@link ComputerSelector} which matches all computers.
|
||||
@ -59,8 +66,13 @@ public record ComputerSelector(
|
||||
*/
|
||||
public Stream<ServerComputer> find(CommandSourceStack source) {
|
||||
var context = ServerContext.get(source.getServer());
|
||||
if (instanceId.isPresent()) {
|
||||
var computer = context.registry().get(instanceId.getAsInt());
|
||||
if (instanceId().isPresent()) {
|
||||
var computer = context.registry().get(instanceId().getAsInt());
|
||||
return computer != null && matches(source, computer) ? Stream.of(computer) : Stream.of();
|
||||
}
|
||||
|
||||
if (instanceUuid() != null) {
|
||||
var computer = context.registry().get(instanceUuid());
|
||||
return computer != null && matches(source, computer) ? Stream.of(computer) : Stream.of();
|
||||
}
|
||||
|
||||
@ -79,7 +91,7 @@ public record ComputerSelector(
|
||||
if (computers.isEmpty()) throw COMPUTER_ARG_NONE.create(selector);
|
||||
if (computers.size() == 1) return computers.iterator().next();
|
||||
|
||||
var builder = new StringBuilder();
|
||||
var builder = MutableComponent.create(ComponentContents.EMPTY);
|
||||
var first = true;
|
||||
for (var computer : computers) {
|
||||
if (first) {
|
||||
@ -88,12 +100,12 @@ public record ComputerSelector(
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
builder.append(computer.getInstanceID());
|
||||
builder.append(makeComputerDumpCommand(computer));
|
||||
}
|
||||
|
||||
|
||||
// We have an incorrect number of computers: throw an error
|
||||
throw COMPUTER_ARG_MANY.create(selector, builder.toString());
|
||||
throw COMPUTER_ARG_MANY.create(selector, builder);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,6 +117,7 @@ public record ComputerSelector(
|
||||
*/
|
||||
public boolean matches(CommandSourceStack source, ServerComputer computer) {
|
||||
return (instanceId().isEmpty() || computer.getInstanceID() == instanceId().getAsInt())
|
||||
&& (instanceUuid() == null || computer.getInstanceUUID().equals(instanceUuid()))
|
||||
&& (computerId().isEmpty() || computer.getID() == computerId().getAsInt())
|
||||
&& (label == null || Objects.equals(computer.getLabel(), label))
|
||||
&& (family == null || computer.getFamily() == family)
|
||||
@ -127,6 +140,7 @@ public record ComputerSelector(
|
||||
if (consume(reader, "@c[")) {
|
||||
parseSelector(builder, reader);
|
||||
} else {
|
||||
// TODO(1.20.5): Only parse computer ids here.
|
||||
var kind = reader.peek();
|
||||
if (kind == '@') {
|
||||
reader.skip();
|
||||
@ -143,7 +157,7 @@ public record ComputerSelector(
|
||||
}
|
||||
|
||||
var selector = reader.getString().substring(start, reader.getCursor());
|
||||
return new ComputerSelector(selector, builder.instanceId, builder.computerId, builder.label, builder.family, builder.bounds, builder.range);
|
||||
return new ComputerSelector(selector, builder.instanceId, builder.instanceUuid, builder.computerId, builder.label, builder.family, builder.bounds, builder.range);
|
||||
}
|
||||
|
||||
private static void parseSelector(Builder builder, StringReader reader) throws CommandSyntaxException {
|
||||
@ -260,6 +274,7 @@ public record ComputerSelector(
|
||||
|
||||
private static final class Builder {
|
||||
private OptionalInt instanceId = OptionalInt.empty();
|
||||
private @Nullable UUID instanceUuid = null;
|
||||
private OptionalInt computerId = OptionalInt.empty();
|
||||
private @Nullable String label;
|
||||
private @Nullable ComputerFamily family;
|
||||
@ -282,8 +297,8 @@ public record ComputerSelector(
|
||||
var optionList = new Option[]{
|
||||
new Option(
|
||||
"instance",
|
||||
(reader, builder) -> builder.instanceId = OptionalInt.of(reader.readInt()),
|
||||
suggestComputers(c -> Integer.toString(c.getInstanceID()))
|
||||
(reader, builder) -> builder.instanceUuid = uuidArgument.parse(reader),
|
||||
suggestComputers(c -> c.getInstanceUUID().toString())
|
||||
),
|
||||
new Option(
|
||||
"id",
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package dan200.computercraft.shared.command.text;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
@ -53,6 +55,13 @@ public final class ChatHelpers {
|
||||
return link(component, new ClickEvent(ClickEvent.Action.RUN_COMMAND, command), toolTip);
|
||||
}
|
||||
|
||||
public static Component clientLink(MutableComponent component, String command, Component toolTip) {
|
||||
var event = PlatformHelper.get().canClickRunClientCommand()
|
||||
? new ClickEvent(ClickEvent.Action.RUN_COMMAND, command)
|
||||
: new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command);
|
||||
return link(component, event, toolTip);
|
||||
}
|
||||
|
||||
public static Component link(Component component, ClickEvent click, Component toolTip) {
|
||||
var style = component.getStyle();
|
||||
|
||||
@ -73,4 +82,16 @@ public final class ChatHelpers {
|
||||
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("gui.computercraft.tooltip.copy")))
|
||||
);
|
||||
}
|
||||
|
||||
public static String makeComputerCommand(String command, ServerComputer computer) {
|
||||
return String.format("/computercraft %s @c[instance=%s]", command, computer.getInstanceUUID());
|
||||
}
|
||||
|
||||
public static Component makeComputerDumpCommand(ServerComputer computer) {
|
||||
return link(
|
||||
text("#" + computer.getID()),
|
||||
makeComputerCommand("dump", computer),
|
||||
Component.translatable("commands.computercraft.dump.action")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
@ -174,6 +175,15 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
if (be instanceof AbstractComputerBlockEntity computer) computer.neighborChanged(neighbour);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
||||
var be = level.getBlockEntity(pos);
|
||||
if (be instanceof AbstractComputerBlockEntity computer) computer.neighbourShapeChanged(direction);
|
||||
|
||||
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Deprecated
|
||||
|
@ -36,13 +36,14 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class AbstractComputerBlockEntity extends BlockEntity implements IComputerBlockEntity, Nameable, MenuProvider {
|
||||
private static final String NBT_ID = "ComputerId";
|
||||
private static final String NBT_LABEL = "Label";
|
||||
private static final String NBT_ON = "On";
|
||||
|
||||
private int instanceID = -1;
|
||||
private @Nullable UUID instanceID = null;
|
||||
private int computerID = -1;
|
||||
protected @Nullable String label = null;
|
||||
private boolean on = false;
|
||||
@ -66,7 +67,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
|
||||
var computer = getServerComputer();
|
||||
if (computer != null) computer.close();
|
||||
instanceID = -1;
|
||||
instanceID = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -122,7 +123,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
// Update any peripherals that have changed.
|
||||
if (invalidSides != 0) {
|
||||
for (var direction : DirectionUtil.FACINGS) {
|
||||
if ((invalidSides & (1 << direction.ordinal())) != 0) refreshPeripheral(computer, direction);
|
||||
if (DirectionUtil.isSet(invalidSides, direction)) refreshPeripheral(computer, direction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +295,18 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
// If the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods
|
||||
// handle this incorrectly.
|
||||
for (var dir : DirectionUtil.FACINGS) updateRedstoneInput(computer, dir, getBlockPos().relative(dir));
|
||||
invalidSides = (1 << 6) - 1; // Mark all peripherals as dirty.
|
||||
invalidSides = DirectionUtil.ALL_SIDES; // Mark all peripherals as dirty.
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a neighbour block's shape changes.
|
||||
* <p>
|
||||
* Unlike {@link #neighborChanged(BlockPos)}, we don't update redstone, only peripherals.
|
||||
*
|
||||
* @param direction The side that changed.
|
||||
*/
|
||||
public void neighbourShapeChanged(Direction direction) {
|
||||
invalidSides |= 1 << direction.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -401,7 +413,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
}
|
||||
|
||||
protected void transferStateFrom(AbstractComputerBlockEntity copy) {
|
||||
if (copy.computerID != computerID || copy.instanceID != instanceID) {
|
||||
if (copy.computerID != computerID || !Objects.equals(copy.instanceID, instanceID)) {
|
||||
unload();
|
||||
instanceID = copy.instanceID;
|
||||
computerID = copy.computerID;
|
||||
@ -411,7 +423,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
lockCode = copy.lockCode;
|
||||
BlockEntityHelpers.updateBlock(this);
|
||||
}
|
||||
copy.instanceID = -1;
|
||||
copy.instanceID = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,11 +26,13 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
private final int instanceID;
|
||||
private final UUID instanceUUID = UUID.randomUUID();
|
||||
|
||||
private ServerLevel level;
|
||||
private BlockPos position;
|
||||
@ -119,9 +121,9 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
return computer.pollAndResetChanges();
|
||||
}
|
||||
|
||||
public int register() {
|
||||
ServerContext.get(level.getServer()).registry().add(instanceID, this);
|
||||
return instanceID;
|
||||
public UUID register() {
|
||||
ServerContext.get(level.getServer()).registry().add(this);
|
||||
return instanceUUID;
|
||||
}
|
||||
|
||||
void unload() {
|
||||
@ -130,7 +132,7 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
|
||||
public void close() {
|
||||
unload();
|
||||
ServerContext.get(level.getServer()).registry().remove(instanceID);
|
||||
ServerContext.get(level.getServer()).registry().remove(this);
|
||||
}
|
||||
|
||||
private void sendToAllInteracting(Function<AbstractContainerMenu, NetworkMessage<ClientNetworkContext>> createPacket) {
|
||||
@ -150,6 +152,10 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
||||
return instanceID;
|
||||
}
|
||||
|
||||
public UUID getInstanceUUID() {
|
||||
return instanceUUID;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return computer.getID();
|
||||
}
|
||||
|
@ -8,14 +8,14 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
public class ServerComputerRegistry {
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private final int sessionId = RANDOM.nextInt();
|
||||
private final Int2ObjectMap<ServerComputer> computers = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<ServerComputer> computersByInstanceId = new Int2ObjectOpenHashMap<>();
|
||||
private final Map<UUID, ServerComputer> computersByInstanceUuid = new HashMap<>();
|
||||
private int nextInstanceId;
|
||||
|
||||
public int getSessionID() {
|
||||
@ -28,50 +28,64 @@ public class ServerComputerRegistry {
|
||||
|
||||
@Nullable
|
||||
public ServerComputer get(int instanceID) {
|
||||
return instanceID >= 0 ? computers.get(instanceID) : null;
|
||||
return instanceID >= 0 ? computersByInstanceId.get(instanceID) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerComputer get(int sessionId, int instanceId) {
|
||||
public ServerComputer get(@Nullable UUID instanceID) {
|
||||
return instanceID != null ? computersByInstanceUuid.get(instanceID) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ServerComputer get(int sessionId, @Nullable UUID instanceId) {
|
||||
return sessionId == this.sessionId ? get(instanceId) : null;
|
||||
}
|
||||
|
||||
void update() {
|
||||
var it = getComputers().iterator();
|
||||
var it = computersByInstanceId.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
var computer = it.next();
|
||||
if (computer.hasTimedOut()) {
|
||||
computer.unload();
|
||||
computer.onRemoved();
|
||||
it.remove();
|
||||
computersByInstanceUuid.remove(computer.getInstanceUUID());
|
||||
} else {
|
||||
computer.tickServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add(int instanceID, ServerComputer computer) {
|
||||
remove(instanceID);
|
||||
computers.put(instanceID, computer);
|
||||
nextInstanceId = Math.max(nextInstanceId, instanceID + 1);
|
||||
}
|
||||
void add(ServerComputer computer) {
|
||||
var instanceID = computer.getInstanceID();
|
||||
var instanceUUID = computer.getInstanceUUID();
|
||||
|
||||
void remove(int instanceID) {
|
||||
var computer = get(instanceID);
|
||||
if (computer != null) {
|
||||
computer.unload();
|
||||
computer.onRemoved();
|
||||
if (computersByInstanceId.containsKey(instanceID)) {
|
||||
throw new IllegalStateException("Duplicate computer " + instanceID);
|
||||
}
|
||||
|
||||
computers.remove(instanceID);
|
||||
if (computersByInstanceUuid.containsKey(instanceUUID)) {
|
||||
throw new IllegalStateException("Duplicate computer " + instanceUUID);
|
||||
}
|
||||
|
||||
computersByInstanceId.put(instanceID, computer);
|
||||
computersByInstanceUuid.put(instanceUUID, computer);
|
||||
}
|
||||
|
||||
void remove(ServerComputer computer) {
|
||||
computer.unload();
|
||||
computer.onRemoved();
|
||||
computersByInstanceId.remove(computer.getInstanceID());
|
||||
computersByInstanceUuid.remove(computer.getInstanceUUID());
|
||||
}
|
||||
|
||||
void close() {
|
||||
for (var computer : getComputers()) computer.unload();
|
||||
computers.clear();
|
||||
computersByInstanceId.clear();
|
||||
computersByInstanceUuid.clear();
|
||||
}
|
||||
|
||||
public Collection<ServerComputer> getComputers() {
|
||||
return computers.values();
|
||||
return computersByInstanceId.values();
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class ViewComputerMenu extends ComputerMenuWithoutInventory {
|
||||
|
||||
private static boolean canInteractWith(ServerComputer computer, Player player) {
|
||||
// If this computer no longer exists then discard it.
|
||||
if (ServerContext.get(computer.getLevel().getServer()).registry().get(computer.getInstanceID()) != computer) {
|
||||
if (ServerContext.get(computer.getLevel().getServer()).registry().get(computer.getInstanceUUID()) != computer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,9 @@ import javax.annotation.Nullable;
|
||||
* states, etc...
|
||||
*/
|
||||
public class TerminalState {
|
||||
public final boolean colour;
|
||||
|
||||
public final int width;
|
||||
public final int height;
|
||||
private final boolean colour;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
@Nullable
|
||||
private final ByteBuf buffer;
|
||||
|
@ -8,6 +8,7 @@ import dan200.computercraft.shared.command.text.TableBuilder;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@ -15,7 +16,6 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -30,11 +30,11 @@ public interface ClientNetworkContext {
|
||||
|
||||
void handlePlayRecord(BlockPos pos, @Nullable SoundEvent sound, @Nullable String name);
|
||||
|
||||
void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal);
|
||||
void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, TerminalState terminal);
|
||||
|
||||
void handlePocketComputerDeleted(int instanceId);
|
||||
void handlePocketComputerDeleted(UUID instanceId);
|
||||
|
||||
void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer audio);
|
||||
void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio audio);
|
||||
|
||||
void handleSpeakerMove(UUID source, SpeakerPosition.Message position);
|
||||
|
||||
|
@ -13,24 +13,26 @@ import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Provides additional data about a client computer, such as its ID and current state.
|
||||
*/
|
||||
public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkContext> {
|
||||
private final int instanceId;
|
||||
private final UUID clientId;
|
||||
private final ComputerState state;
|
||||
private final int lightState;
|
||||
private final TerminalState terminal;
|
||||
|
||||
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
||||
instanceId = computer.getInstanceID();
|
||||
clientId = computer.getInstanceUUID();
|
||||
state = computer.getState();
|
||||
lightState = computer.getLight();
|
||||
terminal = sendTerminal ? computer.getTerminalState() : new TerminalState((NetworkedTerminal) null);
|
||||
}
|
||||
|
||||
public PocketComputerDataMessage(FriendlyByteBuf buf) {
|
||||
instanceId = buf.readVarInt();
|
||||
clientId = buf.readUUID();
|
||||
state = buf.readEnum(ComputerState.class);
|
||||
lightState = buf.readVarInt();
|
||||
terminal = new TerminalState(buf);
|
||||
@ -38,7 +40,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(instanceId);
|
||||
buf.writeUUID(clientId);
|
||||
buf.writeEnum(state);
|
||||
buf.writeVarInt(lightState);
|
||||
terminal.write(buf);
|
||||
@ -46,7 +48,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
||||
|
||||
@Override
|
||||
public void handle(ClientNetworkContext context) {
|
||||
context.handlePocketComputerData(instanceId, state, lightState, terminal);
|
||||
context.handlePocketComputerData(clientId, state, lightState, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,21 +9,23 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
public class PocketComputerDeletedClientMessage implements NetworkMessage<ClientNetworkContext> {
|
||||
private final int instanceId;
|
||||
private final UUID instanceId;
|
||||
|
||||
public PocketComputerDeletedClientMessage(int instanceId) {
|
||||
public PocketComputerDeletedClientMessage(UUID instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public PocketComputerDeletedClientMessage(FriendlyByteBuf buffer) {
|
||||
instanceId = buffer.readVarInt();
|
||||
instanceId = buffer.readUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(instanceId);
|
||||
buf.writeUUID(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -7,11 +7,11 @@ package dan200.computercraft.shared.network.client;
|
||||
import dan200.computercraft.shared.network.MessageType;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.NetworkMessages;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -24,10 +24,10 @@ import java.util.UUID;
|
||||
public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkContext> {
|
||||
private final UUID source;
|
||||
private final SpeakerPosition.Message pos;
|
||||
private final ByteBuffer content;
|
||||
private final EncodedAudio content;
|
||||
private final float volume;
|
||||
|
||||
public SpeakerAudioClientMessage(UUID source, SpeakerPosition pos, float volume, ByteBuffer content) {
|
||||
public SpeakerAudioClientMessage(UUID source, SpeakerPosition pos, float volume, EncodedAudio content) {
|
||||
this.source = source;
|
||||
this.pos = pos.asMessage();
|
||||
this.content = content;
|
||||
@ -38,10 +38,7 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
|
||||
source = buf.readUUID();
|
||||
pos = SpeakerPosition.Message.read(buf);
|
||||
volume = buf.readFloat();
|
||||
|
||||
var bytes = new byte[buf.readableBytes()];
|
||||
buf.readBytes(bytes);
|
||||
content = ByteBuffer.wrap(bytes);
|
||||
content = EncodedAudio.read(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,7 +46,7 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
|
||||
buf.writeUUID(source);
|
||||
pos.write(buf);
|
||||
buf.writeFloat(volume);
|
||||
buf.writeBytes(content.duplicate());
|
||||
content.write(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -11,7 +11,7 @@ import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import dan200.computercraft.core.computer.GuardedLuaContext;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
@ -27,13 +27,16 @@ public final class GenericPeripheral implements IDynamicPeripheral {
|
||||
private final Set<String> additionalTypes;
|
||||
private final List<SaturatedMethod> methods;
|
||||
|
||||
GenericPeripheral(BlockEntity tile, Direction side, @Nullable String name, Set<String> additionalTypes, List<SaturatedMethod> methods) {
|
||||
private @Nullable GuardedLuaContext contextWrapper;
|
||||
private final GuardedLuaContext.Guard guard;
|
||||
|
||||
GenericPeripheral(BlockEntity tile, Direction side, String type, Set<String> additionalTypes, List<SaturatedMethod> methods) {
|
||||
this.side = side;
|
||||
var type = RegistryWrappers.BLOCK_ENTITY_TYPES.getKey(tile.getType());
|
||||
this.tile = tile;
|
||||
this.type = name != null ? name : type.toString();
|
||||
this.type = type;
|
||||
this.additionalTypes = additionalTypes;
|
||||
this.methods = methods;
|
||||
this.guard = () -> !tile.isRemoved() && tile.getLevel() != null && tile.getLevel().isLoaded(tile.getBlockPos());
|
||||
}
|
||||
|
||||
public Direction side() {
|
||||
@ -49,7 +52,12 @@ public final class GenericPeripheral implements IDynamicPeripheral {
|
||||
|
||||
@Override
|
||||
public MethodResult callMethod(IComputerAccess computer, ILuaContext context, int method, IArguments arguments) throws LuaException {
|
||||
return methods.get(method).apply(context, computer, arguments);
|
||||
var contextWrapper = this.contextWrapper;
|
||||
if (contextWrapper == null || !contextWrapper.wraps(context)) {
|
||||
contextWrapper = this.contextWrapper = new GuardedLuaContext(context, guard);
|
||||
}
|
||||
|
||||
return methods.get(method).apply(contextWrapper, computer, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -10,6 +10,9 @@ import dan200.computercraft.core.methods.NamedMethod;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
@ -25,6 +28,8 @@ import java.util.Set;
|
||||
* See the platform-specific peripheral providers for the usage of this.
|
||||
*/
|
||||
final class GenericPeripheralBuilder {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralBuilder.class);
|
||||
|
||||
private @Nullable String name;
|
||||
private final Set<String> additionalTypes = new HashSet<>(0);
|
||||
private final ArrayList<SaturatedMethod> methods = new ArrayList<>();
|
||||
@ -33,8 +38,24 @@ final class GenericPeripheralBuilder {
|
||||
IPeripheral toPeripheral(BlockEntity blockEntity, Direction side) {
|
||||
if (methods.isEmpty()) return null;
|
||||
|
||||
String type;
|
||||
if (name == null) {
|
||||
var typeId = BlockEntityType.getKey(blockEntity.getType());
|
||||
if (typeId == null) {
|
||||
LOG.error(
|
||||
"Block entity {} for {} was not registered. Skipping creating a generic peripheral for it.",
|
||||
blockEntity, blockEntity.getBlockState().getBlock()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
type = typeId.toString();
|
||||
} else {
|
||||
type = name;
|
||||
}
|
||||
|
||||
methods.trimToSize();
|
||||
return new GenericPeripheral(blockEntity, side, name, additionalTypes, methods);
|
||||
return new GenericPeripheral(blockEntity, side, type, additionalTypes, methods);
|
||||
}
|
||||
|
||||
void addMethod(Object target, String name, PeripheralMethod method, @Nullable NamedMethod<PeripheralMethod> info) {
|
||||
|
@ -129,7 +129,6 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
|
||||
|
||||
world.setBlockAndUpdate(pos, correctConnections(world, pos, newState));
|
||||
|
||||
cable.modemChanged();
|
||||
cable.connectionsChanged();
|
||||
if (!world.isClientSide && !player.getAbilities().instabuild) {
|
||||
Block.popResource(world, pos, item);
|
||||
@ -162,10 +161,7 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
|
||||
@Override
|
||||
public void setPlacedBy(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
var tile = world.getBlockEntity(pos);
|
||||
if (tile instanceof CableBlockEntity cable) {
|
||||
if (cable.hasCable()) cable.connectionsChanged();
|
||||
}
|
||||
|
||||
if (tile instanceof CableBlockEntity cable && cable.hasCable()) cable.connectionsChanged();
|
||||
super.setPlacedBy(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@ -177,14 +173,37 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public BlockState updateShape(BlockState state, Direction side, BlockState otherState, LevelAccessor world, BlockPos pos, BlockPos otherPos) {
|
||||
WaterloggableHelpers.updateShape(state, world, pos);
|
||||
public BlockState updateShape(BlockState state, Direction side, BlockState otherState, LevelAccessor level, BlockPos pos, BlockPos otherPos) {
|
||||
WaterloggableHelpers.updateShape(state, level, pos);
|
||||
|
||||
// Should never happen, but handle the case where we've no modem or cable.
|
||||
if (!state.getValue(CABLE) && state.getValue(MODEM) == CableModemVariant.None) {
|
||||
return getFluidState(state).createLegacyBlock();
|
||||
}
|
||||
|
||||
return world instanceof Level level ? state.setValue(CONNECTIONS.get(side), doesConnectVisually(state, level, pos, side)) : state;
|
||||
// Pop our modem if needed.
|
||||
var dir = state.getValue(MODEM).getFacing();
|
||||
if (dir != null && dir.equals(side) && !canSupportCenter(level, otherPos, side.getOpposite())) {
|
||||
// If we've no cable, follow normal Minecraft logic and just remove the block.
|
||||
if (!state.getValue(CABLE)) return getFluidState(state).createLegacyBlock();
|
||||
|
||||
// Otherwise remove the cable and drop the modem manually.
|
||||
state = state.setValue(CableBlock.MODEM, CableModemVariant.None);
|
||||
if (level instanceof Level actualLevel) {
|
||||
Block.popResource(actualLevel, pos, new ItemStack(ModRegistry.Items.WIRED_MODEM.get()));
|
||||
}
|
||||
|
||||
if (level.getBlockEntity(pos) instanceof CableBlockEntity cable) cable.scheduleConnectionsChanged();
|
||||
}
|
||||
|
||||
var modem = state.getValue(MODEM);
|
||||
if (modem.getFacing() == side && modem.isPeripheralOn() && level.getBlockEntity(pos) instanceof CableBlockEntity cable) {
|
||||
cable.queueRefreshPeripheral();
|
||||
}
|
||||
|
||||
return level instanceof Level actualLevel
|
||||
? state.setValue(CONNECTIONS.get(side), doesConnectVisually(state, actualLevel, pos, side))
|
||||
: state;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -230,6 +249,7 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
|
||||
@Override
|
||||
@Deprecated
|
||||
public final InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
if (player.isCrouching() || !player.mayBuild()) return InteractionResult.PASS;
|
||||
return world.getBlockEntity(pos) instanceof CableBlockEntity modem ? modem.use(player) : InteractionResult.PASS;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ package dan200.computercraft.shared.peripheral.modem.wired;
|
||||
import dan200.computercraft.api.network.wired.WiredElement;
|
||||
import dan200.computercraft.api.network.wired.WiredNode;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.command.text.ChatHelpers;
|
||||
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||
import dan200.computercraft.shared.platform.ComponentAccess;
|
||||
@ -20,21 +19,16 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CableBlockEntity extends BlockEntity {
|
||||
private static final String NBT_PERIPHERAL_ENABLED = "PeripheralAccess";
|
||||
|
||||
private final class CableElement extends WiredModemElement {
|
||||
@Override
|
||||
public Level getLevel() {
|
||||
@ -57,34 +51,22 @@ public class CableBlockEntity extends BlockEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean invalidPeripheral;
|
||||
private boolean peripheralAccessAllowed;
|
||||
private boolean refreshPeripheral;
|
||||
private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral(PlatformHelper.get().createPeripheralAccess(this, x -> queueRefreshPeripheral()));
|
||||
private @Nullable Runnable modemChanged;
|
||||
|
||||
private boolean connectionsFormed = false;
|
||||
private boolean connectionsChanged = false;
|
||||
private boolean refreshConnections = false;
|
||||
|
||||
private final WiredModemElement cable = new CableElement();
|
||||
private final WiredNode node = cable.getNode();
|
||||
private final TickScheduler.Token tickToken = new TickScheduler.Token(this);
|
||||
private final WiredModemPeripheral modem = new WiredModemPeripheral(
|
||||
new ModemState(() -> TickScheduler.schedule(tickToken)),
|
||||
cable
|
||||
new ModemState(() -> TickScheduler.schedule(tickToken)), cable, peripheral, this
|
||||
) {
|
||||
@Override
|
||||
protected WiredModemLocalPeripheral getLocalPeripheral() {
|
||||
return peripheral;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 getPosition() {
|
||||
return Vec3.atCenterOf(getBlockPos().relative(getDirection()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return CableBlockEntity.this;
|
||||
var dir = getModemDirection();
|
||||
return Vec3.atCenterOf(dir == null ? getBlockPos() : getBlockPos().relative(dir));
|
||||
}
|
||||
};
|
||||
|
||||
@ -94,93 +76,61 @@ public class CableBlockEntity extends BlockEntity {
|
||||
super(type, pos, state);
|
||||
}
|
||||
|
||||
private void onRemove() {
|
||||
if (level == null || !level.isClientSide) {
|
||||
node.remove();
|
||||
connectionsFormed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoved() {
|
||||
super.setRemoved();
|
||||
modem.removed();
|
||||
onRemove();
|
||||
if (level == null || !level.isClientSide) node.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearRemoved() {
|
||||
super.clearRemoved();
|
||||
refreshConnections = refreshPeripheral = true;
|
||||
TickScheduler.schedule(tickToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setBlockState(BlockState state) {
|
||||
var direction = getMaybeDirection();
|
||||
var direction = getModemDirection();
|
||||
var hasCable = hasCable();
|
||||
super.setBlockState(state);
|
||||
|
||||
// We invalidate both the modem and element if the modem's direction is different.
|
||||
if (getMaybeDirection() != direction && modemChanged != null) modemChanged.run();
|
||||
// We invalidate both the modem and element if the modem direction or cable are different.
|
||||
if (modemChanged != null && (hasCable() != hasCable || getModemDirection() != direction)) modemChanged.run();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Direction getMaybeDirection() {
|
||||
private Direction getModemDirection() {
|
||||
return getBlockState().getValue(CableBlock.MODEM).getFacing();
|
||||
}
|
||||
|
||||
private Direction getDirection() {
|
||||
var direction = getMaybeDirection();
|
||||
return direction == null ? Direction.NORTH : direction;
|
||||
}
|
||||
|
||||
void neighborChanged(BlockPos neighbour) {
|
||||
var dir = getDirection();
|
||||
if (neighbour.equals(getBlockPos().relative(dir)) && hasModem() && !getBlockState().canSurvive(getLevel(), getBlockPos())) {
|
||||
if (hasCable()) {
|
||||
// Drop the modem and convert to cable
|
||||
Block.popResource(getLevel(), getBlockPos(), new ItemStack(ModRegistry.Items.WIRED_MODEM.get()));
|
||||
getLevel().setBlockAndUpdate(getBlockPos(), getBlockState().setValue(CableBlock.MODEM, CableModemVariant.None));
|
||||
modemChanged();
|
||||
connectionsChanged();
|
||||
} else {
|
||||
// Drop everything and remove block
|
||||
Block.popResource(getLevel(), getBlockPos(), new ItemStack(ModRegistry.Items.WIRED_MODEM.get()));
|
||||
getLevel().removeBlock(getBlockPos(), false);
|
||||
// This'll call #destroy(), so we don't need to reset the network here.
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!level.isClientSide && peripheralAccessAllowed) {
|
||||
var facing = getDirection();
|
||||
if (getBlockPos().relative(facing).equals(neighbour)) queueRefreshPeripheral();
|
||||
var dir = getModemDirection();
|
||||
if (!level.isClientSide && dir != null && getBlockPos().relative(dir).equals(neighbour) && isPeripheralOn()) {
|
||||
queueRefreshPeripheral();
|
||||
}
|
||||
}
|
||||
|
||||
private void queueRefreshPeripheral() {
|
||||
if (invalidPeripheral) return;
|
||||
invalidPeripheral = true;
|
||||
void queueRefreshPeripheral() {
|
||||
refreshPeripheral = true;
|
||||
TickScheduler.schedule(tickToken);
|
||||
}
|
||||
|
||||
private void refreshPeripheral() {
|
||||
invalidPeripheral = false;
|
||||
if (level != null && !isRemoved() && peripheral.attach(level, getBlockPos(), getDirection())) {
|
||||
updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
|
||||
InteractionResult use(Player player) {
|
||||
if (player.isCrouching() || !player.mayBuild()) return InteractionResult.PASS;
|
||||
if (!canAttachPeripheral()) return InteractionResult.FAIL;
|
||||
|
||||
if (getLevel().isClientSide) return InteractionResult.SUCCESS;
|
||||
|
||||
var oldName = peripheral.getConnectedName();
|
||||
togglePeripheralAccess();
|
||||
if (isPeripheralOn()) {
|
||||
detachPeripheral();
|
||||
} else {
|
||||
attachPeripheral();
|
||||
}
|
||||
var newName = peripheral.getConnectedName();
|
||||
|
||||
if (!Objects.equals(newName, oldName)) {
|
||||
if (oldName != null) {
|
||||
player.displayClientMessage(Component.translatable("chat.computercraft.wired_modem.peripheral_disconnected",
|
||||
@ -198,14 +148,11 @@ public class CableBlockEntity extends BlockEntity {
|
||||
@Override
|
||||
public void load(CompoundTag nbt) {
|
||||
super.load(nbt);
|
||||
// Fallback to the previous (incorrect) key
|
||||
peripheralAccessAllowed = nbt.getBoolean(NBT_PERIPHERAL_ENABLED) || nbt.getBoolean("PeirpheralAccess");
|
||||
peripheral.read(nbt, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAdditional(CompoundTag nbt) {
|
||||
nbt.putBoolean(NBT_PERIPHERAL_ENABLED, peripheralAccessAllowed);
|
||||
peripheral.write(nbt, "");
|
||||
super.saveAdditional(nbt);
|
||||
}
|
||||
@ -214,7 +161,7 @@ public class CableBlockEntity extends BlockEntity {
|
||||
var state = getBlockState();
|
||||
var oldVariant = state.getValue(CableBlock.MODEM);
|
||||
var newVariant = CableModemVariant
|
||||
.from(oldVariant.getFacing(), modem.getModemState().isOpen(), peripheralAccessAllowed);
|
||||
.from(oldVariant.getFacing(), modem.getModemState().isOpen(), peripheral.hasPeripheral());
|
||||
|
||||
if (oldVariant != newVariant) {
|
||||
level.setBlockAndUpdate(getBlockPos(), state.setValue(CableBlock.MODEM, newVariant));
|
||||
@ -224,31 +171,24 @@ public class CableBlockEntity extends BlockEntity {
|
||||
void blockTick() {
|
||||
if (getLevel().isClientSide) return;
|
||||
|
||||
if (invalidPeripheral) refreshPeripheral();
|
||||
if (refreshPeripheral) {
|
||||
refreshPeripheral = false;
|
||||
if (isPeripheralOn()) attachPeripheral();
|
||||
}
|
||||
|
||||
if (modem.getModemState().pollChanged()) updateBlockState();
|
||||
|
||||
if (!connectionsFormed) {
|
||||
connectionsFormed = true;
|
||||
|
||||
connectionsChanged();
|
||||
if (peripheralAccessAllowed) {
|
||||
peripheral.attach(level, worldPosition, getDirection());
|
||||
updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionsChanged) connectionsChanged();
|
||||
if (refreshConnections) connectionsChanged();
|
||||
}
|
||||
|
||||
private void scheduleConnectionsChanged() {
|
||||
connectionsChanged = true;
|
||||
void scheduleConnectionsChanged() {
|
||||
refreshConnections = true;
|
||||
TickScheduler.schedule(tickToken);
|
||||
}
|
||||
|
||||
void connectionsChanged() {
|
||||
if (getLevel().isClientSide) return;
|
||||
connectionsChanged = false;
|
||||
refreshConnections = false;
|
||||
|
||||
var state = getBlockState();
|
||||
var world = getLevel();
|
||||
@ -269,51 +209,24 @@ public class CableBlockEntity extends BlockEntity {
|
||||
this.node.disconnectFrom(node);
|
||||
}
|
||||
}
|
||||
|
||||
// If we can no longer attach peripherals, then detach any which may have existed
|
||||
if (!canAttachPeripheral()) detachPeripheral();
|
||||
}
|
||||
|
||||
void modemChanged() {
|
||||
// Tell anyone who cares that the connection state has changed
|
||||
if (modemChanged != null) modemChanged.run();
|
||||
|
||||
if (getLevel().isClientSide) return;
|
||||
|
||||
// If we can no longer attach peripherals, then detach any
|
||||
// which may have existed
|
||||
if (!canAttachPeripheral() && peripheralAccessAllowed) {
|
||||
peripheralAccessAllowed = false;
|
||||
peripheral.detach();
|
||||
node.updatePeripherals(Map.of());
|
||||
setChanged();
|
||||
updateBlockState();
|
||||
}
|
||||
private void attachPeripheral() {
|
||||
var dir = Objects.requireNonNull(getModemDirection(), "Attaching without a modem");
|
||||
if (peripheral.attach(getLevel(), getBlockPos(), dir)) updateConnectedPeripherals();
|
||||
updateBlockState();
|
||||
}
|
||||
|
||||
private void togglePeripheralAccess() {
|
||||
if (!peripheralAccessAllowed) {
|
||||
peripheral.attach(level, getBlockPos(), getDirection());
|
||||
if (!peripheral.hasPeripheral()) return;
|
||||
|
||||
peripheralAccessAllowed = true;
|
||||
node.updatePeripherals(peripheral.toMap());
|
||||
} else {
|
||||
peripheral.detach();
|
||||
|
||||
peripheralAccessAllowed = false;
|
||||
node.updatePeripherals(Map.of());
|
||||
}
|
||||
|
||||
private void detachPeripheral() {
|
||||
if (peripheral.detach()) updateConnectedPeripherals();
|
||||
updateBlockState();
|
||||
}
|
||||
|
||||
private void updateConnectedPeripherals() {
|
||||
var peripherals = peripheral.toMap();
|
||||
if (peripherals.isEmpty()) {
|
||||
// If there are no peripherals then disable access and update the display state.
|
||||
peripheralAccessAllowed = false;
|
||||
updateBlockState();
|
||||
}
|
||||
|
||||
node.updatePeripherals(peripherals);
|
||||
node.updatePeripherals(peripheral.toMap());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -323,7 +236,11 @@ public class CableBlockEntity extends BlockEntity {
|
||||
|
||||
@Nullable
|
||||
public IPeripheral getPeripheral(@Nullable Direction direction) {
|
||||
return direction == null || getMaybeDirection() == direction ? modem : null;
|
||||
return direction == null || getModemDirection() == direction ? modem : null;
|
||||
}
|
||||
|
||||
private boolean isPeripheralOn() {
|
||||
return getBlockState().getValue(CableBlock.MODEM).isPeripheralOn();
|
||||
}
|
||||
|
||||
public void onModemChanged(Runnable callback) {
|
||||
|
@ -35,10 +35,7 @@ public abstract class CableBlockItem extends BlockItem {
|
||||
world.playSound(null, pos, soundType.getPlaceSound(), SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F);
|
||||
|
||||
var tile = world.getBlockEntity(pos);
|
||||
if (tile instanceof CableBlockEntity cable) {
|
||||
cable.modemChanged();
|
||||
cable.connectionsChanged();
|
||||
}
|
||||
if (tile instanceof CableBlockEntity cable) cable.connectionsChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -10,49 +10,57 @@ import net.minecraft.util.StringRepresentable;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public enum CableModemVariant implements StringRepresentable {
|
||||
None("none", null),
|
||||
DownOff("down_off", Direction.DOWN),
|
||||
UpOff("up_off", Direction.UP),
|
||||
NorthOff("north_off", Direction.NORTH),
|
||||
SouthOff("south_off", Direction.SOUTH),
|
||||
WestOff("west_off", Direction.WEST),
|
||||
EastOff("east_off", Direction.EAST),
|
||||
DownOn("down_on", Direction.DOWN),
|
||||
UpOn("up_on", Direction.UP),
|
||||
NorthOn("north_on", Direction.NORTH),
|
||||
SouthOn("south_on", Direction.SOUTH),
|
||||
WestOn("west_on", Direction.WEST),
|
||||
EastOn("east_on", Direction.EAST),
|
||||
DownOffPeripheral("down_off_peripheral", Direction.DOWN),
|
||||
UpOffPeripheral("up_off_peripheral", Direction.UP),
|
||||
NorthOffPeripheral("north_off_peripheral", Direction.NORTH),
|
||||
SouthOffPeripheral("south_off_peripheral", Direction.SOUTH),
|
||||
WestOffPeripheral("west_off_peripheral", Direction.WEST),
|
||||
EastOffPeripheral("east_off_peripheral", Direction.EAST),
|
||||
DownOnPeripheral("down_on_peripheral", Direction.DOWN),
|
||||
UpOnPeripheral("up_on_peripheral", Direction.UP),
|
||||
NorthOnPeripheral("north_on_peripheral", Direction.NORTH),
|
||||
SouthOnPeripheral("south_on_peripheral", Direction.SOUTH),
|
||||
WestOnPeripheral("west_on_peripheral", Direction.WEST),
|
||||
EastOnPeripheral("east_on_peripheral", Direction.EAST);
|
||||
None("none", null, false, false),
|
||||
DownOff("down_off", Direction.DOWN, false, false),
|
||||
UpOff("up_off", Direction.UP, false, false),
|
||||
NorthOff("north_off", Direction.NORTH, false, false),
|
||||
SouthOff("south_off", Direction.SOUTH, false, false),
|
||||
WestOff("west_off", Direction.WEST, false, false),
|
||||
EastOff("east_off", Direction.EAST, false, false),
|
||||
DownOn("down_on", Direction.DOWN, true, false),
|
||||
UpOn("up_on", Direction.UP, true, false),
|
||||
NorthOn("north_on", Direction.NORTH, true, false),
|
||||
SouthOn("south_on", Direction.SOUTH, true, false),
|
||||
WestOn("west_on", Direction.WEST, true, false),
|
||||
EastOn("east_on", Direction.EAST, true, false),
|
||||
DownOffPeripheral("down_off_peripheral", Direction.DOWN, false, true),
|
||||
UpOffPeripheral("up_off_peripheral", Direction.UP, false, true),
|
||||
NorthOffPeripheral("north_off_peripheral", Direction.NORTH, false, true),
|
||||
SouthOffPeripheral("south_off_peripheral", Direction.SOUTH, false, true),
|
||||
WestOffPeripheral("west_off_peripheral", Direction.WEST, false, true),
|
||||
EastOffPeripheral("east_off_peripheral", Direction.EAST, false, true),
|
||||
DownOnPeripheral("down_on_peripheral", Direction.DOWN, true, true),
|
||||
UpOnPeripheral("up_on_peripheral", Direction.UP, true, true),
|
||||
NorthOnPeripheral("north_on_peripheral", Direction.NORTH, true, true),
|
||||
SouthOnPeripheral("south_on_peripheral", Direction.SOUTH, true, true),
|
||||
WestOnPeripheral("west_on_peripheral", Direction.WEST, true, true),
|
||||
EastOnPeripheral("east_on_peripheral", Direction.EAST, true, true);
|
||||
|
||||
private static final CableModemVariant[] VALUES = values();
|
||||
|
||||
private final String name;
|
||||
private final @Nullable Direction facing;
|
||||
private final boolean modemOn, peripheralOn;
|
||||
|
||||
CableModemVariant(String name, @Nullable Direction facing) {
|
||||
CableModemVariant(String name, @Nullable Direction facing, boolean modemOn, boolean peripheralOn) {
|
||||
this.name = name;
|
||||
this.facing = facing;
|
||||
this.modemOn = modemOn;
|
||||
this.peripheralOn = peripheralOn;
|
||||
if (ordinal() != getIndex(facing, modemOn, peripheralOn)) throw new IllegalStateException("Mismatched ordinal");
|
||||
}
|
||||
|
||||
public static CableModemVariant from(Direction facing) {
|
||||
return facing == null ? None : VALUES[1 + facing.get3DDataValue()];
|
||||
return VALUES[1 + facing.get3DDataValue()];
|
||||
}
|
||||
|
||||
private static int getIndex(@Nullable Direction facing, boolean modem, boolean peripheral) {
|
||||
var state = (modem ? 1 : 0) + (peripheral ? 2 : 0);
|
||||
return facing == null ? 0 : 1 + 6 * state + facing.get3DDataValue();
|
||||
}
|
||||
|
||||
public static CableModemVariant from(@Nullable Direction facing, boolean modem, boolean peripheral) {
|
||||
var state = (modem ? 1 : 0) + (peripheral ? 2 : 0);
|
||||
return facing == null ? None : VALUES[1 + 6 * state + facing.get3DDataValue()];
|
||||
return VALUES[getIndex(facing, modem, peripheral)];
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -64,6 +72,14 @@ public enum CableModemVariant implements StringRepresentable {
|
||||
return facing;
|
||||
}
|
||||
|
||||
public boolean isModemOn() {
|
||||
return modemOn;
|
||||
}
|
||||
|
||||
public boolean isPeripheralOn() {
|
||||
return peripheralOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
|
@ -7,12 +7,14 @@ package dan200.computercraft.shared.peripheral.modem.wired;
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
@ -49,13 +51,27 @@ public class WiredModemFullBlock extends Block implements EntityBlock {
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final void neighborChanged(BlockState state, Level world, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos, boolean isMoving) {
|
||||
if (world.getBlockEntity(pos) instanceof WiredModemFullBlockEntity modem) modem.neighborChanged(neighbourPos);
|
||||
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
||||
if (state.getValue(PERIPHERAL_ON) && level.getBlockEntity(pos) instanceof WiredModemFullBlockEntity modem) {
|
||||
modem.queueRefreshPeripheral(direction);
|
||||
}
|
||||
|
||||
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public final void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighbourBlock, BlockPos neighbourPos, boolean isMoving) {
|
||||
if (state.getValue(PERIPHERAL_ON) && level.getBlockEntity(pos) instanceof WiredModemFullBlockEntity modem) {
|
||||
modem.neighborChanged(neighbourPos);
|
||||
}
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public final void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbour) {
|
||||
if (world.getBlockEntity(pos) instanceof WiredModemFullBlockEntity modem) modem.neighborChanged(neighbour);
|
||||
public final void onNeighborChange(BlockState state, LevelReader level, BlockPos pos, BlockPos neighbour) {
|
||||
if (state.getValue(PERIPHERAL_ON) && level.getBlockEntity(pos) instanceof WiredModemFullBlockEntity modem) {
|
||||
modem.neighborChanged(neighbour);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,8 +32,6 @@ import static dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullB
|
||||
import static dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlock.PERIPHERAL_ON;
|
||||
|
||||
public class WiredModemFullBlockEntity extends BlockEntity {
|
||||
private static final String NBT_PERIPHERAL_ENABLED = "PeripheralAccess";
|
||||
|
||||
private static final class FullElement extends WiredModemElement {
|
||||
private final WiredModemFullBlockEntity entity;
|
||||
|
||||
@ -70,11 +68,9 @@ public class WiredModemFullBlockEntity extends BlockEntity {
|
||||
|
||||
private final WiredModemPeripheral[] modems = new WiredModemPeripheral[6];
|
||||
|
||||
private boolean peripheralAccessAllowed = false;
|
||||
private final WiredModemLocalPeripheral[] peripherals = new WiredModemLocalPeripheral[6];
|
||||
|
||||
private boolean connectionsFormed = false;
|
||||
private boolean connectionsChanged = false;
|
||||
private boolean refreshConnections = false;
|
||||
|
||||
private final TickScheduler.Token tickToken = new TickScheduler.Token(this);
|
||||
private final ModemState modemState = new ModemState(() -> TickScheduler.schedule(tickToken));
|
||||
@ -96,31 +92,30 @@ public class WiredModemFullBlockEntity extends BlockEntity {
|
||||
@Override
|
||||
public void setRemoved() {
|
||||
super.setRemoved();
|
||||
if (level == null || !level.isClientSide) {
|
||||
node.remove();
|
||||
connectionsFormed = false;
|
||||
|
||||
for (var modem : modems) {
|
||||
if (modem != null) modem.removed();
|
||||
}
|
||||
if (level == null || !level.isClientSide) node.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearRemoved() {
|
||||
super.clearRemoved();
|
||||
refreshConnections = true;
|
||||
invalidSides = DirectionUtil.ALL_SIDES;
|
||||
TickScheduler.schedule(tickToken);
|
||||
}
|
||||
|
||||
void neighborChanged(BlockPos neighbour) {
|
||||
if (!level.isClientSide && peripheralAccessAllowed) {
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
if (getBlockPos().relative(facing).equals(neighbour)) queueRefreshPeripheral(facing);
|
||||
}
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
if (getBlockPos().relative(facing).equals(neighbour)) queueRefreshPeripheral(facing);
|
||||
}
|
||||
}
|
||||
|
||||
private void queueRefreshPeripheral(Direction facing) {
|
||||
if (invalidSides == 0) TickScheduler.schedule(tickToken);
|
||||
void queueRefreshPeripheral(Direction facing) {
|
||||
invalidSides |= 1 << facing.ordinal();
|
||||
}
|
||||
|
||||
private void refreshPeripheral(Direction facing) {
|
||||
invalidSides &= ~(1 << facing.ordinal());
|
||||
var peripheral = peripherals[facing.ordinal()];
|
||||
if (level != null && !isRemoved() && peripheral.attach(level, getBlockPos(), facing)) {
|
||||
updateConnectedPeripherals();
|
||||
}
|
||||
TickScheduler.schedule(tickToken);
|
||||
}
|
||||
|
||||
public InteractionResult use(Player player) {
|
||||
@ -129,7 +124,11 @@ public class WiredModemFullBlockEntity extends BlockEntity {
|
||||
|
||||
// On server, we interacted if a peripheral was found
|
||||
var oldPeriphNames = getConnectedPeripheralNames();
|
||||
togglePeripheralAccess();
|
||||
if (isPeripheralOn()) {
|
||||
detachPeripherals();
|
||||
} else {
|
||||
attachPeripherals(DirectionUtil.ALL_SIDES);
|
||||
}
|
||||
var periphNames = getConnectedPeripheralNames();
|
||||
|
||||
if (!Objects.equals(periphNames, oldPeriphNames)) {
|
||||
@ -158,65 +157,45 @@ public class WiredModemFullBlockEntity extends BlockEntity {
|
||||
@Override
|
||||
public void load(CompoundTag nbt) {
|
||||
super.load(nbt);
|
||||
peripheralAccessAllowed = nbt.getBoolean(NBT_PERIPHERAL_ENABLED);
|
||||
for (var i = 0; i < peripherals.length; i++) peripherals[i].read(nbt, Integer.toString(i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAdditional(CompoundTag nbt) {
|
||||
nbt.putBoolean(NBT_PERIPHERAL_ENABLED, peripheralAccessAllowed);
|
||||
for (var i = 0; i < peripherals.length; i++) peripherals[i].write(nbt, Integer.toString(i));
|
||||
super.saveAdditional(nbt);
|
||||
}
|
||||
|
||||
private void updateBlockState() {
|
||||
var state = getBlockState();
|
||||
boolean modemOn = modemState.isOpen(), peripheralOn = peripheralAccessAllowed;
|
||||
if (state.getValue(MODEM_ON) == modemOn && state.getValue(PERIPHERAL_ON) == peripheralOn) return;
|
||||
|
||||
getLevel().setBlockAndUpdate(getBlockPos(), state.setValue(MODEM_ON, modemOn).setValue(PERIPHERAL_ON, peripheralOn));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearRemoved() {
|
||||
super.clearRemoved();
|
||||
TickScheduler.schedule(tickToken);
|
||||
}
|
||||
|
||||
void blockTick() {
|
||||
if (getLevel().isClientSide) return;
|
||||
|
||||
if (invalidSides != 0) {
|
||||
for (var direction : DirectionUtil.FACINGS) {
|
||||
if ((invalidSides & (1 << direction.ordinal())) != 0) refreshPeripheral(direction);
|
||||
}
|
||||
var oldInvalidSides = invalidSides;
|
||||
invalidSides = 0;
|
||||
if (isPeripheralOn()) attachPeripherals(oldInvalidSides);
|
||||
}
|
||||
|
||||
if (modemState.pollChanged()) updateBlockState();
|
||||
if (modemState.pollChanged()) updateModemBlockState();
|
||||
|
||||
if (!connectionsFormed) {
|
||||
connectionsFormed = true;
|
||||
if (refreshConnections) connectionsChanged();
|
||||
}
|
||||
|
||||
connectionsChanged();
|
||||
if (peripheralAccessAllowed) {
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
peripherals[facing.ordinal()].attach(level, getBlockPos(), facing);
|
||||
}
|
||||
updateConnectedPeripherals();
|
||||
}
|
||||
}
|
||||
private void updateModemBlockState() {
|
||||
var state = getBlockState();
|
||||
var modemOn = modemState.isOpen();
|
||||
if (state.getValue(MODEM_ON) == modemOn) return;
|
||||
|
||||
if (connectionsChanged) connectionsChanged();
|
||||
getLevel().setBlockAndUpdate(getBlockPos(), state.setValue(MODEM_ON, modemOn));
|
||||
}
|
||||
|
||||
private void scheduleConnectionsChanged() {
|
||||
connectionsChanged = true;
|
||||
refreshConnections = true;
|
||||
TickScheduler.schedule(tickToken);
|
||||
}
|
||||
|
||||
private void connectionsChanged() {
|
||||
if (getLevel().isClientSide) return;
|
||||
connectionsChanged = false;
|
||||
refreshConnections = false;
|
||||
|
||||
var world = getLevel();
|
||||
var current = getBlockPos();
|
||||
@ -231,57 +210,48 @@ public class WiredModemFullBlockEntity extends BlockEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private void togglePeripheralAccess() {
|
||||
if (!peripheralAccessAllowed) {
|
||||
var hasAny = false;
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
var peripheral = peripherals[facing.ordinal()];
|
||||
peripheral.attach(level, getBlockPos(), facing);
|
||||
hasAny |= peripheral.hasPeripheral();
|
||||
}
|
||||
|
||||
if (!hasAny) return;
|
||||
|
||||
peripheralAccessAllowed = true;
|
||||
node.updatePeripherals(getConnectedPeripherals());
|
||||
} else {
|
||||
peripheralAccessAllowed = false;
|
||||
|
||||
for (var peripheral : peripherals) peripheral.detach();
|
||||
node.updatePeripherals(Map.of());
|
||||
}
|
||||
|
||||
updateBlockState();
|
||||
}
|
||||
|
||||
private Set<String> getConnectedPeripheralNames() {
|
||||
if (!peripheralAccessAllowed) return Set.of();
|
||||
|
||||
Set<String> peripherals = new HashSet<>(6);
|
||||
private List<String> getConnectedPeripheralNames() {
|
||||
List<String> peripherals = new ArrayList<>(6);
|
||||
for (var peripheral : this.peripherals) {
|
||||
var name = peripheral.getConnectedName();
|
||||
if (name != null) peripherals.add(name);
|
||||
}
|
||||
peripherals.sort(String::compareTo);
|
||||
return peripherals;
|
||||
}
|
||||
|
||||
private Map<String, IPeripheral> getConnectedPeripherals() {
|
||||
if (!peripheralAccessAllowed) return Map.of();
|
||||
private void attachPeripherals(int sides) {
|
||||
var anyChanged = false;
|
||||
|
||||
Map<String, IPeripheral> peripherals = new HashMap<>(6);
|
||||
for (var peripheral : this.peripherals) peripheral.extendMap(peripherals);
|
||||
return Collections.unmodifiableMap(peripherals);
|
||||
}
|
||||
Map<String, IPeripheral> attachedPeripherals = new HashMap<>(6);
|
||||
|
||||
private void updateConnectedPeripherals() {
|
||||
var peripherals = getConnectedPeripherals();
|
||||
if (peripherals.isEmpty()) {
|
||||
// If there are no peripherals then disable access and update the display state.
|
||||
peripheralAccessAllowed = false;
|
||||
updateBlockState();
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
var peripheral = peripherals[facing.ordinal()];
|
||||
if (DirectionUtil.isSet(sides, facing)) anyChanged |= peripheral.attach(getLevel(), getBlockPos(), facing);
|
||||
peripheral.extendMap(attachedPeripherals);
|
||||
}
|
||||
|
||||
node.updatePeripherals(peripherals);
|
||||
if (anyChanged) node.updatePeripherals(attachedPeripherals);
|
||||
|
||||
updatePeripheralBlocKState(!attachedPeripherals.isEmpty());
|
||||
}
|
||||
|
||||
private void detachPeripherals() {
|
||||
var anyChanged = false;
|
||||
for (var peripheral : peripherals) anyChanged |= peripheral.detach();
|
||||
if (anyChanged) node.updatePeripherals(Map.of());
|
||||
|
||||
updatePeripheralBlocKState(false);
|
||||
}
|
||||
|
||||
private void updatePeripheralBlocKState(boolean peripheralOn) {
|
||||
var state = getBlockState();
|
||||
if (state.getValue(PERIPHERAL_ON) == peripheralOn) return;
|
||||
getLevel().setBlockAndUpdate(getBlockPos(), state.setValue(PERIPHERAL_ON, peripheralOn));
|
||||
}
|
||||
|
||||
private boolean isPeripheralOn() {
|
||||
return getBlockState().getValue(PERIPHERAL_ON);
|
||||
}
|
||||
|
||||
public WiredElement getElement() {
|
||||
@ -295,22 +265,11 @@ public class WiredModemFullBlockEntity extends BlockEntity {
|
||||
var peripheral = modems[side.ordinal()];
|
||||
if (peripheral != null) return peripheral;
|
||||
|
||||
var localPeripheral = peripherals[side.ordinal()];
|
||||
return modems[side.ordinal()] = new WiredModemPeripheral(modemState, element) {
|
||||
@Override
|
||||
protected WiredModemLocalPeripheral getLocalPeripheral() {
|
||||
return localPeripheral;
|
||||
}
|
||||
|
||||
return modems[side.ordinal()] = new WiredModemPeripheral(modemState, element, peripherals[side.ordinal()], this) {
|
||||
@Override
|
||||
public Vec3 getPosition() {
|
||||
return Vec3.atCenterOf(getBlockPos().relative(side));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return WiredModemFullBlockEntity.this;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared.peripheral.modem.wired;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.util.PeripheralHelpers;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.platform.ComponentAccess;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@ -65,7 +66,7 @@ public final class WiredModemLocalPeripheral {
|
||||
this.id = ServerContext.get(assertNonNull(world.getServer())).getNextId("peripheral." + type);
|
||||
}
|
||||
|
||||
return oldPeripheral == null || !oldPeripheral.equals(peripheral);
|
||||
return !PeripheralHelpers.equals(oldPeripheral, peripheral);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.NotAttachedException;
|
||||
import dan200.computercraft.api.peripheral.WorkMonitor;
|
||||
import dan200.computercraft.core.apis.PeripheralAPI;
|
||||
import dan200.computercraft.core.computer.GuardedLuaContext;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.core.util.LuaUtil;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
@ -22,6 +23,7 @@ import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -34,12 +36,21 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WiredModemPeripheral.class);
|
||||
|
||||
private final WiredModemElement modem;
|
||||
private final WiredModemLocalPeripheral localPeripheral;
|
||||
private final BlockEntity target;
|
||||
|
||||
private final Map<IComputerAccess, ConcurrentMap<String, RemotePeripheralWrapper>> peripheralWrappers = new HashMap<>(1);
|
||||
|
||||
public WiredModemPeripheral(ModemState state, WiredModemElement modem) {
|
||||
public WiredModemPeripheral(
|
||||
ModemState state,
|
||||
WiredModemElement modem,
|
||||
WiredModemLocalPeripheral localPeripheral,
|
||||
BlockEntity target
|
||||
) {
|
||||
super(state);
|
||||
this.modem = modem;
|
||||
this.localPeripheral = localPeripheral;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
//region IPacketSender implementation
|
||||
@ -62,8 +73,6 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
public Level getLevel() {
|
||||
return modem.getLevel();
|
||||
}
|
||||
|
||||
protected abstract WiredModemLocalPeripheral getLocalPeripheral();
|
||||
//endregion
|
||||
|
||||
@Override
|
||||
@ -207,7 +216,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
*/
|
||||
@LuaFunction
|
||||
public final @Nullable Object[] getNameLocal() {
|
||||
var local = getLocalPeripheral().getConnectedName();
|
||||
var local = localPeripheral.getConnectedName();
|
||||
return local == null ? null : new Object[]{ local };
|
||||
}
|
||||
|
||||
@ -218,8 +227,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
|
||||
ConcurrentMap<String, RemotePeripheralWrapper> wrappers;
|
||||
synchronized (peripheralWrappers) {
|
||||
wrappers = peripheralWrappers.get(computer);
|
||||
if (wrappers == null) peripheralWrappers.put(computer, wrappers = new ConcurrentHashMap<>());
|
||||
wrappers = peripheralWrappers.computeIfAbsent(computer, k -> new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
synchronized (modem.getRemotePeripherals()) {
|
||||
@ -245,11 +253,13 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable IPeripheral other) {
|
||||
if (other instanceof WiredModemPeripheral otherModem) {
|
||||
return otherModem.modem == modem;
|
||||
}
|
||||
return false;
|
||||
public final boolean equals(@Nullable IPeripheral other) {
|
||||
return other instanceof WiredModemPeripheral otherModem && otherModem.modem == modem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object getTarget() {
|
||||
return target;
|
||||
}
|
||||
//endregion
|
||||
|
||||
@ -272,12 +282,11 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
var wrapper = wrappers.remove(name);
|
||||
if (wrapper != null) wrapper.detach();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void attachPeripheralImpl(IComputerAccess computer, ConcurrentMap<String, RemotePeripheralWrapper> peripherals, String periphName, IPeripheral peripheral) {
|
||||
if (!peripherals.containsKey(periphName) && !periphName.equals(getLocalPeripheral().getConnectedName())) {
|
||||
if (!peripherals.containsKey(periphName) && !periphName.equals(localPeripheral.getConnectedName())) {
|
||||
var methods = ServerContext.get(((ServerLevel) getLevel()).getServer()).peripheralMethods().getSelfMethods(peripheral);
|
||||
var wrapper = new RemotePeripheralWrapper(modem, peripheral, computer, periphName, methods);
|
||||
peripherals.put(periphName, wrapper);
|
||||
@ -296,7 +305,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
return wrappers == null ? null : wrappers.get(remoteName);
|
||||
}
|
||||
|
||||
private static class RemotePeripheralWrapper implements IComputerAccess {
|
||||
private static class RemotePeripheralWrapper implements IComputerAccess, GuardedLuaContext.Guard {
|
||||
private final WiredModemElement element;
|
||||
private final IPeripheral peripheral;
|
||||
private final IComputerAccess computer;
|
||||
@ -309,6 +318,8 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
private volatile boolean attached;
|
||||
private final Set<String> mounts = new HashSet<>();
|
||||
|
||||
private @Nullable GuardedLuaContext contextWrapper;
|
||||
|
||||
RemotePeripheralWrapper(WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name, Map<String, PeripheralMethod> methods) {
|
||||
this.element = element;
|
||||
this.peripheral = peripheral;
|
||||
@ -356,7 +367,19 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
|
||||
public MethodResult callMethod(ILuaContext context, String methodName, IArguments arguments) throws LuaException {
|
||||
var method = methodMap.get(methodName);
|
||||
if (method == null) throw new LuaException("No such method " + methodName);
|
||||
return method.apply(peripheral, context, this, arguments);
|
||||
|
||||
// Wrap the ILuaContext. We try to reuse the previous context where possible to avoid allocations.
|
||||
var contextWrapper = this.contextWrapper;
|
||||
if (contextWrapper == null || !contextWrapper.wraps(context)) {
|
||||
contextWrapper = this.contextWrapper = new GuardedLuaContext(context, this);
|
||||
}
|
||||
|
||||
return method.apply(peripheral, contextWrapper, this, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkValid() {
|
||||
return attached;
|
||||
}
|
||||
|
||||
// IComputerAccess implementation
|
||||
|
@ -56,8 +56,11 @@ public final class ClientMonitor {
|
||||
|
||||
void read(TerminalState state) {
|
||||
if (state.hasTerminal()) {
|
||||
if (terminal == null) terminal = new NetworkedTerminal(state.width, state.height, state.colour);
|
||||
state.apply(terminal);
|
||||
if (terminal == null) {
|
||||
terminal = state.create();
|
||||
} else {
|
||||
state.apply(terminal);
|
||||
}
|
||||
terminalChanged = true;
|
||||
} else {
|
||||
if (terminal != null) {
|
||||
|
@ -37,7 +37,7 @@ class DfpwmState {
|
||||
private boolean unplayed = true;
|
||||
private long clientEndTime = PauseAwareTimer.getTime();
|
||||
private float pendingVolume = 1.0f;
|
||||
private @Nullable ByteBuffer pendingAudio;
|
||||
private @Nullable EncodedAudio pendingAudio;
|
||||
|
||||
synchronized boolean pushBuffer(LuaTable<?, ?> table, int size, Optional<Double> volume) throws LuaException {
|
||||
if (pendingAudio != null) return false;
|
||||
@ -45,6 +45,10 @@ class DfpwmState {
|
||||
var outSize = size / 8;
|
||||
var buffer = ByteBuffer.allocate(outSize);
|
||||
|
||||
var initialCharge = charge;
|
||||
var initialStrength = strength;
|
||||
var initialPreviousBit = previousBit;
|
||||
|
||||
for (var i = 0; i < outSize; i++) {
|
||||
var thisByte = 0;
|
||||
for (var j = 1; j <= 8; j++) {
|
||||
@ -80,7 +84,7 @@ class DfpwmState {
|
||||
|
||||
buffer.flip();
|
||||
|
||||
pendingAudio = buffer;
|
||||
pendingAudio = new EncodedAudio(initialCharge, initialStrength, initialPreviousBit, buffer);
|
||||
pendingVolume = (float) clampVolume(volume.orElse((double) pendingVolume));
|
||||
return true;
|
||||
}
|
||||
@ -89,12 +93,12 @@ class DfpwmState {
|
||||
return pendingAudio != null && now >= clientEndTime - CLIENT_BUFFER;
|
||||
}
|
||||
|
||||
ByteBuffer pullPending(long now) {
|
||||
EncodedAudio pullPending(long now) {
|
||||
var audio = pendingAudio;
|
||||
if (audio == null) throw new IllegalStateException("Should not pull pending audio yet");
|
||||
pendingAudio = null;
|
||||
// Compute when we should consider sending the next packet.
|
||||
clientEndTime = Math.max(now, clientEndTime) + (audio.remaining() * SECOND * 8 / SAMPLE_RATE);
|
||||
clientEndTime = Math.max(now, clientEndTime) + (audio.audio().remaining() * SECOND * 8 / SAMPLE_RATE);
|
||||
unplayed = false;
|
||||
return audio;
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A chunk of encoded audio, along with the state required for the decoder to reproduce the original audio samples.
|
||||
*
|
||||
* @param charge The DFPWM charge.
|
||||
* @param strength The DFPWM strength.
|
||||
* @param previousBit The previous bit.
|
||||
* @param audio The block of encoded audio.
|
||||
*/
|
||||
public record EncodedAudio(int charge, int strength, boolean previousBit, ByteBuffer audio) {
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(charge());
|
||||
buf.writeVarInt(strength());
|
||||
buf.writeBoolean(previousBit());
|
||||
buf.writeBytes(audio().duplicate());
|
||||
}
|
||||
|
||||
public static EncodedAudio read(FriendlyByteBuf buf) {
|
||||
var charge = buf.readVarInt();
|
||||
var strength = buf.readVarInt();
|
||||
var previousBit = buf.readBoolean();
|
||||
|
||||
var bytes = new byte[buf.readableBytes()];
|
||||
buf.readBytes(bytes);
|
||||
|
||||
return new EncodedAudio(charge, strength, previousBit, ByteBuffer.wrap(bytes));
|
||||
}
|
||||
}
|
@ -18,16 +18,18 @@ import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
|
||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||
import net.minecraft.ResourceLocationException;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.item.RecordItem;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -252,15 +254,15 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
||||
var volume = (float) clampVolume(checkFinite(1, volumeA.orElse(1.0)));
|
||||
var pitch = (float) checkFinite(2, pitchA.orElse(1.0));
|
||||
|
||||
ResourceLocation identifier;
|
||||
try {
|
||||
identifier = new ResourceLocation(name);
|
||||
} catch (ResourceLocationException e) {
|
||||
throw new LuaException("Malformed sound name '" + name + "' ");
|
||||
}
|
||||
var identifier = ResourceLocation.tryParse(name);
|
||||
if (identifier == null) throw new LuaException("Malformed sound name '" + name + "' ");
|
||||
|
||||
// Prevent playing music discs.
|
||||
var soundEvent = PlatformHelper.get().tryGetRegistryObject(Registries.SOUND_EVENT, identifier);
|
||||
if (soundEvent != null && RecordItem.getBySound(soundEvent) != null) return false;
|
||||
|
||||
synchronized (lock) {
|
||||
if (dfpwmState != null && dfpwmState.isPlaying()) return false;
|
||||
if (pendingSound != null || (dfpwmState != null && dfpwmState.isPlaying())) return false;
|
||||
dfpwmState = null;
|
||||
pendingSound = new PendingSound<>(identifier, volume, pitch);
|
||||
return true;
|
||||
|
@ -389,4 +389,13 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
||||
* @see ServerPlayerGameMode#useItemOn(ServerPlayer, Level, ItemStack, InteractionHand, BlockHitResult)
|
||||
*/
|
||||
InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock);
|
||||
|
||||
/**
|
||||
* Whether {@link net.minecraft.network.chat.ClickEvent.Action#RUN_COMMAND} can be used to run client commands.
|
||||
*
|
||||
* @return Whether client commands can be triggered from chat components.
|
||||
*/
|
||||
default boolean canClickRunClientCommand() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -190,6 +190,6 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
@Override
|
||||
protected void onRemoved() {
|
||||
super.onRemoved();
|
||||
ServerNetworking.sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceID()), getLevel().getServer());
|
||||
ServerNetworking.sendToAllPlayers(new PocketComputerDeletedClientMessage(getInstanceUUID()), getLevel().getServer());
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import net.minecraft.world.level.Level;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PocketComputerItem extends Item implements IComputerItem, IMedia, IColouredItem {
|
||||
private static final String NBT_UPGRADE = "Upgrade";
|
||||
@ -188,10 +189,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
}
|
||||
|
||||
public PocketServerComputer createServerComputer(ServerLevel level, Entity entity, @Nullable Container inventory, ItemStack stack) {
|
||||
var sessionID = getSessionID(stack);
|
||||
|
||||
var registry = ServerContext.get(level.getServer()).registry();
|
||||
var computer = (PocketServerComputer) registry.get(sessionID, getInstanceID(stack));
|
||||
var computer = (PocketServerComputer) registry.get(getSessionID(stack), getInstanceID(stack));
|
||||
if (computer == null) {
|
||||
var computerID = getComputerID(stack);
|
||||
if (computerID < 0) {
|
||||
@ -201,8 +201,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
|
||||
computer = new PocketServerComputer(level, entity.blockPosition(), getComputerID(stack), getLabel(stack), getFamily());
|
||||
|
||||
setInstanceID(stack, computer.register());
|
||||
setSessionID(stack, registry.getSessionID());
|
||||
var tag = stack.getOrCreateTag();
|
||||
tag.putInt(NBT_SESSION, registry.getSessionID());
|
||||
tag.putUUID(NBT_INSTANCE, computer.register());
|
||||
|
||||
var upgrade = getUpgrade(stack);
|
||||
|
||||
@ -267,13 +268,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getInstanceID(ItemStack stack) {
|
||||
public static @Nullable UUID getInstanceID(ItemStack stack) {
|
||||
var nbt = stack.getTag();
|
||||
return nbt != null && nbt.contains(NBT_INSTANCE) ? nbt.getInt(NBT_INSTANCE) : -1;
|
||||
}
|
||||
|
||||
private static void setInstanceID(ItemStack stack, int instanceID) {
|
||||
stack.getOrCreateTag().putInt(NBT_INSTANCE, instanceID);
|
||||
return nbt != null && nbt.hasUUID(NBT_INSTANCE) ? nbt.getUUID(NBT_INSTANCE) : null;
|
||||
}
|
||||
|
||||
private static int getSessionID(ItemStack stack) {
|
||||
@ -281,10 +278,6 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
return nbt != null && nbt.contains(NBT_SESSION) ? nbt.getInt(NBT_SESSION) : -1;
|
||||
}
|
||||
|
||||
private static void setSessionID(ItemStack stack, int sessionID) {
|
||||
stack.getOrCreateTag().putInt(NBT_SESSION, sessionID);
|
||||
}
|
||||
|
||||
private static boolean isMarkedOn(ItemStack stack) {
|
||||
var nbt = stack.getTag();
|
||||
return nbt != null && nbt.getBoolean(NBT_ON);
|
||||
|
@ -24,10 +24,9 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
@ -136,17 +135,8 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
super.loadServer(nbt);
|
||||
|
||||
// Read inventory
|
||||
var nbttaglist = nbt.getList("Items", Tag.TAG_COMPOUND);
|
||||
inventory.clear();
|
||||
inventorySnapshot.clear();
|
||||
for (var i = 0; i < nbttaglist.size(); i++) {
|
||||
var tag = nbttaglist.getCompound(i);
|
||||
var slot = tag.getByte("Slot") & 0xff;
|
||||
if (slot < getContainerSize()) {
|
||||
inventory.set(slot, ItemStack.of(tag));
|
||||
inventorySnapshot.set(slot, inventory.get(slot).copy());
|
||||
}
|
||||
}
|
||||
ContainerHelper.loadAllItems(nbt, inventory);
|
||||
for (var i = 0; i < inventory.size(); i++) inventorySnapshot.set(i, inventory.get(i).copy());
|
||||
|
||||
// Read state
|
||||
brain.readFromNBT(nbt);
|
||||
@ -155,16 +145,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
@Override
|
||||
public void saveAdditional(CompoundTag nbt) {
|
||||
// Write inventory
|
||||
var nbttaglist = new ListTag();
|
||||
for (var i = 0; i < INVENTORY_SIZE; i++) {
|
||||
if (!inventory.get(i).isEmpty()) {
|
||||
var tag = new CompoundTag();
|
||||
tag.putByte("Slot", (byte) i);
|
||||
inventory.get(i).save(tag);
|
||||
nbttaglist.add(tag);
|
||||
}
|
||||
}
|
||||
nbt.put("Items", nbttaglist);
|
||||
ContainerHelper.saveAllItems(nbt, inventory);
|
||||
|
||||
// Write brain
|
||||
nbt = brain.writeToNBT(nbt);
|
||||
|
@ -14,6 +14,7 @@ import dan200.computercraft.api.turtle.TurtleCommand;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.util.PeripheralHelpers;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
@ -589,7 +590,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
}
|
||||
|
||||
var existing = peripherals.get(side);
|
||||
if (existing == peripheral || (existing != null && peripheral != null && existing.equals(peripheral))) {
|
||||
if (PeripheralHelpers.equals(existing, peripheral)) {
|
||||
// If the peripheral is the same, just use that.
|
||||
peripheral = existing;
|
||||
} else {
|
||||
|
@ -54,7 +54,7 @@ public class TurtleSuckCommand implements TurtleCommand {
|
||||
case ContainerTransfer.NO_SPACE:
|
||||
return TurtleCommandResult.failure("No space for items");
|
||||
case ContainerTransfer.NO_ITEMS:
|
||||
return TurtleCommandResult.failure("No items to drop");
|
||||
return TurtleCommandResult.failure("No items to take");
|
||||
default:
|
||||
turtle.playAnimation(TurtleAnimation.WAIT);
|
||||
return TurtleCommandResult.success();
|
||||
|
@ -11,6 +11,11 @@ public final class DirectionUtil {
|
||||
private DirectionUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A bitmask indicating all sides.
|
||||
*/
|
||||
public static final int ALL_SIDES = (1 << 6) - 1;
|
||||
|
||||
public static final Direction[] FACINGS = Direction.values();
|
||||
|
||||
public static ComputerSide toLocal(Direction front, Direction dir) {
|
||||
@ -31,4 +36,15 @@ public final class DirectionUtil {
|
||||
default -> 0.0f;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a direction is in a bitmask.
|
||||
*
|
||||
* @param mask The bitmask to test
|
||||
* @param direction The direction to check.
|
||||
* @return Whether the direction is in a bitmask.
|
||||
*/
|
||||
public static boolean isSet(int mask, Direction direction) {
|
||||
return (mask & (1 << direction.ordinal())) != 0;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ accessWidener v1 named
|
||||
# Additional access wideners for vanilla code. This is a effectively the subset of Fabric's transitive access wideners
|
||||
# that we actually use
|
||||
|
||||
accessible method net/minecraft/client/renderer/item/ItemProperties register (Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/renderer/item/ClampedItemPropertyFunction;)V
|
||||
accessible method net/minecraft/client/renderer/blockentity/BlockEntityRenderers register (Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/client/renderer/blockentity/BlockEntityRendererProvider;)V
|
||||
accessible class net/minecraft/world/item/CreativeModeTab$Output
|
||||
accessible field net/minecraft/world/item/CreativeModeTabs OP_BLOCKS Lnet/minecraft/resources/ResourceKey;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.client.sound;
|
||||
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@ -16,7 +17,7 @@ public class DfpwmStreamTest {
|
||||
var stream = new DfpwmStream();
|
||||
|
||||
var input = ByteBuffer.wrap(new byte[]{ 43, -31, 33, 44, 30, -16, -85, 23, -3, -55, 46, -70, 68, -67, 74, -96, -68, 16, 94, -87, -5, 87, 11, -16, 19, 92, 85, -71, 126, 5, -84, 64, 17, -6, 85, -11, -1, -87, -12, 1, 85, -56, 33, -80, 82, 104, -93, 17, 126, 23, 91, -30, 37, -32, 117, -72, -58, 11, -76, 19, -108, 86, -65, -10, -1, -68, -25, 10, -46, 85, 124, -54, 15, -24, 43, -94, 117, 63, -36, 15, -6, 88, 87, -26, -83, 106, 41, 13, -28, -113, -10, -66, 119, -87, -113, 68, -55, 40, -107, 62, 20, 72, 3, -96, 114, -87, -2, 39, -104, 30, 20, 42, 84, 24, 47, 64, 43, 61, -35, 95, -65, 42, 61, 42, -50, 4, -9, 81 });
|
||||
stream.push(input);
|
||||
stream.push(new EncodedAudio(0, 0, false, input));
|
||||
|
||||
var buffer = stream.read(1024 + 1);
|
||||
assertEquals(1024, buffer.remaining(), "Must have read 1024 bytes");
|
||||
|
@ -22,6 +22,7 @@ import org.junit.jupiter.params.provider.MethodSource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
@ -39,19 +40,19 @@ class ComputerSelectorTest {
|
||||
public static Arguments[] getArgumentTestCases() {
|
||||
return new Arguments[]{
|
||||
// Legacy selectors
|
||||
Arguments.of("@some_label", new ComputerSelector("@some_label", OptionalInt.empty(), OptionalInt.empty(), "some_label", null, null, null)),
|
||||
Arguments.of("~normal", new ComputerSelector("~normal", OptionalInt.empty(), OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
||||
Arguments.of("#123", new ComputerSelector("#123", OptionalInt.empty(), OptionalInt.of(123), null, null, null, null)),
|
||||
Arguments.of("123", new ComputerSelector("123", OptionalInt.of(123), OptionalInt.empty(), null, null, null, null)),
|
||||
Arguments.of("@some_label", new ComputerSelector("@some_label", OptionalInt.empty(), null, OptionalInt.empty(), "some_label", null, null, null)),
|
||||
Arguments.of("~normal", new ComputerSelector("~normal", OptionalInt.empty(), null, OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
||||
Arguments.of("#123", new ComputerSelector("#123", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
|
||||
Arguments.of("123", new ComputerSelector("123", OptionalInt.of(123), null, OptionalInt.empty(), null, null, null, null)),
|
||||
// New selectors
|
||||
Arguments.of("@c[]", new ComputerSelector("@c[]", OptionalInt.empty(), OptionalInt.empty(), null, null, null, null)),
|
||||
Arguments.of("@c[instance=123]", new ComputerSelector("@c[instance=123]", OptionalInt.of(123), OptionalInt.empty(), null, null, null, null)),
|
||||
Arguments.of("@c[id=123]", new ComputerSelector("@c[id=123]", OptionalInt.empty(), OptionalInt.of(123), null, null, null, null)),
|
||||
Arguments.of("@c[label=\"foo\"]", new ComputerSelector("@c[label=\"foo\"]", OptionalInt.empty(), OptionalInt.empty(), "foo", null, null, null)),
|
||||
Arguments.of("@c[family=normal]", new ComputerSelector("@c[family=normal]", OptionalInt.empty(), OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
||||
Arguments.of("@c[]", new ComputerSelector("@c[]", OptionalInt.empty(), null, OptionalInt.empty(), null, null, null, null)),
|
||||
Arguments.of("@c[instance=5e18f505-62f7-46f8-83f3-792f03224724]", new ComputerSelector("@c[instance=5e18f505-62f7-46f8-83f3-792f03224724]", OptionalInt.empty(), UUID.fromString("5e18f505-62f7-46f8-83f3-792f03224724"), OptionalInt.empty(), null, null, null, null)),
|
||||
Arguments.of("@c[id=123]", new ComputerSelector("@c[id=123]", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
|
||||
Arguments.of("@c[label=\"foo\"]", new ComputerSelector("@c[label=\"foo\"]", OptionalInt.empty(), null, OptionalInt.empty(), "foo", null, null, null)),
|
||||
Arguments.of("@c[family=normal]", new ComputerSelector("@c[family=normal]", OptionalInt.empty(), null, OptionalInt.empty(), null, ComputerFamily.NORMAL, null, null)),
|
||||
// Complex selectors
|
||||
Arguments.of("@c[ id = 123 , ]", new ComputerSelector("@c[ id = 123 , ]", OptionalInt.empty(), OptionalInt.of(123), null, null, null, null)),
|
||||
Arguments.of("@c[id=123,family=normal]", new ComputerSelector("@c[id=123,family=normal]", OptionalInt.empty(), OptionalInt.of(123), null, ComputerFamily.NORMAL, null, null)),
|
||||
Arguments.of("@c[ id = 123 , ]", new ComputerSelector("@c[ id = 123 , ]", OptionalInt.empty(), null, OptionalInt.of(123), null, null, null, null)),
|
||||
Arguments.of("@c[id=123,family=normal]", new ComputerSelector("@c[id=123,family=normal]", OptionalInt.empty(), null, OptionalInt.of(123), null, ComputerFamily.NORMAL, null, null)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,8 @@ import org.junit.jupiter.api.RepeatedTest;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
/**
|
||||
* Tests {@link TerminalState} round tripping works as expected.
|
||||
@ -42,6 +43,7 @@ public class TerminalStateTest {
|
||||
private static void checkEqual(Terminal expected, Terminal actual) {
|
||||
assertNotNull(expected, "Expected cannot be null");
|
||||
assertNotNull(actual, "Actual cannot be null");
|
||||
assertEquals(expected.isColour(), actual.isColour(), "isColour must match");
|
||||
assertEquals(expected.getHeight(), actual.getHeight(), "Heights must match");
|
||||
assertEquals(expected.getWidth(), actual.getWidth(), "Widths must match");
|
||||
|
||||
@ -51,13 +53,6 @@ public class TerminalStateTest {
|
||||
}
|
||||
|
||||
private static NetworkedTerminal read(FriendlyByteBuf buffer) {
|
||||
var state = new TerminalState(buffer);
|
||||
assertTrue(state.colour);
|
||||
|
||||
if (!state.hasTerminal()) return null;
|
||||
|
||||
var other = new NetworkedTerminal(state.width, state.height, true);
|
||||
state.apply(other);
|
||||
return other;
|
||||
return new TerminalState(buffer).create();
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class DfpwmStateTest {
|
||||
|
||||
var state = new DfpwmState();
|
||||
state.pushBuffer(new ObjectLuaTable(inputTbl), input.length, Optional.empty());
|
||||
var result = state.pullPending(0);
|
||||
var result = state.pullPending(0).audio();
|
||||
var contents = new byte[result.remaining()];
|
||||
result.get(contents);
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.peripheral.speaker;
|
||||
|
||||
import dan200.computercraft.test.core.ArbitraryByteBuffer;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.jqwik.api.*;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class EncodedAudioTest {
|
||||
/**
|
||||
* Sends the audio on a roundtrip, ensuring that its contents are reassembled on the other end.
|
||||
*
|
||||
* @param audio The message to send.
|
||||
*/
|
||||
@Property
|
||||
public void testRoundTrip(@ForAll("audio") EncodedAudio audio) {
|
||||
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
|
||||
audio.write(buffer);
|
||||
|
||||
var converted = EncodedAudio.read(buffer);
|
||||
assertEquals(buffer.readableBytes(), 0, "Whole packet was read");
|
||||
|
||||
assertThat("Messages are equal", converted, equalTo(converted));
|
||||
}
|
||||
|
||||
@Provide
|
||||
Arbitrary<EncodedAudio> audio() {
|
||||
return Combinators.combine(
|
||||
Arbitraries.integers(),
|
||||
Arbitraries.integers(),
|
||||
Arbitraries.of(true, false),
|
||||
ArbitraryByteBuffer.bytes().ofMaxSize(1000)
|
||||
).as(EncodedAudio::new);
|
||||
}
|
||||
}
|
@ -11,11 +11,15 @@ import dan200.computercraft.core.apis.TermAPI
|
||||
import dan200.computercraft.core.computer.ComputerSide
|
||||
import dan200.computercraft.gametest.api.*
|
||||
import dan200.computercraft.shared.ModRegistry
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import dan200.computercraft.test.core.computer.getApi
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.LeverBlock
|
||||
import net.minecraft.world.level.block.RedstoneLampBlock
|
||||
@ -101,6 +105,17 @@ class Computer_Test {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check chest peripherals are reattached with a new size.
|
||||
*/
|
||||
@GameTest
|
||||
fun Chest_resizes_on_change(context: GameTestHelper) = context.sequence {
|
||||
thenOnComputer { callPeripheral("right", "size").assertArrayEquals(27) }
|
||||
thenExecute { context.placeItemAt(ItemStack(Items.CHEST), BlockPos(2, 2, 2), Direction.WEST) }
|
||||
thenIdle(1)
|
||||
thenOnComputer { callPeripheral("right", "size").assertArrayEquals(54) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the client can open the computer UI and interact with it.
|
||||
*/
|
||||
|
@ -7,18 +7,21 @@ package dan200.computercraft.gametest
|
||||
import dan200.computercraft.api.lua.ObjectArguments
|
||||
import dan200.computercraft.core.apis.PeripheralAPI
|
||||
import dan200.computercraft.core.computer.ComputerSide
|
||||
import dan200.computercraft.gametest.api.getBlockEntity
|
||||
import dan200.computercraft.gametest.api.sequence
|
||||
import dan200.computercraft.gametest.api.thenOnComputer
|
||||
import dan200.computercraft.gametest.api.thenStartComputer
|
||||
import dan200.computercraft.gametest.api.*
|
||||
import dan200.computercraft.impl.network.wired.WiredNodeImpl
|
||||
import dan200.computercraft.shared.ModRegistry
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import dan200.computercraft.test.core.computer.LuaTaskContext
|
||||
import dan200.computercraft.test.core.computer.getApi
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@ -83,7 +86,86 @@ class Modem_Test {
|
||||
thenExecute {
|
||||
val modem1 = helper.getBlockEntity(BlockPos(1, 2, 1), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get())
|
||||
val modem2 = helper.getBlockEntity(BlockPos(3, 2, 1), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get())
|
||||
assertEquals(modem1.element.node.network, modem2.element.node.network, "On the same network")
|
||||
assertEquals((modem1.element.node as WiredNodeImpl).network, (modem2.element.node as WiredNodeImpl).network, "On the same network")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modems do not include the current peripheral when attached.
|
||||
*/
|
||||
@GameTest
|
||||
fun Cable_modem_does_not_report_self(helper: GameTestHelper) = helper.sequence {
|
||||
// Modem does not report the computer as a peripheral.
|
||||
thenOnComputer { assertEquals(listOf("back", "right"), getPeripheralNames()) }
|
||||
|
||||
// However, if we connect the network, the other modem does.
|
||||
thenExecute {
|
||||
helper.setBlock(
|
||||
BlockPos(1, 2, 3),
|
||||
ModRegistry.Blocks.CABLE.get().defaultBlockState().setValue(CableBlock.CABLE, true),
|
||||
)
|
||||
}
|
||||
thenIdle(2)
|
||||
thenOnComputer { assertEquals(listOf("back", "computer_0", "right"), getPeripheralNames()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Modems do not include the current peripheral when attached.
|
||||
*/
|
||||
@GameTest
|
||||
fun Full_block_modem_does_not_report_self(helper: GameTestHelper) = helper.sequence {
|
||||
// Modem does not report the computer as a peripheral.
|
||||
thenOnComputer { assertEquals(listOf("back", "right"), getPeripheralNames()) }
|
||||
|
||||
// However, if we connect the network, the other modem does.
|
||||
thenExecute {
|
||||
helper.setBlock(
|
||||
BlockPos(1, 2, 3),
|
||||
ModRegistry.Blocks.CABLE.get().defaultBlockState().setValue(CableBlock.CABLE, true),
|
||||
)
|
||||
}
|
||||
thenIdle(2)
|
||||
thenOnComputer { assertEquals(listOf("back", "computer_1", "right"), getPeripheralNames()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Test wired modems (without a cable) drop an item when the adjacent block is removed.
|
||||
*/
|
||||
@GameTest
|
||||
fun Modem_drops_when_neighbour_removed(helper: GameTestHelper) = helper.sequence {
|
||||
thenExecute {
|
||||
helper.setBlock(BlockPos(2, 3, 2), Blocks.AIR)
|
||||
helper.assertItemEntityPresent(ModRegistry.Items.WIRED_MODEM.get(), BlockPos(2, 2, 2), 0.0)
|
||||
helper.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 2))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test wired modems (with a cable) drop an item, but keep their cable when the adjacent block is removed.
|
||||
*/
|
||||
@GameTest
|
||||
fun Modem_keeps_cable_when_neighbour_removed(helper: GameTestHelper) = helper.sequence {
|
||||
thenExecute {
|
||||
helper.setBlock(BlockPos(2, 3, 2), Blocks.AIR)
|
||||
helper.assertItemEntityPresent(ModRegistry.Items.WIRED_MODEM.get(), BlockPos(2, 2, 2), 0.0)
|
||||
helper.assertBlockIs(BlockPos(2, 2, 2)) {
|
||||
it.block == ModRegistry.Blocks.CABLE.get() && it.getValue(CableBlock.MODEM) == CableModemVariant.None && it.getValue(CableBlock.CABLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check chest peripherals are reattached with a new size.
|
||||
*/
|
||||
@GameTest
|
||||
fun Chest_resizes_on_change(context: GameTestHelper) = context.sequence {
|
||||
thenOnComputer {
|
||||
callRemotePeripheral("minecraft:chest_0", "size").assertArrayEquals(27)
|
||||
}
|
||||
thenExecute { context.placeItemAt(ItemStack(Items.CHEST), BlockPos(2, 2, 2), Direction.WEST) }
|
||||
thenIdle(1)
|
||||
thenOnComputer {
|
||||
callRemotePeripheral("minecraft:chest_0", "size").assertArrayEquals(54)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,7 +187,7 @@ private suspend fun LuaTaskContext.getPeripheralNames(): List<String> {
|
||||
if (!peripheral.isPresent(side)) continue
|
||||
peripherals.add(side)
|
||||
|
||||
val hasType = peripheral.hasType(side, "modem")
|
||||
val hasType = peripheral.hasType(side, "peripheral_hub")
|
||||
if (hasType == null || hasType[0] != true) continue
|
||||
|
||||
val names = peripheral.call(context, ObjectArguments(side, "getNamesRemote")).await() ?: continue
|
||||
@ -116,3 +198,22 @@ private suspend fun LuaTaskContext.getPeripheralNames(): List<String> {
|
||||
peripherals.sort()
|
||||
return peripherals
|
||||
}
|
||||
|
||||
private suspend fun LuaTaskContext.callRemotePeripheral(name: String, method: String, vararg args: Any): Array<out Any?>? {
|
||||
val peripheral = getApi<PeripheralAPI>()
|
||||
if (peripheral.isPresent(name)) return peripheral.call(context, ObjectArguments(name, method, *args)).await()
|
||||
|
||||
for (side in ComputerSide.NAMES) {
|
||||
if (!peripheral.isPresent(side)) continue
|
||||
|
||||
val hasType = peripheral.hasType(side, "peripheral_hub")
|
||||
if (hasType == null || hasType[0] != true) continue
|
||||
|
||||
val isPresent = peripheral.call(context, ObjectArguments(side, "isPresentRemote", name)).await() ?: continue
|
||||
if (isPresent[0] as Boolean) {
|
||||
return peripheral.call(context, ObjectArguments(side, "callRemote", name, method, *args)).await()
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("No such peripheral $name")
|
||||
}
|
||||
|
@ -38,10 +38,10 @@ class Pocket_Computer_Test {
|
||||
// And ensure its synced to the client.
|
||||
thenIdle(4)
|
||||
thenOnClient {
|
||||
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)
|
||||
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)!!
|
||||
assertEquals(ComputerState.ON, pocketComputer.state)
|
||||
|
||||
val term = pocketComputer.terminal
|
||||
val term = pocketComputer.terminal!!
|
||||
assertEquals("Hello, world!", term.getLine(0).toString().trim(), "Terminal contents is synced")
|
||||
}
|
||||
// Update the terminal contents again.
|
||||
@ -54,10 +54,10 @@ class Pocket_Computer_Test {
|
||||
// And ensure the new computer state and terminal are sent.
|
||||
thenIdle(4)
|
||||
thenOnClient {
|
||||
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)
|
||||
val pocketComputer = ClientPocketComputers.get(minecraft.player!!.mainHandItem)!!
|
||||
assertEquals(ComputerState.BLINKING, pocketComputer.state)
|
||||
|
||||
val term = pocketComputer.terminal
|
||||
val term = pocketComputer.terminal!!
|
||||
assertEquals("Updated text :)", term.getLine(0).toString().trim(), "Terminal contents is synced")
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.gametest.api.sequence
|
||||
import dan200.computercraft.gametest.api.thenOnComputer
|
||||
import dan200.computercraft.gametest.api.tryMultipleTimes
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import net.minecraft.gametest.framework.GameTest
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
|
||||
class Speaker_Test {
|
||||
/**
|
||||
* [SpeakerPeripheral.playSound] fails if there is already a sound queued.
|
||||
*/
|
||||
@GameTest
|
||||
fun Fails_to_play_multiple_sounds(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
callPeripheral("right", "playSound", SoundEvents.NOTE_BLOCK_HARP.key().location().toString())
|
||||
.assertArrayEquals(true)
|
||||
|
||||
tryMultipleTimes(2) { // We could technically call this a tick later, so try twice
|
||||
callPeripheral("right", "playSound", SoundEvents.NOTE_BLOCK_HARP.key().location().toString())
|
||||
.assertArrayEquals(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [SpeakerPeripheral.playSound] will not play records.
|
||||
*/
|
||||
@GameTest
|
||||
fun Will_not_play_record(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
callPeripheral("right", "playSound", SoundEvents.MUSIC_DISC_PIGSTEP.location.toString())
|
||||
.assertArrayEquals(false)
|
||||
}
|
||||
}
|
||||
}
|
@ -329,8 +329,6 @@ class Turtle_Test {
|
||||
|
||||
/**
|
||||
* Checks turtles can be cleaned in cauldrons.
|
||||
*
|
||||
* Currently not required as turtles can no longer right-click cauldrons.
|
||||
*/
|
||||
@GameTest
|
||||
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence {
|
||||
@ -643,7 +641,20 @@ class Turtle_Test {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Turtle sucking from items
|
||||
/**
|
||||
* `turtle.suck` only pulls for the current side.
|
||||
*/
|
||||
@GameTest
|
||||
fun Sided_suck(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
turtle.suckUp(Optional.empty()).await().assertArrayEquals(true)
|
||||
turtle.getItemDetail(context, Optional.empty(), Optional.empty()).await().assertArrayEquals(
|
||||
mapOf("name" to "minecraft:iron_ingot", "count" to 8),
|
||||
)
|
||||
|
||||
turtle.suckUp(Optional.empty()).await().assertArrayEquals(false, "No items to take")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render turtles as an item.
|
||||
|
@ -19,15 +19,19 @@ import net.minecraft.core.Direction
|
||||
import net.minecraft.gametest.framework.*
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.Container
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.EntityType
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.context.UseOnContext
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.entity.BarrelBlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.hamcrest.Matchers
|
||||
import org.hamcrest.StringDescription
|
||||
|
||||
@ -304,3 +308,29 @@ fun GameTestHelper.setContainerItem(pos: BlockPos, slot: Int, item: ItemStack) {
|
||||
container.setItem(slot, item)
|
||||
container.setChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative version ot [GameTestHelper.placeAt], which sets the player's held item first.
|
||||
*
|
||||
* This is required for compatibility with Forge, which uses the in-hand stack, rather than the stack requested.
|
||||
*/
|
||||
fun GameTestHelper.placeItemAt(stack: ItemStack, pos: BlockPos, direction: Direction) {
|
||||
val player = makeMockPlayer()
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, stack)
|
||||
val absolutePos = absolutePos(pos.relative(direction))
|
||||
val hit = BlockHitResult(Vec3.atCenterOf(absolutePos), direction, absolutePos, false)
|
||||
stack.useOn(UseOnContext(player, InteractionHand.MAIN_HAND, hit))
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a function multiple times until it succeeds.
|
||||
*/
|
||||
inline fun tryMultipleTimes(count: Int, action: () -> Unit) {
|
||||
for (remaining in count - 1 downTo 0) {
|
||||
try {
|
||||
action()
|
||||
} catch (e: AssertionError) {
|
||||
if (remaining == 0) throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ object TestHooks {
|
||||
Printer_Test::class.java,
|
||||
Printout_Test::class.java,
|
||||
Recipe_Test::class.java,
|
||||
Speaker_Test::class.java,
|
||||
Turtle_Test::class.java,
|
||||
)
|
||||
|
||||
|
138
projects/common/src/testMod/resources/data/cctest/structures/computer_test.chest_resizes_on_change.snbt
generated
Normal file
138
projects/common/src/testMod/resources/data/cctest/structures/computer_test.chest_resizes_on_change.snbt
generated
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "minecraft:chest{facing:north,type:single,waterlogged:false}", nbt: {Items: [], id: "minecraft:chest"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "computercraft:computer_normal{facing:north,state:on}", nbt: {ComputerId: 1, Label: "computer_test.chest_resizes_on_change", On: 1b, id: "computercraft:computer_normal"}},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"minecraft:chest{facing:north,type:single,waterlogged:false}",
|
||||
"computercraft:computer_normal{facing:north,state:on}"
|
||||
]
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "computercraft:cable{cable:true,down:false,east:true,modem:east_off_peripheral,north:false,south:false,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 1b, PeripheralId: 0, PeripheralType: "computer", id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:computer_normal{facing:north,state:on}", nbt: {ComputerId: 1, Label: "modem_test.cable_modem_does_not_report_self", On: 1b, id: "computercraft:computer_normal"}},
|
||||
{pos: [2, 1, 3], state: "computercraft:cable{cable:true,down:false,east:false,modem:north_off,north:true,south:false,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:east_off_peripheral,north:false,south:false,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:computer_normal{facing:north,state:on}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:north_off,north:true,south:false,up:false,waterlogged:false,west:false}"
|
||||
]
|
||||
}
|
139
projects/common/src/testMod/resources/data/cctest/structures/modem_test.chest_resizes_on_change.snbt
generated
Normal file
139
projects/common/src/testMod/resources/data/cctest/structures/modem_test.chest_resizes_on_change.snbt
generated
Normal file
@ -0,0 +1,139 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "minecraft:chest{facing:north,type:single,waterlogged:false}", nbt: {Items: [], id: "minecraft:chest"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "computercraft:computer_normal{facing:north,state:on}", nbt: {ComputerId: 1, Label: "modem_test.chest_resizes_on_change", On: 1b, id: "computercraft:computer_normal"}},
|
||||
{pos: [3, 1, 2], state: "computercraft:wired_modem_full{modem:false,peripheral:true}", nbt: {PeripheralId2: 2, PeripheralId4: 0, PeripheralType2: "computer", PeripheralType4: "minecraft:chest", id: "computercraft:wired_modem_full"}},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"minecraft:chest{facing:north,type:single,waterlogged:false}",
|
||||
"computercraft:computer_normal{facing:north,state:on}",
|
||||
"computercraft:wired_modem_full{modem:false,peripheral:true}"
|
||||
]
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "computercraft:wired_modem_full{modem:false,peripheral:true}", nbt: {PeripheralAccess: 1b, PeripheralId5: 1, PeripheralType5: "computer", id: "computercraft:wired_modem_full"}},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:computer_normal{facing:north,state:on}", nbt: {ComputerId: 1, Label: "modem_test.full_block_modem_does_not_report_self", On: 1b, id: "computercraft:computer_normal"}},
|
||||
{pos: [2, 1, 3], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:wired_modem_full{modem:false,peripheral:true}",
|
||||
"computercraft:computer_normal{facing:north,state:on}",
|
||||
"computercraft:wired_modem_full{modem:false,peripheral:false}"
|
||||
]
|
||||
}
|
@ -28,7 +28,7 @@
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "computercraft:printer{bottom:false,facing:north,top:false}", nbt: {Items: [], PageTitle: "", Printing: 0b, id: "computercraft:printer", term_bgColour: 15, term_cursorBlink: 0b, term_cursorX: 0, term_cursorY: 0, term_palette: [I; 1118481, 13388876, 5744206, 8349260, 3368652, 11691749, 5020082, 10066329, 5000268, 15905484, 8375321, 14605932, 10072818, 15040472, 15905331, 15790320], term_textBgColour_0: "fffffffffffffffffffffffff", term_textBgColour_1: "fffffffffffffffffffffffff", term_textBgColour_10: "fffffffffffffffffffffffff", term_textBgColour_11: "fffffffffffffffffffffffff", term_textBgColour_12: "fffffffffffffffffffffffff", term_textBgColour_13: "fffffffffffffffffffffffff", term_textBgColour_14: "fffffffffffffffffffffffff", term_textBgColour_15: "fffffffffffffffffffffffff", term_textBgColour_16: "fffffffffffffffffffffffff", term_textBgColour_17: "fffffffffffffffffffffffff", term_textBgColour_18: "fffffffffffffffffffffffff", term_textBgColour_19: "fffffffffffffffffffffffff", term_textBgColour_2: "fffffffffffffffffffffffff", term_textBgColour_20: "fffffffffffffffffffffffff", term_textBgColour_3: "fffffffffffffffffffffffff", term_textBgColour_4: "fffffffffffffffffffffffff", term_textBgColour_5: "fffffffffffffffffffffffff", term_textBgColour_6: "fffffffffffffffffffffffff", term_textBgColour_7: "fffffffffffffffffffffffff", term_textBgColour_8: "fffffffffffffffffffffffff", term_textBgColour_9: "fffffffffffffffffffffffff", term_textColour: 0, term_textColour_0: "0000000000000000000000000", term_textColour_1: "0000000000000000000000000", term_textColour_10: "0000000000000000000000000", term_textColour_11: "0000000000000000000000000", term_textColour_12: "0000000000000000000000000", term_textColour_13: "0000000000000000000000000", term_textColour_14: "0000000000000000000000000", term_textColour_15: "0000000000000000000000000", term_textColour_16: "0000000000000000000000000", term_textColour_17: "0000000000000000000000000", term_textColour_18: "0000000000000000000000000", term_textColour_19: "0000000000000000000000000", term_textColour_2: "0000000000000000000000000", term_textColour_20: "0000000000000000000000000", term_textColour_3: "0000000000000000000000000", term_textColour_4: "0000000000000000000000000", term_textColour_5: "0000000000000000000000000", term_textColour_6: "0000000000000000000000000", term_textColour_7: "0000000000000000000000000", term_textColour_8: "0000000000000000000000000", term_textColour_9: "0000000000000000000000000", term_text_0: " ", term_text_1: " ", term_text_10: " ", term_text_11: " ", term_text_12: " ", term_text_13: " ", term_text_14: " ", term_text_15: " ", term_text_16: " ", term_text_17: " ", term_text_18: " ", term_text_19: " ", term_text_2: " ", term_text_20: " ", term_text_3: " ", term_text_4: " ", term_text_5: " ", term_text_6: " ", term_text_7: " ", term_text_8: " ", term_text_9: " "}},
|
||||
{pos: [0, 1, 1], state: "computercraft:cable{cable:true,down:false,east:true,modem:north_on,north:true,south:false,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 1b, PeripheralId: 1, PeripheralType: "printer", id: "computercraft:cable"}},
|
||||
{pos: [0, 1, 1], state: "computercraft:cable{cable:true,down:false,east:true,modem:north_off_peripheral,north:true,south:false,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 1b, PeripheralId: 1, PeripheralType: "printer", id: "computercraft:cable"}},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:none}", nbt: {Height: 1, Width: 1, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||
@ -36,7 +36,7 @@
|
||||
{pos: [1, 1, 1], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 2], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 3], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 4], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 1b, PeripheralId: 1, PeripheralType: "monitor", id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 4], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_off_peripheral,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 1b, PeripheralId: 1, PeripheralType: "monitor", id: "computercraft:cable"}},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "minecraft:air"},
|
||||
@ -133,11 +133,11 @@
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:printer{bottom:false,facing:north,top:false}",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:north_on,north:true,south:false,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:north_off_peripheral,north:true,south:false,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:monitor_advanced{facing:north,orientation:north,state:none}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:west_off_peripheral,north:true,south:false,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:false,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:computer_advanced{facing:north,state:blinking}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:north_off,north:true,south:false,up:false,waterlogged:false,west:true}"
|
||||
|
@ -28,7 +28,7 @@
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "computercraft:printer{bottom:false,facing:north,top:false}", nbt: {Items: [], PageTitle: "", Printing: 0b, id: "computercraft:printer", term_bgColour: 15, term_cursorBlink: 0b, term_cursorX: 0, term_cursorY: 0, term_palette: [I; 1118481, 13388876, 5744206, 8349260, 3368652, 11691749, 5020082, 10066329, 5000268, 15905484, 8375321, 14605932, 10072818, 15040472, 15905331, 15790320], term_textBgColour_0: "fffffffffffffffffffffffff", term_textBgColour_1: "fffffffffffffffffffffffff", term_textBgColour_10: "fffffffffffffffffffffffff", term_textBgColour_11: "fffffffffffffffffffffffff", term_textBgColour_12: "fffffffffffffffffffffffff", term_textBgColour_13: "fffffffffffffffffffffffff", term_textBgColour_14: "fffffffffffffffffffffffff", term_textBgColour_15: "fffffffffffffffffffffffff", term_textBgColour_16: "fffffffffffffffffffffffff", term_textBgColour_17: "fffffffffffffffffffffffff", term_textBgColour_18: "fffffffffffffffffffffffff", term_textBgColour_19: "fffffffffffffffffffffffff", term_textBgColour_2: "fffffffffffffffffffffffff", term_textBgColour_20: "fffffffffffffffffffffffff", term_textBgColour_3: "fffffffffffffffffffffffff", term_textBgColour_4: "fffffffffffffffffffffffff", term_textBgColour_5: "fffffffffffffffffffffffff", term_textBgColour_6: "fffffffffffffffffffffffff", term_textBgColour_7: "fffffffffffffffffffffffff", term_textBgColour_8: "fffffffffffffffffffffffff", term_textBgColour_9: "fffffffffffffffffffffffff", term_textColour: 0, term_textColour_0: "0000000000000000000000000", term_textColour_1: "0000000000000000000000000", term_textColour_10: "0000000000000000000000000", term_textColour_11: "0000000000000000000000000", term_textColour_12: "0000000000000000000000000", term_textColour_13: "0000000000000000000000000", term_textColour_14: "0000000000000000000000000", term_textColour_15: "0000000000000000000000000", term_textColour_16: "0000000000000000000000000", term_textColour_17: "0000000000000000000000000", term_textColour_18: "0000000000000000000000000", term_textColour_19: "0000000000000000000000000", term_textColour_2: "0000000000000000000000000", term_textColour_20: "0000000000000000000000000", term_textColour_3: "0000000000000000000000000", term_textColour_4: "0000000000000000000000000", term_textColour_5: "0000000000000000000000000", term_textColour_6: "0000000000000000000000000", term_textColour_7: "0000000000000000000000000", term_textColour_8: "0000000000000000000000000", term_textColour_9: "0000000000000000000000000", term_text_0: " ", term_text_1: " ", term_text_10: " ", term_text_11: " ", term_text_12: " ", term_text_13: " ", term_text_14: " ", term_text_15: " ", term_text_16: " ", term_text_17: " ", term_text_18: " ", term_text_19: " ", term_text_2: " ", term_text_20: " ", term_text_3: " ", term_text_4: " ", term_text_5: " ", term_text_6: " ", term_text_7: " ", term_text_8: " ", term_text_9: " "}},
|
||||
{pos: [0, 1, 1], state: "computercraft:cable{cable:true,down:false,east:false,modem:north_on,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 1b, PeripheralId: 0, PeripheralType: "printer", id: "computercraft:cable"}},
|
||||
{pos: [0, 1, 1], state: "computercraft:cable{cable:true,down:false,east:false,modem:north_off_peripheral,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 1b, PeripheralId: 0, PeripheralType: "printer", id: "computercraft:cable"}},
|
||||
{pos: [0, 1, 2], state: "computercraft:cable{cable:true,down:false,east:true,modem:none,north:true,south:false,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:none}", nbt: {Height: 1, Width: 1, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||
@ -36,7 +36,7 @@
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 3], state: "computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 4], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 1b, PeripheralId: 0, PeripheralType: "monitor", id: "computercraft:cable"}},
|
||||
{pos: [1, 1, 4], state: "computercraft:cable{cable:true,down:false,east:false,modem:west_off_peripheral,north:true,south:false,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 1b, PeripheralId: 0, PeripheralType: "monitor", id: "computercraft:cable"}},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:false,up:false,waterlogged:false,west:true}", nbt: {PeripheralAccess: 0b, id: "computercraft:cable"}},
|
||||
@ -133,12 +133,12 @@
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:printer{bottom:false,facing:north,top:false}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:north_on,north:true,south:true,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:north_off_peripheral,north:true,south:true,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:true,south:false,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:monitor_advanced{facing:north,orientation:north,state:none}",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:true,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:none,north:true,south:true,up:false,waterlogged:false,west:false}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:west_on,north:true,south:false,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:west_off_peripheral,north:true,south:false,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:none,north:false,south:false,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:cable{cable:true,down:false,east:true,modem:east_off,north:false,south:false,up:false,waterlogged:false,west:true}",
|
||||
"computercraft:computer_advanced{facing:north,state:blinking}"
|
||||
|
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 1, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 1, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 1, 2], state: "computercraft:cable{cable:false,down:false,east:false,modem:up_off,north:false,south:false,up:false,waterlogged:false,west:false}", nbt: {id: "computercraft:cable"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 1, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 1, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 2, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 2, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 2, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 2, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 2, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 2, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:light_gray_stained_glass",
|
||||
"minecraft:air",
|
||||
"computercraft:cable{cable:false,down:false,east:false,modem:up_off,north:false,south:false,up:false,waterlogged:false,west:false}"
|
||||
]
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 1, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 1, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 1, 2], state: "computercraft:cable{cable:true,down:false,east:false,modem:up_off,north:false,south:false,up:true,waterlogged:false,west:false}", nbt: {id: "computercraft:cable"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 1, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 1, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 2, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 2, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 2, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 2, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 2, 2], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 2, 3], state: "minecraft:light_gray_stained_glass"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:light_gray_stained_glass",
|
||||
"minecraft:air",
|
||||
"computercraft:cable{cable:true,down:false,east:false,modem:up_off,north:false,south:false,up:true,waterlogged:false,west:false}"
|
||||
]
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "computercraft:speaker{facing:north}", nbt: {id: "computercraft:speaker"}},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:computer_normal{facing:north,state:on}", nbt: {ComputerId: 1, Label: "speaker_test.fails_to_play_multiple_sounds", On: 1b, id: "computercraft:computer_normal"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:speaker{facing:north}",
|
||||
"computercraft:computer_normal{facing:north,state:on}"
|
||||
]
|
||||
}
|
138
projects/common/src/testMod/resources/data/cctest/structures/speaker_test.will_not_play_record.snbt
generated
Normal file
138
projects/common/src/testMod/resources/data/cctest/structures/speaker_test.will_not_play_record.snbt
generated
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "computercraft:speaker{facing:north}", nbt: {id: "computercraft:speaker"}},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:computer_normal{facing:north,state:on}", nbt: {ComputerId: 1, Label: "speaker_test.will_not_play_record", On: 1b, id: "computercraft:computer_normal"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:speaker{facing:north}",
|
||||
"computercraft:computer_normal{facing:north,state:on}"
|
||||
]
|
||||
}
|
138
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.sided_suck.snbt
generated
Normal file
138
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.sided_suck.snbt
generated
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [5, 5, 5],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:north,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [], Label: "turtle_test.sided_suck", On: 1b, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:furnace{facing:north,lit:false}", nbt: {BurnTime: 0s, CookTime: 0s, CookTimeTotal: 200s, Items: [{Count: 64b, Slot: 1b, id: "minecraft:coal"}, {Count: 8b, Slot: 2b, id: "minecraft:iron_ingot"}], RecipesUsed: {"minecraft:iron_ingot_from_smelting_raw_iron": 8}, id: "minecraft:furnace"}},
|
||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:turtle_normal{facing:north,waterlogged:false}",
|
||||
"minecraft:furnace{facing:north,lit:false}"
|
||||
]
|
||||
}
|
@ -143,27 +143,27 @@ public class OSAPI implements ILuaAPI {
|
||||
|
||||
/**
|
||||
* Starts a timer that will run for the specified number of seconds. Once
|
||||
* the timer fires, a {@code timer} event will be added to the queue with
|
||||
* the ID returned from this function as the first parameter.
|
||||
* the timer fires, a [`timer`] event will be added to the queue with the ID
|
||||
* returned from this function as the first parameter.
|
||||
* <p>
|
||||
* As with [sleep][`os.sleep`], {@code timer} will automatically be rounded up
|
||||
* to the nearest multiple of 0.05 seconds, as it waits for a fixed amount
|
||||
* of world ticks.
|
||||
* As with [sleep][`os.sleep`], the time will automatically be rounded up to
|
||||
* the nearest multiple of 0.05 seconds, as it waits for a fixed amount of
|
||||
* world ticks.
|
||||
*
|
||||
* @param timer The number of seconds until the timer fires.
|
||||
* @return The ID of the new timer. This can be used to filter the
|
||||
* {@code timer} event, or {@link #cancelTimer cancel the timer}.
|
||||
* @param time The number of seconds until the timer fires.
|
||||
* @return The ID of the new timer. This can be used to filter the [`timer`]
|
||||
* event, or {@linkplain #cancelTimer cancel the timer}.
|
||||
* @throws LuaException If the time is below zero.
|
||||
* @see #cancelTimer To cancel a timer.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int startTimer(double timer) throws LuaException {
|
||||
return apiEnvironment.startTimer(Math.round(checkFinite(0, timer) / 0.05));
|
||||
public final int startTimer(double time) throws LuaException {
|
||||
return apiEnvironment.startTimer(Math.round(checkFinite(0, time) / 0.05));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a timer previously started with startTimer. This will stop the
|
||||
* timer from firing.
|
||||
* Cancels a timer previously started with {@link #startTimer(double)}. This
|
||||
* will stop the timer from firing.
|
||||
*
|
||||
* @param token The ID of the timer to cancel.
|
||||
* @cc.since 1.6
|
||||
@ -399,10 +399,9 @@ public class OSAPI implements ILuaAPI {
|
||||
* Returns a date string (or table) using a specified format string and
|
||||
* optional time to format.
|
||||
* <p>
|
||||
* The format string takes the same formats as C's {@code strftime} function
|
||||
* (http://www.cplusplus.com/reference/ctime/strftime/). In extension, it
|
||||
* can be prefixed with an exclamation mark ({@code !}) to use UTC time
|
||||
* instead of the server's local timezone.
|
||||
* The format string takes the same formats as C's [strftime](http://www.cplusplus.com/reference/ctime/strftime/)
|
||||
* function. The format string can also be prefixed with an exclamation mark
|
||||
* ({@code !}) to use UTC time instead of the server's local timezone.
|
||||
* <p>
|
||||
* If the format is exactly {@code *t} (optionally prefixed with {@code !}), a
|
||||
* table will be returned instead. This table has fields for the year, month,
|
||||
|
@ -11,6 +11,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.NotAttachedException;
|
||||
import dan200.computercraft.api.peripheral.WorkMonitor;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.computer.GuardedLuaContext;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
@ -26,7 +27,7 @@ import java.util.*;
|
||||
* @hidden
|
||||
*/
|
||||
public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener {
|
||||
private class PeripheralWrapper extends ComputerAccess {
|
||||
private class PeripheralWrapper extends ComputerAccess implements GuardedLuaContext.Guard {
|
||||
private final String side;
|
||||
private final IPeripheral peripheral;
|
||||
|
||||
@ -35,6 +36,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
private final Map<String, PeripheralMethod> methodMap;
|
||||
private boolean attached = false;
|
||||
|
||||
private @Nullable GuardedLuaContext contextWrapper;
|
||||
|
||||
PeripheralWrapper(IPeripheral peripheral, String side) {
|
||||
super(environment);
|
||||
this.side = side;
|
||||
@ -91,9 +94,21 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
|
||||
|
||||
if (method == null) throw new LuaException("No such method " + methodName);
|
||||
|
||||
try (var ignored = environment.time(Metrics.PERIPHERAL_OPS)) {
|
||||
return method.apply(peripheral, context, this, arguments);
|
||||
// Wrap the ILuaContext. We try to reuse the previous context where possible to avoid allocations - this
|
||||
// should be pretty common as ILuaMachine uses a constant context.
|
||||
var contextWrapper = this.contextWrapper;
|
||||
if (contextWrapper == null || !contextWrapper.wraps(context)) {
|
||||
contextWrapper = this.contextWrapper = new GuardedLuaContext(context, this);
|
||||
}
|
||||
|
||||
try (var ignored = environment.time(Metrics.PERIPHERAL_OPS)) {
|
||||
return method.apply(peripheral, contextWrapper, this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkValid() {
|
||||
return isAttached();
|
||||
}
|
||||
|
||||
// IComputerAccess implementation
|
||||
|
@ -11,6 +11,7 @@ import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||
import dan200.computercraft.core.filesystem.FileSystem;
|
||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.PeripheralHelpers;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
@ -268,9 +269,7 @@ public final class Environment implements IAPIEnvironment {
|
||||
synchronized (peripherals) {
|
||||
var index = side.ordinal();
|
||||
var existing = peripherals[index];
|
||||
if ((existing == null && peripheral != null) ||
|
||||
(existing != null && peripheral == null) ||
|
||||
(existing != null && !existing.equals(peripheral))) {
|
||||
if (!PeripheralHelpers.equals(existing, peripheral)) {
|
||||
peripherals[index] = peripheral;
|
||||
if (peripheralListener != null) peripheralListener.onPeripheralChanged(side, peripheral);
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.computer;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaTask;
|
||||
|
||||
/**
|
||||
* A {@link ILuaContext} which checks if context is valid when before executing
|
||||
* {@linkplain #issueMainThreadTask(LuaTask) main-thread tasks}.
|
||||
*/
|
||||
public final class GuardedLuaContext implements ILuaContext {
|
||||
private final ILuaContext original;
|
||||
private final Guard guard;
|
||||
|
||||
public GuardedLuaContext(ILuaContext original, Guard guard) {
|
||||
this.original = original;
|
||||
this.guard = guard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this {@link GuardedLuaContext} wraps another context.
|
||||
* <p>
|
||||
* This may be used to avoid constructing new guarded contexts, in a pattern something like:
|
||||
*
|
||||
* <pre>{@code
|
||||
* var contextWrapper = this.contextWrapper;
|
||||
* if(contextWrapper == null || !contextWrapper.wraps(context)) {
|
||||
* contextWrapper = this.contextWrapper = new GuardedLuaContext(context, this);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param context The original context.
|
||||
* @return Whether {@code this} wraps {@code context}.
|
||||
*/
|
||||
public boolean wraps(ILuaContext context) {
|
||||
return original == context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long issueMainThreadTask(LuaTask task) throws LuaException {
|
||||
return original.issueMainThreadTask(() -> guard.checkValid() ? task.execute() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function which checks if the context is still valid.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Guard {
|
||||
boolean checkValid();
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.util;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Utilities for working with {@linkplain IPeripheral peripherals}.
|
||||
*/
|
||||
public final class PeripheralHelpers {
|
||||
private PeripheralHelpers() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if two peripherals are equal. This is equivalent to {@link java.util.Objects#equals(Object, Object)},
|
||||
* but using {@link IPeripheral#equals(IPeripheral)} instead.
|
||||
*
|
||||
* @param a The first peripheral.
|
||||
* @param b The second peripheral.
|
||||
* @return If the two peripherals are equal.
|
||||
*/
|
||||
public static boolean equals(@Nullable IPeripheral a, @Nullable IPeripheral b) {
|
||||
return a == b || (a != null && b != null && a.equals(b));
|
||||
}
|
||||
}
|
@ -1,3 +1,24 @@
|
||||
# New features in CC: Tweaked 1.110.1
|
||||
|
||||
Several bug fixes:
|
||||
* Fix computers not turning on after they're unloaded/not-ticked for a while.
|
||||
* Fix networking cables sometimes not connecting on Forge.
|
||||
|
||||
# New features in CC: Tweaked 1.110.0
|
||||
|
||||
* Add a new `@c[...]` syntax for selecting computers in the `/computercraft` command.
|
||||
* Remove custom breaking progress of modems on Forge.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix client and server DFPWM transcoders getting out of sync.
|
||||
* Fix `turtle.suck` reporting incorrect error when failing to suck items.
|
||||
* Fix pocket computers displaying state (blinking, modem light) for the wrong computer.
|
||||
* Fix crash when wrapping an invalid BE as a generic peripheral.
|
||||
* Chest peripherals now reattach when a chest is converted into a double chest.
|
||||
* Fix `speaker` program not resolving files relative to the current directory.
|
||||
* Skip main-thread tasks if the peripheral is detached.
|
||||
* Fix internal Lua VM errors if yielding inside `__tostring`.
|
||||
|
||||
# New features in CC: Tweaked 1.109.7
|
||||
|
||||
* Improve performance of removing and unloading wired cables/modems.
|
||||
|
@ -1,11 +1,7 @@
|
||||
New features in CC: Tweaked 1.109.7
|
||||
|
||||
* Improve performance of removing and unloading wired cables/modems.
|
||||
New features in CC: Tweaked 1.110.1
|
||||
|
||||
Several bug fixes:
|
||||
* Fix monitors sometimes not updating on the client when chunks are unloaded and reloaded.
|
||||
* `colour.toBlit` correctly errors on out-of-bounds values.
|
||||
* Round non-standard colours in `window`, like `term.native()` does.
|
||||
* Fix the client monitor rendering both the current and outdated contents.
|
||||
* Fix computers not turning on after they're unloaded/not-ticked for a while.
|
||||
* Fix networking cables sometimes not connecting on Forge.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
@ -89,7 +89,7 @@ end
|
||||
--
|
||||
-- @tparam table image An image, as returned from [`load`] or [`parse`].
|
||||
-- @tparam number xPos The x position to start drawing at.
|
||||
-- @tparam number xPos The y position to start drawing at.
|
||||
-- @tparam number yPos The y position to start drawing at.
|
||||
-- @tparam[opt] term.Redirect target The terminal redirect to draw to. Defaults to the
|
||||
-- current terminal.
|
||||
local function draw(image, xPos, yPos, target)
|
||||
|
@ -43,6 +43,10 @@ if cmd == "stop" then
|
||||
for _, speaker in pairs(get_speakers(name)) do speaker.stop() end
|
||||
elseif cmd == "play" then
|
||||
local _, file, name = ...
|
||||
if not file then
|
||||
error("Usage: speaker play <file or url> [speaker]", 0)
|
||||
end
|
||||
|
||||
local speaker = get_speakers(name)[1]
|
||||
|
||||
if not file then
|
||||
@ -54,7 +58,7 @@ elseif cmd == "play" then
|
||||
print("Downloading...")
|
||||
handle, err = http.get(file)
|
||||
else
|
||||
handle, err = fs.open(file, "r")
|
||||
handle, err = fs.open(shell.resolve(file), "r")
|
||||
end
|
||||
|
||||
if not handle then
|
||||
|
@ -27,6 +27,7 @@ import net.fabricmc.fabric.api.event.client.player.ClientPickBlockGatherCallback
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
@ -45,7 +46,7 @@ public class ComputerCraftClient {
|
||||
ClientRegistry.register();
|
||||
ClientRegistry.registerTurtleModellers(FabricComputerCraftAPIClient::registerTurtleUpgradeModeller);
|
||||
ClientRegistry.registerItemColours(ColorProviderRegistry.ITEM::register);
|
||||
ClientRegistry.registerMainThread();
|
||||
ClientRegistry.registerMainThread(ItemProperties::register);
|
||||
|
||||
PreparableModelLoadingPlugin.register(CustomModelLoader::prepare, (state, context) -> {
|
||||
ClientRegistry.registerExtraModels(context::addModels);
|
||||
|
@ -13,6 +13,8 @@ import dan200.computercraft.shared.platform.FabricContainerTransfer;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.SlottedStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.base.CombinedSlottedStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.base.CombinedStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@ -42,6 +44,34 @@ public final class InventoryMethods extends AbstractInventoryMethods<InventoryMe
|
||||
* @param storage The underlying storage
|
||||
*/
|
||||
public record StorageWrapper(SlottedStorage<ItemVariant> storage) {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof StorageWrapper other)) return false;
|
||||
|
||||
var otherStorage = other.storage;
|
||||
|
||||
/*
|
||||
Equality for inventory storage isn't really defined, and most of the time falls back to reference
|
||||
equality.
|
||||
- Vanilla inventories are exposed via InventoryStorage - the creation of this is cached, so will be
|
||||
the same object.
|
||||
- Double chests are combined into a CombinedSlottedStorage. We check the parts are equal.
|
||||
*/
|
||||
if (
|
||||
storage instanceof CombinedSlottedStorage<?, ?> cs && storage.getClass() == otherStorage.getClass()
|
||||
&& cs.parts.equals(((CombinedStorage<?, ?>) otherStorage).parts)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return storage.equals(otherStorage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return storage instanceof CombinedSlottedStorage<?, ?> cs ? cs.parts.hashCode() : storage.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,12 +9,11 @@ import net.fabricmc.fabric.api.transfer.v1.storage.SlottedStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil;
|
||||
import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
|
||||
import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class FabricContainerTransfer implements ContainerTransfer {
|
||||
@ -34,37 +33,31 @@ public class FabricContainerTransfer implements ContainerTransfer {
|
||||
|
||||
@Override
|
||||
public int moveTo(ContainerTransfer destination, int maxAmount) {
|
||||
var predicate = new GatePredicate<ItemVariant>();
|
||||
var hasItem = false;
|
||||
|
||||
var moved = StorageUtil.move(storage, ((FabricContainerTransfer) destination).storage, predicate, maxAmount, null);
|
||||
if (moved > 0) return moved > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) moved;
|
||||
var destStorage = ((FabricContainerTransfer) destination).storage;
|
||||
for (var slot : storage.nonEmptyViews()) {
|
||||
var resource = slot.getResource();
|
||||
|
||||
// Nasty hack here to check if move() actually found an item in the original inventory. Saves having to
|
||||
// iterate over the source twice.
|
||||
return predicate.hasItem() ? NO_SPACE : NO_ITEMS;
|
||||
}
|
||||
try (var transaction = Transaction.openOuter()) {
|
||||
// Check how much can be extracted and inserted.
|
||||
var maxExtracted = StorageUtil.simulateExtract(slot, resource, maxAmount, transaction);
|
||||
if (maxExtracted == 0) continue;
|
||||
|
||||
/**
|
||||
* A predicate which accepts the first value it sees, and then only those matching that value.
|
||||
*
|
||||
* @param <T> The type of the object to accept.
|
||||
*/
|
||||
private static final class GatePredicate<T> implements Predicate<T> {
|
||||
private @Nullable T instance = null;
|
||||
hasItem = true;
|
||||
|
||||
@Override
|
||||
public boolean test(T o) {
|
||||
if (instance == null) {
|
||||
instance = o;
|
||||
return true;
|
||||
var accepted = destStorage.insert(resource, maxExtracted, transaction);
|
||||
if (accepted == 0) continue;
|
||||
|
||||
// Extract or rollback.
|
||||
if (slot.extract(resource, accepted, transaction) == accepted) {
|
||||
transaction.commit();
|
||||
return (int) accepted;
|
||||
}
|
||||
}
|
||||
|
||||
return instance.equals(o);
|
||||
}
|
||||
|
||||
boolean hasItem() {
|
||||
return instance != null;
|
||||
}
|
||||
return hasItem ? NO_SPACE : NO_ITEMS;
|
||||
}
|
||||
|
||||
private static final class SlottedImpl extends FabricContainerTransfer implements ContainerTransfer.Slotted {
|
||||
|
@ -8,7 +8,6 @@ import net.minecraftforge.gradle.common.util.RunConfig
|
||||
plugins {
|
||||
id("cc-tweaked.forge")
|
||||
id("cc-tweaked.gametest")
|
||||
alias(libs.plugins.mixinGradle)
|
||||
id("cc-tweaked.mod-publishing")
|
||||
}
|
||||
|
||||
@ -103,12 +102,6 @@ minecraft {
|
||||
}
|
||||
}
|
||||
|
||||
mixin {
|
||||
add(sourceSets.client.get(), "client-computercraft.refmap.json")
|
||||
|
||||
config("computercraft-client.forge.mixins.json")
|
||||
}
|
||||
|
||||
configurations {
|
||||
minecraftLibrary { extendsFrom(minecraftEmbed.get()) }
|
||||
|
||||
@ -121,9 +114,6 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor("org.spongepowered:mixin:0.8.5-SQUID:processor")
|
||||
clientAnnotationProcessor("org.spongepowered:mixin:0.8.5-SQUID:processor")
|
||||
|
||||
compileOnly(libs.jetbrainsAnnotations)
|
||||
annotationProcessorEverywhere(libs.autoService)
|
||||
|
||||
@ -246,12 +236,6 @@ modPublishing {
|
||||
output.set(tasks.jarJar)
|
||||
}
|
||||
|
||||
// Make sure configureReobfTaskForReobfJarJar runs after compilation
|
||||
// see - https://github.com/SpongePowered/MixinGradle/pull/51
|
||||
tasks.configureEach {
|
||||
if (name == "configureReobfTaskForReobfJarJar") mustRunAfter(tasks.jarJar)
|
||||
}
|
||||
|
||||
// Don't publish the slim jar
|
||||
for (cfg in listOf(configurations.apiElements, configurations.runtimeElements)) {
|
||||
cfg.configure { artifacts.removeIf { it.classifier == "slim" } }
|
||||
|
@ -8,6 +8,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent;
|
||||
import dan200.computercraft.client.model.turtle.TurtleModelLoader;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.item.ItemProperties;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ModelEvent;
|
||||
import net.minecraftforge.client.event.RegisterClientReloadListenersEvent;
|
||||
@ -82,6 +83,6 @@ public final class ForgeClientRegistry {
|
||||
@SubscribeEvent
|
||||
public static void setupClient(FMLClientSetupEvent event) {
|
||||
ClientRegistry.register();
|
||||
event.enqueueWork(ClientRegistry::registerMainThread);
|
||||
event.enqueueWork(() -> ClientRegistry.registerMainThread(ItemProperties::register));
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.mixin.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import dan200.computercraft.client.ClientHooks;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* Provides custom block breaking progress for modems, so it only applies to the current part.
|
||||
*
|
||||
* @see BlockRenderDispatcher#renderBreakingTexture(BlockState, BlockPos, BlockAndTintGetter, PoseStack, VertexConsumer, ModelData)
|
||||
*/
|
||||
@Mixin(BlockRenderDispatcher.class)
|
||||
public class BlockRenderDispatcherMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private RandomSource random;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private BlockModelShaper blockModelShaper;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private ModelBlockRenderer modelRenderer;
|
||||
|
||||
@Inject(
|
||||
method = "name=/^renderBreakingTexture/ desc=/ModelData;\\)V$/",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true,
|
||||
require = 0 // This isn't critical functionality, so don't worry if we can't apply it.
|
||||
)
|
||||
public void renderBlockDamage(
|
||||
BlockState state, BlockPos pos, BlockAndTintGetter world, PoseStack pose, VertexConsumer buffers, ModelData modelData,
|
||||
CallbackInfo info
|
||||
) {
|
||||
var newState = ClientHooks.getBlockBreakingState(state, pos);
|
||||
if (newState != null) {
|
||||
info.cancel();
|
||||
|
||||
var model = blockModelShaper.getBlockModel(newState);
|
||||
modelRenderer.tesselateBlock(
|
||||
world, model, newState, pos, pose, buffers, true, random, newState.getSeed(pos),
|
||||
OverlayTexture.NO_OVERLAY, modelData, null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.mixin.client;
|
||||
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraftforge.client.ClientCommandHandler;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/**
|
||||
* Allows triggering ComputerCraft's client commands from chat components events.
|
||||
*/
|
||||
@Mixin(ClientPacketListener.class)
|
||||
class ClientPacketListenerMixin {
|
||||
@Inject(method = "sendUnsignedCommand", at = @At("HEAD"), cancellable = true)
|
||||
void commandUnsigned(String command, CallbackInfoReturnable<Boolean> ci) {
|
||||
if (command.startsWith(CommandComputerCraft.CLIENT_OPEN_FOLDER) && ClientCommandHandler.runCommand(command)) {
|
||||
ci.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "dan200.computercraft.mixin.client",
|
||||
"minVersion": "0.8",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"client": [
|
||||
"BlockRenderDispatcherMixin",
|
||||
"ClientPacketListenerMixin"
|
||||
],
|
||||
"refmap": "client-computercraft.refmap.json"
|
||||
}
|
@ -8,7 +8,9 @@ import dan200.computercraft.api.detail.ForgeDetailRegistries;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
@ -53,7 +55,7 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
var location = computer.getAvailablePeripheral(toName);
|
||||
if (location == null) throw new LuaException("Target '" + toName + "' does not exist");
|
||||
|
||||
var to = extractHandler(location.getTarget());
|
||||
var to = extractHandler(location);
|
||||
if (to == null) throw new LuaException("Target '" + toName + "' is not an tank");
|
||||
|
||||
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
||||
@ -78,7 +80,7 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
var location = computer.getAvailablePeripheral(fromName);
|
||||
if (location == null) throw new LuaException("Target '" + fromName + "' does not exist");
|
||||
|
||||
var from = extractHandler(location.getTarget());
|
||||
var from = extractHandler(location);
|
||||
if (from == null) throw new LuaException("Target '" + fromName + "' is not an tank");
|
||||
|
||||
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
||||
@ -90,11 +92,14 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static IFluidHandler extractHandler(@Nullable Object object) {
|
||||
private static IFluidHandler extractHandler(IPeripheral peripheral) {
|
||||
var object = peripheral.getTarget();
|
||||
var direction = peripheral instanceof dan200.computercraft.shared.peripheral.generic.GenericPeripheral sided ? sided.side() : null;
|
||||
|
||||
if (object instanceof BlockEntity blockEntity && blockEntity.isRemoved()) return null;
|
||||
|
||||
if (object instanceof ICapabilityProvider provider) {
|
||||
var cap = provider.getCapability(ForgeCapabilities.FLUID_HANDLER);
|
||||
var cap = CapabilityUtil.getCapability(provider, ForgeCapabilities.FLUID_HANDLER, direction);
|
||||
if (cap.isPresent()) return cap.orElseThrow(NullPointerException::new);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,9 @@ import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.shared.platform.ForgeContainerTransfer;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||
@ -73,7 +75,7 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
var location = computer.getAvailablePeripheral(toName);
|
||||
if (location == null) throw new LuaException("Target '" + toName + "' does not exist");
|
||||
|
||||
var to = extractHandler(location.getTarget());
|
||||
var to = extractHandler(location);
|
||||
if (to == null) throw new LuaException("Target '" + toName + "' is not an inventory");
|
||||
|
||||
// Validate slots
|
||||
@ -95,7 +97,7 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
var location = computer.getAvailablePeripheral(fromName);
|
||||
if (location == null) throw new LuaException("Source '" + fromName + "' does not exist");
|
||||
|
||||
var from = extractHandler(location.getTarget());
|
||||
var from = extractHandler(location);
|
||||
if (from == null) throw new LuaException("Source '" + fromName + "' is not an inventory");
|
||||
|
||||
// Validate slots
|
||||
@ -108,11 +110,14 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static IItemHandler extractHandler(@Nullable Object object) {
|
||||
private static IItemHandler extractHandler(IPeripheral peripheral) {
|
||||
var object = peripheral.getTarget();
|
||||
var direction = peripheral instanceof dan200.computercraft.shared.peripheral.generic.GenericPeripheral sided ? sided.side() : null;
|
||||
|
||||
if (object instanceof BlockEntity blockEntity && blockEntity.isRemoved()) return null;
|
||||
|
||||
if (object instanceof ICapabilityProvider provider) {
|
||||
var cap = provider.getCapability(ForgeCapabilities.ITEM_HANDLER);
|
||||
var cap = CapabilityUtil.getCapability(provider, ForgeCapabilities.ITEM_HANDLER, direction);
|
||||
if (cap.isPresent()) return cap.orElseThrow(NullPointerException::new);
|
||||
}
|
||||
|
||||
|
@ -345,6 +345,11 @@ public class PlatformHelperImpl implements PlatformHelper {
|
||||
return event.getUseItem() == Event.Result.DENY ? InteractionResult.PASS : stack.useOn(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canClickRunClientCommand() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private record RegistryWrapperImpl<T>(
|
||||
ResourceLocation name, ForgeRegistry<T> registry
|
||||
) implements RegistryWrappers.RegistryWrapper<T> {
|
||||
|
@ -50,8 +50,8 @@ public final class CapabilityUtil {
|
||||
* @param <T> The type of the underlying capability.
|
||||
* @return The extracted capability, if present.
|
||||
*/
|
||||
public static <T> LazyOptional<T> getCapability(ICapabilityProvider provider, Capability<T> capability, Direction side) {
|
||||
public static <T> LazyOptional<T> getCapability(ICapabilityProvider provider, Capability<T> capability, @Nullable Direction side) {
|
||||
var cap = provider.getCapability(capability);
|
||||
return cap.isPresent() ? cap : provider.getCapability(capability, side);
|
||||
return !cap.isPresent() && side != null ? provider.getCapability(capability, side) : cap;
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,6 @@ internal class SideProvider {
|
||||
companion object {
|
||||
private val notClientPackages = listOf(
|
||||
// Ugly! But we do what we must.
|
||||
"net.fabricmc.fabric.api.client.itemgroup",
|
||||
"dan200.computercraft.shared.network.client",
|
||||
)
|
||||
|
||||
|
@ -78,11 +78,11 @@ public class JavascriptConv {
|
||||
* @return The wrapped array.
|
||||
*/
|
||||
public static byte[] asByteArray(ArrayBuffer view) {
|
||||
return asByteArray(Int8Array.create(view));
|
||||
return asByteArray(new Int8Array(view));
|
||||
}
|
||||
|
||||
public static Int8Array toArray(ByteBuffer buffer) {
|
||||
var array = Int8Array.create(buffer.remaining());
|
||||
var array = new Int8Array(buffer.remaining());
|
||||
for (var i = 0; i < array.getLength(); i++) array.set(i, buffer.get(i));
|
||||
return array;
|
||||
}
|
||||
|
@ -53,11 +53,13 @@ public class SpeakerPeripheral implements TickablePeripheral {
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
@SuppressWarnings("DoNotCallSuggester")
|
||||
public final boolean playNote(String instrumentA, Optional<Double> volumeA, Optional<Double> pitchA) throws LuaException {
|
||||
throw new LuaException("Cannot play notes outside of Minecraft");
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
@SuppressWarnings("DoNotCallSuggester")
|
||||
public final boolean playSound(String name, Optional<Double> volumeA, Optional<Double> pitchA) throws LuaException {
|
||||
throw new LuaException("Cannot play sounds outside of Minecraft");
|
||||
}
|
||||
@ -70,7 +72,7 @@ public class SpeakerPeripheral implements TickablePeripheral {
|
||||
if (length <= 0) throw new LuaException("Cannot play empty audio");
|
||||
if (length > 128 * 1024) throw new LuaException("Audio data is too large");
|
||||
|
||||
if (audioContext == null) audioContext = AudioContext.create();
|
||||
if (audioContext == null) audioContext = new AudioContext();
|
||||
if (state == null || !state.isPlaying()) state = new AudioState(audioContext);
|
||||
|
||||
return state.pushBuffer(audio, length, volume);
|
||||
|
@ -92,7 +92,7 @@ public class THttpRequest extends Resource<THttpRequest> {
|
||||
if (isClosed()) return;
|
||||
|
||||
try {
|
||||
var request = XMLHttpRequest.create();
|
||||
var request = new XMLHttpRequest();
|
||||
request.setOnReadyStateChange(() -> onResponseStateChange(request));
|
||||
request.setResponseType("arraybuffer");
|
||||
var address = uri.toASCIIString();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user