Merge branch 'mc-1.16.x' into mc-1.17.x
@ -10,6 +10,7 @@ This event is normally handled by @{http.Websocket.receive}, but it can also be
|
|||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
2. @{string}: The URL of the WebSocket.
|
2. @{string}: The URL of the WebSocket.
|
||||||
3. @{string}: The contents of the message.
|
3. @{string}: The contents of the message.
|
||||||
|
4. @{boolean}: Whether this is a binary message.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message sent by a WebSocket:
|
Prints a message sent by a WebSocket:
|
||||||
|
BIN
doc/logo.png
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.5 KiB |
@ -61,6 +61,8 @@
|
|||||||
(table space)
|
(table space)
|
||||||
(index no-space))
|
(index no-space))
|
||||||
|
|
||||||
|
(allow-clarifying-parens true)
|
||||||
|
|
||||||
;; colours imports from colors, and we don't handle that right now.
|
;; colours imports from colors, and we don't handle that right now.
|
||||||
;; keys is entirely dynamic, so we skip it.
|
;; keys is entirely dynamic, so we skip it.
|
||||||
(dynamic-modules colours keys _G)
|
(dynamic-modules colours keys _G)
|
||||||
|
@ -43,6 +43,10 @@ public class NoTermComputerScreen<T extends ContainerComputerBase> extends Scree
|
|||||||
@Override
|
@Override
|
||||||
protected void init()
|
protected void init()
|
||||||
{
|
{
|
||||||
|
this.passEvents = true; // to allow gui click events pass through mouseHelper protection (see MouseHelper.OnPres:105 code string)
|
||||||
|
minecraft.mouseHandler.grabMouse();
|
||||||
|
minecraft.screen = this;
|
||||||
|
|
||||||
super.init();
|
super.init();
|
||||||
minecraft.keyboardHandler.setSendRepeatsToGui( true );
|
minecraft.keyboardHandler.setSendRepeatsToGui( true );
|
||||||
|
|
||||||
@ -66,6 +70,13 @@ public class NoTermComputerScreen<T extends ContainerComputerBase> extends Scree
|
|||||||
terminal.update();
|
terminal.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseScrolled( double pMouseX, double pMouseY, double pDelta )
|
||||||
|
{
|
||||||
|
minecraft.player.getInventory().swapPaint( pDelta );
|
||||||
|
return super.mouseScrolled( pMouseX, pMouseY, pDelta );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClose()
|
public void onClose()
|
||||||
{
|
{
|
||||||
|
@ -127,8 +127,8 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
|
|||||||
|
|
||||||
FixedWidthFontRenderer.drawBlocker(
|
FixedWidthFontRenderer.drawBlocker(
|
||||||
transform.last().pose(), renderer,
|
transform.last().pose(), renderer,
|
||||||
(float) -TileMonitor.RENDER_MARGIN, (float) TileMonitor.RENDER_MARGIN,
|
-MARGIN, MARGIN,
|
||||||
(float) (xSize + 2 * TileMonitor.RENDER_MARGIN), (float) -(ySize + TileMonitor.RENDER_MARGIN * 2)
|
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in
|
// Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core.apis;
|
package dan200.computercraft.core.apis;
|
||||||
|
|
||||||
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
@ -30,6 +31,11 @@ public abstract class ComputerAccess implements IComputerAccess
|
|||||||
public void unmountAll()
|
public void unmountAll()
|
||||||
{
|
{
|
||||||
FileSystem fileSystem = environment.getFileSystem();
|
FileSystem fileSystem = environment.getFileSystem();
|
||||||
|
if( !mounts.isEmpty() )
|
||||||
|
{
|
||||||
|
ComputerCraft.log.warn( "Peripheral or API called mount but did not call unmount for {}", mounts );
|
||||||
|
}
|
||||||
|
|
||||||
for( String mount : mounts )
|
for( String mount : mounts )
|
||||||
{
|
{
|
||||||
fileSystem.unmount( mount );
|
fileSystem.unmount( mount );
|
||||||
|
@ -419,6 +419,7 @@ public class Terminal
|
|||||||
{
|
{
|
||||||
if( c >= '0' && c <= '9' ) return c - '0';
|
if( c >= '0' && c <= '9' ) return c - '0';
|
||||||
if( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
|
if( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
|
||||||
|
if( c >= 'A' && c <= 'F' ) return c - 'A' + 10;
|
||||||
return 15 - def.ordinal();
|
return 15 - def.ordinal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package dan200.computercraft.shared.peripheral.modem.wired;
|
package dan200.computercraft.shared.peripheral.modem.wired;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import dan200.computercraft.ComputerCraft;
|
||||||
import dan200.computercraft.api.filesystem.IMount;
|
import dan200.computercraft.api.filesystem.IMount;
|
||||||
import dan200.computercraft.api.filesystem.IWritableMount;
|
import dan200.computercraft.api.filesystem.IWritableMount;
|
||||||
import dan200.computercraft.api.lua.*;
|
import dan200.computercraft.api.lua.*;
|
||||||
@ -15,6 +16,7 @@ import dan200.computercraft.api.network.wired.IWiredSender;
|
|||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
import dan200.computercraft.api.peripheral.IWorkMonitor;
|
||||||
|
import dan200.computercraft.api.peripheral.NotAttachedException;
|
||||||
import dan200.computercraft.core.apis.PeripheralAPI;
|
import dan200.computercraft.core.apis.PeripheralAPI;
|
||||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||||
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
|
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
|
||||||
@ -23,10 +25,7 @@ import net.minecraft.world.level.Level;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
@ -310,6 +309,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
private final String type;
|
private final String type;
|
||||||
private final Map<String, PeripheralMethod> methodMap;
|
private final Map<String, PeripheralMethod> methodMap;
|
||||||
|
|
||||||
|
private volatile boolean attached;
|
||||||
|
private final Set<String> mounts = new HashSet<>();
|
||||||
|
|
||||||
RemotePeripheralWrapper( WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name )
|
RemotePeripheralWrapper( WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name )
|
||||||
{
|
{
|
||||||
this.element = element;
|
this.element = element;
|
||||||
@ -323,6 +325,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
|
|
||||||
public void attach()
|
public void attach()
|
||||||
{
|
{
|
||||||
|
attached = true;
|
||||||
peripheral.attach( this );
|
peripheral.attach( this );
|
||||||
computer.queueEvent( "peripheral", getAttachmentName() );
|
computer.queueEvent( "peripheral", getAttachmentName() );
|
||||||
}
|
}
|
||||||
@ -331,6 +334,18 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
{
|
{
|
||||||
peripheral.detach( this );
|
peripheral.detach( this );
|
||||||
computer.queueEvent( "peripheral_detach", getAttachmentName() );
|
computer.queueEvent( "peripheral_detach", getAttachmentName() );
|
||||||
|
attached = false;
|
||||||
|
|
||||||
|
synchronized( this )
|
||||||
|
{
|
||||||
|
if( !mounts.isEmpty() )
|
||||||
|
{
|
||||||
|
ComputerCraft.log.warn( "Peripheral {} called mount but did not call unmount for {}", peripheral, mounts );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( String mount : mounts ) computer.unmount( mount );
|
||||||
|
mounts.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType()
|
public String getType()
|
||||||
@ -353,44 +368,60 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
// IComputerAccess implementation
|
// IComputerAccess implementation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String mount( @Nonnull String desiredLocation, @Nonnull IMount mount )
|
public synchronized String mount( @Nonnull String desiredLocation, @Nonnull IMount mount )
|
||||||
{
|
{
|
||||||
return computer.mount( desiredLocation, mount, name );
|
if( !attached ) throw new NotAttachedException();
|
||||||
|
String mounted = computer.mount( desiredLocation, mount, name );
|
||||||
|
mounts.add( mounted );
|
||||||
|
return mounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String mount( @Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName )
|
public synchronized String mount( @Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName )
|
||||||
{
|
{
|
||||||
return computer.mount( desiredLocation, mount, driveName );
|
if( !attached ) throw new NotAttachedException();
|
||||||
|
String mounted = computer.mount( desiredLocation, mount, driveName );
|
||||||
|
mounts.add( mounted );
|
||||||
|
return mounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount )
|
public synchronized String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount )
|
||||||
{
|
{
|
||||||
return computer.mountWritable( desiredLocation, mount, name );
|
if( !attached ) throw new NotAttachedException();
|
||||||
|
String mounted = computer.mountWritable( desiredLocation, mount, name );
|
||||||
|
mounts.add( mounted );
|
||||||
|
return mounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName )
|
public synchronized String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName )
|
||||||
{
|
{
|
||||||
return computer.mountWritable( desiredLocation, mount, driveName );
|
if( !attached ) throw new NotAttachedException();
|
||||||
|
String mounted = computer.mountWritable( desiredLocation, mount, driveName );
|
||||||
|
mounts.add( mounted );
|
||||||
|
return mounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unmount( String location )
|
public synchronized void unmount( String location )
|
||||||
{
|
{
|
||||||
|
if( !attached ) throw new NotAttachedException();
|
||||||
computer.unmount( location );
|
computer.unmount( location );
|
||||||
|
mounts.remove( location );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getID()
|
public int getID()
|
||||||
{
|
{
|
||||||
|
if( !attached ) throw new NotAttachedException();
|
||||||
return computer.getID();
|
return computer.getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueEvent( @Nonnull String event, Object... arguments )
|
public void queueEvent( @Nonnull String event, Object... arguments )
|
||||||
{
|
{
|
||||||
|
if( !attached ) throw new NotAttachedException();
|
||||||
computer.queueEvent( event, arguments );
|
computer.queueEvent( event, arguments );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,6 +429,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
@Override
|
@Override
|
||||||
public IWorkMonitor getMainThreadMonitor()
|
public IWorkMonitor getMainThreadMonitor()
|
||||||
{
|
{
|
||||||
|
if( !attached ) throw new NotAttachedException();
|
||||||
return computer.getMainThreadMonitor();
|
return computer.getMainThreadMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,6 +437,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
@Override
|
@Override
|
||||||
public String getAttachmentName()
|
public String getAttachmentName()
|
||||||
{
|
{
|
||||||
|
if( !attached ) throw new NotAttachedException();
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,6 +445,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
@Override
|
@Override
|
||||||
public Map<String, IPeripheral> getAvailablePeripherals()
|
public Map<String, IPeripheral> getAvailablePeripherals()
|
||||||
{
|
{
|
||||||
|
if( !attached ) throw new NotAttachedException();
|
||||||
synchronized( element.getRemotePeripherals() )
|
synchronized( element.getRemotePeripherals() )
|
||||||
{
|
{
|
||||||
return ImmutableMap.copyOf( element.getRemotePeripherals() );
|
return ImmutableMap.copyOf( element.getRemotePeripherals() );
|
||||||
@ -422,6 +456,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
|
|||||||
@Override
|
@Override
|
||||||
public IPeripheral getAvailablePeripheral( @Nonnull String name )
|
public IPeripheral getAvailablePeripheral( @Nonnull String name )
|
||||||
{
|
{
|
||||||
|
if( !attached ) throw new NotAttachedException();
|
||||||
synchronized( element.getRemotePeripherals() )
|
synchronized( element.getRemotePeripherals() )
|
||||||
{
|
{
|
||||||
return element.getRemotePeripherals().get( name );
|
return element.getRemotePeripherals().get( name );
|
||||||
|
@ -5,3 +5,5 @@ public net.minecraft.client.renderer.ItemInHandRenderer m_109346_(Lcom/mojang/bl
|
|||||||
# ClientTableFormatter
|
# ClientTableFormatter
|
||||||
public net.minecraft.client.gui.components.ChatComponent m_93787_(Lnet/minecraft/network/chat/Component;I)V # addMessage
|
public net.minecraft.client.gui.components.ChatComponent m_93787_(Lnet/minecraft/network/chat/Component;I)V # addMessage
|
||||||
public net.minecraft.client.gui.components.ChatComponent m_93803_(I)V # removeById
|
public net.minecraft.client.gui.components.ChatComponent m_93803_(I)V # removeById
|
||||||
|
# NoTermComputerScreen
|
||||||
|
public net.minecraft.client.Minecraft f_91080_ # screen
|
||||||
|
@ -11,7 +11,7 @@ license="ComputerCraft Public License (https://raw.githubusercontent.com/dan200/
|
|||||||
|
|
||||||
[[mods]]
|
[[mods]]
|
||||||
modId="computercraft"
|
modId="computercraft"
|
||||||
version="${version}"
|
version="1.2.3.4"
|
||||||
displayName="CC: Tweaked"
|
displayName="CC: Tweaked"
|
||||||
description='''
|
description='''
|
||||||
CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.
|
CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
"commands.computercraft.synopsis": "Verschiedene Befehle um Computer zu kontrollieren.",
|
"commands.computercraft.synopsis": "Verschiedene Befehle um Computer zu kontrollieren.",
|
||||||
"commands.computercraft.desc": "Der /computercraft Befehl enthält verschiedene Werkzeuge um Computer zu debuggen, kontrollieren oder mit ihnen zu interagieren.",
|
"commands.computercraft.desc": "Der /computercraft Befehl enthält verschiedene Werkzeuge um Computer zu debuggen, kontrollieren oder mit ihnen zu interagieren.",
|
||||||
"commands.computercraft.help.synopsis": "Zeigt die Hilfe für den angegebenen Befehl",
|
"commands.computercraft.help.synopsis": "Zeigt die Hilfe für den angegebenen Befehl",
|
||||||
"commands.computercraft.help.desc": "Zeigt diese Hilfe",
|
"commands.computercraft.help.desc": "Zeigt diese Hilfe Nachricht",
|
||||||
"commands.computercraft.help.no_children": "%s hat keine Unterbefehle",
|
"commands.computercraft.help.no_children": "%s hat keine Unterbefehle",
|
||||||
"commands.computercraft.help.no_command": "Unbekannter Befehl '%s'",
|
"commands.computercraft.help.no_command": "Unbekannter Befehl '%s'",
|
||||||
"commands.computercraft.dump.synopsis": "Zeigt den Status eines Computers.",
|
"commands.computercraft.dump.synopsis": "Zeigt den Status eines Computers.",
|
||||||
|
@ -109,5 +109,23 @@
|
|||||||
"tracking_field.computercraft.coroutines_dead.name": "Coroutines mortes",
|
"tracking_field.computercraft.coroutines_dead.name": "Coroutines mortes",
|
||||||
"gui.computercraft.tooltip.copy": "Copier dans le Presse-Papiers",
|
"gui.computercraft.tooltip.copy": "Copier dans le Presse-Papiers",
|
||||||
"gui.computercraft.tooltip.computer_id": "ID d'ordinateur : %s",
|
"gui.computercraft.tooltip.computer_id": "ID d'ordinateur : %s",
|
||||||
"gui.computercraft.tooltip.disk_id": "ID de disque : %s"
|
"gui.computercraft.tooltip.disk_id": "ID de disque : %s",
|
||||||
|
"gui.computercraft.tooltip.turn_on.key": "Ternir Ctrl+R",
|
||||||
|
"gui.computercraft.tooltip.turn_off": "Éteindre cet ordinateur",
|
||||||
|
"gui.computercraft.tooltip.turn_off.key": "Tenir Ctrl+S",
|
||||||
|
"gui.computercraft.tooltip.terminate": "Arrêter le programme en cours d'éxecution",
|
||||||
|
"gui.computercraft.tooltip.terminate.key": "Tenir Ctrl+T",
|
||||||
|
"gui.computercraft.upload.overwrite": "Les fichiers seraient écrasés",
|
||||||
|
"gui.computercraft.upload.overwrite.detail": "Les fichiers suivants seront écrasés lors de l'envoie. Continuer ?%s",
|
||||||
|
"gui.computercraft.upload.overwrite_button": "Écraser",
|
||||||
|
"gui.computercraft.upload.success": "Envoie avec succès",
|
||||||
|
"gui.computercraft.upload.success.msg": "Le fichier %d est envoyé.",
|
||||||
|
"gui.computercraft.upload.failed": "Echec de l'envoie",
|
||||||
|
"gui.computercraft.upload.failed.out_of_space": "Il n'y a pas assez de place sur cet ordinateur pour ce fichier.",
|
||||||
|
"gui.computercraft.upload.failed.computer_off": "Vous devez allumer cet ordinateur avant d'envoyer ce fichier.",
|
||||||
|
"gui.computercraft.upload.failed.too_much": "Votre fichier est trop lourd pour être envoyé.",
|
||||||
|
"gui.computercraft.upload.failed.overwrite_dir": "%s ne peut pas être envoyé, il y a déjà un dossier avec le même nom.",
|
||||||
|
"gui.computercraft.upload.failed.generic": "Echec de l'envoie des fichiers(%s)",
|
||||||
|
"commands.computercraft.dump.open_path": "Voir les fichiers de cet ordinateur",
|
||||||
|
"gui.computercraft.tooltip.turn_on": "Allumer cet ordinateur"
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,17 @@ CHANNEL_BROADCAST = 65535
|
|||||||
--- The channel used by the Rednet API to repeat messages.
|
--- The channel used by the Rednet API to repeat messages.
|
||||||
CHANNEL_REPEAT = 65533
|
CHANNEL_REPEAT = 65533
|
||||||
|
|
||||||
|
--- The number of channels rednet reserves for computer IDs. Computers with IDs
|
||||||
|
-- greater or equal to this limit wrap around to 0.
|
||||||
|
MAX_ID_CHANNELS = 65500
|
||||||
|
|
||||||
local tReceivedMessages = {}
|
local tReceivedMessages = {}
|
||||||
local tReceivedMessageTimeouts = {}
|
|
||||||
local tHostnames = {}
|
local tHostnames = {}
|
||||||
|
local nClearTimer
|
||||||
|
|
||||||
|
local function id_as_channel(id)
|
||||||
|
return (id or os.getComputerID()) % MAX_ID_CHANNELS
|
||||||
|
end
|
||||||
|
|
||||||
--[[- Opens a modem with the given @{peripheral} name, allowing it to send and
|
--[[- Opens a modem with the given @{peripheral} name, allowing it to send and
|
||||||
receive messages over rednet.
|
receive messages over rednet.
|
||||||
@ -47,7 +55,7 @@ function open(modem)
|
|||||||
if peripheral.getType(modem) ~= "modem" then
|
if peripheral.getType(modem) ~= "modem" then
|
||||||
error("No such modem: " .. modem, 2)
|
error("No such modem: " .. modem, 2)
|
||||||
end
|
end
|
||||||
peripheral.call(modem, "open", os.getComputerID())
|
peripheral.call(modem, "open", id_as_channel())
|
||||||
peripheral.call(modem, "open", CHANNEL_BROADCAST)
|
peripheral.call(modem, "open", CHANNEL_BROADCAST)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -64,7 +72,7 @@ function close(modem)
|
|||||||
if peripheral.getType(modem) ~= "modem" then
|
if peripheral.getType(modem) ~= "modem" then
|
||||||
error("No such modem: " .. modem, 2)
|
error("No such modem: " .. modem, 2)
|
||||||
end
|
end
|
||||||
peripheral.call(modem, "close", os.getComputerID())
|
peripheral.call(modem, "close", id_as_channel())
|
||||||
peripheral.call(modem, "close", CHANNEL_BROADCAST)
|
peripheral.call(modem, "close", CHANNEL_BROADCAST)
|
||||||
else
|
else
|
||||||
-- Close all modems
|
-- Close all modems
|
||||||
@ -87,7 +95,7 @@ function isOpen(modem)
|
|||||||
if modem then
|
if modem then
|
||||||
-- Check if a specific modem is open
|
-- Check if a specific modem is open
|
||||||
if peripheral.getType(modem) == "modem" then
|
if peripheral.getType(modem) == "modem" then
|
||||||
return peripheral.call(modem, "isOpen", os.getComputerID()) and peripheral.call(modem, "isOpen", CHANNEL_BROADCAST)
|
return peripheral.call(modem, "isOpen", id_as_channel()) and peripheral.call(modem, "isOpen", CHANNEL_BROADCAST)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Check if any modem is open
|
-- Check if any modem is open
|
||||||
@ -130,14 +138,15 @@ function send(nRecipient, message, sProtocol)
|
|||||||
-- We could do other things to guarantee uniqueness, but we really don't need to
|
-- We could do other things to guarantee uniqueness, but we really don't need to
|
||||||
-- Store it to ensure we don't get our own messages back
|
-- Store it to ensure we don't get our own messages back
|
||||||
local nMessageID = math.random(1, 2147483647)
|
local nMessageID = math.random(1, 2147483647)
|
||||||
tReceivedMessages[nMessageID] = true
|
tReceivedMessages[nMessageID] = os.clock() + 9.5
|
||||||
tReceivedMessageTimeouts[os.startTimer(30)] = nMessageID
|
if not nClearTimer then nClearTimer = os.startTimer(10) end
|
||||||
|
|
||||||
-- Create the message
|
-- Create the message
|
||||||
local nReplyChannel = os.getComputerID()
|
local nReplyChannel = id_as_channel()
|
||||||
local tMessage = {
|
local tMessage = {
|
||||||
nMessageID = nMessageID,
|
nMessageID = nMessageID,
|
||||||
nRecipient = nRecipient,
|
nRecipient = nRecipient,
|
||||||
|
nSender = os.getComputerID(),
|
||||||
message = message,
|
message = message,
|
||||||
sProtocol = sProtocol,
|
sProtocol = sProtocol,
|
||||||
}
|
}
|
||||||
@ -145,10 +154,14 @@ function send(nRecipient, message, sProtocol)
|
|||||||
local sent = false
|
local sent = false
|
||||||
if nRecipient == os.getComputerID() then
|
if nRecipient == os.getComputerID() then
|
||||||
-- Loopback to ourselves
|
-- Loopback to ourselves
|
||||||
os.queueEvent("rednet_message", nReplyChannel, message, sProtocol)
|
os.queueEvent("rednet_message", os.getComputerID(), message, sProtocol)
|
||||||
sent = true
|
sent = true
|
||||||
else
|
else
|
||||||
-- Send on all open modems, to the target and to repeaters
|
-- Send on all open modems, to the target and to repeaters
|
||||||
|
if nRecipient ~= CHANNEL_BROADCAST then
|
||||||
|
nRecipient = id_as_channel(nRecipient)
|
||||||
|
end
|
||||||
|
|
||||||
for _, sModem in ipairs(peripheral.getNames()) do
|
for _, sModem in ipairs(peripheral.getNames()) do
|
||||||
if isOpen(sModem) then
|
if isOpen(sModem) then
|
||||||
peripheral.call(sModem, "transmit", nRecipient, nReplyChannel, tMessage)
|
peripheral.call(sModem, "transmit", nRecipient, nReplyChannel, tMessage)
|
||||||
@ -390,13 +403,14 @@ function run()
|
|||||||
if sEvent == "modem_message" then
|
if sEvent == "modem_message" then
|
||||||
-- Got a modem message, process it and add it to the rednet event queue
|
-- Got a modem message, process it and add it to the rednet event queue
|
||||||
local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
|
local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
|
||||||
if isOpen(sModem) and (nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST) then
|
if isOpen(sModem) and (nChannel == id_as_channel() or nChannel == CHANNEL_BROADCAST) then
|
||||||
if type(tMessage) == "table" and type(tMessage.nMessageID) == "number"
|
if type(tMessage) == "table" and type(tMessage.nMessageID) == "number"
|
||||||
and tMessage.nMessageID == tMessage.nMessageID and not tReceivedMessages[tMessage.nMessageID]
|
and tMessage.nMessageID == tMessage.nMessageID and not tReceivedMessages[tMessage.nMessageID]
|
||||||
|
and ((tMessage.nRecipient and tMessage.nRecipient == os.getComputerID()) or nChannel == CHANNEL_BROADCAST)
|
||||||
then
|
then
|
||||||
tReceivedMessages[tMessage.nMessageID] = true
|
tReceivedMessages[tMessage.nMessageID] = os.clock() + 9.5
|
||||||
tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID
|
if not nClearTimer then nClearTimer = os.startTimer(10) end
|
||||||
os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol)
|
os.queueEvent("rednet_message", tMessage.nSender or nReplyChannel, tMessage.message, tMessage.sProtocol)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -414,14 +428,15 @@ function run()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif sEvent == "timer" then
|
elseif sEvent == "timer" and p1 == nClearTimer then
|
||||||
-- Got a timer event, use it to clear the event queue
|
-- Got a timer event, use it to clear the event queue
|
||||||
local nTimer = p1
|
nClearTimer = nil
|
||||||
local nMessage = tReceivedMessageTimeouts[nTimer]
|
local nNow, bHasMore = os.clock(), nil
|
||||||
if nMessage then
|
for nMessageID, nDeadline in pairs(tReceivedMessages) do
|
||||||
tReceivedMessageTimeouts[nTimer] = nil
|
if nDeadline <= nNow then tReceivedMessages[nMessageID] = nil
|
||||||
tReceivedMessages[nMessage] = nil
|
else bHasMore = true end
|
||||||
end
|
end
|
||||||
|
nClearTimer = bHasMore and os.startTimer(10)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -265,6 +265,8 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||||||
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
|
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
|
||||||
error("Arguments must be the same length", 2)
|
error("Arguments must be the same length", 2)
|
||||||
end
|
end
|
||||||
|
sTextColor = sTextColor:lower()
|
||||||
|
sBackgroundColor = sBackgroundColor:lower()
|
||||||
internalBlit(sText, sTextColor, sBackgroundColor)
|
internalBlit(sText, sTextColor, sBackgroundColor)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,6 +14,10 @@ else
|
|||||||
print(#tModems .. " modems found.")
|
print(#tModems .. " modems found.")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function idAsChannel(id)
|
||||||
|
return (id or os.getComputerID()) % rednet.MAX_ID_CHANNELS
|
||||||
|
end
|
||||||
|
|
||||||
local function open(nChannel)
|
local function open(nChannel)
|
||||||
for n = 1, #tModems do
|
for n = 1, #tModems do
|
||||||
local sModem = tModems[n]
|
local sModem = tModems[n]
|
||||||
@ -53,7 +57,7 @@ local ok, error = pcall(function()
|
|||||||
for n = 1, #tModems do
|
for n = 1, #tModems do
|
||||||
local sOtherModem = tModems[n]
|
local sOtherModem = tModems[n]
|
||||||
peripheral.call(sOtherModem, "transmit", rednet.CHANNEL_REPEAT, nReplyChannel, tMessage)
|
peripheral.call(sOtherModem, "transmit", rednet.CHANNEL_REPEAT, nReplyChannel, tMessage)
|
||||||
peripheral.call(sOtherModem, "transmit", tMessage.nRecipient, nReplyChannel, tMessage)
|
peripheral.call(sOtherModem, "transmit", idAsChannel(tMessage.nRecipient), nReplyChannel, tMessage)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Log the event
|
-- Log the event
|
||||||
|
@ -97,7 +97,7 @@ public class ComputerTestDelegate
|
|||||||
|
|
||||||
if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." );
|
if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." );
|
||||||
|
|
||||||
Terminal term = new Terminal( 78, 20 );
|
Terminal term = new Terminal( 80, 30 );
|
||||||
IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 );
|
IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 );
|
||||||
|
|
||||||
// Remove any existing files
|
// Remove any existing files
|
||||||
|
@ -83,4 +83,144 @@ describe("The rednet library", function()
|
|||||||
expect(rednet.lookup("a_protocol", "a_hostname")):eq(os.getComputerID())
|
expect(rednet.lookup("a_protocol", "a_hostname")):eq(os.getComputerID())
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("on fake computers", function()
|
||||||
|
local fake_computer = require "support.fake_computer"
|
||||||
|
local debugx = require "support.debug_ext"
|
||||||
|
|
||||||
|
local function computer_with_rednet(id, fn, options)
|
||||||
|
local computer = fake_computer.make_computer(id, function(env)
|
||||||
|
local fns = { env.rednet.run }
|
||||||
|
if options and options.rep then
|
||||||
|
fns[#fns + 1] = function() env.dofile("rom/programs/rednet/repeat.lua") end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fn then
|
||||||
|
fns[#fns + 1] = function()
|
||||||
|
if options and options.open then
|
||||||
|
env.rednet.open("back")
|
||||||
|
env.os.queueEvent("x") env.os.pullEvent("x")
|
||||||
|
end
|
||||||
|
return fn(env.rednet, env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return parallel.waitForAny(table.unpack(fns))
|
||||||
|
end)
|
||||||
|
local modem = fake_computer.add_modem(computer, "back")
|
||||||
|
fake_computer.add_api(computer, "rom/apis/rednet.lua")
|
||||||
|
return computer, modem
|
||||||
|
end
|
||||||
|
|
||||||
|
it("opens and closes channels", function()
|
||||||
|
local id = math.random(256)
|
||||||
|
local computer = computer_with_rednet(id, function(rednet)
|
||||||
|
expect(rednet.isOpen()):eq(false)
|
||||||
|
|
||||||
|
rednet.open("back")
|
||||||
|
rednet.open("front")
|
||||||
|
|
||||||
|
expect(rednet.isOpen()):eq(true)
|
||||||
|
expect(rednet.isOpen("back")):eq(true)
|
||||||
|
expect(rednet.isOpen("front")):eq(true)
|
||||||
|
|
||||||
|
rednet.close("back")
|
||||||
|
expect(rednet.isOpen("back")):eq(false)
|
||||||
|
expect(rednet.isOpen("front")):eq(true)
|
||||||
|
expect(rednet.isOpen()):eq(true)
|
||||||
|
|
||||||
|
rednet.close()
|
||||||
|
|
||||||
|
expect(rednet.isOpen("back")):eq(false)
|
||||||
|
expect(rednet.isOpen("front")):eq(false)
|
||||||
|
expect(rednet.isOpen()):eq(false)
|
||||||
|
end)
|
||||||
|
fake_computer.add_modem(computer, "front")
|
||||||
|
|
||||||
|
fake_computer.run_all { computer }
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("sends and receives rednet messages", function()
|
||||||
|
local computer_1, modem_1 = computer_with_rednet(1, function(rednet)
|
||||||
|
rednet.send(2, "Hello")
|
||||||
|
end, { open = true })
|
||||||
|
local computer_2, modem_2 = computer_with_rednet(2, function(rednet)
|
||||||
|
local id, message = rednet.receive()
|
||||||
|
expect(id):eq(1)
|
||||||
|
expect(message):eq("Hello")
|
||||||
|
end, { open = true })
|
||||||
|
fake_computer.add_modem_edge(modem_1, modem_2)
|
||||||
|
|
||||||
|
fake_computer.run_all { computer_1, computer_2 }
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("repeats messages between computers", function()
|
||||||
|
local computer_1, modem_1 = computer_with_rednet(1, function(rednet)
|
||||||
|
rednet.send(3, "Hello")
|
||||||
|
end, { open = true })
|
||||||
|
local computer_2, modem_2 = computer_with_rednet(2, nil, { open = true, rep = true })
|
||||||
|
local computer_3, modem_3 = computer_with_rednet(3, function(rednet)
|
||||||
|
local id, message = rednet.receive()
|
||||||
|
expect(id):eq(1)
|
||||||
|
expect(message):eq("Hello")
|
||||||
|
end, { open = true })
|
||||||
|
fake_computer.add_modem_edge(modem_1, modem_2)
|
||||||
|
fake_computer.add_modem_edge(modem_2, modem_3)
|
||||||
|
|
||||||
|
fake_computer.run_all({ computer_1, computer_2, computer_3 }, { computer_1, computer_3 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("repeats messages between computers with massive ids", function()
|
||||||
|
local id_1, id_3 = 24283947, 93428798
|
||||||
|
local computer_1, modem_1 = computer_with_rednet(id_1, function(rednet)
|
||||||
|
rednet.send(id_3, "Hello")
|
||||||
|
local id, message = rednet.receive()
|
||||||
|
expect { id, message }:same { id_3, "World" }
|
||||||
|
end, { open = true })
|
||||||
|
local computer_2, modem_2 = computer_with_rednet(2, nil, { open = true, rep = true })
|
||||||
|
local computer_3, modem_3 = computer_with_rednet(id_3, function(rednet)
|
||||||
|
rednet.send(id_1, "World")
|
||||||
|
local id, message = rednet.receive()
|
||||||
|
expect { id, message }:same { id_1, "Hello" }
|
||||||
|
end, { open = true })
|
||||||
|
fake_computer.add_modem_edge(modem_1, modem_2)
|
||||||
|
fake_computer.add_modem_edge(modem_2, modem_3)
|
||||||
|
|
||||||
|
fake_computer.run_all({ computer_1, computer_2, computer_3 }, { computer_1, computer_3 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("ignores duplicate messages", function()
|
||||||
|
local computer_1, modem_1 = computer_with_rednet(1, function(rednet)
|
||||||
|
rednet.send(2, "Hello")
|
||||||
|
end, { open = true })
|
||||||
|
local computer_2, modem_2 = computer_with_rednet(2, function(rednet, env)
|
||||||
|
local id, message = rednet.receive()
|
||||||
|
expect { id, message }:same { 1, "Hello" }
|
||||||
|
|
||||||
|
local id = rednet.receive(nil, 1)
|
||||||
|
expect(id):eq(nil)
|
||||||
|
|
||||||
|
env.sleep(10)
|
||||||
|
|
||||||
|
-- Ensure our pending message store is empty. Bit ugly to prod internals, but there's no other way.
|
||||||
|
expect(debugx.getupvalue(rednet.run, "tReceivedMessages")):same({})
|
||||||
|
expect(debugx.getupvalue(rednet.run, "nClearTimer")):eq(nil)
|
||||||
|
end, { open = true })
|
||||||
|
|
||||||
|
local computer_3, modem_3 = computer_with_rednet(3, nil, { open = true, rep = true })
|
||||||
|
fake_computer.add_modem_edge(modem_1, modem_3)
|
||||||
|
fake_computer.add_modem_edge(modem_3, modem_2)
|
||||||
|
|
||||||
|
local computer_4, modem_4 = computer_with_rednet(4, nil, { open = true, rep = true })
|
||||||
|
fake_computer.add_modem_edge(modem_1, modem_4)
|
||||||
|
fake_computer.add_modem_edge(modem_4, modem_2)
|
||||||
|
|
||||||
|
local computers = { computer_1, computer_2, computer_3, computer_4 }
|
||||||
|
fake_computer.run_all(computers, false)
|
||||||
|
fake_computer.advance_all(computers, 1)
|
||||||
|
fake_computer.run_all(computers, { computer_1 })
|
||||||
|
fake_computer.advance_all(computers, 10)
|
||||||
|
fake_computer.run_all(computers, { computer_1, computer_2 })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
9
src/test/resources/test-rom/spec/support/debug_ext.lua
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
local function getupvalue(fn, name)
|
||||||
|
for i = 1, debug.getinfo(fn, "u").nups do
|
||||||
|
local up_name, value = debug.getupvalue(fn, i)
|
||||||
|
if up_name == name then return value end
|
||||||
|
end
|
||||||
|
error("Cannot find upvalue with name " .. name, 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
return { getupvalue = getupvalue }
|
180
src/test/resources/test-rom/spec/support/fake_computer.lua
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
local function keys(tbl)
|
||||||
|
local keys = {}
|
||||||
|
for k in pairs(tbl) do keys[#keys + 1] = k end
|
||||||
|
return keys
|
||||||
|
end
|
||||||
|
|
||||||
|
local safe_globals = {
|
||||||
|
"assert", "bit32", "coroutine", "debug", "error", "fs", "getmetatable", "io", "ipairs", "math", "next", "pairs",
|
||||||
|
"pcall", "print", "printError", "rawequal", "rawget", "rawlen", "rawset", "select", "setmetatable", "string",
|
||||||
|
"table", "term", "textutils", "tonumber", "tostring", "type", "utf8", "xpcall",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Create a fake computer.
|
||||||
|
local function make_computer(id, fn)
|
||||||
|
local env = setmetatable({}, _G)
|
||||||
|
|
||||||
|
local peripherals = {}
|
||||||
|
local pending_timers, next_timer, clock = {}, 0, 0
|
||||||
|
local events = { { n = 1, env } }
|
||||||
|
local function queue_event(...) events[#events + 1] = table.pack(...) end
|
||||||
|
|
||||||
|
for _, k in pairs(safe_globals) do env[k] = _G[k] end
|
||||||
|
env.peripheral = {
|
||||||
|
getNames = function() return keys(peripherals) end,
|
||||||
|
isPresent = function(name) return peripherals[name] ~= nil end,
|
||||||
|
getType = function(name) return peripherals[name] and getmetatable(peripherals[name]).type end,
|
||||||
|
getMethods = function(name) return peripherals[name] and keys(peripherals[name]) end,
|
||||||
|
call = function(name, method, ...)
|
||||||
|
local p = peripherals[name]
|
||||||
|
if p then return p[method](...) end
|
||||||
|
return nil
|
||||||
|
end,
|
||||||
|
wrap = function(name) return peripherals[name] end,
|
||||||
|
}
|
||||||
|
env.os = {
|
||||||
|
getComputerID = function() return id end,
|
||||||
|
queueEvent = queue_event,
|
||||||
|
pullEventRaw = coroutine.yield,
|
||||||
|
pullEvent = function(filter)
|
||||||
|
local event_data = table.pack(coroutine.yield(filter))
|
||||||
|
if event_data[1] == "terminate" then error("Terminated", 0) end
|
||||||
|
return table.unpack(event_data, 1, event_data.n)
|
||||||
|
end,
|
||||||
|
startTimer = function(delay)
|
||||||
|
local t = next_timer
|
||||||
|
pending_timers[t], next_timer = clock + delay, next_timer + 1
|
||||||
|
return t
|
||||||
|
end,
|
||||||
|
clock = function() return clock end,
|
||||||
|
sleep = function(time)
|
||||||
|
local timer = env.os.startTimer(time or 0)
|
||||||
|
repeat local _, id = env.os.pullEvent("timer") until id == timer
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
env.sleep = env.os.sleep
|
||||||
|
env.dofile = function(path)
|
||||||
|
local fn, err = loadfile(path, nil, env)
|
||||||
|
if fn then return fn() else error(err, 2) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local co = coroutine.create(fn)
|
||||||
|
local filter = nil
|
||||||
|
local function step()
|
||||||
|
while true do
|
||||||
|
if #events == 0 or coroutine.status(co) == "dead" then return false end
|
||||||
|
|
||||||
|
local ev = table.remove(events, 1)
|
||||||
|
if filter == nil or ev[1] == filter or ev[1] == "terminated" then
|
||||||
|
local ok, result = coroutine.resume(co, table.unpack(ev, 1, ev.n))
|
||||||
|
if not ok then
|
||||||
|
if type(result) == "table" and result.trace == nil then result.trace = debug.traceback(co) end
|
||||||
|
error(result, 0)
|
||||||
|
end
|
||||||
|
filter = result
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function advance(dt)
|
||||||
|
clock = clock + dt
|
||||||
|
for id, clk in pairs(pending_timers) do
|
||||||
|
if clk <= clock then
|
||||||
|
queue_event("timer", id)
|
||||||
|
pending_timers[id] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return { env = env, peripherals = peripherals, queue_event = queue_event, step = step, co = co, advance = advance }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parse_channel(c)
|
||||||
|
if c < 0 or c > 65535 then error("Expected number in range 0-65535", 3) end
|
||||||
|
return c
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a modem to a computer on a particular side
|
||||||
|
local function add_modem(owner, side)
|
||||||
|
local open, adjacent = {}, {}
|
||||||
|
local peripheral = setmetatable({
|
||||||
|
open = function(channel) open[parse_channel(channel)] = true end,
|
||||||
|
close = function(channel) open[parse_channel(channel)] = false end,
|
||||||
|
closeAll = function(channel) open = {} end,
|
||||||
|
isOpen = function(channel) return open[parse_channel(channel)] == true end,
|
||||||
|
transmit = function(channel, reply_channel, payload)
|
||||||
|
channel, reply_channel = parse_channel(channel), parse_channel(reply_channel)
|
||||||
|
|
||||||
|
for _, adjacent in pairs(adjacent) do
|
||||||
|
if adjacent.open[channel] then
|
||||||
|
adjacent.owner.queue_event("modem_message", adjacent.side, channel, reply_channel, payload, 123)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}, { type = "modem" })
|
||||||
|
owner.peripherals[side] = peripheral
|
||||||
|
return { adjacent = adjacent, side = side, owner = owner, open = open }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_modem_edge(modem1, modem2)
|
||||||
|
table.insert(modem1.adjacent, modem2)
|
||||||
|
table.insert(modem2.adjacent, modem1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load an API into the computer's environment.
|
||||||
|
local function add_api(computer, path)
|
||||||
|
local name = fs.getName(path)
|
||||||
|
if name:sub(-4) == ".lua" then name = name:sub(1, -5) end
|
||||||
|
|
||||||
|
local child_env = {}
|
||||||
|
setmetatable(child_env, { __index = computer.env })
|
||||||
|
assert(loadfile(path, nil, child_env))()
|
||||||
|
|
||||||
|
local api = {}
|
||||||
|
for k, v in pairs(child_env) do api[k] = v end
|
||||||
|
|
||||||
|
computer.env[name] = api
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Step all computers forward by one event.
|
||||||
|
local function step_all(computers)
|
||||||
|
local any = false
|
||||||
|
for _, computer in pairs(computers) do
|
||||||
|
if computer.step() then any = true end
|
||||||
|
end
|
||||||
|
return any
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Run all computers until their event queue is empty.
|
||||||
|
local function run_all(computers, require_done)
|
||||||
|
while step_all(computers) do end
|
||||||
|
|
||||||
|
if require_done ~= false then
|
||||||
|
if type(require_done) == "table" then
|
||||||
|
for _, v in ipairs(require_done) do require_done[v] = true end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, computer in pairs(computers) do
|
||||||
|
if coroutine.status(computer.co) ~= "dead" and (type(require_done) ~= "table" or require_done[computer]) then
|
||||||
|
error(debug.traceback(computer.co, ("Computer #%d did not shutdown"):format(computer.env.os.getComputerID())), 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Advance all computers by a given time.
|
||||||
|
local function advance_all(computers, dt)
|
||||||
|
for _, computer in pairs(computers) do computer.advance(dt) end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
make_computer = make_computer,
|
||||||
|
add_modem = add_modem,
|
||||||
|
add_modem_edge = add_modem_edge,
|
||||||
|
add_api = add_api,
|
||||||
|
|
||||||
|
step_all = step_all,
|
||||||
|
run_all = run_all,
|
||||||
|
advance_all = advance_all,
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
package dan200.computercraft.ingame
|
package dan200.computercraft.ingame
|
||||||
|
|
||||||
|
import dan200.computercraft.ComputerCraft
|
||||||
import dan200.computercraft.ingame.api.*
|
import dan200.computercraft.ingame.api.*
|
||||||
|
import dan200.computercraft.ingame.api.Timeouts.CLIENT_TIMEOUT
|
||||||
import dan200.computercraft.shared.Capabilities
|
import dan200.computercraft.shared.Capabilities
|
||||||
import dan200.computercraft.shared.Registry
|
import dan200.computercraft.shared.Registry
|
||||||
|
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer
|
||||||
import dan200.computercraft.shared.peripheral.monitor.TileMonitor
|
import dan200.computercraft.shared.peripheral.monitor.TileMonitor
|
||||||
import net.minecraft.commands.arguments.blocks.BlockInput
|
import net.minecraft.commands.arguments.blocks.BlockInput
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
@ -44,15 +47,14 @@ class Monitor_Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GameTest(batch = "client:Monitor_Test.Looks_acceptable", timeoutTicks = 400)
|
private fun looksAcceptable(helper: GameTestHelper, renderer: MonitorRenderer) = helper.sequence {
|
||||||
fun Looks_acceptable(helper: GameTestHelper) = helper.sequence {
|
|
||||||
this
|
this
|
||||||
.thenExecute { helper.normaliseScene() }
|
|
||||||
.thenExecute {
|
.thenExecute {
|
||||||
|
ComputerCraft.monitorRenderer = renderer
|
||||||
helper.positionAtArmorStand()
|
helper.positionAtArmorStand()
|
||||||
|
|
||||||
// Get the monitor and peripheral. This forces us to create a server monitor at this location.
|
// Get the monitor and peripheral. This forces us to create a server monitor at this location.
|
||||||
val monitor = helper.getBlockEntity(BlockPos(2, 2, 2)) as TileMonitor
|
val monitor = helper.getBlockEntity(BlockPos(2, 2, 3), Registry.ModBlockEntities.MONITOR_ADVANCED.get())
|
||||||
monitor.getCapability(Capabilities.CAPABILITY_PERIPHERAL)
|
monitor.getCapability(Capabilities.CAPABILITY_PERIPHERAL)
|
||||||
|
|
||||||
val terminal = monitor.cachedServerMonitor.terminal
|
val terminal = monitor.cachedServerMonitor.terminal
|
||||||
@ -64,4 +66,21 @@ class Monitor_Test {
|
|||||||
}
|
}
|
||||||
.thenScreenshot()
|
.thenScreenshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GameTest(batch = "client:Monitor_Test.Looks_acceptable", timeoutTicks = CLIENT_TIMEOUT, template = LOOKS_ACCEPTABLE)
|
||||||
|
fun Looks_acceptable(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.TBO)
|
||||||
|
|
||||||
|
@GameTest(batch = "client:Monitor_Test.Looks_acceptable_dark", timeoutTicks = CLIENT_TIMEOUT, template = LOOKS_ACCEPTABLE_DARK)
|
||||||
|
fun Looks_acceptable_dark(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.TBO)
|
||||||
|
|
||||||
|
@GameTest(batch = "client:Monitor_Test.Looks_acceptable_vbo", timeoutTicks = CLIENT_TIMEOUT, template = LOOKS_ACCEPTABLE)
|
||||||
|
fun Looks_acceptable_vbo(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.VBO)
|
||||||
|
|
||||||
|
@GameTest(batch = "client:Monitor_Test.Looks_acceptable_dark_vbo", timeoutTicks = CLIENT_TIMEOUT, template = LOOKS_ACCEPTABLE_DARK)
|
||||||
|
fun Looks_acceptable_dark_vbo(helper: GameTestHelper) = looksAcceptable(helper, renderer = MonitorRenderer.VBO)
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val LOOKS_ACCEPTABLE = "looks_acceptable"
|
||||||
|
const val LOOKS_ACCEPTABLE_DARK = "looks_acceptable_dark"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
17
src/testMod/java/dan200/computercraft/ingame/PrintoutTest.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package dan200.computercraft.ingame
|
||||||
|
|
||||||
|
import dan200.computercraft.ingame.api.Timeouts
|
||||||
|
import dan200.computercraft.ingame.api.positionAtArmorStand
|
||||||
|
import dan200.computercraft.ingame.api.sequence
|
||||||
|
import dan200.computercraft.ingame.api.thenScreenshot
|
||||||
|
import net.minecraft.gametest.framework.GameTest
|
||||||
|
import net.minecraft.gametest.framework.GameTestHelper
|
||||||
|
|
||||||
|
class PrintoutTest {
|
||||||
|
@GameTest(batch = "client:Printout_Test.In_frame_at_night", timeoutTicks = Timeouts.CLIENT_TIMEOUT)
|
||||||
|
fun In_frame_at_night(helper: GameTestHelper) = helper.sequence {
|
||||||
|
this
|
||||||
|
.thenExecute { helper.positionAtArmorStand() }
|
||||||
|
.thenScreenshot()
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
package dan200.computercraft.ingame
|
package dan200.computercraft.ingame
|
||||||
|
|
||||||
|
import dan200.computercraft.ingame.api.Timeouts.COMPUTER_TIMEOUT
|
||||||
import dan200.computercraft.ingame.api.sequence
|
import dan200.computercraft.ingame.api.sequence
|
||||||
import dan200.computercraft.ingame.api.thenComputerOk
|
import dan200.computercraft.ingame.api.thenComputerOk
|
||||||
import net.minecraft.gametest.framework.GameTest
|
import net.minecraft.gametest.framework.GameTest
|
||||||
import net.minecraft.gametest.framework.GameTestHelper
|
import net.minecraft.gametest.framework.GameTestHelper
|
||||||
|
|
||||||
class Turtle_Test {
|
class Turtle_Test {
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Unequip_refreshes_peripheral(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,7 +15,7 @@ class Turtle_Test {
|
|||||||
*
|
*
|
||||||
* @see [#537](https://github.com/SquidDev-CC/CC-Tweaked/issues/537)
|
* @see [#537](https://github.com/SquidDev-CC/CC-Tweaked/issues/537)
|
||||||
*/
|
*/
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Shears_sheep(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Shears_sheep(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,7 +23,7 @@ class Turtle_Test {
|
|||||||
*
|
*
|
||||||
* @see [#518](https://github.com/SquidDev-CC/CC-Tweaked/issues/518)
|
* @see [#518](https://github.com/SquidDev-CC/CC-Tweaked/issues/518)
|
||||||
*/
|
*/
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Place_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Place_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +31,7 @@ class Turtle_Test {
|
|||||||
*
|
*
|
||||||
* @see [#385](https://github.com/SquidDev-CC/CC-Tweaked/issues/385)
|
* @see [#385](https://github.com/SquidDev-CC/CC-Tweaked/issues/385)
|
||||||
*/
|
*/
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Place_waterlogged(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Place_waterlogged(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +39,7 @@ class Turtle_Test {
|
|||||||
*
|
*
|
||||||
* @see [#297](https://github.com/SquidDev-CC/CC-Tweaked/issues/297)
|
* @see [#297](https://github.com/SquidDev-CC/CC-Tweaked/issues/297)
|
||||||
*/
|
*/
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Gather_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Gather_lava(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +47,7 @@ class Turtle_Test {
|
|||||||
*
|
*
|
||||||
* @see [#258](https://github.com/SquidDev-CC/CC-Tweaked/issues/258)
|
* @see [#258](https://github.com/SquidDev-CC/CC-Tweaked/issues/258)
|
||||||
*/
|
*/
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Hoe_dirt(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Hoe_dirt(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,14 +55,14 @@ class Turtle_Test {
|
|||||||
*
|
*
|
||||||
* @see [#691](https://github.com/SquidDev-CC/CC-Tweaked/issues/691)
|
* @see [#691](https://github.com/SquidDev-CC/CC-Tweaked/issues/691)
|
||||||
*/
|
*/
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Place_monitor(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Place_monitor(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks turtles can place into compostors. These are non-typical inventories, so
|
* Checks turtles can place into compostors. These are non-typical inventories, so
|
||||||
* worth testing.
|
* worth testing.
|
||||||
*/
|
*/
|
||||||
@GameTest(timeoutTicks = TIMEOUT)
|
@GameTest(timeoutTicks = COMPUTER_TIMEOUT)
|
||||||
fun Use_compostors(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Use_compostors(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -71,8 +72,4 @@ class Turtle_Test {
|
|||||||
*/
|
*/
|
||||||
@GameTest(required = false)
|
@GameTest(required = false)
|
||||||
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { thenComputerOk() }
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TIMEOUT = 200
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,32 @@ import net.minecraft.client.Screenshot
|
|||||||
import net.minecraft.commands.arguments.blocks.BlockInput
|
import net.minecraft.commands.arguments.blocks.BlockInput
|
||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.gametest.framework.GameTestAssertException
|
import net.minecraft.gametest.framework.GameTestAssertException
|
||||||
|
import net.minecraft.gametest.framework.GameTestAssertPosException
|
||||||
import net.minecraft.gametest.framework.GameTestHelper
|
import net.minecraft.gametest.framework.GameTestHelper
|
||||||
import net.minecraft.gametest.framework.GameTestSequence
|
import net.minecraft.gametest.framework.GameTestSequence
|
||||||
import net.minecraft.world.entity.decoration.ArmorStand
|
import net.minecraft.world.entity.decoration.ArmorStand
|
||||||
import net.minecraft.world.level.block.Blocks
|
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.BlockState
|
||||||
import net.minecraft.world.level.levelgen.Heightmap
|
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
|
object Times {
|
||||||
|
const val NOON: Long = 6000
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom timeouts for various test types.
|
||||||
|
*/
|
||||||
|
object Timeouts {
|
||||||
|
const val COMPUTER_TIMEOUT: Int = 200
|
||||||
|
|
||||||
|
const val CLIENT_TIMEOUT: Int = 400
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait until a computer has finished running and check it is OK.
|
* Wait until a computer has finished running and check it is OK.
|
||||||
@ -58,25 +71,31 @@ fun GameTestSequence.thenScreenshot(name: String? = null): GameTestSequence {
|
|||||||
val suffix = if (name == null) "" else "-$name"
|
val suffix = if (name == null) "" else "-$name"
|
||||||
val fullName = "${parent.testName}$suffix"
|
val fullName = "${parent.testName}$suffix"
|
||||||
|
|
||||||
val counter = AtomicInteger()
|
var counter = 0
|
||||||
|
val hasScreenshot = AtomicBoolean()
|
||||||
|
|
||||||
return this
|
return this
|
||||||
// Wait until all chunks have been rendered and we're idle for an extended period.
|
// Wait until all chunks have been rendered and we're idle for an extended period.
|
||||||
.thenExecute { counter.set(0) }
|
.thenExecute { counter = 0 }
|
||||||
.thenWaitUntil {
|
.thenWaitUntil {
|
||||||
if (Minecraft.getInstance().levelRenderer.hasRenderedAllChunks()) {
|
val renderer = Minecraft.getInstance().levelRenderer
|
||||||
val idleFor = counter.getAndIncrement()
|
if (renderer.chunkRenderDispatcher != null && renderer.hasRenderedAllChunks()) {
|
||||||
|
val idleFor = ++counter
|
||||||
if (idleFor <= 20) throw GameTestAssertException("Only idle for $idleFor ticks")
|
if (idleFor <= 20) throw GameTestAssertException("Only idle for $idleFor ticks")
|
||||||
} else {
|
} else {
|
||||||
counter.set(0)
|
counter = 0
|
||||||
throw GameTestAssertException("Waiting for client to finish rendering")
|
throw GameTestAssertException("Waiting for client to finish rendering")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now disable the GUI, take a screenshot and reenable it. We sleep either side to give the client time to do
|
// Now disable the GUI, take a screenshot and reenable it. We sleep either side to give the client time to do
|
||||||
// its thing.
|
// its thing.
|
||||||
.thenExecute { Minecraft.getInstance().options.hideGui = true }
|
.thenExecute {
|
||||||
|
Minecraft.getInstance().options.hideGui = true
|
||||||
|
hasScreenshot.set(false)
|
||||||
|
}
|
||||||
.thenIdle(5) // Some delay before/after to ensure the render thread has caught up.
|
.thenIdle(5) // Some delay before/after to ensure the render thread has caught up.
|
||||||
.thenOnClient { screenshot("$fullName.png") }
|
.thenOnClient { screenshot("$fullName.png") { hasScreenshot.set(true) } }
|
||||||
.thenIdle(2)
|
.thenWaitUntil { if (!hasScreenshot.get()) throw GameTestAssertException("Screenshot does not exist") }
|
||||||
.thenExecute {
|
.thenExecute {
|
||||||
Minecraft.getInstance().options.hideGui = false
|
Minecraft.getInstance().options.hideGui = false
|
||||||
|
|
||||||
@ -87,21 +106,22 @@ fun GameTestSequence.thenScreenshot(name: String? = null): GameTestSequence {
|
|||||||
if (!Files.exists(originalPath)) throw GameTestAssertException("$fullName does not exist. Use `/cctest promote' to create it.");
|
if (!Files.exists(originalPath)) throw GameTestAssertException("$fullName does not exist. Use `/cctest promote' to create it.");
|
||||||
|
|
||||||
val screenshot = ImageIO.read(screenshotPath.toFile())
|
val screenshot = ImageIO.read(screenshotPath.toFile())
|
||||||
|
?: throw GameTestAssertException("Error reading screenshot from $screenshotPath")
|
||||||
val original = ImageIO.read(originalPath.toFile())
|
val original = ImageIO.read(originalPath.toFile())
|
||||||
|
|
||||||
if (screenshot.width != original.width || screenshot.height != original.height) {
|
if (screenshot.width != original.width || screenshot.height != original.height) {
|
||||||
throw GameTestAssertException("$fullName screenshot is ${screenshot.width}x${screenshot.height} but original is ${original.width}x${original.height}")
|
throw GameTestAssertException("$fullName screenshot is ${screenshot.width}x${screenshot.height} but original is ${original.width}x${original.height}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImageUtils.areSame(screenshot, original)) return@thenExecute
|
|
||||||
|
|
||||||
ImageUtils.writeDifference(screenshotsPath.resolve("$fullName.diff.png"), screenshot, original)
|
ImageUtils.writeDifference(screenshotsPath.resolve("$fullName.diff.png"), screenshot, original)
|
||||||
throw GameTestAssertException("Images are different.")
|
if (!ImageUtils.areSame(screenshot, original)) throw GameTestAssertException("Images are different.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val GameTestHelper.testName: String get() = testInfo.testName
|
val GameTestHelper.testName: String get() = testInfo.testName
|
||||||
|
|
||||||
|
val GameTestHelper.structureName: String get() = testInfo.structureName
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify a block state within the test.
|
* Modify a block state within the test.
|
||||||
*/
|
*/
|
||||||
@ -113,38 +133,30 @@ fun GameTestHelper.sequence(run: GameTestSequence.() -> GameTestSequence) {
|
|||||||
run(startSequence()).thenSucceed()
|
run(startSequence()).thenSucceed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T : BlockEntity> GameTestHelper.getBlockEntity(pos: BlockPos, type: BlockEntityType<T>): T {
|
||||||
|
val tile = getBlockEntity(pos)
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return when {
|
||||||
|
tile == null -> throw GameTestAssertPosException("Expected ${type.registryName}, but no tile was there", absolutePos(pos), pos, 0)
|
||||||
|
tile.type != type -> throw GameTestAssertPosException("Expected ${type.registryName} but got ${tile.type.registryName}", absolutePos(pos), pos, 0)
|
||||||
|
else -> tile as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a block within the test structure.
|
* Set a block within the test structure.
|
||||||
*/
|
*/
|
||||||
fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3)
|
fun GameTestHelper.setBlock(pos: BlockPos, state: BlockInput) = state.place(level, absolutePos(pos), 3)
|
||||||
|
|
||||||
/**
|
|
||||||
* "Normalise" the current world in preparation for screenshots.
|
|
||||||
*
|
|
||||||
* Basically removes any dirt and replaces it with concrete.
|
|
||||||
*/
|
|
||||||
fun GameTestHelper.normaliseScene() {
|
|
||||||
val y = level.getHeightmapPos(Heightmap.Types.WORLD_SURFACE, absolutePos(BlockPos.ZERO))
|
|
||||||
for (x in -100..100) {
|
|
||||||
for (z in -100..100) {
|
|
||||||
val pos = y.offset(x, -3, z)
|
|
||||||
val block = level.getBlockState(pos).block
|
|
||||||
if (block == Blocks.DIRT || block == Blocks.GRASS_BLOCK) {
|
|
||||||
level.setBlock(pos, Blocks.WHITE_CONCRETE.defaultBlockState(), 3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position the player at an armor stand.
|
* Position the player at an armor stand.
|
||||||
*/
|
*/
|
||||||
fun GameTestHelper.positionAtArmorStand() {
|
fun GameTestHelper.positionAtArmorStand() {
|
||||||
val entities = level.getEntities(null, bounds) { it.name.string == testName }
|
val entities = level.getEntities(null, bounds) { it.name.string == structureName }
|
||||||
if (entities.size <= 0 || entities[0] !is ArmorStand) throw IllegalStateException("Cannot find armor stand")
|
if (entities.size <= 0 || entities[0] !is ArmorStand) throw GameTestAssertException("Cannot find armor stand")
|
||||||
|
|
||||||
val stand = entities[0] as ArmorStand
|
val stand = entities[0] as ArmorStand
|
||||||
val player = level.randomPlayer ?: throw NullPointerException("Player does not exist")
|
val player = level.randomPlayer ?: throw GameTestAssertException("Player does not exist")
|
||||||
|
|
||||||
player.connection.teleport(stand.x, stand.y, stand.z, stand.yRot, stand.xRot)
|
player.connection.teleport(stand.x, stand.y, stand.z, stand.yRot, stand.xRot)
|
||||||
}
|
}
|
||||||
@ -153,7 +165,10 @@ fun GameTestHelper.positionAtArmorStand() {
|
|||||||
class ClientTestHelper {
|
class ClientTestHelper {
|
||||||
val minecraft: Minecraft = Minecraft.getInstance()
|
val minecraft: Minecraft = Minecraft.getInstance()
|
||||||
|
|
||||||
fun screenshot(name: String) {
|
fun screenshot(name: String, callback: () -> Unit = {}) {
|
||||||
Screenshot.grab(minecraft.gameDirectory, name, minecraft.mainRenderTarget) { TestMod.log.info(it.string) }
|
Screenshot.grab(minecraft.gameDirectory, name, minecraft.mainRenderTarget) {
|
||||||
|
TestMod.log.info(it.string)
|
||||||
|
callback()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,13 +59,6 @@ class CCTestCommand
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} ) )
|
} ) )
|
||||||
.then( literal( "runall" ).executes( context -> {
|
|
||||||
GameTestRegistry.forgetFailedTests();
|
|
||||||
MultipleTestTracker result = TestHooks.runTests();
|
|
||||||
result.addListener( new Callback( context.getSource(), result ) );
|
|
||||||
result.addFailureListener( x -> GameTestRegistry.rememberFailedTest( x.getTestFunction() ) );
|
|
||||||
return 0;
|
|
||||||
} ) )
|
|
||||||
|
|
||||||
.then( literal( "promote" ).executes( context -> {
|
.then( literal( "promote" ).executes( context -> {
|
||||||
if( !FMLLoader.getDist().isClient() ) return error( context.getSource(), "Cannot run on server" );
|
if( !FMLLoader.getDist().isClient() ) return error( context.getSource(), "Cannot run on server" );
|
||||||
@ -94,6 +87,7 @@ class CCTestCommand
|
|||||||
armorStand.readAdditionalSaveData( nbt );
|
armorStand.readAdditionalSaveData( nbt );
|
||||||
armorStand.copyPosition( player );
|
armorStand.copyPosition( player );
|
||||||
armorStand.setCustomName( new TextComponent( info.getTestName() ) );
|
armorStand.setCustomName( new TextComponent( info.getTestName() ) );
|
||||||
|
player.getLevel().addFreshEntity( armorStand );
|
||||||
return 0;
|
return 0;
|
||||||
} ) )
|
} ) )
|
||||||
);
|
);
|
||||||
|
@ -18,9 +18,9 @@ public final class ImageUtils
|
|||||||
private static final Logger LOG = LogManager.getLogger( ImageUtils.class );
|
private static final Logger LOG = LogManager.getLogger( ImageUtils.class );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow 0.08% of pixels to fail. This allows for slight differences at the edges.
|
* Allow 0.3% of pixels to fail. This allows for slight differences at the edges.
|
||||||
*/
|
*/
|
||||||
private static final double PIXEL_THRESHOLD = 0.0008;
|
private static final double PIXEL_THRESHOLD = 0.003;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum possible distance between two colours. Floating point differences means we need some fuzziness here.
|
* Maximum possible distance between two colours. Floating point differences means we need some fuzziness here.
|
||||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.ingame.mod;
|
|||||||
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.SharedConstants;
|
import net.minecraft.SharedConstants;
|
||||||
|
import dan200.computercraft.ingame.api.Times;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@ -29,7 +30,6 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID )
|
@Mod.EventBusSubscriber( modid = TestMod.MOD_ID )
|
||||||
public class TestHooks
|
public class TestHooks
|
||||||
@ -58,7 +58,7 @@ public class TestHooks
|
|||||||
rules.getRule( GameRules.RULE_DOMOBSPAWNING ).set( false, server );
|
rules.getRule( GameRules.RULE_DOMOBSPAWNING ).set( false, server );
|
||||||
|
|
||||||
ServerLevel world = event.getServer().getLevel( Level.OVERWORLD );
|
ServerLevel world = event.getServer().getLevel( Level.OVERWORLD );
|
||||||
if( world != null ) world.setDayTime( 6000 );
|
if( world != null ) world.setDayTime( Times.NOON );
|
||||||
|
|
||||||
LOG.info( "Cleaning up after last run" );
|
LOG.info( "Cleaning up after last run" );
|
||||||
CommandSourceStack source = server.createCommandSourceStack();
|
CommandSourceStack source = server.createCommandSourceStack();
|
||||||
@ -86,16 +86,12 @@ public class TestHooks
|
|||||||
{
|
{
|
||||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||||
CommandSourceStack source = server.createCommandSourceStack();
|
CommandSourceStack source = server.createCommandSourceStack();
|
||||||
Collection<TestFunction> tests = GameTestRegistry.getAllTestFunctions()
|
Collection<TestFunction> tests = GameTestRegistry.getAllTestFunctions();
|
||||||
.stream()
|
|
||||||
.filter( x -> FMLLoader.getDist().isClient() | !x.getBatchName().startsWith( "client" ) )
|
|
||||||
.collect( Collectors.toList() );
|
|
||||||
|
|
||||||
LOG.info( "Running {} tests...", tests.size() );
|
LOG.info( "Running {} tests...", tests.size() );
|
||||||
|
|
||||||
Collection<GameTestBatch> batches = GameTestRunner.groupTestsIntoBatches( tests );
|
return new MultipleTestTracker( GameTestRunner.runTests(
|
||||||
return new MultipleTestTracker( GameTestRunner.runTestBatches(
|
tests, getStart( source ), Rotation.NONE, source.getLevel(), GameTestTicker.SINGLETON, 8
|
||||||
batches, getStart( source ), Rotation.NONE, source.getLevel(), GameTestTicker.SINGLETON, 8
|
|
||||||
) );
|
) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ package dan200.computercraft.ingame.mod;
|
|||||||
import net.minecraft.gametest.framework.GameTest;
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
import net.minecraft.gametest.framework.GameTestRegistry;
|
import net.minecraft.gametest.framework.GameTestRegistry;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.fml.loading.FMLLoader;
|
||||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
|
|
||||||
@ -47,6 +48,9 @@ class TestLoader
|
|||||||
throw new RuntimeException( e );
|
throw new RuntimeException( e );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GameTest test = method.getAnnotation( GameTest.class );
|
||||||
|
if( test.batch().startsWith( "client" ) && !FMLLoader.getDist().isClient() ) return;
|
||||||
|
|
||||||
GameTestRegistry.register( method );
|
GameTestRegistry.register( method );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 44 KiB |
@ -27,112 +27,114 @@
|
|||||||
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
{pos: [0, 1, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
{pos: [0, 1, 1], state: "minecraft:shroomlight"},
|
||||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
{pos: [0, 1, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 1, 3], state: "minecraft:air"},
|
{pos: [0, 1, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 1, 4], state: "minecraft:air"},
|
{pos: [0, 1, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
{pos: [1, 1, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 1, 1], state: "minecraft:air"},
|
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||||
{pos: [1, 1, 2], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lu}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||||
{pos: [1, 1, 3], state: "minecraft:air"},
|
{pos: [1, 1, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lu}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||||
{pos: [1, 1, 4], state: "minecraft:air"},
|
{pos: [1, 1, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
{pos: [2, 1, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||||
{pos: [2, 1, 2], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lru}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
{pos: [2, 1, 2], state: "minecraft:air"},
|
||||||
{pos: [2, 1, 3], state: "minecraft:air"},
|
{pos: [2, 1, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lru}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||||
{pos: [2, 1, 4], state: "minecraft:air"},
|
{pos: [2, 1, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 1, 0], state: "minecraft:air"},
|
{pos: [3, 1, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 1, 1], state: "minecraft:air"},
|
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||||
{pos: [3, 1, 2], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:ru}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||||
{pos: [3, 1, 3], state: "minecraft:air"},
|
{pos: [3, 1, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:ru}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||||
{pos: [3, 1, 4], state: "minecraft:air"},
|
{pos: [3, 1, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 1, 0], state: "minecraft:air"},
|
{pos: [4, 1, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 1, 1], state: "minecraft:air"},
|
{pos: [4, 1, 1], state: "minecraft:shroomlight"},
|
||||||
{pos: [4, 1, 2], state: "minecraft:air"},
|
{pos: [4, 1, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 1, 3], state: "minecraft:air"},
|
{pos: [4, 1, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 1, 4], state: "minecraft:air"},
|
{pos: [4, 1, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
{pos: [0, 2, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
{pos: [0, 2, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
{pos: [0, 2, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 2, 3], state: "minecraft:air"},
|
{pos: [0, 2, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 2, 4], state: "minecraft:air"},
|
{pos: [0, 2, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
{pos: [1, 2, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||||
{pos: [1, 2, 2], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:ld}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||||
{pos: [1, 2, 3], state: "minecraft:air"},
|
{pos: [1, 2, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:ld}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
||||||
{pos: [1, 2, 4], state: "minecraft:air"},
|
{pos: [1, 2, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
{pos: [2, 2, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||||
{pos: [2, 2, 2], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lrd}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||||
{pos: [2, 2, 3], state: "minecraft:air"},
|
{pos: [2, 2, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lrd}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
||||||
{pos: [2, 2, 4], state: "minecraft:air"},
|
{pos: [2, 2, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 2, 0], state: "minecraft:air"},
|
{pos: [3, 2, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 2, 1], state: "minecraft:air"},
|
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||||
{pos: [3, 2, 2], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:rd}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||||
{pos: [3, 2, 3], state: "minecraft:air"},
|
{pos: [3, 2, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:rd}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
||||||
{pos: [3, 2, 4], state: "minecraft:air"},
|
{pos: [3, 2, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 2, 0], state: "minecraft:air"},
|
{pos: [4, 2, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 2, 1], state: "minecraft:air"},
|
{pos: [4, 2, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 2, 2], state: "minecraft:air"},
|
{pos: [4, 2, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 2, 3], state: "minecraft:air"},
|
{pos: [4, 2, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 2, 4], state: "minecraft:air"},
|
{pos: [4, 2, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 3, 0], state: "minecraft:air"},
|
{pos: [0, 3, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 3, 1], state: "minecraft:air"},
|
{pos: [0, 3, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 3, 2], state: "minecraft:air"},
|
{pos: [0, 3, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 3, 3], state: "minecraft:air"},
|
{pos: [0, 3, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 3, 4], state: "minecraft:air"},
|
{pos: [0, 3, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 3, 0], state: "minecraft:air"},
|
{pos: [1, 3, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 3, 1], state: "minecraft:air"},
|
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||||
{pos: [1, 3, 2], state: "minecraft:air"},
|
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||||
{pos: [1, 3, 3], state: "minecraft:air"},
|
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||||
{pos: [1, 3, 4], state: "minecraft:air"},
|
{pos: [1, 3, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 3, 0], state: "minecraft:air"},
|
{pos: [2, 3, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 3, 1], state: "minecraft:air"},
|
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||||
{pos: [2, 3, 2], state: "minecraft:air"},
|
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||||
{pos: [2, 3, 3], state: "minecraft:air"},
|
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||||
{pos: [2, 3, 4], state: "minecraft:air"},
|
{pos: [2, 3, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 3, 0], state: "minecraft:air"},
|
{pos: [3, 3, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 3, 1], state: "minecraft:air"},
|
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||||
{pos: [3, 3, 2], state: "minecraft:air"},
|
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||||
{pos: [3, 3, 3], state: "minecraft:air"},
|
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||||
{pos: [3, 3, 4], state: "minecraft:air"},
|
{pos: [3, 3, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 3, 0], state: "minecraft:air"},
|
{pos: [4, 3, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 3, 1], state: "minecraft:air"},
|
{pos: [4, 3, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 3, 2], state: "minecraft:air"},
|
{pos: [4, 3, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 3, 3], state: "minecraft:air"},
|
{pos: [4, 3, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 3, 4], state: "minecraft:air"},
|
{pos: [4, 3, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 4, 0], state: "minecraft:air"},
|
{pos: [0, 4, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 4, 1], state: "minecraft:air"},
|
{pos: [0, 4, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 4, 2], state: "minecraft:air"},
|
{pos: [0, 4, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 4, 3], state: "minecraft:air"},
|
{pos: [0, 4, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [0, 4, 4], state: "minecraft:air"},
|
{pos: [0, 4, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 4, 0], state: "minecraft:air"},
|
{pos: [1, 4, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 4, 1], state: "minecraft:air"},
|
{pos: [1, 4, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 4, 2], state: "minecraft:air"},
|
{pos: [1, 4, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [1, 4, 3], state: "minecraft:air"},
|
{pos: [1, 4, 3], state: "minecraft:shroomlight"},
|
||||||
{pos: [1, 4, 4], state: "minecraft:air"},
|
{pos: [1, 4, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 4, 0], state: "minecraft:air"},
|
{pos: [2, 4, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 4, 1], state: "minecraft:air"},
|
{pos: [2, 4, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 4, 2], state: "minecraft:air"},
|
{pos: [2, 4, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 4, 3], state: "minecraft:air"},
|
{pos: [2, 4, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [2, 4, 4], state: "minecraft:air"},
|
{pos: [2, 4, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 4, 0], state: "minecraft:air"},
|
{pos: [3, 4, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 4, 1], state: "minecraft:air"},
|
{pos: [3, 4, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 4, 2], state: "minecraft:air"},
|
{pos: [3, 4, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [3, 4, 3], state: "minecraft:air"},
|
{pos: [3, 4, 3], state: "minecraft:shroomlight"},
|
||||||
{pos: [3, 4, 4], state: "minecraft:air"},
|
{pos: [3, 4, 4], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 4, 0], state: "minecraft:air"},
|
{pos: [4, 4, 0], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 4, 1], state: "minecraft:air"},
|
{pos: [4, 4, 1], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 4, 2], state: "minecraft:air"},
|
{pos: [4, 4, 2], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 4, 3], state: "minecraft:air"},
|
{pos: [4, 4, 3], state: "minecraft:white_concrete"},
|
||||||
{pos: [4, 4, 4], state: "minecraft:air"}
|
{pos: [4, 4, 4], state: "minecraft:white_concrete"}
|
||||||
],
|
],
|
||||||
entities: [
|
entities: [
|
||||||
{blockPos: [2, 1, 0], pos: [2.573085437203247d, 1.0d, 0.40392497295766816d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CanUpdate: 1b, CustomName: '{"text":"monitor_test.looks_acceptable"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: 0s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 1b, PortalCooldown: 0, Pos: [121.57308543720325d, 6.0d, 139.40392497295767d], Pose: {}, Rotation: [-0.2999616f, 16.965813f], ShowArms: 0b, Small: 0b, UUID: [I; -1245769654, -1089124211, -1971323071, 221540869], id: "minecraft:armor_stand"}}
|
{blockPos: [2, 1, 1], pos: [2.392713937302208d, 1.0d, 1.300000011920929d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CanUpdate: 1b, CustomName: '{"text":"monitor_test.looks_acceptable"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [21.392713937302208d, 6.0d, 35.30000001192093d], Pose: {}, Rotation: [0.15043798f, 15.347454f], ShowArms: 0b, Small: 0b, UUID: [I; 474729512, 2108312608, -1494837479, 630038770], id: "minecraft:armor_stand"}}
|
||||||
],
|
],
|
||||||
palette: [
|
palette: [
|
||||||
"minecraft:polished_andesite",
|
"minecraft:polished_andesite",
|
||||||
|
"minecraft:white_concrete",
|
||||||
|
"minecraft:shroomlight",
|
||||||
"minecraft:air",
|
"minecraft:air",
|
||||||
"computercraft:monitor_advanced{facing:north,orientation:north,state:lu}",
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:lu}",
|
||||||
"computercraft:monitor_advanced{facing:north,orientation:north,state:lru}",
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:lru}",
|
||||||
|
@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
DataVersion: 2730,
|
||||||
|
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:white_concrete"},
|
||||||
|
{pos: [0, 1, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 1, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 1, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lu}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||||
|
{pos: [1, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lru}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||||
|
{pos: [2, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:ru}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 0, id: "computercraft:monitor_advanced"}},
|
||||||
|
{pos: [3, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:ld}", nbt: {Height: 2, Width: 3, XIndex: 2, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
||||||
|
{pos: [1, 2, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:lrd}", nbt: {Height: 2, Width: 3, XIndex: 1, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
||||||
|
{pos: [2, 2, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 3], state: "computercraft:monitor_advanced{facing:north,orientation:north,state:rd}", nbt: {Height: 2, Width: 3, XIndex: 0, YIndex: 1, id: "computercraft:monitor_advanced"}},
|
||||||
|
{pos: [3, 2, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [2, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [3, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [4, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 4], state: "minecraft:white_concrete"}
|
||||||
|
],
|
||||||
|
entities: [
|
||||||
|
{blockPos: [2, 1, 1], pos: [2.3927139373022044d, 1.0d, 1.300000011920929d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CanUpdate: 1b, CustomName: '{"text":"monitor_test.looks_acceptable_dark"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [64.3927139373022d, 6.0d, 59.30000001192093d], Pose: {}, Rotation: [0.15043798f, 15.347454f], ShowArms: 0b, Small: 0b, UUID: [I; -1516632699, -1770765897, -1362337958, -475677268], id: "minecraft:armor_stand"}}
|
||||||
|
],
|
||||||
|
palette: [
|
||||||
|
"minecraft:polished_andesite",
|
||||||
|
"minecraft:white_concrete",
|
||||||
|
"minecraft:air",
|
||||||
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:lu}",
|
||||||
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:lru}",
|
||||||
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:ru}",
|
||||||
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:ld}",
|
||||||
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:lrd}",
|
||||||
|
"computercraft:monitor_advanced{facing:north,orientation:north,state:rd}"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
{
|
||||||
|
DataVersion: 2730,
|
||||||
|
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:white_concrete"},
|
||||||
|
{pos: [0, 1, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 1, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 1, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [2, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [4, 1, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 1, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 2, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [2, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [3, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [4, 2, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 2, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 3, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [2, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [3, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{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:white_concrete"},
|
||||||
|
{pos: [4, 3, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 3, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [0, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [1, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [2, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [3, 4, 4], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 0], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 1], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 2], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 3], state: "minecraft:white_concrete"},
|
||||||
|
{pos: [4, 4, 4], state: "minecraft:white_concrete"}
|
||||||
|
],
|
||||||
|
entities: [
|
||||||
|
{blockPos: [2, 2, 3], pos: [2.5d, 2.5d, 3.96875d], nbt: {Air: 300s, CanUpdate: 1b, Facing: 2b, FallDistance: 0.0f, Fire: -1s, Fixed: 0b, Invisible: 0b, Invulnerable: 0b, Item: {Count: 1b, id: "computercraft:printed_page", tag: {Color0: "eeeeeeeeeeeeeeeeeeeeeeeee", Color1: "eeeeeeeeeeeeeeeeeeeeeeeee", Color10: "eeeeeeeeeeeeeeeeeeeeeeeee", Color11: "eeeeeeeeeeeeeeeeeeeeeeeee", Color12: "eeeeeeeeeeeeeeeeeeeeeeeee", Color13: "eeeeeeeeeeeeeeeeeeeeeeeee", Color14: "eeeeeeeeeeeeeeeeeeeeeeeee", Color15: "eeeeeeeeeeeeeeeeeeeeeeeee", Color16: "eeeeeeeeeeeeeeeeeeeeeeeee", Color17: "eeeeeeeeeeeeeeeeeeeeeeeee", Color18: "eeeeeeeeeeeeeeeeeeeeeeeee", Color19: "eeeeeeeeeeeeeeeeeeeeeeeee", Color2: "eeeeeeeeeeeeeeeeeeeeeeeee", Color20: "eeeeeeeeeeeeeeeeeeeeeeeee", Color3: "eeeeeeeeeeeeeeeeeeeeeeeee", Color4: "eeeeeeeeeeeeeeeeeeeeeeeee", Color5: "eeeeeeeeeeeeeeeeeeeeeeeee", Color6: "eeeeeeeeeeeeeeeeeeeeeeeee", Color7: "eeeeeeeeeeeeeeeeeeeeeeeee", Color8: "eeeeeeeeeeeeeeeeeeeeeeeee", Color9: "eeeeeeeeeeeeeeeeeeeeeeeee", Pages: 1, Text0: "If you're reading this, ", Text1: "the test failed. ", Text10: " ", Text11: " ", Text12: " ", Text13: " ", Text14: " ", Text15: " ", Text16: " ", Text17: " ", Text18: " ", Text19: " ", Text2: " ", Text20: " ", Text3: " ", Text4: " ", Text5: " ", Text6: " ", Text7: " ", Text8: " ", Text9: " ", Title: "a.lua"}}, ItemDropChance: 1.0f, ItemRotation: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [10.5d, 7.5d, 44.96875d], Rotation: [180.0f, 0.0f], TileX: 10, TileY: 7, TileZ: 44, UUID: [I; 1043973837, -2076424529, -1762893135, -165665834], id: "minecraft:item_frame"}},
|
||||||
|
{blockPos: [2, 1, 2], pos: [2.583196949396914d, 1.0d, 2.6089749199596d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CanUpdate: 1b, CustomName: '{"text":"printouttest.in_frame_at_night"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [10.583196949396914d, 6.0d, 43.6089749199596d], Pose: {}, Rotation: [1.3504658f, 6.7031174f], ShowArms: 0b, Small: 0b, UUID: [I; -1917933016, 1390888530, -2109873447, -2136052677], id: "minecraft:armor_stand"}}
|
||||||
|
],
|
||||||
|
palette: [
|
||||||
|
"minecraft:polished_andesite",
|
||||||
|
"minecraft:white_concrete",
|
||||||
|
"minecraft:air"
|
||||||
|
]
|
||||||
|
}
|
1
tools/convert-structure.py
Normal file → Executable file
@ -1,3 +1,4 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
import sys
|
import sys
|
||||||
import nbtlib
|
import nbtlib
|
||||||
from nbtlib.tag import Compound, Int, List, String
|
from nbtlib.tag import Compound, Int, List, String
|
||||||
|