1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-29 12:57:46 +00:00

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

This commit is contained in:
Jonathan Coates
2024-07-31 07:34:49 +01:00
72 changed files with 1603 additions and 557 deletions

View File

@@ -4,7 +4,6 @@
package dan200.computercraft.core;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.core.asm.GenericMethod;
import dan200.computercraft.core.asm.LuaMethodSupplier;
import dan200.computercraft.core.asm.PeripheralMethodSupplier;
@@ -35,21 +34,19 @@ public final class ComputerContext {
private final ComputerScheduler computerScheduler;
private final MainThreadScheduler mainThreadScheduler;
private final ILuaMachine.Factory luaFactory;
private final List<ILuaAPIFactory> apiFactories;
private final MethodSupplier<LuaMethod> luaMethods;
private final MethodSupplier<PeripheralMethod> peripheralMethods;
ComputerContext(
private ComputerContext(
GlobalEnvironment globalEnvironment, ComputerScheduler computerScheduler,
MainThreadScheduler mainThreadScheduler, ILuaMachine.Factory luaFactory,
List<ILuaAPIFactory> apiFactories, MethodSupplier<LuaMethod> luaMethods,
MethodSupplier<LuaMethod> luaMethods,
MethodSupplier<PeripheralMethod> peripheralMethods
) {
this.globalEnvironment = globalEnvironment;
this.computerScheduler = computerScheduler;
this.mainThreadScheduler = mainThreadScheduler;
this.luaFactory = luaFactory;
this.apiFactories = apiFactories;
this.luaMethods = luaMethods;
this.peripheralMethods = peripheralMethods;
}
@@ -91,15 +88,6 @@ public final class ComputerContext {
return luaFactory;
}
/**
* Additional APIs to inject into each computer.
*
* @return All available API factories.
*/
public List<ILuaAPIFactory> apiFactories() {
return apiFactories;
}
/**
* Get the {@link MethodSupplier} used to find methods on Lua values.
*
@@ -166,7 +154,6 @@ public final class ComputerContext {
private @Nullable ComputerScheduler computerScheduler = null;
private @Nullable MainThreadScheduler mainThreadScheduler;
private @Nullable ILuaMachine.Factory luaFactory;
private @Nullable List<ILuaAPIFactory> apiFactories;
private @Nullable List<GenericMethod> genericMethods;
Builder(GlobalEnvironment environment) {
@@ -227,20 +214,6 @@ public final class ComputerContext {
return this;
}
/**
* Set the additional {@linkplain ILuaAPIFactory APIs} to add to each computer.
*
* @param apis A list of API factories.
* @return {@code this}, for chaining
* @see ComputerContext#apiFactories()
*/
public Builder apiFactories(Collection<ILuaAPIFactory> apis) {
Objects.requireNonNull(apis);
if (apiFactories != null) throw new IllegalStateException("Main-thread scheduler already specified");
apiFactories = List.copyOf(apis);
return this;
}
/**
* Set the set of {@link GenericMethod}s used by the {@linkplain MethodSupplier method suppliers}.
*
@@ -267,7 +240,6 @@ public final class ComputerContext {
computerScheduler == null ? new ComputerThread(1) : computerScheduler,
mainThreadScheduler == null ? new NoWorkMainThreadScheduler() : mainThreadScheduler,
luaFactory == null ? CobaltLuaMachine::new : luaFactory,
apiFactories == null ? List.of() : apiFactories,
LuaMethodSupplier.create(genericMethods == null ? List.of() : genericMethods),
PeripheralMethodSupplier.create(genericMethods == null ? List.of() : genericMethods)
);

View File

@@ -21,7 +21,7 @@ public abstract class ComputerAccess implements IComputerAccess {
private static final Logger LOG = LoggerFactory.getLogger(ComputerAccess.class);
private final IAPIEnvironment environment;
private final Set<String> mounts = new HashSet<>();
private final Set<String> mounts = new HashSet<>(0);
protected ComputerAccess(IAPIEnvironment environment) {
this.environment = environment;

View File

@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.computer;
import dan200.computercraft.api.lua.ILuaAPI;
/**
* Hooks for managing the lifecycle of an API. This allows adding additional logic to an API's {@link ILuaAPI#startup()}
* and {@link ILuaAPI#shutdown()} methods.
*
* @see ILuaAPI
* @see Computer#addApi(ILuaAPI, ApiLifecycle)
*/
public interface ApiLifecycle {
/**
* Called before the API's {@link ILuaAPI#startup()} method, may be used to set up resources.
*/
default void startup() {
}
/**
* Called after the API's {@link ILuaAPI#shutdown()} method, may be used to tear down resources.
*/
default void shutdown() {
}
}

View File

@@ -9,18 +9,14 @@ import dan200.computercraft.api.lua.ILuaAPI;
import javax.annotation.Nullable;
/**
* A wrapper for {@link ILuaAPI}s which optionally manages the lifecycle of a {@link ComputerSystem}.
* A wrapper for {@link ILuaAPI}s which provides an optional shutdown hook to clean up resources.
*
* @param api The original API.
* @param lifecycle The optional lifecycle hooks for this API.
*/
final class ApiWrapper {
private final ILuaAPI api;
private final @Nullable ComputerSystem system;
ApiWrapper(ILuaAPI api, @Nullable ComputerSystem system) {
this.api = api;
this.system = system;
}
record ApiWrapper(ILuaAPI api, @Nullable ApiLifecycle lifecycle) {
public void startup() {
if (lifecycle != null) lifecycle.startup();
api.startup();
}
@@ -30,7 +26,7 @@ final class ApiWrapper {
public void shutdown() {
api.shutdown();
if (system != null) system.unmountAll();
if (lifecycle != null) lifecycle.shutdown();
}
public ILuaAPI api() {

View File

@@ -184,6 +184,10 @@ public class Computer {
executor.addApi(api);
}
public void addApi(ILuaAPI api, ApiLifecycle lifecycleHooks) {
executor.addApi(api, lifecycleHooks);
}
long getUniqueTaskId() {
return lastTaskId.incrementAndGet();
}

View File

@@ -162,13 +162,6 @@ final class ComputerExecutor implements ComputerScheduler.Worker {
addApi(new PeripheralAPI(environment, context.peripheralMethods()));
addApi(new OSAPI(environment));
if (CoreConfig.httpEnabled) addApi(new HTTPAPI(environment));
// Load in the externally registered APIs.
for (var factory : context.apiFactories()) {
var system = new ComputerSystem(environment);
var api = factory.create(system);
if (api != null) apis.add(new ApiWrapper(api, system));
}
}
@Override
@@ -190,6 +183,10 @@ final class ComputerExecutor implements ComputerScheduler.Worker {
apis.add(new ApiWrapper(api, null));
}
void addApi(ILuaAPI api, ApiLifecycle lifecycleHooks) {
apis.add(new ApiWrapper(api, lifecycleHooks));
}
/**
* Schedule this computer to be started if not already on.
*/

View File

@@ -1,53 +0,0 @@
// SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.core.computer;
import dan200.computercraft.api.lua.IComputerSystem;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.apis.ComputerAccess;
import dan200.computercraft.core.apis.IAPIEnvironment;
import javax.annotation.Nullable;
import java.util.Map;
/**
* Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs.
*
* @see ILuaAPIFactory
* @see ApiWrapper
*/
public class ComputerSystem extends ComputerAccess implements IComputerSystem {
private final IAPIEnvironment environment;
ComputerSystem(IAPIEnvironment environment) {
super(environment);
this.environment = environment;
}
@Override
public String getAttachmentName() {
return "computer";
}
@Nullable
@Override
public String getLabel() {
return environment.getLabel();
}
@Override
public Map<String, IPeripheral> getAvailablePeripherals() {
// TODO: Should this return peripherals on the current computer?
return Map.of();
}
@Nullable
@Override
public IPeripheral getAvailablePeripheral(String name) {
return null;
}
}

View File

@@ -7,9 +7,13 @@
-- @module textutils
-- @since 1.2
local expect = dofile("rom/modules/main/cc/expect.lua")
local pgk_env = setmetatable({}, { __index = _ENV })
pgk_env.require = dofile("rom/modules/main/cc/require.lua").make(pgk_env, "rom/modules/main")
local require = pgk_env.require
local expect = require("cc.expect")
local expect, field = expect.expect, expect.field
local wrap = dofile("rom/modules/main/cc/strings.lua").wrap
local wrap = require("cc.strings").wrap
--- Slowly writes string text at current cursor position,
-- character-by-character.

View File

@@ -1,3 +1,16 @@
# New features in CC: Tweaked 1.112.0
* Report a custom error when using `!` instead of `not`.
* Update several translations (zyxkad, MineKID-LP).
* Add `cc.strings.split` function.
Several bug fixes:
* Fix `drive.getAudioTitle` returning `nil` when no disk is inserted.
* Preserve item data when upgrading pocket computers.
* Add missing bounds check to `cc.strings.wrap` (Lupus950).
* Fix dyed turtles rendering transparent.
* Fix dupe bug when crafting with turtles.
# New features in CC: Tweaked 1.111.1
* Add support for data-driven turtle upgrades.

View File

@@ -1,11 +1,14 @@
New features in CC: Tweaked 1.111.1
New features in CC: Tweaked 1.112.0
* Add support for data-driven turtle upgrades.
* Report a custom error when using `!` instead of `not`.
* Update several translations (zyxkad, MineKID-LP).
* Add `cc.strings.split` function.
Several bug fixes:
* Fix monitors not rendering on NeoForge.
* Fix turtle labels not rendering.
* Fix compatibility with newer versions of NeoForge.
* Fix heights of turtle flags.
* Fix `drive.getAudioTitle` returning `nil` when no disk is inserted.
* Preserve item data when upgrading pocket computers.
* Add missing bounds check to `cc.strings.wrap` (Lupus950).
* Fix dyed turtles rendering transparent.
* Fix dupe bug when crafting with turtles.
Type "help changelog" to see the full version history.

View File

@@ -118,8 +118,8 @@ end
--- Expect a number to be within a specific range.
--
-- @tparam number num The value to check.
-- @tparam number min The minimum value, if nil then `-math.huge` is used.
-- @tparam number max The maximum value, if nil then `math.huge` is used.
-- @tparam[opt=-math.huge] number min The minimum value.
-- @tparam[opt=math.huge] number max The maximum value.
-- @return The given `value`.
-- @throws If the value is outside of the allowed range.
-- @since 1.96.0

View File

@@ -8,7 +8,8 @@
-- @since 1.95.0
-- @see textutils For additional string related utilities.
local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")).expect
local expect = require("cc.expect")
local expect, range = expect.expect, expect.range
--[[- Wraps a block of text, so that each line fits within the given width.
@@ -32,7 +33,7 @@ local function wrap(text, width)
expect(1, text, "string")
expect(2, width, "number", "nil")
width = width or term.getSize()
range(width, 1)
local lines, lines_n, current_line = {}, 0, ""
local function push_line()
@@ -109,7 +110,63 @@ local function ensure_width(line, width)
return line
end
--[[- Split a string into parts, each separated by a deliminator.
For instance, splitting the string `"a b c"` with the deliminator `" "`, would
return a table with three strings: `"a"`, `"b"`, and `"c"`.
By default, the deliminator is given as a [Lua pattern][pattern]. Passing `true`
to the `plain` argument will cause the deliminator to be treated as a litteral
string.
[pattern]: https://www.lua.org/manual/5.3/manual.html#6.4.1
@tparam string str The string to split.
@tparam string deliminator The pattern to split this string on.
@tparam[opt=false] boolean plain Treat the deliminator as a plain string, rather than a pattern.
@tparam[opt] number limit The maximum number of elements in the returned list.
@treturn { string... } The list of split strings.
@usage Split a string into words.
require "cc.strings".split("This is a sentence.", "%s+")
@usage Split a string by "-" into at most 3 elements.
require "cc.strings".split("a-separated-string-of-sorts", "-", true, 3)
@see table.concat To join strings together.
@since 1.112.0
]]
local function split(str, deliminator, plain, limit)
expect(1, str, "string")
expect(2, deliminator, "string")
expect(3, plain, "boolean", "nil")
expect(4, limit, "number", "nil")
local out, out_n, pos = {}, 0, 1
while not limit or out_n < limit - 1 do
local start, finish = str:find(deliminator, pos, plain)
if not start then break end
out_n = out_n + 1
out[out_n] = str:sub(pos, start - 1)
-- Require us to advance by at least one character.
if finish < start then error("separator is empty", 2) end
pos = finish + 1
end
if pos == 1 then return { str } end
out[out_n + 1] = str:sub(pos)
return out
end
return {
wrap = wrap,
ensure_width = ensure_width,
split = split,
}

View File

@@ -2,7 +2,7 @@
--
-- SPDX-License-Identifier: MPL-2.0
describe("cc.pretty", function()
describe("cc.strings", function()
local str = require("cc.strings")
describe("wrap", function()
@@ -11,6 +11,8 @@ describe("cc.pretty", function()
str.wrap("test string is long", 11)
expect.error(str.wrap, nil):eq("bad argument #1 (string expected, got nil)")
expect.error(str.wrap, "", false):eq("bad argument #2 (number expected, got boolean)")
expect.error(str.wrap, "", 0):eq("number outside of range (expected 0 to be within 1 and inf)")
end)
it("wraps lines", function()
@@ -42,4 +44,33 @@ describe("cc.pretty", function()
expect(str.ensure_width("test string is long", 15)):eq("test string is ")
end)
end)
describe("split", function()
it("splits with empty segments", function()
expect(str.split("", "%-")):same { "" }
expect(str.split("-", "%-")):same { "", "" }
expect(str.split("---", "%-")):same { "", "", "", "" }
expect(str.split("-a", "%-")):same { "", "a" }
expect(str.split("a-", "%-")):same { "a", "" }
end)
it("cannot split with an empty separator", function()
expect.error(str.split, "abc", ""):eq("separator is empty")
end)
it("splits on patterns", function()
expect(str.split("a.bcd ef", "%W+")):same { "a", "bcd", "ef" }
end)
it("splits on literal strings", function()
expect(str.split("a-bcd-ef", "-", true)):same { "a", "bcd", "ef" }
end)
it("accepts a limit", function()
expect(str.split("foo-bar-baz-qux-quyux", "-", true, 3)):same { "foo", "bar", "baz-qux-quyux" }
expect(str.split("foo-bar-baz", "-", true, 5)):same { "foo", "bar", "baz" }
expect(str.split("foo-bar-baz", "-", true, 1)):same { "foo-bar-baz" }
expect(str.split("foo-bar-baz", "-", true, 1)):same { "foo-bar-baz" }
end)
end)
end)