1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-07-04 19:13:21 +00:00

Merge branch 'mc-1.15.x' into mc-1.16.x

This commit is contained in:
Jonathan Coates 2021-07-25 16:42:22 +01:00
commit 7012ac7163
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
22 changed files with 1510 additions and 51 deletions

View File

@ -62,6 +62,7 @@ interface IPeripheralChangeListener
@Nullable
IPeripheral getPeripheral( ComputerSide side );
@Nullable
String getLabel();
void setLabel( @Nullable String label );

View File

@ -28,7 +28,6 @@
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import io.netty.handler.ssl.SslContext;
import java.lang.ref.WeakReference;
@ -160,7 +159,7 @@ protected void initChannel( SocketChannel ch )
p.addLast(
new HttpClientCodec(),
new HttpObjectAggregator( 8192 ),
WebSocketClientCompressionHandler.INSTANCE,
WebsocketCompressionHandler.INSTANCE,
new WebsocketHandler( Websocket.this, handshaker, options )
);
}

View File

@ -0,0 +1,38 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis.http.websocket;
import io.netty.channel.ChannelHandler;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.DeflateFrameClientExtensionHandshaker;
import io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateClientExtensionHandshaker;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler;
import static io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateServerExtensionHandshaker.MAX_WINDOW_SIZE;
/**
* An alternative to {@link WebSocketClientCompressionHandler} which supports the {@literal client_no_context_takeover}
* extension. Makes CC <em>slightly</em> more flexible.
*/
@ChannelHandler.Sharable
final class WebsocketCompressionHandler extends WebSocketClientExtensionHandler
{
public static final WebsocketCompressionHandler INSTANCE = new WebsocketCompressionHandler();
private WebsocketCompressionHandler()
{
super(
new PerMessageDeflateClientExtensionHandshaker(
6, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), MAX_WINDOW_SIZE,
true, false
),
new DeflateFrameClientExtensionHandshaker( false ),
new DeflateFrameClientExtensionHandshaker( true )
);
}
}

View File

