1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-27 03:47:38 +00:00

Merge branch 'mc-1.20.x' into mc-1.21.x

This commit is contained in:
Jonathan Coates
2024-09-11 19:28:13 +01:00
27 changed files with 422 additions and 196 deletions

View File

@@ -7,6 +7,7 @@ import cc.tweaked.gradle.getAbsolutePath
plugins {
`java-library`
`java-test-fixtures`
alias(libs.plugins.shadow)
id("cc-tweaked.kotlin-convention")
id("cc-tweaked.java-convention")
@@ -57,3 +58,22 @@ val checkChangelog by tasks.registering(cc.tweaked.gradle.CheckChangelog::class)
}
tasks.check { dependsOn(checkChangelog) }
// We configure the shadow jar to ship netty-codec and all its dependencies, relocating them under the
// dan200.computercraft.core package.
// This is used as part of the Forge build, so that our version of netty-codec is loaded under the GAME layer, and so
// has access to our jar-in-jar'ed jzlib.
tasks.shadowJar {
minimize()
dependencies {
include(dependency(libs.netty.codec.get()))
include(dependency(libs.netty.http.get()))
include(dependency(libs.netty.socks.get()))
include(dependency(libs.netty.proxy.get()))
}
for (pkg in listOf("io.netty.handler.codec", "io.netty.handler.proxy")) {
relocate(pkg, "dan200.computercraft.core.vendor.$pkg")
}
}

View File

