1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-06-26 15:13:01 +00:00

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

This commit is contained in:
SquidDev 2020-11-07 12:46:10 +00:00
commit 83df64e520
63 changed files with 380 additions and 373 deletions

View File

@ -34,6 +34,7 @@ jobs:
- name: Upload Coverage - name: Upload Coverage
run: bash <(curl -s https://codecov.io/bash) run: bash <(curl -s https://codecov.io/bash)
continue-on-error: true
- name: Generate Java documentation stubs - name: Generate Java documentation stubs
run: ./gradlew luaJavadoc --no-daemon run: ./gradlew luaJavadoc --no-daemon

View File

@ -12,5 +12,5 @@ chmod 600 "$HOME/.ssh/key"
# And upload # And upload
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \ rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
"$GITHUB_WORKSPACE/doc/" \ "$GITHUB_WORKSPACE/doc/out/" \
"$SSH_USER@$SSH_HOST:/var/www/tweaked.cc/$DEST" "$SSH_USER@$SSH_HOST:/var/www/tweaked.cc/$DEST"

3
.gitignore vendored
View File

@ -3,9 +3,8 @@
/logs /logs
/build /build
/out /out
/doc/**/*.html /doc/out/
/doc/javadoc/ /doc/javadoc/
/doc/index.json
# Runtime directories # Runtime directories
/run /run

View File

@ -9,7 +9,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.google.code.gson:gson:2.8.1' classpath 'com.google.code.gson:gson:2.8.1'
classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.181' classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.187'
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2' classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0' classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0'
} }
@ -122,7 +122,7 @@ dependencies {
deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.0" deployerJars "org.apache.maven.wagon:wagon-ssh:3.0.0"
cctJavadoc 'cc.tweaked:cct-javadoc:1.1.0' cctJavadoc 'cc.tweaked:cct-javadoc:1.2.1'
} }
// Compile tasks // Compile tasks

View File

@ -1,8 +1,6 @@
--- The http library allows communicating with web servers, sending and --- The http library allows communicating with web servers, sending and
-- receiving data from them. -- receiving data from them.
-- --
-- #### `http_check` event
--
-- @module http -- @module http
--- Asynchronously make a HTTP request to the given url. --- Asynchronously make a HTTP request to the given url.

View File

