mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-01 06:03:00 +00:00
Compare commits
25 Commits
v1.15.2-1.
...
v1.15.2-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
748ebbe66b | ||
|
|
59de21eae2 | ||
|
|
50473afea8 | ||
|
|
37f925de0a | ||
|
|
cefde3f003 | ||
|
|
ae6124d1f4 | ||
|
|
7e121ff72f | ||
|
|
5155e18de2 | ||
|
|
7365741088 | ||
|
|
d5368d0719 | ||
|
|
04509cefec | ||
|
|
74b9f5dcb0 | ||
|
|
183b342071 | ||
|
|
0bb5515055 | ||
|
|
9acfc0316f | ||
|
|
29fb0baa09 | ||
|
|
d5de39ebd4 | ||
|
|
0faf76e4bd | ||
|
|
e8e2ed9fe5 | ||
|
|
9f72448ecd | ||
|
|
3da3f16deb | ||
|
|
0e2ce3c634 | ||
|
|
fe00e00537 | ||
|
|
cd879b067f | ||
|
|
053cb1b53c |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -12,4 +12,5 @@ labels: bug
|
||||
## Useful information to include:
|
||||
- Minecraft version
|
||||
- CC: Tweaked version
|
||||
- Logs: These will be located in the `logs/` directory of your Minecraft instance. Please upload them as a gist or directly into this editor.
|
||||
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
||||
|
||||
10
README.md
10
README.md
@@ -1,4 +1,4 @@
|
||||
# 
|
||||
# 
|
||||
[](https://github.com/SquidDev-CC/CC-Tweaked/actions "Current build status") [](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,
|
||||
@@ -50,12 +50,12 @@ I'd generally recommend you don't contact me directly (email, DM, etc...) unless
|
||||
report exploits). You'll get a far quicker response if you ask the whole community!
|
||||
|
||||
## Using
|
||||
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
|
||||
using [cc.crzd.me's maven](https://cc.crzd.me/maven/) instead.
|
||||
CC: Tweaked is hosted on my maven repo, and so is relatively simple to depend on. You may wish to add a soft (or hard)
|
||||
dependency in your `mods.toml` file, with the appropriate version bounds, to ensure that API functionality you depend
|
||||
on is present.
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
repositories {
|
||||
maven { url 'https://squiddev.cc/maven/' }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Mod properties
|
||||
mod_version=1.90.2
|
||||
mod_version=1.92.0
|
||||
|
||||
# Minecraft properties (update mods.toml when changing)
|
||||
mc_version=1.15.2
|
||||
|
||||
@@ -101,6 +101,9 @@
|
||||
(linters -doc:unresolved-reference))
|
||||
|
||||
(at /src/test/resources/test-rom
|
||||
; We should still be able to test deprecated members.
|
||||
(linters -var:deprecated)
|
||||
|
||||
(lint
|
||||
(globals
|
||||
:max sleep write
|
||||
|
||||
@@ -39,6 +39,7 @@ public final class ComputerCraft
|
||||
public static final String[] DEFAULT_HTTP_ALLOW = new String[] { "*" };
|
||||
public static final String[] DEFAULT_HTTP_DENY = new String[] {
|
||||
"127.0.0.0/8",
|
||||
"0.0.0.0/8",
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
"192.168.0.0/16",
|
||||
|
||||
@@ -479,6 +479,7 @@ public class FSAPI implements ILuaAPI
|
||||
BasicFileAttributes attributes = fileSystem.getAttributes( path );
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put( "modification", getFileTime( attributes.lastModifiedTime() ) );
|
||||
result.put( "modified", getFileTime( attributes.lastModifiedTime() ) );
|
||||
result.put( "created", getFileTime( attributes.creationTime() ) );
|
||||
result.put( "size", attributes.isDirectory() ? 0 : attributes.size() );
|
||||
result.put( "isDir", attributes.isDirectory() );
|
||||
|
||||
@@ -395,14 +395,7 @@ public final class ComputerThread
|
||||
executor.timeout.hardAbort();
|
||||
executor.abort();
|
||||
|
||||
if( afterHardAbort >= ABORT_TIMEOUT )
|
||||
{
|
||||
// If we've hard aborted but we're still not dead, dump the stack trace and interrupt
|
||||
// the task.
|
||||
timeoutTask( executor, runner.owner, afterStart );
|
||||
runner.owner.interrupt();
|
||||
}
|
||||
else if( afterHardAbort >= ABORT_TIMEOUT * 2 )
|
||||
if( afterHardAbort >= ABORT_TIMEOUT * 2 )
|
||||
{
|
||||
// If we've hard aborted and interrupted, and we're still not dead, then mark the runner
|
||||
// as dead, finish off the task, and spawn a new runner.
|
||||
@@ -421,6 +414,13 @@ public final class ComputerThread
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( afterHardAbort >= ABORT_TIMEOUT )
|
||||
{
|
||||
// If we've hard aborted but we're still not dead, dump the stack trace and interrupt
|
||||
// the task.
|
||||
timeoutTask( executor, runner.owner, afterStart );
|
||||
runner.owner.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public class FileSystemWrapperMount implements IFileSystem
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_filesystem.exists( path );
|
||||
return m_filesystem.isDir( path );
|
||||
}
|
||||
catch( FileSystemException e )
|
||||
{
|
||||
|
||||
@@ -83,9 +83,9 @@ class VarargArguments implements IArguments
|
||||
public ByteBuffer getBytes( int index ) throws LuaException
|
||||
{
|
||||
LuaValue value = varargs.arg( index + 1 );
|
||||
if( !(value instanceof LuaString) ) throw LuaValues.badArgument( index, "string", value.typeName() );
|
||||
if( !(value instanceof LuaBaseString) ) throw LuaValues.badArgument( index, "string", value.typeName() );
|
||||
|
||||
LuaString str = (LuaString) value;
|
||||
LuaString str = ((LuaBaseString) value).strvalue();
|
||||
return ByteBuffer.wrap( str.bytes, str.offset, str.length ).asReadOnlyBuffer();
|
||||
}
|
||||
|
||||
@@ -94,9 +94,9 @@ class VarargArguments implements IArguments
|
||||
{
|
||||
LuaValue value = varargs.arg( index + 1 );
|
||||
if( value.isNil() ) return Optional.empty();
|
||||
if( !(value instanceof LuaString) ) throw LuaValues.badArgument( index, "string", value.typeName() );
|
||||
if( !(value instanceof LuaBaseString) ) throw LuaValues.badArgument( index, "string", value.typeName() );
|
||||
|
||||
LuaString str = (LuaString) value;
|
||||
LuaString str = ((LuaBaseString) value).strvalue();
|
||||
return Optional.of( ByteBuffer.wrap( str.bytes, str.offset, str.length ).asReadOnlyBuffer() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,8 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.network.NetworkEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Starts or stops a record on the client, depending on if {@link #soundEvent} is {@code null}.
|
||||
@@ -51,7 +49,7 @@ public class PlayRecordClientMessage implements NetworkMessage
|
||||
if( buf.readBoolean() )
|
||||
{
|
||||
name = buf.readString( Short.MAX_VALUE );
|
||||
soundEvent = ForgeRegistries.SOUND_EVENTS.getValue( buf.readResourceLocation() );
|
||||
soundEvent = buf.readRegistryIdSafe( SoundEvent.class );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -72,7 +70,7 @@ public class PlayRecordClientMessage implements NetworkMessage
|
||||
{
|
||||
buf.writeBoolean( true );
|
||||
buf.writeString( name );
|
||||
buf.writeResourceLocation( Objects.requireNonNull( soundEvent.getRegistryName(), "Sound is not registered" ) );
|
||||
buf.writeRegistryId( soundEvent );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
package dan200.computercraft.shared.peripheral.generic.data;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.item.EnchantedBookItem;
|
||||
@@ -22,13 +24,29 @@ import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Data providers for items.
|
||||
*
|
||||
* We guard using {@link ComputerCraft#genericPeripheral} in several places, as advanced functionality should not be
|
||||
* exposed for {@code turtle.getItemDetail} when generic peripehrals are disabled.
|
||||
*/
|
||||
public class ItemData
|
||||
{
|
||||
@Nonnull
|
||||
public static <T extends Map<? super String, Object>> T fillBasic( @Nonnull T data, @Nonnull ItemStack stack )
|
||||
public static <T extends Map<? super String, Object>> T fillBasicSafe( @Nonnull T data, @Nonnull ItemStack stack )
|
||||
{
|
||||
data.put( "name", DataHelpers.getId( stack.getItem() ) );
|
||||
data.put( "count", stack.getCount() );
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <T extends Map<? super String, Object>> T fillBasic( @Nonnull T data, @Nonnull ItemStack stack )
|
||||
{
|
||||
fillBasicSafe( data, stack );
|
||||
String hash = NBTUtil.getNBTHash( stack.getTag() );
|
||||
if( hash != null ) data.put( "nbt", hash );
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -55,6 +73,8 @@ public class ItemData
|
||||
|
||||
data.put( "tags", DataHelpers.getTags( stack.getItem().getTags() ) );
|
||||
|
||||
if( !ComputerCraft.genericPeripheral ) return data;
|
||||
|
||||
CompoundNBT tag = stack.getTag();
|
||||
if( tag != null && tag.contains( "display", Constants.NBT.TAG_COMPOUND ) )
|
||||
{
|
||||
|
||||
@@ -605,7 +605,7 @@ public class TurtleAPI implements ILuaAPI
|
||||
|
||||
Map<String, Object> table = detailed
|
||||
? ItemData.fill( new HashMap<>(), stack )
|
||||
: ItemData.fillBasic( new HashMap<>(), stack );
|
||||
: ItemData.fillBasicSafe( new HashMap<>(), stack );
|
||||
|
||||
TurtleActionEvent event = new TurtleInspectItemEvent( turtle, stack, table, detailed );
|
||||
if( MinecraftForge.EVENT_BUS.post( event ) ) return new Object[] { false, event.getFailureMessage() };
|
||||
|
||||
@@ -42,8 +42,6 @@ public final class DropConsumer
|
||||
dropEntity = entity;
|
||||
dropWorld = entity.world;
|
||||
dropBounds = new AxisAlignedBB( entity.getPosition() ).grow( 2, 2, 2 );
|
||||
|
||||
entity.captureDrops( new ArrayList<>() );
|
||||
}
|
||||
|
||||
public static void set( World world, BlockPos pos, Function<ItemStack, ItemStack> consumer )
|
||||
@@ -86,7 +84,7 @@ public final class DropConsumer
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent( priority = EventPriority.LOW )
|
||||
public static void onLivingDrops( LivingDropsEvent drops )
|
||||
{
|
||||
if( dropEntity == null || drops.getEntity() != dropEntity ) return;
|
||||
|
||||
@@ -5,9 +5,19 @@
|
||||
*/
|
||||
package dan200.computercraft.shared.util;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -159,4 +169,46 @@ public final class NBTUtil
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getNBTHash( @Nullable CompoundNBT tag )
|
||||
{
|
||||
if( tag == null ) return null;
|
||||
|
||||
try
|
||||
{
|
||||
MessageDigest digest = MessageDigest.getInstance( "MD5" );
|
||||
DataOutput output = new DataOutputStream( new DigestOutputStream( digest ) );
|
||||
CompressedStreamTools.write( tag, output );
|
||||
byte[] hash = digest.digest();
|
||||
return new String( Hex.encodeHex( hash ) );
|
||||
}
|
||||
catch( NoSuchAlgorithmException | IOException e )
|
||||
{
|
||||
ComputerCraft.log.error( "Cannot hash NBT", e );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DigestOutputStream extends OutputStream
|
||||
{
|
||||
private final MessageDigest digest;
|
||||
|
||||
DigestOutputStream( MessageDigest digest )
|
||||
{
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write( @Nonnull byte[] b, int off, int len )
|
||||
{
|
||||
digest.update( b, off, len );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write( int b )
|
||||
{
|
||||
digest.update( (byte) b );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles a
|
||||
[[dependencies.computercraft]]
|
||||
modId="forge"
|
||||
mandatory=true
|
||||
versionRange="[31.0.13,32)"
|
||||
versionRange="[31.1.41,32)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
|
||||
48
src/main/resources/assets/computercraft/lang/vi.json
Normal file
48
src/main/resources/assets/computercraft/lang/vi.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"gui.computercraft.tooltip.disk_id": "ID của đĩa: %s",
|
||||
"upgrade.computercraft.speaker.adjective": "Ồn ào",
|
||||
"upgrade.computercraft.wireless_modem_advanced.adjective": "Ender",
|
||||
"upgrade.computercraft.wireless_modem_normal.adjective": "Không dây",
|
||||
"upgrade.minecraft.crafting_table.adjective": "Chế tạo",
|
||||
"upgrade.minecraft.diamond_hoe.adjective": "Trồng trọt",
|
||||
"upgrade.minecraft.diamond_axe.adjective": "Đốn",
|
||||
"upgrade.minecraft.diamond_pickaxe.adjective": "Khai thác",
|
||||
"upgrade.minecraft.diamond_shovel.adjective": "Đào",
|
||||
"item.computercraft.pocket_computer_advanced.upgraded": "Máy tính bỏ túi tiên tiến %s",
|
||||
"item.computercraft.pocket_computer_advanced": "Máy tính bỏ túi tiên tiến",
|
||||
"item.computercraft.pocket_computer_normal.upgraded": "Máy tính bỏ túi %s",
|
||||
"item.computercraft.pocket_computer_normal": "Máy tính bỏ túi",
|
||||
"item.computercraft.printed_book": "Sách in",
|
||||
"item.computercraft.printed_page": "Trang in",
|
||||
"item.computercraft.treasure_disk": "Đĩa mềm",
|
||||
"item.computercraft.disk": "Đĩa mềm",
|
||||
"block.computercraft.turtle_advanced.upgraded_twice": "Rùa tiên tiến %s %s",
|
||||
"block.computercraft.turtle_advanced.upgraded": "Rùa tiên tiến %s",
|
||||
"block.computercraft.turtle_advanced": "Rùa tiên tiến",
|
||||
"block.computercraft.turtle_normal.upgraded_twice": "Rùa %s %s",
|
||||
"block.computercraft.turtle_normal.upgraded": "Rùa %s",
|
||||
"block.computercraft.turtle_normal": "Rùa",
|
||||
"block.computercraft.wired_modem_full": "Modem có dây",
|
||||
"block.computercraft.cable": "Dây cáp mạng",
|
||||
"block.computercraft.wired_modem": "Modem có dây",
|
||||
"block.computercraft.wireless_modem_advanced": "Modem Ender",
|
||||
"block.computercraft.wireless_modem_normal": "Modem không dây",
|
||||
"block.computercraft.monitor_advanced": "Màn hình tiên tiếng",
|
||||
"block.computercraft.monitor_normal": "Màn hình",
|
||||
"block.computercraft.speaker": "Loa",
|
||||
"block.computercraft.printer": "Máy in",
|
||||
"block.computercraft.disk_drive": "Ỗ đĩa",
|
||||
"block.computercraft.computer_command": "Máy tính điều khiển",
|
||||
"block.computercraft.computer_normal": "Máy tính",
|
||||
"itemGroup.computercraft": "ComputerCraft",
|
||||
"block.computercraft.computer_advanced": "Máy tính tiên tiến",
|
||||
"tracking_field.computercraft.websocket_incoming.name": "Websocket đến",
|
||||
"tracking_field.computercraft.websocket_outgoing.name": "Websocket đi",
|
||||
"gui.computercraft.tooltip.computer_id": "ID của máy tính: %s",
|
||||
"tracking_field.computercraft.coroutines_dead.name": "Coroutine bỏ đi",
|
||||
"tracking_field.computercraft.coroutines_created.name": "Coroutine đã tạo",
|
||||
"tracking_field.computercraft.http_download.name": "HTTP tải xuống",
|
||||
"tracking_field.computercraft.http_upload.name": "HTTP tải lên",
|
||||
"tracking_field.computercraft.http.name": "Yêu cầu HTTP",
|
||||
"gui.computercraft.tooltip.copy": "Sao chép vào clipboard"
|
||||
}
|
||||
@@ -181,9 +181,6 @@ end
|
||||
--- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many
|
||||
-- arguments it receives.
|
||||
--
|
||||
-- **Note:** This function is deprecated, and it is recommended you use the
|
||||
-- specific pack/unpack function directly.
|
||||
--
|
||||
-- @tparam[1] number r The red channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[1] number g The green channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[1] number b The blue channel, as an argument to @{colors.packRGB}.
|
||||
@@ -192,6 +189,7 @@ end
|
||||
-- @treturn[2] number The red channel, as returned by @{colors.unpackRGB}
|
||||
-- @treturn[2] number The green channel, as returned by @{colors.unpackRGB}
|
||||
-- @treturn[2] number The blue channel, as returned by @{colors.unpackRGB}
|
||||
-- @deprecated Use @{packRGB} or @{unpackRGB} directly.
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.rgb(0xb23399)
|
||||
|
||||
@@ -77,7 +77,7 @@ function formatTime(nTime, bTwentyFourHour)
|
||||
local nHour = math.floor(nTime)
|
||||
local nMinute = math.floor((nTime - nHour) * 60)
|
||||
if sTOD then
|
||||
return string.format("%d:%02d %s", nHour, nMinute, sTOD)
|
||||
return string.format("%d:%02d %s", nHour == 0 and 12 or nHour, nMinute, sTOD)
|
||||
else
|
||||
return string.format("%d:%02d", nHour, nMinute)
|
||||
end
|
||||
@@ -335,6 +335,31 @@ empty_json_array = mk_tbl("[]", "empty_json_array")
|
||||
-- @see textutils.unserialiseJSON
|
||||
json_null = mk_tbl("null", "json_null")
|
||||
|
||||
local serializeJSONString
|
||||
do
|
||||
local function hexify(c)
|
||||
return ("\\u00%02X"):format(c:byte())
|
||||
end
|
||||
|
||||
local map = {
|
||||
["\""] = "\\\"",
|
||||
["\\"] = "\\\\",
|
||||
["\b"] = "\\b",
|
||||
["\f"] = "\\f",
|
||||
["\n"] = "\\n",
|
||||
["\r"] = "\\r",
|
||||
["\t"] = "\\t",
|
||||
}
|
||||
for i = 0, 0x1f do
|
||||
local c = string.char(i)
|
||||
if map[c] == nil then map[c] = hexify(c) end
|
||||
end
|
||||
|
||||
serializeJSONString = function(s)
|
||||
return ('"%s"'):format(s:gsub("[\0-\x1f\"\\]", map):gsub("[\x7f-\xff]", hexify))
|
||||
end
|
||||
end
|
||||
|
||||
local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
local sType = type(t)
|
||||
if t == empty_json_array then return "[]"
|
||||
@@ -361,7 +386,7 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
if bNBTStyle then
|
||||
sEntry = tostring(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
else
|
||||
sEntry = string.format("%q", k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
sEntry = serializeJSONString(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
end
|
||||
if nObjectSize == 0 then
|
||||
sObjectResult = sObjectResult .. sEntry
|
||||
@@ -390,7 +415,7 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
end
|
||||
|
||||
elseif sType == "string" then
|
||||
return string.format("%q", t)
|
||||
return serializeJSONString(t)
|
||||
|
||||
elseif sType == "number" or sType == "boolean" then
|
||||
return tostring(t)
|
||||
@@ -407,7 +432,7 @@ do
|
||||
|
||||
--- Skip any whitespace
|
||||
local function skip(str, pos)
|
||||
local _, last = find(str, "^[ \n\r\v]+", pos)
|
||||
local _, last = find(str, "^[ \n\r\t]+", pos)
|
||||
if last then return last + 1 else return pos end
|
||||
end
|
||||
|
||||
@@ -447,7 +472,7 @@ do
|
||||
buf[n], n, pos = utf8.char(tonumber(num_str, 16)), n + 1, pos + 6
|
||||
else
|
||||
local unesc = escapes[c]
|
||||
if not unesc then error_at(pos + 1, "Unknown escape character %q.", unesc) end
|
||||
if not unesc then error_at(pos + 1, "Unknown escape character %q.", c) end
|
||||
buf[n], n, pos = unesc, n + 1, pos + 2
|
||||
end
|
||||
elseif c >= '\x20' then
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
# New features in CC: Tweaked 1.92.0
|
||||
|
||||
* Bump Cobalt version:
|
||||
* Add support for the __pairs metamethod.
|
||||
* string.format now uses the __tostring metamethod.
|
||||
* Add date-specific MOTDs (MCJack123).
|
||||
|
||||
And several bug fixes:
|
||||
* Correctly handle tabs within textutils.unserailizeJSON.
|
||||
* Fix sheep not dropping items when sheered by turtles.
|
||||
|
||||
# New features in CC: Tweaked 1.91.0
|
||||
|
||||
* [Generic peripherals] Expose NBT hashes of items to inventory methods.
|
||||
* Bump Cobalt version
|
||||
* Optimise handling of string concatenation.
|
||||
* Add string.{pack,unpack,packsize} (MCJack123)
|
||||
|
||||
And several bug fixes:
|
||||
* Escape non-ASCII characters in JSON strings (neumond)
|
||||
* Make field names in fs.attributes more consistent (abby)
|
||||
* Fix textutils.formatTime correctly handle 12 AM (R93950X)
|
||||
|
||||
# New features in CC: Tweaked 1.90.2
|
||||
|
||||
* Fix generic peripherals not being registered outside a dev environment.
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
New features in CC: Tweaked 1.90.2
|
||||
New features in CC: Tweaked 1.92.0
|
||||
|
||||
* Fix generic peripherals not being registered outside a dev environment.
|
||||
* Bump Cobalt version:
|
||||
* Add support for the __pairs metamethod.
|
||||
* string.format now uses the __tostring metamethod.
|
||||
* Add date-specific MOTDs (MCJack123).
|
||||
|
||||
And several bug fixes:
|
||||
* Correctly handle tabs within textutils.unserailizeJSON.
|
||||
* Fix sheep not dropping items when sheered by turtles.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
local tMotd = {}
|
||||
local date = os.date("*t")
|
||||
if date.month == 1 and date.day == 1 then
|
||||
print("Happy new year!")
|
||||
elseif date.month == 12 and date.day == 24 then
|
||||
print("Merry X-mas!")
|
||||
elseif date.month == 10 and date.day == 31 then
|
||||
print("OOoooOOOoooo! Spooky!")
|
||||
else
|
||||
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)
|
||||
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
|
||||
end
|
||||
|
||||
if #tMotd == 0 then
|
||||
print("missingno")
|
||||
else
|
||||
print(tMotd[math.random(1, #tMotd)])
|
||||
if #tMotd == 0 then
|
||||
print("missingno")
|
||||
else
|
||||
print(tMotd[math.random(1, #tMotd)])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[ ]
|
||||
@@ -208,9 +208,11 @@ describe("The fs library", function()
|
||||
fail(("Expected created time (%d) to be within 1000ms of now (%d"):format(attributes.created, now))
|
||||
end
|
||||
|
||||
if attributes.modification - now >= 1000 then
|
||||
fail(("Expected modification time (%d) to be within 1000ms of now (%d"):format(attributes.modification, now))
|
||||
if attributes.modified - now >= 1000 then
|
||||
fail(("Expected modified time (%d) to be within 1000ms of now (%d"):format(attributes.modified, now))
|
||||
end
|
||||
|
||||
expect(attributes.modification):eq(attributes.modified)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -12,6 +12,14 @@ describe("The textutils library", function()
|
||||
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)
|
||||
|
||||
it("correctly formats 12 o'clock", function()
|
||||
expect(textutils.formatTime(0, false)):eq("12:00 AM")
|
||||
expect(textutils.formatTime(0.1, false)):eq("12:06 AM")
|
||||
|
||||
expect(textutils.formatTime(0, true)):eq("0:00")
|
||||
expect(textutils.formatTime(0.1, true)):eq("0:06")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("textutils.pagedPrint", function()
|
||||
@@ -49,7 +57,7 @@ describe("The textutils library", function()
|
||||
describe("textutils.empty_json_array", function()
|
||||
it("is immutable", function()
|
||||
expect.error(function() textutils.empty_json_array[1] = true end)
|
||||
:str_match("^[^:]+:51: attempt to mutate textutils.empty_json_array$")
|
||||
:str_match("^[^:]+:%d+: attempt to mutate textutils.empty_json_array$")
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -78,6 +86,20 @@ describe("The textutils library", function()
|
||||
it("serializes null", function()
|
||||
expect(textutils.serializeJSON(textutils.json_null)):eq("null")
|
||||
end)
|
||||
|
||||
it("serializes strings", function()
|
||||
expect(textutils.serializeJSON('a')):eq('"a"')
|
||||
expect(textutils.serializeJSON('"')):eq('"\\""')
|
||||
expect(textutils.serializeJSON('\\')):eq('"\\\\"')
|
||||
expect(textutils.serializeJSON('/')):eq('"/"')
|
||||
expect(textutils.serializeJSON('\b')):eq('"\\b"')
|
||||
expect(textutils.serializeJSON('\n')):eq('"\\n"')
|
||||
expect(textutils.serializeJSON(string.char(0))):eq('"\\u0000"')
|
||||
expect(textutils.serializeJSON(string.char(0x0A))):eq('"\\n"')
|
||||
expect(textutils.serializeJSON(string.char(0x1D))):eq('"\\u001D"')
|
||||
expect(textutils.serializeJSON(string.char(0x81))):eq('"\\u0081"')
|
||||
expect(textutils.serializeJSON(string.char(0xFF))):eq('"\\u00FF"')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("textutils.unserializeJSON", function()
|
||||
|
||||
@@ -1,14 +1,36 @@
|
||||
local capture = require "test_helpers".capture_program
|
||||
|
||||
describe("The motd program", function()
|
||||
local function setup_date(month, day)
|
||||
stub(os, "date", function() return { month = month, day = day } end)
|
||||
end
|
||||
|
||||
it("displays MODT", function()
|
||||
local file = fs.open("/modt_check.txt", "w")
|
||||
it("displays MOTD", function()
|
||||
setup_date(0, 0)
|
||||
local file = fs.open("/motd_check.txt", "w")
|
||||
file.write("Hello World!")
|
||||
file.close()
|
||||
settings.set("motd.path", "/modt_check.txt")
|
||||
settings.set("motd.path", "/motd_check.txt")
|
||||
|
||||
expect(capture(stub, "motd"))
|
||||
:matches { ok = true, output = "Hello World!\n", error = "" }
|
||||
end)
|
||||
|
||||
it("displays date-specific MOTD (1/1)", function()
|
||||
setup_date(1, 1)
|
||||
expect(capture(stub, "motd"))
|
||||
:matches { ok = true, output = "Happy new year!\n", error = "" }
|
||||
end)
|
||||
|
||||
it("displays date-specific MOTD (10/31)", function()
|
||||
setup_date(10, 31)
|
||||
expect(capture(stub, "motd"))
|
||||
:matches { ok = true, output = "OOoooOOOoooo! Spooky!\n", error = "" }
|
||||
end)
|
||||
|
||||
it("displays date-specific MOTD (12/24)", function()
|
||||
setup_date(12, 24)
|
||||
expect(capture(stub, "motd"))
|
||||
:matches { ok = true, output = "Merry X-mas!\n", error = "" }
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user