mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-13 11:40:29 +00:00
Merge branch 'master' into mc-1.13.x
This commit is contained in:
commit
c82d8a7c2a
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Report some misbehaviour in the mod
|
about: Report some misbehaviour in the mod
|
||||||
|
labels: bug
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea or improvement
|
about: Suggest an idea or improvement
|
||||||
|
labels: enhancement
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@ -11,4 +11,4 @@ about: Suggest an idea or improvement
|
|||||||
|
|
||||||
## Useful information to include:
|
## Useful information to include:
|
||||||
- Explanation of how the feature/change should work.
|
- Explanation of how the feature/change should work.
|
||||||
- Some rationale/use case for a feature. I'd like to keep CC:T as minimal as possible, so I like have a solid justification for each feature.
|
- Some rationale/use case for a feature. My general approach to designing new features is to ask yourself "what issue are we trying to solve" and _then_ "is this the best way to solve this issue?".
|
||||||
|
3
.github/pull_request_template.md
vendored
Normal file
3
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## A quick checklist
|
||||||
|
- If there's a existing issue, please link to it. If not, provide fill out the same information you would in a normal issue - reproduction steps for bugs, rationale for use-case.
|
||||||
|
- If you're working on CraftOS, try to write a few test cases so we can ensure everything continues to work in the future. Tests live in `src/test/resources/test-rom/spec` and can be run with `./gradlew check`.
|
15
README.md
15
README.md
@ -1,5 +1,5 @@
|
|||||||
# ![CC: Tweaked](logo.png)
|
# ![CC: Tweaked](logo.png)
|
||||||
[![Build Status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked)
|
[![Current build status](https://travis-ci.org/SquidDev-CC/CC-Tweaked.svg?branch=master)](https://travis-ci.org/SquidDev-CC/CC-Tweaked "Current build status") [![Download CC: Tweaked on CurseForge](https://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge")
|
||||||
|
|
||||||
CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers,
|
CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers,
|
||||||
turtles and more to Minecraft.
|
turtles and more to Minecraft.
|
||||||
@ -9,7 +9,7 @@ ComputerCraft has always held a fond place in my heart: it's the mod which reall
|
|||||||
mod which has kept me playing it for many years. However, development of the original mod has slowed, as the original
|
mod which has kept me playing it for many years. However, development of the original mod has slowed, as the original
|
||||||
developers have had less time to work on the mod, and moved onto other projects and commitments.
|
developers have had less time to work on the mod, and moved onto other projects and commitments.
|
||||||
|
|
||||||
CC:Tweaked (or CC:T for short) is an attempt to continue ComputerCraft's legacy. It's not intended to be a competitor
|
CC: Tweaked (or CC:T for short) is an attempt to continue ComputerCraft's legacy. It's not intended to be a competitor
|
||||||
to CC, nor do I want to take it in a vastly different direction to the original mod. Instead, CC:T focuses on making the
|
to CC, nor do I want to take it in a vastly different direction to the original mod. Instead, CC:T focuses on making the
|
||||||
ComputerCraft experience as _solid_ as possible, ironing out any wrinkles that may have developed over time.
|
ComputerCraft experience as _solid_ as possible, ironing out any wrinkles that may have developed over time.
|
||||||
|
|
||||||
@ -46,8 +46,17 @@ develop CC:T, you'll need to follow these steps:
|
|||||||
|
|
||||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
||||||
|
|
||||||
|
## Community
|
||||||
|
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
|
||||||
|
ComputerCraft we have a [forum](https://forums.computercraft.cc/) and [Discord guild](https://discord.gg/H2UyJXe)!
|
||||||
|
There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=#computercraft), if
|
||||||
|
that's more your cup of tea.
|
||||||
|
|
||||||
|
I'd generally recommend you don't contact me directly (email, DM, etc...) unless absolutely necessary (i.e. in order to
|
||||||
|
report exploits). You'll get a far quicker response if you ask the whole community!
|
||||||
|
|
||||||
## Using
|
## Using
|
||||||
If you want to depend on CC:Tweaked, we have a maven repo. However, you should be wary that some functionality is only
|
If you want to depend on CC: Tweaked, we have a maven repo. However, you should be wary that some functionality is only
|
||||||
exposed by CC:T's API and not vanilla ComputerCraft. If you wish to support all variations of ComputerCraft, I recommend
|
exposed by CC:T's API and not vanilla ComputerCraft. If you wish to support all variations of ComputerCraft, I recommend
|
||||||
using [cc.crzd.me's maven](https://cc.crzd.me/maven/) instead.
|
using [cc.crzd.me's maven](https://cc.crzd.me/maven/) instead.
|
||||||
|
|
||||||
|
60
build.gradle
60
build.gradle
@ -165,7 +165,7 @@ task proguard(type: ProGuardTask, dependsOn: jar) {
|
|||||||
dontobfuscate; dontoptimize; keepattributes; keepparameternames
|
dontobfuscate; dontoptimize; keepattributes; keepparameternames
|
||||||
|
|
||||||
// Proguard will remove directories by default, but that breaks JarMount.
|
// Proguard will remove directories by default, but that breaks JarMount.
|
||||||
keepdirectories 'assets/computercraft/lua**'
|
keepdirectories 'data/computercraft/lua**'
|
||||||
|
|
||||||
// Preserve ComputerCraft classes - we only want to strip shadowed files.
|
// Preserve ComputerCraft classes - we only want to strip shadowed files.
|
||||||
keep 'class dan200.computercraft.** { *; }'
|
keep 'class dan200.computercraft.** { *; }'
|
||||||
@ -269,6 +269,46 @@ task compressJson(dependsOn: jar) {
|
|||||||
|
|
||||||
assemble.dependsOn compressJson
|
assemble.dependsOn compressJson
|
||||||
|
|
||||||
|
task checkRelease {
|
||||||
|
group "upload"
|
||||||
|
description "Verifies that everything is ready for a release"
|
||||||
|
|
||||||
|
inputs.property "version", mod_version
|
||||||
|
inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.txt")
|
||||||
|
inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
def ok = true
|
||||||
|
|
||||||
|
// Check we're targetting the current version
|
||||||
|
def whatsnew = new File("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt").readLines()
|
||||||
|
if (whatsnew[0] != "New features in CC: Tweaked $mod_version") {
|
||||||
|
ok = false
|
||||||
|
project.logger.error("Expected `whatsnew.txt' to target $mod_version.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check "read more" exists and trim it
|
||||||
|
def idx = whatsnew.findIndexOf { it == 'Type "help changelog" to see the full version history.' }
|
||||||
|
if (idx == -1) {
|
||||||
|
ok = false
|
||||||
|
project.logger.error("Must mention the changelog in whatsnew.txt")
|
||||||
|
} else {
|
||||||
|
whatsnew = whatsnew.getAt(0 ..< idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whatsnew and changelog match.
|
||||||
|
def versionChangelog = "# " + whatsnew.join("\n")
|
||||||
|
def changelog = new File("src/main/resources/data/computercraft/lua/rom/help/changelog.txt").getText()
|
||||||
|
if (!changelog.startsWith(versionChangelog)) {
|
||||||
|
ok = false
|
||||||
|
project.logger.error("whatsnew and changelog are not in sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) throw new IllegalStateException("Could not check release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
curseforge {
|
curseforge {
|
||||||
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
|
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
|
||||||
project {
|
project {
|
||||||
@ -339,17 +379,23 @@ githubRelease {
|
|||||||
token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
|
token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
|
||||||
owner 'SquidDev-CC'
|
owner 'SquidDev-CC'
|
||||||
repo 'CC-Tweaked'
|
repo 'CC-Tweaked'
|
||||||
targetCommitish "mc-1.13.x" // TODO: Pull from GrGit
|
targetCommitish { Grgit.open(dir: '.').branch.current().name }
|
||||||
|
|
||||||
tagName "v${mc_version}-${mod_version}"
|
tagName "v${mc_version}-${mod_version}"
|
||||||
releaseName "[${mc_version}] ${mod_version}"
|
releaseName "[${mc_version}] ${mod_version}"
|
||||||
body ''
|
body {
|
||||||
prerelease true
|
"## " + new File("src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
|
||||||
|
.readLines()
|
||||||
releaseAssets.from(jar.archivePath)
|
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
|
||||||
|
.join("\n").trim()
|
||||||
|
}
|
||||||
|
prerelease false
|
||||||
}
|
}
|
||||||
|
|
||||||
task uploadAll(dependsOn: [uploadArchives, "curseforge", "githubRelease"]) {
|
def uploadTasks = ["uploadArchives", "curseforge", "githubRelease"]
|
||||||
|
uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease }
|
||||||
|
|
||||||
|
task uploadAll(dependsOn: uploadTasks) {
|
||||||
group "upload"
|
group "upload"
|
||||||
description "Uploads to all repositories (Maven, Curse, GitHub release)"
|
description "Uploads to all repositories (Maven, Curse, GitHub release)"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Mod properties
|
# Mod properties
|
||||||
mod_version=1.82.3
|
mod_version=1.83.1
|
||||||
|
|
||||||
# Minecraft properties
|
# Minecraft properties
|
||||||
mc_version=1.13.2
|
mc_version=1.13.2
|
||||||
|
@ -20,9 +20,10 @@ import net.minecraft.util.ResourceLocation;
|
|||||||
|
|
||||||
public class GuiComputer extends GuiContainer
|
public class GuiComputer extends GuiContainer
|
||||||
{
|
{
|
||||||
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( "computercraft", "textures/gui/corners_normal.png" );
|
public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_normal.png" );
|
||||||
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( "computercraft", "textures/gui/corners_advanced.png" );
|
public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" );
|
||||||
private static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( "computercraft", "textures/gui/corners_command.png" );
|
public static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_command.png" );
|
||||||
|
public static final ResourceLocation BACKGROUND_COLOUR = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_colour.png" );
|
||||||
|
|
||||||
private final ComputerFamily m_family;
|
private final ComputerFamily m_family;
|
||||||
private final ClientComputer m_computer;
|
private final ClientComputer m_computer;
|
||||||
@ -118,12 +119,12 @@ public class GuiComputer extends GuiContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawTexturedModalRect( startX - 12, startY - 12, 12, 28, 12, 12 );
|
drawTexturedModalRect( startX - 12, startY - 12, 12, 28, 12, 12 );
|
||||||
drawTexturedModalRect( startX - 12, endY, 12, 40, 12, 16 );
|
drawTexturedModalRect( startX - 12, endY, 12, 40, 12, 12 );
|
||||||
drawTexturedModalRect( endX, startY - 12, 24, 28, 12, 12 );
|
drawTexturedModalRect( endX, startY - 12, 24, 28, 12, 12 );
|
||||||
drawTexturedModalRect( endX, endY, 24, 40, 12, 16 );
|
drawTexturedModalRect( endX, endY, 24, 40, 12, 12 );
|
||||||
|
|
||||||
drawTexturedModalRect( startX, startY - 12, 0, 0, endX - startX, 12 );
|
drawTexturedModalRect( startX, startY - 12, 0, 0, endX - startX, 12 );
|
||||||
drawTexturedModalRect( startX, endY, 0, 12, endX - startX, 16 );
|
drawTexturedModalRect( startX, endY, 0, 12, endX - startX, 12 );
|
||||||
|
|
||||||
drawTexturedModalRect( startX - 12, startY, 0, 28, 12, endY - startY );
|
drawTexturedModalRect( startX - 12, startY, 0, 28, 12, endY - startY );
|
||||||
drawTexturedModalRect( endX, startY, 36, 28, 12, endY - startY );
|
drawTexturedModalRect( endX, startY, 36, 28, 12, endY - startY );
|
||||||
|
@ -26,9 +26,9 @@ import net.minecraftforge.fml.common.Mod;
|
|||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||||
public final class RenderOverlayCable
|
public final class CableHighlightRenderer
|
||||||
{
|
{
|
||||||
private RenderOverlayCable()
|
private CableHighlightRenderer()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -12,24 +12,24 @@ import dan200.computercraft.client.gui.FixedWidthFontRenderer;
|
|||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import dan200.computercraft.core.terminal.TextBuffer;
|
import dan200.computercraft.core.terminal.TextBuffer;
|
||||||
import dan200.computercraft.shared.computer.core.ClientComputer;
|
import dan200.computercraft.shared.computer.core.ClientComputer;
|
||||||
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||||
|
import dan200.computercraft.shared.util.Colour;
|
||||||
import dan200.computercraft.shared.util.Palette;
|
import dan200.computercraft.shared.util.Palette;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.BufferBuilder;
|
||||||
import net.minecraft.client.renderer.GlStateManager;
|
import net.minecraft.client.renderer.GlStateManager;
|
||||||
import net.minecraft.client.renderer.ItemRenderer;
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
import net.minecraft.client.renderer.model.IBakedModel;
|
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||||
import net.minecraft.client.renderer.texture.TextureManager;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureMap;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.client.ForgeHooksClient;
|
|
||||||
import net.minecraftforge.client.event.RenderSpecificHandEvent;
|
import net.minecraftforge.client.event.RenderSpecificHandEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT;
|
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*;
|
||||||
import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
import static dan200.computercraft.client.gui.GuiComputer.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates map rendering for pocket computers
|
* Emulates map rendering for pocket computers
|
||||||
@ -37,6 +37,10 @@ import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
|
|||||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||||
public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
||||||
{
|
{
|
||||||
|
private static final int MARGIN = 2;
|
||||||
|
private static final int FRAME = 12;
|
||||||
|
private static final int LIGHT_HEIGHT = 8;
|
||||||
|
|
||||||
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
|
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
|
||||||
|
|
||||||
private ItemPocketRenderer()
|
private ItemPocketRenderer()
|
||||||
@ -56,119 +60,195 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
|
|||||||
@Override
|
@Override
|
||||||
protected void renderItem( ItemStack stack )
|
protected void renderItem( ItemStack stack )
|
||||||
{
|
{
|
||||||
|
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
|
||||||
|
Terminal terminal = computer == null ? null : computer.getTerminal();
|
||||||
|
|
||||||
|
int termWidth, termHeight;
|
||||||
|
if( terminal == null )
|
||||||
|
{
|
||||||
|
termWidth = ComputerCraft.terminalWidth_pocketComputer;
|
||||||
|
termHeight = ComputerCraft.terminalHeight_pocketComputer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
termWidth = terminal.getWidth();
|
||||||
|
termHeight = terminal.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = termWidth * FONT_WIDTH + MARGIN * 2;
|
||||||
|
int height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
||||||
|
|
||||||
// Setup various transformations. Note that these are partially adapted from the corresponding method
|
// Setup various transformations. Note that these are partially adapted from the corresponding method
|
||||||
// in ItemRenderer
|
// in ItemRenderer
|
||||||
|
GlStateManager.pushMatrix();
|
||||||
|
|
||||||
GlStateManager.disableLighting();
|
GlStateManager.disableLighting();
|
||||||
|
GlStateManager.disableDepthTest();
|
||||||
|
|
||||||
GlStateManager.rotatef( 180f, 0f, 1f, 0f );
|
GlStateManager.rotatef( 180f, 0f, 1f, 0f );
|
||||||
GlStateManager.rotatef( 180f, 0f, 0f, 1f );
|
GlStateManager.rotatef( 180f, 0f, 0f, 1f );
|
||||||
GlStateManager.scalef( 0.5f, 0.5f, 0.5f );
|
GlStateManager.scalef( 0.5f, 0.5f, 0.5f );
|
||||||
|
|
||||||
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
|
double scale = 0.75 / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
|
||||||
|
GlStateManager.scaled( scale, scale, 0 );
|
||||||
|
GlStateManager.translated( -0.5 * width, -0.5 * height, 0 );
|
||||||
|
|
||||||
|
// Render the main frame
|
||||||
|
ItemPocketComputer item = (ItemPocketComputer) stack.getItem();
|
||||||
|
ComputerFamily family = item.getFamily();
|
||||||
|
int frameColour = item.getColour( stack );
|
||||||
|
renderFrame( family, frameColour, width, height );
|
||||||
|
|
||||||
|
// Render the light
|
||||||
|
int lightColour = ItemPocketComputer.getLightState( stack );
|
||||||
|
if( lightColour == -1 ) lightColour = Colour.Black.getHex();
|
||||||
|
renderLight( lightColour, width, height );
|
||||||
|
|
||||||
|
if( computer != null && terminal != null )
|
||||||
{
|
{
|
||||||
// First render the background item. We use the item's model rather than a direct texture as this ensures
|
// If we've a computer and terminal then attempt to render it.
|
||||||
// we display the pocket light and other such decorations.
|
renderTerminal( terminal, !computer.isColour(), width, height );
|
||||||
GlStateManager.pushMatrix();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise render a plain background
|
||||||
|
Minecraft.getInstance().getTextureManager().bindTexture( BACKGROUND );
|
||||||
|
|
||||||
GlStateManager.scalef( 1.0f, -1.0f, 1.0f );
|
Tessellator tessellator = Tessellator.getInstance();
|
||||||
|
BufferBuilder buffer = tessellator.getBuffer();
|
||||||
|
|
||||||
Minecraft minecraft = Minecraft.getInstance();
|
Colour black = Colour.Black;
|
||||||
TextureManager textureManager = minecraft.getTextureManager();
|
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION );
|
||||||
ItemRenderer renderItem = minecraft.getItemRenderer();
|
renderTexture( buffer, 0, 0, 0, 0, width, height, black.getR(), black.getG(), black.getB() );
|
||||||
|
tessellator.draw();
|
||||||
// Copy of RenderItem#renderItemModelIntoGUI but without the translation or scaling
|
}
|
||||||
textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
|
|
||||||
textureManager.getTexture( TextureMap.LOCATION_BLOCKS_TEXTURE ).setBlurMipmap( false, false );
|
|
||||||
|
|
||||||
GlStateManager.enableRescaleNormal();
|
|
||||||
GlStateManager.enableAlphaTest();
|
|
||||||
GlStateManager.alphaFunc( GL11.GL_GREATER, 0.1F );
|
|
||||||
GlStateManager.enableBlend();
|
|
||||||
GlStateManager.blendFunc( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA );
|
|
||||||
GlStateManager.color4f( 1.0F, 1.0F, 1.0F, 1.0F );
|
|
||||||
|
|
||||||
renderItem.renderItem( stack, transform( renderItem.getItemModelWithOverrides( stack, null, null ) ) );
|
|
||||||
|
|
||||||
GlStateManager.disableAlphaTest();
|
|
||||||
GlStateManager.disableRescaleNormal();
|
|
||||||
|
|
||||||
|
GlStateManager.enableDepthTest();
|
||||||
|
GlStateManager.enableLighting();
|
||||||
GlStateManager.popMatrix();
|
GlStateManager.popMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've a computer and terminal then attempt to render it.
|
private static void renderFrame( ComputerFamily family, int colour, int width, int height )
|
||||||
if( computer != null )
|
|
||||||
{
|
{
|
||||||
Terminal terminal = computer.getTerminal();
|
|
||||||
if( terminal != null )
|
Minecraft.getInstance().getTextureManager().bindTexture( colour != -1
|
||||||
|
? BACKGROUND_COLOUR
|
||||||
|
: family == ComputerFamily.Normal ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
|
||||||
|
);
|
||||||
|
|
||||||
|
float r = ((colour >>> 16) & 0xFF) / 255.0f;
|
||||||
|
float g = ((colour >>> 8) & 0xFF) / 255.0f;
|
||||||
|
float b = (colour & 0xFF) / 255.0f;
|
||||||
|
|
||||||
|
Tessellator tessellator = Tessellator.getInstance();
|
||||||
|
BufferBuilder buffer = tessellator.getBuffer();
|
||||||
|
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR );
|
||||||
|
|
||||||
|
// Top left, middle, right
|
||||||
|
renderTexture( buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
|
||||||
|
renderTexture( buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
|
||||||
|
renderTexture( buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
|
||||||
|
|
||||||
|
// Left and bright border
|
||||||
|
renderTexture( buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
|
||||||
|
renderTexture( buffer, width, 0, 36, 28, FRAME, height, r, g, b );
|
||||||
|
|
||||||
|
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
|
||||||
|
// lights, and then the bottom outer corners.
|
||||||
|
renderTexture( buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
|
||||||
|
renderTexture( buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
|
||||||
|
renderTexture( buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
|
||||||
|
|
||||||
|
renderTexture( buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
|
||||||
|
renderTexture( buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
|
||||||
|
renderTexture( buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
|
||||||
|
|
||||||
|
renderTexture( buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
|
||||||
|
renderTexture( buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
|
||||||
|
renderTexture( buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
|
||||||
|
|
||||||
|
tessellator.draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderLight( int colour, int width, int height )
|
||||||
|
{
|
||||||
|
GlStateManager.enableBlend();
|
||||||
|
GlStateManager.disableTexture2D();
|
||||||
|
|
||||||
|
float r = ((colour >>> 16) & 0xFF) / 255.0f;
|
||||||
|
float g = ((colour >>> 8) & 0xFF) / 255.0f;
|
||||||
|
float b = (colour & 0xFF) / 255.0f;
|
||||||
|
|
||||||
|
Tessellator tessellator = Tessellator.getInstance();
|
||||||
|
BufferBuilder buffer = tessellator.getBuffer();
|
||||||
|
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
|
||||||
|
buffer.pos( width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
|
||||||
|
buffer.pos( width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
|
||||||
|
buffer.pos( width, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
|
||||||
|
buffer.pos( width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
|
||||||
|
|
||||||
|
tessellator.draw();
|
||||||
|
GlStateManager.enableTexture2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderTerminal( Terminal terminal, boolean greyscale, int width, int height )
|
||||||
{
|
{
|
||||||
synchronized( terminal )
|
synchronized( terminal )
|
||||||
{
|
{
|
||||||
GlStateManager.pushMatrix();
|
int termWidth = terminal.getWidth();
|
||||||
GlStateManager.disableDepthTest();
|
int termHeight = terminal.getHeight();
|
||||||
|
|
||||||
// Reset the position to be at the top left corner of the pocket computer
|
|
||||||
// Note we translate towards the screen slightly too.
|
|
||||||
GlStateManager.translated( -8 / 16.0, -8 / 16.0, 0.5 / 16.0 );
|
|
||||||
// Translate to the top left of the screen.
|
|
||||||
GlStateManager.translated( 4 / 16.0, 3 / 16.0, 0 );
|
|
||||||
|
|
||||||
// Work out the scaling required to resize the terminal in order to fit on the computer
|
|
||||||
final int margin = 2;
|
|
||||||
int tw = terminal.getWidth();
|
|
||||||
int th = terminal.getHeight();
|
|
||||||
int width = tw * FONT_WIDTH + margin * 2;
|
|
||||||
int height = th * FONT_HEIGHT + margin * 2;
|
|
||||||
int max = Math.max( height, width );
|
|
||||||
|
|
||||||
// The grid is 8 * 8 wide, so we start with a base of 1/2 (8 / 16).
|
|
||||||
double scale = 1.0 / 2.0 / max;
|
|
||||||
GlStateManager.scaled( scale, scale, scale );
|
|
||||||
|
|
||||||
// The margin/start positions are determined in order for the terminal to be centred.
|
|
||||||
int startX = (max - width) / 2 + margin;
|
|
||||||
int startY = (max - height) / 2 + margin;
|
|
||||||
|
|
||||||
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
|
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
|
||||||
boolean greyscale = !computer.isColour();
|
|
||||||
Palette palette = terminal.getPalette();
|
Palette palette = terminal.getPalette();
|
||||||
|
|
||||||
|
// Render top/bottom borders
|
||||||
|
TextBuffer emptyLine = new TextBuffer( ' ', termWidth );
|
||||||
|
fontRenderer.drawString(
|
||||||
|
emptyLine, MARGIN, 0,
|
||||||
|
terminal.getTextColourLine( 0 ), terminal.getBackgroundColourLine( 0 ), MARGIN, MARGIN, greyscale, palette
|
||||||
|
);
|
||||||
|
fontRenderer.drawString(
|
||||||
|
emptyLine, MARGIN, 2 * MARGIN + (termHeight - 1) * FixedWidthFontRenderer.FONT_HEIGHT,
|
||||||
|
terminal.getTextColourLine( termHeight - 1 ), terminal.getBackgroundColourLine( termHeight - 1 ), MARGIN, MARGIN, greyscale, palette
|
||||||
|
);
|
||||||
|
|
||||||
// Render the actual text
|
// Render the actual text
|
||||||
for( int line = 0; line < th; line++ )
|
for( int line = 0; line < termWidth; line++ )
|
||||||
{
|
{
|
||||||
TextBuffer text = terminal.getLine( line );
|
TextBuffer text = terminal.getLine( line );
|
||||||
TextBuffer colour = terminal.getTextColourLine( line );
|
TextBuffer colour = terminal.getTextColourLine( line );
|
||||||
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
|
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
|
||||||
fontRenderer.drawString(
|
fontRenderer.drawString(
|
||||||
text, startX, startY + line * FONT_HEIGHT,
|
text, MARGIN, MARGIN + line * FONT_HEIGHT,
|
||||||
colour, backgroundColour, margin, margin, greyscale, palette
|
colour, backgroundColour, MARGIN, MARGIN, greyscale, palette
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// And render the cursor;
|
// And render the cursor;
|
||||||
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
|
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
|
||||||
if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
|
if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
|
||||||
tx >= 0 && ty >= 0 && tx < tw && ty < th )
|
tx >= 0 && ty >= 0 && tx < termWidth && ty < termHeight )
|
||||||
{
|
{
|
||||||
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
|
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
|
||||||
fontRenderer.drawString(
|
fontRenderer.drawString(
|
||||||
new TextBuffer( '_', 1 ), startX + FONT_WIDTH * tx, startY + FONT_HEIGHT * ty,
|
new TextBuffer( '_', 1 ), MARGIN + FONT_WIDTH * tx, MARGIN + FONT_HEIGHT * ty,
|
||||||
cursorColour, null, 0, 0, greyscale, palette
|
cursorColour, null, 0, 0, greyscale, palette
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlStateManager.enableDepthTest();
|
|
||||||
GlStateManager.popMatrix();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlStateManager.enableLighting();
|
private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings( { "deprecation" } )
|
|
||||||
private static IBakedModel transform( IBakedModel model )
|
|
||||||
{
|
{
|
||||||
return ForgeHooksClient.handleCameraTransforms( model, net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType.GUI, false );
|
renderTexture( builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b )
|
||||||
|
{
|
||||||
|
float scale = 1 / 255.0f;
|
||||||
|
builder.pos( x, y + height, 0 ).tex( textureX * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
|
||||||
|
builder.pos( x + width, y + height, 0 ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
|
||||||
|
builder.pos( x + width, y, 0 ).tex( (textureX + textureWidth) * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
|
||||||
|
builder.pos( x, y, 0 ).tex( textureX * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dan200.computercraft.client.render;
|
||||||
|
|
||||||
|
import dan200.computercraft.ComputerCraft;
|
||||||
|
import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.BufferBuilder;
|
||||||
|
import net.minecraft.client.renderer.GlStateManager;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
import net.minecraft.util.EnumFacing;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.RayTraceResult;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import static net.minecraft.util.EnumFacing.*;
|
||||||
|
|
||||||
|
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||||
|
public final class MonitorHighlightRenderer
|
||||||
|
{
|
||||||
|
private static final float EXPAND = 0.002f;
|
||||||
|
|
||||||
|
private MonitorHighlightRenderer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void drawHighlight( DrawBlockHighlightEvent event )
|
||||||
|
{
|
||||||
|
if( event.getTarget().type != RayTraceResult.Type.BLOCK || event.getPlayer().isSneaking() ) return;
|
||||||
|
|
||||||
|
World world = event.getPlayer().getEntityWorld();
|
||||||
|
BlockPos pos = event.getTarget().getBlockPos();
|
||||||
|
|
||||||
|
TileEntity tile = world.getTileEntity( pos );
|
||||||
|
if( !(tile instanceof TileMonitor) ) return;
|
||||||
|
|
||||||
|
TileMonitor monitor = (TileMonitor) tile;
|
||||||
|
event.setCanceled( true );
|
||||||
|
|
||||||
|
// Determine which sides are part of the external faces of the monitor, and so which need to be rendered.
|
||||||
|
EnumSet<EnumFacing> faces = EnumSet.allOf( EnumFacing.class );
|
||||||
|
EnumFacing front = monitor.getFront();
|
||||||
|
faces.remove( front );
|
||||||
|
if( monitor.getXIndex() != 0 ) faces.remove( monitor.getRight().getOpposite() );
|
||||||
|
if( monitor.getXIndex() != monitor.getWidth() - 1 ) faces.remove( monitor.getRight() );
|
||||||
|
if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() );
|
||||||
|
if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() );
|
||||||
|
|
||||||
|
GlStateManager.enableBlend();
|
||||||
|
GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
|
||||||
|
GlStateManager.lineWidth(Math.max(2.5F, (float) Minecraft.getInstance().mainWindow.getFramebufferWidth() / 1920.0F * 2.5F));
|
||||||
|
GlStateManager.disableTexture2D();
|
||||||
|
GlStateManager.depthMask( false );
|
||||||
|
GlStateManager.pushMatrix();
|
||||||
|
|
||||||
|
EntityPlayer player = event.getPlayer();
|
||||||
|
double x = player.lastTickPosX + (player.posX - player.lastTickPosX) * event.getPartialTicks();
|
||||||
|
double y = player.lastTickPosY + (player.posY - player.lastTickPosY) * event.getPartialTicks();
|
||||||
|
double z = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * event.getPartialTicks();
|
||||||
|
|
||||||
|
GlStateManager.translated( -x + pos.getX(), -y + pos.getY(), -z + pos.getZ() );
|
||||||
|
|
||||||
|
Tessellator tessellator = Tessellator.getInstance();
|
||||||
|
BufferBuilder buffer = tessellator.getBuffer();
|
||||||
|
buffer.begin( GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR );
|
||||||
|
|
||||||
|
// I wish I could think of a better way to do this
|
||||||
|
if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 0, UP );
|
||||||
|
if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, 0, 0, 1, UP );
|
||||||
|
if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 0, UP );
|
||||||
|
if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, 1, 0, 1, UP );
|
||||||
|
if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, EAST );
|
||||||
|
if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 1, EAST );
|
||||||
|
if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, EAST );
|
||||||
|
if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, 0, 1, 1, EAST );
|
||||||
|
if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, 0, 0, 0, SOUTH );
|
||||||
|
if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, 1, 0, 0, SOUTH );
|
||||||
|
if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, 0, 1, 0, SOUTH );
|
||||||
|
if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, 1, 1, 0, SOUTH );
|
||||||
|
|
||||||
|
tessellator.draw();
|
||||||
|
|
||||||
|
GlStateManager.popMatrix();
|
||||||
|
GlStateManager.depthMask( true );
|
||||||
|
GlStateManager.enableTexture2D();
|
||||||
|
GlStateManager.disableBlend();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void line( BufferBuilder buffer, int x, int y, int z, EnumFacing direction )
|
||||||
|
{
|
||||||
|
double minX = x == 0 ? -EXPAND : 1 + EXPAND;
|
||||||
|
double minY = y == 0 ? -EXPAND : 1 + EXPAND;
|
||||||
|
double minZ = z == 0 ? -EXPAND : 1 + EXPAND;
|
||||||
|
|
||||||
|
buffer.pos( minX, minY, minZ ).color( 0, 0, 0, 0.4f ).endVertex();
|
||||||
|
buffer.pos(
|
||||||
|
minX + direction.getXOffset() * (1 + EXPAND * 2),
|
||||||
|
minY + direction.getYOffset() * (1 + EXPAND * 2),
|
||||||
|
minZ + direction.getZOffset() * (1 + EXPAND * 2)
|
||||||
|
).color( 0, 0, 0, 0.4f ).endVertex();
|
||||||
|
}
|
||||||
|
}
|
281
src/main/java/dan200/computercraft/core/apis/LuaDateTime.java
Normal file
281
src/main/java/dan200/computercraft/core/apis/LuaDateTime.java
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dan200.computercraft.core.apis;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
|
import java.time.format.TextStyle;
|
||||||
|
import java.time.temporal.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.LongUnaryOperator;
|
||||||
|
|
||||||
|
final class LuaDateTime
|
||||||
|
{
|
||||||
|
private LuaDateTime()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void format( DateTimeFormatterBuilder formatter, String format, ZoneOffset offset ) throws LuaException
|
||||||
|
{
|
||||||
|
for( int i = 0; i < format.length(); )
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
switch( c = format.charAt( i++ ) )
|
||||||
|
{
|
||||||
|
case '\n':
|
||||||
|
formatter.appendLiteral( '\n' );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
formatter.appendLiteral( c );
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
if( i >= format.length() ) break;
|
||||||
|
switch( c = format.charAt( i++ ) )
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new LuaException( "bad argument #1: invalid conversion specifier '%" + c + "'" );
|
||||||
|
|
||||||
|
case '%':
|
||||||
|
formatter.appendLiteral( '%' );
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
formatter.appendText( ChronoField.DAY_OF_WEEK, TextStyle.SHORT );
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
formatter.appendText( ChronoField.DAY_OF_WEEK, TextStyle.FULL );
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
case 'h':
|
||||||
|
formatter.appendText( ChronoField.MONTH_OF_YEAR, TextStyle.SHORT );
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
formatter.appendText( ChronoField.MONTH_OF_YEAR, TextStyle.FULL );
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
format( formatter, "%a %b %e %H:%M:%S %Y", offset );
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
formatter.appendValueReduced( CENTURY, 2, 2, 0 );
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
formatter.appendValue( ChronoField.DAY_OF_MONTH, 2 );
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
case 'x':
|
||||||
|
format( formatter, "%m/%d/%y", offset );
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
formatter.padNext( 2 ).appendValue( ChronoField.DAY_OF_MONTH );
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
format( formatter, "%Y-%m-%d", offset );
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
formatter.appendValueReduced( IsoFields.WEEK_BASED_YEAR, 2, 2, 0 );
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
formatter.appendValue( IsoFields.WEEK_BASED_YEAR );
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
formatter.appendValue( ChronoField.HOUR_OF_DAY, 2 );
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
formatter.appendValue( ChronoField.HOUR_OF_AMPM );
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
formatter.appendValue( ChronoField.DAY_OF_YEAR, 3 );
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
formatter.appendValue( ChronoField.MONTH_OF_YEAR, 2 );
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
formatter.appendValue( ChronoField.MINUTE_OF_HOUR, 2 );
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
formatter.appendLiteral( '\n' );
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
formatter.appendText( ChronoField.AMPM_OF_DAY );
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
format( formatter, "%I:%M:%S %p", offset );
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
format( formatter, "%H:%M", offset );
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
formatter.appendValue( ChronoField.SECOND_OF_MINUTE, 2 );
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
formatter.appendLiteral( '\t' );
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
case 'X':
|
||||||
|
format( formatter, "%H:%M:%S", offset );
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
formatter.appendValue( ChronoField.DAY_OF_WEEK );
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
formatter.appendValue( ChronoField.ALIGNED_WEEK_OF_YEAR, 2 );
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
formatter.appendValue( IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2 );
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
formatter.appendValue( ZERO_WEEK );
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
formatter.appendValue( WeekFields.ISO.weekOfYear(), 2 );
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
formatter.appendValueReduced( ChronoField.YEAR, 2, 2, 0 );
|
||||||
|
break;
|
||||||
|
case 'Y':
|
||||||
|
formatter.appendValue( ChronoField.YEAR );
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
formatter.appendOffset( "+HHMM", "+0000" );
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
formatter.appendChronologyId();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static long fromTable( Map<?, ?> table ) throws LuaException
|
||||||
|
{
|
||||||
|
int year = getField( table, "year", -1 );
|
||||||
|
int month = getField( table, "month", -1 );
|
||||||
|
int day = getField( table, "day", -1 );
|
||||||
|
int hour = getField( table, "hour", 12 );
|
||||||
|
int minute = getField( table, "min", 12 );
|
||||||
|
int second = getField( table, "sec", 12 );
|
||||||
|
LocalDateTime time = LocalDateTime.of( year, month, day, hour, minute, second );
|
||||||
|
|
||||||
|
Boolean isDst = getBoolField( table, "isdst" );
|
||||||
|
if( isDst != null )
|
||||||
|
{
|
||||||
|
boolean requireDst = isDst;
|
||||||
|
for( ZoneOffset possibleOffset : ZoneOffset.systemDefault().getRules().getValidOffsets( time ) )
|
||||||
|
{
|
||||||
|
Instant instant = time.toInstant( possibleOffset );
|
||||||
|
if( possibleOffset.getRules().getDaylightSavings( instant ).isZero() == requireDst )
|
||||||
|
{
|
||||||
|
return instant.getEpochSecond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZoneOffset offset = ZoneOffset.systemDefault().getRules().getOffset( time );
|
||||||
|
return time.toInstant( offset ).getEpochSecond();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, ?> toTable( TemporalAccessor date, ZoneId offset, Instant instant )
|
||||||
|
{
|
||||||
|
HashMap<String, Object> table = new HashMap<>( 9 );
|
||||||
|
table.put( "year", date.getLong( ChronoField.YEAR ) );
|
||||||
|
table.put( "month", date.getLong( ChronoField.MONTH_OF_YEAR ) );
|
||||||
|
table.put( "day", date.getLong( ChronoField.DAY_OF_MONTH ) );
|
||||||
|
table.put( "hour", date.getLong( ChronoField.HOUR_OF_DAY ) );
|
||||||
|
table.put( "min", date.getLong( ChronoField.MINUTE_OF_HOUR ) );
|
||||||
|
table.put( "sec", date.getLong( ChronoField.SECOND_OF_MINUTE ) );
|
||||||
|
table.put( "wday", date.getLong( WeekFields.SUNDAY_START.dayOfWeek() ) );
|
||||||
|
table.put( "yday", date.getLong( ChronoField.DAY_OF_YEAR ) );
|
||||||
|
table.put( "isdst", offset.getRules().isDaylightSavings( instant ) );
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getField( Map<?, ?> table, String field, int def ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( field );
|
||||||
|
if( value instanceof Number ) return ((Number) value).intValue();
|
||||||
|
if( def < 0 ) throw new LuaException( "field \"" + field + "\" missing in date table" );
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Boolean getBoolField( Map<?, ?> table, String field ) throws LuaException
|
||||||
|
{
|
||||||
|
Object value = table.get( field );
|
||||||
|
if( value instanceof Boolean || value == null ) return (Boolean) value;
|
||||||
|
throw new LuaException( "field \"" + field + "\" missing in date table" );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final TemporalField CENTURY = map( ChronoField.YEAR, ValueRange.of( 0, 6 ), x -> (x / 100) % 100 );
|
||||||
|
private static final TemporalField ZERO_WEEK = map( WeekFields.SUNDAY_START.dayOfWeek(), ValueRange.of( 0, 6 ), x -> x - 1 );
|
||||||
|
|
||||||
|
private static TemporalField map( TemporalField field, ValueRange range, LongUnaryOperator convert )
|
||||||
|
{
|
||||||
|
return new TemporalField()
|
||||||
|
{
|
||||||
|
private final ValueRange range = ValueRange.of( 0, 99 );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TemporalUnit getBaseUnit()
|
||||||
|
{
|
||||||
|
return field.getBaseUnit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TemporalUnit getRangeUnit()
|
||||||
|
{
|
||||||
|
return field.getRangeUnit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueRange range()
|
||||||
|
{
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDateBased()
|
||||||
|
{
|
||||||
|
return field.isDateBased();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTimeBased()
|
||||||
|
{
|
||||||
|
return field.isTimeBased();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupportedBy( TemporalAccessor temporal )
|
||||||
|
{
|
||||||
|
return field.isSupportedBy( temporal );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValueRange rangeRefinedBy( TemporalAccessor temporal )
|
||||||
|
{
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getFrom( TemporalAccessor temporal )
|
||||||
|
{
|
||||||
|
return convert.applyAsLong( temporal.getLong( field ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public <R extends Temporal> R adjustInto( R temporal, long newValue )
|
||||||
|
{
|
||||||
|
return (R) temporal.with( field, newValue );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,11 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.shared.util.StringUtil;
|
import dan200.computercraft.shared.util.StringUtil;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
import static dan200.computercraft.core.apis.ArgumentHelper.*;
|
||||||
@ -184,11 +189,12 @@ public class OSAPI implements ILuaAPI
|
|||||||
"day",
|
"day",
|
||||||
"cancelTimer",
|
"cancelTimer",
|
||||||
"cancelAlarm",
|
"cancelAlarm",
|
||||||
"epoch"
|
"epoch",
|
||||||
|
"date",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private float getTimeForCalendar( Calendar c )
|
private static float getTimeForCalendar( Calendar c )
|
||||||
{
|
{
|
||||||
float time = c.get( Calendar.HOUR_OF_DAY );
|
float time = c.get( Calendar.HOUR_OF_DAY );
|
||||||
time += c.get( Calendar.MINUTE ) / 60.0f;
|
time += c.get( Calendar.MINUTE ) / 60.0f;
|
||||||
@ -196,7 +202,7 @@ public class OSAPI implements ILuaAPI
|
|||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getDayForCalendar( Calendar c )
|
private static int getDayForCalendar( Calendar c )
|
||||||
{
|
{
|
||||||
GregorianCalendar g = c instanceof GregorianCalendar ? (GregorianCalendar) c : new GregorianCalendar();
|
GregorianCalendar g = c instanceof GregorianCalendar ? (GregorianCalendar) c : new GregorianCalendar();
|
||||||
int year = c.get( Calendar.YEAR );
|
int year = c.get( Calendar.YEAR );
|
||||||
@ -209,7 +215,7 @@ public class OSAPI implements ILuaAPI
|
|||||||
return day;
|
return day;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getEpochForCalendar( Calendar c )
|
private static long getEpochForCalendar( Calendar c )
|
||||||
{
|
{
|
||||||
return c.getTime().getTime();
|
return c.getTime().getTime();
|
||||||
}
|
}
|
||||||
@ -282,6 +288,9 @@ public class OSAPI implements ILuaAPI
|
|||||||
case 11:
|
case 11:
|
||||||
{
|
{
|
||||||
// time
|
// time
|
||||||
|
Object value = args.length > 0 ? args[0] : null;
|
||||||
|
if( value instanceof Map ) return new Object[] { LuaDateTime.fromTable( (Map<?, ?>) value ) };
|
||||||
|
|
||||||
String param = optString( args, 0, "ingame" );
|
String param = optString( args, 0, "ingame" );
|
||||||
switch( param.toLowerCase( Locale.ROOT ) )
|
switch( param.toLowerCase( Locale.ROOT ) )
|
||||||
{
|
{
|
||||||
@ -355,9 +364,8 @@ public class OSAPI implements ILuaAPI
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
case 15:
|
case 15: // epoch
|
||||||
{
|
{
|
||||||
// epoch
|
|
||||||
String param = optString( args, 0, "ingame" );
|
String param = optString( args, 0, "ingame" );
|
||||||
switch( param.toLowerCase( Locale.ROOT ) )
|
switch( param.toLowerCase( Locale.ROOT ) )
|
||||||
{
|
{
|
||||||
@ -385,6 +393,34 @@ public class OSAPI implements ILuaAPI
|
|||||||
throw new LuaException( "Unsupported operation" );
|
throw new LuaException( "Unsupported operation" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 16: // date
|
||||||
|
{
|
||||||
|
String format = optString( args, 0, "%c" );
|
||||||
|
long time = optLong( args, 1, Instant.now().getEpochSecond() );
|
||||||
|
|
||||||
|
Instant instant = Instant.ofEpochSecond( time );
|
||||||
|
ZonedDateTime date;
|
||||||
|
ZoneOffset offset;
|
||||||
|
boolean isDst;
|
||||||
|
if( format.startsWith( "!" ) )
|
||||||
|
{
|
||||||
|
offset = ZoneOffset.UTC;
|
||||||
|
date = ZonedDateTime.ofInstant( instant, offset );
|
||||||
|
format = format.substring( 1 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ZoneId id = ZoneId.systemDefault();
|
||||||
|
offset = id.getRules().getOffset( instant );
|
||||||
|
date = ZonedDateTime.ofInstant( instant, id );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( format.equals( "*t" ) ) return new Object[] { LuaDateTime.toTable( date, offset, instant ) };
|
||||||
|
|
||||||
|
DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder();
|
||||||
|
LuaDateTime.format( formatter, format, offset );
|
||||||
|
return new Object[] { formatter.toFormatter( Locale.ROOT ).format( date ) };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import java.io.Closeable;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
|
import static dan200.computercraft.core.apis.ArgumentHelper.optBoolean;
|
||||||
|
import static dan200.computercraft.core.apis.http.websocket.Websocket.CLOSE_EVENT;
|
||||||
import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT;
|
import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT;
|
||||||
|
|
||||||
public class WebsocketHandle implements ILuaObject, Closeable
|
public class WebsocketHandle implements ILuaObject, Closeable
|
||||||
@ -53,15 +54,18 @@ public class WebsocketHandle implements ILuaObject, Closeable
|
|||||||
switch( method )
|
switch( method )
|
||||||
{
|
{
|
||||||
case 0: // receive
|
case 0: // receive
|
||||||
|
checkOpen();
|
||||||
while( true )
|
while( true )
|
||||||
{
|
{
|
||||||
checkOpen();
|
Object[] event = context.pullEvent( null );
|
||||||
|
if( event.length >= 3 && Objects.equal( event[0], MESSAGE_EVENT ) && Objects.equal( event[1], websocket.address() ) )
|
||||||
Object[] event = context.pullEvent( MESSAGE_EVENT );
|
|
||||||
if( event.length >= 3 && Objects.equal( event[1], websocket.address() ) )
|
|
||||||
{
|
{
|
||||||
return Arrays.copyOfRange( event, 2, event.length );
|
return Arrays.copyOfRange( event, 2, event.length );
|
||||||
}
|
}
|
||||||
|
else if( event.length >= 2 && Objects.equal( event[0], CLOSE_EVENT ) && Objects.equal( event[1], websocket.address() ) && closed )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 1: // send
|
case 1: // send
|
||||||
|
@ -133,7 +133,6 @@ public final class ComputerThread
|
|||||||
synchronized( threadLock )
|
synchronized( threadLock )
|
||||||
{
|
{
|
||||||
running = true;
|
running = true;
|
||||||
if( monitor == null || !monitor.isAlive() ) (monitor = monitorFactory.newThread( new Monitor() )).start();
|
|
||||||
|
|
||||||
if( runners == null )
|
if( runners == null )
|
||||||
{
|
{
|
||||||
@ -158,6 +157,8 @@ public final class ComputerThread
|
|||||||
runnerFactory.newThread( runners[i] = new TaskRunner() ).start();
|
runnerFactory.newThread( runners[i] = new TaskRunner() ).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( monitor == null || !monitor.isAlive() ) (monitor = monitorFactory.newThread( new Monitor() )).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,7 +369,16 @@ public final class ComputerThread
|
|||||||
{
|
{
|
||||||
TaskRunner runner = currentRunners[i];
|
TaskRunner runner = currentRunners[i];
|
||||||
// If we've no runner, skip.
|
// If we've no runner, skip.
|
||||||
if( runner == null ) continue;
|
if( runner == null || runner.owner == null || !runner.owner.isAlive() )
|
||||||
|
{
|
||||||
|
if( !running ) continue;
|
||||||
|
|
||||||
|
// Mark the old runner as dead and start a new one.
|
||||||
|
ComputerCraft.log.warn( "Previous runner ({}) has crashed, restarting!",
|
||||||
|
runner != null && runner.owner != null ? runner.owner.getName() : runner );
|
||||||
|
if( runner != null ) runner.running = false;
|
||||||
|
runnerFactory.newThread( runners[i] = new TaskRunner() ).start();
|
||||||
|
}
|
||||||
|
|
||||||
// If the runner has no work, skip
|
// If the runner has no work, skip
|
||||||
ComputerExecutor executor = runner.currentExecutor.get();
|
ComputerExecutor executor = runner.currentExecutor.get();
|
||||||
@ -492,7 +502,7 @@ public final class ComputerThread
|
|||||||
{
|
{
|
||||||
executor.work();
|
executor.work();
|
||||||
}
|
}
|
||||||
catch( Exception e )
|
catch( Exception | LinkageError | VirtualMachineError e )
|
||||||
{
|
{
|
||||||
ComputerCraft.log.error( "Error running task on computer #" + executor.getComputer().getID(), e );
|
ComputerCraft.log.error( "Error running task on computer #" + executor.getComputer().getID(), e );
|
||||||
}
|
}
|
||||||
|
@ -173,10 +173,14 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
|
|||||||
// Send terminal state to clients who are currently interacting with the computer.
|
// Send terminal state to clients who are currently interacting with the computer.
|
||||||
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||||
|
|
||||||
NetworkMessage packet = createTerminalPacket();
|
NetworkMessage packet = null;
|
||||||
for( EntityPlayer player : server.getPlayerList().getPlayers() )
|
for( EntityPlayer player : server.getPlayerList().getPlayers() )
|
||||||
{
|
{
|
||||||
if( isInteracting( player ) ) NetworkHandler.sendToPlayer( player, packet );
|
if( isInteracting( player ) )
|
||||||
|
{
|
||||||
|
if( packet == null ) packet = createTerminalPacket();
|
||||||
|
NetworkHandler.sendToPlayer( player, packet );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
|
|||||||
return getDirection().rotateYCCW();
|
return getDirection().rotateYCCW();
|
||||||
}
|
}
|
||||||
|
|
||||||
private EnumFacing getDown()
|
public EnumFacing getDown()
|
||||||
{
|
{
|
||||||
EnumFacing orientation = getOrientation();
|
EnumFacing orientation = getOrientation();
|
||||||
if( orientation == EnumFacing.NORTH ) return EnumFacing.UP;
|
if( orientation == EnumFacing.NORTH ) return EnumFacing.UP;
|
||||||
|
13
src/main/resources/assets/computercraft/lua/rom/motd.txt
Normal file
13
src/main/resources/assets/computercraft/lua/rom/motd.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
View the source code at https://github.com/SquidDev-CC/CC-Tweaked
|
||||||
|
View the documentation at https://wiki.computercraft.cc
|
||||||
|
Visit the forum at https://forums.computercraft.cc
|
||||||
|
You can disable these messages by running "set motd.enable false"
|
||||||
|
You can create directories with "mkdir".
|
||||||
|
Want to see hidden files? Run "set list.show_hidden true".
|
||||||
|
Run "list" or "ls" to see all files in a directory.
|
||||||
|
You can delete files and directories with "delete" or "rm".
|
||||||
|
Use "pastebin put" to upload a program to pastebin.
|
||||||
|
Use "pastebin get" to download a program from pastebin.
|
||||||
|
Use "pastebin run" to run a program from pastebin without saving it.
|
||||||
|
Use the "edit" program to create and edit your programs.
|
||||||
|
You can copy files with "copy" or "cp".
|
@ -0,0 +1,15 @@
|
|||||||
|
local tMotd = {}
|
||||||
|
|
||||||
|
for sPath in string.gmatch(settings.get( "motd.path" ), "[^:]+") do
|
||||||
|
if fs.exists(sPath) then
|
||||||
|
for sLine in io.lines(sPath) do
|
||||||
|
table.insert(tMotd,sLine)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #tMotd == 0 then
|
||||||
|
print("missingno")
|
||||||
|
else
|
||||||
|
print(tMotd[math.random(1,#tMotd)])
|
||||||
|
end
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@ -1,5 +1,48 @@
|
|||||||
|
local native_select, native_type = select, type
|
||||||
|
|
||||||
|
--- Expect an argument to have a specific type.
|
||||||
|
--
|
||||||
|
-- @tparam int index The 1-based argument index.
|
||||||
|
-- @param value The argument's value.
|
||||||
|
-- @tparam string ... The allowed types of the argument.
|
||||||
|
-- @throws If the value is not one of the allowed types.
|
||||||
|
local function expect(index, value, ...)
|
||||||
|
local t = native_type(value)
|
||||||
|
for i = 1, native_select("#", ...) do
|
||||||
|
if t == native_select(i, ...) then return true end
|
||||||
|
end
|
||||||
|
|
||||||
|
local types = table.pack(...)
|
||||||
|
for i = types.n, 1, -1 do
|
||||||
|
if types[i] == "nil" then table.remove(types, i) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local type_names
|
||||||
|
if #types <= 1 then
|
||||||
|
type_names = tostring(...)
|
||||||
|
else
|
||||||
|
type_names = table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If we can determine the function name with a high level of confidence, try to include it.
|
||||||
|
local name
|
||||||
|
if native_type(debug) == "table" and native_type(debug.getinfo) == "function" then
|
||||||
|
local ok, info = pcall(debug.getinfo, 3, "nS")
|
||||||
|
if ok and info.name and #info.name ~= "" and info.what ~= "C" then name = info.name end
|
||||||
|
end
|
||||||
|
|
||||||
|
if name then
|
||||||
|
error( ("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3 )
|
||||||
|
else
|
||||||
|
error( ("bad argument #%d (expected %s, got %s)"):format(index, type_names, t), 3 )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We expose expect in the global table as APIs need to access it, but give it
|
||||||
|
-- a non-identifier name - meaning it does not show up in auto-completion.
|
||||||
|
-- expect is an internal function, and should not be used by users.
|
||||||
|
_G["~expect"] = expect
|
||||||
|
|
||||||
local nativegetfenv = getfenv
|
|
||||||
if _VERSION == "Lua 5.1" then
|
if _VERSION == "Lua 5.1" then
|
||||||
-- If we're on Lua 5.1, install parts of the Lua 5.2/5.3 API so that programs can be written against it
|
-- If we're on Lua 5.1, install parts of the Lua 5.2/5.3 API so that programs can be written against it
|
||||||
local type = type
|
local type = type
|
||||||
@ -20,18 +63,11 @@ if _VERSION == "Lua 5.1" then
|
|||||||
end
|
end
|
||||||
|
|
||||||
function load( x, name, mode, env )
|
function load( x, name, mode, env )
|
||||||
if type( x ) ~= "string" and type( x ) ~= "function" then
|
expect(1, x, "function", "string")
|
||||||
error( "bad argument #1 (expected string or function, got " .. type( x ) .. ")", 2 )
|
expect(2, name, "string", "nil")
|
||||||
end
|
expect(3, mode, "string", "nil")
|
||||||
if name ~= nil and type( name ) ~= "string" then
|
expect(4, env, "table", "nil")
|
||||||
error( "bad argument #2 (expected string, got " .. type( name ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if mode ~= nil and type( mode ) ~= "string" then
|
|
||||||
error( "bad argument #3 (expected string, got " .. type( mode ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if env ~= nil and type( env) ~= "table" then
|
|
||||||
error( "bad argument #4 (expected table, got " .. type( env ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local ok, p1, p2 = pcall( function()
|
local ok, p1, p2 = pcall( function()
|
||||||
if type(x) == "string" then
|
if type(x) == "string" then
|
||||||
local result, err = nativeloadstring( x, name )
|
local result, err = nativeloadstring( x, name )
|
||||||
@ -76,10 +112,9 @@ if _VERSION == "Lua 5.1" then
|
|||||||
math.log10 = nil
|
math.log10 = nil
|
||||||
table.maxn = nil
|
table.maxn = nil
|
||||||
else
|
else
|
||||||
loadstring = function(string, chunkname) return nativeloadstring(string, prefix( chunkname ))
|
loadstring = function(string, chunkname) return nativeloadstring(string, prefix( chunkname )) end
|
||||||
|
|
||||||
-- Inject a stub for the old bit library
|
-- Inject a stub for the old bit library
|
||||||
end
|
|
||||||
_G.bit = {
|
_G.bit = {
|
||||||
bnot = bit32.bnot,
|
bnot = bit32.bnot,
|
||||||
band = bit32.band,
|
band = bit32.band,
|
||||||
@ -175,9 +210,7 @@ end
|
|||||||
|
|
||||||
-- Install globals
|
-- Install globals
|
||||||
function sleep( nTime )
|
function sleep( nTime )
|
||||||
if nTime ~= nil and type( nTime ) ~= "number" then
|
expect(1, nTime, "number", "nil")
|
||||||
error( "bad argument #1 (expected number, got " .. type( nTime ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local timer = os.startTimer( nTime or 0 )
|
local timer = os.startTimer( nTime or 0 )
|
||||||
repeat
|
repeat
|
||||||
local sEvent, param = os.pullEvent( "timer" )
|
local sEvent, param = os.pullEvent( "timer" )
|
||||||
@ -185,9 +218,7 @@ function sleep( nTime )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function write( sText )
|
function write( sText )
|
||||||
if type( sText ) ~= "string" and type( sText ) ~= "number" then
|
expect(1, sText, "string", "number")
|
||||||
error( "bad argument #1 (expected string or number, got " .. type( sText ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
|
|
||||||
local w,h = term.getSize()
|
local w,h = term.getSize()
|
||||||
local x,y = term.getCursorPos()
|
local x,y = term.getCursorPos()
|
||||||
@ -275,18 +306,11 @@ function printError( ... )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
||||||
if _sReplaceChar ~= nil and type( _sReplaceChar ) ~= "string" then
|
expect(1, _sReplaceChar, "string", "nil")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sReplaceChar ) .. ")", 2 )
|
expect(2, _tHistory, "table", "nil")
|
||||||
end
|
expect(3, _fnComplete, "function", "nil")
|
||||||
if _tHistory ~= nil and type( _tHistory ) ~= "table" then
|
expect(4, _sDefault, "string", "nil")
|
||||||
error( "bad argument #2 (expected table, got " .. type( _tHistory ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _fnComplete ~= nil and type( _fnComplete ) ~= "function" then
|
|
||||||
error( "bad argument #3 (expected function, got " .. type( _fnComplete ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _sDefault ~= nil and type( _sDefault ) ~= "string" then
|
|
||||||
error( "bad argument #4 (expected string, got " .. type( _sDefault ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
term.setCursorBlink( true )
|
term.setCursorBlink( true )
|
||||||
|
|
||||||
local sLine
|
local sLine
|
||||||
@ -544,13 +568,10 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault )
|
|||||||
return sLine
|
return sLine
|
||||||
end
|
end
|
||||||
|
|
||||||
loadfile = function( _sFile, _tEnv )
|
function loadfile( _sFile, _tEnv )
|
||||||
if type( _sFile ) ~= "string" then
|
expect(1, _sFile, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sFile ) .. ")", 2 )
|
expect(2, _tEnv, "table", "nil")
|
||||||
end
|
|
||||||
if _tEnv ~= nil and type( _tEnv ) ~= "table" then
|
|
||||||
error( "bad argument #2 (expected table, got " .. type( _tEnv ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local file = fs.open( _sFile, "r" )
|
local file = fs.open( _sFile, "r" )
|
||||||
if file then
|
if file then
|
||||||
local func, err = load( file.readAll(), "@" .. fs.getName( _sFile ), "t", _tEnv )
|
local func, err = load( file.readAll(), "@" .. fs.getName( _sFile ), "t", _tEnv )
|
||||||
@ -560,10 +581,9 @@ loadfile = function( _sFile, _tEnv )
|
|||||||
return nil, "File not found"
|
return nil, "File not found"
|
||||||
end
|
end
|
||||||
|
|
||||||
dofile = function( _sFile )
|
function dofile( _sFile )
|
||||||
if type( _sFile ) ~= "string" then
|
expect(1, _sFile, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sFile ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local fnFile, e = loadfile( _sFile, _G )
|
local fnFile, e = loadfile( _sFile, _G )
|
||||||
if fnFile then
|
if fnFile then
|
||||||
return fnFile()
|
return fnFile()
|
||||||
@ -574,12 +594,9 @@ end
|
|||||||
|
|
||||||
-- Install the rest of the OS api
|
-- Install the rest of the OS api
|
||||||
function os.run( _tEnv, _sPath, ... )
|
function os.run( _tEnv, _sPath, ... )
|
||||||
if type( _tEnv ) ~= "table" then
|
expect(1, _tEnv, "table")
|
||||||
error( "bad argument #1 (expected table, got " .. type( _tEnv ) .. ")", 2 )
|
expect(2, _sPath, "string")
|
||||||
end
|
|
||||||
if type( _sPath ) ~= "string" then
|
|
||||||
error( "bad argument #2 (expected string, got " .. type( _sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local tArgs = table.pack( ... )
|
local tArgs = table.pack( ... )
|
||||||
local tEnv = _tEnv
|
local tEnv = _tEnv
|
||||||
setmetatable( tEnv, { __index = _G } )
|
setmetatable( tEnv, { __index = _G } )
|
||||||
@ -604,9 +621,7 @@ end
|
|||||||
|
|
||||||
local tAPIsLoading = {}
|
local tAPIsLoading = {}
|
||||||
function os.loadAPI( _sPath )
|
function os.loadAPI( _sPath )
|
||||||
if type( _sPath ) ~= "string" then
|
expect(1, _sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local sName = fs.getName( _sPath )
|
local sName = fs.getName( _sPath )
|
||||||
if sName:sub(-4) == ".lua" then
|
if sName:sub(-4) == ".lua" then
|
||||||
sName = sName:sub(1,-5)
|
sName = sName:sub(1,-5)
|
||||||
@ -644,9 +659,7 @@ function os.loadAPI( _sPath )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function os.unloadAPI( _sName )
|
function os.unloadAPI( _sName )
|
||||||
if type( _sName ) ~= "string" then
|
expect(1, _sName, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sName ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _sName ~= "_G" and type(_G[_sName]) == "table" then
|
if _sName ~= "_G" and type(_G[_sName]) == "table" then
|
||||||
_G[_sName] = nil
|
_G[_sName] = nil
|
||||||
end
|
end
|
||||||
@ -692,9 +705,11 @@ if http then
|
|||||||
|
|
||||||
local function checkOptions( options, body )
|
local function checkOptions( options, body )
|
||||||
checkKey( options, "url", "string")
|
checkKey( options, "url", "string")
|
||||||
if body == false
|
if body == false then
|
||||||
then checkKey( options, "body", "nil" )
|
checkKey( options, "body", "nil" )
|
||||||
else checkKey( options, "body", "string", not body ) end
|
else
|
||||||
|
checkKey( options, "body", "string", not body )
|
||||||
|
end
|
||||||
checkKey( options, "headers", "table", true )
|
checkKey( options, "headers", "table", true )
|
||||||
checkKey( options, "method", "string", true )
|
checkKey( options, "method", "string", true )
|
||||||
checkKey( options, "redirect", "boolean", true )
|
checkKey( options, "redirect", "boolean", true )
|
||||||
@ -725,15 +740,9 @@ if http then
|
|||||||
return wrapRequest( _url.url, _url )
|
return wrapRequest( _url.url, _url )
|
||||||
end
|
end
|
||||||
|
|
||||||
if type( _url ) ~= "string" then
|
expect(1, _url, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
expect(2, _headers, "table", "nil")
|
||||||
end
|
expect(3, _binary, "boolean", "nil")
|
||||||
if _headers ~= nil and type( _headers ) ~= "table" then
|
|
||||||
error( "bad argument #2 (expected table, got " .. type( _headers ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
|
||||||
error( "bad argument #3 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
return wrapRequest( _url, _url, nil, _headers, _binary )
|
return wrapRequest( _url, _url, nil, _headers, _binary )
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -743,18 +752,10 @@ if http then
|
|||||||
return wrapRequest( _url.url, _url )
|
return wrapRequest( _url.url, _url )
|
||||||
end
|
end
|
||||||
|
|
||||||
if type( _url ) ~= "string" then
|
expect(1, _url, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
expect(2, _post, "string")
|
||||||
end
|
expect(3, _headers, "table", "nil")
|
||||||
if type( _post ) ~= "string" then
|
expect(4, _binary, "boolean", "nil")
|
||||||
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _headers ~= nil and type( _headers ) ~= "table" then
|
|
||||||
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
|
||||||
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
return wrapRequest( _url, _url, _post, _headers, _binary )
|
return wrapRequest( _url, _url, _post, _headers, _binary )
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -764,19 +765,10 @@ if http then
|
|||||||
checkOptions( _url )
|
checkOptions( _url )
|
||||||
url = _url.url
|
url = _url.url
|
||||||
else
|
else
|
||||||
if type( _url ) ~= "string" then
|
expect(1, _url, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
expect(2, _post, "string", "nil")
|
||||||
end
|
expect(3, _headers, "table", "nil")
|
||||||
if _post ~= nil and type( _post ) ~= "string" then
|
expect(4, _binary, "boolean", "nil")
|
||||||
error( "bad argument #2 (expected string, got " .. type( _post ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _headers ~= nil and type( _headers ) ~= "table" then
|
|
||||||
error( "bad argument #3 (expected table, got " .. type( _headers ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if _binary ~= nil and type( _binary ) ~= "boolean" then
|
|
||||||
error( "bad argument #4 (expected boolean, got " .. type( _binary ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
|
|
||||||
url = _url.url
|
url = _url.url
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -802,12 +794,9 @@ if http then
|
|||||||
local nativeWebsocket = http.websocket
|
local nativeWebsocket = http.websocket
|
||||||
http.websocketAsync = nativeWebsocket
|
http.websocketAsync = nativeWebsocket
|
||||||
http.websocket = function( _url, _headers )
|
http.websocket = function( _url, _headers )
|
||||||
if type( _url ) ~= "string" then
|
expect(1, _url, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _url ) .. ")", 2 )
|
expect(2, _headers, "table", "nil")
|
||||||
end
|
|
||||||
if _headers ~= nil and type( _headers ) ~= "table" then
|
|
||||||
error( "bad argument #2 (expected table, got " .. type( _headers ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local ok, err = nativeWebsocket( _url, _headers )
|
local ok, err = nativeWebsocket( _url, _headers )
|
||||||
if not ok then return ok, err end
|
if not ok then return ok, err end
|
||||||
|
|
||||||
@ -825,18 +814,11 @@ end
|
|||||||
-- Install the lua part of the FS api
|
-- Install the lua part of the FS api
|
||||||
local tEmpty = {}
|
local tEmpty = {}
|
||||||
function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs )
|
function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs )
|
||||||
if type( sPath ) ~= "string" then
|
expect(1, sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
|
expect(2, sLocation, "string")
|
||||||
end
|
expect(3, bIncludeFiles, "boolean", "nil")
|
||||||
if type( sLocation ) ~= "string" then
|
expect(4, bIncludeDirs, "boolean", "nil")
|
||||||
error( "bad argument #2 (expected string, got " .. type( sLocation ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if bIncludeFiles ~= nil and type( bIncludeFiles ) ~= "boolean" then
|
|
||||||
error( "bad argument #3 (expected boolean, got " .. type( bIncludeFiles ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if bIncludeDirs ~= nil and type( bIncludeDirs ) ~= "boolean" then
|
|
||||||
error( "bad argument #4 (expected boolean, got " .. type( bIncludeDirs ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
bIncludeFiles = (bIncludeFiles ~= false)
|
bIncludeFiles = (bIncludeFiles ~= false)
|
||||||
bIncludeDirs = (bIncludeDirs ~= false)
|
bIncludeDirs = (bIncludeDirs ~= false)
|
||||||
local sDir = sLocation
|
local sDir = sLocation
|
||||||
@ -982,6 +964,8 @@ settings.set( "edit.default_extension", "lua" )
|
|||||||
settings.set( "paint.default_extension", "nfp" )
|
settings.set( "paint.default_extension", "nfp" )
|
||||||
settings.set( "lua.autocomplete", true )
|
settings.set( "lua.autocomplete", true )
|
||||||
settings.set( "list.show_hidden", false )
|
settings.set( "list.show_hidden", false )
|
||||||
|
settings.set( "motd.enable", false )
|
||||||
|
settings.set( "motd.path", "/rom/motd.txt:/motd.txt" )
|
||||||
if term.isColour() then
|
if term.isColour() then
|
||||||
settings.set( "bios.use_multishell", true )
|
settings.set( "bios.use_multishell", true )
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
-- Colors
|
-- Colors
|
||||||
white = 1
|
white = 1
|
||||||
orange = 2
|
orange = 2
|
||||||
@ -18,49 +20,35 @@ black = 32768
|
|||||||
|
|
||||||
function combine( ... )
|
function combine( ... )
|
||||||
local r = 0
|
local r = 0
|
||||||
for n,c in ipairs( { ... } ) do
|
for i = 1, select('#', ...) do
|
||||||
if type( c ) ~= "number" then
|
local c = select(i, ...)
|
||||||
error( "bad argument #"..n.." (expected number, got " .. type( c ) .. ")", 2 )
|
expect(i, c, "number")
|
||||||
end
|
|
||||||
r = bit32.bor(r,c)
|
r = bit32.bor(r,c)
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
function subtract( colors, ... )
|
function subtract( colors, ... )
|
||||||
if type( colors ) ~= "number" then
|
expect(1, colors, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( colors ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local r = colors
|
local r = colors
|
||||||
for n,c in ipairs( { ... } ) do
|
for i = 1, select('#', ...) do
|
||||||
if type( c ) ~= "number" then
|
local c = select(i, ...)
|
||||||
error( "bad argument #"..tostring( n+1 ).." (expected number, got " .. type( c ) .. ")", 2 )
|
expect(i + 1, c, "number")
|
||||||
end
|
|
||||||
r = bit32.band(r, bit32.bnot(c))
|
r = bit32.band(r, bit32.bnot(c))
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
function test( colors, color )
|
function test( colors, color )
|
||||||
if type( colors ) ~= "number" then
|
expect(1, colors, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( colors ) .. ")", 2 )
|
expect(2, color, "number")
|
||||||
end
|
|
||||||
if type( color ) ~= "number" then
|
|
||||||
error( "bad argument #2 (expected number, got " .. type( color ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
return bit32.band(colors, color) == color
|
return bit32.band(colors, color) == color
|
||||||
end
|
end
|
||||||
|
|
||||||
function packRGB( r, g, b )
|
function packRGB( r, g, b )
|
||||||
if type( r ) ~= "number" then
|
expect(1, r, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( r ) .. ")", 2 )
|
expect(2, g, "number")
|
||||||
end
|
expect(3, b, "number")
|
||||||
if type( g ) ~= "number" then
|
|
||||||
error( "bad argument #2 (expected number, got " .. type( g ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if type( b ) ~= "number" then
|
|
||||||
error( "bad argument #3 (expected number, got " .. type( b ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
return
|
return
|
||||||
bit32.band( r * 255, 0xFF ) * 2^16 +
|
bit32.band( r * 255, 0xFF ) * 2^16 +
|
||||||
bit32.band( g * 255, 0xFF ) * 2^8 +
|
bit32.band( g * 255, 0xFF ) * 2^8 +
|
||||||
@ -68,9 +56,7 @@ function packRGB( r, g, b )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function unpackRGB( rgb )
|
function unpackRGB( rgb )
|
||||||
if type( rgb ) ~= "number" then
|
expect(1, rgb, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( rgb ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
return
|
return
|
||||||
bit32.band( bit32.rshift( rgb, 16 ), 0xFF ) / 255,
|
bit32.band( bit32.rshift( rgb, 16 ), 0xFF ) / 255,
|
||||||
bit32.band( bit32.rshift( rgb, 8 ), 0xFF ) / 255,
|
bit32.band( bit32.rshift( rgb, 8 ), 0xFF ) / 255,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
CHANNEL_GPS = 65534
|
CHANNEL_GPS = 65534
|
||||||
|
|
||||||
local function trilaterate( A, B, C )
|
local function trilaterate( A, B, C )
|
||||||
@ -55,12 +57,8 @@ local function narrow( p1, p2, fix )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function locate( _nTimeout, _bDebug )
|
function locate( _nTimeout, _bDebug )
|
||||||
if _nTimeout ~= nil and type( _nTimeout ) ~= "number" then
|
expect(1, _nTimeout, "number", "nil")
|
||||||
error( "bad argument #1 (expected number, got " .. type( _nTimeout ) .. ")", 2 )
|
expect(2, _bDebug, "boolean", "nil")
|
||||||
end
|
|
||||||
if _bDebug ~= nil and type( _bDebug ) ~= "boolean" then
|
|
||||||
error( "bad argument #2 (expected boolean, got " .. type( _bDebug) .. ")", 2 )
|
|
||||||
end
|
|
||||||
-- Let command computers use their magic fourth-wall-breaking special abilities
|
-- Let command computers use their magic fourth-wall-breaking special abilities
|
||||||
if commands then
|
if commands then
|
||||||
return commands.getBlockPosition()
|
return commands.getBlockPosition()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
local sPath = "/rom/help"
|
local sPath = "/rom/help"
|
||||||
|
|
||||||
@ -6,16 +7,12 @@ function path()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function setPath( _sPath )
|
function setPath( _sPath )
|
||||||
if type( _sPath ) ~= "string" then
|
expect(1, _sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
sPath = _sPath
|
sPath = _sPath
|
||||||
end
|
end
|
||||||
|
|
||||||
function lookup( _sTopic )
|
function lookup( _sTopic )
|
||||||
if type( _sTopic ) ~= "string" then
|
expect(1, _sTopic, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sTopic ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
-- Look on the path variable
|
-- Look on the path variable
|
||||||
for sPath in string.gmatch(sPath, "[^:]+") do
|
for sPath in string.gmatch(sPath, "[^:]+") do
|
||||||
sPath = fs.combine( sPath, _sTopic )
|
sPath = fs.combine( sPath, _sTopic )
|
||||||
@ -63,9 +60,7 @@ function topics()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function completeTopic( sText )
|
function completeTopic( sText )
|
||||||
if type( sText ) ~= "string" then
|
expect(1, sText, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sText ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local tTopics = topics()
|
local tTopics = topics()
|
||||||
local tResults = {}
|
local tResults = {}
|
||||||
for n=1,#tTopics do
|
for n=1,#tTopics do
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
-- Definition for the IO API
|
-- Definition for the IO API
|
||||||
local typeOf = _G.type
|
|
||||||
|
local expect, typeOf = _G["~expect"], _G.type
|
||||||
|
|
||||||
--- If we return nil then close the file, as we've reached the end.
|
--- If we return nil then close the file, as we've reached the end.
|
||||||
-- We use this weird wrapper function as we wish to preserve the varargs
|
-- We use this weird wrapper function as we wish to preserve the varargs
|
||||||
@ -65,7 +66,7 @@ handleMetatable = {
|
|||||||
local handle = self._handle
|
local handle = self._handle
|
||||||
if not handle.read and not handle.readLine then return nil, "Not opened for reading" end
|
if not handle.read and not handle.readLine then return nil, "Not opened for reading" end
|
||||||
|
|
||||||
local n = select('#', ...)
|
local n = select("#", ...)
|
||||||
local output = {}
|
local output = {}
|
||||||
for i = 1, n do
|
for i = 1, n do
|
||||||
local arg = select(i, ...)
|
local arg = select(i, ...)
|
||||||
@ -184,9 +185,7 @@ function input(_arg)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function lines(_sFileName)
|
function lines(_sFileName)
|
||||||
if _sFileName ~= nil and typeOf(_sFileName) ~= "string" then
|
expect(1, _sFileName, "string", "nil")
|
||||||
error("bad argument #1 (expected string, got " .. typeOf(_sFileName) .. ")", 2)
|
|
||||||
end
|
|
||||||
if _sFileName then
|
if _sFileName then
|
||||||
local ok, err = open(_sFileName, "rb")
|
local ok, err = open(_sFileName, "rb")
|
||||||
if not ok then error(err, 2) end
|
if not ok then error(err, 2) end
|
||||||
@ -201,12 +200,8 @@ function lines(_sFileName)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function open(_sPath, _sMode)
|
function open(_sPath, _sMode)
|
||||||
if typeOf(_sPath) ~= "string" then
|
expect(1, _sPath, "string")
|
||||||
error("bad argument #1 (expected string, got " .. typeOf(_sPath) .. ")", 2)
|
expect(2, _sMode, "string", "nil")
|
||||||
end
|
|
||||||
if _sMode ~= nil and typeOf(_sMode) ~= "string" then
|
|
||||||
error("bad argument #2 (expected string, got " .. typeOf(_sMode) .. ")", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sMode = _sMode and _sMode:gsub("%+", "") or "rb"
|
local sMode = _sMode and _sMode:gsub("%+", "") or "rb"
|
||||||
local file, err = fs.open(_sPath, sMode)
|
local file, err = fs.open(_sPath, sMode)
|
||||||
|
@ -136,11 +136,9 @@ end
|
|||||||
-- Alias some keys for ease-of-use and backwards compatibility
|
-- Alias some keys for ease-of-use and backwards compatibility
|
||||||
keys["return"] = keys.enter
|
keys["return"] = keys.enter
|
||||||
keys.scollLock = keys.scrollLock
|
keys.scollLock = keys.scrollLock
|
||||||
-- keys.cimcumflex = keys.circumflex
|
keys.cimcumflex = keys.circumflex
|
||||||
|
|
||||||
function getName( _nKey )
|
function getName( _nKey )
|
||||||
if type( _nKey ) ~= "number" then
|
expect(1, _nKey, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( _nKey ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
return tKeys[ _nKey ]
|
return tKeys[ _nKey ]
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
local function drawPixelInternal( xPos, yPos )
|
local function drawPixelInternal( xPos, yPos )
|
||||||
term.setCursorPos( xPos, yPos )
|
term.setCursorPos( xPos, yPos )
|
||||||
@ -18,9 +19,7 @@ local function parseLine( tImageArg, sLine )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function parseImage( sRawData )
|
function parseImage( sRawData )
|
||||||
if type( sRawData ) ~= "string" then
|
expect(1, sRawData, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sRawData ) .. ")" )
|
|
||||||
end
|
|
||||||
local tImage = {}
|
local tImage = {}
|
||||||
for sLine in ( sRawData .. "\n" ):gmatch( "(.-)\n" ) do -- read each line like original file handling did
|
for sLine in ( sRawData .. "\n" ):gmatch( "(.-)\n" ) do -- read each line like original file handling did
|
||||||
parseLine( tImage, sLine )
|
parseLine( tImage, sLine )
|
||||||
@ -29,9 +28,7 @@ function parseImage( sRawData )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function loadImage( sPath )
|
function loadImage( sPath )
|
||||||
if type( sPath ) ~= "string" then
|
expect(1, sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
|
|
||||||
if fs.exists( sPath ) then
|
if fs.exists( sPath ) then
|
||||||
local file = io.open( sPath, "r" )
|
local file = io.open( sPath, "r" )
|
||||||
@ -43,21 +40,21 @@ function loadImage( sPath )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function drawPixel( xPos, yPos, nColour )
|
function drawPixel( xPos, yPos, nColour )
|
||||||
if type( xPos ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( xPos ) .. ")", 2 ) end
|
expect(1, xPos, "number")
|
||||||
if type( yPos ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( yPos ) .. ")", 2 ) end
|
expect(2, yPos, "number")
|
||||||
if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( nColour ) .. ")", 2 ) end
|
expect(3, nColour, "number", "nil")
|
||||||
if nColour then
|
if nColour then
|
||||||
term.setBackgroundColor( nColour )
|
term.setBackgroundColor( nColour )
|
||||||
end
|
end
|
||||||
drawPixelInternal( xPos, yPos )
|
return drawPixelInternal( xPos, yPos )
|
||||||
end
|
end
|
||||||
|
|
||||||
function drawLine( startX, startY, endX, endY, nColour )
|
function drawLine( startX, startY, endX, endY, nColour )
|
||||||
if type( startX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( startX ) .. ")", 2 ) end
|
expect(1, startX, "number")
|
||||||
if type( startY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( startY ) .. ")", 2 ) end
|
expect(2, startY, "number")
|
||||||
if type( endX ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( endX ) .. ")", 2 ) end
|
expect(3, endX, "number")
|
||||||
if type( endY ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( endY ) .. ")", 2 ) end
|
expect(4, endY, "number")
|
||||||
if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nColour ) .. ")", 2 ) end
|
expect(5, nColour, "number", "nil")
|
||||||
|
|
||||||
startX = math.floor(startX)
|
startX = math.floor(startX)
|
||||||
startY = math.floor(startY)
|
startY = math.floor(startY)
|
||||||
@ -114,11 +111,11 @@ function drawLine( startX, startY, endX, endY, nColour )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function drawBox( startX, startY, endX, endY, nColour )
|
function drawBox( startX, startY, endX, endY, nColour )
|
||||||
if type( startX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( startX ) .. ")", 2 ) end
|
expect(1, startX, "number")
|
||||||
if type( startY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( startY ) .. ")", 2 ) end
|
expect(2, startY, "number")
|
||||||
if type( endX ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( endX ) .. ")", 2 ) end
|
expect(3, endX, "number")
|
||||||
if type( endY ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( endY ) .. ")", 2 ) end
|
expect(4, endY, "number")
|
||||||
if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nColour ) .. ")", 2 ) end
|
expect(5, nColour, "number", "nil")
|
||||||
|
|
||||||
startX = math.floor(startX)
|
startX = math.floor(startX)
|
||||||
startY = math.floor(startY)
|
startY = math.floor(startY)
|
||||||
@ -159,11 +156,11 @@ function drawBox( startX, startY, endX, endY, nColour )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function drawFilledBox( startX, startY, endX, endY, nColour )
|
function drawFilledBox( startX, startY, endX, endY, nColour )
|
||||||
if type( startX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( startX ) .. ")", 2 ) end
|
expect(1, startX, "number")
|
||||||
if type( startY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( startY ) .. ")", 2 ) end
|
expect(2, startY, "number")
|
||||||
if type( endX ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( endX ) .. ")", 2 ) end
|
expect(3, endX, "number")
|
||||||
if type( endY ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( endY ) .. ")", 2 ) end
|
expect(4, endY, "number")
|
||||||
if nColour ~= nil and type( nColour ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nColour ) .. ")", 2 ) end
|
expect(5, nColour, "number", "nil")
|
||||||
|
|
||||||
startX = math.floor(startX)
|
startX = math.floor(startX)
|
||||||
startY = math.floor(startY)
|
startY = math.floor(startY)
|
||||||
@ -198,9 +195,9 @@ function drawFilledBox( startX, startY, endX, endY, nColour )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function drawImage( tImage, xPos, yPos )
|
function drawImage( tImage, xPos, yPos )
|
||||||
if type( tImage ) ~= "table" then error( "bad argument #1 (expected table, got " .. type( tImage ) .. ")", 2 ) end
|
expect(1, tImage, "table")
|
||||||
if type( xPos ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( xPos ) .. ")", 2 ) end
|
expect(2, xPos, "number")
|
||||||
if type( yPos ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( yPos ) .. ")", 2 ) end
|
expect(3, yPos, "number")
|
||||||
for y=1,#tImage do
|
for y=1,#tImage do
|
||||||
local tLine = tImage[y]
|
local tLine = tImage[y]
|
||||||
for x=1,#tLine do
|
for x=1,#tLine do
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
local native = peripheral
|
local native = peripheral
|
||||||
|
|
||||||
function getNames()
|
function getNames()
|
||||||
@ -17,9 +19,7 @@ function getNames()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function isPresent( _sSide )
|
function isPresent( _sSide )
|
||||||
if type( _sSide ) ~= "string" then
|
expect(1, _sSide, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if native.isPresent( _sSide ) then
|
if native.isPresent( _sSide ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -34,9 +34,7 @@ function isPresent( _sSide )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function getType( _sSide )
|
function getType( _sSide )
|
||||||
if type( _sSide ) ~= "string" then
|
expect(1, _sSide, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if native.isPresent( _sSide ) then
|
if native.isPresent( _sSide ) then
|
||||||
return native.getType( _sSide )
|
return native.getType( _sSide )
|
||||||
end
|
end
|
||||||
@ -51,9 +49,7 @@ function getType( _sSide )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function getMethods( _sSide )
|
function getMethods( _sSide )
|
||||||
if type( _sSide ) ~= "string" then
|
expect(1, _sSide, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if native.isPresent( _sSide ) then
|
if native.isPresent( _sSide ) then
|
||||||
return native.getMethods( _sSide )
|
return native.getMethods( _sSide )
|
||||||
end
|
end
|
||||||
@ -68,12 +64,8 @@ function getMethods( _sSide )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function call( _sSide, _sMethod, ... )
|
function call( _sSide, _sMethod, ... )
|
||||||
if type( _sSide ) ~= "string" then
|
expect(1, _sSide, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
|
expect(2, _sMethod, "string")
|
||||||
end
|
|
||||||
if type( _sSide ) ~= "string" then
|
|
||||||
error( "bad argument #2 (expected string, got " .. type( _sMethod ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if native.isPresent( _sSide ) then
|
if native.isPresent( _sSide ) then
|
||||||
return native.call( _sSide, _sMethod, ... )
|
return native.call( _sSide, _sMethod, ... )
|
||||||
end
|
end
|
||||||
@ -88,9 +80,7 @@ function call( _sSide, _sMethod, ... )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function wrap( _sSide )
|
function wrap( _sSide )
|
||||||
if type( _sSide ) ~= "string" then
|
expect(1, _sSide, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sSide ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if peripheral.isPresent( _sSide ) then
|
if peripheral.isPresent( _sSide ) then
|
||||||
local tMethods = peripheral.getMethods( _sSide )
|
local tMethods = peripheral.getMethods( _sSide )
|
||||||
local tResult = {}
|
local tResult = {}
|
||||||
@ -105,12 +95,8 @@ function wrap( _sSide )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function find( sType, fnFilter )
|
function find( sType, fnFilter )
|
||||||
if type( sType ) ~= "string" then
|
expect(1, sType, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sType ) .. ")", 2 )
|
expect(2, fnFilter, "function", "nil")
|
||||||
end
|
|
||||||
if fnFilter ~= nil and type( fnFilter ) ~= "function" then
|
|
||||||
error( "bad argument #2 (expected function, got " .. type( fnFilter ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local tResults = {}
|
local tResults = {}
|
||||||
for n,sName in ipairs( peripheral.getNames() ) do
|
for n,sName in ipairs( peripheral.getNames() ) do
|
||||||
if peripheral.getType( sName ) == sType then
|
if peripheral.getType( sName ) == sType then
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
CHANNEL_BROADCAST = 65535
|
CHANNEL_BROADCAST = 65535
|
||||||
CHANNEL_REPEAT = 65533
|
CHANNEL_REPEAT = 65533
|
||||||
@ -7,9 +8,7 @@ local tReceivedMessageTimeouts = {}
|
|||||||
local tHostnames = {}
|
local tHostnames = {}
|
||||||
|
|
||||||
function open( sModem )
|
function open( sModem )
|
||||||
if type( sModem ) ~= "string" then
|
expect(1, sModem, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sModem ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if peripheral.getType( sModem ) ~= "modem" then
|
if peripheral.getType( sModem ) ~= "modem" then
|
||||||
error( "No such modem: "..sModem, 2 )
|
error( "No such modem: "..sModem, 2 )
|
||||||
end
|
end
|
||||||
@ -18,11 +17,9 @@ function open( sModem )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function close( sModem )
|
function close( sModem )
|
||||||
|
expect(1, sModem, "string", "nil")
|
||||||
if sModem then
|
if sModem then
|
||||||
-- Close a specific modem
|
-- Close a specific modem
|
||||||
if type( sModem ) ~= "string" then
|
|
||||||
error( "bad argument #1 (expected string, got " .. type( sModem ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if peripheral.getType( sModem ) ~= "modem" then
|
if peripheral.getType( sModem ) ~= "modem" then
|
||||||
error( "No such modem: "..sModem, 2 )
|
error( "No such modem: "..sModem, 2 )
|
||||||
end
|
end
|
||||||
@ -39,11 +36,9 @@ function close( sModem )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function isOpen( sModem )
|
function isOpen( sModem )
|
||||||
|
expect(1, sModem, "string", "nil")
|
||||||
if sModem then
|
if sModem then
|
||||||
-- Check if a specific modem is open
|
-- Check if a specific modem is open
|
||||||
if type( sModem ) ~= "string" then
|
|
||||||
error( "bad argument #1 (expected string, got " .. type( sModem ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if peripheral.getType( sModem ) == "modem" then
|
if peripheral.getType( sModem ) == "modem" then
|
||||||
return peripheral.call( sModem, "isOpen", os.getComputerID() ) and peripheral.call( sModem, "isOpen", CHANNEL_BROADCAST )
|
return peripheral.call( sModem, "isOpen", os.getComputerID() ) and peripheral.call( sModem, "isOpen", CHANNEL_BROADCAST )
|
||||||
end
|
end
|
||||||
@ -59,12 +54,8 @@ function isOpen( sModem )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function send( nRecipient, message, sProtocol )
|
function send( nRecipient, message, sProtocol )
|
||||||
if type( nRecipient ) ~= "number" then
|
expect(1, nRecipient, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( nRecipient ) .. ")", 2 )
|
expect(3, sProtocol, "string", "nil")
|
||||||
end
|
|
||||||
if sProtocol ~= nil and type( sProtocol ) ~= "string" then
|
|
||||||
error( "bad argument #3 (expected string, got " .. type( sProtocol ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
-- Generate a (probably) unique message ID
|
-- Generate a (probably) unique message ID
|
||||||
-- We could do other things to guarantee uniqueness, but we really don't need to
|
-- We could do other things to guarantee uniqueness, but we really don't need to
|
||||||
-- Store it to ensure we don't get our own messages back
|
-- Store it to ensure we don't get our own messages back
|
||||||
@ -101,9 +92,7 @@ function send( nRecipient, message, sProtocol )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function broadcast( message, sProtocol )
|
function broadcast( message, sProtocol )
|
||||||
if sProtocol ~= nil and type( sProtocol ) ~= "string" then
|
expect(2, sProtocol, "string", "nil")
|
||||||
error( "bad argument #2 (expected string, got " .. type( sProtocol ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
send( CHANNEL_BROADCAST, message, sProtocol )
|
send( CHANNEL_BROADCAST, message, sProtocol )
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -112,12 +101,8 @@ function receive( sProtocolFilter, nTimeout )
|
|||||||
if type(sProtocolFilter) == "number" and nTimeout == nil then
|
if type(sProtocolFilter) == "number" and nTimeout == nil then
|
||||||
sProtocolFilter, nTimeout = nil, sProtocolFilter
|
sProtocolFilter, nTimeout = nil, sProtocolFilter
|
||||||
end
|
end
|
||||||
if sProtocolFilter ~= nil and type( sProtocolFilter ) ~= "string" then
|
expect(1, sProtocolFilter, "string", "nil")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sProtocolFilter ) .. ")", 2 )
|
expect(2, nTimeout, "number", "nil")
|
||||||
end
|
|
||||||
if nTimeout ~= nil and type( nTimeout ) ~= "number" then
|
|
||||||
error( "bad argument #2 (expected number, got " .. type( nTimeout ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Start the timer
|
-- Start the timer
|
||||||
local timer = nil
|
local timer = nil
|
||||||
@ -148,12 +133,8 @@ function receive( sProtocolFilter, nTimeout )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function host( sProtocol, sHostname )
|
function host( sProtocol, sHostname )
|
||||||
if type( sProtocol ) ~= "string" then
|
expect(1, sProtocol, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sProtocol ) .. ")", 2 )
|
expect(2, sHostname, "string")
|
||||||
end
|
|
||||||
if type( sHostname ) ~= "string" then
|
|
||||||
error( "bad argument #2 (expected string, got " .. type( sHostname ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if sHostname == "localhost" then
|
if sHostname == "localhost" then
|
||||||
error( "Reserved hostname", 2 )
|
error( "Reserved hostname", 2 )
|
||||||
end
|
end
|
||||||
@ -166,19 +147,13 @@ function host( sProtocol, sHostname )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function unhost( sProtocol )
|
function unhost( sProtocol )
|
||||||
if type( sProtocol ) ~= "string" then
|
expect(1, sProtocol, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sProtocol ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
tHostnames[ sProtocol ] = nil
|
tHostnames[ sProtocol ] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function lookup( sProtocol, sHostname )
|
function lookup( sProtocol, sHostname )
|
||||||
if type( sProtocol ) ~= "string" then
|
expect(1, sProtocol, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sProtocol ) .. ")", 2 )
|
expect(2, sHostname, "string", "nil")
|
||||||
end
|
|
||||||
if sHostname ~= nil and type( sHostname ) ~= "string" then
|
|
||||||
error( "bad argument #2 (expected string, got " .. type( sHostname ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Build list of host IDs
|
-- Build list of host IDs
|
||||||
local tResults = nil
|
local tResults = nil
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
local tSettings = {}
|
local tSettings = {}
|
||||||
|
|
||||||
function set( sName, value )
|
function set( sName, value )
|
||||||
if type( sName ) ~= "string" then error( "bad argument #1 (expected string, got " .. type( sName ) .. ")", 2 ) end
|
expect(1, sName, "string")
|
||||||
|
expect(2, value, "number", "string", "boolean", "table")
|
||||||
|
|
||||||
local sValueTy = type(value)
|
if type(value) == "table" then
|
||||||
if sValueTy ~= "number" and sValueTy ~= "string" and sValueTy ~= "boolean" and sValueTy ~= "table" then
|
|
||||||
error( "bad argument #2 (expected value, got " .. sValueTy .. ")", 2 )
|
|
||||||
end
|
|
||||||
if sValueTy == "table" then
|
|
||||||
-- Ensure value is serializeable
|
-- Ensure value is serializeable
|
||||||
value = textutils.unserialize( textutils.serialize(value) )
|
value = textutils.unserialize( textutils.serialize(value) )
|
||||||
end
|
end
|
||||||
@ -29,9 +27,7 @@ function copy( value )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function get( sName, default )
|
function get( sName, default )
|
||||||
if type(sName) ~= "string" then
|
expect(1, sName, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sName ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local result = tSettings[ sName ]
|
local result = tSettings[ sName ]
|
||||||
if result ~= nil then
|
if result ~= nil then
|
||||||
return copy(result)
|
return copy(result)
|
||||||
@ -41,9 +37,7 @@ function get( sName, default )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function unset( sName )
|
function unset( sName )
|
||||||
if type(sName) ~= "string" then
|
expect(1, sName, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sName ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
tSettings[ sName ] = nil
|
tSettings[ sName ] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -61,9 +55,7 @@ function getNames()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function load( sPath )
|
function load( sPath )
|
||||||
if type(sPath) ~= "string" then
|
expect(1, sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local file = fs.open( sPath, "r" )
|
local file = fs.open( sPath, "r" )
|
||||||
if not file then
|
if not file then
|
||||||
return false
|
return false
|
||||||
@ -88,9 +80,7 @@ function load( sPath )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function save( sPath )
|
function save( sPath )
|
||||||
if type(sPath) ~= "string" then
|
expect(1, sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local file = fs.open( sPath, "w" )
|
local file = fs.open( sPath, "w" )
|
||||||
if not file then
|
if not file then
|
||||||
return false
|
return false
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
local native = (term.native and term.native()) or term
|
local native = (term.native and term.native()) or term
|
||||||
local redirectTarget = native
|
local redirectTarget = native
|
||||||
@ -11,10 +12,8 @@ end
|
|||||||
local term = {}
|
local term = {}
|
||||||
|
|
||||||
term.redirect = function( target )
|
term.redirect = function( target )
|
||||||
if type( target ) ~= "table" then
|
expect(1, target, "table")
|
||||||
error( "bad argument #1 (expected table, got " .. type( target ) .. ")", 2 )
|
if target == term or target == _G.term then
|
||||||
end
|
|
||||||
if target == term then
|
|
||||||
error( "term is not a recommended redirect target, try term.current() instead", 2 )
|
error( "term is not a recommended redirect target, try term.current() instead", 2 )
|
||||||
end
|
end
|
||||||
for k,v in pairs( native ) do
|
for k,v in pairs( native ) do
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
function slowWrite( sText, nRate )
|
function slowWrite( sText, nRate )
|
||||||
if nRate ~= nil and type( nRate ) ~= "number" then
|
expect(2, nRate, "number", "nil")
|
||||||
error( "bad argument #2 (expected number, got " .. type( nRate ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
nRate = nRate or 20
|
nRate = nRate or 20
|
||||||
if nRate < 0 then
|
if nRate < 0 then
|
||||||
error( "Rate must be positive", 2 )
|
error( "Rate must be positive", 2 )
|
||||||
@ -28,12 +27,8 @@ function slowPrint( sText, nRate )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function formatTime( nTime, bTwentyFourHour )
|
function formatTime( nTime, bTwentyFourHour )
|
||||||
if type( nTime ) ~= "number" then
|
expect(1, nTime, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( nTime ) .. ")", 2 )
|
expect(2, bTwentyFourHour, "boolean", "nil")
|
||||||
end
|
|
||||||
if bTwentyFourHour ~= nil and type( bTwentyFourHour ) ~= "boolean" then
|
|
||||||
error( "bad argument #2 (expected boolean, got " .. type( bTwentyFourHour ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local sTOD = nil
|
local sTOD = nil
|
||||||
if not bTwentyFourHour then
|
if not bTwentyFourHour then
|
||||||
if nTime >= 12 then
|
if nTime >= 12 then
|
||||||
@ -77,9 +72,7 @@ local function makePagedScroll( _term, _nFreeLines )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function pagedPrint( _sText, _nFreeLines )
|
function pagedPrint( _sText, _nFreeLines )
|
||||||
if _nFreeLines ~= nil and type( _nFreeLines ) ~= "number" then
|
expect(2, _nFreeLines, "number", "nil")
|
||||||
error( "bad argument #2 (expected number, got " .. type( _nFreeLines ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
-- Setup a redirector
|
-- Setup a redirector
|
||||||
local oldTerm = term.current()
|
local oldTerm = term.current()
|
||||||
local newTerm = {}
|
local newTerm = {}
|
||||||
@ -110,11 +103,9 @@ function pagedPrint( _sText, _nFreeLines )
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function tabulateCommon( bPaged, ... )
|
local function tabulateCommon( bPaged, ... )
|
||||||
local tAll = { ... }
|
local tAll = table.pack(...)
|
||||||
for k,v in ipairs( tAll ) do
|
for i = 1, tAll.n do
|
||||||
if type( v ) ~= "number" and type( v ) ~= "table" then
|
expect(i, tAll[i], "number", "table")
|
||||||
error( "bad argument #"..k.." (expected number or table, got " .. type( v ) .. ")", 3 )
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local w,h = term.getSize()
|
local w,h = term.getSize()
|
||||||
@ -169,11 +160,11 @@ local function tabulateCommon( bPaged, ... )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function tabulate( ... )
|
function tabulate( ... )
|
||||||
tabulateCommon( false, ... )
|
return tabulateCommon( false, ... )
|
||||||
end
|
end
|
||||||
|
|
||||||
function pagedTabulate( ... )
|
function pagedTabulate( ... )
|
||||||
tabulateCommon( true, ... )
|
return tabulateCommon( true, ... )
|
||||||
end
|
end
|
||||||
|
|
||||||
local g_tLuaKeywords = {
|
local g_tLuaKeywords = {
|
||||||
@ -247,7 +238,11 @@ local function serializeImpl( t, tTracking, sIndent )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
empty_json_array = {}
|
empty_json_array = setmetatable({}, {
|
||||||
|
__newindex = function()
|
||||||
|
error("attempt to mutate textutils.empty_json_array", 2)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
local function serializeJSONImpl( t, tTracking, bNBTStyle )
|
local function serializeJSONImpl( t, tTracking, bNBTStyle )
|
||||||
local sType = type(t)
|
local sType = type(t)
|
||||||
@ -321,9 +316,7 @@ function serialize( t )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function unserialize( s )
|
function unserialize( s )
|
||||||
if type( s ) ~= "string" then
|
expect(1, s, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( s ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local func = load( "return "..s, "unserialize", "t", {} )
|
local func = load( "return "..s, "unserialize", "t", {} )
|
||||||
if func then
|
if func then
|
||||||
local ok, result = pcall( func )
|
local ok, result = pcall( func )
|
||||||
@ -335,20 +328,14 @@ function unserialize( s )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function serializeJSON( t, bNBTStyle )
|
function serializeJSON( t, bNBTStyle )
|
||||||
if type( t ) ~= "table" and type( t ) ~= "string" and type( t ) ~= "number" and type( t ) ~= "boolean" then
|
expect(1, t, "table", "string", "number", "boolean")
|
||||||
error( "bad argument #1 (expected table/string/number/boolean, got " .. type( t ) .. ")", 2 )
|
expect(2, bNBTStyle, "boolean", "nil")
|
||||||
end
|
|
||||||
if bNBTStyle ~= nil and type( bNBTStyle ) ~= "boolean" then
|
|
||||||
error( "bad argument #2 (expected boolean, got " .. type( bNBTStyle ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local tTracking = {}
|
local tTracking = {}
|
||||||
return serializeJSONImpl( t, tTracking, bNBTStyle or false )
|
return serializeJSONImpl( t, tTracking, bNBTStyle or false )
|
||||||
end
|
end
|
||||||
|
|
||||||
function urlEncode( str )
|
function urlEncode( str )
|
||||||
if type( str ) ~= "string" then
|
expect(1, str, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( str ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if str then
|
if str then
|
||||||
str = string.gsub(str, "\n", "\r\n")
|
str = string.gsub(str, "\n", "\r\n")
|
||||||
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
|
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
|
||||||
@ -370,12 +357,8 @@ end
|
|||||||
|
|
||||||
local tEmpty = {}
|
local tEmpty = {}
|
||||||
function complete( sSearchText, tSearchTable )
|
function complete( sSearchText, tSearchTable )
|
||||||
if type( sSearchText ) ~= "string" then
|
expect(1, sSearchText, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sSearchText ) .. ")", 2 )
|
expect(2, tSearchTable, "table", "nil")
|
||||||
end
|
|
||||||
if tSearchTable ~= nil and type( tSearchTable ) ~= "table" then
|
|
||||||
error( "bad argument #2 (expected table, got " .. type( tSearchTable ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
|
|
||||||
if g_tLuaKeywords[sSearchText] then return tEmpty end
|
if g_tLuaKeywords[sSearchText] then return tEmpty end
|
||||||
local nStart = 1
|
local nStart = 1
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
local tHex = {
|
local tHex = {
|
||||||
[ colors.white ] = "0",
|
[ colors.white ] = "0",
|
||||||
@ -21,15 +22,14 @@ local tHex = {
|
|||||||
local type = type
|
local type = type
|
||||||
local string_rep = string.rep
|
local string_rep = string.rep
|
||||||
local string_sub = string.sub
|
local string_sub = string.sub
|
||||||
local table_unpack = table.unpack
|
|
||||||
|
|
||||||
function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
||||||
if type( parent ) ~= "table" then error( "bad argument #1 (expected table, got " .. type( parent ) .. ")", 2 ) end
|
expect(1, parent, "table")
|
||||||
if type( nX ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( nX ) .. ")", 2 ) end
|
expect(2, nX, "number")
|
||||||
if type( nY ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( nY ) .. ")", 2 ) end
|
expect(3, nY, "number")
|
||||||
if type( nWidth ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( nWidth ) .. ")", 2 ) end
|
expect(4, nWidth, "number")
|
||||||
if type( nHeight ) ~= "number" then error( "bad argument #5 (expected number, got " .. type( nHeight ) .. ")", 2 ) end
|
expect(5, nHeight, "number")
|
||||||
if bStartVisible ~= nil and type( bStartVisible ) ~= "boolean" then error( "bad argument #6 (expected boolean, got " .. type( bStartVisible ) .. ")", 2 ) end
|
expect(6, bStartVisible, "boolean", "nil")
|
||||||
|
|
||||||
if parent == term then
|
if parent == term then
|
||||||
error( "term is not a recommended window parent, try term.current() instead", 2 )
|
error( "term is not a recommended window parent, try term.current() instead", 2 )
|
||||||
@ -191,9 +191,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function window.blit( sText, sTextColor, sBackgroundColor )
|
function window.blit( sText, sTextColor, sBackgroundColor )
|
||||||
if type( sText ) ~= "string" then error( "bad argument #1 (expected string, got " .. type( sText ) .. ")", 2 ) end
|
if type(sText) ~= "string" then expect(1, sText, "string") end
|
||||||
if type( sTextColor ) ~= "string" then error( "bad argument #2 (expected string, got " .. type( sTextColor ) .. ")", 2 ) end
|
if type(sTextColor) ~= "string" then expect(2, sTextColor, "string") end
|
||||||
if type( sBackgroundColor ) ~= "string" then error( "bad argument #3 (expected string, got " .. type( sBackgroundColor ) .. ")", 2 ) end
|
if type(sBackgroundColor) ~= "string" then expect(3, sBackgroundColor, "string") end
|
||||||
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
|
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
|
||||||
error( "Arguments must be the same length", 2 )
|
error( "Arguments must be the same length", 2 )
|
||||||
end
|
end
|
||||||
@ -241,8 +241,8 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function window.setCursorPos( x, y )
|
function window.setCursorPos( x, y )
|
||||||
if type( x ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( x ) .. ")", 2 ) end
|
if type(x) ~= "number" then expect(1, x, "number") end
|
||||||
if type( y ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( y ) .. ")", 2 ) end
|
if type(y) ~= "number" then expect(2, y, "number") end
|
||||||
nCursorX = math.floor( x )
|
nCursorX = math.floor( x )
|
||||||
nCursorY = math.floor( y )
|
nCursorY = math.floor( y )
|
||||||
if bVisible then
|
if bVisible then
|
||||||
@ -251,7 +251,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function window.setCursorBlink( blink )
|
function window.setCursorBlink( blink )
|
||||||
if type( blink ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( blink ) .. ")", 2 ) end
|
if type(blink) ~= "boolean" then expect(1, blink, "boolean") end
|
||||||
bCursorBlink = blink
|
bCursorBlink = blink
|
||||||
if bVisible then
|
if bVisible then
|
||||||
updateCursorBlink()
|
updateCursorBlink()
|
||||||
@ -275,11 +275,11 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function setTextColor( color )
|
local function setTextColor( color )
|
||||||
if type( color ) ~= "number" then
|
if type(color) ~= "number" then expect(1, color, "number") end
|
||||||
error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 )
|
if tHex[color] == nil then
|
||||||
elseif tHex[color] == nil then
|
|
||||||
error( "Invalid color (got " .. color .. ")" , 2 )
|
error( "Invalid color (got " .. color .. ")" , 2 )
|
||||||
end
|
end
|
||||||
|
|
||||||
nTextColor = color
|
nTextColor = color
|
||||||
if bVisible then
|
if bVisible then
|
||||||
updateCursorColor()
|
updateCursorColor()
|
||||||
@ -290,7 +290,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
window.setTextColour = setTextColor
|
window.setTextColour = setTextColor
|
||||||
|
|
||||||
function window.setPaletteColour( colour, r, g, b )
|
function window.setPaletteColour( colour, r, g, b )
|
||||||
if type( colour ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( colour ) .. ")", 2 ) end
|
if type(colour) ~= "number" then expect(1, colour, "number") end
|
||||||
|
|
||||||
if tHex[colour] == nil then
|
if tHex[colour] == nil then
|
||||||
error( "Invalid color (got " .. colour .. ")" , 2 )
|
error( "Invalid color (got " .. colour .. ")" , 2 )
|
||||||
@ -301,9 +301,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
tCol = { colours.unpackRGB( r ) }
|
tCol = { colours.unpackRGB( r ) }
|
||||||
tPalette[ colour ] = tCol
|
tPalette[ colour ] = tCol
|
||||||
else
|
else
|
||||||
if type( r ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( r ) .. ")", 2 ) end
|
if type(r) ~= "number" then expect(2, r, "number") end
|
||||||
if type( g ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( g ) .. ")", 2 ) end
|
if type(g) ~= "number" then expect(3, g, "number") end
|
||||||
if type( b ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( b ) .. ")", 2 ) end
|
if type(b) ~= "number" then expect(4, b, "number") end
|
||||||
|
|
||||||
tCol = tPalette[ colour ]
|
tCol = tPalette[ colour ]
|
||||||
tCol[1] = r
|
tCol[1] = r
|
||||||
@ -319,7 +319,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
window.setPaletteColor = window.setPaletteColour
|
window.setPaletteColor = window.setPaletteColour
|
||||||
|
|
||||||
function window.getPaletteColour( colour )
|
function window.getPaletteColour( colour )
|
||||||
if type( colour ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( colour ) .. ")", 2 ) end
|
if type(colour) ~= "number" then expect(1, colour, "number") end
|
||||||
if tHex[colour] == nil then
|
if tHex[colour] == nil then
|
||||||
error( "Invalid color (got " .. colour .. ")" , 2 )
|
error( "Invalid color (got " .. colour .. ")" , 2 )
|
||||||
end
|
end
|
||||||
@ -330,9 +330,8 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
window.getPaletteColor = window.getPaletteColour
|
window.getPaletteColor = window.getPaletteColour
|
||||||
|
|
||||||
local function setBackgroundColor( color )
|
local function setBackgroundColor( color )
|
||||||
if type( color ) ~= "number" then
|
if type(color) ~= "number" then expect(1, color, "number") end
|
||||||
error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 )
|
if tHex[color] == nil then
|
||||||
elseif tHex[color] == nil then
|
|
||||||
error( "Invalid color (got " .. color .. ")", 2 )
|
error( "Invalid color (got " .. color .. ")", 2 )
|
||||||
end
|
end
|
||||||
nBackgroundColor = color
|
nBackgroundColor = color
|
||||||
@ -346,7 +345,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function window.scroll( n )
|
function window.scroll( n )
|
||||||
if type( n ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 ) end
|
if type(n) ~= "number" then expect(1, n, "number") end
|
||||||
if n ~= 0 then
|
if n ~= 0 then
|
||||||
local tNewLines = {}
|
local tNewLines = {}
|
||||||
local sEmptyText = sEmptySpaceLine
|
local sEmptyText = sEmptySpaceLine
|
||||||
@ -391,7 +390,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
|
|
||||||
-- Other functions
|
-- Other functions
|
||||||
function window.setVisible( bVis )
|
function window.setVisible( bVis )
|
||||||
if type( bVis ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( bVis ) .. ")", 2 ) end
|
if type(bVis) ~= "boolean" then expect(1, bVis, "boolean") end
|
||||||
if bVisible ~= bVis then
|
if bVisible ~= bVis then
|
||||||
bVisible = bVis
|
bVisible = bVis
|
||||||
if bVisible then
|
if bVisible then
|
||||||
@ -423,11 +422,11 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight )
|
function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight )
|
||||||
if type( nNewX ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( nNewX ) .. ")", 2 ) end
|
if type(nNewX) ~= "number" then expect(1, nNewX, "number") end
|
||||||
if type( nNewY ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( nNewY ) .. ")", 2 ) end
|
if type(nNewY) ~= "number" then expect(2, nNewY, "number") end
|
||||||
if nNewWidth ~= nil or nNewHeight ~= nil then
|
if nNewWidth ~= nil or nNewHeight ~= nil then
|
||||||
if type( nNewWidth ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( nNewWidth ) .. ")", 2 ) end
|
expect(3, nNewWidth, "number")
|
||||||
if type( nNewHeight ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( nNewHeight ) .. ")", 2 ) end
|
expect(4, nNewHeight, "number")
|
||||||
end
|
end
|
||||||
|
|
||||||
nX = nNewX
|
nX = nNewX
|
||||||
|
@ -1,80 +1,342 @@
|
|||||||
New Features in ComputerCraft 1.80:
|
# New features in CC: Tweaked 1.83.1
|
||||||
|
|
||||||
* Added .getResponseHeaders() to HTTP responses.
|
* Add several new MOTD messages (JakobDev)
|
||||||
* Return a HTTP response when a HTTP error occurs.
|
|
||||||
* Added a GUI to change ComputerCraft config options.
|
|
||||||
* os.time() and os.day() now accept parameters to give the real world time.
|
|
||||||
* Added os.epoch()
|
|
||||||
* Monitor text now glows in the dark.
|
|
||||||
* Added a "Pocket Computer upgrade API" so mod developers can add their own pocket upgrades.
|
|
||||||
* Added pocket.equipBack()/pocket.unequipBack() to add/remove pocket upgrades.
|
|
||||||
* Added term.setPaletteColor()/term.getPaletteColor() to change/check colors
|
|
||||||
* Added colors.rgb8()/colours.rgb8()
|
|
||||||
* Performance improvements to fs.find
|
|
||||||
* Requires the player to be interacting with the computer when typing
|
|
||||||
* Disk labels are limited to 32 characters
|
|
||||||
* Labels can now only include characters within the printable range ( to ~)
|
|
||||||
* Various model improvements
|
|
||||||
* There is now a configurable file descriptor limit
|
|
||||||
* Threads are now daemon threads
|
|
||||||
* Termination signals are now sent unless the computer is off
|
|
||||||
* Fixed compilation errors
|
|
||||||
* Now handles tile entity changes
|
|
||||||
* GPS coordinates now have to be numbers
|
|
||||||
* Turtle upgrades now act as tools and peripherals
|
|
||||||
* The Filesystem.list result is now sorted
|
|
||||||
* The number of values to unpack can now be manually specified
|
|
||||||
* Small terminal & monitor rendering improvements
|
|
||||||
* General improvements to the documentation
|
|
||||||
* Redstone inputs are no longer reset when adding peripherals
|
|
||||||
* Turtles now use tinting
|
|
||||||
* shell.resolveProgram now picks up on *.lua files
|
|
||||||
* Fixed a handful of bugs in ComputerCraft
|
|
||||||
* Added speaker block, turtle upgrade, pocket upgrade, and peripheral api
|
|
||||||
* Startup can now be a directory containing multiple startup files
|
|
||||||
* Added .getLabel to the computer peripheral
|
|
||||||
|
|
||||||
New Features in ComputerCraft 1.79:
|
And several bug fixes:
|
||||||
|
* Fix type check in `rednet.lookup`
|
||||||
|
* Error if turtle and pocket computer programs are run on the wrong system (JakobDev)
|
||||||
|
* Do not discard varargs after a nil.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.83.0
|
||||||
|
|
||||||
|
* Add Chinese translation (XuyuEre)
|
||||||
|
* Small performance optimisations for packet sending.
|
||||||
|
* Provide an `arg` table to programs fun from the shell, similar to PUC Lua.
|
||||||
|
* Add `os.date`, and handle passing datetime tables to `os.time`, making them largely compatible with PUC Lua.
|
||||||
|
* `rm` and `mkdir` accept multiple arguments (hydraz, JakobDev).
|
||||||
|
* Rework rendering of in-hand pocket computers.
|
||||||
|
* Prevent rendering of a bounding box on a monitor's screen.
|
||||||
|
* Refactor Lua-side type checking code into a single method. Also include the function name in error messages.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix incorrect computation of server-tick budget.
|
||||||
|
* Fix list-based config options not reloading.
|
||||||
|
* Ensure `require` is usable within the Lua REPL.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.82.3
|
||||||
|
|
||||||
|
* Make computers' redstone input handling consistent with repeaters. Redstone inputs parallel to the computer will now be picked up.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix `turtle.compare*()` crashing the server.
|
||||||
|
* Fix Cobalt leaking threads when coroutines blocked on Java methods are discarded.
|
||||||
|
* Fix `rawset` allowing nan keys
|
||||||
|
* Fix several out-of-bounds exceptions when handling malformed patterns.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.82.2
|
||||||
|
|
||||||
|
* Don't tie `turtle.refuel`/the `refuel` script's limits to item stack sizes
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix changes to Project:Red inputs not being detected.
|
||||||
|
* Convert non-modem peripherals to multiparts too, fixing crash with Proportional Destruction Particles
|
||||||
|
* Remove a couple of over-eager error messages
|
||||||
|
* Fix wired modems not correctly saving their attached peripherals
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.82.1
|
||||||
|
|
||||||
|
* Make redstone updates identical to vanilla behaviour
|
||||||
|
* Update German translation
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.82.0
|
||||||
|
|
||||||
|
* Warn when `pastebin put` potentially triggers spam protection (Lemmmy)
|
||||||
|
* Display HTTP errors on pastebin requests (Lemmmy)
|
||||||
|
* Attach peripherals on the main thread, rather than deferring to the computer thread.
|
||||||
|
* Computers may now be preemptively interrupted if they run for too long. This reduces the risk of malicious or badly written programs making other computers unusable.
|
||||||
|
* Reduce overhead of running with a higher number of computer threads.
|
||||||
|
* Set the initial multishell tab when starting the computer. This fixes the issue where you would not see any content until the first yield.
|
||||||
|
* Allow running `pastebin get|url` with the URL instead (e.g. `pastebin run https://pastebin.com/LYAxmSby`).
|
||||||
|
* Make `os.time`/`os.day` case insensitive.
|
||||||
|
* Add translations for several languages: Brazilian Portuguese (zardyh), Swedish (nothjarnan), Italian (Ale32bit), French(absolument), German (Wilma456), Spanish (daelvn)
|
||||||
|
* Improve JEI integration for turtle/pocket computer upgrades. You can now see recipes and usages of any upgrade or upgrade combination.
|
||||||
|
* Associate turtle/pocket computer upgrades with the mod which registered them. For instance, a "Sensing Turtle" will now be labelled as belonging to Plethora.
|
||||||
|
* Fire `key_up` and `mouse_up` events when closing the GUI.
|
||||||
|
* Allow limiting the amount of server time computers can consume.
|
||||||
|
* Add several new events for turtle refuelling and item inspection. Should allow for greater flexibility in add on mods in the future.
|
||||||
|
* `rednet.send` returns if the message was sent. Restores behaviour present before CC 1.6 (Luca0208)
|
||||||
|
* Add MCMP integration for wireless and ender modems.
|
||||||
|
* Make turtle crafting more consistent with vanilla behaviour.
|
||||||
|
* `commands.getBlockInfo(s)` now also includes NBT.
|
||||||
|
* Turtles will no longer reset their label when clicked with an unnamed name tag.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Update Cobalt (fixes `load` not unwind the stack)
|
||||||
|
* Fix `commands.collapseArgs` appending a trailing space.
|
||||||
|
* Fix leaking file descriptors when closing (see [this JVM bug!](https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8220477))
|
||||||
|
* Fix NPE on some invalid URLs
|
||||||
|
* Fix pocket computer API working outside of the player inventory
|
||||||
|
* Fix printing not updating the output display state.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.81.1
|
||||||
|
|
||||||
|
* Fix colour.*RGB using 8-bit values, rather than 0-1 floats.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.81.0
|
||||||
|
|
||||||
|
* Handle connection errors on websockets (Devilholk)
|
||||||
|
* Make `require` a little more consistent with PUC Lua, passing the required name to modules and improving error messages.
|
||||||
|
* Track how long each turtle action takes within the profiling tools
|
||||||
|
* Bump Cobalt version
|
||||||
|
* Coroutines are no longer backed by threads, reducing overhead of coroutines.
|
||||||
|
* Maximum stack depth is much larger (2^16 rather than 2^8)
|
||||||
|
* Stack is no longer unwound when an unhandled error occurs, meaning `debug.traceback` can be used on dead coroutines.
|
||||||
|
* Reduce jar size by reducing how many extra files we bundle.
|
||||||
|
* Add `term.nativePaletteColo(u)r` (Lignum)
|
||||||
|
* Split `colours.rgb8` into `colours.packRGB` and `colours.unpackRGB` (Lignum)
|
||||||
|
* Printers now only accept paper and ink, rather than any item
|
||||||
|
* Allow scrolling through the multishell tab bar, when lots of programs are running. (Wilma456)
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix modems not being advanced when they should be
|
||||||
|
* Fix direction of some peripheral blocks not being set
|
||||||
|
* Strip `\r` from `.readLine` on binary handles.
|
||||||
|
* Websockets handle pings correctly
|
||||||
|
* Fix turtle peripherals becoming desynced on chunk unload.
|
||||||
|
* `/computercraft` table are now truncated correctly.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.14
|
||||||
|
|
||||||
|
* Allow seeking within ROM files.
|
||||||
|
* Fix not being able to craft upgraded turtles or pocket computers when Astral Sorcery was installed.
|
||||||
|
* Make several tile entities (modems, cables, and monitors) non-ticking, substantially reducing their overhead,
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix cables not rendering the breaking steps
|
||||||
|
* Try to prevent `/computercraft_copy` showing up in auto-complete.
|
||||||
|
* Fix several memory leaks and other issues with ROM mounts.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.13
|
||||||
|
|
||||||
|
* `websocket_message` and `.receive` now return whether a message was binary or not.
|
||||||
|
* `websocket_close` events may contain a status code and reason the socket was closed.
|
||||||
|
* Enable the `debug` library by default.
|
||||||
|
* Clean up configuration files, moving various properties into sub-categories.
|
||||||
|
* Rewrite the HTTP API to use Netty.
|
||||||
|
* HTTP requests may now redirect from http to https if the server requests it.
|
||||||
|
* Add config options to limit various parts of the HTTP API:
|
||||||
|
* Restrict the number of active http requests and websockets.
|
||||||
|
* Limit the size of HTTP requests and responses.
|
||||||
|
* Introduce a configurable timeout
|
||||||
|
* `.getResponseCode` also returns the status text associated with that status code.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix being unable to create resource mounts from individual files.
|
||||||
|
* Sync computer state using TE data instead.
|
||||||
|
* Fix `.read` always consuming a multiple of 8192 bytes for binary handles.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.12
|
||||||
|
|
||||||
|
* Using longs inside `.seek` rather than 32 bit integers. This allows you to seek in very large files.
|
||||||
|
* Move the `/computer` command into the main `/computercraft` command
|
||||||
|
* Allow copying peripheral names from a wired modem's attach/detach chat message.
|
||||||
|
|
||||||
|
And several bug fixes
|
||||||
|
* Fix `InventoryUtil` ignoring the stack limit when extracting items
|
||||||
|
* Fix computers not receiving redstone inputs sent through another block.
|
||||||
|
* Fix JEI responding to key-presses when within a computer or turtle's inventory.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.11
|
||||||
|
|
||||||
|
* Rename all tile entities to have the correct `computercraft:` prefix.
|
||||||
|
* Fix files not being truncated when opened for a write.
|
||||||
|
* `.read*` methods no longer fail on malformed unicode. Malformed input is replaced with a fake character.
|
||||||
|
* Fix numerous issues with wireless modems being attached to wired ones.
|
||||||
|
* Prevent deadlocks within the wireless modem code.
|
||||||
|
* Create coroutines using a thread pool, rather than creating a new thread each time. Should make short-lived coroutines (such as iterators) much more performance friendly.
|
||||||
|
* Create all CC threads under appropriately named thread groups.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.10
|
||||||
|
|
||||||
|
This is just a minor bugfix release to solve some issues with the filesystem rewrite
|
||||||
|
* Fix computers not loading if resource packs are enabled
|
||||||
|
* Fix stdin not being recognised as a usable input
|
||||||
|
* Return an unsigned byte rather than a signed one for no-args `.read()`
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.9
|
||||||
|
|
||||||
|
* Add German translation (Vexatos)
|
||||||
|
* Add `.getCursorBlink` to monitors and terminals.
|
||||||
|
* Allow sending binary messages with websockets.
|
||||||
|
* Extend `fs` and `io` APIs
|
||||||
|
* `io` should now be largely compatible with PUC Lua's implementation (`:read("n")` is not currently supported).
|
||||||
|
* Binary readable file handles now support `.readLine`
|
||||||
|
* Binary file handles now support `.seek(whence: string[, position:number])`, taking the same arguments as PUC Lua's method.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix `repeat` program crashing when malformed rednet packets are received (gollark/osmarks)
|
||||||
|
* Reduce risk of deadlock when calling peripheral methods.
|
||||||
|
* Fix speakers being unable to play sounds.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.8
|
||||||
|
|
||||||
|
* Bump Cobalt version
|
||||||
|
* Default to using little endian in string.dump
|
||||||
|
* Remove propagation of debug hooks to child coroutines
|
||||||
|
* Allow passing functions to `debug.getlocal`, al-la Lua 5.2
|
||||||
|
* Add Charset support for bundled cables
|
||||||
|
* `/computercraft` commands are more generous in allowing computer selectors to fail.
|
||||||
|
* Remove bytecode loading disabling from bios.lua.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix stack overflow when using `turtle.place` with a full inventory
|
||||||
|
* Fix in-hand printout rendering causing visual glitches.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.7
|
||||||
|
|
||||||
|
* Add `.getNameLocal` to wired modems: provides the name that computer is exposed as on the network. This is mostly useful for working with Plethora's transfer locations, though could have other purposes.
|
||||||
|
* Change turtle block breaking to closer conform to how players break blocks.
|
||||||
|
* Rewrite rendering of printed pages, allowing them to be held in hand, and placed in item frames.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Improve formatting of `/computercraft` when run by a non-player.
|
||||||
|
* Fix pocket computer terminals not updating when being held.
|
||||||
|
* Fix a couple of minor blemishes in the GUI textures.
|
||||||
|
* Fix sign text not always being set when placed.
|
||||||
|
* Cache turtle fakeplayer, hopefully proving some minor performance improvements.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.6
|
||||||
|
|
||||||
|
* Allow network cables to work with compact machines.
|
||||||
|
* A large number of improvements to the `/computercraft` command, including:
|
||||||
|
* Ensure the tables are correctly aligned
|
||||||
|
* Remove the output of the previous invocation of that command when posting to chat.
|
||||||
|
* `/computercraft track` is now per-user, instead of global.
|
||||||
|
* We now track additional fields, such as the number of peripheral calls, http requests, etc... You can specify these as an optional argument to `/computercraft track dump` to see them.
|
||||||
|
* `wget` automatically determines the filename (Luca0208)
|
||||||
|
* Allow using alternative HTTP request methods (`DELETE`, `PUT`, etc...)
|
||||||
|
* Enable Gzip compression for websockets.
|
||||||
|
* Fix monitors not rendering when optifine shaders are enabled. There are still issues (they are tinted orange during the night), but it is an improvement.
|
||||||
|
|
||||||
|
And several bug fixes:
|
||||||
|
* Fix `.isDiskPresent()` always returning true.
|
||||||
|
* Fix peripherals showing up on wired networks when they shouldn't be.
|
||||||
|
* Fix `turtle.place()` crashing the server in some esoteric conditions.
|
||||||
|
* Remove upper bound on the number of characters than can be read with `.read(n: number)`.
|
||||||
|
* Fix various typos in `keys.lua` (hugeblank)
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.5
|
||||||
|
|
||||||
|
* Several additional fixes to monitors, solving several crashes and graphical glitches.
|
||||||
|
* Add recipes to upgrade computers, turtles and pocket computers.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.4
|
||||||
|
|
||||||
|
* Verify the action can be completed in `copy`, `rename` and `mkdir` commands.
|
||||||
|
* Add `/rom/modules` so the package path.
|
||||||
|
* Add `read` to normal file handles - allowing reading a given number of characters.
|
||||||
|
* Various minor bug fixes.
|
||||||
|
* Ensure ComputerCraft peripherals are thread-safe. This fixes multiple Lua errors and crashes with modems monitors.
|
||||||
|
* Add `/computercraft track` command, for monitoring how long computers execute for.
|
||||||
|
* Add ore dictionary support for recipes.
|
||||||
|
* Track which player owns a turtle. This allows turtles to play nicely with various claim/grief prevention systems.
|
||||||
|
* Add config option to disable various turtle actions.
|
||||||
|
* Add an API for extending wired networks.
|
||||||
|
* Add full-block wired modems.
|
||||||
|
* Several minor bug fixes.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.3
|
||||||
|
|
||||||
|
* Add `/computercraft` command, providing various diagnostic tools.
|
||||||
|
* Make `http.websocket` synchronous and add `http.websocketAsync`.
|
||||||
|
* Restore binary compatibility for `ILuaAPI`.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.2
|
||||||
|
|
||||||
|
* Fix `term.getTextScale()` not working across multiple monitors.
|
||||||
|
* Fix computer state not being synced to client when turning on/off.
|
||||||
|
* Provide an API for registering custom APIs.
|
||||||
|
* Render turtles called "Dinnerbone" or "Grumm" upside*down.
|
||||||
|
* Fix `getCollisionBoundingBox` not using all AABBs.
|
||||||
|
* **Experimental:** Add map-like rendering for pocket computers.
|
||||||
|
|
||||||
|
# New features in CC: Tweaked 1.80pr1.1
|
||||||
|
|
||||||
|
* Large numbers of bug fixes, stabilisation and hardening.
|
||||||
|
* Replace LuaJ with Cobalt.
|
||||||
|
* Allow running multiple computers at the same time.
|
||||||
|
* Add config option to enable Lua's debug API.
|
||||||
|
* Add websocket support to HTTP library.
|
||||||
|
* Add `/computer` command, allowing one to queue events on command computers.
|
||||||
|
* Fix JEI's handling of various ComputerCraft items.
|
||||||
|
* Make wired cables act more like multiparts.
|
||||||
|
* Add turtle and pocket recipes to recipe book.
|
||||||
|
* Flash pocket computer's light when playing a note.
|
||||||
|
|
||||||
|
# New Features in ComputerCraft 1.80pr1:
|
||||||
|
|
||||||
|
* Update to Minecraft 1.12.2
|
||||||
|
* Large number of bug fixes and stabilisation.
|
||||||
|
* Allow loading bios.lua files from resource packs.
|
||||||
|
* Fix texture artefacts when rendering monitors.
|
||||||
|
* Improve HTTP whitelist functionality and add an optional blacklist.
|
||||||
|
* Add support for completing Lua's self calls (`foo:bar()`).
|
||||||
|
* Add binary mode to HTTP.
|
||||||
|
* Use file extensions for ROM files.
|
||||||
|
* Automatically add `.lua` when editing files, and handle running them in the shell.
|
||||||
|
* Add require to the shell environment.
|
||||||
|
* Allow startup to be a directory.
|
||||||
|
* Add speaker peripheral and corresponding turtle and pocket upgrades.
|
||||||
|
* Add pocket computer upgrades.
|
||||||
|
* Allow turtles and pocket computers to be dyed any colour.
|
||||||
|
* Allow computer and monitors to configure their palette. Also allow normal computer/monitors to use any colour converting it to greyscale.
|
||||||
|
* Add extensible pocket computer upgrade system, including ender modem upgrade.
|
||||||
|
* Add config option to limit the number of open files on a computer.
|
||||||
|
* Monitors glow in the dark.
|
||||||
|
* http_failure event includes the HTTP handle if available.
|
||||||
|
* HTTP responses include the response headers.
|
||||||
|
|
||||||
|
# New Features in ComputerCraft 1.79:
|
||||||
|
|
||||||
* Ported ComputerCraftEdu to Minecraft 1.8.9
|
* Ported ComputerCraftEdu to Minecraft 1.8.9
|
||||||
* Fixed a handful of bugs in ComputerCraft
|
* Fixed a handful of bugs in ComputerCraft
|
||||||
|
|
||||||
New Features in ComputerCraft 1.77:
|
# New Features in ComputerCraft 1.77:
|
||||||
|
|
||||||
* Ported to Minecraft 1.8.9
|
* Ported to Minecraft 1.8.9
|
||||||
* Added "settings" API
|
* Added `settings` API
|
||||||
* Added "set" and "wget" programs
|
* Added `set` and `wget` programs
|
||||||
* Added settings to disable multishell, startup scripts, and tab completion on a per-computer basis. The default values for these settings can be customised in ComputerCraft.cfg
|
* Added settings to disable multishell, startup scripts, and tab completion on a per-computer basis. The default values for these settings can be customised in ComputerCraft.cfg
|
||||||
* All Computer and Turtle items except Command Computers can now be mounted in Disk Drives
|
* All Computer and Turtle items except Command Computers can now be mounted in Disk Drives
|
||||||
|
|
||||||
New Features in ComputerCraft 1.76:
|
# New Features in ComputerCraft 1.76:
|
||||||
|
|
||||||
* Ported to Minecraft 1.8
|
* Ported to Minecraft 1.8
|
||||||
* Added Ender Modems for cross-dimensional communication
|
* Added Ender Modems for cross-dimensional communication
|
||||||
* Fixed handling of 8-bit characters. All the characters in the ISO 8859-1 codepage can now be displayed
|
* Fixed handling of 8-bit characters. All the characters in the ISO 8859-1 codepage can now be displayed
|
||||||
* Added some extra graphical characters in the unused character positions, including a suite of characters for Teletext style drawing
|
* Added some extra graphical characters in the unused character positions, including a suite of characters for Teletext style drawing
|
||||||
* Added support for the new commands in Minecraft 1.8 to the Command Computer
|
* Added support for the new commands in Minecraft 1.8 to the Command Computer
|
||||||
* The return values of turtle.inspect() and commands.getBlockInfo() now include blockstate information
|
* The return values of `turtle.inspect()` and `commands.getBlockInfo()` now include blockstate information
|
||||||
* Added commands.getBlockInfos() function for Command Computers
|
* Added `commands.getBlockInfos()` function for Command Computers
|
||||||
* Added new "peripherals" program
|
* Added new `peripherals` program
|
||||||
* Replaced the "_CC_VERSION" and "_MC_VERSION" constants with a new "_HOST" constant
|
* Replaced the `_CC_VERSION` and `_MC_VERSION` constants with a new `_HOST` constant
|
||||||
* Shortened the length of time that "Ctrl+T", "Ctrl+S" and "Ctrl+R" must be held down for to terminate, shutdown and reboot the computer
|
* Shortened the length of time that "Ctrl+T", "Ctrl+S" and "Ctrl+R" must be held down for to terminate, shutdown and reboot the computer
|
||||||
* textutils.serialiseJSON() now takes an optional parameter allowing it to produce JSON text with unquoted object keys. This is used by all autogenerated methods in the "commands" api except for "title" and "tellraw"
|
* `textutils.serialiseJSON()` now takes an optional parameter allowing it to produce JSON text with unquoted object keys. This is used by all autogenerated methods in the `commands` api except for "title" and "tellraw"
|
||||||
* Fixed many bugs
|
* Fixed many bugs
|
||||||
|
|
||||||
New Features in ComputerCraft 1.75:
|
# New Features in ComputerCraft 1.75:
|
||||||
|
|
||||||
* Fixed monitors sometimes rendering without part of their text.
|
* Fixed monitors sometimes rendering without part of their text.
|
||||||
* Fixed a regression in the "bit" API.
|
* Fixed a regression in the `bit` API.
|
||||||
|
|
||||||
New Features in ComputerCraft 1.74:
|
# New Features in ComputerCraft 1.74:
|
||||||
|
|
||||||
* Added tab completion to "edit", "lua" and the shell.
|
* Added tab completion to `edit`, `lua` and the shell.
|
||||||
* Added textutils.complete(), fs.complete(), shell.complete(), shell.setCompletionFunction() and help.complete().
|
* Added `textutils.complete()`, `fs.complete()`, `shell.complete()`, `shell.setCompletionFunction()` and `help.complete()`.
|
||||||
* Added tab completion options to read().
|
* Added tab completion options to `read()`.
|
||||||
* Added "key_up" and "mouse_up" events.
|
* Added `key_up` and `mouse_up` events.
|
||||||
* Non-advanced terminals now accept both grey colours.
|
* Non-advanced terminals now accept both grey colours.
|
||||||
* Added term.getTextColour(), term.getBackgroundColour() and term.blit().
|
* Added `term.getTextColour()`, `term.getBackgroundColour()` and `term.blit()`.
|
||||||
* Improved the performance of text rendering on Advanced Computers.
|
* Improved the performance of text rendering on Advanced Computers.
|
||||||
* Added a "Run" button to the edit program on Advanced Computers.
|
* Added a "Run" button to the edit program on Advanced Computers.
|
||||||
* Turtles can now push players and entities (configurable).
|
* Turtles can now push players and entities (configurable).
|
||||||
@ -84,54 +346,54 @@ New Features in ComputerCraft 1.74:
|
|||||||
* Added a config option to disable parts of the Lua 5.1 API which will be removed when a future Lua version upgrade happens.
|
* Added a config option to disable parts of the Lua 5.1 API which will be removed when a future Lua version upgrade happens.
|
||||||
* Command Computers can no longer be broken by survival players.
|
* Command Computers can no longer be broken by survival players.
|
||||||
* Fixed the "pick block" key not working on ComputerCraft items in creative mode.
|
* Fixed the "pick block" key not working on ComputerCraft items in creative mode.
|
||||||
* Fixed the "edit" program being hard to use on certain European keyboards.
|
* Fixed the `edit` program being hard to use on certain European keyboards.
|
||||||
* Added "_CC_VERSION" and "_MC_VERSION" constants.
|
* Added `_CC_VERSION` and `_MC_VERSION` constants.
|
||||||
|
|
||||||
New Features in ComputerCraft 1.73:
|
# New Features in ComputerCraft 1.73:
|
||||||
|
|
||||||
* The "exec" program, commands.exec() and all related Command Computer functions now return the console output of the command.
|
* The `exec` program, `commands.exec()` and all related Command Computer functions now return the console output of the command.
|
||||||
* Fixed two multiplayer crash bugs.
|
* Fixed two multiplayer crash bugs.
|
||||||
|
|
||||||
New Features in ComputerCraft 1.7:
|
# New Features in ComputerCraft 1.7:
|
||||||
|
|
||||||
* Added Command Computers
|
* Added Command Computers
|
||||||
* Added new API: commands
|
* Added new API: `commands`
|
||||||
* Added new programs: commands, exec
|
* Added new programs: `commands`, `exec`
|
||||||
* Added textutils.serializeJSON()
|
* Added `textutils.serializeJSON()`
|
||||||
* Added ILuaContext.executeMainThreadTask() for peripheral developers
|
* Added `ILuaContext.executeMainThreadTask()` for peripheral developers
|
||||||
* Disk Drives and Printers can now be renamed with Anvils
|
* Disk Drives and Printers can now be renamed with Anvils
|
||||||
* Fixed various bugs, crashes and exploits
|
* Fixed various bugs, crashes and exploits
|
||||||
* Fixed problems with HD texture packs
|
* Fixed problems with HD texture packs
|
||||||
* Documented the new features in the in-game help
|
* Documented the new features in the in-game help
|
||||||
|
|
||||||
New Features in ComputerCraft 1.65:
|
# New Features in ComputerCraft 1.65:
|
||||||
|
|
||||||
* Fixed a multiplayer-only crash with turtle.place()
|
* Fixed a multiplayer-only crash with `turtle.place()`
|
||||||
* Fixed some problems with http.post()
|
* Fixed some problems with `http.post()`
|
||||||
* Fixed fs.getDrive() returning incorrect results on remote peripherals
|
* Fixed `fs.getDrive()` returning incorrect results on remote peripherals
|
||||||
|
|
||||||
New Features in ComputerCraft 1.64:
|
# New Features in ComputerCraft 1.64:
|
||||||
|
|
||||||
* Ported to Minecraft 1.7.10
|
* Ported to Minecraft 1.7.10
|
||||||
* New turtle functions: turtle.inspect(), turtle.inspectUp(), turtle.inspectDown(), turtle.getItemDetail()
|
* New turtle functions: `turtle.inspect()`, `turtle.inspectUp()`, `turtle.inspectDown()`, `turtle.getItemDetail()`
|
||||||
* Lots of bug and crash fixes, a huge stability improvement over previous versions
|
* Lots of bug and crash fixes, a huge stability improvement over previous versions
|
||||||
|
|
||||||
New Features in ComputerCraft 1.63:
|
# New Features in ComputerCraft 1.63:
|
||||||
|
|
||||||
* Turtles can now be painted with dyes, and cleaned with water buckets
|
* Turtles can now be painted with dyes, and cleaned with water buckets
|
||||||
* Added a new game: Redirection - ComputerCraft Edition
|
* Added a new game: Redirection - ComputerCraft Edition
|
||||||
* Turtle label nameplates now only show when the Turtle is moused-over
|
* Turtle label nameplates now only show when the Turtle is moused-over
|
||||||
* The HTTP API is now enabled by default, and can be configured with a whitelist of permitted domains
|
* The HTTP API is now enabled by default, and can be configured with a whitelist of permitted domains
|
||||||
* http.get() and http.post() now accept parameters to control the request headers
|
* `http.get()` and `http.post()` now accept parameters to control the request headers
|
||||||
* New fs function: fs.getDir( path )
|
* New fs function: `fs.getDir( path )`
|
||||||
* Fixed some bugs
|
* Fixed some bugs
|
||||||
|
|
||||||
New Features in ComputerCraft 1.62:
|
# New Features in ComputerCraft 1.62:
|
||||||
|
|
||||||
* Added IRC-style commands to the "chat" program
|
* Added IRC-style commands to the `chat` program
|
||||||
* Fixed some bugs and crashes
|
* Fixed some bugs and crashes
|
||||||
|
|
||||||
New Features in ComputerCraft 1.6:
|
# New Features in ComputerCraft 1.6:
|
||||||
|
|
||||||
* Added Pocket Computers
|
* Added Pocket Computers
|
||||||
* Added a multi-tasking system for Advanced Computers and Turtles
|
* Added a multi-tasking system for Advanced Computers and Turtles
|
||||||
@ -144,114 +406,114 @@ New Features in ComputerCraft 1.6:
|
|||||||
* Added a new game, only on Pocket Computers: "falling" by GopherATL
|
* Added a new game, only on Pocket Computers: "falling" by GopherATL
|
||||||
* File system commands in the shell now accept wildcard arguments
|
* File system commands in the shell now accept wildcard arguments
|
||||||
* The shell now accepts long arguments in quotes
|
* The shell now accepts long arguments in quotes
|
||||||
* Terminal redirection now no longer uses a stack-based system. Instead: term.current() gets the current terminal object and term.redirect() replaces it. term.restore() has been removed.
|
* Terminal redirection now no longer uses a stack-based system. Instead: `term.current()` gets the current terminal object and `term.redirect()` replaces it. `term.restore()` has been removed.
|
||||||
* Added a new Windowing API for addressing sub-areas of the terminal
|
* Added a new Windowing API for addressing sub-areas of the terminal
|
||||||
* New programs: fg, bg, multishell, chat, repeat, redstone, equip, unequip
|
* New programs: `fg`, `bg`, `multishell`, `chat`, `repeat`, `redstone`, `equip`, `unequip`
|
||||||
* Improved programs: copy, move, delete, rename, paint, shell
|
* Improved programs: `copy`, `move`, `delete`, `rename`, `paint`, `shell`
|
||||||
* Removed programs: redset, redprobe, redpulse
|
* Removed programs: `redset`, `redprobe`, `redpulse`
|
||||||
* New APIs: window, multishell
|
* New APIs: `window`, `multishell`
|
||||||
* New turtle functions: turtle.equipLeft() and turtle.equipRight()
|
* New turtle functions: `turtle.equipLeft()` and `turtle.equipRight()`
|
||||||
* New peripheral functions: peripheral.find( [type] )
|
* New peripheral functions: `peripheral.find( [type] )`
|
||||||
* New rednet functions: rednet.host( protocol, hostname ), rednet.unhost( protocol ), rednet.locate( protocol, [hostname] )
|
* New rednet functions: `rednet.host( protocol, hostname )`, `rednet.unhost( protocol )`, `rednet.locate( protocol, [hostname] )`
|
||||||
* New fs function: fs.find( wildcard )
|
* New fs function: `fs.find( wildcard )`
|
||||||
* New shell functions: shell.openTab(), shell.switchTab( [number] )
|
* New shell functions: `shell.openTab()`, `shell.switchTab( [number] )`
|
||||||
* New event "term_resize" fired when the size of a terminal changes
|
* New event `term_resize` fired when the size of a terminal changes
|
||||||
* Improved rednet functions: rednet.send(), rednet.broadcast() and rednet.receive() now take optional protocol parameters
|
* Improved rednet functions: `rednet.send()`, `rednet.broadcast()` and `rednet.receive()`now take optional protocol parameters
|
||||||
* turtle.craft(0) and turtle.refuel(0) now return true if there is a valid recipe or fuel item, but do not craft of refuel anything
|
* `turtle.craft(0)` and `turtle.refuel(0)` now return true if there is a valid recipe or fuel item, but do not craft of refuel anything
|
||||||
* turtle.suck( [limit] ) can now be used to limit the number of items picked up
|
* `turtle.suck( [limit] )` can now be used to limit the number of items picked up
|
||||||
* Users of turtle.dig() and turtle.attack() can now specify which side of the turtle to look for a tool to use (by default, both will be considered)
|
* Users of `turtle.dig()` and `turtle.attack()` can now specify which side of the turtle to look for a tool to use (by default, both will be considered)
|
||||||
* textutils.serialise( text ) now produces human-readable output
|
* `textutils.serialise( text )` now produces human-readable output
|
||||||
* Refactored most of the codebase and fixed many old bugs and instabilities, turtles should never ever lose their content now
|
* Refactored most of the codebase and fixed many old bugs and instabilities, turtles should never ever lose their content now
|
||||||
* Fixed the "turtle_inventory" event firing when it shouldn't have
|
* Fixed the `turtle_inventory` event firing when it shouldn't have
|
||||||
* Added error messages to many more turtle functions after they return false
|
* Added error messages to many more turtle functions after they return false
|
||||||
* Documented all new programs and API changes in the "help" system
|
* Documented all new programs and API changes in the `help` system
|
||||||
|
|
||||||
New Features in ComputerCraft 1.58:
|
# New Features in ComputerCraft 1.58:
|
||||||
|
|
||||||
* Fixed a long standing bug where turtles could lose their identify if they travel too far away
|
* Fixed a long standing bug where turtles could lose their identify if they travel too far away
|
||||||
* Fixed use of deprecated code, ensuring mod compatibility with the latest versions of Minecraft Forge, and world compatibility with future versions of Minecraft
|
* Fixed use of deprecated code, ensuring mod compatibility with the latest versions of Minecraft Forge, and world compatibility with future versions of Minecraft
|
||||||
|
|
||||||
New Features in ComputerCraft 1.57:
|
# New Features in ComputerCraft 1.57:
|
||||||
|
|
||||||
* Ported to Minecraft 1.6.4
|
* Ported to Minecraft 1.6.4
|
||||||
* Added two new Treasure Disks: Conway's Game of Life by vilsol and Protector by fredthead
|
* Added two new Treasure Disks: Conway's Game of Life by vilsol and Protector by fredthead
|
||||||
* Fixed a very nasty item duplication bug
|
* Fixed a very nasty item duplication bug
|
||||||
|
|
||||||
New Features in ComputerCraft 1.56:
|
# New Features in ComputerCraft 1.56:
|
||||||
|
|
||||||
* Added Treasure Disks: Floppy Disks in dungeons which contain interesting community made programs. Find them all!
|
* Added Treasure Disks: Floppy Disks in dungeons which contain interesting community made programs. Find them all!
|
||||||
* All turtle functions now return additional error messages when they fail.
|
* All turtle functions now return additional error messages when they fail.
|
||||||
* Resource Packs with Lua Programs can now be edited when extracted to a folder, for easier editing.
|
* Resource Packs with Lua Programs can now be edited when extracted to a folder, for easier editing.
|
||||||
|
|
||||||
New Features in ComputerCraft 1.55:
|
# New Features in ComputerCraft 1.55:
|
||||||
|
|
||||||
* Ported to Minecraft 1.6.2
|
* Ported to Minecraft 1.6.2
|
||||||
* Added Advanced Turtles
|
* Added Advanced Turtles
|
||||||
* Added "turtle_inventory" event. Fires when any change is made to the inventory of a turtle
|
* Added `turtle_inventory` event. Fires when any change is made to the inventory of a turtle
|
||||||
* Added missing functions io.close, io.flush, io.input, io.lines, io.output
|
* Added missing functions `io.close`, `io.flush`, `io.input`, `io.lines`, `io.output`
|
||||||
* Tweaked the screen colours used by Advanced Computers, Monitors and Turtles
|
* Tweaked the screen colours used by Advanced Computers, Monitors and Turtles
|
||||||
* Added new features for Peripheral authors
|
* Added new features for Peripheral authors
|
||||||
* Lua programs can now be included in Resource Packs
|
* Lua programs can now be included in Resource Packs
|
||||||
|
|
||||||
New Features in ComputerCraft 1.52:
|
# New Features in ComputerCraft 1.52:
|
||||||
|
|
||||||
* Ported to Minecraft 1.5.1
|
* Ported to Minecraft 1.5.1
|
||||||
|
|
||||||
New Features in ComputerCraft 1.51:
|
# New Features in ComputerCraft 1.51:
|
||||||
|
|
||||||
* Ported to Minecraft 1.5
|
* Ported to Minecraft 1.5
|
||||||
* Added Wired Modems
|
* Added Wired Modems
|
||||||
* Added Networking Cables
|
* Added Networking Cables
|
||||||
* Made Wireless Modems more expensive to craft
|
* Made Wireless Modems more expensive to craft
|
||||||
* New redstone API functions: getAnalogInput(), setAnalogOutput(), getAnalogOutput()
|
* New redstone API functions: `getAnalogInput()`, `setAnalogOutput()`, `getAnalogOutput()`
|
||||||
* Peripherals can now be controlled remotely over wired networks. New peripheral API function: getNames()
|
* Peripherals can now be controlled remotely over wired networks. New peripheral API function: `getNames()`
|
||||||
* New event: "monitor_resize" when the size of a monitor changes
|
* New event: `monitor_resize` when the size of a monitor changes
|
||||||
* Except for labelled computers and turtles, ComputerCraft blocks no longer drop items in creative mode
|
* Except for labelled computers and turtles, ComputerCraft blocks no longer drop items in creative mode
|
||||||
* The pick block function works in creative mode now works for all ComputerCraft blocks
|
* The pick block function works in creative mode now works for all ComputerCraft blocks
|
||||||
* All blocks and items now use the IDs numbers assigned by FTB by default
|
* All blocks and items now use the IDs numbers assigned by FTB by default
|
||||||
* Fixed turtles sometimes placing blocks with incorrect orientations
|
* Fixed turtles sometimes placing blocks with incorrect orientations
|
||||||
* Fixed Wireless modems being able to send messages to themselves
|
* Fixed Wireless modems being able to send messages to themselves
|
||||||
* Fixed turtle.attack() having a very short range
|
* Fixed `turtle.attack()` having a very short range
|
||||||
* Various bugfixes
|
* Various bugfixes
|
||||||
|
|
||||||
New Features in ComputerCraft 1.5:
|
# New Features in ComputerCraft 1.5:
|
||||||
|
|
||||||
* Redesigned Wireless Modems; they can now send and receive on multiple channels, independent of the computer ID. To use these features, interface with modem peripherals directly. The rednet API still functions as before
|
* Redesigned Wireless Modems; they can now send and receive on multiple channels, independent of the computer ID. To use these features, interface with modem peripherals directly. The rednet API still functions as before
|
||||||
* Floppy Disks can now be dyed with multiple dyes, just like armour
|
* Floppy Disks can now be dyed with multiple dyes, just like armour
|
||||||
* The "excavate" program now retains fuel in it's inventory, so can run unattended
|
* The `excavate` program now retains fuel in it's inventory, so can run unattended
|
||||||
* turtle.place() now tries all possible block orientations before failing
|
* `turtle.place()` now tries all possible block orientations before failing
|
||||||
* turtle.refuel(0) returns true if a fuel item is selected
|
* `turtle.refuel(0)` returns true if a fuel item is selected
|
||||||
* turtle.craft(0) returns true if the inventory is a valid recipe
|
* `turtle.craft(0)` returns true if the inventory is a valid recipe
|
||||||
* The in-game help system now has documentation for all the peripherals and their methods, including the new modem functionality
|
* The in-game help system now has documentation for all the peripherals and their methods, including the new modem functionality
|
||||||
* A romantic surprise
|
* A romantic surprise
|
||||||
|
|
||||||
New Features in ComputerCraft 1.48:
|
# New Features in ComputerCraft 1.48:
|
||||||
|
|
||||||
* Ported to Minecraft 1.4.6
|
* Ported to Minecraft 1.4.6
|
||||||
* Advanced Monitors now emit a "monitor_touch" event when right clicked
|
* Advanced Monitors now emit a `monitor_touch` event when right clicked
|
||||||
* Advanced Monitors are now cheaper to craft
|
* Advanced Monitors are now cheaper to craft
|
||||||
* Turtles now get slightly less fuel from items
|
* Turtles now get slightly less fuel from items
|
||||||
* Computers can now interact with Command Blocks (if enabled in ComputerCraft.cfg)
|
* Computers can now interact with Command Blocks (if enabled in ComputerCraft.cfg)
|
||||||
* New API function: os.day()
|
* New API function: `os.day()`
|
||||||
* A christmas surprise
|
* A christmas surprise
|
||||||
|
|
||||||
New Features in ComputerCraft 1.45:
|
# New Features in ComputerCraft 1.45:
|
||||||
|
|
||||||
* Added Advanced Computers
|
* Added Advanced Computers
|
||||||
* Added Advanced Monitors
|
* Added Advanced Monitors
|
||||||
* New program: paint by nitrogenfingers
|
* New program: paint by nitrogenfingers
|
||||||
* New API: paintutils
|
* New API: `paintutils`
|
||||||
* New term functions: term.setBackgroundColor, term.setTextColor, term.isColor
|
* New term functions: `term.setBackgroundColor`, `term.setTextColor`, `term.isColor`
|
||||||
* New turtle function: turtle.transferTo
|
* New turtle function: `turtle.transferTo`
|
||||||
|
|
||||||
New Features in ComputerCraft 1.43:
|
# New Features in ComputerCraft 1.43:
|
||||||
|
|
||||||
* Added Printed Pages
|
* Added Printed Pages
|
||||||
* Added Printed Books
|
* Added Printed Books
|
||||||
* Fixed incompatibility with Forge 275 and above
|
* Fixed incompatibility with Forge 275 and above
|
||||||
* Labelled Turtles now keep their fuel when broken
|
* Labelled Turtles now keep their fuel when broken
|
||||||
|
|
||||||
New Features in ComputerCraft 1.42:
|
# New Features in ComputerCraft 1.42:
|
||||||
|
|
||||||
* Ported to Minecraft 1.3.2
|
* Ported to Minecraft 1.3.2
|
||||||
* Added Printers
|
* Added Printers
|
||||||
@ -261,7 +523,7 @@ New Features in ComputerCraft 1.42:
|
|||||||
* New forge config file
|
* New forge config file
|
||||||
* Bug fixes
|
* Bug fixes
|
||||||
|
|
||||||
New Features in ComputerCraft 1.4:
|
# New Features in ComputerCraft 1.4:
|
||||||
|
|
||||||
* Ported to Forge Mod Loader. ComputerCraft can now be ran directly from the .zip without extraction
|
* Ported to Forge Mod Loader. ComputerCraft can now be ran directly from the .zip without extraction
|
||||||
* Added Farming Turtles
|
* Added Farming Turtles
|
||||||
@ -272,48 +534,48 @@ New Features in ComputerCraft 1.4:
|
|||||||
* Added 14 new Turtle Combinations accessible by combining the turtle upgrades above
|
* Added 14 new Turtle Combinations accessible by combining the turtle upgrades above
|
||||||
* Labelled computers and turtles can now be crafted into turtles or other turtle types without losing their ID, label and data
|
* Labelled computers and turtles can now be crafted into turtles or other turtle types without losing their ID, label and data
|
||||||
* Added a "Turtle Upgrade API" for mod developers to create their own tools and peripherals for turtles
|
* Added a "Turtle Upgrade API" for mod developers to create their own tools and peripherals for turtles
|
||||||
* Turtles can now attack entities with turtle.attack(), and collect their dropped items
|
* Turtles can now attack entities with `turtle.attack()`, and collect their dropped items
|
||||||
* Turtles can now use turtle.place() with any item the player can, and can interact with entities
|
* Turtles can now use `turtle.place()` with any item the player can, and can interact with entities
|
||||||
* Turtles can now craft items with turtle.craft()
|
* Turtles can now craft items with `turtle.craft()`
|
||||||
* Turtles can now place items into inventories with turtle.drop()
|
* Turtles can now place items into inventories with `turtle.drop()`
|
||||||
* Changed the behaviour of turtle.place() and turtle.drop() to only consider the currently selected slot
|
* Changed the behaviour of `turtle.place()` and `turtle.drop()` to only consider the currently selected slot
|
||||||
* Turtles can now pick up items from the ground, or from inventories, with turtle.suck()
|
* Turtles can now pick up items from the ground, or from inventories, with `turtle.suck()`
|
||||||
* Turtles can now compare items in their inventories
|
* Turtles can now compare items in their inventories
|
||||||
* Turtles can place signs with text on them with turtle.place( [signText] )
|
* Turtles can place signs with text on them with `turtle.place( [signText] )`
|
||||||
* Turtles now optionally require fuel items to move, and can refuel themselves
|
* Turtles now optionally require fuel items to move, and can refuel themselves
|
||||||
* The size of the the turtle inventory has been increased to 16
|
* The size of the the turtle inventory has been increased to 16
|
||||||
* The size of the turtle screen has been increased
|
* The size of the turtle screen has been increased
|
||||||
* New turtle functions: turtle.compareTo( [slotNum] ), turtle.craft(), turtle.attack(), turtle.attackUp(), turtle.attackDown(), turtle.dropUp(), turtle.dropDown(), turtle.getFuelLevel(), turtle.refuel()
|
* New turtle functions: `turtle.compareTo( [slotNum] )`, `turtle.craft()`, `turtle.attack()`, `turtle.attackUp()`, `turtle.attackDown()`, `turtle.dropUp()`, `turtle.dropDown()`, `turtle.getFuelLevel()`, `turtle.refuel()`
|
||||||
* New disk function: disk.getID()
|
* New disk function: disk.getID()
|
||||||
* New turtle programs: craft, refuel
|
* New turtle programs: `craft`, `refuel`
|
||||||
* "excavate" program now much smarter: Will return items to a chest when full, attack mobs, and refuel itself automatically
|
* `excavate` program now much smarter: Will return items to a chest when full, attack mobs, and refuel itself automatically
|
||||||
* New API: keys
|
* New API: `keys`
|
||||||
* Added optional Floppy Disk and Hard Drive space limits for computers and turtles
|
* Added optional Floppy Disk and Hard Drive space limits for computers and turtles
|
||||||
* New fs function: fs.getFreeSpace( path ), also fs.getDrive() works again
|
* New `fs` function: `fs.getFreeSpace( path )`, also `fs.getDrive()` works again
|
||||||
* The send and receive range of wireless modems now increases with altitude, allowing long range networking from high-altitude computers (great for GPS networks)
|
* The send and receive range of wireless modems now increases with altitude, allowing long range networking from high-altitude computers (great for GPS networks)
|
||||||
* http.request() now supports https:// URLs
|
* `http.request()` now supports https:// URLs
|
||||||
* Right clicking a Disk Drive with a Floppy Disk or a Record when sneaking will insert the item into the Disk Drive automatically
|
* Right clicking a Disk Drive with a Floppy Disk or a Record when sneaking will insert the item into the Disk Drive automatically
|
||||||
* The default size of the computer screen has been increased
|
* The default size of the computer screen has been increased
|
||||||
* Several stability and security fixes. LuaJ can now no longer leave dangling threads when a computer is unloaded, turtles can no longer be destroyed by tree leaves or walking off the edge of the loaded map. Computers no longer crash when used with RedPower frames.
|
* Several stability and security fixes. LuaJ can now no longer leave dangling threads when a computer is unloaded, turtles can no longer be destroyed by tree leaves or walking off the edge of the loaded map. Computers no longer crash when used with RedPower frames.
|
||||||
|
|
||||||
New Features in ComputerCraft 1.31:
|
# New Features in ComputerCraft 1.31:
|
||||||
|
|
||||||
* Ported to Minecraft 1.2.3
|
* Ported to Minecraft 1.2.3
|
||||||
* Added Monitors (thanks to Cloudy)
|
* Added Monitors (thanks to Cloudy)
|
||||||
* Updated LuaJ to a newer, less memory hungry version
|
* Updated LuaJ to a newer, less memory hungry version
|
||||||
* rednet_message event now has a third parameter, "distance", to support position triangulation.
|
* `rednet_message` event now has a third parameter, "distance", to support position triangulation.
|
||||||
* New programs: gps, monitor, pastebin.
|
* New programs: `gps`, `monitor`, `pastebin`.
|
||||||
* Added a secret program. Use with large monitors!
|
* Added a secret program. Use with large monitors!
|
||||||
* New apis: gps, vector
|
* New apis: `gps`, `vector`
|
||||||
* New turtle functions: turtle.compare(), turtle.compareUp(), turtle.compareDown(), turtle.drop( quantity )
|
* New turtle functions: `turtle.compare()`, `turtle.compareUp()`, `turtle.compareDown()`, `turtle.drop( quantity )`
|
||||||
* New http functions: http.post().
|
* New `http` functions: `http.post()`.
|
||||||
* New term functions: term.redirect(), term.restore()
|
* New `term` functions: `term.redirect()`, `term.restore()`
|
||||||
* New textutils functions: textutils.urlEncode()
|
* New `textutils` functions: `textutils.urlEncode()`
|
||||||
* New rednet functions: rednet.isOpen()
|
* New `rednet` functions: `rednet.isOpen()`
|
||||||
* New config options: modem_range, modem_rangeDuringStorm
|
* New config options: modem_range, modem_rangeDuringStorm
|
||||||
* Bug fixes, program tweaks, and help updates
|
* Bug fixes, program tweaks, and help updates
|
||||||
|
|
||||||
New Features in ComputerCraft 1.3:
|
# New Features in ComputerCraft 1.3:
|
||||||
|
|
||||||
* Ported to Minecraft Forge
|
* Ported to Minecraft Forge
|
||||||
* Added Turtles
|
* Added Turtles
|
||||||
@ -325,36 +587,34 @@ New Features in ComputerCraft 1.3:
|
|||||||
* Computers and Turtles can now be labelled with the label program, and labelled devices keep their state when destroyed.
|
* Computers and Turtles can now be labelled with the label program, and labelled devices keep their state when destroyed.
|
||||||
* Computers/Turtles can connect to adjacent devices, and turn them on and off
|
* Computers/Turtles can connect to adjacent devices, and turn them on and off
|
||||||
* User programs now give line numbers in their error messages
|
* User programs now give line numbers in their error messages
|
||||||
* New APIs: turtle, peripheral
|
* New APIs: `turtle`, `peripheral`
|
||||||
* New programs for turtles: tunnel, excavate, go, turn, dance
|
* New programs for turtles: tunnel, excavate, go, turn, dance
|
||||||
* New os functions: os.getComputerLabel(), os.setComputerLabel()
|
* New os functions: `os.getComputerLabel()`, `os.setComputerLabel()`
|
||||||
* Added "filter" parameter to os.pullEvent()
|
* Added "filter" parameter to `os.pullEvent()`
|
||||||
* New shell function: shell.getCurrentProgram()
|
* New shell function: `shell.getCurrentProgram()`
|
||||||
* New textutils functions: textutils.serialize(), textutils.unserialize(), textutils.tabulate(), textutils.pagedTabulate(), textutils.slowWrite()
|
* New textutils functions: `textutils.serialize()`, `textutils.unserialize()`, `textutils.tabulate()`, `textutils.pagedTabulate()`, `textutils.slowWrite()`
|
||||||
* New io file function: file:lines()
|
* New io file function: `file:lines()`
|
||||||
* New fs function: fs.getSize()
|
* New fs function: `fs.getSize()`
|
||||||
* Disk Drives can now play records from other mods
|
* Disk Drives can now play records from other mods
|
||||||
* Bug fixes, program tweaks, and help updates
|
* Bug fixes, program tweaks, and help updates
|
||||||
|
|
||||||
New Features in ComputerCraft 1.2:
|
# New Features in ComputerCraft 1.2:
|
||||||
|
|
||||||
* Added Disk Drives and Floppy Disks
|
* Added Disk Drives and Floppy Disks
|
||||||
* Added Ctrl+T shortcut to terminate the current program (hold)
|
* Added Ctrl+T shortcut to terminate the current program (hold)
|
||||||
* Added Ctrl+S shortcut to shutdown the computer (hold)
|
* Added Ctrl+S shortcut to shutdown the computer (hold)
|
||||||
* Added Ctrl+R shortcut to reboot the computer (hold)
|
* Added Ctrl+R shortcut to reboot the computer (hold)
|
||||||
* New Programs: alias, apis, copy, delete, dj, drive, eject, id, label, list, move, reboot, redset, rename, time, worm.
|
* New Programs: `alias`, `apis`, `copy`, `delete`, `dj`, `drive`, `eject`, `id`, `label`, `list`, `move`, `reboot`, `redset`, `rename`, `time`, `worm`.
|
||||||
* New APIs: bit, colours, disk, help, rednet, parallel, textutils.
|
* New APIs: `bit`, `colours`, `disk`, `help`, `rednet`, `parallel`, `textutils`.
|
||||||
* New color functions: colors.combine(), colors.subtract(), colors.test()
|
* New color functions: `colors.combine()`, `colors.subtract()`, `colors.test()`
|
||||||
* New fs functions: fs.getName(), new modes for fs.open()
|
* New fs functions: `fs.getName()`, new modes for `fs.open()`
|
||||||
* New os functions: os.loadAPI(), os.unloadAPI(),
|
* New os functions: `os.loadAPI()`, `os.unloadAPI()`, `os.clock()`, `os.time()`, `os.setAlarm()`, `os.reboot()`, `os.queueEvent()`
|
||||||
os.clock(), os.time(), os.setAlarm(),
|
* New redstone function: `redstone.getSides()`
|
||||||
os.reboot(), os.queueEvent()
|
* New shell functions: `shell.setPath()`, `shell.programs()`, `shell.resolveProgram()`, `shell.setAlias()`
|
||||||
* New redstone function: redstone.getSides()
|
|
||||||
* New shell functions: shell.setPath(), shell.programs(), shell.resolveProgram(), shell.setAlias()
|
|
||||||
* Lots of updates to the help pages
|
* Lots of updates to the help pages
|
||||||
* Bug fixes
|
* Bug fixes
|
||||||
|
|
||||||
New Features in ComputerCraft 1.1:
|
# New Features in ComputerCraft 1.1:
|
||||||
|
|
||||||
* Added Multiplayer support throughout.
|
* Added Multiplayer support throughout.
|
||||||
* Added connectivity with RedPower bundled cables
|
* Added connectivity with RedPower bundled cables
|
||||||
@ -363,6 +623,6 @@ New Features in ComputerCraft 1.1:
|
|||||||
* Programs which spin in an infinite loop without yielding will no longer freeze minecraft
|
* Programs which spin in an infinite loop without yielding will no longer freeze minecraft
|
||||||
* Help updates and bug fixes
|
* Help updates and bug fixes
|
||||||
|
|
||||||
New Features in ComputerCraft 1.0:
|
# New Features in ComputerCraft 1.0:
|
||||||
|
|
||||||
* First Release!
|
* First Release!
|
||||||
|
@ -1,37 +1,10 @@
|
|||||||
New Features in ComputerCraft 1.80:
|
New features in CC: Tweaked 1.83.1
|
||||||
|
|
||||||
* Added .getResponseHeaders() to HTTP responses.
|
* Add several new MOTD messages (JakobDev)
|
||||||
* Return a HTTP response when a HTTP error occurs.
|
|
||||||
* Added a GUI to change ComputerCraft config options.
|
And several bug fixes:
|
||||||
* os.time() and os.day() now accept parameters to give the real world time.
|
* Fix type check in `rednet.lookup`
|
||||||
* Added os.epoch()
|
* Error if turtle and pocket computer programs are run on the wrong system (JakobDev)
|
||||||
* Monitor text now glows in the dark.
|
* Do not discard varargs after a nil.
|
||||||
* Added a "Pocket Computer upgrade API" so mod developers can add their own pocket upgrades.
|
|
||||||
* Added pocket.equipBack()/pocket.unequipBack() to add/remove pocket upgrades.
|
|
||||||
* Added term.setPaletteColor()/term.getPaletteColor() to change/check colors
|
|
||||||
* Added colors.rgb8()/colours.rgb8()
|
|
||||||
* Performance improvements to fs.find
|
|
||||||
* Requires the player to be interacting with the computer when typing
|
|
||||||
* Disk labels are limited to 32 characters
|
|
||||||
* Labels can now only include characters within the printable range ( to ~)
|
|
||||||
* Various model improvements
|
|
||||||
* There is now a configurable file descriptor limit
|
|
||||||
* Threads are now daemon threads
|
|
||||||
* Termination signals are now sent unless the computer is off
|
|
||||||
* Fixed compilation errors
|
|
||||||
* Now handles tile entity changes
|
|
||||||
* GPS coordinates now have to be numbers
|
|
||||||
* Turtle upgrades now act as tools and peripherals
|
|
||||||
* The Filesystem.list result is now sorted
|
|
||||||
* The number of values to unpack can now be manually specified
|
|
||||||
* Small terminal & monitor rendering improvements
|
|
||||||
* General improvements to the documentation
|
|
||||||
* Redstone inputs are no longer reset when adding peripherals
|
|
||||||
* Turtles now use tinting
|
|
||||||
* shell.resolveProgram now picks up on *.lua files
|
|
||||||
* Fixed a handful of bugs in ComputerCraft
|
|
||||||
* Added speaker block, turtle upgrade, pocket upgrade, and peripheral api
|
|
||||||
* Startup can now be a directory containing multiple startup files
|
|
||||||
* Added .getLabel to the computer peripheral
|
|
||||||
|
|
||||||
Type "help changelog" to see the full version history.
|
Type "help changelog" to see the full version history.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
-- Setup process switching
|
-- Setup process switching
|
||||||
local parentTerm = term.current()
|
local parentTerm = term.current()
|
||||||
@ -197,9 +198,7 @@ function multishell.getFocus()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function multishell.setFocus( n )
|
function multishell.setFocus( n )
|
||||||
if type( n ) ~= "number" then
|
expect(1, n, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if n >= 1 and n <= #tProcesses then
|
if n >= 1 and n <= #tProcesses then
|
||||||
selectProcess( n )
|
selectProcess( n )
|
||||||
redrawMenu()
|
redrawMenu()
|
||||||
@ -209,9 +208,7 @@ function multishell.setFocus( n )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function multishell.getTitle( n )
|
function multishell.getTitle( n )
|
||||||
if type( n ) ~= "number" then
|
expect(1, n, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if n >= 1 and n <= #tProcesses then
|
if n >= 1 and n <= #tProcesses then
|
||||||
return tProcesses[n].sTitle
|
return tProcesses[n].sTitle
|
||||||
end
|
end
|
||||||
@ -219,12 +216,8 @@ function multishell.getTitle( n )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function multishell.setTitle( n, sTitle )
|
function multishell.setTitle( n, sTitle )
|
||||||
if type( n ) ~= "number" then
|
expect(1, n, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( n ) .. ")", 2 )
|
expect(2, sTitle, "string")
|
||||||
end
|
|
||||||
if type( sTitle ) ~= "string" then
|
|
||||||
error( "bad argument #2 (expected string, got " .. type( sTitle ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if n >= 1 and n <= #tProcesses then
|
if n >= 1 and n <= #tProcesses then
|
||||||
setProcessTitle( n, sTitle )
|
setProcessTitle( n, sTitle )
|
||||||
redrawMenu()
|
redrawMenu()
|
||||||
@ -236,12 +229,8 @@ function multishell.getCurrent()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function multishell.launch( tProgramEnv, sProgramPath, ... )
|
function multishell.launch( tProgramEnv, sProgramPath, ... )
|
||||||
if type( tProgramEnv ) ~= "table" then
|
expect(1, tProgramEnv, "table")
|
||||||
error( "bad argument #1 (expected table, got " .. type( tProgramEnv ) .. ")", 2 )
|
expect(2, sProgramPath, "string")
|
||||||
end
|
|
||||||
if type( sProgramPath ) ~= "string" then
|
|
||||||
error( "bad argument #2 (expected string, got " .. type( sProgramPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local previousTerm = term.current()
|
local previousTerm = term.current()
|
||||||
setMenuVisible( (#tProcesses + 1) >= 2 )
|
setMenuVisible( (#tProcesses + 1) >= 2 )
|
||||||
local nResult = launchProcess( false, tProgramEnv, sProgramPath, ... )
|
local nResult = launchProcess( false, tProgramEnv, sProgramPath, ... )
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
|
local args = table.pack(...)
|
||||||
|
|
||||||
local tArgs = { ... }
|
if args.n < 1 then
|
||||||
if #tArgs < 1 then
|
print("Usage: rm <paths>")
|
||||||
print( "Usage: rm <path>" )
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local sPath = shell.resolve( tArgs[1] )
|
for i = 1, args.n do
|
||||||
local tFiles = fs.find( sPath )
|
local files = fs.find(shell.resolve(args[i]))
|
||||||
if #tFiles > 0 then
|
if #files > 0 then
|
||||||
for n,sFile in ipairs( tFiles ) do
|
for n, file in ipairs(files) do
|
||||||
fs.delete( sFile )
|
fs.delete(file)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
printError(args[i] .. ": No matching files")
|
||||||
end
|
end
|
||||||
else
|
|
||||||
printError( "No matching files" )
|
|
||||||
end
|
end
|
||||||
|
@ -18,6 +18,23 @@ local tEnv = {
|
|||||||
}
|
}
|
||||||
setmetatable( tEnv, { __index = _ENV } )
|
setmetatable( tEnv, { __index = _ENV } )
|
||||||
|
|
||||||
|
-- Replace our package.path, so that it loads from the current directory, rather
|
||||||
|
-- than from /rom/programs. This makes it a little more friendly to use and
|
||||||
|
-- closer to what you'd expect.
|
||||||
|
do
|
||||||
|
local dir = shell.dir()
|
||||||
|
if dir:sub(1, 1) ~= "/" then dir = "/" .. dir end
|
||||||
|
if dir:sub(-1) ~= "/" then dir = dir .. "/" end
|
||||||
|
|
||||||
|
local strip_path = "?;?.lua;?/init.lua;"
|
||||||
|
local path = package.path
|
||||||
|
if path:sub(1, #strip_path) == strip_path then
|
||||||
|
path = path:sub(#strip_path + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
package.path = dir .. "?;" .. dir .. "?.lua;" .. dir .. "?/init.lua;" .. path
|
||||||
|
end
|
||||||
|
|
||||||
if term.isColour() then
|
if term.isColour() then
|
||||||
term.setTextColour( colours.yellow )
|
term.setTextColour( colours.yellow )
|
||||||
end
|
end
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
|
|
||||||
if #tArgs < 1 then
|
if #tArgs < 1 then
|
||||||
print( "Usage: mkdir <path>" )
|
print( "Usage: mkdir <paths>" )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local sNewDir = shell.resolve( tArgs[1] )
|
for _, v in ipairs( tArgs ) do
|
||||||
|
local sNewDir = shell.resolve( v )
|
||||||
if fs.exists( sNewDir ) and not fs.isDir(sNewDir) then
|
if fs.exists( sNewDir ) and not fs.isDir( sNewDir ) then
|
||||||
printError( "Destination exists" )
|
printError( v..": Destination exists" )
|
||||||
return
|
elseif fs.isReadOnly( sNewDir ) then
|
||||||
|
printError( v..": Access denied" )
|
||||||
|
else
|
||||||
|
fs.makeDir( sNewDir )
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
fs.makeDir( sNewDir )
|
|
||||||
|
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
if not pocket then
|
||||||
|
printError( "Requires a Pocket Computer" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local ok, err = pocket.equipBack()
|
local ok, err = pocket.equipBack()
|
||||||
if not ok then
|
if not ok then
|
||||||
printError( err )
|
printError( err )
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
if not pocket then
|
||||||
|
printError( "Requires a Pocket Computer" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local ok, err = pocket.unequipBack()
|
local ok, err = pocket.unequipBack()
|
||||||
if not ok then
|
if not ok then
|
||||||
printError( err )
|
printError( err )
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
local expect = _G["~expect"]
|
||||||
|
|
||||||
local multishell = multishell
|
local multishell = multishell
|
||||||
local parentShell = shell
|
local parentShell = shell
|
||||||
@ -74,9 +75,7 @@ local function createShellEnv( sDir )
|
|||||||
|
|
||||||
local sentinel = {}
|
local sentinel = {}
|
||||||
local function require( name )
|
local function require( name )
|
||||||
if type( name ) ~= "string" then
|
expect(1, name, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if package.loaded[name] == sentinel then
|
if package.loaded[name] == sentinel then
|
||||||
error("loop or previous error loading module '" .. name .. "'", 0)
|
error("loop or previous error loading module '" .. name .. "'", 0)
|
||||||
end
|
end
|
||||||
@ -130,8 +129,12 @@ local function run( _sCommand, ... )
|
|||||||
end
|
end
|
||||||
multishell.setTitle( multishell.getCurrent(), sTitle )
|
multishell.setTitle( multishell.getCurrent(), sTitle )
|
||||||
end
|
end
|
||||||
|
|
||||||
local sDir = fs.getDir( sPath )
|
local sDir = fs.getDir( sPath )
|
||||||
local result = os.run( createShellEnv( sDir ), sPath, ... )
|
local env = createShellEnv( sDir )
|
||||||
|
env[ "arg" ] = { [0] = _sCommand, ... }
|
||||||
|
local result = os.run( env, sPath, ... )
|
||||||
|
|
||||||
tProgramStack[#tProgramStack] = nil
|
tProgramStack[#tProgramStack] = nil
|
||||||
if multishell then
|
if multishell then
|
||||||
if #tProgramStack > 0 then
|
if #tProgramStack > 0 then
|
||||||
@ -187,9 +190,7 @@ function shell.dir()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function shell.setDir( _sDir )
|
function shell.setDir( _sDir )
|
||||||
if type( _sDir ) ~= "string" then
|
expect(1, _sDir, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sDir ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if not fs.isDir( _sDir ) then
|
if not fs.isDir( _sDir ) then
|
||||||
error( "Not a directory", 2 )
|
error( "Not a directory", 2 )
|
||||||
end
|
end
|
||||||
@ -201,16 +202,12 @@ function shell.path()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function shell.setPath( _sPath )
|
function shell.setPath( _sPath )
|
||||||
if type( _sPath ) ~= "string" then
|
expect(1, _sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
sPath = _sPath
|
sPath = _sPath
|
||||||
end
|
end
|
||||||
|
|
||||||
function shell.resolve( _sPath )
|
function shell.resolve( _sPath )
|
||||||
if type( _sPath ) ~= "string" then
|
expect(1, _sPath, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sPath ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
local sStartChar = string.sub( _sPath, 1, 1 )
|
local sStartChar = string.sub( _sPath, 1, 1 )
|
||||||
if sStartChar == "/" or sStartChar == "\\" then
|
if sStartChar == "/" or sStartChar == "\\" then
|
||||||
return fs.combine( "", _sPath )
|
return fs.combine( "", _sPath )
|
||||||
@ -230,9 +227,7 @@ local function pathWithExtension( _sPath, _sExt )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function shell.resolveProgram( _sCommand )
|
function shell.resolveProgram( _sCommand )
|
||||||
if type( _sCommand ) ~= "string" then
|
expect(1, _sCommand, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sCommand ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
-- Substitute aliases firsts
|
-- Substitute aliases firsts
|
||||||
if tAliases[ _sCommand ] ~= nil then
|
if tAliases[ _sCommand ] ~= nil then
|
||||||
_sCommand = tAliases[ _sCommand ]
|
_sCommand = tAliases[ _sCommand ]
|
||||||
@ -357,9 +352,7 @@ local function completeProgramArgument( sProgram, nArgument, sPart, tPreviousPar
|
|||||||
end
|
end
|
||||||
|
|
||||||
function shell.complete( sLine )
|
function shell.complete( sLine )
|
||||||
if type( sLine ) ~= "string" then
|
expect(1, sLine, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sLine ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
if #sLine > 0 then
|
if #sLine > 0 then
|
||||||
local tWords = tokenise( sLine )
|
local tWords = tokenise( sLine )
|
||||||
local nIndex = #tWords
|
local nIndex = #tWords
|
||||||
@ -396,19 +389,13 @@ function shell.complete( sLine )
|
|||||||
end
|
end
|
||||||
|
|
||||||
function shell.completeProgram( sProgram )
|
function shell.completeProgram( sProgram )
|
||||||
if type( sProgram ) ~= "string" then
|
expect(1, sProgram, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sProgram ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
return completeProgram( sProgram )
|
return completeProgram( sProgram )
|
||||||
end
|
end
|
||||||
|
|
||||||
function shell.setCompletionFunction( sProgram, fnComplete )
|
function shell.setCompletionFunction( sProgram, fnComplete )
|
||||||
if type( sProgram ) ~= "string" then
|
expect(1, sProgram, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( sProgram ) .. ")", 2 )
|
expect(2, fnComplete, "function")
|
||||||
end
|
|
||||||
if type( fnComplete ) ~= "function" then
|
|
||||||
error( "bad argument #2 (expected function, got " .. type( fnComplete ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
tCompletionInfo[ sProgram ] = {
|
tCompletionInfo[ sProgram ] = {
|
||||||
fnComplete = fnComplete
|
fnComplete = fnComplete
|
||||||
}
|
}
|
||||||
@ -426,19 +413,13 @@ function shell.getRunningProgram()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function shell.setAlias( _sCommand, _sProgram )
|
function shell.setAlias( _sCommand, _sProgram )
|
||||||
if type( _sCommand ) ~= "string" then
|
expect(1, _sCommand, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sCommand ) .. ")", 2 )
|
expect(2, _sProgram, "string")
|
||||||
end
|
|
||||||
if type( _sProgram ) ~= "string" then
|
|
||||||
error( "bad argument #2 (expected string, got " .. type( _sProgram ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
tAliases[ _sCommand ] = _sProgram
|
tAliases[ _sCommand ] = _sProgram
|
||||||
end
|
end
|
||||||
|
|
||||||
function shell.clearAlias( _sCommand )
|
function shell.clearAlias( _sCommand )
|
||||||
if type( _sCommand ) ~= "string" then
|
expect(1, _sCommand, "string")
|
||||||
error( "bad argument #1 (expected string, got " .. type( _sCommand ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
tAliases[ _sCommand ] = nil
|
tAliases[ _sCommand ] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -468,9 +449,7 @@ if multishell then
|
|||||||
end
|
end
|
||||||
|
|
||||||
function shell.switchTab( nID )
|
function shell.switchTab( nID )
|
||||||
if type( nID ) ~= "number" then
|
expect(1, nID, "number")
|
||||||
error( "bad argument #1 (expected number, got " .. type( nID ) .. ")", 2 )
|
|
||||||
end
|
|
||||||
multishell.setFocus( nID )
|
multishell.setFocus( nID )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if not turtle.craft then
|
if not turtle.craft then
|
||||||
print( "Requires a Crafty Turtle" )
|
print( "Requires a Crafty Turtle" )
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
end
|
||||||
|
|
||||||
local tMoves = {
|
local tMoves = {
|
||||||
function()
|
function()
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
local function printUsage()
|
local function printUsage()
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
if #tArgs ~= 1 then
|
if #tArgs ~= 1 then
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
if #tArgs < 1 then
|
if #tArgs < 1 then
|
||||||
print( "Usage: go <direction> <distance>" )
|
print( "Usage: go <direction> <distance>" )
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
local nLimit = 1
|
local nLimit = 1
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
if #tArgs ~= 1 then
|
if #tArgs ~= 1 then
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
if #tArgs < 1 then
|
if #tArgs < 1 then
|
||||||
print( "Usage: turn <direction> <turns>" )
|
print( "Usage: turn <direction> <turns>" )
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
if not turtle then
|
||||||
|
printError( "Requires a Turtle" )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local tArgs = { ... }
|
local tArgs = { ... }
|
||||||
local function printUsage()
|
local function printUsage()
|
||||||
|
@ -66,6 +66,9 @@ local function completeFile( shell, nIndex, sText, tPreviousText )
|
|||||||
return fs.complete( sText, shell.dir(), true, false )
|
return fs.complete( sText, shell.dir(), true, false )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local function completeFileMany( shell, nIndex, sText, tPreviousText )
|
||||||
|
return fs.complete( sText, shell.dir(), true, false )
|
||||||
|
end
|
||||||
local function completeDir( shell, nIndex, sText, tPreviousText )
|
local function completeDir( shell, nIndex, sText, tPreviousText )
|
||||||
if nIndex == 1 then
|
if nIndex == 1 then
|
||||||
return fs.complete( sText, shell.dir(), false, true )
|
return fs.complete( sText, shell.dir(), false, true )
|
||||||
@ -76,6 +79,9 @@ local function completeEither( shell, nIndex, sText, tPreviousText )
|
|||||||
return fs.complete( sText, shell.dir(), true, true )
|
return fs.complete( sText, shell.dir(), true, true )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local function completeEitherMany( shell, nIndex, sText, tPreviousText )
|
||||||
|
return fs.complete( sText, shell.dir(), true, true )
|
||||||
|
end
|
||||||
local function completeEitherEither( shell, nIndex, sText, tPreviousText )
|
local function completeEitherEither( shell, nIndex, sText, tPreviousText )
|
||||||
if nIndex == 1 then
|
if nIndex == 1 then
|
||||||
local tResults = fs.complete( sText, shell.dir(), true, true )
|
local tResults = fs.complete( sText, shell.dir(), true, true )
|
||||||
@ -180,7 +186,7 @@ end
|
|||||||
shell.setCompletionFunction( "rom/programs/alias.lua", completeAlias )
|
shell.setCompletionFunction( "rom/programs/alias.lua", completeAlias )
|
||||||
shell.setCompletionFunction( "rom/programs/cd.lua", completeDir )
|
shell.setCompletionFunction( "rom/programs/cd.lua", completeDir )
|
||||||
shell.setCompletionFunction( "rom/programs/copy.lua", completeEitherEither )
|
shell.setCompletionFunction( "rom/programs/copy.lua", completeEitherEither )
|
||||||
shell.setCompletionFunction( "rom/programs/delete.lua", completeEither )
|
shell.setCompletionFunction( "rom/programs/delete.lua", completeEitherMany )
|
||||||
shell.setCompletionFunction( "rom/programs/drive.lua", completeDir )
|
shell.setCompletionFunction( "rom/programs/drive.lua", completeDir )
|
||||||
shell.setCompletionFunction( "rom/programs/edit.lua", completeFile )
|
shell.setCompletionFunction( "rom/programs/edit.lua", completeFile )
|
||||||
shell.setCompletionFunction( "rom/programs/eject.lua", completePeripheral )
|
shell.setCompletionFunction( "rom/programs/eject.lua", completePeripheral )
|
||||||
@ -189,7 +195,7 @@ shell.setCompletionFunction( "rom/programs/help.lua", completeHelp )
|
|||||||
shell.setCompletionFunction( "rom/programs/id.lua", completePeripheral )
|
shell.setCompletionFunction( "rom/programs/id.lua", completePeripheral )
|
||||||
shell.setCompletionFunction( "rom/programs/label.lua", completeLabel )
|
shell.setCompletionFunction( "rom/programs/label.lua", completeLabel )
|
||||||
shell.setCompletionFunction( "rom/programs/list.lua", completeDir )
|
shell.setCompletionFunction( "rom/programs/list.lua", completeDir )
|
||||||
shell.setCompletionFunction( "rom/programs/mkdir.lua", completeFile )
|
shell.setCompletionFunction( "rom/programs/mkdir.lua", completeFileMany )
|
||||||
shell.setCompletionFunction( "rom/programs/monitor.lua", completeMonitor )
|
shell.setCompletionFunction( "rom/programs/monitor.lua", completeMonitor )
|
||||||
shell.setCompletionFunction( "rom/programs/move.lua", completeEitherEither )
|
shell.setCompletionFunction( "rom/programs/move.lua", completeEitherEither )
|
||||||
shell.setCompletionFunction( "rom/programs/redstone.lua", completeRedstone )
|
shell.setCompletionFunction( "rom/programs/redstone.lua", completeRedstone )
|
||||||
@ -269,6 +275,11 @@ local function findStartups( sBaseDir )
|
|||||||
return tStartups
|
return tStartups
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Show MOTD
|
||||||
|
if settings.get( "motd.enable" ) then
|
||||||
|
shell.run( "motd" )
|
||||||
|
end
|
||||||
|
|
||||||
-- Run the user created startup, either from disk drives or the root
|
-- Run the user created startup, either from disk drives or the root
|
||||||
local tUserStartups = nil
|
local tUserStartups = nil
|
||||||
if settings.get( "shell.allow_startup" ) then
|
if settings.get( "shell.allow_startup" ) then
|
||||||
|
@ -14,8 +14,8 @@ import dan200.computercraft.api.lua.LuaException;
|
|||||||
import dan200.computercraft.core.computer.BasicEnvironment;
|
import dan200.computercraft.core.computer.BasicEnvironment;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import dan200.computercraft.core.computer.MainThread;
|
import dan200.computercraft.core.computer.MainThread;
|
||||||
|
import dan200.computercraft.core.filesystem.FileMount;
|
||||||
import dan200.computercraft.core.filesystem.FileSystemException;
|
import dan200.computercraft.core.filesystem.FileSystemException;
|
||||||
import dan200.computercraft.core.filesystem.MemoryMount;
|
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
@ -25,9 +25,13 @@ import org.opentest4j.AssertionFailedError;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collections;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.io.Writer;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
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;
|
||||||
@ -70,13 +74,24 @@ public class ComputerTestDelegate
|
|||||||
private boolean finished = false;
|
private boolean finished = false;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before()
|
public void before() throws IOException
|
||||||
{
|
{
|
||||||
ComputerCraft.logPeripheralErrors = true;
|
ComputerCraft.logPeripheralErrors = true;
|
||||||
|
|
||||||
Terminal term = new Terminal( 78, 20 );
|
Terminal term = new Terminal( 78, 20 );
|
||||||
IWritableMount mount = new MemoryMount()
|
IWritableMount mount = new FileMount( new File( "test-files/mount" ), Long.MAX_VALUE );
|
||||||
.addFile( "startup.lua", "loadfile('test/mcfly.lua', _ENV)('test/spec') cct_test.finish()" );
|
|
||||||
|
// Remove any existing files
|
||||||
|
List<String> children = new ArrayList<>();
|
||||||
|
mount.list( "", children );
|
||||||
|
for( String child : children ) mount.delete( child );
|
||||||
|
|
||||||
|
// And add our startup file
|
||||||
|
try( WritableByteChannel channel = mount.openChannelForWrite( "startup.lua" );
|
||||||
|
Writer writer = Channels.newWriter( channel, StandardCharsets.UTF_8.newEncoder(), -1 ) )
|
||||||
|
{
|
||||||
|
writer.write( "loadfile('test/mcfly.lua', _ENV)('test/spec') cct_test.finish()" );
|
||||||
|
}
|
||||||
|
|
||||||
computer = new Computer( new BasicEnvironment( mount ), term, 0 );
|
computer = new Computer( new BasicEnvironment( mount ), term, 0 );
|
||||||
computer.addApi( new ILuaAPI()
|
computer.addApi( new ILuaAPI()
|
||||||
@ -400,6 +415,6 @@ public class ComputerTestDelegate
|
|||||||
|
|
||||||
private static String formatName( String name )
|
private static String formatName( String name )
|
||||||
{
|
{
|
||||||
return name.replace( "\0", " \u2192 " );
|
return name.replace( "\0", " -> " );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import org.junit.jupiter.api.Assertions;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,7 +116,7 @@ public class ComputerBootstrap
|
|||||||
@Override
|
@Override
|
||||||
public String[] getMethodNames()
|
public String[] getMethodNames()
|
||||||
{
|
{
|
||||||
return new String[] { "assert" };
|
return new String[] { "assert", "log" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -137,6 +138,9 @@ public class ComputerBootstrap
|
|||||||
|
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
case 1:
|
||||||
|
ComputerCraft.log.info( "[Computer] {}", Arrays.toString( arguments ) );
|
||||||
|
return null;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
@ -120,7 +120,7 @@ public class MemoryMount implements IWritableMount
|
|||||||
{
|
{
|
||||||
for( String file : this.files.keySet() )
|
for( String file : this.files.keySet() )
|
||||||
{
|
{
|
||||||
if( file.startsWith( path ) ) files.add( file );
|
if( file.startsWith( path ) ) files.add( file.substring( path.length() + 1 ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,44 @@ local function check(func, arg, ty, val)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local active_stubs = {}
|
||||||
|
|
||||||
|
--- Stub a global variable with a specific value
|
||||||
|
--
|
||||||
|
-- @tparam string var The variable to stub
|
||||||
|
-- @param value The value to stub it with
|
||||||
|
local function stub(tbl, var, value)
|
||||||
|
table.insert(active_stubs, { tbl = tbl, var = var, value = tbl[var] })
|
||||||
|
_G[var] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Capture the current global state of the computer
|
||||||
|
local function push_state()
|
||||||
|
local stubs = active_stubs
|
||||||
|
active_stubs = {}
|
||||||
|
return {
|
||||||
|
term = term.current(),
|
||||||
|
input = io.input(),
|
||||||
|
output = io.output(),
|
||||||
|
stubs = stubs,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Restore the global state of the computer to a previous version
|
||||||
|
local function pop_state(state)
|
||||||
|
for i = #active_stubs, 1, -1 do
|
||||||
|
local stub = active_stubs[i]
|
||||||
|
stub.tbl[stub.var] = stub.value
|
||||||
|
end
|
||||||
|
|
||||||
|
active_stubs = state.stubs
|
||||||
|
|
||||||
|
term.redirect(state.term)
|
||||||
|
io.input(state.input)
|
||||||
|
io.output(state.output)
|
||||||
|
end
|
||||||
|
|
||||||
local error_mt = { __tostring = function(self) return self.message end }
|
local error_mt = { __tostring = function(self) return self.message end }
|
||||||
|
|
||||||
--- Attempt to execute the provided function, gathering a stack trace when it
|
--- Attempt to execute the provided function, gathering a stack trace when it
|
||||||
@ -47,22 +85,19 @@ local function try(fn)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = xpcall(fn, function(err)
|
local ok, err = xpcall(fn, function(err)
|
||||||
return { message = err, trace = debug.traceback() }
|
return { message = err, trace = debug.traceback(nil, 2) }
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Restore a whole bunch of state
|
-- If we succeeded, propagate it
|
||||||
io.input(io.stdin)
|
|
||||||
io.output(io.stdout)
|
|
||||||
|
|
||||||
-- If we're an existing error, or we succeded then propagate it.
|
|
||||||
if ok then return ok, err end
|
if ok then return ok, err end
|
||||||
|
|
||||||
|
-- Error handling failed for some reason - just return a simpler error
|
||||||
if type(err) ~= "table" then
|
if type(err) ~= "table" then
|
||||||
return setmetatable({ message = tostring(err) }, error_mt)
|
return ok, setmetatable({ message = tostring(err) }, error_mt)
|
||||||
end
|
end
|
||||||
|
|
||||||
if getmetatable(err.message) == error_mt then return ok, err.message end
|
-- Find the common substring the errors' trace and the current one. Then
|
||||||
|
-- eliminate it.
|
||||||
-- Find the common substring between the two traces. Yes, this is horrible.
|
|
||||||
local trace = debug.traceback()
|
local trace = debug.traceback()
|
||||||
for i = 1, #trace do
|
for i = 1, #trace do
|
||||||
if trace:sub(-i) ~= err.trace:sub(-i) then
|
if trace:sub(-i) ~= err.trace:sub(-i) then
|
||||||
@ -71,6 +106,12 @@ local function try(fn)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- If we've received a rethrown error, copy
|
||||||
|
if getmetatable(err.message) == error_mt then
|
||||||
|
for k, v in pairs(err.message) do err[k] = v end
|
||||||
|
return ok, err
|
||||||
|
end
|
||||||
|
|
||||||
return ok, setmetatable(err, error_mt)
|
return ok, setmetatable(err, error_mt)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -189,13 +230,32 @@ function expect_mt:matches(value)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Construct a new expectation from the provided value
|
local expect = setmetatable( {
|
||||||
--
|
--- Construct an expectation on the error message calling this function
|
||||||
-- @param value The value to apply assertions to
|
-- produces
|
||||||
-- @return The new expectation
|
--
|
||||||
local function expect(value)
|
-- @tparam fun The function to call
|
||||||
return setmetatable({ value = value}, expect_mt)
|
-- @param ... The function arguments
|
||||||
end
|
-- @return The new expectation
|
||||||
|
error = function(fun, ...)
|
||||||
|
local ok, res = pcall(fun, ...) local _, line = pcall(error, "", 2)
|
||||||
|
if ok then fail("expected function to error") end
|
||||||
|
if res:sub(1, #line) == line then
|
||||||
|
res = res:sub(#line + 1)
|
||||||
|
elseif res:sub(1, 7) == "pcall: " then
|
||||||
|
res = res:sub(8)
|
||||||
|
end
|
||||||
|
return setmetatable({ value = res }, expect_mt)
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
--- Construct a new expectation from the provided value
|
||||||
|
--
|
||||||
|
-- @param value The value to apply assertions to
|
||||||
|
-- @return The new expectation
|
||||||
|
__call = function(_, value)
|
||||||
|
return setmetatable({ value = value }, expect_mt)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
--- The stack of "describe"s.
|
--- The stack of "describe"s.
|
||||||
local test_stack = { n = 0 }
|
local test_stack = { n = 0 }
|
||||||
@ -295,10 +355,15 @@ end
|
|||||||
|
|
||||||
do
|
do
|
||||||
-- Load in the tests from all our files
|
-- Load in the tests from all our files
|
||||||
local env = setmetatable({
|
local env = setmetatable({}, { __index = _ENV })
|
||||||
expect = expect, fail = fail,
|
|
||||||
describe = describe, it = it, pending = pending
|
local function set_env(tbl)
|
||||||
}, { __index = _ENV })
|
for k in pairs(env) do env[k] = nil end
|
||||||
|
for k, v in pairs(tbl) do env[k] = v end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- When declaring tests, you shouldn't be able to use test methods
|
||||||
|
set_env { describe = describe, it = it, pending = pending }
|
||||||
|
|
||||||
local suffix = "_spec.lua"
|
local suffix = "_spec.lua"
|
||||||
local function run_in(sub_dir)
|
local function run_in(sub_dir)
|
||||||
@ -319,6 +384,9 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
run_in(root_dir)
|
run_in(root_dir)
|
||||||
|
|
||||||
|
-- When running tests, you shouldn't be able to declare new ones.
|
||||||
|
set_env { expect = expect, fail = fail, stub = stub }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Error if we've found no tests
|
-- Error if we've found no tests
|
||||||
@ -352,9 +420,13 @@ local function do_run(test)
|
|||||||
err = test.error
|
err = test.error
|
||||||
status = "error"
|
status = "error"
|
||||||
elseif test.action then
|
elseif test.action then
|
||||||
|
local state = push_state()
|
||||||
|
|
||||||
local ok
|
local ok
|
||||||
ok, err = try(test.action)
|
ok, err = try(test.action)
|
||||||
status = ok and "pass" or (err.fail and "fail" or "error")
|
status = ok and "pass" or (err.fail and "fail" or "error")
|
||||||
|
|
||||||
|
pop_state(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If we've a boolean status, then convert it into a string
|
-- If we've a boolean status, then convert it into a string
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
describe("The colors library", function()
|
describe("The colors library", function()
|
||||||
it("colors.combine", function()
|
describe("colors.combine", function()
|
||||||
expect(colors.combine(colors.red, colors.brown, colors.green)):equals(0x7000)
|
it("validates arguments", function()
|
||||||
|
expect.error(colors.combine, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(colors.combine, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("combines colours", function()
|
||||||
|
expect(colors.combine()):eq(0)
|
||||||
|
expect(colors.combine(colors.red, colors.brown, colors.green)):eq(0x7000)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("colors.subtract", function()
|
describe("colors.subtract", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(colors.subtract, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(colors.subtract, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(colors.subtract, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
it("subtracts colours", function()
|
it("subtracts colours", function()
|
||||||
expect(colors.subtract(0x7000, colors.green)):equals(0x5000)
|
expect(colors.subtract(0x7000, colors.green)):equals(0x5000)
|
||||||
expect(colors.subtract(0x5000, colors.red)):equals(0x1000)
|
expect(colors.subtract(0x5000, colors.red)):equals(0x1000)
|
||||||
@ -17,6 +31,11 @@ describe("The colors library", function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe("colors.test", function()
|
describe("colors.test", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(colors.test, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(colors.test, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
it("returns true when present", function()
|
it("returns true when present", function()
|
||||||
expect(colors.test(0x7000, colors.green)):equals(true)
|
expect(colors.test(0x7000, colors.green)):equals(true)
|
||||||
end)
|
end)
|
||||||
@ -28,16 +47,30 @@ describe("The colors library", function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("colors.packRGB", function()
|
describe("colors.packRGB", function()
|
||||||
expect(colors.packRGB(0.3, 0.5, 0.6)):equals(0x4c7f99)
|
it("validates arguments", function()
|
||||||
|
expect.error(colors.packRGB, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(colors.packRGB, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(colors.packRGB, 1, 1, nil):eq("bad argument #3 (expected number, got nil)")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("colors.unpackRGB", function()
|
it("packs colours", function()
|
||||||
|
expect(colors.packRGB(0.3, 0.5, 0.6)):equals(0x4c7f99)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("colors.unpackRGB", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(colors.unpackRGB, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("unpacks colours", function()
|
||||||
expect({ colors.unpackRGB(0x4c7f99) }):same { 0x4c / 0xFF, 0x7f / 0xFF, 0.6 }
|
expect({ colors.unpackRGB(0x4c7f99) }):same { 0x4c / 0xFF, 0x7f / 0xFF, 0.6 }
|
||||||
end)
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
it("colors.rgb8", function()
|
it("colors.rgb8", 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)
|
||||||
end )
|
end)
|
||||||
|
14
src/test/resources/test-rom/spec/apis/fs_spec.lua
Normal file
14
src/test/resources/test-rom/spec/apis/fs_spec.lua
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
describe("The fs library", function()
|
||||||
|
describe("fs.complete", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
fs.complete("", "")
|
||||||
|
fs.complete("", "", true)
|
||||||
|
fs.complete("", "", nil, true)
|
||||||
|
|
||||||
|
expect.error(fs.complete, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(fs.complete, "", nil):eq("bad argument #2 (expected string, got nil)")
|
||||||
|
expect.error(fs.complete, "", "", 1):eq("bad argument #3 (expected boolean, got number)")
|
||||||
|
expect.error(fs.complete, "", "", true, 1):eq("bad argument #4 (expected boolean, got number)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
15
src/test/resources/test-rom/spec/apis/gps_spec.lua
Normal file
15
src/test/resources/test-rom/spec/apis/gps_spec.lua
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
describe("The gps library", function()
|
||||||
|
describe("gps.locate", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
stub(_G, "commands", { getBlockPosition = function()
|
||||||
|
end })
|
||||||
|
|
||||||
|
gps.locate()
|
||||||
|
gps.locate(1)
|
||||||
|
gps.locate(1, true)
|
||||||
|
|
||||||
|
expect.error(gps.locate, ""):eq("bad argument #1 (expected number, got string)")
|
||||||
|
expect.error(gps.locate, 1, ""):eq("bad argument #2 (expected boolean, got string)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
22
src/test/resources/test-rom/spec/apis/help_spec.lua
Normal file
22
src/test/resources/test-rom/spec/apis/help_spec.lua
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
describe("The help library", function()
|
||||||
|
describe("help.setPath", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
help.setPath(help.path())
|
||||||
|
expect.error(help.setPath, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("help.lookup", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
help.lookup("")
|
||||||
|
expect.error(help.lookup, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("help.completeTopic", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
help.completeTopic("")
|
||||||
|
expect.error(help.completeTopic, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
@ -18,13 +18,32 @@ describe("The io library", function()
|
|||||||
expect(io.type(handle)):equals("file")
|
expect(io.type(handle)):equals("file")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("returns nil on values", function() expect(io.type(8)):equals(nil) end)
|
it("returns nil on values", function()
|
||||||
|
expect(io.type(8)):equals(nil)
|
||||||
|
end)
|
||||||
|
|
||||||
it("returns nil on tables", function()
|
it("returns nil on tables", function()
|
||||||
expect(io.type(setmetatable({}, {}))):equals(nil)
|
expect(io.type(setmetatable({}, {}))):equals(nil)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("io.lines", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
io.lines(nil)
|
||||||
|
expect.error(io.lines, ""):eq("/: No such file")
|
||||||
|
expect.error(io.lines, false):eq("bad argument #1 (expected string, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
describe("io.open", function()
|
describe("io.open", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
io.open("")
|
||||||
|
io.open("", "r")
|
||||||
|
|
||||||
|
expect.error(io.open, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(io.open, "", false):eq("bad argument #2 (expected string, got boolean)")
|
||||||
|
end)
|
||||||
|
|
||||||
it("returns an error message on non-existent files", function()
|
it("returns an error message on non-existent files", function()
|
||||||
local a, b = io.open('xuxu_nao_existe')
|
local a, b = io.open('xuxu_nao_existe')
|
||||||
expect(a):equals(nil)
|
expect(a):equals(nil)
|
||||||
@ -42,7 +61,7 @@ describe("The io library", function()
|
|||||||
expect(io.output():seek()):equal(0)
|
expect(io.output():seek()):equal(0)
|
||||||
assert(io.write("alo alo"))
|
assert(io.write("alo alo"))
|
||||||
expect(io.output():seek()):equal(#("alo alo"))
|
expect(io.output():seek()):equal(#("alo alo"))
|
||||||
expect(io.output():seek("cur", -3)):equal(#("alo alo")-3)
|
expect(io.output():seek("cur", -3)):equal(#("alo alo") - 3)
|
||||||
assert(io.write("joao"))
|
assert(io.write("joao"))
|
||||||
expect(io.output():seek("end"):equal(#("alo joao")))
|
expect(io.output():seek("end"):equal(#("alo joao")))
|
||||||
|
|
||||||
|
8
src/test/resources/test-rom/spec/apis/keys_spec.lua
Normal file
8
src/test/resources/test-rom/spec/apis/keys_spec.lua
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
describe("The keys library", function()
|
||||||
|
describe("keys.getName", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
keys.getName(1)
|
||||||
|
expect.error(keys.getName, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
133
src/test/resources/test-rom/spec/apis/os_spec.lua
Normal file
133
src/test/resources/test-rom/spec/apis/os_spec.lua
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
describe("The os library", function()
|
||||||
|
describe("os.date and os.time", function()
|
||||||
|
it("round trips correctly", function()
|
||||||
|
local t = math.floor(os.epoch("local") / 1000)
|
||||||
|
local T = os.date("*t", t)
|
||||||
|
|
||||||
|
expect(os.time(T)):eq(t)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("dst field is guessed", function()
|
||||||
|
local T = os.date("*t")
|
||||||
|
local t = os.time(T)
|
||||||
|
expect(T.isdst):type("boolean")
|
||||||
|
T.isdst = nil
|
||||||
|
expect(os.time(T)):eq(t) -- if isdst is absent uses correct default
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("has 365 days in a year", function()
|
||||||
|
local T = os.date("*t")
|
||||||
|
local t = os.time(T)
|
||||||
|
T.year = T.year - 1
|
||||||
|
local t1 = os.time(T)
|
||||||
|
local delta = (t - t1) / (24 * 3600) - 365
|
||||||
|
-- allow for leap years
|
||||||
|
assert(math.abs(delta) < 2, ("expected abs(%d )< 2"):format(delta))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("os.date uses local timezone", function()
|
||||||
|
local epoch = os.epoch("local") / 1000
|
||||||
|
local date = os.time(os.date("*t"))
|
||||||
|
assert(date - epoch <= 2, ("expected %d - %d <= 2, but not the case"):format(date, epoch))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("os.date", function()
|
||||||
|
it("formats as expected", function()
|
||||||
|
-- From the PUC Lua tests, hence the weird style
|
||||||
|
local t = os.epoch("local")
|
||||||
|
local T = os.date("*t", t)
|
||||||
|
|
||||||
|
_G.T = T
|
||||||
|
loadstring(os.date([[assert(T.year==%Y and T.month==%m and T.day==%d and
|
||||||
|
T.hour==%H and T.min==%M and T.sec==%S and
|
||||||
|
T.wday==%w+1 and T.yday==%j and type(T.isdst) == 'boolean')]], t))()
|
||||||
|
|
||||||
|
T = os.date("!*t", t)
|
||||||
|
_G.T = T
|
||||||
|
loadstring(os.date([[!assert(T.year==%Y and T.month==%m and T.day==%d and
|
||||||
|
T.hour==%H and T.min==%M and T.sec==%S and
|
||||||
|
T.wday==%w+1 and T.yday==%j and type(T.isdst) == 'boolean')]], t))()
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("produces output consistent with PUC Lua", function()
|
||||||
|
-- Create a separate test for each code, just so it's easier to see what's broken
|
||||||
|
local t1 = os.time { year = 2000, month = 10, day = 1, hour = 23, min = 12, sec = 17 }
|
||||||
|
local function exp_code(code, value)
|
||||||
|
it(("for code '%s'"):format(code), function()
|
||||||
|
expect(os.date(code, t1)):eq(value)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
exp_code("%a", "Sun")
|
||||||
|
exp_code("%A", "Sunday")
|
||||||
|
exp_code("%b", "Oct")
|
||||||
|
exp_code("%B", "October")
|
||||||
|
exp_code("%c", "Sun Oct 1 23:12:17 2000")
|
||||||
|
exp_code("%C", "20")
|
||||||
|
exp_code("%d", "01")
|
||||||
|
exp_code("%D", "10/01/00")
|
||||||
|
exp_code("%e", " 1")
|
||||||
|
exp_code("%F", "2000-10-01")
|
||||||
|
exp_code("%g", "00")
|
||||||
|
exp_code("%G", "2000")
|
||||||
|
exp_code("%h", "Oct")
|
||||||
|
exp_code("%H", "23")
|
||||||
|
exp_code("%I", "11")
|
||||||
|
exp_code("%j", "275")
|
||||||
|
exp_code("%m", "10")
|
||||||
|
exp_code("%M", "12")
|
||||||
|
exp_code("%n", "\n")
|
||||||
|
exp_code("%p", "PM")
|
||||||
|
exp_code("%r", "11:12:17 PM")
|
||||||
|
exp_code("%R", "23:12")
|
||||||
|
exp_code("%S", "17")
|
||||||
|
exp_code("%t", "\t")
|
||||||
|
exp_code("%T", "23:12:17")
|
||||||
|
exp_code("%u", "7")
|
||||||
|
exp_code("%U", "40")
|
||||||
|
exp_code("%V", "39")
|
||||||
|
exp_code("%w", "0")
|
||||||
|
exp_code("%W", "39")
|
||||||
|
exp_code("%x", "10/01/00")
|
||||||
|
exp_code("%X", "23:12:17")
|
||||||
|
exp_code("%y", "00")
|
||||||
|
exp_code("%Y", "2000")
|
||||||
|
exp_code("%%", "%")
|
||||||
|
|
||||||
|
it("zones are numbers", function()
|
||||||
|
local zone = os.date("%z", t1)
|
||||||
|
if not zone:match("^[+-]%d%d%d%d$") then
|
||||||
|
error("Invalid zone: " .. zone)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("zones id is made of letters", function()
|
||||||
|
local zone = os.date("%Z", t1)
|
||||||
|
if not zone:match("^%a%a+$") then
|
||||||
|
error("Non letter character in zone: " .. zone)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("os.time", function()
|
||||||
|
it("maps directly to seconds", function()
|
||||||
|
local t1 = os.time { year = 2000, month = 10, day = 1, hour = 23, min = 12, sec = 17 }
|
||||||
|
local t2 = os.time { year = 2000, month = 10, day = 1, hour = 23, min = 10, sec = 19 }
|
||||||
|
expect(t1 - t2):eq(60 * 2 - 2)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("os.loadAPI", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(os.loadAPI, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("os.unloadAPI", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(os.loadAPI, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
60
src/test/resources/test-rom/spec/apis/paintutils_spec.lua
Normal file
60
src/test/resources/test-rom/spec/apis/paintutils_spec.lua
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
describe("The paintutils library", function()
|
||||||
|
describe("paintutils.parseImage", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
paintutils.parseImage("")
|
||||||
|
expect.error(paintutils.parseImage, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("paintutils.loadImage", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(paintutils.loadImage, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("paintutils.drawPixel", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(paintutils.drawPixel, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawPixel, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawPixel, 1, 1, false):eq("bad argument #3 (expected number, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("paintutils.drawLine", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(paintutils.drawLine, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawLine, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawLine, 1, 1, nil):eq("bad argument #3 (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)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("paintutils.drawBox", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(paintutils.drawBox, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawBox, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawBox, 1, 1, nil):eq("bad argument #3 (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)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("paintutils.drawFilledBox", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(paintutils.drawFilledBox, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawFilledBox, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawFilledBox, 1, 1, nil):eq("bad argument #3 (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)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("paintutils.drawImage", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(paintutils.drawImage, nil):eq("bad argument #1 (expected table, got nil)")
|
||||||
|
expect.error(paintutils.drawImage, {}, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(paintutils.drawImage, {}, 1, nil):eq("bad argument #3 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
47
src/test/resources/test-rom/spec/apis/peripheral_spec.lua
Normal file
47
src/test/resources/test-rom/spec/apis/peripheral_spec.lua
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
describe("The peripheral library", function()
|
||||||
|
describe("peripheral.isPresent", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
peripheral.isPresent("")
|
||||||
|
expect.error(peripheral.isPresent, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("peripheral.getType", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
peripheral.getType("")
|
||||||
|
expect.error(peripheral.getType, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("peripheral.getMethods", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
peripheral.getMethods("")
|
||||||
|
expect.error(peripheral.getMethods, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("peripheral.call", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
peripheral.call("", "")
|
||||||
|
expect.error(peripheral.call, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(peripheral.call, "", nil):eq("bad argument #2 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("peripheral.wrap", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
peripheral.wrap("")
|
||||||
|
expect.error(peripheral.wrap, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("peripheral.find", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
peripheral.find("")
|
||||||
|
peripheral.find("", function()
|
||||||
|
end)
|
||||||
|
expect.error(peripheral.find, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(peripheral.find, "", false):eq("bad argument #2 (expected function, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
86
src/test/resources/test-rom/spec/apis/rednet_spec.lua
Normal file
86
src/test/resources/test-rom/spec/apis/rednet_spec.lua
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
describe("The rednet library", function()
|
||||||
|
describe("rednet.open", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(rednet.open, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("requires a modem to be present", function()
|
||||||
|
expect.error(rednet.open, "not_there"):eq("No such modem: not_there")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.close", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
rednet.close()
|
||||||
|
expect.error(rednet.close, 1):eq("bad argument #1 (expected string, got number)")
|
||||||
|
expect.error(rednet.close, false):eq("bad argument #1 (expected string, got boolean)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("requires a modem to be present", function()
|
||||||
|
expect.error(rednet.close, "not_there"):eq("No such modem: not_there")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.isOpen", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
rednet.isOpen()
|
||||||
|
rednet.isOpen("")
|
||||||
|
expect.error(rednet.isOpen, 1):eq("bad argument #1 (expected string, got number)")
|
||||||
|
expect.error(rednet.isOpen, false):eq("bad argument #1 (expected string, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.send", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
rednet.send(1)
|
||||||
|
rednet.send(1, nil, "")
|
||||||
|
expect.error(rednet.send, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(rednet.send, 1, nil, false):eq("bad argument #3 (expected string, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.broadcast", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
rednet.broadcast(nil)
|
||||||
|
rednet.broadcast(nil, "")
|
||||||
|
expect.error(rednet.broadcast, nil, false):eq("bad argument #2 (expected string, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.receive", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(rednet.receive, false):eq("bad argument #1 (expected string, got boolean)")
|
||||||
|
expect.error(rednet.receive, "", false):eq("bad argument #2 (expected number, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.host", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(rednet.host, "", "localhost"):eq("Reserved hostname")
|
||||||
|
expect.error(rednet.host, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(rednet.host, "", nil):eq("bad argument #2 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.unhost", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
rednet.unhost("")
|
||||||
|
expect.error(rednet.unhost, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("rednet.lookup", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(rednet.lookup, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(rednet.lookup, "", false):eq("bad argument #2 (expected string, got boolean)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("gets a locally hosted protocol", function()
|
||||||
|
rednet.host("a_protocol", "a_hostname")
|
||||||
|
|
||||||
|
expect(rednet.lookup("a_protocol")):eq(os.getComputerID())
|
||||||
|
expect(rednet.lookup("a_protocol", "localhost")):eq(os.getComputerID())
|
||||||
|
expect(rednet.lookup("a_protocol", "a_hostname")):eq(os.getComputerID())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
43
src/test/resources/test-rom/spec/apis/settings_spec.lua
Normal file
43
src/test/resources/test-rom/spec/apis/settings_spec.lua
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
describe("The settings library", function()
|
||||||
|
describe("settings.set", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
settings.set("test", 1)
|
||||||
|
settings.set("test", "")
|
||||||
|
settings.set("test", {})
|
||||||
|
settings.set("test", false)
|
||||||
|
|
||||||
|
expect.error(settings.set, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(settings.set, "", nil):eq("bad argument #2 (expected number, string, boolean or table, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("prevents storing unserialisable types", function()
|
||||||
|
expect.error(settings.set, "", { print }):eq("Cannot serialize type function")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("settings.get", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
settings.get("test")
|
||||||
|
expect.error(settings.get, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("settings.unset", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
settings.unset("test")
|
||||||
|
expect.error(settings.unset, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("settings.load", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(settings.load, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("settings.save", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(settings.save, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
12
src/test/resources/test-rom/spec/apis/term_spec.lua
Normal file
12
src/test/resources/test-rom/spec/apis/term_spec.lua
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
describe("The term library", function()
|
||||||
|
describe("term.redirect", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(term.redirect, nil):eq("bad argument #1 (expected table, got nil)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("prevents redirecting to term", function()
|
||||||
|
expect.error(term.redirect, term)
|
||||||
|
:eq("term is not a recommended redirect target, try term.current() instead")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
90
src/test/resources/test-rom/spec/apis/textutils_spec.lua
Normal file
90
src/test/resources/test-rom/spec/apis/textutils_spec.lua
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
describe("The textutils library", function()
|
||||||
|
describe("textutils.slowWrite", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(textutils.slowWrite, nil, false):eq("bad argument #2 (expected number, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.formatTime", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
textutils.formatTime(0)
|
||||||
|
textutils.formatTime(0, false)
|
||||||
|
expect.error(textutils.formatTime, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(textutils.formatTime, 1, 1):eq("bad argument #2 (expected boolean, got number)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.pagedPrint", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(textutils.pagedPrint, nil, false):eq("bad argument #2 (expected number, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.tabulate", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
term.redirect(window.create(term.current(), 1, 1, 5, 5, false))
|
||||||
|
|
||||||
|
textutils.tabulate()
|
||||||
|
textutils.tabulate({ "test" })
|
||||||
|
textutils.tabulate(colors.white)
|
||||||
|
|
||||||
|
expect.error(textutils.tabulate, nil):eq("bad argument #1 (expected number or table, got nil)")
|
||||||
|
expect.error(textutils.tabulate, { "test" }, nil):eq("bad argument #2 (expected number or table, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.pagedTabulate", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
term.redirect(window.create(term.current(), 1, 1, 5, 5, false))
|
||||||
|
|
||||||
|
textutils.pagedTabulate()
|
||||||
|
textutils.pagedTabulate({ "test" })
|
||||||
|
textutils.pagedTabulate(colors.white)
|
||||||
|
|
||||||
|
expect.error(textutils.pagedTabulate, nil):eq("bad argument #1 (expected number or table, got nil)")
|
||||||
|
expect.error(textutils.pagedTabulate, { "test" }, nil):eq("bad argument #2 (expected number or table, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.empty_json_array", function()
|
||||||
|
it("is immutable", function()
|
||||||
|
expect.error(function() textutils.empty_json_array[1] = true end)
|
||||||
|
:eq("textutils_spec.lua:51: attempt to mutate textutils.empty_json_array")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.unserialise", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
textutils.unserialise("")
|
||||||
|
expect.error(textutils.unserialise, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.serialiseJSON", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
textutils.serialiseJSON("")
|
||||||
|
textutils.serialiseJSON(1)
|
||||||
|
textutils.serialiseJSON({})
|
||||||
|
textutils.serialiseJSON(false)
|
||||||
|
textutils.serialiseJSON("", true)
|
||||||
|
expect.error(textutils.serialiseJSON, nil):eq("bad argument #1 (expected table, string, number or boolean, got nil)")
|
||||||
|
expect.error(textutils.serialiseJSON, "", 1):eq("bad argument #2 (expected boolean, got number)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.urlEncode", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
textutils.urlEncode("")
|
||||||
|
expect.error(textutils.urlEncode, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("textutils.complete", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
textutils.complete("pri")
|
||||||
|
textutils.complete("pri", _G)
|
||||||
|
expect.error(textutils.complete, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(textutils.complete, "", false):eq("bad argument #2 (expected table, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
123
src/test/resources/test-rom/spec/apis/window_spec.lua
Normal file
123
src/test/resources/test-rom/spec/apis/window_spec.lua
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
describe("The window library", function()
|
||||||
|
local function mk()
|
||||||
|
return window.create(term.current(), 1, 1, 5, 5, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe("window.create", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local r = mk()
|
||||||
|
window.create(r, 1, 1, 5, 5)
|
||||||
|
window.create(r, 1, 1, 5, 5, false)
|
||||||
|
|
||||||
|
expect.error(window.create, nil):eq("bad argument #1 (expected table, got nil)")
|
||||||
|
expect.error(window.create, r, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(window.create, r, 1, nil):eq("bad argument #3 (expected number, got nil)")
|
||||||
|
expect.error(window.create, r, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
|
||||||
|
expect.error(window.create, r, 1, 1, 1, nil):eq("bad argument #5 (expected number, got nil)")
|
||||||
|
expect.error(window.create, r, 1, 1, 1, 1, ""):eq("bad argument #6 (expected boolean, got string)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.blit", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.blit("a", "a", "a")
|
||||||
|
|
||||||
|
expect.error(w.blit, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(w.blit, "", nil):eq("bad argument #2 (expected string, got nil)")
|
||||||
|
expect.error(w.blit, "", "", nil):eq("bad argument #3 (expected string, got nil)")
|
||||||
|
expect.error(w.blit, "", "", "a"):eq("Arguments must be the same length")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.setCursorPos", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.setCursorPos(1, 1)
|
||||||
|
|
||||||
|
expect.error(w.setCursorPos, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(w.setCursorPos, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.setCursorBlink", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.setCursorBlink(false)
|
||||||
|
expect.error(w.setCursorBlink, nil):eq("bad argument #1 (expected boolean, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.setTextColour", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.setTextColour(colors.white)
|
||||||
|
|
||||||
|
expect.error(w.setTextColour, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(w.setTextColour, -5):eq("Invalid color (got -5)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.setPaletteColour", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.setPaletteColour(colors.white, 0, 0, 0)
|
||||||
|
w.setPaletteColour(colors.white, 0x000000)
|
||||||
|
|
||||||
|
expect.error(w.setPaletteColour, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(w.setPaletteColour, -5):eq("Invalid color (got -5)")
|
||||||
|
expect.error(w.setPaletteColour, colors.white):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(w.setPaletteColour, colors.white, 1, false):eq("bad argument #3 (expected number, got boolean)")
|
||||||
|
expect.error(w.setPaletteColour, colors.white, 1, nil, 1):eq("bad argument #3 (expected number, got nil)")
|
||||||
|
expect.error(w.setPaletteColour, colors.white, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.getPaletteColour", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.getPaletteColour(colors.white)
|
||||||
|
expect.error(w.getPaletteColour, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(w.getPaletteColour, -5):eq("Invalid color (got -5)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.setBackgroundColour", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.setBackgroundColour(colors.white)
|
||||||
|
|
||||||
|
expect.error(w.setBackgroundColour, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(w.setBackgroundColour, -5):eq("Invalid color (got -5)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.scroll", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.scroll(0)
|
||||||
|
expect.error(w.scroll, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.setVisible", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.setVisible(false)
|
||||||
|
expect.error(w.setVisible, nil):eq("bad argument #1 (expected boolean, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("Window.reposition", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
local w = mk()
|
||||||
|
w.reposition(1, 1)
|
||||||
|
w.reposition(1, 1, 5, 5)
|
||||||
|
expect.error(w.reposition, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(w.reposition, 1, nil):eq("bad argument #2 (expected number, got nil)")
|
||||||
|
expect.error(w.reposition, 1, 1, false, 1):eq("bad argument #3 (expected number, got boolean)")
|
||||||
|
expect.error(w.reposition, 1, 1, nil, 1):eq("bad argument #3 (expected number, got nil)")
|
||||||
|
expect.error(w.reposition, 1, 1, 1, nil):eq("bad argument #4 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
@ -1,11 +1,74 @@
|
|||||||
describe("The Lua base library", function()
|
describe("The Lua base library", function()
|
||||||
|
describe("expect", function()
|
||||||
|
local e = _G["~expect"]
|
||||||
|
|
||||||
|
it("checks a single type", function()
|
||||||
|
expect(e(1, "test", "string")):eq(true)
|
||||||
|
expect(e(1, 2, "number")):eq(true)
|
||||||
|
|
||||||
|
expect.error(e, 1, nil, "string"):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(e, 2, 1, "nil"):eq("bad argument #2 (expected nil, got number)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("checks multiple types", function()
|
||||||
|
expect(e(1, "test", "string", "number")):eq(true)
|
||||||
|
expect(e(1, 2, "string", "number")):eq(true)
|
||||||
|
|
||||||
|
expect.error(e, 1, nil, "string", "number"):eq("bad argument #1 (expected string or number, got nil)")
|
||||||
|
expect.error(e, 2, false, "string", "table", "number", "nil")
|
||||||
|
:eq("bad argument #2 (expected string, table or number, got boolean)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("includes the function name", function()
|
||||||
|
local function worker()
|
||||||
|
expect(e(1, nil, "string")):eq(true)
|
||||||
|
end
|
||||||
|
local function trampoline()
|
||||||
|
worker()
|
||||||
|
end
|
||||||
|
|
||||||
|
expect.error(trampoline):eq("base_spec.lua:27: bad argument #1 to 'worker' (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("sleep", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
sleep(0)
|
||||||
|
sleep(nil)
|
||||||
|
|
||||||
|
expect.error(sleep, false):eq("bad argument #1 (expected number, got boolean)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("write", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
write("")
|
||||||
|
expect.error(write, nil):eq("bad argument #1 (expected string or number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
describe("loadfile", function()
|
describe("loadfile", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
loadfile("")
|
||||||
|
loadfile("", {})
|
||||||
|
|
||||||
|
expect.error(loadfile, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(loadfile, "", false):eq("bad argument #2 (expected table, got boolean)")
|
||||||
|
end)
|
||||||
|
|
||||||
it("prefixes the filename with @", function()
|
it("prefixes the filename with @", function()
|
||||||
local info = debug.getinfo(loadfile("/rom/startup.lua"), "S")
|
local info = debug.getinfo(loadfile("/rom/startup.lua"), "S")
|
||||||
expect(info):matches { short_src = "startup.lua", source = "@startup.lua" }
|
expect(info):matches { short_src = "startup.lua", source = "@startup.lua" }
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe("dofile", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(dofile, ""):eq("File not found")
|
||||||
|
expect.error(dofile, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
describe("loadstring", function()
|
describe("loadstring", function()
|
||||||
it("prefixes the chunk name with '='", function()
|
it("prefixes the chunk name with '='", function()
|
||||||
local info = debug.getinfo(loadstring("return 1", "name"), "S")
|
local info = debug.getinfo(loadstring("return 1", "name"), "S")
|
||||||
@ -27,9 +90,25 @@ describe("The Lua base library", function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe("load", function()
|
describe("load", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
load("")
|
||||||
|
load(function()
|
||||||
|
end)
|
||||||
|
load("", "")
|
||||||
|
load("", "", "")
|
||||||
|
load("", "", "", _ENV)
|
||||||
|
|
||||||
|
expect.error(load, nil):eq("bad argument #1 (expected function or string, got nil)")
|
||||||
|
expect.error(load, "", false):eq("bad argument #2 (expected string, got boolean)")
|
||||||
|
expect.error(load, "", "", false):eq("bad argument #3 (expected string, got boolean)")
|
||||||
|
expect.error(load, "", "", "", false):eq("bad argument #4 (expected table, got boolean)")
|
||||||
|
end)
|
||||||
|
|
||||||
local function generator(parts)
|
local function generator(parts)
|
||||||
return coroutine.wrap(function()
|
return coroutine.wrap(function()
|
||||||
for i = 1, #parts do coroutine.yield(parts[i]) end
|
for i = 1, #parts do
|
||||||
|
coroutine.yield(parts[i])
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
describe("The multishell program", function()
|
||||||
|
describe("multishell.setFocus", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
multishell.setFocus(multishell.getFocus())
|
||||||
|
expect.error(multishell.setFocus, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("multishell.getTitle", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
multishell.getTitle(1)
|
||||||
|
expect.error(multishell.getTitle, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("multishell.setTitle", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
multishell.setTitle(1, multishell.getTitle(1))
|
||||||
|
expect.error(multishell.setTitle, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
expect.error(multishell.setTitle, 1, nil):eq("bad argument #2 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("multishell.launch", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(multishell.launch, nil):eq("bad argument #1 (expected table, got nil)")
|
||||||
|
expect.error(multishell.launch, _ENV, nil):eq("bad argument #2 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
35
src/test/resources/test-rom/spec/programs/delete_spec.lua
Normal file
35
src/test/resources/test-rom/spec/programs/delete_spec.lua
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
describe("The rm program", function()
|
||||||
|
local function touch(file)
|
||||||
|
io.open(file, "w"):close()
|
||||||
|
end
|
||||||
|
|
||||||
|
it("deletes one file", function()
|
||||||
|
touch("/test-files/a.txt")
|
||||||
|
|
||||||
|
shell.run("rm /test-files/a.txt")
|
||||||
|
|
||||||
|
expect(fs.exists("/test-files/a.txt")):eq(false)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("deletes many files", function()
|
||||||
|
touch("/test-files/a.txt")
|
||||||
|
touch("/test-files/b.txt")
|
||||||
|
touch("/test-files/c.txt")
|
||||||
|
|
||||||
|
shell.run("rm /test-files/a.txt /test-files/b.txt")
|
||||||
|
|
||||||
|
expect(fs.exists("/test-files/a.txt")):eq(false)
|
||||||
|
expect(fs.exists("/test-files/b.txt")):eq(false)
|
||||||
|
expect(fs.exists("/test-files/c.txt")):eq(true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("deletes a glob", function()
|
||||||
|
touch("/test-files/a.txt")
|
||||||
|
touch("/test-files/b.txt")
|
||||||
|
|
||||||
|
shell.run("rm /test-files/*.txt")
|
||||||
|
|
||||||
|
expect(fs.exists("/test-files/a.txt")):eq(false)
|
||||||
|
expect(fs.exists("/test-files/b.txt")):eq(false)
|
||||||
|
end)
|
||||||
|
end)
|
29
src/test/resources/test-rom/spec/programs/mkdir_spec.lua
Normal file
29
src/test/resources/test-rom/spec/programs/mkdir_spec.lua
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
describe("The mkdir program", function()
|
||||||
|
it("creates a directory", function()
|
||||||
|
fs.delete("/test-files")
|
||||||
|
|
||||||
|
shell.run("mkdir /test-files/a")
|
||||||
|
|
||||||
|
expect(fs.isDir("/test-files/a")):eq(true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("creates many directories", function()
|
||||||
|
fs.delete("/test-files")
|
||||||
|
|
||||||
|
shell.run("mkdir /test-files/a /test-files/b")
|
||||||
|
|
||||||
|
expect(fs.isDir("/test-files/a")):eq(true)
|
||||||
|
expect(fs.isDir("/test-files/b")):eq(true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can be completed", function()
|
||||||
|
fs.delete("/test-files")
|
||||||
|
fs.makeDir("/test-files/a")
|
||||||
|
fs.makeDir("/test-files/b")
|
||||||
|
io.open("/test-files.a.txt", "w"):close()
|
||||||
|
|
||||||
|
local complete = shell.getCompletionInfo()["rom/programs/mkdir.lua"].fnComplete
|
||||||
|
expect(complete(shell, 1, "/test-files/", {})):same { "a/", "b/" }
|
||||||
|
expect(complete(shell, 2, "/test-files/", { "/" })):same { "a/", "b/" }
|
||||||
|
end)
|
||||||
|
end)
|
94
src/test/resources/test-rom/spec/programs/shell_spec.lua
Normal file
94
src/test/resources/test-rom/spec/programs/shell_spec.lua
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
describe("The shell", function()
|
||||||
|
describe("require", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
require("math")
|
||||||
|
expect.error(require, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.run", function()
|
||||||
|
it("sets the arguments", function()
|
||||||
|
local handle = fs.open("test-files/out.txt", "w")
|
||||||
|
handle.writeLine("_G.__arg = arg")
|
||||||
|
handle.close()
|
||||||
|
|
||||||
|
shell.run("/test-files/out.txt", "arg1", "arg2")
|
||||||
|
fs.delete("test-files/out.txt")
|
||||||
|
|
||||||
|
local args = _G.__arg
|
||||||
|
_G.__arg = nil
|
||||||
|
|
||||||
|
expect(args):same { [0] = "/test-files/out.txt", "arg1", "arg2" }
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.setDir", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
shell.setDir(shell.dir())
|
||||||
|
expect.error(shell.setDir, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.setPath", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
shell.setPath(shell.path())
|
||||||
|
expect.error(shell.setPath, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.resolve", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
shell.resolve("")
|
||||||
|
expect.error(shell.resolve, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.resolveProgram", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
shell.resolveProgram("ls")
|
||||||
|
expect.error(shell.resolveProgram, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.complete", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
shell.complete("ls")
|
||||||
|
expect.error(shell.complete, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.setCompletionFunction", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(shell.setCompletionFunction, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(shell.setCompletionFunction, "", nil):eq("bad argument #2 (expected function, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.setCompletionFunction", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(shell.setCompletionFunction, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(shell.setCompletionFunction, "", nil):eq("bad argument #2 (expected function, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.setAlias", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
shell.setAlias("sl", "ls")
|
||||||
|
expect.error(shell.setAlias, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
expect.error(shell.setAlias, "", nil):eq("bad argument #2 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.clearAlias", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
shell.clearAlias("sl")
|
||||||
|
expect.error(shell.clearAlias, nil):eq("bad argument #1 (expected string, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("shell.switchTab", function()
|
||||||
|
it("validates arguments", function()
|
||||||
|
expect.error(shell.switchTab, nil):eq("bad argument #1 (expected number, got nil)")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
Loading…
Reference in New Issue
Block a user