@ -1,141 +1,9 @@
/* Basic reset on elements */ /* Pretty tables, mostly inherited from table.definition-list */
h1, h2, h3, h4, p, table, div, body { table.pretty-table {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* Make the page a little more airy */
body {
margin: 20px auto;
max-width: 1200px;
padding: 0 10px;
line-height: 1.6;
color: #222;
background: #fff;
}
/* Try to use system default fonts. */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans",
"Droid Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
code, pre, kbd, .parameter, .type, .definition-name, .reference-code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
}
/* Some definitions of basic tags */
code {
color: #c7254e;
background-color: #f9f2f4;
}
p {
margin: 0.9em 0;
}
h1 {
font-size: 1.5em;
font-weight: lighter;
border-bottom: solid 1px #aaa;
}
h2, h3, h4 { margin: 1.4em 0 0.3em;}
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; font-weight: bold; }
h4 { font-size: 1.06em; }
a, a:visited, a:active { font-weight: bold; color: #004080; text-decoration: none; }
a:hover { text-decoration: underline; }
blockquote {
padding: 0.3em;
margin: 1em 0;
background: #f0f0f0;
border-left: solid 0.5em #ccc;
}
/* Stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* Make the target distinct; helps when we're navigating to a function */
a:target + * { background-color: #FFFF99; }
/* Allow linking to any subsection */
a[name]::before { content: "#"; }
/* Layout */
#main {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
min-height: calc(100vh - 100px);
}
#main > nav {
flex-basis: 30%;
min-width: 150px;
max-width: 250px;
background-color: #f0f0f0;
}
nav h1, nav ul { padding: 0em 10px; }
nav h2 {
background-color:#e7e7e7;
font-size: 1.1em;
color:#000000;
padding: 5px 10px;
}
nav ul {
list-style-type: none;
margin: 0;
}
#content {
flex-shrink: 1;
flex-basis: 80%;
padding: 0px 10px;
}
footer {
text-align: right;
font-size: 0.8em;
}
/* The definition lists at the top of each page */
table.definition-list, table.pretty-table {
border-collapse: collapse; border-collapse: collapse;
width: 100%; width: 100%;
} }
table.definition-list td, table.definition-list th {
border: 1px solid #cccccc;
padding: 5px;
}
table.definition-list th {
background-color: #f0f0f0;
min-width: 200px;
white-space: nowrap;
text-align: right;
}
/* Deprecated definitions */
table.definition-list tr.definition-deprecated th {
text-decoration: line-through;
}
table.definition-list td { width: 100%; }
/* Pretty tables, mostly inherited from table.definition-list */
table.pretty-table td, table.pretty-table th { table.pretty-table td, table.pretty-table th {
border: 1px solid #cccccc; border: 1px solid #cccccc;
padding: 2px 4px; padding: 2px 4px;
@ -144,81 +12,3 @@ table.pretty-table td, table.pretty-table th {
table.pretty-table th { table.pretty-table th {
background-color: #f0f0f0; background-color: #f0f0f0;
} }
dl.definition dt {
border-top: 1px solid #ccc;
padding-top: 1em;
display: flex;
}
dl.definition dt .definition-name {
padding: 0 0.1em;
margin: 0 0.1em;
flex-grow: 1;
}
/* Deprecated definitions */
dl.definition dt .definition-name.definition-deprecated {
text-decoration: line-through;
}
dl.definition dd {
padding-bottom: 1em;
margin: 10px 0 0 20px;
}
dl.definition h3 {
font-size: .95em;
}
/* Links to source-code */
.source-link { font-size: 0.8em; }
.source-link::before { content: '[' }
.source-link::after { content: ']' }
a.source-link, a.source-link:visited, a.source-link:active { color: #505050; }
/* Method definitions */
span.parameter:after { content:":"; padding-left: 0.3em; }
.optional { text-decoration: underline dotted; }
/** Fancy colour display. */
.colour-ref {
display: inline-block;
width: 0.8em;
height: 0.8em;
margin: 0.1em 0.1em 0.3em 0.1em; /* Terrrible hack to force vertical alignment. */
border: solid 1px black;
box-sizing: border-box;
vertical-align: middle;
}
/** Fancy keyboard shortcut styling, inspired by GitHub markdown. */
kbd {
display: inline-block;
padding: 4px 5px;
font-size: 0.8em;
line-height: 10px;
color: #444d56;
vertical-align: middle;
background-color: #fafbfc;
border: 1px solid #d1d5da;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #d1d5da;
}
/* styles for prettification of source */
.highlight .comment { color: #558817; }
.highlight .constant { color: #a8660d; }
.highlight .escape { color: #844631; }
.highlight .keyword { color: #aa5050; font-weight: bold; }
.highlight .library { color: #0e7c6b; }
.highlight .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
.highlight .string { color: #8080ff; }
.highlight .literal-kw { color: #8080ff; }
.highlight .number { color: #f8660d; }
.highlight .operator { color: #2239a8; font-weight: bold; }
.highlight .preprocessor, pre .prepro { color: #a33243; }
.highlight .global { color: #800080; }
.highlight .user-keyword { color: #800080; }
.highlight .prompt { color: #558817; }
.highlight .url { color: #272fc2; text-decoration: underline; }

View File

@ -1,5 +1,5 @@
# Mod properties # Mod properties
mod_version=1.93.1 mod_version=1.94.0
# Minecraft properties (update mods.toml when changing) # Minecraft properties (update mods.toml when changing)
mc_version=1.16.4 mc_version=1.16.4

View File

@ -10,7 +10,10 @@
(doc (doc
(title "CC: Tweaked") (title "CC: Tweaked")
(destination doc/out)
(logo src/main/resources/pack.png)
(index doc/index.md) (index doc/index.md)
(styles doc/styles.css)
(source-link https://github.com/SquidDev-CC/CC-Tweaked/blob/${commit}/${path}#L${line}) (source-link https://github.com/SquidDev-CC/CC-Tweaked/blob/${commit}/${path}#L${line})
(module-kinds (module-kinds

View File

@ -90,8 +90,6 @@ public final class ComputerCraft
public static boolean turtlesCanPush = true; public static boolean turtlesCanPush = true;
public static EnumSet<TurtleAction> turtleDisabledActions = EnumSet.noneOf( TurtleAction.class ); public static EnumSet<TurtleAction> turtleDisabledActions = EnumSet.noneOf( TurtleAction.class );
public static boolean genericPeripheral = false;
public static int computerTermWidth = 51; public static int computerTermWidth = 51;
public static int computerTermHeight = 19; public static int computerTermHeight = 19;

View File

@ -139,7 +139,7 @@ public interface ITurtleAccess
* *
* @return This turtle's owner. * @return This turtle's owner.
*/ */
@Nonnull @Nullable
GameProfile getOwningPlayer(); GameProfile getOwningPlayer();
/** /**

View File

@ -119,6 +119,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height ) private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height )
{ {
RenderSystem.enableBlend();
Minecraft.getInstance().getTextureManager() Minecraft.getInstance().getTextureManager()
.bindTexture( colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ) ); .bindTexture( colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ) );
@ -137,7 +138,6 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
private static void renderLight( Matrix4f transform, int colour, int width, int height ) private static void renderLight( Matrix4f transform, int colour, int width, int height )
{ {
RenderSystem.enableBlend();
RenderSystem.disableTexture(); RenderSystem.disableTexture();
float r = ((colour >>> 16) & 0xFF) / 255.0f; float r = ((colour >>> 16) & 0xFF) / 255.0f;

View File

@ -82,8 +82,6 @@ public final class Config
private static final ConfigValue<Integer> monitorWidth; private static final ConfigValue<Integer> monitorWidth;
private static final ConfigValue<Integer> monitorHeight; private static final ConfigValue<Integer> monitorHeight;
private static final ConfigValue<Boolean> genericPeripheral;
private static final ConfigValue<MonitorRenderer> monitorRenderer; private static final ConfigValue<MonitorRenderer> monitorRenderer;
private static final ConfigValue<Integer> monitorDistance; private static final ConfigValue<Integer> monitorDistance;
@ -294,17 +292,6 @@ public final class Config
builder.pop(); builder.pop();
} }
{
builder.comment( "Options for various experimental features. These are not guaranteed to be stable, and may change or be removed across versions." );
builder.push( "experimental" );
genericPeripheral = builder
.comment( "Attempt to make any existing block (or tile entity) a peripheral.\n" +
"This provides peripheral methods for any inventory, fluid tank or energy storage block. It will" +
"_not_ provide methods which have an existing peripheral provider." )
.define( "generic_peripherals", false );
}
serverSpec = builder.build(); serverSpec = builder.build();
Builder clientBuilder = new Builder(); Builder clientBuilder = new Builder();
@ -379,9 +366,6 @@ public final class Config
ComputerCraft.monitorWidth = monitorWidth.get(); ComputerCraft.monitorWidth = monitorWidth.get();
ComputerCraft.monitorHeight = monitorHeight.get(); ComputerCraft.monitorHeight = monitorHeight.get();
// Experimental
ComputerCraft.genericPeripheral = genericPeripheral.get();
// Client // Client
ComputerCraft.monitorRenderer = monitorRenderer.get(); ComputerCraft.monitorRenderer = monitorRenderer.get();
ComputerCraft.monitorDistanceSq = monitorDistance.get() * monitorDistance.get(); ComputerCraft.monitorDistanceSq = monitorDistance.get() * monitorDistance.get();

View File

@ -116,7 +116,6 @@ public class CommandAPI implements ILuaAPI
* @param command The command to execute. * @param command The command to execute.
* @return The "task id". When this command has been executed, it will queue a `task_complete` event with a matching id. * @return The "task id". When this command has been executed, it will queue a `task_complete` event with a matching id.
* @throws LuaException (hidden) If the task cannot be created. * @throws LuaException (hidden) If the task cannot be created.
* @cc.tparam string command The command to execute.
* @cc.usage Asynchronously sets the block above the computer to stone. * @cc.usage Asynchronously sets the block above the computer to stone.
* <pre> * <pre>
* commands.execAsync("~ ~1 ~ minecraft:stone") * commands.execAsync("~ ~1 ~ minecraft:stone")

View File

@ -140,7 +140,8 @@ public class DiskDrivePeripheral implements IPeripheral
/** /**
* Returns the title of the inserted audio disk. * Returns the title of the inserted audio disk.
* *
* @return The title of the audio, or {@code nil} if no audio disk is inserted. * @return The title of the audio, or {@code false} if no audio disk is inserted.
* @cc.treturn string|nil|false The title of the audio, {@code false} if no disk is inserted, or {@code nil} if the disk has no audio.
*/ */
@LuaFunction @LuaFunction
@Nullable @Nullable

View File

@ -6,7 +6,6 @@
package dan200.computercraft.shared.peripheral.generic; package dan200.computercraft.shared.peripheral.generic;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.asm.NamedMethod; import dan200.computercraft.core.asm.NamedMethod;
import dan200.computercraft.core.asm.PeripheralMethod; import dan200.computercraft.core.asm.PeripheralMethod;
@ -35,8 +34,6 @@ public class GenericPeripheralProvider
@Nonnull @Nonnull
public static LazyOptional<IPeripheral> getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ) public static LazyOptional<IPeripheral> getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side )
{ {
if( !ComputerCraft.genericPeripheral ) return LazyOptional.empty();
TileEntity tile = world.getTileEntity( pos ); TileEntity tile = world.getTileEntity( pos );
if( tile == null ) return LazyOptional.empty(); if( tile == null ) return LazyOptional.empty();

View File

@ -7,7 +7,6 @@
package dan200.computercraft.shared.peripheral.generic.data; package dan200.computercraft.shared.peripheral.generic.data;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.util.NBTUtil; import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
@ -26,9 +25,6 @@ import java.util.stream.Collectors;
/** /**
* Data providers for items. * Data providers for items.
*
* We guard using {@link ComputerCraft#genericPeripheral} in several places, as advanced functionality should not be
* exposed for {@code turtle.getItemDetail} when generic peripehrals are disabled.
*/ */
public class ItemData public class ItemData
{ {
@ -73,8 +69,6 @@ public class ItemData
data.put( "tags", DataHelpers.getTags( stack.getItem().getTags() ) ); data.put( "tags", DataHelpers.getTags( stack.getItem().getTags() ) );
if( !ComputerCraft.genericPeripheral ) return data;
CompoundNBT tag = stack.getTag(); CompoundNBT tag = stack.getTag();
if( tag != null && tag.contains( "display", Constants.NBT.TAG_COMPOUND ) ) if( tag != null && tag.contains( "display", Constants.NBT.TAG_COMPOUND ) )
{ {

View File

@ -45,6 +45,7 @@ import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.InvWrapper; import net.minecraftforge.items.wrapper.InvWrapper;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -598,7 +599,7 @@ public class TurtleBrain implements ITurtleAccess
m_owningPlayer = profile; m_owningPlayer = profile;
} }
@Nonnull @Nullable
@Override @Override
public GameProfile getOwningPlayer() public GameProfile getOwningPlayer()
{ {

View File

@ -17,6 +17,7 @@ import net.minecraft.entity.EntitySize;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.Pose; import net.minecraft.entity.Pose;
import net.minecraft.entity.passive.horse.AbstractHorseEntity; import net.minecraft.entity.passive.horse.AbstractHorseEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.IInventory; import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.container.INamedContainerProvider; import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -41,11 +42,30 @@ public final class TurtlePlayer extends FakePlayer
"[ComputerCraft]" "[ComputerCraft]"
); );
private TurtlePlayer( ITurtleAccess turtle ) private TurtlePlayer( ServerWorld world, GameProfile name )
{ {
super( (ServerWorld) turtle.getWorld(), getProfile( turtle.getOwningPlayer() ) ); super( world, name );
this.connection = new FakeNetHandler( this ); }
setState( turtle );
private static TurtlePlayer create( ITurtleAccess turtle )
{
ServerWorld world = (ServerWorld) turtle.getWorld();
GameProfile profile = turtle.getOwningPlayer();
TurtlePlayer player = new TurtlePlayer( world, getProfile( profile ) );
player.connection = new FakeNetHandler( player );
player.setState( turtle );
if( profile != null && profile.getId() != null )
{
// Constructing a player overrides the "active player" variable in advancements. As fake players cannot
// get advancements, this prevents a normal player who has placed a turtle from getting advancements.
// We try to locate the "actual" player and restore them.
ServerPlayerEntity actualPlayer = world.getServer().getPlayerList().getPlayerByUUID( profile.getId() );
if( actualPlayer != null ) player.getAdvancements().setPlayer( actualPlayer );
}
return player;
} }
private static GameProfile getProfile( @Nullable GameProfile profile ) private static GameProfile getProfile( @Nullable GameProfile profile )
@ -72,14 +92,14 @@ public final class TurtlePlayer extends FakePlayer
public static TurtlePlayer get( ITurtleAccess access ) public static TurtlePlayer get( ITurtleAccess access )
{ {
if( !(access instanceof TurtleBrain) ) return new TurtlePlayer( access ); if( !(access instanceof TurtleBrain) ) return create( access );
TurtleBrain brain = (TurtleBrain) access; TurtleBrain brain = (TurtleBrain) access;
TurtlePlayer player = brain.m_cachedPlayer; TurtlePlayer player = brain.m_cachedPlayer;
if( player == null || player.getGameProfile() != getProfile( access.getOwningPlayer() ) if( player == null || player.getGameProfile() != getProfile( access.getOwningPlayer() )
|| player.getEntityWorld() != access.getWorld() ) || player.getEntityWorld() != access.getWorld() )
{ {
player = brain.m_cachedPlayer = new TurtlePlayer( brain ); player = brain.m_cachedPlayer = create( brain );
} }
else else
{ {

View File

@ -332,3 +332,21 @@ function rgb8(r, g, b)
return packRGB(r, g, b) return packRGB(r, g, b)
end end
end end
-- Colour to hex lookup table for toBlit
local color_hex_lookup = {}
for i = 0, 15 do
color_hex_lookup[2 ^ i] = string.format("%x", i)
end
--- Converts the given color to a paint/blit hex character (0-9a-f).
--
-- This is equivalent to converting floor(log_2(color)) to hexadecimal.
--
-- @tparam number color The color to convert.
-- @treturn string The blit hex code of the color.
function toBlit(color)
expect(1, color, "number")
return color_hex_lookup[color] or
string.format("%x", math.floor(math.log(color) / math.log(2)))
end

View File

@ -23,6 +23,25 @@ local function parseLine(tImageArg, sLine)
table.insert(tImageArg, tLine) table.insert(tImageArg, tLine)
end end
-- Sorts pairs of startX/startY/endX/endY such that the start is always the min
local function sortCoords(startX, startY, endX, endY)
local minX, maxX, minY, maxY
if startX <= endX then
minX, maxX = startX, endX
else
minX, maxX = endX, startX
end
if startY <= endY then
minY, maxY = startY, endY
else
minY, maxY = endY, startY
end
return minX, maxX, minY, maxY
end
--- Parses an image from a multi-line string --- Parses an image from a multi-line string
-- --
-- @tparam string image The string containing the raw-image data. -- @tparam string image The string containing the raw-image data.
@ -71,9 +90,6 @@ function drawPixel(xPos, yPos, colour)
expect(2, yPos, "number") expect(2, yPos, "number")
expect(3, colour, "number", "nil") expect(3, colour, "number", "nil")
if type(xPos) ~= "number" then error("bad argument #1 (expected number, got " .. type(xPos) .. ")", 2) end
if type(yPos) ~= "number" then error("bad argument #2 (expected number, got " .. type(yPos) .. ")", 2) end
if colour ~= nil and type(colour) ~= "number" then error("bad argument #3 (expected number, got " .. type(colour) .. ")", 2) end
if colour then if colour then
term.setBackgroundColor(colour) term.setBackgroundColor(colour)
end end
@ -111,17 +127,7 @@ function drawLine(startX, startY, endX, endY, colour)
return return
end end
local minX = math.min(startX, endX) local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
local maxX, minY, maxY
if minX == startX then
minY = startY
maxX = endX
maxY = endY
else
minY = endY
maxX = startX
maxY = startY
end
-- TODO: clip to screen rectangle? -- TODO: clip to screen rectangle?
@ -177,37 +183,33 @@ function drawBox(startX, startY, endX, endY, nColour)
endY = math.floor(endY) endY = math.floor(endY)
if nColour then if nColour then
term.setBackgroundColor(nColour) term.setBackgroundColor(nColour) -- Maintain legacy behaviour
else
nColour = term.getBackgroundColour()
end end
local colourHex = colours.toBlit(nColour)
if startX == endX and startY == endY then if startX == endX and startY == endY then
drawPixelInternal(startX, startY) drawPixelInternal(startX, startY)
return return
end end
local minX = math.min(startX, endX) local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
local maxX, minY, maxY local width = maxX - minX + 1
if minX == startX then
minY = startY for y = minY, maxY do
maxX = endX if y == minY or y == maxY then
maxY = endY term.setCursorPos(minX, y)
term.blit((" "):rep(width), colourHex:rep(width), colourHex:rep(width))
else else
minY = endY term.setCursorPos(minX, y)
maxX = startX term.blit(" ", colourHex, colourHex)
maxY = startY term.setCursorPos(maxX, y)
end term.blit(" ", colourHex, colourHex)
for x = minX, maxX do
drawPixelInternal(x, minY)
drawPixelInternal(x, maxY)
end
if maxY - minY >= 2 then
for y = minY + 1, maxY - 1 do
drawPixelInternal(minX, y)
drawPixelInternal(maxX, y)
end end
end end
end end
--- Draws a filled box on the current term from the specified start position to --- Draws a filled box on the current term from the specified start position to
-- the specified end position. -- the specified end position.
-- --
@ -233,29 +235,23 @@ function drawFilledBox(startX, startY, endX, endY, nColour)
endY = math.floor(endY) endY = math.floor(endY)
if nColour then if nColour then
term.setBackgroundColor(nColour) term.setBackgroundColor(nColour) -- Maintain legacy behaviour
else
nColour = term.getBackgroundColour()
end end
local colourHex = colours.toBlit(nColour)
if startX == endX and startY == endY then if startX == endX and startY == endY then
drawPixelInternal(startX, startY) drawPixelInternal(startX, startY)
return return
end end
local minX = math.min(startX, endX) local minX, maxX, minY, maxY = sortCoords(startX, startY, endX, endY)
local maxX, minY, maxY local width = maxX - minX + 1
if minX == startX then
minY = startY
maxX = endX
maxY = endY
else
minY = endY
maxX = startX
maxY = startY
end
for x = minX, maxX do
for y = minY, maxY do for y = minY, maxY do
drawPixelInternal(x, y) term.setCursorPos(minX, y)
end term.blit((" "):rep(width), colourHex:rep(width), colourHex:rep(width))
end end
end end

View File

@ -453,13 +453,13 @@ do
error_at(pos, "Unexpected %s, expected %s.", actual, exp) error_at(pos, "Unexpected %s, expected %s.", actual, exp)
end end
local function parse_string(str, pos) local function parse_string(str, pos, terminate)
local buf, n = {}, 1 local buf, n = {}, 1
while true do while true do
local c = sub(str, pos, pos) local c = sub(str, pos, pos)
if c == "" then error_at(pos, "Unexpected end of input, expected '\"'.") end if c == "" then error_at(pos, "Unexpected end of input, expected '\"'.") end
if c == '"' then break end if c == terminate then break end
if c == '\\' then if c == '\\' then
-- Handle the various escapes -- Handle the various escapes
@ -485,13 +485,13 @@ do
return concat(buf, "", 1, n - 1), pos + 1 return concat(buf, "", 1, n - 1), pos + 1
end end
local valid = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true } local num_types = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true }
local function parse_number(str, pos, opts) local function parse_number(str, pos, opts)
local _, last, num_str = find(str, '^(-?%d+%.?%d*[eE]?[+-]?%d*)', pos) local _, last, num_str = find(str, '^(-?%d+%.?%d*[eE]?[+-]?%d*)', pos)
local val = tonumber(num_str) local val = tonumber(num_str)
if not val then error_at(pos, "Malformed number %q.", num_str) end if not val then error_at(pos, "Malformed number %q.", num_str) end
if opts.nbt_style and valid[sub(str, pos + 1, pos + 1)] then return val, last + 2 end if opts.nbt_style and num_types[sub(str, last + 1, last + 1)] then return val, last + 2 end
return val, last + 1 return val, last + 1
end end
@ -501,9 +501,11 @@ do
return val, last + 1 return val, last + 1
end end
local arr_types = { I = true, L = true, B = true }
local function decode_impl(str, pos, opts) local function decode_impl(str, pos, opts)
local c = sub(str, pos, pos) local c = sub(str, pos, pos)
if c == '"' then return parse_string(str, pos + 1) if c == '"' then return parse_string(str, pos + 1, '"')
elseif c == "'" and opts.nbt_style then return parse_string(str, pos + 1, "\'")
elseif c == "-" or c >= "0" and c <= "9" then return parse_number(str, pos, opts) elseif c == "-" or c >= "0" and c <= "9" then return parse_number(str, pos, opts)
elseif c == "t" then elseif c == "t" then
if sub(str, pos + 1, pos + 3) == "rue" then return true, pos + 4 end if sub(str, pos + 1, pos + 3) == "rue" then return true, pos + 4 end
@ -528,7 +530,7 @@ do
while true do while true do
local key, value local key, value
if c == "\"" then key, pos = parse_string(str, pos + 1) if c == "\"" then key, pos = parse_string(str, pos + 1, "\"")
elseif opts.nbt_style then key, pos = parse_ident(str, pos) elseif opts.nbt_style then key, pos = parse_ident(str, pos)
else return expected(pos, c, "object key") else return expected(pos, c, "object key")
end end
@ -560,6 +562,11 @@ do
pos = skip(str, pos + 1) pos = skip(str, pos + 1)
c = sub(str, pos, pos) c = sub(str, pos, pos)
if arr_types[c] and sub(str, pos + 1, pos + 1) == ";" and opts.nbt_style then
pos = skip(str, pos + 2)
c = sub(str, pos, pos)
end
if c == "" then return expected(pos, c, "']'") end if c == "" then return expected(pos, c, "']'") end
if c == "]" then return empty_json_array, pos + 1 end if c == "]" then return empty_json_array, pos + 1 end

View File

@ -474,6 +474,14 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
end end
end end
--- Get whether this window is visible. Invisible windows will not be
-- drawn to the screen until they are made visible again.
--
-- @treturn boolean Whether this window is visible.
-- @see Window:setVisible
function window.isVisible()
return bVisible
end
--- Draw this window. This does nothing if the window is not visible. --- Draw this window. This does nothing if the window is not visible.
-- --
-- @see Window:setVisible -- @see Window:setVisible

View File

@ -1,3 +1,15 @@
# New features in CC: Tweaked 1.94.0
* Add getter for window visibility (devomaa)
* Generic peripherals are no longer experimental, and on by default.
* Use term.blit to draw boxes in paintutils (Lemmmy).
And several bug fixes:
* Fix turtles not getting advancements when turtles are on.
* Draw in-hand pocket computers with the correct transparent flags enabled.
* Several bug fixes to SNBT parsing.
* Fix several programs using their original name instead of aliases in usage hints (Lupus590).
# New features in CC: Tweaked 1.93.1 # New features in CC: Tweaked 1.93.1
* Various documentation improvements (Lemmmy). * Various documentation improvements (Lemmmy).
@ -74,8 +86,6 @@ And several bug fixes:
* Fix deadlock when mistakenly "watching" an unloaded chunk. * Fix deadlock when mistakenly "watching" an unloaded chunk.
* Fix full path of files being leaked in some errors. * Fix full path of files being leaked in some errors.
Type "help changelog" to see the full version history.
# New features in CC: Tweaked 1.89.1 # New features in CC: Tweaked 1.89.1
* Fix crashes when rendering monitors of varying sizes. * Fix crashes when rendering monitors of varying sizes.

View File

@ -1,6 +1,13 @@
New features in CC: Tweaked 1.93.1 New features in CC: Tweaked 1.94.0
* Various documentation improvements (Lemmmy). * Add getter for window visibility (devomaa)
* Fix TBO monitor renderer on some older graphics cards (Lemmmy). * Generic peripherals are no longer experimental, and on by default.
* Use term.blit to draw boxes in paintutils (Lemmmy).
And several bug fixes:
* Fix turtles not getting advancements when turtles are on.
* Draw in-hand pocket computers with the correct transparent flags enabled.
* Several bug fixes to SNBT parsing.
* Fix several programs using their original name instead of aliases in usage hints (Lupus590).
Type "help changelog" to see the full version history. Type "help changelog" to see the full version history.

View File

@ -1,6 +1,7 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs > 2 then if #tArgs > 2 then
print("Usage: alias <alias> <program>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <alias> <program>")
return return
end end

View File

@ -1,6 +1,7 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 1 then if #tArgs < 1 then
print("Usage: cd <path>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return return
end end

View File

@ -4,7 +4,8 @@ if not commands then
return return
end end
if #tArgs == 0 then if #tArgs == 0 then
printError("Usage: exec <command>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
printError("Usage: " .. programName .. " <command>")
return return
end end

View File

@ -1,6 +1,7 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 2 then if #tArgs < 2 then
print("Usage: cp <source> <destination>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <source> <destination>")
return return
end end

View File

@ -1,7 +1,8 @@
local args = table.pack(...) local args = table.pack(...)
if args.n < 1 then if args.n < 1 then
print("Usage: rm <paths>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <paths>")
return return
end end

View File

@ -1,7 +1,8 @@
-- Get file to edit -- Get file to edit
local tArgs = { ... } local tArgs = { ... }
if #tArgs == 0 then if #tArgs == 0 then
print("Usage: edit <path>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return return
end end

View File

@ -1,7 +1,8 @@
-- Get arguments -- Get arguments
local tArgs = { ... } local tArgs = { ... }
if #tArgs == 0 then if #tArgs == 0 then
print("Usage: eject <drive>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <drive>")
return return
end end

View File

@ -34,7 +34,8 @@ end
-- Determines if the file exists, and can be edited on this computer -- Determines if the file exists, and can be edited on this computer
local tArgs = { ... } local tArgs = { ... }
if #tArgs == 0 then if #tArgs == 0 then
print("Usage: paint <path>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return return
end end
local sPath = shell.resolve(tArgs[1]) local sPath = shell.resolve(tArgs[1])

View File

@ -1,10 +1,11 @@
local tArgs = { ... } local tArgs = { ... }
local function printUsage() local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:") print("Usages:")
print("dj play") print(programName .. " play")
print("dj play <drive>") print(programName .. " play <drive>")
print("dj stop") print(programName .. " stop")
end end
if #tArgs > 2 then if #tArgs > 2 then

View File

@ -1,8 +1,9 @@
local function printUsage() local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:") print("Usages:")
print("gps host") print(programName .. " host")
print("gps host <x> <y> <z>") print(programName .. " host <x> <y> <z>")
print("gps locate") print(programName .. " locate")
end end
local tArgs = { ... } local tArgs = { ... }

View File

@ -1,8 +1,9 @@
local function printUsage() local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:") print("Usages:")
print("pastebin put <filename>") print(programName .. " put <filename>")
print("pastebin get <code> <filename>") print(programName .. " get <code> <filename>")
print("pastebin run <code> <arguments>") print(programName .. " run <code> <arguments>")
end end
local tArgs = { ... } local tArgs = { ... }

View File

@ -1,7 +1,8 @@
local function printUsage() local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage:") print("Usage:")
print("wget <url> [filename]") print(programName .. " <url> [filename]")
print("wget run <url>") print(programName .. " run <url>")
end end
local tArgs = { ... } local tArgs = { ... }

View File

@ -1,11 +1,12 @@
local function printUsage() local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:") print("Usages:")
print("label get") print(programName .. " get")
print("label get <drive>") print(programName .. " get <drive>")
print("label set <text>") print(programName .. " set <text>")
print("label set <drive> <text>") print(programName .. " set <drive> <text>")
print("label clear") print(programName .. " clear")
print("label clear <drive>") print(programName .. " clear <drive>")
end end
local function checkDrive(sDrive) local function checkDrive(sDrive)

View File

@ -1,7 +1,8 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 1 then if #tArgs < 1 then
print("Usage: mkdir <paths>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <paths>")
return return
end end

View File

@ -1,5 +1,6 @@
local function printUsage() local function printUsage()
print("Usage: monitor <name> <program> <arguments>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <name> <program> <arguments>")
return return
end end

View File

@ -1,6 +1,7 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 2 then if #tArgs < 2 then
print("Usage: mv <source> <destination>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <source> <destination>")
return return
end end

View File

@ -1,9 +1,10 @@
local tArgs = { ... } local tArgs = { ... }
local function printUsage() local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:") print("Usages:")
print("chat host <hostname>") print(programName .. " host <hostname>")
print("chat join <hostname> <nickname>") print(programName .. " join <hostname> <nickname>")
end end
local sOpenedModem = nil local sOpenedModem = nil

View File

@ -1,11 +1,12 @@
local tArgs = { ... } local tArgs = { ... }
local function printUsage() local function printUsage()
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usages:") print("Usages:")
print("redstone probe") print(programName .. " probe")
print("redstone set <side> <value>") print(programName .. " set <side> <value>")
print("redstone set <side> <color> <value>") print(programName .. " set <side> <color> <value>")
print("redstone pulse <side> <count> <period>") print(programName .. " pulse <side> <count> <period>")
end end
local sCommand = tArgs[1] local sCommand = tArgs[1]

View File

@ -1,6 +1,7 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 2 then if #tArgs < 2 then
print("Usage: rename <source> <destination>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <source> <destination>")
return return
end end

View File

@ -11,7 +11,8 @@ end
local tArgs = { ... } local tArgs = { ... }
local nLimit = nil local nLimit = nil
if #tArgs < 1 then if #tArgs < 1 then
print("Usage: craft [number]") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " [number]")
return return
else else
nLimit = tonumber(tArgs[1]) nLimit = tonumber(tArgs[1])

View File

@ -5,7 +5,8 @@ end
local tArgs = { ... } local tArgs = { ... }
local function printUsage() local function printUsage()
print("Usage: equip <slot> <side>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <slot> <side>")
end end
if #tArgs ~= 2 then if #tArgs ~= 2 then

View File

@ -5,7 +5,8 @@ end
local tArgs = { ... } local tArgs = { ... }
if #tArgs ~= 1 then if #tArgs ~= 1 then
print("Usage: excavate <diameter>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <diameter>")
return return
end end

View File

@ -5,7 +5,8 @@ end
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 1 then if #tArgs < 1 then
print("Usage: go <direction> <distance>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <direction> <distance>")
return return
end end

View File

@ -6,7 +6,8 @@ end
local tArgs = { ... } local tArgs = { ... }
local nLimit = 1 local nLimit = 1
if #tArgs > 1 then if #tArgs > 1 then
print("Usage: refuel [number]") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " [number]")
return return
elseif #tArgs > 0 then elseif #tArgs > 0 then
if tArgs[1] == "all" then if tArgs[1] == "all" then

View File

@ -5,7 +5,8 @@ end
local tArgs = { ... } local tArgs = { ... }
if #tArgs ~= 1 then if #tArgs ~= 1 then
print("Usage: tunnel <length>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <length>")
return return
end end

View File

@ -5,7 +5,8 @@ end
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 1 then if #tArgs < 1 then
print("Usage: turn <direction> <turns>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <direction> <turns>")
return return
end end

View File

@ -5,7 +5,8 @@ end
local tArgs = { ... } local tArgs = { ... }
local function printUsage() local function printUsage()
print("Usage: unequip <side>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <side>")
end end
if #tArgs ~= 1 then if #tArgs ~= 1 then

View File

@ -1,6 +1,7 @@
local tArgs = { ... } local tArgs = { ... }
if #tArgs < 1 then if #tArgs < 1 then
print("Usage: type <path>") local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " <path>")
return return
end end

View File

@ -73,4 +73,20 @@ describe("The colors library", function()
expect(colors.rgb8(0.3, 0.5, 0.6)):equals(0x4c7f99) expect(colors.rgb8(0.3, 0.5, 0.6)):equals(0x4c7f99)
expect({ colors.rgb8(0x4c7f99) }):same { 0x4c / 0xFF, 0x7f / 0xFF, 0.6 } expect({ colors.rgb8(0x4c7f99) }):same { 0x4c / 0xFF, 0x7f / 0xFF, 0.6 }
end) end)
describe("colors.toBlit", function()
it("validates arguments", function()
expect.error(colors.toBlit, nil):eq("bad argument #1 (expected number, got nil)")
end)
it("converts all colors", function()
for i = 0, 15 do
expect(colors.toBlit(2 ^ i)):eq(string.format("%x", i))
end
end)
it("floors colors", function()
expect(colors.toBlit(16385)):eq("e")
end)
end)
end) end)

View File

@ -1,4 +1,19 @@
local with_window = require "test_helpers".with_window
describe("The paintutils library", function() describe("The paintutils library", function()
-- Verifies that a window's lines are equal to the given table of blit
-- strings ({{"text", "fg", "bg"}, {"text", "fg", "bg"}...})
local function window_eq(w, state)
-- Verification of the size isn't really important in the tests, but
-- better safe than sorry.
local _, height = w.getSize()
expect(#state):eq(height)
for line = 1, height do
expect({ w.getLine(line) }):same(state[line])
end
end
describe("paintutils.parseImage", function() describe("paintutils.parseImage", function()
it("validates arguments", function() it("validates arguments", function()
paintutils.parseImage("") paintutils.parseImage("")
@ -28,6 +43,30 @@ describe("The paintutils library", function()
expect.error(paintutils.drawLine, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)") expect.error(paintutils.drawLine, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
expect.error(paintutils.drawLine, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)") expect.error(paintutils.drawLine, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
end) end)
it("draws a line going across with custom colour", function()
local w = with_window(3, 2, function()
paintutils.drawLine(1, 1, 3, 1, colours.red)
end)
window_eq(w, {
{ " ", "000", "eee" },
{ " ", "000", "fff" },
})
end)
it("draws a line going diagonally with term colour", function()
local w = with_window(3, 3, function()
term.setBackgroundColour(colours.red)
paintutils.drawLine(1, 1, 3, 3)
end)
window_eq(w, {
{ " ", "000", "eff" },
{ " ", "000", "fef" },
{ " ", "000", "ffe" },
})
end)
end) end)
describe("paintutils.drawBox", function() describe("paintutils.drawBox", function()
@ -38,6 +77,45 @@ describe("The paintutils library", function()
expect.error(paintutils.drawBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)") expect.error(paintutils.drawBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
expect.error(paintutils.drawBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)") expect.error(paintutils.drawBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
end) end)
it("draws a box with term colour", function()
local w = with_window(3, 3, function()
term.setBackgroundColour(colours.red)
paintutils.drawBox(1, 1, 3, 3)
end)
window_eq(w, {
{ " ", "eee", "eee" },
{ " ", "e0e", "efe" },
{ " ", "eee", "eee" },
})
end)
it("draws a box with custom colour", function()
local w = with_window(3, 3, function()
paintutils.drawBox(1, 1, 3, 3, colours.red)
end)
window_eq(w, {
{ " ", "eee", "eee" },
{ " ", "e0e", "efe" },
{ " ", "eee", "eee" },
})
end)
it("draws a box without overwriting existing content", function()
local w = with_window(3, 3, function()
term.setCursorPos(2, 2)
term.write("a")
paintutils.drawBox(1, 1, 3, 3, colours.red)
end)
window_eq(w, {
{ " ", "eee", "eee" },
{ " a ", "e0e", "efe" },
{ " ", "eee", "eee" },
})
end)
end) end)
describe("paintutils.drawFilledBox", function() describe("paintutils.drawFilledBox", function()
@ -48,6 +126,31 @@ describe("The paintutils library", function()
expect.error(paintutils.drawFilledBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)") expect.error(paintutils.drawFilledBox, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
expect.error(paintutils.drawFilledBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)") expect.error(paintutils.drawFilledBox, 1, 1, 1, 1, false):eq("bad argument #5 (expected number, got boolean)")
end) end)
it("draws a filled box with term colour", function()
local w = with_window(3, 3, function()
term.setBackgroundColour(colours.red)
paintutils.drawFilledBox(1, 1, 3, 3)
end)
window_eq(w, {
{ " ", "eee", "eee" },
{ " ", "eee", "eee" },
{ " ", "eee", "eee" },
})
end)
it("draws a filled box with custom colour", function()
local w = with_window(3, 3, function()
paintutils.drawFilledBox(1, 1, 3, 3, colours.red)
end)
window_eq(w, {
{ " ", "eee", "eee" },
{ " ", "eee", "eee" },
{ " ", "eee", "eee" },
})
end)
end) end)
describe("paintutils.drawImage", function() describe("paintutils.drawImage", function()

View File

@ -126,12 +126,28 @@ describe("The textutils library", function()
end) end)
describe("parses using NBT-style syntax", function() describe("parses using NBT-style syntax", function()
local function exp(x)
local res, err = textutils.unserializeJSON(x, { nbt_style = true })
if not res then error(err, 2) end
return expect(res)
end
it("basic objects", function() it("basic objects", function()
expect(textutils.unserializeJSON([[{ a: 1, b:2 }]], { nbt_style = true })):same { a = 1, b = 2 } exp([[{ a: 1, b:2 }]]):same { a = 1, b = 2 }
end) end)
it("suffixed numbers", function() it("suffixed numbers", function()
expect(textutils.unserializeJSON("1b", { nbt_style = true })):eq(1) exp("1b"):eq(1)
exp("1.1d"):eq(1.1)
end)
it("strings", function()
exp("'123'"):eq("123")
exp("\"123\""):eq("123")
end)
it("typed arrays", function()
exp("[B; 1, 2, 3]"):same { 1, 2, 3 }
exp("[B;]"):same {}
end) end)
end) end)

View File

@ -157,4 +157,17 @@ describe("The window library", function()
expect({ w.getLine(1) }):same { "test ", "aaaa0", "4444f" } expect({ w.getLine(1) }):same { "test ", "aaaa0", "4444f" }
end) end)
end) end)
describe("Window.setVisible", function()
it("validates arguments", function()
local w = mk()
expect.error(w.setVisible, nil):eq("bad argument #1 (expected boolean, got nil)")
end)
end)
describe("Window.isVisible", function()
it("gets window visibility", function()
local w = mk()
w.setVisible(false)
expect(w.isVisible()):same(false)
end)
end)
end) end)

View File

@ -10,7 +10,7 @@ describe("The exec program", function()
it("displays its usage when given no argument", function() it("displays its usage when given no argument", function()
stub(_G, "commands", {}) stub(_G, "commands", {})
expect(capture(stub, "/rom/programs/command/exec.lua")) expect(capture(stub, "/rom/programs/command/exec.lua"))
:matches { ok = true, output = "", error = "Usage: exec <command>\n" } :matches { ok = true, output = "", error = "Usage: /rom/programs/command/exec.lua <command>\n" }
end) end)
it("runs a command", function() it("runs a command", function()

View File

@ -35,6 +35,6 @@ describe("The copy program", function()
it("displays the usage when given no arguments", function() it("displays the usage when given no arguments", function()
expect(capture(stub, "copy")) expect(capture(stub, "copy"))
:matches { ok = true, output = "Usage: cp <source> <destination>\n", error = "" } :matches { ok = true, output = "Usage: copy <source> <destination>\n", error = "" }
end) end)
end) end)

View File

@ -69,6 +69,6 @@ describe("The move program", function()
it("displays the usage with no arguments", function() it("displays the usage with no arguments", function()
expect(capture(stub, "move")) expect(capture(stub, "move"))
:matches { ok = true, output = "Usage: mv <source> <destination>\n", error = "" } :matches { ok = true, output = "Usage: move <source> <destination>\n", error = "" }
end) end)
end) end)

View File

@ -19,7 +19,7 @@ describe("The craft program", function()
stub(_G, "turtle", { craft = function() end }) stub(_G, "turtle", { craft = function() end })
expect(capture(stub, "/rom/programs/turtle/craft.lua")) expect(capture(stub, "/rom/programs/turtle/craft.lua"))
:matches { ok = true, output = "Usage: craft [number]\n", error = "" } :matches { ok = true, output = "Usage: /rom/programs/turtle/craft.lua [number]\n", error = "" }
end) end)
it("crafts multiple items", function() it("crafts multiple items", function()

View File

@ -13,7 +13,7 @@ describe("The turtle equip program", function()
stub(_G, "turtle", {}) stub(_G, "turtle", {})
expect(capture(stub, "/rom/programs/turtle/equip.lua")) expect(capture(stub, "/rom/programs/turtle/equip.lua"))
:matches { ok = true, output = "Usage: equip <slot> <side>\n", error = "" } :matches { ok = true, output = "Usage: /rom/programs/turtle/equip.lua <slot> <side>\n", error = "" }
end) end)
it("equip nothing", function() it("equip nothing", function()

View File

@ -32,7 +32,7 @@ describe("The refuel program", function()
it("displays its usage when given too many argument", function() it("displays its usage when given too many argument", function()
setup_turtle(0, 5, 0) setup_turtle(0, 5, 0)
expect(capture(stub, "/rom/programs/turtle/refuel.lua a b")) expect(capture(stub, "/rom/programs/turtle/refuel.lua a b"))
:matches { ok = true, output = "Usage: refuel [number]\n", error = "" } :matches { ok = true, output = "Usage: /rom/programs/turtle/refuel.lua [number]\n", error = "" }
end) end)
it("requires a numeric argument", function() it("requires a numeric argument", function()

View File

@ -13,7 +13,7 @@ describe("The turtle unequip program", function()
stub(_G, "turtle", {}) stub(_G, "turtle", {})
expect(capture(stub, "/rom/programs/turtle/unequip.lua")) expect(capture(stub, "/rom/programs/turtle/unequip.lua"))
:matches { ok = true, output = "Usage: unequip <side>\n", error = "" } :matches { ok = true, output = "Usage: /rom/programs/turtle/unequip.lua <side>\n", error = "" }
end) end)
it("says when nothing was unequipped", function() it("says when nothing was unequipped", function()