When a turtle was unloaded but not actually disposed of, the
m_peripheral map hangs around. As a result, when creating a new
ServerComputer, the peripherals aren't considered changed and so they're
never attached.
Fixes#50.
Also fix that blumin' deprecated method which has been around for a wee
while now.
The Computer class currently has several resposiblities such as storing
id/label, managing redstone/peirpherals, handling management of the
computer (on/off/events) and updating the output.
In order to simplify this a little bit, we move our IAPIEnvironment
implementation into a separate file, and store all "world state"
(redstone + peripherals) in there. While we still need to have some
level of updating them within the main Computer instance, it's
substantially simpler.
- Fire close events instead of failure when open websockets error.
- Handle ping events. I thought I was doing this already, but this
requires a WebsocketProtocolHandler. Fixes#118
- Run optipng on all our images. This has very little effect on most of
them (as they're all so small anyway), but has resulted in a 50%
reduction in some cases.
- Run Proguard on our shadowed dependencies (Cobalt).
- Minify our JSON files, stripping all whitespace. This is mostly
useful for FML's annotation cache, as that's a massive file, but
still a semi-useful optimisation to make.
This has helped reduce the jar by about 110kb, which isn't much but
still feels somewhat worth it.
The latest version of Cobalt has several major changes, which I'm
looking forward to taking advantage of in the coming months:
- The Lua interpreter has been split up from the actual LuaClosure
instance. It now runs multiple functions within one loop, handling
pushing/popping and resuming method calls correctly.
This means we have a theoretically infinite call depth, as we're no
longer bounded by Java's stack size. In reality, this is limited to
32767 (Short.MAX_VALUE), as that's a mostly equivalent to the limits
PUC Lua exposes.
- The stack is no longer unwound in the event of errors. This both
simplifies error handling (not that CC:T needs to care about that)
but also means one can call debug.traceback on a now-dead coroutine
(which is more useful for debugging than using xpcall).
- Most significantly, coroutines are no longer each run on a dedicated
thread. Instead, yielding or resuming throws an exception to unwind
the Java stack and switches to a different coroutine.
In order to preserve compatability with CC's assumption about LuaJ's
threading model (namely that yielding blocks the thread), we also
provide a yieldBlock method (which CC:T consumes). This suspends the
current thread and switches execution to a new thread (see
SquidDev/Cobalt@b5ddf164f1 for more
details). While this does mean we need to use more than 1 thread,
it's still /substantially/ less than would otherwise be needed.
We've been running these changes on SwitchCraft for a few days now and
haven't seen any issues. One nice thing to observe is that the number of
CC thread has gone down from ~1.9k to ~100 (of those, ~70 are dedicated
to running coroutines). Similarly, the server has gone from generating
~15k threads over its lifetime, to ~3k. While this is still a lot, it's
a substantial improvement.
This is far more elegant than our weird method of baking things and
manually inserting them into the model map. Also means we no longer need
the whole turtle_dynamic thing.
We moved the direction call within the if block, but never actally
updated the condition! I'm on a roll of stupid bug fixes today, which
really isn't a good sign.
- Only have computers implement custom block drop logic: everything
else only drops in creative mode.
- Fix redstone inputs not being received correctly. Introduced in
8b86a954ee, yes I'm a silly billy.
- Only update the neighbour which changed.
- Convert terminals from a polling-based system to a more event-driven
one: they now accept an onChanged callback, which marks the parent as
dirty.
- Schedule ticks when monitors are marked as dirty.
- Add several missing @Overrides. This has nothing to do with the rest
of the changes, but I'm bad at good git practice.
- Merge BlockPeripheralBase and BlockPeripheral, as no other classes
extended the former.
- Make BlockPeripheral use ITilePeripheral instead of
TilePeripheralBase. This allows us to use other, non-ticking tiles
instead.
- Convert advanced and normal modems to extend from a generic
TileWirelessModemBase class, and thus neither now tick.
- Move getPeripheralType and getLabel from IPeripheralTile to
TilePeripheralBase. These were mostly constant on all other tiles, so
were rather redundant.
- Make TileAdvancedModem extend TileGeneric, and be non-ticking (using
similar logic to all other blocks).
- Move updateTick onto BlockGeneric/TileGeneric instead of the full
wired modem, as it is used by several tiles now.
- Make *Cable extend from *Generic, and schedule ticks instead of
running every tick.
We currently generate the crafting item once when the upgrade is first
created, and cache it for the duration of the game. As the item never
changes throughout the game, and constructing a stack is a little
expensive (we need to fire an event, etc...), the caching is worth
having.
However, some mods may register capabilities after we've constructed our
ItemStack. This means the capability will be present on other items but
not ours, meaning they are not considered equivalent, and thus the item
cannot be equipped.
In order to avoid this, we use compare items using their share-tag, like
Forge's IngredientNBT. This means the items must still be "mostly" the
same (same enchantements, etc...), but allow differing capabilities.
See NillerMedDild/Enigmatica2Expert#655 for the original bug report -
in this case, Astral Sourcery was registering the capability in init,
but we construct upgrades just before then.
- Move IDirectionalTile constraint from IPeripheralTile to
TilePeripheralBase.
- Make *WiredModemFull no longer inherit from *PeripheralBase. While
there is still some shared logic (namely in the syncing of "anim"),
it's largely fine as we don't store label or direction in NBT.
- Add a TickScheduler. This is a thread-safe version of
World.scheduleUpdate. We simply build a set of all TEs, and schedule
them to be updated the next tick.
- Make ModemState receive an "onChanged" listener, which is fired
whenever the modem changes.
- Make WiredModemFull no longer tick, instead scheduling updates when
it is first loaded and whenever the modem changes.
FileSystemMount was originally added to allow using ReadableByteChannels
instead of InputStreams. However, as zip files do not allow seeking,
there is no benefit of using them over the original JarMount (which we
need to preserve for backwards compatibility).
Instead of maintaining two near-identical mounts, we remove the
FileSystemMount and rewrite the JarMount implementation with several
improvements:
- Rewrite the jar scanning algorithm to be closer to 1.13+'s data pack
mount. This means we no longer require the jar file to have
directories before the file (though this was not a problem in
practice).
- Add all JarMounts to a ReferenceQueue, closing up the ZipFile when
they have been garbage collected (fixes#100).
- Cache the contents of all files for 60 seconds (with some constraints
on size). This allows us to seek on ROM files too (assuming they are
small), by reading the whole thing into memory.
The cache is shared across all mounts, and has a 64MiB limit, and
thus should not have an adverse impact on memory.
This is done in 1.13+ for items and blocks, so we might as well do it
for upgrades now. Note we can't do it for ender pocket modems, as the
upgrade ID is spelled incorrectly there.
- For those where placement is stored in the metadata (computers),
don't also set it in onBlockPlacedBy.
- Remove .getDefaultState(int, EnumFacing) override, as this means we
have more control over what is passed to us (namely, placer's
direction too).
"/computercraf" auto-completes to "/computercraft_copy" instead of
"/computercraft", which is rather annoying, as the former is not meant
to be used normally.
It's rather embarassing that it's been restructured _again_, but I think
this is a nice middle-ground. The previous implementation was written
mostly for Fabric, which doesn't always map perfectly to Forge.
- Move the message identifier into the registration phrase. It's not
really a property of the message itself, rather a property of the
registry, so better suited there.
- Move message handling into the message itself. Honestly, it was just
ending up being rather messy mixing the logic in two places.
This also means we can drop some proxy methods, as it's easier to
have conditionally loaded methods.
- Move network registry into a dedicated class, as that's what we're
doing for everything else.
This means we can avoid several rather ugly instances of getItemBlock
and a cast. We also derive the ItemBlock's registered name from the
block's name, which makes the register a little less ugly.
- Move the "world directory" getter out of the proxy - we can just use
Forge's code here.
- Remove the server proxies, as both were empty. We don't tend to
register any dedicated-server specific code, so I think we can leave
them out.
- All "named" entries (blocks, items, recipes, TEs and pocket/turtle
upgrades) are registeredin one place.
- Most client side models/textures are registered in ClientRegistry -
we can't do item colours or TEs for now, as these aren't event based.
- A little cleanup to how we handle ItemPocketComputer models.
This offers several advantages
- Less registration code: the subscribers are reigstered automatically,
and we don't need to worry about sided-proxies.
- We no longer have so many .instance() calls.
- Move SpeakerPeripheral's TileSpeaker functionality to a sub-class.
- Use Vec3d instead of BlockPos for speaker's positions.
- Use WorldUtil.dropItemStack to spawn in items.
- Remove redundant lock on ModemPeripheral.
- We now error if there are too many websockets, instead of queuing
them up. As these have a more explicit "lifetime", it could be
confusing if http.websocket just blocks indefinitely.
- Fix a CCME when cleaning up resources.
- Move all HTTP tasks to a unified "MonitoredResource" model. This
provides a uniform way of tracking object's lifetimes and disposing
of them when complete.
- Rewrite HTTP requests to use Netty instead of standard Java. This
offers several advantages:
- We have access to more HTTP verbs (mostly PATCH).
- We can now do http -> https redirects.
- We no longer need to spawn in a new thread for each HTTP request.
While we do need to run some tasks off-thread in order to resolve
IPs, it's generally a much shorter task, and so is less likely to
inflate the thread pool.
- Introduce several limits for the http API:
- There's a limit on how many HTTP requests and websockets may exist
at the same time. If the limit is reached, additional ones will be
queued up until pending requests have finished.
- HTTP requests may upload a maximum of 4Mib and download a maximum
of 16Mib (configurable).
- .getResponseCode now returns the status text, as well as the status
code.
While Plethora has been updated to no longer require these, it's
probably worth keeping them around a little longer, as people may not
upgrade them in sync.
We've had this on SC for most of a year now (and SC 1 for most of its
lifetime) and not seen any issues. I'm fairly confident that it does not
allow breaking the sandbox.
- Move configuration loading into a separate file, just so it doesn't
clutter up ComputerCraft.java.
- Normalise property names, so they're all snake_case.
- Split properties into separate categories (http, turtle, peripheral),
so the main one is less cluttered.
- Define an explicit ordering of each category.
- Provide whether a message was binary or text in websocket_message
and handle.receive(). (Fixes#96)
- Provide an optional reason and status code within the websocket_close
event.
Off topic, but also cleanup the file handles a little.
- Remove redundant constructors and super calls
- Standardise naming of texture fields
- Always use postfix notations for loops
- Cleanup several peripheral classes
Previously we would send computer state (labels, id, on/off) through the
ClientComputer rather than as part of the TE description. While this
mostly worked fine, it did end up making things more complex than they
needed to be.
We sync most data to the tile each tick, so there's little risk of
things getting out of date.
There's several reasons for this change:
- Try to make ComputerCraft.java less monolithic by moving
functionality into separate module-specific classes.
- Hopefully make the core class less Minecraft dependent, meaning
emulators are a little less dependent on anything outside of /core.
Note we still need /some/ methods in the main ComputerCraft class in
order to maintain backwards compatibility with Plethora and
Computronics.
Many bits of IInventory (open/close, fields, etc...) are not actually
needed most implementations, so we can clean things up a little with a
common interface.
Some methods act the same on both sides, and so can be in utility
classes. Others are only needed on one side, and so do not really need
to be part of the proxy.
- Remove TurtleVisionCamera. It would be possible to add this back in
the future, but for now it is unused and so should be removed.
- Move frame info (cursor blink, current render frame) into a
FrameInfo class.
- Move record methods (name, playing a record) into a RecordUtil class.
- getPickBlock is now implemented directly on computers and turtles,
rather than on the tile.
- Bounding boxes are handled on the block rather than tile. This ends
up being a little ugly in the case of BlockPeripheral, but it's not
the end of the world.
- Explosion resistance is only implemented for turtles now.
- Replace BlockCableCableVariant with a boolean, removing any custom
logic around it.
- Replace our previous cable Forge-blockstate with a vanilla one, which
handles cable connections in the model logic.
This ends up being slightly cleaner as we can rely on Minecraft's own
model dependency system. Also reduces reliance on Forge's APIs, which
_potentially_ makes porting a little easier.
- Remove the two redstone commands. These are not used (maybe only by
CCEdu), and so are somewhat redundant.
- Inline the select command, converting it to a lambda.
- Inline the attack/dig commands, as they did little more than wrap the
tool command.
As CCEdu has not been updated, and is unlikely to be updated as Dan does
not have the rights to open source it, we're removing explicit support
for now.
If an alternative arises in the future, it would be good to support, but
in a way which requires less workarounds in CC's core.
Most upgrades provides a couple of constant getters (upgrade ID,
adjective, crafting item). We move the getters into a parent class and
pass the values in via the constructor instead.
Also do a tiny bit of cleanup to the upgrades. Mostly just reducing
nesting, renaming fields, etc...
- Only generate resource pack mounts if the desired directory exists.
- Allow mounting files, as well as directories (fixes#90).
As always, also a wee bit of cleanup to some of the surrounding code.
This allows us to block JEI processing key events such as "o", meaning
the GUI is not constantly toggled when interacting with a turtle.
Also clean up the widget code, as there's a lot of functionality here
which only is needed in CCEdu.
This is implemented in a rather ugly way: we register a client command
(/computercraft_copy) which updates the clipboard, and run that via a
click handler on the chat message.
This hopefully makes wired modems a little easier to use. We'll see.
I don't think anyone has actually ended up using this, so it's unlikely
to break anything (though do tell me if this is the case). On the flip
side, this allows us to queue events on multiple computers, and means
we can provide a little more documentation.
Using turtle.suck on an inventory filled with tools would fill the
entire chest with said item, rather than extracting a single item. In
order to avoid that, we clamp the extract limit to the max stack size
when first extracting an item.
This also inlines the makeSlotList logic, which means we can avoid
creating an array for each inventory operation. This probably won't
have any meaninful performance impact (even on large inventories), but
is a nice optimisation to make.
- Remove a redundant logger
- Provide a getter for the ComputerCraft thread group. This allows us
to monitor child threads within prometheus.
- Replace a deprecated call with a fastutils alternative.
Some mods (*cough* Computronics *cough*) directly access this class,
rather than using the API. We add this back to ensure they still behave
as expected.
Truth be told, I can't really complain, as Plethora also does dodgy
thing with CC internals.
- Keep track of the number of created and destroyed coroutines for each
computer.
- Run coroutines with a thread pool executor, which will keep stale
threads around for 60 seconds. This substantially reduces the
pressure from short-lived coroutines.
- Update to the latest Cobalt version.
- Introduce a ModemState, which shares the open channels across all
modem instances of a wired modem.
- Keep a set of computers for all modem peripherals.
- Keep a map of computers -> (string, peripheral) for wired modem
peripherals. We shouldn't need this one, as you cannot attach one
modem to another, but it's good to be consistent.
One major change here is that modems will continue to be "on", even if
no computers are attached. This would substantially increase
implementation complexity, so I think this is an acceptable compromise
for now.
Should fix#74
Java configured the charset decoders/encoders for streams to REPLACE
malformed characters rather than the default REPORT. It does not do the
same for channels, and so we were catching an IO exception and returning
null.