@@ -95,11 +95,8 @@ public class CobaltLuaMachine implements ILuaMachine {
private void addAPI(LuaState state, LuaTable globals, ILuaAPI api) throws LuaError {
// Add the methods of an API to the global table
var table = wrapLuaObject(api);
if (table == null) {
LOG.warn("API {} does not provide any methods", api);
table = new LuaTable();
}
var table = new LuaTable();
if (!makeLuaObject(api, table)) LOG.warn("API {} does not provide any methods", api);
var names = api.getNames();
for (var name : names) globals.rawset(name, table);
@@ -163,13 +160,16 @@ public class CobaltLuaMachine implements ILuaMachine {
timeout.removeListener(timeoutListener);
}
@Nullable
private LuaTable wrapLuaObject(Object object) {
var table = new LuaTable();
var found = luaMethods.forEachMethod(object, (target, name, method, info) ->
/**
* Populate a table with methods from an object.
*
* @param object The object to draw methods from.
* @param table The table to fill.
* @return Whether any methods were found.
*/
private boolean makeLuaObject(Object object, LuaTable table) {
return luaMethods.forEachMethod(object, (target, name, method, info) ->
table.rawset(name, new ResultInterpreterFunction(this, method, target, context, name)));
return found ? table : null;
}
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) throws LuaError {
@@ -184,47 +184,35 @@ public class CobaltLuaMachine implements ILuaMachine {
return ValueFactory.valueOf(bytes);
}
// Don't share singleton values, and instead convert them to a new table.
if (LuaUtil.isSingletonCollection(object)) return new LuaTable();
// We have a more complex object, which is possibly recursive. First look up our object in the lookup map,
// and reuse it if present.
if (values == null) values = new IdentityHashMap<>(1);
var result = values.get(object);
if (result != null) return result;
var wrapped = toValueWorker(object, values);
if (wrapped == null) {
LOG.warn(Logging.JAVA_ERROR, "Received unknown type '{}', returning nil.", object.getClass().getName());
return Constants.NIL;
}
values.put(object, wrapped);
return wrapped;
}
/**
* Convert a complex Java object (such as a collection or Lua object) to a Lua value.
* <p>
* This is a worker function for {@link #toValue(Object, IdentityHashMap)}, which handles the actual construction
* of values, without reading/writing from the value map.
*
* @param object The object to convert.
* @param values The map of Java to Lua values.
* @return The converted value, or {@code null} if it could not be converted.
* @throws LuaError If the value could not be converted.
*/
private @Nullable LuaValue toValueWorker(Object object, IdentityHashMap<Object, LuaValue> values) throws LuaError {
if (object instanceof ILuaFunction) {
return new ResultInterpreterFunction(this, FUNCTION_METHOD, object, context, object.toString());
var function = new ResultInterpreterFunction(this, FUNCTION_METHOD, object, context, object.toString());
values.put(object, function);
return function;
}
if (object instanceof IDynamicLuaObject) {
LuaValue wrapped = wrapLuaObject(object);
if (wrapped == null) wrapped = new LuaTable();
return wrapped;
var table = new LuaTable();
makeLuaObject(object, table);
values.put(object, table);
return table;
}
// The following objects may be recursive. In these instances, we need to be careful to store the value *before*
// recursing, to avoid stack overflows.
if (object instanceof Map<?, ?> map) {
// Don't share singleton values, and instead convert them to a new table.
if (LuaUtil.isSingletonMap(map)) return new LuaTable();
var table = new LuaTable();
values.put(object, table);
for (var pair : map.entrySet()) {
var key = toValue(pair.getKey(), values);
var value = toValue(pair.getValue(), values);
@@ -234,7 +222,12 @@ public class CobaltLuaMachine implements ILuaMachine {
}
if (object instanceof Collection<?> objects) {
// Don't share singleton values, and instead convert them to a new table.
if (LuaUtil.isSingletonCollection(objects)) return new LuaTable();
var table = new LuaTable(objects.size(), 0);
values.put(object, table);
var i = 0;
for (var child : objects) table.rawset(++i, toValue(child, values));
return table;
@@ -242,11 +235,20 @@ public class CobaltLuaMachine implements ILuaMachine {
if (object instanceof Object[] objects) {
var table = new LuaTable(objects.length, 0);
values.put(object, table);
for (var i = 0; i < objects.length; i++) table.rawset(i + 1, toValue(objects[i], values));
return table;
}
return wrapLuaObject(object);
var table = new LuaTable();
if (makeLuaObject(object, table)) {
values.put(object, table);
return table;
}
LOG.warn(Logging.JAVA_ERROR, "Received unknown type '{}', returning nil.", object.getClass().getName());
return Constants.NIL;
}
Varargs toValues(@Nullable Object[] objects) throws LuaError {

View File

@@ -12,15 +12,13 @@ public class Palette {
private final boolean colour;
private final double[][] colours = new double[PALETTE_SIZE][3];
private final byte[][] byteColours = new byte[PALETTE_SIZE][4];
private final int[] byteColours = new int[PALETTE_SIZE];
public static final Palette DEFAULT = new Palette(true);
public Palette(boolean colour) {
this.colour = colour;
resetColours();
for (var i = 0; i < PALETTE_SIZE; i++) byteColours[i][3] = (byte) 255;
}
public void setColour(int i, double r, double g, double b) {
@@ -30,15 +28,17 @@ public class Palette {
colours[i][2] = b;
if (colour) {
byteColours[i][0] = (byte) (int) (r * 255);
byteColours[i][1] = (byte) (int) (g * 255);
byteColours[i][2] = (byte) (int) (b * 255);
byteColours[i] = packColour((int) (r * 255), (int) (g * 255), (int) (b * 255));
} else {
var grey = (byte) (int) ((r + g + b) / 3 * 255);
byteColours[i][0] = byteColours[i][1] = byteColours[i][2] = grey;
var grey = (int) ((r + g + b) / 3 * 255);
byteColours[i] = packColour(grey, grey, grey);
}
}
private static int packColour(int r, int g, int b) {
return 255 << 24 | (r & 255) << 16 | (g & 255) << 8 | b & 255;
}
public void setColour(int i, Colour colour) {
setColour(i, colour.getR(), colour.getG(), colour.getB());
}
@@ -48,26 +48,20 @@ public class Palette {
}
/**
* Get the colour as a set of RGB values suitable for rendering. Colours are automatically converted to greyscale
* Get the colour as a set of ARGB values suitable for rendering. Colours are automatically converted to greyscale
* when using a black and white palette.
* <p>
* This returns a byte array, suitable for being used directly by our terminal vertex format.
* This returns a packed 32-bit ARGB colour.
*
* @param i The colour index.
* @return The number as a tuple of bytes.
* @return The actual RGB colour.
*/
public byte[] getRenderColours(int i) {
public int getRenderColours(int i) {
return byteColours[i];
}
public void resetColour(int i) {
if (i >= 0 && i < PALETTE_SIZE) setColour(i, Colour.VALUES[i]);
}
public void resetColours() {
for (var i = 0; i < Colour.VALUES.length; i++) {
resetColour(i);
}
for (var i = 0; i < Colour.VALUES.length; i++) setColour(i, Colour.VALUES[i]);
}
public static int encodeRGB8(double[] rgb) {

View File

@@ -36,7 +36,20 @@ public class LuaUtil {
* @param value The value to test.
* @return Whether this is a singleton collection.
*/
public static boolean isSingletonCollection(Object value) {
return value == EMPTY_LIST || value == EMPTY_SET || value == EMPTY_MAP;
public static boolean isSingletonCollection(Collection<?> value) {
return value == EMPTY_LIST || value == EMPTY_SET;
}
/**
* Determine whether a value is a singleton map, such as one created with {@link Map#of()}.
* <p>
* These collections are treated specially by {@link ILuaMachine} implementations: we skip sharing for them, and
* create a new table each time.
*
* @param value The value to test.
* @return Whether this is a singleton map.
*/
public static boolean isSingletonMap(Map<?, ?> value) {
return value == EMPTY_MAP;
}
}

View File

@@ -944,22 +944,21 @@ unserialiseJSON = unserialise_json
-- @since 1.31
function urlEncode(str)
expect(1, str, "string")
if str then
str = string.gsub(str, "\n", "\r\n")
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
local n = string.byte(c)
if n < 128 then
-- ASCII
return string.format("%%%02X", n)
else
-- Non-ASCII (encode as UTF-8)
return
string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) ..
string.format("%%%02X", 128 + bit32.band(n, 63))
end
end)
str = string.gsub(str, " ", "+")
end
local gsub, byte, format, band, arshift = string.gsub, string.byte, string.format, bit32.band, bit32.arshift
str = gsub(str, "\n", "\r\n")
str = gsub(str, "[^A-Za-z0-9%-%_%.]", function(c)
if c == " " then return "+" end
local n = byte(c)
if n < 128 then
-- ASCII
return format("%%%02X", n)
else
-- Non-ASCII (encode as UTF-8)
return format("%%%02X%%%02X", 192 + band(arshift(n, 6), 31), 128 + band(n, 63))
end
end)
return str
end

View File

@@ -1,3 +1,12 @@
# New features in CC: Tweaked 1.113.1
* Update Japanese translation (konumatakaki).
* Improve performance of `textutils.urlEncode`.
Several bug fixes:
* Fix overflow when converting recursive objects from Java to Lua.
* Fix websocket compression not working under Forge.
# New features in CC: Tweaked 1.113.0
* Allow placing printed pages and books in lecterns.

View File

@@ -1,13 +1,10 @@
New features in CC: Tweaked 1.113.0
New features in CC: Tweaked 1.113.1
* Allow placing printed pages and books in lecterns.
* Update Japanese translation (konumatakaki).
* Improve performance of `textutils.urlEncode`.
Several bug fixes:
* Various documentation fixes (MCJack123)
* Fix computers and turtles not being dropped when exploded with TNT.
* Fix crash when turtles are broken while mining a block.
* Fix pocket computer terminals not updating when in the off-hand.
* Fix disk drives not being exposed as a peripheral.
* Fix item details being non-serialisable due to duplicated tables.
* Fix overflow when converting recursive objects from Java to Lua.
* Fix websocket compression not working under Forge.
Type "help changelog" to see the full version history.

View File

@@ -6,8 +6,7 @@
Convert between streams of DFPWM audio data and a list of amplitudes.
DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec designed by GreaseMonkey. It's a relatively compact
format compared to raw PCM data, only using 1 bit per sample, but is simple enough to simple enough to encode and decode
in real time.
format compared to raw PCM data, only using 1 bit per sample, but is simple enough to encode and decode in real time.
Typically DFPWM audio is read from [the filesystem][`fs.ReadHandle`] or a [a web request][`http.Response`] as a string,
and converted a format suitable for [`speaker.playAudio`].

View File

@@ -202,6 +202,14 @@ describe("The os library", function()
expect(xs[1]):eq(xs[2])
end)
it("handles recursive tables", function()
local tbl = {}
tbl[1] = tbl
local xs = roundtrip(tbl)
expect(xs):eq(xs[1])
end)
it("does not preserve references in separate args", function()
-- I'm not sure I like this behaviour, but it is what CC has always done.
local tbl = {}

View File

@@ -296,6 +296,22 @@ describe("The textutils library", function()
textutils.urlEncode("")
expect.error(textutils.urlEncode, nil):eq("bad argument #1 (string expected, got nil)")
end)
it("encodes newlines", function()
expect(textutils.urlEncode("a\nb")):eq("a%0D%0Ab")
end)
it("leaves normal characters as-is", function()
expect(textutils.urlEncode("abcABC0123")):eq("abcABC0123")
end)
it("escapes spaces", function()
expect(textutils.urlEncode("a b c")):eq("a+b+c")
end)
it("escapes special characters", function()
expect(textutils.urlEncode("a%b\0\255")):eq("a%25b%00%C3%BF")
end)
end)
describe("textutils.complete", function()