@ -7,11 +7,13 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Util;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ClientChatEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import java.io.File;
@ -35,6 +37,9 @@ public static void onClientSendMessage( ClientChatEvent event )
// Emulate the command on the client side
if( event.getMessage().startsWith( OPEN_COMPUTER ) )
{
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if( server == null || server.isDedicatedServer() ) return;
event.setCanceled( true );
String idStr = event.getMessage().substring( OPEN_COMPUTER.length() ).trim();

View File

@ -21,7 +21,6 @@
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.PacketDistributor;
import net.minecraftforge.fml.network.simple.SimpleChannel;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import java.util.function.Function;
import java.util.function.Supplier;
@ -71,10 +70,7 @@ public static void sendToPlayer( PlayerEntity player, NetworkMessage packet )
public static void sendToAllPlayers( NetworkMessage packet )
{
for( ServerPlayerEntity player : ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayers() )
{
sendToPlayer( player, packet );
}
network.send( PacketDistributor.ALL.noArg(), packet );
}
public static void sendToServer( NetworkMessage packet )

View File

@ -0,0 +1,52 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.peripheral.monitor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
final class MonitorState
{
public static final MonitorState UNLOADED = new MonitorState( State.UNLOADED, null );
public static final MonitorState MISSING = new MonitorState( State.MISSING, null );
private final State state;
private final TileMonitor monitor;
private MonitorState( @Nonnull State state, @Nullable TileMonitor monitor )
{
this.state = state;
this.monitor = monitor;
}
public static MonitorState present( @Nonnull TileMonitor monitor )
{
return new MonitorState( State.PRESENT, monitor );
}
public boolean isPresent()
{
return state == State.PRESENT;
}
public boolean isMissing()
{
return state == State.MISSING;
}
@Nullable
public TileMonitor getMonitor()
{
return monitor;
}
enum State
{
UNLOADED,
MISSING,
PRESENT,
}
}

View File

@ -56,6 +56,7 @@ public class TileMonitor extends TileGeneric
private final Set<IComputerAccess> computers = new HashSet<>();
private boolean needsUpdate = false;
private boolean needsValidating = false;
private boolean destroyed = false;
private boolean visiting = false;
@ -78,6 +79,7 @@ public TileMonitor( TileEntityType<? extends TileMonitor> type, boolean advanced
public void onLoad()
{
super.onLoad();
needsValidating = true;
TickScheduler.schedule( this );
}
@ -149,6 +151,12 @@ public void load( @Nonnull BlockState state, @Nonnull CompoundNBT nbt )
@Override
public void blockTick()
{
if( needsValidating )
{
needsValidating = false;
validate();
}
if( needsUpdate )
{
needsUpdate = false;
@ -165,7 +173,7 @@ public void blockTick()
{
for( int y = 0; y < height; y++ )
{
TileMonitor monitor = getNeighbour( x, y );
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
if( monitor == null ) continue;
for( IComputerAccess computer : monitor.computers )
@ -209,7 +217,7 @@ private ServerMonitor getServerMonitor()
{
if( serverMonitor != null ) return serverMonitor;
TileMonitor origin = getOrigin();
TileMonitor origin = getOrigin().getMonitor();
if( origin == null ) return null;
return serverMonitor = origin.serverMonitor;
@ -230,7 +238,7 @@ private ServerMonitor createServerMonitor()
{
for( int y = 0; y < height; y++ )
{
TileMonitor monitor = getNeighbour( x, y );
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
if( monitor != null ) monitor.serverMonitor = serverMonitor;
}
}
@ -375,24 +383,24 @@ public int getYIndex()
return yIndex;
}
private TileMonitor getSimilarMonitorAt( BlockPos pos )
@Nonnull
private MonitorState getSimilarMonitorAt( BlockPos pos )
{
if( pos.equals( getBlockPos() ) ) return this;
if( pos.equals( getBlockPos() ) ) return MonitorState.present( this );
int y = pos.getY();
World world = getLevel();
if( world == null || !world.isAreaLoaded( pos, 0 ) ) return null;
if( world == null || !world.isAreaLoaded( pos, 0 ) ) return MonitorState.UNLOADED;
TileEntity tile = world.getBlockEntity( pos );
if( !(tile instanceof TileMonitor) ) return null;
if( !(tile instanceof TileMonitor) ) return MonitorState.MISSING;
TileMonitor monitor = (TileMonitor) tile;
return !monitor.visiting && !monitor.destroyed && advanced == monitor.advanced
&& getDirection() == monitor.getDirection() && getOrientation() == monitor.getOrientation()
? monitor : null;
? MonitorState.present( monitor ) : MonitorState.MISSING;
}
private TileMonitor getNeighbour( int x, int y )
private MonitorState getNeighbour( int x, int y )
{
BlockPos pos = getBlockPos();
Direction right = getRight();
@ -402,7 +410,7 @@ private TileMonitor getNeighbour( int x, int y )
return getSimilarMonitorAt( pos.relative( right, xOffset ).relative( down, yOffset ) );
}
private TileMonitor getOrigin()
private MonitorState getOrigin()
{
return getNeighbour( 0, 0 );
}
@ -426,7 +434,7 @@ private void resize( int width, int height )
{
for( int y = 0; y < height; y++ )
{
TileMonitor monitor = getNeighbour( x, y );
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
if( monitor != null && monitor.peripheral != null )
{
needsTerminal = true;
@ -454,7 +462,7 @@ private void resize( int width, int height )
{
for( int y = 0; y < height; y++ )
{
TileMonitor monitor = getNeighbour( x, y );
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
if( monitor == null ) continue;
monitor.xIndex = x;
@ -470,13 +478,13 @@ private void resize( int width, int height )
private boolean mergeLeft()
{
TileMonitor left = getNeighbour( -1, 0 );
TileMonitor left = getNeighbour( -1, 0 ).getMonitor();
if( left == null || left.yIndex != 0 || left.height != height ) return false;
int width = left.width + this.width;
if( width > ComputerCraft.monitorWidth ) return false;
TileMonitor origin = left.getOrigin();
TileMonitor origin = left.getOrigin().getMonitor();
if( origin != null ) origin.resize( width, height );
left.expand();
return true;
@ -484,13 +492,13 @@ private boolean mergeLeft()
private boolean mergeRight()
{
TileMonitor right = getNeighbour( width, 0 );
TileMonitor right = getNeighbour( width, 0 ).getMonitor();
if( right == null || right.yIndex != 0 || right.height != height ) return false;
int width = this.width + right.width;
if( width > ComputerCraft.monitorWidth ) return false;
TileMonitor origin = getOrigin();
TileMonitor origin = getOrigin().getMonitor();
if( origin != null ) origin.resize( width, height );
expand();
return true;
@ -498,13 +506,13 @@ private boolean mergeRight()
private boolean mergeUp()
{
TileMonitor above = getNeighbour( 0, height );
TileMonitor above = getNeighbour( 0, height ).getMonitor();
if( above == null || above.xIndex != 0 || above.width != width ) return false;
int height = above.height + this.height;
if( height > ComputerCraft.monitorHeight ) return false;
TileMonitor origin = getOrigin();
TileMonitor origin = getOrigin().getMonitor();
if( origin != null ) origin.resize( width, height );
expand();
return true;
@ -512,13 +520,13 @@ private boolean mergeUp()
private boolean mergeDown()
{
TileMonitor below = getNeighbour( 0, -1 );
TileMonitor below = getNeighbour( 0, -1 ).getMonitor();
if( below == null || below.xIndex != 0 || below.width != width ) return false;
int height = this.height + below.height;
if( height > ComputerCraft.monitorHeight ) return false;
TileMonitor origin = below.getOrigin();
TileMonitor origin = below.getOrigin().getMonitor();
if( origin != null ) origin.resize( width, height );
below.expand();
return true;
@ -547,22 +555,22 @@ void contractNeighbours()
visiting = true;
if( xIndex > 0 )
{
TileMonitor left = getNeighbour( xIndex - 1, yIndex );
TileMonitor left = getNeighbour( xIndex - 1, yIndex ).getMonitor();
if( left != null ) left.contract();
}
if( xIndex + 1 < width )
{
TileMonitor right = getNeighbour( xIndex + 1, yIndex );
TileMonitor right = getNeighbour( xIndex + 1, yIndex ).getMonitor();
if( right != null ) right.contract();
}
if( yIndex > 0 )
{
TileMonitor below = getNeighbour( xIndex, yIndex - 1 );
TileMonitor below = getNeighbour( xIndex, yIndex - 1 ).getMonitor();
if( below != null ) below.contract();
}
if( yIndex + 1 < height )
{
TileMonitor above = getNeighbour( xIndex, yIndex + 1 );
TileMonitor above = getNeighbour( xIndex, yIndex + 1 ).getMonitor();
if( above != null ) above.contract();
}
visiting = false;
@ -573,11 +581,11 @@ void contract()
int height = this.height;
int width = this.width;
TileMonitor origin = getOrigin();
TileMonitor origin = getOrigin().getMonitor();
if( origin == null )
{
TileMonitor right = width > 1 ? getNeighbour( 1, 0 ) : null;
TileMonitor below = height > 1 ? getNeighbour( 0, 1 ) : null;
TileMonitor right = width > 1 ? getNeighbour( 1, 0 ).getMonitor() : null;
TileMonitor below = height > 1 ? getNeighbour( 0, 1 ).getMonitor() : null;
if( right != null ) right.resize( width - 1, 1 );
if( below != null ) below.resize( width, height - 1 );
@ -591,7 +599,7 @@ void contract()
{
for( int x = 0; x < width; x++ )
{
TileMonitor monitor = origin.getNeighbour( x, y );
TileMonitor monitor = origin.getNeighbour( x, y ).getMonitor();
if( monitor != null ) continue;
// Decompose
@ -607,17 +615,17 @@ void contract()
}
if( x > 0 )
{
left = origin.getNeighbour( 0, y );
left = origin.getNeighbour( 0, y ).getMonitor();
left.resize( x, 1 );
}
if( x + 1 < width )
{
right = origin.getNeighbour( x + 1, y );
right = origin.getNeighbour( x + 1, y ).getMonitor();
right.resize( width - (x + 1), 1 );
}
if( y + 1 < height )
{
below = origin.getNeighbour( 0, y + 1 );
below = origin.getNeighbour( 0, y + 1 ).getMonitor();
below.resize( width, height - (y + 1) );
}
@ -631,6 +639,38 @@ void contract()
}
}
private boolean checkMonitorAt( int xIndex, int yIndex )
{
BlockPos pos = getBlockPos();
Direction right = getRight();
Direction down = getDown();
MonitorState state = getSimilarMonitorAt( pos.relative( right, xIndex ).relative( down, yIndex ) );
if( state.isMissing() ) return false;
TileMonitor monitor = state.getMonitor();
if( monitor == null ) return true;
return monitor.xIndex == xIndex && monitor.yIndex == yIndex && monitor.width == width && monitor.height == height;
}
private void validate()
{
if( xIndex == 0 && yIndex == 0 && width == 1 || height == 1 ) return;
if( checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) &&
checkMonitorAt( width - 1, 0 ) && checkMonitorAt( width - 1, height - 1 ) )
{
return;
}
// Something in our monitor is invalid. For now, let's just reset ourselves and then try to integrate ourselves
// later.
resize( 1, 1 );
needsUpdate = true;
}
private void monitorTouched( float xPos, float yPos, float zPos )
{
XYPair pair = XYPair
@ -658,7 +698,7 @@ private void monitorTouched( float xPos, float yPos, float zPos )
{
for( int x = 0; x < width; x++ )
{
TileMonitor monitor = getNeighbour( x, y );
TileMonitor monitor = getNeighbour( x, y ).getMonitor();
if( monitor == null ) continue;
for( IComputerAccess computer : monitor.computers )
@ -684,8 +724,8 @@ void removeComputer( IComputerAccess computer )
@Override
public AxisAlignedBB getRenderBoundingBox()
{
TileMonitor start = getNeighbour( 0, 0 );
TileMonitor end = getNeighbour( width - 1, height - 1 );
TileMonitor start = getNeighbour( 0, 0 ).getMonitor();
TileMonitor end = getNeighbour( width - 1, height - 1 ).getMonitor();
if( start != null && end != null )
{
BlockPos startPos = start.getBlockPos();

View File

@ -47,7 +47,10 @@ public void tick()
public void setRemoved()
{
super.setRemoved();
NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) );
if( level != null && !level.isClientSide )
{
NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) );
}
}
@Nonnull

View File

@ -382,12 +382,12 @@ function run()
-- Got a modem message, process it and add it to the rednet event queue
local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
if isOpen(sModem) and (nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST) then
if type(tMessage) == "table" and tMessage.nMessageID then
if not tReceivedMessages[tMessage.nMessageID] then
tReceivedMessages[tMessage.nMessageID] = true
tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID
os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol)
end
if type(tMessage) == "table" and type(tMessage.nMessageID) == "number"
and tMessage.nMessageID == tMessage.nMessageID and not tReceivedMessages[tMessage.nMessageID]
then
tReceivedMessages[tMessage.nMessageID] = true
tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID
os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol)
end
end

View File

@ -265,7 +265,7 @@ while true do
local new_width, new_height = term.getSize()
if new_width ~= width then
lines = word_wrap(contents, new_width)
lines, fg, bg = word_wrap(contents, new_width)
print_height = #lines
end

View File

@ -0,0 +1,121 @@
package dan200.computercraft.core.apis
import dan200.computercraft.ComputerCraft
import dan200.computercraft.api.lua.ILuaAPI
import dan200.computercraft.api.lua.MethodResult
import dan200.computercraft.api.peripheral.IPeripheral
import dan200.computercraft.api.peripheral.IWorkMonitor
import dan200.computercraft.core.computer.BasicEnvironment
import dan200.computercraft.core.computer.ComputerSide
import dan200.computercraft.core.computer.IComputerEnvironment
import dan200.computercraft.core.filesystem.FileSystem
import dan200.computercraft.core.terminal.Terminal
import dan200.computercraft.core.tracking.TrackingField
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.seconds
abstract class NullApiEnvironment : IAPIEnvironment {
private val computerEnv = BasicEnvironment()
override fun getComputerID(): Int = 0
override fun getComputerEnvironment(): IComputerEnvironment = computerEnv
override fun getMainThreadMonitor(): IWorkMonitor = throw IllegalStateException("Work monitor not available")
override fun getTerminal(): Terminal = throw IllegalStateException("Terminal not available")
override fun getFileSystem(): FileSystem = throw IllegalStateException("Terminal not available")
override fun shutdown() {}
override fun reboot() {}
override fun setOutput(side: ComputerSide?, output: Int) {}
override fun getOutput(side: ComputerSide?): Int = 0
override fun getInput(side: ComputerSide?): Int = 0
override fun setBundledOutput(side: ComputerSide?, output: Int) {}
override fun getBundledOutput(side: ComputerSide?): Int = 0
override fun getBundledInput(side: ComputerSide?): Int = 0
override fun setPeripheralChangeListener(listener: IAPIEnvironment.IPeripheralChangeListener?) {}
override fun getPeripheral(side: ComputerSide?): IPeripheral? = null
override fun getLabel(): String? = null
override fun setLabel(label: String?) {}
override fun startTimer(ticks: Long): Int = 0
override fun cancelTimer(id: Int) {}
override fun addTrackingChange(field: TrackingField, change: Long) {}
}
class EventResult(val name: String, val args: Array<Any?>)
class AsyncRunner : NullApiEnvironment() {
private val eventStream: Channel<Array<Any?>> = Channel(Int.MAX_VALUE)
private val apis: MutableList<ILuaAPI> = mutableListOf()
override fun queueEvent(event: String?, vararg args: Any?) {
ComputerCraft.log.debug("Queue event $event ${args.contentToString()}")
if (!eventStream.offer(arrayOf(event, *args))) {
throw IllegalStateException("Queue is full")
}
}
override fun shutdown() {
super.shutdown()
eventStream.close()
apis.forEach { it.shutdown() }
}
fun <T : ILuaAPI> addApi(api: T): T {
apis.add(api)
api.startup()
return api
}
suspend fun resultOf(toRun: MethodResult): Array<Any?> {
var running = toRun
while (running.callback != null) running = runOnce(running)
return running.result ?: empty
}
private suspend fun runOnce(obj: MethodResult): MethodResult {
val callback = obj.callback ?: throw NullPointerException("Callback cannot be null")
val result = obj.result
val filter: String? = if (result.isNullOrEmpty() || result[0] !is String) {
null
} else {
result[0] as String
}
return callback.resume(pullEventImpl(filter))
}
private suspend fun pullEventImpl(filter: String?): Array<Any?> {
for (event in eventStream) {
ComputerCraft.log.debug("Pulled event ${event.contentToString()}")
val eventName = event[0] as String
if (filter == null || eventName == filter || eventName == "terminate") return event
}
throw IllegalStateException("No more events")
}
suspend fun pullEvent(filter: String? = null): EventResult {
val result = pullEventImpl(filter)
return EventResult(result[0] as String, result.copyOfRange(1, result.size))
}
companion object {
private val empty: Array<Any?> = arrayOf()
@OptIn(ExperimentalTime::class)
fun runTest(timeout: Duration = 5.seconds, fn: suspend AsyncRunner.() -> Unit) {
runBlocking {
val runner = AsyncRunner()
try {
withTimeout(timeout) { fn(runner) }
} finally {
runner.shutdown()
}
}
}
}
}

View File

@ -0,0 +1,51 @@
package dan200.computercraft.core.apis.http.options
import dan200.computercraft.ComputerCraft
import dan200.computercraft.core.apis.AsyncRunner
import dan200.computercraft.core.apis.HTTPAPI
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Assertions.assertArrayEquals
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import java.util.*
@Disabled("Requires some setup locally.")
class TestHttpApi {
companion object {
private const val WS_ADDRESS = "ws://127.0.0.1:8080"
@JvmStatic
@BeforeAll
fun before() {
ComputerCraft.httpRules = listOf(AddressRule.parse("*", null, Action.ALLOW.toPartial()))
}
@JvmStatic
@AfterAll
fun after() {
ComputerCraft.httpRules = Collections.unmodifiableList(
listOf(
AddressRule.parse("\$private", null, Action.DENY.toPartial()),
AddressRule.parse("*", null, Action.ALLOW.toPartial())
)
)
}
}
@Test
fun `Connects to websocket`() {
AsyncRunner.runTest {
val httpApi = addApi(HTTPAPI(this))
val result = httpApi.websocket(WS_ADDRESS, Optional.empty())
assertArrayEquals(arrayOf(true), result, "Should have created websocket")
val event = pullEvent()
assertEquals("websocket_success", event.name) {
"Websocket failed to connect: ${event.args.contentToString()}"
}
}
}
}

View File

@ -0,0 +1,13 @@
package dan200.computercraft.ingame
import dan200.computercraft.ingame.api.GameTest
import dan200.computercraft.ingame.api.TestContext
import dan200.computercraft.ingame.api.checkComputerOk
class CraftOsTest {
/**
* Sends a rednet message to another a computer and back again.
*/
@GameTest
suspend fun `Sends basic rednet messages`(context: TestContext) = context.checkComputerOk(13)
}

View File

@ -0,0 +1,39 @@
package dan200.computercraft.ingame
import dan200.computercraft.ingame.api.*
import dan200.computercraft.shared.Registry
import dan200.computercraft.shared.peripheral.monitor.TileMonitor
import net.minecraft.block.Blocks
import net.minecraft.command.arguments.BlockStateInput
import net.minecraft.nbt.CompoundNBT
import net.minecraft.util.math.BlockPos
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.fail
import java.util.*
class MonitorTest {
@GameTest
suspend fun `Ensures valid on place`(context: TestContext) {
val pos = BlockPos(2, 0, 2)
val tag = CompoundNBT()
tag.putInt("Width", 2)
tag.putInt("Height", 2)
val toSet = BlockStateInput(
Registry.ModBlocks.MONITOR_ADVANCED.get().defaultBlockState(),
Collections.emptySet(),
tag
)
context.setBlock(pos, Blocks.AIR.defaultBlockState())
context.setBlock(pos, toSet)
context.sleep(2)
val tile = context.getTile(pos)
if (tile !is TileMonitor) fail("Expected tile to be monitor, is $tile")
assertEquals(1, tile.width, "Width should be 1")
assertEquals(1, tile.height, "Width should be 1")
}
}

View File

@ -3,6 +3,7 @@
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import net.minecraft.block.BlockState
import net.minecraft.command.arguments.BlockStateInput
import net.minecraft.entity.Entity
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.math.AxisAlignedBB
@ -54,6 +55,11 @@
tracker.level.setBlockAndUpdate(offset(pos), state)
}
/**
* Set a block within the test structure.
*/
fun TestContext.setBlock(pos: BlockPos, state: BlockStateInput) = state.place(tracker.level, offset(pos), 3)
/**
* Modify a block state within the test.
*/

View File

@ -1,3 +1,5 @@
-- TurtleTest.`Use compostors`
test.eq(true, turtle.dropDown(), "Drop items into compostor")
test.ok()

View File

@ -1,3 +1,5 @@
-- TurtleTest.`Cleaned with cauldrons`
local old_details = turtle.getItemDetail(1, true)
test.assert(turtle.place(), "Dyed turtle")

View File

@ -0,0 +1,14 @@
-- CraftOsTest.`Sends basic rednet messages`
rednet.open("top")
local id, msg
repeat
rednet.send(14, "Test msg") -- Keep sending, as other computer may not have started yet.
id, msg = rednet.receive(nil, 1)
print(id, msg)
until id == 14
test.eq("Test msg", msg)
test.ok()

View File

@ -0,0 +1,7 @@
-- CraftOsTest.`Sends basic rednet messages`
rednet.open("top")
while true do
local id, msg, protocol = rednet.receive()
rednet.send(id, msg, protocol)
end

View File

@ -1,3 +1,3 @@
{
"computer": 12
"computer": 14
}

View File

@ -0,0 +1,555 @@
{
size: [5, 5, 5],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [3, 0, 0],
state: 0
},
{
pos: [4, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [3, 0, 1],
state: 0
},
{
pos: [4, 0, 1],
state: 0
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
pos: [3, 0, 2],
state: 0
},
{
pos: [4, 0, 2],
state: 0
},
{
pos: [0, 0, 3],
state: 0
},
{
pos: [1, 0, 3],
state: 0
},
{
pos: [2, 0, 3],
state: 0
},
{
pos: [3, 0, 3],
state: 0
},
{
pos: [4, 0, 3],
state: 0
},
{
pos: [0, 0, 4],
state: 0
},
{
pos: [1, 0, 4],
state: 0
},
{
pos: [2, 0, 4],
state: 0
},
{
pos: [3, 0, 4],
state: 0
},
{
pos: [4, 0, 4],
state: 0
},
{
nbt: {
Label: "Echo",
id: "computercraft:computer_advanced",
ComputerId: 14,
On: 1b
},
pos: [1, 1, 2],
state: 1
},
{
nbt: {
Label: "Main",
id: "computercraft:computer_advanced",
ComputerId: 13,
On: 1b
},
pos: [3, 1, 2],
state: 2
},
{
nbt: {
id: "computercraft:wireless_modem_normal"
},
pos: [1, 2, 2],
state: 3
},
{
nbt: {
id: "computercraft:wireless_modem_normal"
},
pos: [3, 2, 2],
state: 3
},
{
pos: [0, 1, 0],
state: 4
},
{
pos: [1, 1, 0],
state: 4
},
{
pos: [2, 1, 0],
state: 4
},
{
pos: [3, 1, 0],
state: 4
},
{
pos: [4, 1, 0],
state: 4
},
{
pos: [0, 2, 0],
state: 4
},
{
pos: [1, 2, 0],
state: 4
},
{
pos: [2, 2, 0],
state: 4
},
{
pos: [3, 2, 0],
state: 4
},
{
pos: [4, 2, 0],
state: 4
},
{
pos: [0, 3, 0],
state: 4
},
{
pos: [1, 3, 0],
state: 4
},
{
pos: [2, 3, 0],
state: 4
},
{
pos: [3, 3, 0],
state: 4
},
{
pos: [4, 3, 0],
state: 4
},
{
pos: [0, 4, 0],
state: 4
},
{
pos: [1, 4, 0],
state: 4
},
{
pos: [2, 4, 0],
state: 4
},
{
pos: [3, 4, 0],
state: 4
},
{
pos: [4, 4, 0],
state: 4
},
{
pos: [0, 1, 1],
state: 4
},
{
pos: [1, 1, 1],
state: 4
},
{
pos: [2, 1, 1],
state: 4
},
{
pos: [3, 1, 1],
state: 4
},
{
pos: [4, 1, 1],
state: 4
},
{
pos: [0, 2, 1],
state: 4
},
{
pos: [1, 2, 1],
state: 4
},
{
pos: [2, 2, 1],
state: 4
},
{
pos: [3, 2, 1],
state: 4
},
{
pos: [4, 2, 1],
state: 4
},
{
pos: [0, 3, 1],
state: 4
},
{
pos: [1, 3, 1],
state: 4
},
{
pos: [2, 3, 1],
state: 4
},
{
pos: [3, 3, 1],
state: 4
},
{
pos: [4, 3, 1],
state: 4
},
{
pos: [0, 4, 1],
state: 4
},
{
pos: [1, 4, 1],
state: 4
},
{
pos: [2, 4, 1],
state: 4
},
{
pos: [3, 4, 1],
state: 4
},
{
pos: [4, 4, 1],
state: 4
},
{
pos: [0, 1, 2],
state: 4
},
{
pos: [2, 1, 2],
state: 4
},
{
pos: [4, 1, 2],
state: 4
},
{
pos: [0, 2, 2],
state: 4
},
{
pos: [2, 2, 2],
state: 4
},
{
pos: [4, 2, 2],
state: 4
},
{
pos: [0, 3, 2],
state: 4
},
{
pos: [1, 3, 2],
state: 4
},
{
pos: [2, 3, 2],
state: 4
},
{
pos: [3, 3, 2],
state: 4
},
{
pos: [4, 3, 2],
state: 4
},
{
pos: [0, 4, 2],
state: 4
},
{
pos: [1, 4, 2],
state: 4
},
{
pos: [2, 4, 2],
state: 4
},
{
pos: [3, 4, 2],
state: 4
},
{
pos: [4, 4, 2],
state: 4
},
{
pos: [0, 1, 3],
state: 4
},
{
pos: [1, 1, 3],
state: 4
},
{
pos: [2, 1, 3],
state: 4
},
{
pos: [3, 1, 3],
state: 4
},
{
pos: [4, 1, 3],
state: 4
},
{
pos: [0, 2, 3],
state: 4
},
{
pos: [1, 2, 3],
state: 4
},
{
pos: [2, 2, 3],
state: 4
},
{
pos: [3, 2, 3],
state: 4
},
{
pos: [4, 2, 3],
state: 4
},
{
pos: [0, 3, 3],
state: 4
},
{
pos: [1, 3, 3],
state: 4
},
{
pos: [2, 3, 3],
state: 4
},
{
pos: [3, 3, 3],
state: 4
},
{
pos: [4, 3, 3],
state: 4
},
{
pos: [0, 4, 3],
state: 4
},
{
pos: [1, 4, 3],
state: 4
},
{
pos: [2, 4, 3],
state: 4
},
{
pos: [3, 4, 3],
state: 4
},
{
pos: [4, 4, 3],
state: 4
},
{
pos: [0, 1, 4],
state: 4
},
{
pos: [1, 1, 4],
state: 4
},
{
pos: [2, 1, 4],
state: 4
},
{
pos: [3, 1, 4],
state: 4
},
{
pos: [4, 1, 4],
state: 4
},
{
pos: [0, 2, 4],
state: 4
},
{
pos: [1, 2, 4],
state: 4
},
{
pos: [2, 2, 4],
state: 4
},
{
pos: [3, 2, 4],
state: 4
},
{
pos: [4, 2, 4],
state: 4
},
{
pos: [0, 3, 4],
state: 4
},
{
pos: [1, 3, 4],
state: 4
},
{
pos: [2, 3, 4],
state: 4
},
{
pos: [3, 3, 4],
state: 4
},
{
pos: [4, 3, 4],
state: 4
},
{
pos: [0, 4, 4],
state: 4
},
{
pos: [1, 4, 4],
state: 4
},
{
pos: [2, 4, 4],
state: 4
},
{
pos: [3, 4, 4],
state: 4
},
{
pos: [4, 4, 4],
state: 4
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Properties: {
facing: "north",
state: "on"
},
Name: "computercraft:computer_advanced"
},
{
Properties: {
facing: "north",
state: "blinking"
},
Name: "computercraft:computer_advanced"
},
{
Properties: {
waterlogged: "false",
facing: "down",
on: "true"
},
Name: "computercraft:wireless_modem_normal"
},
{
Name: "minecraft:air"
}
],
DataVersion: 2230
}

View File

@ -0,0 +1,515 @@
{
size: [5, 5, 5],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [3, 0, 0],
state: 0
},
{
pos: [4, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [3, 0, 1],
state: 0
},
{
pos: [4, 0, 1],
state: 0
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
pos: [3, 0, 2],
state: 0
},
{
pos: [4, 0, 2],
state: 0
},
{
pos: [0, 0, 3],
state: 0
},
{
pos: [1, 0, 3],
state: 0
},
{
pos: [2, 0, 3],
state: 0
},
{
pos: [3, 0, 3],
state: 0
},
{
pos: [4, 0, 3],
state: 0
},
{
pos: [0, 0, 4],
state: 0
},
{
pos: [1, 0, 4],
state: 0
},
{
pos: [2, 0, 4],
state: 0
},
{
pos: [3, 0, 4],
state: 0
},
{
pos: [4, 0, 4],
state: 0
},
{
pos: [0, 1, 0],
state: 1
},
{
pos: [1, 1, 0],
state: 1
},
{
pos: [2, 1, 0],
state: 1
},
{
pos: [3, 1, 0],
state: 1
},
{
pos: [4, 1, 0],
state: 1
},
{
pos: [0, 2, 0],
state: 1
},
{
pos: [1, 2, 0],
state: 1
},
{
pos: [2, 2, 0],
state: 1
},
{
pos: [3, 2, 0],
state: 1
},
{
pos: [4, 2, 0],
state: 1
},
{
pos: [0, 3, 0],
state: 1
},
{
pos: [1, 3, 0],
state: 1
},
{
pos: [2, 3, 0],
state: 1
},
{
pos: [3, 3, 0],
state: 1
},
{
pos: [4, 3, 0],
state: 1
},
{
pos: [0, 4, 0],
state: 1
},
{
pos: [1, 4, 0],
state: 1
},
{
pos: [2, 4, 0],
state: 1
},
{
pos: [3, 4, 0],
state: 1
},
{
pos: [4, 4, 0],
state: 1
},
{
pos: [0, 1, 1],
state: 1
},
{
pos: [1, 1, 1],
state: 1
},
{
pos: [2, 1, 1],
state: 1
},
{
pos: [3, 1, 1],
state: 1
},
{
pos: [4, 1, 1],
state: 1
},
{
pos: [0, 2, 1],
state: 1
},
{
pos: [1, 2, 1],
state: 1
},
{
pos: [2, 2, 1],
state: 1
},
{
pos: [3, 2, 1],
state: 1
},
{
pos: [4, 2, 1],
state: 1
},
{
pos: [0, 3, 1],
state: 1
},
{
pos: [1, 3, 1],
state: 1
},
{
pos: [2, 3, 1],
state: 1
},
{
pos: [3, 3, 1],
state: 1
},
{
pos: [4, 3, 1],
state: 1
},
{
pos: [0, 4, 1],
state: 1
},
{
pos: [1, 4, 1],
state: 1
},
{
pos: [2, 4, 1],
state: 1
},
{
pos: [3, 4, 1],
state: 1
},
{
pos: [4, 4, 1],
state: 1
},
{
pos: [0, 1, 2],
state: 1
},
{
pos: [1, 1, 2],
state: 1
},
{
pos: [2, 1, 2],
state: 1
},
{
pos: [3, 1, 2],
state: 1
},
{
pos: [4, 1, 2],
state: 1
},
{
pos: [0, 2, 2],
state: 1
},
{
pos: [1, 2, 2],
state: 1
},
{
pos: [2, 2, 2],
state: 1
},
{
pos: [3, 2, 2],
state: 1
},
{
pos: [4, 2, 2],
state: 1
},
{
pos: [0, 3, 2],
state: 1
},
{
pos: [1, 3, 2],
state: 1
},
{
pos: [2, 3, 2],
state: 1
},
{
pos: [3, 3, 2],
state: 1
},
{
pos: [4, 3, 2],
state: 1
},
{
pos: [0, 4, 2],
state: 1
},
{
pos: [1, 4, 2],
state: 1
},
{
pos: [2, 4, 2],
state: 1
},
{
pos: [3, 4, 2],
state: 1
},
{
pos: [4, 4, 2],
state: 1
},
{
pos: [0, 1, 3],
state: 1
},
{
pos: [1, 1, 3],
state: 1
},
{
pos: [2, 1, 3],
state: 1
},
{
pos: [3, 1, 3],
state: 1
},
{
pos: [4, 1, 3],
state: 1
},
{
pos: [0, 2, 3],
state: 1
},
{
pos: [1, 2, 3],
state: 1
},
{
pos: [2, 2, 3],
state: 1
},
{
pos: [3, 2, 3],
state: 1
},
{
pos: [4, 2, 3],
state: 1
},
{
pos: [0, 3, 3],
state: 1
},
{
pos: [1, 3, 3],
state: 1
},
{
pos: [2, 3, 3],
state: 1
},
{
pos: [3, 3, 3],
state: 1
},
{
pos: [4, 3, 3],
state: 1
},
{
pos: [0, 4, 3],
state: 1
},
{
pos: [1, 4, 3],
state: 1
},
{
pos: [2, 4, 3],
state: 1
},
{
pos: [3, 4, 3],
state: 1
},
{
pos: [4, 4, 3],
state: 1
},
{
pos: [0, 1, 4],
state: 1
},
{
pos: [1, 1, 4],
state: 1
},
{
pos: [2, 1, 4],
state: 1
},
{
pos: [3, 1, 4],
state: 1
},
{
pos: [4, 1, 4],
state: 1
},
{
pos: [0, 2, 4],
state: 1
},
{
pos: [1, 2, 4],
state: 1
},
{
pos: [2, 2, 4],
state: 1
},
{
pos: [3, 2, 4],
state: 1
},
{
pos: [4, 2, 4],
state: 1
},
{
pos: [0, 3, 4],
state: 1
},
{
pos: [1, 3, 4],
state: 1
},
{
pos: [2, 3, 4],
state: 1
},
{
pos: [3, 3, 4],
state: 1
},
{
pos: [4, 3, 4],
state: 1
},
{
pos: [0, 4, 4],
state: 1
},
{
pos: [1, 4, 4],
state: 1
},
{
pos: [2, 4, 4],
state: 1
},
{
pos: [3, 4, 4],
state: 1
},
{
pos: [4, 4, 4],
state: 1
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Name: "minecraft:air"
}
],
DataVersion: 2230
}