Historically CC has supported two modes when working with file handles
(and HTTP requests):
- Text mode, which reads/write using UTF-8.
- Binary mode, which reads/writes the raw bytes.
However, this can be confusing at times. CC/Lua doesn't actually support
unicode, so any characters beyond the 0.255 range were replaced with
'?'. This meant that most of the time you were better off just using
binary mode.
This commit unifies text and binary mode - we now /always/ read the raw
bytes of the file, rather than converting to/from UTF-8. Binary mode now
only specifies whether handle.read() returns a number (and .write(123)
writes a byte rather than coercing to a string).
- Refactor the entire handle hierarchy. We now have an AbstractMount
base class, which has the concrete implementation of all methods. The
public-facing classes then re-export these methods by annotating
them with @LuaFunction.
These implementations are based on the
Binary{Readable,Writable}Handle classes. The Encoded{..}Handle
versions are now entirely removed.
- As we no longer need to use BufferedReader/BufferedWriter, we can
remove quite a lot of logic in Filesystem to handle wrapping
closeable objects.
- Add a new WritableMount.openFile method, which generalises
openForWrite/openForAppend to accept OpenOptions. This allows us to
support update mode (r+, w+) in fs.open.
- fs.open now uses the new handle types, and supports update (r+, w+)
mode.
- http.request now uses the new readable handle type. We no longer
encode the request body to UTF-8, nor decode the response from UTF-8.
- Websockets now return text frame's contents directly, rather than
converting it from UTF-8. Sending text frames now attempts to treat
the passed string as UTF-8, rather than treating it as latin1.
The two implementations aren't entirely compatible - the implementation
returned by .of will throw an NPE on .contains(null), whereas the
Collections implementations just return false. However, we try to avoid
passing null to collections methods, so this should be safe.
There's no strong reason to do this, but it helps make the code a little
more consistent
Historically we've used copy-cat to provide a web-based emulator for
running example code on our documentation site. However, copy-cat is
often out-of-date with CC:T, which means example snippets fail when you
try to run them!
This commit vendors in copy-cat (or rather an updated version of it)
into CC:T itself, allowing us to ensure the emulator is always in sync
with the mod.
While the ARCHITECTURE.md documentation goes into a little bit more
detail here, the general implementation is as follows
- In project/src/main we implement the core of the emulator. This
includes a basic reimplementation of some of CC's classes to work on
the web (mostly the HTTP API and ComputerThread), and some additional
code to expose the computers to Javascript.
- This is all then compiled to Javascript using [TeaVM][1] (we actually
use a [personal fork of it][2] as there's a couple of changes I've
not upstreamed yet).
- The Javascript side then pulls in the these compiled classes (and
the CC ROM) and hooks them up to [cc-web-term][3] to display the
actual computer.
- As we're no longer pulling in copy-cat, we can simplify our bundling
system a little - we now just compile to ESM modules directly.
[1]: https://github.com/konsoletyper/teavm
[2]: https://github.com/SquidDev/teavm/tree/squid-patches
[3]: https://github.com/squiddev-cc/cc-web-term
- Add AbstractInMemoryMount, which contains all of ArchiveMount's file
tree logic, but not the caching functionality.
- Convert MemoryMount to inherit from AbstractInMemoryMount.
- Add a helper method to add a file to an AbstractInMemoryMount, and
use that within {Resource,Jar}Mount.
There's definitely more work to be done here - it might be nice to split
FileEntry into separate Directory and File interfaces, or at least make
them slightly more immutable, but that's definitely a future job.
- Fix mainThread=true methods calling IArguments.escapes too late. This
should be done before scheduling on the main thread, not on the main
thread itself!
- Fix VarargsArguments.escapes not checking that the argument haven't
been closed. This is slightly prone to race conditions, but I don't
think it's worth the overhead of tracking the owning thread.
Maybe when panama and its resource scopes are released.
Thanks Sara for pointing this out!
Slightly irked that none of our tests caught this. Alas.
Also fix a typo in AddressPredicate. Yes, no commit discipline.
In this case, we use Lua's tostring(x) semantics (well, modulo
metamethods), instead of Java's Object.toString(x) call. This ensures
that values are formatted (mostly) consistently between Lua and Java
methods.
- Add IArguments.getStringCoerced, which uses Lua's tostring semantics.
- Add a Coerced<T> wrapper type, which says to use the .getXCoerced
methods. I'm not thrilled about this interface - there's definitely
an argument for using annotations - but this is probably more
consistent for now.
- Convert existing methods to use this call.
Closes#1445
This is a horrible commit: It's a breaking change in a pretty subtle
way, which means it won't be visible while updating. Fortunately I think
the only mod on 1.19.4 is Plethora, but other mods (Mek, Advanced
Peripherals) may be impacted when they update. Sorry!
For some motivation behind the original issue:
The default IArguments implementation (VarargArguments) lazily converts
Lua arguments to Java ones. This is mostly important when passing tables
to Java functions, as we can avoid the conversion entirely if the
function uses IArguments.getTableUnsafe.
However, this lazy conversion breaks down if IArguments is accessed on a
separate thread, as Lua values are not thread-safe. Thus we need to
perform this conversion before the cross-thread sharing occurs.
Now, ideally this would be an implementation detail and entirely
invisible to the user. One approach here would be to only perform this
lazy conversion for methods annotated with @LuaFunction(unsafe=true),
and have it be eager otherwise.
However, the peripheral API gets in the way here, as it means we can no
longer inspect the "actual" method being invoked. And so, alas, this
must leak into the public API.
TLDR: If you're getting weird errors about scope, add an
IArguments.escapes() call before sharing the arguments between threads.
Closes#1384
- Fix several inaccuracies with several files not marking Dan's
authorship. Most of these are new files, where the code was moved from
somewhere else:
- In the public API: IDynamicLuaObject, ILuaAPI, TaskCallbakc,
IDynamicPeripheral, UpgradeBase
- In the ROM: fs, http, require
- Do not mark Dan as an author for entirely new code. This affects
DetailHelpers, DropConsumer, FluidData, InventoryMethods, ItemDetails,
MonitorRenderState, NoTermComputerScreen, Palette, PlatformHelperImpl,
UploadFileMessage, the Terminal tests, and any speaker-related files.
- Relicence many files under the MPL where we have permission to do
so. See #1339 for further details.
Thank you to everyone who has contributed so far! Cannot overstate how
appreciated it is <3.
- Consult __name in native code too. Closes#1355. This has the added
advantage that unconvertable values (i.e. functions) will now
correctly be reported as their original type, not just nil.
- Fix the error message in cc.expect, so it matches the rest of Lua.
This has been bugging me for years, and I keep forgetting to change
it.
This adds SPDX license headers to all source code files, following the
REUSE[1] specification. This does not include any asset files (such as
generated JSON files, or textures). While REUSE does support doing so
with ".license" files, for now we define these licences using the
.reuse/dep5 file.
[1]: https://reuse.software/
I've been meaning to fix this for over 6 years, and just kept
forgetting.
Previously ILuaContext.executeMainThreadTask worked by running
ILuaContext.issueMainThreadTask, pulling task_complete events, and then
returning the results.
While this makes the implementation simple, it means that the task's
results were converted into Lua values (in order to queue the event) and
then back into Java ones (when the event was pulled), before eventually
being converted into Lua once more.
Not only is this inefficient, as roundtripping isn't lossless, you
couldn't return functions or rich objects from main thread functions
(see https://github.com/dan200/ComputerCraft/issues/125).
We now store the return value on the Java side and then return that when
the receiving the task_complete event - the event no longer carries the
result. Note this does not affect methods using issueMainThreadTask!
- Correctly handle FileOperationExceptions for the root mount.
- Remove some checks from MountWrapper: Mount/WritableMount should do
these already!
- Normalise file paths, always using a '/'.
- Separate FileMount into separate FileMount and WritableFileMount
classes. This separates the (relatively simple) read-only code from
the (soon to be even more complex) read/write code.
It also allows you to create read-only mounts which don't bother with
filesystem accounting, which is nice.
- Make openForWrite/openForAppend always return a SeekableFileHandle.
Appendable files still cannot be seeked within, but that check is now
done on the FS side.
- Refactor the various mount tests to live in test contract interfaces,
allowing us to reuse them between mounts.
- Clean up our error handling a little better. (Most) file-specific code
has been moved to FileMount, and ArchiveMount-derived classes now
throw correct path-localised exceptions.
In classic squid tradition: 20% code, and 80% test logic.
Closes#962. Alas, whoever reported this has deleted their account, so
they can't even be happy about it :(.
- Remove deprecated API members in prep for 1.19.3. This allows us to
remove the mc-stubs and forge-stubs projects.
- Make several methods take a MinecraftServer instead of a Level (or
nothing at all).
- Remove I prefixes from a whole bunch of interfaces, making things a
little more consistent with Java conventions.
This avoids touching the "main" interfaces people consume for now. I
want to do that another Minecraft version, to avoid making the update
too painful.
- Remove IFileSystem and associated getters. This has never worked very
well and I don't think has got much (any?) usage.
- Publish javadoc again: for now this is just the common-api
- Remove all dependencies from the published Forge jar. This is
technically not needed (fg.deobf does this anyway), but seems
sensible.
We'll do this everywhere eventually, but much easier to do it
incrementally:
- Use checker framework to default all field/methods/parameters to
@Nonnull.
- Start using ErrorProne[1] and NullAway[2] to check for possible null
pointer issues. I did look into using CheckerFramework, but it's much
stricter (i.e. it's actually Correct). This is technically good, but
is a much steeper migration path, which I'm not sure we're prepared
for yet!
[1]: https://github.com/google/error-prone
[2]: https://github.com/uber/NullAway
It should be possible to consume the ComputerCraft's core (i.e.
non-Minecraft code) in other projects, such as emulators. While this
has been possible for years, it's somewhat tricky from a maintenance
perspective - it's very easy to accidentally add an MC dependency
somewhere!
By publishing a separate "core" jar, we can better distinguish the
boundaries between our Lua runtime and the Minecraft-specific code.
Ideally we could have one core project (rather than separate core and
core-api modules), and publish a separate "api" jar, like we do for the
main mod. However, this isn't really possible to express using Maven
dependencies, and so we must resort to this system.
Of course, this is kinda what the Java module system is meant to solve,
but unfortunately getting that working with Minecraft is infeasible.