mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-13 19:57:12 +00:00
Compare commits
5 Commits
v1.19.3-1.
...
v1.19.3-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f561572509 | ||
|
|
edb21f33be | ||
|
|
02b68b259e | ||
|
|
28a55349a9 | ||
|
|
2457a31728 |
@@ -6,7 +6,7 @@ kotlin.jvm.target.validation.mode=error
|
|||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=true
|
isUnstable=true
|
||||||
modVersion=1.102.1
|
modVersion=1.102.2
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.19.3
|
mcVersion=1.19.3
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package dan200.computercraft.shared.peripheral.diskdrive;
|
package dan200.computercraft.shared.peripheral.diskdrive;
|
||||||
|
|
||||||
import com.google.errorprone.annotations.concurrent.GuardedBy;
|
import com.google.errorprone.annotations.concurrent.GuardedBy;
|
||||||
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
import dan200.computercraft.api.filesystem.WritableMount;
|
import dan200.computercraft.api.filesystem.WritableMount;
|
||||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
@@ -47,11 +48,14 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
private final NonNullList<ItemStack> inventory = NonNullList.withSize(1, ItemStack.EMPTY);
|
private final NonNullList<ItemStack> inventory = NonNullList.withSize(1, ItemStack.EMPTY);
|
||||||
|
|
||||||
private MediaStack media = MediaStack.EMPTY;
|
private MediaStack media = MediaStack.EMPTY;
|
||||||
|
private @Nullable Mount mount;
|
||||||
|
|
||||||
private boolean recordPlaying = false;
|
private boolean recordPlaying = false;
|
||||||
// In order to avoid main-thread calls in the peripheral, we set flags to mark which operation should be performed,
|
// In order to avoid main-thread calls in the peripheral, we set flags to mark which operation should be performed,
|
||||||
// then read them when ticking.
|
// then read them when ticking.
|
||||||
private final AtomicReference<RecordCommand> recordQueued = new AtomicReference<>(null);
|
private final AtomicReference<RecordCommand> recordQueued = new AtomicReference<>(null);
|
||||||
private final AtomicBoolean ejectQueued = new AtomicBoolean(false);
|
private final AtomicBoolean ejectQueued = new AtomicBoolean(false);
|
||||||
|
private final AtomicBoolean mountQueued = new AtomicBoolean(false);
|
||||||
|
|
||||||
public DiskDriveBlockEntity(BlockEntityType<DiskDriveBlockEntity> type, BlockPos pos, BlockState state) {
|
public DiskDriveBlockEntity(BlockEntityType<DiskDriveBlockEntity> type, BlockPos pos, BlockState state) {
|
||||||
super(type, pos, state);
|
super(type, pos, state);
|
||||||
@@ -109,6 +113,12 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mountQueued.get()) {
|
||||||
|
synchronized (this) {
|
||||||
|
mountAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -124,9 +134,9 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
|
|
||||||
private void updateItem() {
|
private void updateItem() {
|
||||||
var newDisk = getDiskStack();
|
var newDisk = getDiskStack();
|
||||||
if (ItemStack.isSame(newDisk, media.stack)) return;
|
if (ItemStack.isSameItemSameTags(newDisk, media.stack)) return;
|
||||||
|
|
||||||
var media = new MediaStack(newDisk.copy());
|
var media = MediaStack.of(newDisk);
|
||||||
|
|
||||||
if (newDisk.isEmpty()) {
|
if (newDisk.isEmpty()) {
|
||||||
updateBlockState(DiskDriveState.EMPTY);
|
updateBlockState(DiskDriveState.EMPTY);
|
||||||
@@ -146,12 +156,10 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
recordPlaying = false;
|
recordPlaying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mount = null;
|
||||||
this.media = media;
|
this.media = media;
|
||||||
|
|
||||||
// Mount new disk
|
mountAll();
|
||||||
if (!this.media.stack.isEmpty()) {
|
|
||||||
for (var computer : computers.entrySet()) mountDisk(computer.getKey(), computer.getValue(), this.media);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,11 +171,30 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
return media;
|
return media;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current disk stack, mounting/unmounting if needed.
|
||||||
|
*
|
||||||
|
* @param stack The new disk stack.
|
||||||
|
*/
|
||||||
void setDiskStack(ItemStack stack) {
|
void setDiskStack(ItemStack stack) {
|
||||||
setItem(0, stack);
|
setItem(0, stack);
|
||||||
setChanged();
|
setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current disk stack, assuming the underlying item does not change. Unlike
|
||||||
|
* {@link #setDiskStack(ItemStack)} this will not change any mounts.
|
||||||
|
*
|
||||||
|
* @param stack The new disk stack.
|
||||||
|
*/
|
||||||
|
void updateDiskStack(ItemStack stack) {
|
||||||
|
setItem(0, stack);
|
||||||
|
if (!ItemStack.isSameItemSameTags(stack, media.stack)) {
|
||||||
|
media = MediaStack.of(stack);
|
||||||
|
super.setChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
String getDiskMountPath(IComputerAccess computer) {
|
String getDiskMountPath(IComputerAccess computer) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@@ -176,15 +203,21 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mount(IComputerAccess computer) {
|
/**
|
||||||
|
* Attach a computer to this disk drive. This sets up the {@link MountInfo} map and flags us to mount next tick. We
|
||||||
|
* don't mount here, as that might require mutating the current stack.
|
||||||
|
*
|
||||||
|
* @param computer The computer to attach.
|
||||||
|
*/
|
||||||
|
void attach(IComputerAccess computer) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
var info = new MountInfo();
|
var info = new MountInfo();
|
||||||
computers.put(computer, info);
|
computers.put(computer, info);
|
||||||
mountDisk(computer, info, media);
|
mountQueued.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unmount(IComputerAccess computer) {
|
void detach(IComputerAccess computer) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
unmountDisk(computer, computers.remove(computer));
|
unmountDisk(computer, computers.remove(computer));
|
||||||
}
|
}
|
||||||
@@ -202,10 +235,35 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
ejectQueued.set(true);
|
ejectQueued.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add our mount to all computers.
|
||||||
|
*/
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private void mountDisk(IComputerAccess computer, MountInfo info, MediaStack disk) {
|
private void mountAll() {
|
||||||
var mount = disk.getMount((ServerLevel) getLevel());
|
doMountAll();
|
||||||
if (mount != null) {
|
mountQueued.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The worker for {@link #mountAll()}. This is responsible for creating the mount and placing it on all computers.
|
||||||
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void doMountAll() {
|
||||||
|
if (computers.isEmpty() || media.media == null) return;
|
||||||
|
|
||||||
|
if (mount == null) {
|
||||||
|
var stack = getDiskStack();
|
||||||
|
mount = media.media.createDataMount(stack, (ServerLevel) level);
|
||||||
|
setDiskStack(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount == null) return;
|
||||||
|
|
||||||
|
for (var entry : computers.entrySet()) {
|
||||||
|
var computer = entry.getKey();
|
||||||
|
var info = entry.getValue();
|
||||||
|
if (info.mountPath != null) continue;
|
||||||
|
|
||||||
if (mount instanceof WritableMount writable) {
|
if (mount instanceof WritableMount writable) {
|
||||||
// Try mounting at the lowest numbered "disk" name we can
|
// Try mounting at the lowest numbered "disk" name we can
|
||||||
var n = 1;
|
var n = 1;
|
||||||
@@ -221,12 +279,10 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
|
|||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
info.mountPath = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
computer.queueEvent("disk", computer.getAttachmentName());
|
computer.queueEvent("disk", computer.getAttachmentName());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void unmountDisk(IComputerAccess computer, MountInfo info) {
|
private static void unmountDisk(IComputerAccess computer, MountInfo info) {
|
||||||
if (info.mountPath != null) {
|
if (info.mountPath != null) {
|
||||||
|
|||||||
@@ -84,11 +84,12 @@ public class DiskDrivePeripheral implements IPeripheral {
|
|||||||
var media = diskDrive.getMedia();
|
var media = diskDrive.getMedia();
|
||||||
if (media.media == null) return;
|
if (media.media == null) return;
|
||||||
|
|
||||||
var stack = media.stack.copy();
|
// We're on the main thread so the stack and media should be in sync.
|
||||||
|
var stack = diskDrive.getDiskStack();
|
||||||
if (!media.media.setLabel(stack, label.map(StringUtil::normaliseLabel).orElse(null))) {
|
if (!media.media.setLabel(stack, label.map(StringUtil::normaliseLabel).orElse(null))) {
|
||||||
throw new LuaException("Disk label cannot be changed");
|
throw new LuaException("Disk label cannot be changed");
|
||||||
}
|
}
|
||||||
diskDrive.setDiskStack(stack);
|
diskDrive.updateDiskStack(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,12 +179,12 @@ public class DiskDrivePeripheral implements IPeripheral {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void attach(IComputerAccess computer) {
|
public void attach(IComputerAccess computer) {
|
||||||
diskDrive.mount(computer);
|
diskDrive.attach(computer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void detach(IComputerAccess computer) {
|
public void detach(IComputerAccess computer) {
|
||||||
diskDrive.unmount(computer);
|
diskDrive.detach(computer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,10 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.shared.peripheral.diskdrive;
|
package dan200.computercraft.shared.peripheral.diskdrive;
|
||||||
|
|
||||||
import dan200.computercraft.api.filesystem.Mount;
|
|
||||||
import dan200.computercraft.api.media.IMedia;
|
import dan200.computercraft.api.media.IMedia;
|
||||||
import dan200.computercraft.impl.MediaProviders;
|
import dan200.computercraft.impl.MediaProviders;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
@@ -17,18 +15,22 @@ import javax.annotation.Nullable;
|
|||||||
/**
|
/**
|
||||||
* An immutable snapshot of the current disk. This allows us to read the stack in a thread-safe manner.
|
* An immutable snapshot of the current disk. This allows us to read the stack in a thread-safe manner.
|
||||||
*/
|
*/
|
||||||
class MediaStack {
|
final class MediaStack {
|
||||||
static final MediaStack EMPTY = new MediaStack(ItemStack.EMPTY);
|
static final MediaStack EMPTY = new MediaStack(ItemStack.EMPTY, null);
|
||||||
|
|
||||||
final ItemStack stack;
|
final ItemStack stack;
|
||||||
final @Nullable IMedia media;
|
final @Nullable IMedia media;
|
||||||
|
|
||||||
@Nullable
|
private MediaStack(ItemStack stack, @Nullable IMedia media) {
|
||||||
private Mount mount;
|
|
||||||
|
|
||||||
MediaStack(ItemStack stack) {
|
|
||||||
this.stack = stack;
|
this.stack = stack;
|
||||||
media = MediaProviders.get(stack);
|
this.media = media;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MediaStack of(ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return EMPTY;
|
||||||
|
|
||||||
|
var freshStack = stack.copy();
|
||||||
|
return new MediaStack(freshStack, MediaProviders.get(freshStack));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -40,12 +42,4 @@ class MediaStack {
|
|||||||
String getAudioTitle() {
|
String getAudioTitle() {
|
||||||
return media != null ? media.getAudioTitle(stack) : null;
|
return media != null ? media.getAudioTitle(stack) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Mount getMount(ServerLevel level) {
|
|
||||||
if (media == null) return null;
|
|
||||||
|
|
||||||
if (mount == null) mount = media.createDataMount(stack, level);
|
|
||||||
return mount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package dan200.computercraft.gametest.core;
|
|||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.mixin.gametest.TestCommandAccessor;
|
import dan200.computercraft.mixin.gametest.TestCommandAccessor;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.gametest.framework.GameTestRegistry;
|
import net.minecraft.gametest.framework.GameTestRegistry;
|
||||||
@@ -81,6 +82,27 @@ class CCTestCommand {
|
|||||||
player.getLevel().addFreshEntity(armorStand);
|
player.getLevel().addFreshEntity(armorStand);
|
||||||
return 0;
|
return 0;
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
.then(literal("give-computer").executes(context -> {
|
||||||
|
var player = context.getSource().getPlayerOrException();
|
||||||
|
var pos = StructureUtils.findNearestStructureBlock(player.blockPosition(), 15, player.getLevel());
|
||||||
|
if (pos == null) return error(context.getSource(), "No nearby test");
|
||||||
|
|
||||||
|
var structureBlock = (StructureBlockEntity) player.getLevel().getBlockEntity(pos);
|
||||||
|
if (structureBlock == null) return error(context.getSource(), "No nearby structure block");
|
||||||
|
var info = GameTestRegistry.getTestFunction(structureBlock.getStructurePath());
|
||||||
|
|
||||||
|
var item = ModRegistry.Items.COMPUTER_ADVANCED.get().create(1, info.getTestName());
|
||||||
|
if (!player.getInventory().add(item)) {
|
||||||
|
var itemEntity = player.drop(item, false);
|
||||||
|
if (itemEntity != null) {
|
||||||
|
itemEntity.setNoPickUpDelay();
|
||||||
|
itemEntity.setOwner(player.getUUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package dan200.computercraft.gametest
|
|||||||
import dan200.computercraft.core.apis.FSAPI
|
import dan200.computercraft.core.apis.FSAPI
|
||||||
import dan200.computercraft.gametest.api.*
|
import dan200.computercraft.gametest.api.*
|
||||||
import dan200.computercraft.shared.ModRegistry
|
import dan200.computercraft.shared.ModRegistry
|
||||||
|
import dan200.computercraft.shared.media.items.DiskItem
|
||||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlock
|
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlock
|
||||||
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveState
|
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveState
|
||||||
import dan200.computercraft.test.core.assertArrayEquals
|
import dan200.computercraft.test.core.assertArrayEquals
|
||||||
@@ -45,10 +46,14 @@ class Disk_Drive_Test {
|
|||||||
thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) }
|
thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mount is initially attached, and then removed when the disk is ejected.
|
||||||
|
*/
|
||||||
@GameTest
|
@GameTest
|
||||||
fun Adds_removes_mount(helper: GameTestHelper) = helper.sequence {
|
fun Adds_removes_mount(helper: GameTestHelper) = helper.sequence {
|
||||||
thenIdle(2)
|
thenOnComputer { } // Wait for the computer to start up
|
||||||
thenOnComputer {
|
thenIdle(2) // Let the disk drive tick once to create the mount
|
||||||
|
thenOnComputer { // Then actually assert things!
|
||||||
getApi<FSAPI>().getDrive("disk").assertArrayEquals("right")
|
getApi<FSAPI>().getDrive("disk").assertArrayEquals("right")
|
||||||
callPeripheral("right", "ejectDisk")
|
callPeripheral("right", "ejectDisk")
|
||||||
}
|
}
|
||||||
@@ -56,6 +61,18 @@ class Disk_Drive_Test {
|
|||||||
thenOnComputer { assertEquals(null, getApi<FSAPI>().getDrive("disk")) }
|
thenOnComputer { assertEquals(null, getApi<FSAPI>().getDrive("disk")) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When creating a new mount, the item is with a new disk ID.
|
||||||
|
*/
|
||||||
|
@GameTest
|
||||||
|
fun Creates_disk_id(helper: GameTestHelper) = helper.sequence {
|
||||||
|
val drivePos = BlockPos(2, 2, 2)
|
||||||
|
thenWaitUntil {
|
||||||
|
val drive = helper.getBlockEntity(drivePos, ModRegistry.BlockEntities.DISK_DRIVE.get())
|
||||||
|
if (DiskItem.getDiskID(drive.getItem(0)) == -1) helper.fail("Disk has no item", drivePos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check comparators can read the contents of the disk drive
|
* Check comparators can read the contents of the disk drive
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class Monitor_Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test
|
* Test monitors render correctly
|
||||||
*/
|
*/
|
||||||
@GameTestGenerator
|
@GameTestGenerator
|
||||||
fun Render_monitor_tests(): List<TestFunction> {
|
fun Render_monitor_tests(): List<TestFunction> {
|
||||||
@@ -72,7 +72,7 @@ class Monitor_Test {
|
|||||||
fun addTest(label: String, renderer: MonitorRenderer, time: Long = Times.NOON, tag: String = TestTags.CLIENT) {
|
fun addTest(label: String, renderer: MonitorRenderer, time: Long = Times.NOON, tag: String = TestTags.CLIENT) {
|
||||||
if (!TestTags.isEnabled(tag)) return
|
if (!TestTags.isEnabled(tag)) return
|
||||||
|
|
||||||
val className = Monitor_Test::class.java.simpleName.lowercase()
|
val className = this::class.java.simpleName.lowercase()
|
||||||
val testName = "$className.render_monitor"
|
val testName = "$className.render_monitor"
|
||||||
|
|
||||||
tests.add(
|
tests.add(
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package dan200.computercraft.gametest
|
||||||
|
|
||||||
|
import dan200.computercraft.gametest.api.*
|
||||||
|
import net.minecraft.gametest.framework.GameTestGenerator
|
||||||
|
import net.minecraft.gametest.framework.GameTestHelper
|
||||||
|
import net.minecraft.gametest.framework.TestFunction
|
||||||
|
|
||||||
|
class Printout_Test {
|
||||||
|
/**
|
||||||
|
* Test printouts render correctly
|
||||||
|
*/
|
||||||
|
@GameTestGenerator
|
||||||
|
fun Render_in_frame(): List<TestFunction> {
|
||||||
|
val tests = mutableListOf<TestFunction>()
|
||||||
|
|
||||||
|
fun addTest(label: String, time: Long = Times.NOON, tag: String = TestTags.CLIENT) {
|
||||||
|
if (!TestTags.isEnabled(tag)) return
|
||||||
|
|
||||||
|
val className = this::class.java.simpleName.lowercase()
|
||||||
|
val testName = "$className.render_in_frame"
|
||||||
|
|
||||||
|
tests.add(
|
||||||
|
TestFunction(
|
||||||
|
"$testName.$label",
|
||||||
|
"$testName.$label",
|
||||||
|
testName,
|
||||||
|
Timeouts.DEFAULT,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
) { renderPrintout(it, time) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTest("noon", Times.NOON)
|
||||||
|
addTest("midnight", Times.MIDNIGHT)
|
||||||
|
|
||||||
|
addTest("sodium", tag = "sodium")
|
||||||
|
|
||||||
|
addTest("iris_noon", Times.NOON, tag = "iris")
|
||||||
|
addTest("iris_midnight", Times.MIDNIGHT, tag = "iris")
|
||||||
|
|
||||||
|
return tests
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderPrintout(helper: GameTestHelper, time: Long) = helper.sequence {
|
||||||
|
thenExecute {
|
||||||
|
helper.level.dayTime = time
|
||||||
|
helper.positionAtArmorStand()
|
||||||
|
}
|
||||||
|
|
||||||
|
thenScreenshot()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,6 +80,7 @@ object TestHooks {
|
|||||||
Monitor_Test::class.java,
|
Monitor_Test::class.java,
|
||||||
Pocket_Computer_Test::class.java,
|
Pocket_Computer_Test::class.java,
|
||||||
Printer_Test::class.java,
|
Printer_Test::class.java,
|
||||||
|
Printout_Test::class.java,
|
||||||
Recipe_Test::class.java,
|
Recipe_Test::class.java,
|
||||||
Turtle_Test::class.java,
|
Turtle_Test::class.java,
|
||||||
)
|
)
|
||||||
|
|||||||
138
projects/common/src/testMod/resources/data/cctest/structures/disk_drive_test.creates_disk_id.snbt
generated
Normal file
138
projects/common/src/testMod/resources/data/cctest/structures/disk_drive_test.creates_disk_id.snbt
generated
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
DataVersion: 3218,
|
||||||
|
size: [5, 5, 5],
|
||||||
|
data: [
|
||||||
|
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 2], state: "computercraft:computer_advanced{facing:north,state:on}", nbt: {ComputerId: 1, Label: "disk_drive_test.creates_disk_id", On: 1b, id: "computercraft:computer_advanced"}},
|
||||||
|
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 2], state: "computercraft:disk_drive{facing:north,state:full}", nbt: {Item: {Count: 1b, id: "computercraft:disk", tag: {Color: 1118481}}, id: "computercraft:disk_drive"}},
|
||||||
|
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||||
|
],
|
||||||
|
entities: [],
|
||||||
|
palette: [
|
||||||
|
"minecraft:polished_andesite",
|
||||||
|
"minecraft:air",
|
||||||
|
"computercraft:computer_advanced{facing:north,state:on}",
|
||||||
|
"computercraft:disk_drive{facing:north,state:full}"
|
||||||
|
]
|
||||||
|
}
|
||||||
139
projects/common/src/testMod/resources/data/cctest/structures/printout_test.render_in_frame.snbt
generated
Normal file
139
projects/common/src/testMod/resources/data/cctest/structures/printout_test.render_in_frame.snbt
generated
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
{
|
||||||
|
DataVersion: 3218,
|
||||||
|
size: [5, 5, 5],
|
||||||
|
data: [
|
||||||
|
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 1, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 1, 4], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 2, 4], state: "minecraft:polished_andesite"},
|
||||||
|
{pos: [3, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 2, 4], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 3, 4], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [0, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [1, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [2, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [3, 4, 4], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 0], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 1], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 2], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 3], state: "minecraft:air"},
|
||||||
|
{pos: [4, 4, 4], state: "minecraft:air"}
|
||||||
|
],
|
||||||
|
entities: [
|
||||||
|
{blockPos: [2, 1, 2], pos: [2.583196949396921d, 1.0d, 2.608974919959593d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, 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: [221.58319694939692d, -58.0d, 92.60897491995959d], Pose: {}, Rotation: [1.3504658f, 6.7031174f], ShowArms: 0b, Small: 0b, UUID: [I; 1854159985, -991539606, -1317309541, 1483112386], id: "minecraft:armor_stand"}},
|
||||||
|
{blockPos: [2, 2, 3], pos: [2.5d, 2.5d, 3.96875d], nbt: {Air: 300s, 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: [221.5d, -56.5d, 93.96875d], Rotation: [-540.0f, 0.0f], TileX: 221, TileY: -57, TileZ: 93, UUID: [I; 1972443954, 193152445, -1823446000, -1684171214], id: "minecraft:item_frame"}}
|
||||||
|
],
|
||||||
|
palette: [
|
||||||
|
"minecraft:polished_andesite",
|
||||||
|
"minecraft:air"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
{
|
|
||||||
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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -217,9 +217,10 @@ end
|
|||||||
channel. The message will be received by every device listening to rednet.
|
channel. The message will be received by every device listening to rednet.
|
||||||
|
|
||||||
@param message The message to send. This should not contain coroutines or
|
@param message The message to send. This should not contain coroutines or
|
||||||
functions, as they will be converted to @{nil}. @tparam[opt] string protocol
|
functions, as they will be converted to @{nil}.
|
||||||
The "protocol" to send this message under. When using @{rednet.receive} one can
|
@tparam[opt] string protocol The "protocol" to send this message under. When
|
||||||
filter to only receive messages sent under a particular protocol.
|
using @{rednet.receive} one can filter to only receive messages sent under a
|
||||||
|
particular protocol.
|
||||||
@see rednet.receive
|
@see rednet.receive
|
||||||
@changed 1.6 Added protocol parameter.
|
@changed 1.6 Added protocol parameter.
|
||||||
@usage Broadcast the words "Hello, world!" to every computer using rednet.
|
@usage Broadcast the words "Hello, world!" to every computer using rednet.
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
# New features in CC: Tweaked 1.102.2
|
||||||
|
|
||||||
|
Several bug fixes:
|
||||||
|
* Fix printouts crashing in item frames
|
||||||
|
* Fix disks not being assigned an ID when placed in a disk drive.
|
||||||
|
|
||||||
# New features in CC: Tweaked 1.102.1
|
# New features in CC: Tweaked 1.102.1
|
||||||
|
|
||||||
Several bug fixes:
|
Several bug fixes:
|
||||||
|
|||||||
@@ -1,18 +1,7 @@
|
|||||||
New features in CC: Tweaked 1.102.1
|
New features in CC: Tweaked 1.102.2
|
||||||
|
|
||||||
Several bug fixes:
|
Several bug fixes:
|
||||||
* Fix crash on Fabric when refuelling with a non-fuel item (emmachase).
|
* Fix printouts crashing in item frames
|
||||||
* Fix crash when calling `pocket.equipBack()` with a wireless modem.
|
* Fix disks not being assigned an ID when placed in a disk drive.
|
||||||
* Fix turtles dropping their inventory when moving (emmachase).
|
|
||||||
* Fix crash when inserting items into a full inventory (emmachase).
|
|
||||||
* Simplify wired cable breaking code, fixing items sometimes not dropping.
|
|
||||||
* Correctly handle double chests being treated as single threads under Fabric.
|
|
||||||
* Fix `mouse_up` not being fired under Fabric.
|
|
||||||
* Fix full-block Wired modems not connecting to adjacent cables when placed.
|
|
||||||
* Hide the search tab from the `itemGroups` item details.
|
|
||||||
* Fix speakers playing too loudly.
|
|
||||||
* Change where turtles drop items from, reducing the chance that items clip through blocks.
|
|
||||||
* Fix the `computer_threads` config option not applying under Fabric.
|
|
||||||
* Fix stack overflow in logging code.
|
|
||||||
|
|
||||||
Type "help changelog" to see the full version history.
|
Type "help changelog" to see the full version history.
|
||||||
|
|||||||
@@ -12,20 +12,32 @@ import dan200.computercraft.api.lua.LuaFunction;
|
|||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
|
import dan200.computercraft.core.computer.ComputerThread;
|
||||||
import dan200.computercraft.core.computer.mainthread.NoWorkMainThreadScheduler;
|
import dan200.computercraft.core.computer.mainthread.NoWorkMainThreadScheduler;
|
||||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||||
import dan200.computercraft.core.filesystem.WritableFileMount;
|
import dan200.computercraft.core.filesystem.WritableFileMount;
|
||||||
|
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||||
|
import dan200.computercraft.core.lua.MachineEnvironment;
|
||||||
|
import dan200.computercraft.core.lua.MachineResult;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.test.core.computer.BasicEnvironment;
|
import dan200.computercraft.test.core.computer.BasicEnvironment;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
import org.opentest4j.AssertionFailedError;
|
import org.opentest4j.AssertionFailedError;
|
||||||
|
import org.opentest4j.TestAbortedException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.squiddev.cobalt.*;
|
||||||
|
import org.squiddev.cobalt.debug.DebugFrame;
|
||||||
|
import org.squiddev.cobalt.debug.DebugHook;
|
||||||
|
import org.squiddev.cobalt.debug.DebugState;
|
||||||
|
import org.squiddev.cobalt.function.OneArgFunction;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -36,6 +48,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.locks.Condition;
|
import java.util.concurrent.locks.Condition;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +91,7 @@ public class ComputerTestDelegate {
|
|||||||
|
|
||||||
private final Condition hasFinished = lock.newCondition();
|
private final Condition hasFinished = lock.newCondition();
|
||||||
private boolean finished = false;
|
private boolean finished = false;
|
||||||
private Map<String, Map<Double, Double>> finishedWith;
|
private final Map<LuaString, Int2IntArrayMap> coverage = new HashMap<>();
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() throws IOException {
|
public void before() throws IOException {
|
||||||
@@ -99,7 +112,7 @@ public class ComputerTestDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var environment = new BasicEnvironment(mount);
|
var environment = new BasicEnvironment(mount);
|
||||||
context = new ComputerContext(environment, 1, new NoWorkMainThreadScheduler());
|
context = new ComputerContext(environment, new ComputerThread(1), new NoWorkMainThreadScheduler(), CoverageLuaMachine::new);
|
||||||
computer = new Computer(context, environment, term, 0);
|
computer = new Computer(context, environment, term, 0);
|
||||||
computer.getEnvironment().setPeripheral(ComputerSide.TOP, new FakeModem());
|
computer.getEnvironment().setPeripheral(ComputerSide.TOP, new FakeModem());
|
||||||
computer.getEnvironment().setPeripheral(ComputerSide.BOTTOM, new FakePeripheralHub());
|
computer.getEnvironment().setPeripheral(ComputerSide.BOTTOM, new FakePeripheralHub());
|
||||||
@@ -137,10 +150,12 @@ public class ComputerTestDelegate {
|
|||||||
computer.shutdown();
|
computer.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finishedWith != null) {
|
if (!coverage.isEmpty()) {
|
||||||
Files.createDirectories(REPORT_PATH.getParent());
|
Files.createDirectories(REPORT_PATH.getParent());
|
||||||
try (var writer = Files.newBufferedWriter(REPORT_PATH)) {
|
try (var writer = Files.newBufferedWriter(REPORT_PATH)) {
|
||||||
new LuaCoverage(finishedWith).write(writer);
|
new LuaCoverage(coverage.entrySet().stream().collect(Collectors.toMap(
|
||||||
|
x -> x.getKey().substring(1).toString(), Map.Entry::getValue
|
||||||
|
))).write(writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -414,7 +429,9 @@ public class ComputerTestDelegate {
|
|||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "ok":
|
case "ok":
|
||||||
|
break;
|
||||||
case "pending":
|
case "pending":
|
||||||
|
runResult = new TestAbortedException("Test is pending");
|
||||||
break;
|
break;
|
||||||
case "fail":
|
case "fail":
|
||||||
runResult = new AssertionFailedError(wholeMessage.toString());
|
runResult = new AssertionFailedError(wholeMessage.toString());
|
||||||
@@ -432,9 +449,7 @@ public class ComputerTestDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final void finish(Optional<Map<?, ?>> result) {
|
public final void finish() {
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
var finishedResult = (Map<String, Map<Double, Double>>) result.orElse(null);
|
|
||||||
LOG.info("Finished");
|
LOG.info("Finished");
|
||||||
|
|
||||||
// Signal to after that execution has finished
|
// Signal to after that execution has finished
|
||||||
@@ -445,7 +460,6 @@ public class ComputerTestDelegate {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
finished = true;
|
finished = true;
|
||||||
if (finishedResult != null) finishedWith = finishedResult;
|
|
||||||
|
|
||||||
hasFinished.signal();
|
hasFinished.signal();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -453,4 +467,73 @@ public class ComputerTestDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A subclass of {@link CobaltLuaMachine} which tracks coverage for executed files.
|
||||||
|
* <p>
|
||||||
|
* This is a super nasty hack, but is also an order of magnitude faster than tracking this in Lua.
|
||||||
|
*/
|
||||||
|
private class CoverageLuaMachine extends CobaltLuaMachine {
|
||||||
|
CoverageLuaMachine(MachineEnvironment environment) {
|
||||||
|
super(environment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MachineResult loadBios(InputStream bios) {
|
||||||
|
var result = super.loadBios(bios);
|
||||||
|
if (result != MachineResult.OK) return result;
|
||||||
|
|
||||||
|
LuaTable globals;
|
||||||
|
LuaThread mainRoutine;
|
||||||
|
try {
|
||||||
|
var globalField = CobaltLuaMachine.class.getDeclaredField("globals");
|
||||||
|
globalField.setAccessible(true);
|
||||||
|
globals = (LuaTable) globalField.get(this);
|
||||||
|
|
||||||
|
var threadField = CobaltLuaMachine.class.getDeclaredField("mainRoutine");
|
||||||
|
threadField.setAccessible(true);
|
||||||
|
mainRoutine = (LuaThread) threadField.get(this);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException("Cannot get internal Cobalt state", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
var coverage = ComputerTestDelegate.this.coverage;
|
||||||
|
var hook = new DebugHook() {
|
||||||
|
@Override
|
||||||
|
public void onCall(LuaState state, DebugState ds, DebugFrame frame) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReturn(LuaState state, DebugState ds, DebugFrame frame) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCount(LuaState state, DebugState ds, DebugFrame frame) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLine(LuaState state, DebugState ds, DebugFrame frame, int newLine) {
|
||||||
|
if (frame.closure == null) return;
|
||||||
|
|
||||||
|
var proto = frame.closure.getPrototype();
|
||||||
|
if (!proto.source.startsWith('@')) return;
|
||||||
|
|
||||||
|
var map = coverage.computeIfAbsent(proto.source, x -> new Int2IntArrayMap());
|
||||||
|
map.put(newLine, map.get(newLine) + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
((LuaTable) globals.rawget("coroutine")).rawset("create", new OneArgFunction() {
|
||||||
|
@Override
|
||||||
|
public LuaValue call(LuaState state, LuaValue arg) throws LuaError {
|
||||||
|
var thread = new LuaThread(state, arg.checkFunction(), state.getCurrentThread().getfenv());
|
||||||
|
thread.getDebugState().setHook(hook, false, true, false, 0);
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mainRoutine.getDebugState().setHook(hook, false, true, false, 0);
|
||||||
|
|
||||||
|
return MachineResult.OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.core;
|
package dan200.computercraft.core;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -26,24 +26,25 @@ class LuaCoverage {
|
|||||||
private static final Path ROOT = new File("src/main/resources/data/computercraft/lua").toPath();
|
private static final Path ROOT = new File("src/main/resources/data/computercraft/lua").toPath();
|
||||||
private static final Path BIOS = ROOT.resolve("bios.lua");
|
private static final Path BIOS = ROOT.resolve("bios.lua");
|
||||||
private static final Path APIS = ROOT.resolve("rom/apis");
|
private static final Path APIS = ROOT.resolve("rom/apis");
|
||||||
|
private static final Path STARTUP = ROOT.resolve("rom/startup.lua");
|
||||||
private static final Path SHELL = ROOT.resolve("rom/programs/shell.lua");
|
private static final Path SHELL = ROOT.resolve("rom/programs/shell.lua");
|
||||||
private static final Path MULTISHELL = ROOT.resolve("rom/programs/advanced/multishell.lua");
|
private static final Path MULTISHELL = ROOT.resolve("rom/programs/advanced/multishell.lua");
|
||||||
private static final Path TREASURE = ROOT.resolve("treasure");
|
private static final Path TREASURE = ROOT.resolve("treasure");
|
||||||
|
|
||||||
private final Map<String, Map<Double, Double>> coverage;
|
private final Map<String, Int2IntMap> coverage;
|
||||||
private final String blank;
|
private final String blank;
|
||||||
private final String zero;
|
private final String zero;
|
||||||
private final String countFormat;
|
private final String countFormat;
|
||||||
|
|
||||||
LuaCoverage(Map<String, Map<Double, Double>> coverage) {
|
LuaCoverage(Map<String, Int2IntMap> coverage) {
|
||||||
this.coverage = coverage;
|
this.coverage = coverage;
|
||||||
|
|
||||||
var max = (int) coverage.values().stream()
|
var max = coverage.values().stream()
|
||||||
.flatMapToDouble(x -> x.values().stream().mapToDouble(y -> y))
|
.flatMapToInt(x -> x.values().intStream())
|
||||||
.max().orElse(0);
|
.max().orElse(0);
|
||||||
var maxLen = Math.max(1, (int) Math.ceil(Math.log10(max)));
|
var maxLen = Math.max(1, (int) Math.ceil(Math.log10(max)));
|
||||||
blank = Strings.repeat(" ", maxLen + 1);
|
blank = " ".repeat(maxLen + 1);
|
||||||
zero = Strings.repeat("*", maxLen) + "0";
|
zero = "*".repeat(maxLen) + "0";
|
||||||
countFormat = "%" + (maxLen + 1) + "d";
|
countFormat = "%" + (maxLen + 1) + "d";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,14 +57,15 @@ class LuaCoverage {
|
|||||||
var possiblePaths = Stream.of(
|
var possiblePaths = Stream.of(
|
||||||
coverage.remove("/" + full),
|
coverage.remove("/" + full),
|
||||||
path.equals(BIOS) ? coverage.remove("bios.lua") : null,
|
path.equals(BIOS) ? coverage.remove("bios.lua") : null,
|
||||||
|
path.equals(STARTUP) ? coverage.remove("startup.lua") : null,
|
||||||
path.equals(SHELL) ? coverage.remove("shell.lua") : null,
|
path.equals(SHELL) ? coverage.remove("shell.lua") : null,
|
||||||
path.equals(MULTISHELL) ? coverage.remove("multishell.lua") : null,
|
path.equals(MULTISHELL) ? coverage.remove("multishell.lua") : null,
|
||||||
path.startsWith(APIS) ? coverage.remove(path.getFileName().toString()) : null
|
path.startsWith(APIS) ? coverage.remove(path.getFileName().toString()) : null
|
||||||
);
|
);
|
||||||
var files = possiblePaths
|
var files = possiblePaths
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.flatMap(x -> x.entrySet().stream())
|
.flatMap(x -> x.int2IntEntrySet().stream())
|
||||||
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue, Double::sum));
|
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue, Integer::sum));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writeCoverageFor(out, path, files);
|
writeCoverageFor(out, path, files);
|
||||||
@@ -78,7 +80,7 @@ class LuaCoverage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeCoverageFor(Writer out, Path fullName, Map<Double, Double> visitedLines) throws IOException {
|
private void writeCoverageFor(Writer out, Path fullName, Map<Integer, Integer> visitedLines) throws IOException {
|
||||||
if (!Files.exists(fullName)) {
|
if (!Files.exists(fullName)) {
|
||||||
LOG.error("Cannot locate file {}", fullName);
|
LOG.error("Cannot locate file {}", fullName);
|
||||||
return;
|
return;
|
||||||
@@ -96,9 +98,9 @@ class LuaCoverage {
|
|||||||
var lineNo = 0;
|
var lineNo = 0;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
lineNo++;
|
lineNo++;
|
||||||
var count = visitedLines.get((double) lineNo);
|
var count = visitedLines.get(lineNo);
|
||||||
if (count != null) {
|
if (count != null) {
|
||||||
out.write(String.format(countFormat, count.intValue()));
|
out.write(String.format(countFormat, count));
|
||||||
} else if (activeLines.contains(lineNo)) {
|
} else if (activeLines.contains(lineNo)) {
|
||||||
out.write(zero);
|
out.write(zero);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -518,26 +518,9 @@ local function before_each(body)
|
|||||||
before_each_fns[n], before_each_fns.n = body, n
|
before_each_fns[n], before_each_fns.n = body, n
|
||||||
end
|
end
|
||||||
|
|
||||||
local native_co_create, native_loadfile = coroutine.create, loadfile
|
local native_loadfile = loadfile
|
||||||
local line_counts = {}
|
local line_counts = {}
|
||||||
if cct_test then
|
if cct_test then
|
||||||
local string_sub, debug_getinfo = string.sub, debug.getinfo
|
|
||||||
local function debug_hook(_, line_nr)
|
|
||||||
local name = debug_getinfo(2, "S").source
|
|
||||||
if string_sub(name, 1, 1) ~= "@" then return end
|
|
||||||
name = string_sub(name, 2)
|
|
||||||
|
|
||||||
local file = line_counts[name]
|
|
||||||
if not file then file = {} line_counts[name] = file end
|
|
||||||
file[line_nr] = (file[line_nr] or 0) + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
coroutine.create = function(...)
|
|
||||||
local co = native_co_create(...)
|
|
||||||
debug.sethook(co, debug_hook, "l")
|
|
||||||
return co
|
|
||||||
end
|
|
||||||
|
|
||||||
local expect = require "cc.expect".expect
|
local expect = require "cc.expect".expect
|
||||||
_G.native_loadfile = native_loadfile
|
_G.native_loadfile = native_loadfile
|
||||||
_G.loadfile = function(filename, mode, env)
|
_G.loadfile = function(filename, mode, env)
|
||||||
@@ -557,8 +540,6 @@ if cct_test then
|
|||||||
file.close()
|
file.close()
|
||||||
return func, err
|
return func, err
|
||||||
end
|
end
|
||||||
|
|
||||||
debug.sethook(debug_hook, "l")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local arg = ...
|
local arg = ...
|
||||||
@@ -736,8 +717,6 @@ end
|
|||||||
term.setTextColour(colours.white) io.write(info .. "\n")
|
term.setTextColour(colours.white) io.write(info .. "\n")
|
||||||
|
|
||||||
-- Restore hook stubs
|
-- Restore hook stubs
|
||||||
debug.sethook(nil, "l")
|
|
||||||
coroutine.create = native_co_create
|
|
||||||
_G.loadfile = native_loadfile
|
_G.loadfile = native_loadfile
|
||||||
|
|
||||||
if cct_test then cct_test.finish(line_counts) end
|
if cct_test then cct_test.finish(line_counts) end
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ class ItemFrameRendererMixin {
|
|||||||
)
|
)
|
||||||
@SuppressWarnings("UnusedMethod")
|
@SuppressWarnings("UnusedMethod")
|
||||||
private void render(ItemFrame entity, float yaw, float partialTicks, PoseStack pose, MultiBufferSource buffers, int light, CallbackInfo ci) {
|
private void render(ItemFrame entity, float yaw, float partialTicks, PoseStack pose, MultiBufferSource buffers, int light, CallbackInfo ci) {
|
||||||
if (ClientHooks.onRenderItemFrame(pose, buffers, entity, entity.getItem(), light)) ci.cancel();
|
if (ClientHooks.onRenderItemFrame(pose, buffers, entity, entity.getItem(), light)) {
|
||||||
|
ci.cancel();
|
||||||
|
pose.popPose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user