1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-11-18 15:54:54 +00:00
Commit Graph

226 Commits

Author SHA1 Message Date
Jonathan Coates
e92c2d02f8
Fix turtle.suck reporting incorrect error
Our GatedPredicate hack was clever, but also fundamentally didn't work.
The predicate is called before extraction, so if extraction fails (for
instance, canTakeItemThroughFace returns false), then we still think an
item has been removed.

To fix that, we inline StorageUtil.move, specialising it for what we
need.
2024-03-16 21:27:21 +00:00
Jonathan Coates
f8ef40d378
Add a method for checking peripheral equality
This feels a little overkill, but nice to standardise how this code
looks.

There's a bit of me which wonders if we should remove
IPeripheral.equals, and just use Object.equals, but I do also kinda like
the explicitness of the current interface? IDK.
2024-03-16 14:01:22 +00:00
Jonathan Coates
61f9b1d0c6
Send entire DFPWM encoder state to the client
This ensures the client decoder is in sync with the server. Well, mostly
- we don't handle the anti-jerk, but that should correct itself within a
few samples.

Fixes #1748
2024-03-15 18:25:57 +00:00
Jonathan Coates
b7df91349a
Rewrite computer selectors
This adds support for computer selectors, in the style of entity
selectors. The long-term goal here is to replace our existing ad-hoc
selectors. However, to aid migration, we currently support both - the
previous one will most likely be removed in MC 1.21.

Computer selectors take the form @c[<key>=<value>,...]. Currently we
support filtering by id, instance id, label, family (as before) and
distance from the player (new!). The code also supports computers within
a bounding box, but there's no parsing support for that yet.

This commit also (finally) documents the /computercraft command. Well,
sort of - it's definitely not my best word, but I couldn't find better
words.
2024-03-12 20:12:13 +00:00
Jonathan Coates
a9191a4d4e
Don't cache the client monitor
When rendering non-origin monitors, we would fetch the origin monitor,
read its client state, and then cache that on the current monitor to
avoid repeated lookups.

However, if the origin monitor is unloaded/removed on the client, and
then loaded agin, this cache will be not be invalidated, causing us to
render both the old and new monitor!

I think the correct thing to do here is cache the origin monitor. This
allows us to check when the origin monitor has been removed, and
invalidate the cache if needed.

However, I'm wary of any other edge cases here, so for now we do
something much simpler, and remove the cache entirely. This does mean
that monitors now need to perform extra block entity lookups, but the
performance cost doesn't appear to be too bad.

Fixes #1741
2024-03-10 10:57:56 +00:00
Jonathan Coates
451a2593ce
Move WiredNode default methods to the impl 2024-03-10 10:00:52 +00:00
Jonathan Coates
d38b1da974
Don't propagate redstone when blink/label changes
Historically, computers tracked whether any world-visible state
(on/off/blinking, label and redstone outputs) had changed with a single
"has changed" flag. While this is simple to use, this has the curious
side effect of that term.setCursorBlink() or os.setComputerLabel() would
cause a block update!

This isn't really a problem in practice - it just means slightly more
block updates. However, the redstone propagation sometimes causes the
computer to invalidate/recheck peripherals, which masks several other
(yet unfixed) bugs.
2024-03-06 18:59:38 +00:00
Jonathan Coates
4daa2a2b6a
Reschedule block entities when chunks are loaded
Minecraft sometimes keeps chunks in-memory, but not actively loaded. If
we schedule a block entity to be ticked and that chunk is is then
transitioned to this partially-loaded state, then the block entity is
never actually ticked.

This is most visible with monitors. When a monitor's contents changes,
if the monitor is not already marked as changed, we set it as changed
and schedule a tick (see ServerMonitor). However, if the tick is
dropped, we don't clear the changed flag, meaning subsequent changes
don't requeue the monitor to be ticked, and so the monitor is never
updated.

We fix this by maintaining a list of block entities whose tick was
dropped. If these block entities (or rather their owning chunk) is ever
re-loaded, then we reschedule them to be ticked.

An alternative approach here would be to add the scheduled tick directly
to the LevelChunk. However, getting hold of the LevelChunk for unloaded
blocks is quiet nasty, so I think best avoided.

Fixes #1146. Fixes #1560 - I believe the second one is a duplicate, and
I noticed too late :D.
2024-02-26 19:25:38 +00:00
Jonathan Coates
84b6edab82
More efficient removal of wired nodes from networks
When we remove a wired node from a network, we need to find connected
components in the rest of the graph. Typically, this requires a
traversal of the whole graph, taking O(|V| + |E|) time.

If we remove a lot of nodes at once (such as when unloading chunks),
this ends up being quadratic in the number of nodes. In some test
networks, this can take anywhere from a few seconds, to hanging the game
indefinitely.

This attempts to reduce the cases where this can happen, with a couple
of optimisations:

 - Instead of constructing a new hash set of reachable nodes (requiring
   multiple allocations and hash lookups), we store reachability as a
   temporary field on the WiredNode.

 - We abort our traversal of the graph if we can prove the graph remains
   connected after removing the node.

There's definitely future work to be done here in optimising large wired
networks, but this is a good first step.
2024-02-24 15:02:34 +00:00
Jonathan Coates
31aaf46d09
Deprecate WiredNetwork
We don't actually need this to be in the public API.
2024-02-24 14:55:22 +00:00
Jonathan Coates
2d11b51c62
Clean up the wired network tests
- Replace usages of WiredNetwork.connect/disconnect/remove with the
   WiredNode equivalents.

 - Convert "testLarge" into a proper JMH benchmark.

 - Don't put a peripheral on every node in the benchmarks. This isn't
   entirely representative, and means the peripheral juggling code ends
   up dominating the benchmark time.
2024-02-24 14:52:44 +00:00
Jonathan Coates
27c72a4571
Use client-side commands for opening computer folders
Forge doesn't run client-side commands from sendUnsignedCommand, so we
still require a mixin there.

We do need to change the command name, as Fabric doesn't properly merge
the two command trees.
2024-01-30 22:00:36 +00:00
Jonathan Coates
f284328656
Regenerate Gradle wrapper 2024-01-29 22:14:48 +00:00
Jonathan Coates
6b83c63991
Switch to our own Gradle plugin for vanilla Minecraft
I didn't make a new years resolution to stop writing build tooling, but
maybe I should have.

This replaces our use of VanillaGradle with a new project,
VanillaExtract. This offers a couple of useful features for multi-loader
dev, including Parchment and Unpick support, both of which we now use in
CC:T.
2024-01-29 20:59:16 +00:00
Jonathan Coates
9ccee75a99
Fix the docs for ReadHandle.read's "count"
This was copied over from the old binary handle, and so states we
always return a single number if no count is given. This is only the
case when the file is opened in binary mode.
2024-01-23 22:39:49 +00:00
Jonathan Coates
359c8d6652
Reformat JSON by wrapping CachedOutput
Rather than mixing-in to CachedOutput, we just wrap our DataProviders to
use a custom CachedOutput which reformats the JSON before writing. This
allows us to drop mixins for common+non-client code.
2024-01-21 17:50:59 +00:00
Jonathan Coates
1788afacfc
Remove note about disabling websocket limits
I suspect this was copied from the file limit, which can be turned off
by setting to 0.

Fixes #1691
2024-01-21 16:32:07 +00:00
Jonathan Coates
f695f22d8a
Atomic update of disk drive item stacks
Disk drives have had a long-standing issue with mutating their contents
on the computer thread, potentially leading to all sorts of odd bugs.

We tried to fix this by moving setDiskLabel and the mounting code to run
on the main thread. Unfortunately, this means there is a slight delay to
mounts being attached, breaking disk startup.

This commit implements an alternative solution - we now do mounting on
the computer thread again. If the disk's stack is modified, we update it
in the peripheral-facing item, but not the actual inventory. The next
time the disk drive is ticked, we then sync the two items.

This does mean that there is a fraction of a tick where the two will be
out-of-sync. This isn't ideal - it would potentially be possible to
cycle through disk ids - but I don't really think that's avoidable
without significantly complicating the IMedia API.

Fixes #1649, fixes #1686.
2024-01-20 18:46:43 +00:00
Jonathan Coates
a617d0d566
Rewrite turtle upgrade modeller registration API
Originally we exposed a single registerTurtleUpgradeModellermethod which
could be called from both Fabric (during a mod's client init) and Forge
(during FMLClientSetupEvent).

This was fine until we allowed upgrades to specify model dependencies,
which would then automatically loaded, as this means model loading now
depends on upgrade modellers being loaded. Unknown to me, this is not
guaranteed to be the case on Forge - mod setup happens at the same time
as resource reloading!

Unfortunately there's not really a salvageable way of fixing this with
the current API. Forge now uses a registration event-based system,
meaning we can guarantee all modellers are loaded before models are
baked.
2024-01-16 23:00:49 +00:00
Jonathan Coates
36599b321e
Backport small changes from the 1.20.4 branch
- Add support for version overrides/exclusions in our dependency check.
   Sometimes mod loaders use different versions to vanilla, and we need
   some way to handle that.

 - Rescan wired network connections on the tick after invalidation,
   rather than when invalidated.

 - Convert some constant lambdas to static method references. Lambdas
   don't allocate if they don't capture variables, so this has the same
   performance and is a little less ugly.

 - Small code-style/formatting changes.
2024-01-16 21:42:25 +00:00
Jonathan Coates
1d6e3f4fc0
Change ComponentLookup to use ServerLevel
Makes this more consistent with the rest of the peripheral code, and our
changes in 1.20.4.
2024-01-15 08:28:59 +00:00
Jonathan Coates
30dc4cb38c
Simplify our networking multi-platform code
Historically we used Forge's SimpleChannel methods (and
PacketDistributor) to send the packets to the client. However, we don't
need to do that - it is sufficient to convert it to a vanilla packet,
and send the packet ourselves.

Given we need to do this on Fabric, it makes sense to do this on Forge
as well. This allows us to unify (and thus simplify) a lot of how packet
sending works.

At the same time, we also remove the handling of speaker audio during
decoding. We originally did this to avoid the additional copy of audio
data. However, this doesn't work on 1.20.4 (as packets aren't
encoded/decoded on singleplayer), so it makes sense to do this
Correctly(TM).

This also allows us to get rid of ClientNetworkContext.get(). We do
still need to service load this class (as Forge's networking isn't split
up in the same way Fabric's is), but we'll be able to drop that in
1.20.4.

Finally, we move the record playing code from ClientNetworkContext to
ClientPlatformHelper. This means the network context no longer needs to
be platform-specific!
2024-01-14 22:53:36 +00:00
Jonathan Coates
be4512d1c3
Construct ComponentAccesses with the BE
After embarrassing, let's do some proper work.

Rather than passing the level and position each time we call
ComponentAccess.get(), we now pass them at construction time (in the
form of the BE). This makes the consuming code a little cleaner, and is
required for the NeoForge changes in 1.20.4.
2024-01-14 17:46:37 +00:00
Jonathan Coates
b5923c4462
Flesh out the printer documentation slightly 2024-01-14 12:25:04 +00:00
Jonathan Coates
4d1e689719
Fix endPage() not updating the printer block state
This meant that we didn't show the bottom slot was full until other
items were moved in the inventory.
2024-01-14 12:23:55 +00:00
lonevox
89294f4a22
Fix incorrect Lua list indexes in NBT tags (#1678) 2024-01-10 19:16:15 +00:00
Jonathan Coates
e0889c613a
Mark "check valid item" test as required
This has passed for years now, no reason for it to be optional.
2024-01-07 13:35:38 +00:00
Jonathan Coates
e3bda2f763
Add command computers to the operator blocks tab
Fixes #1666
2024-01-03 18:42:31 +00:00
Jonathan Coates
234f69e8e5
Add a MessageType for network messages
Everything old is new again!

CC's network message implementation has gone through several iterations:

 - Originally network messages were implemented with a single class,
   which held an packet id/type and and opaque blobs of data (as
   string/int/byte/NBT arrays), and a big switch statement to decode and
   process this data.

 - In 42d3901ee3, we split the messages
   into different classes all inheriting from NetworkMessage - this bit
   we've stuck with ever since.

   Each packet had a `getId(): int` method, which returned the
   discriminator for this packet.

 - However, getId() was only used when registering the packet, not when
   sending, and so in ce0685c31f we
   removed it, just passing in a constant integer at registration
   instead.

 - In 53abe5e56e, we made some relatively
   minor changes to make the code more multi-loader/split-source
   friendly. However, this meant when we finally came to add Fabric
   support (8152f19b6e), we had to
   re-implement a lot of Forge's network code.

In 1.20.4, Forge moves to a system much closer to Fabric's (and indeed,
Minecraft's own CustomPacketPayload), and so it makes sense to adapt to
that now. As such, we:

 - Add a new MessageType interface. This is implemented by the
   loader-specific modules, and holds whatever information is needed to
   register the packet (e.g. discriminator, reader function).

 - Each NetworkMessage now has a type(): MessageType<?> function. This
   is used by the Fabric networking code (and for NeoForge's on 1.20.4)
   instead of a class lookup.

 - NetworkMessages now creates/stores these MessageType<T>s (much like
   we'd do for registries), and provides getters for the
   clientbound/serverbound messages. Mod initialisers then call these
   getters to register packets.

 - For Forge, this is relatively unchanged. For Fabric, we now
   `FabricPacket`s.
2024-01-03 10:23:41 +00:00
Jonathan Coates
9d8c933a14
Remove several usages of ComputerFamily
While ComputerFamily is still useful, there's definitely some places
where it adds an extra layer of indirection. This commit attempts to
clean up some places where we no longer need it.

 - Remove ComputerFamily from AbstractComputerBlock. The only place this
   was needed is in TurtleBlock, and that can be replaced with normal
   Minecraft explosion resistence!

 - Pass in the fuel limit to the turtle block entity, rather than
   deriving it from current family.

 - The turtle BERs now derive their model from the turtle's item, rather
   than the turtle's family.

 - When creating upgrade/overlay recipes, use the item's name, rather
   than {pocket,turtle}_family. This means we can drop getFamily() from
   IComputerItem (it is still needed on to handle the UI).

 - We replace IComputerItem.withFamily with a method to change to a
   different item of the same type. ComputerUpgradeRecipe no longer
   takes a family, and instead just uses the result's item.

 - Computer blocks now use the normal Block.asItem() to find their
   corresponding item, rather than looking it up via family.

The above means we can remove all the family-based XyzItem.create(...)
methods, which have always felt a little ugly.

We still need ComputerFamily for a couple of things:
 - Permission checks for command computers.
 - Checks for mouse/colour support in ServerComputer.
 - UI textures.
2023-12-20 14:17:38 +00:00
Jonathan Coates
488f66eead
Fix mouse_drag not firing for right/middle buttons
This is a bit of an odd combination of a few bugs:
 - When the terminal component is blurred, we fire a mouse_up event for
   the last-held button. However, we had an off-by-1 error here, so this
   only triggered for the right/middle buttons.

 - This was obsucuring the second bug, which is when we clicked within
   the terminal, this caused the terminal to be blurred (thus releasing
   the mouse) and then focused again.

   We fix this by only setting the focus if there's actually a change.

Fixes #1655
2023-12-10 12:01:34 +00:00
Jonathan Coates
eb3e8ba677
Fix deadlock when adding/removing observers
When adding/removing observers, we locked on the observer, then
acquired the global lock. When a metric is observed, then we acquire the
global lock and then the observer lock.

If these happen at the same time, we can easily end up with a deadlock.
We simply avoid holding the observer lock for the entire add/remove
process (instead only locking when actually needed).

Closes #1639
2023-12-01 12:33:03 +00:00
Jonathan Coates
2043939531
Add compostors to the list of usable blocks
Fixes #1638
2023-11-22 18:24:59 +00:00
Jonathan Coates
84a799d27a
Add abstract classes for our generic peripherals
This commit adds abstract classes to describe the interface for our
mod-loader-specific generic peripherals (inventories, fluid storage,
item storage).

This offers several advantages:
 - Javadoc to illuaminate conversion no longer needs the Forge project
   (just core and common).

 - Ensures we have a consistent interface between Forge and Fabric.

Note, this does /not/ implement fluid or energy storage for Fabric. We
probably could do fluid without issue, but not something worth doing
right now.
2023-11-22 18:20:15 +00:00
Jonathan Coates
76968f2f28
Track allocations while executing computers
This adds a new "java_allocation" metric, which tracks the number of
bytes allocated while executing the computer (as measured by Java). This
is not an 100% reliable number, but hopefully gives some insight into
what computers are doing.
2023-11-09 18:36:35 +00:00
Jonathan Coates
0c0556a5bc
Always use raw bytes in file handles
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.
2023-11-08 19:40:14 +00:00
Jonathan Coates
87345c6b2e
Add pasting support to the standalone emulator
- Move paste normalisation code to StringUtil, so it can be shared by
   emulators.
 - Add paste support to the emulator.
2023-11-08 19:40:14 +00:00
Jonathan Coates
18c9723308
Add a standalone CC:T UI
Does it count as an emulator when it's official? I hope not, as this'd
make it my fourth or fifth emulator at this point.

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Developing/debugging CraftOS is a massive pain to do inside Minecraft,
as any change to resources requires a compile+hot swap cycle (and
sometimes a `/reload` in-game). As such, it's often more convenient to
spin up an emulator, pointing it to load the ROM from CC:T's sources.

However, this isn't practical when also making changes to the Java
classes. In this case, we either need to go in-game, or build a custom
version of CCEmuX.

This commit offers an alternative option: we now have our own emulator,
which allows us to hot swap both Lua and Java to our heart's content.

Most of the code here is based on our monitor TBO renderer. We probably
could share some more of this, but there's not really a good place for
it - feels a bit weird just to chuck it in :core.

This is *not* a general-purpose emulator. It's limited in a lot of
ways (won't launch on Mac[^1], no support for multiple computers) - just
stick to what's there already.

[^1]: We require OpenGL 4.5 due to our use of DSA.
2023-10-28 17:58:11 +01:00
Jonathan Coates
6656da5877
Remove disable_lua51_features config option
In practice, we're never going to change this to true by default. The
old Tekkit Legends pack enabled this[^1], and that caused a lot of
problems, though admittedly back in 2016 so things might be better now.

If people do want this functionality, it should be fairly easy to
replicate with a datapack, adding a file to rom/autorun.

[^1]: See https://www.computercraft.info/forums2/index.php?/topic/27663-

      Hate that I remember this, why is this still in my brain?
2023-10-25 08:59:55 +01:00
Jonathan Coates
09e521727f
Make mount error messages a bit more consistent
- Move most error message constants to a new MountHelpers class.
 - Be a little more consistent in when we throw "No such file" vs "Not a
   file/directory" messages.
2023-10-22 13:13:07 +01:00
Jonathan Coates
cab66a2d6e
Replace Collections methods with {List,Map,Set}.of
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
2023-10-21 10:37:43 +01:00
Jonathan Coates
8eabd4f303
Fix signs being empty when placed
As of 1.20, sign messages are immutable - we need to do
text = text.setMesssage(...) instead. Also do a tiny bit of cleanup to
this function while we're here.

Probably not the best use of my lunch break :D:.

Fixes #1611.
2023-10-20 13:32:38 +01:00
Jonathan Coates
0ff58cdc3e
Unify the generic peirpheral system a litte
Allows registering arbitrary block lookup functions instead of a
platform-specific capability. This is roughly what Fabric did before,
but generalised to also take an invalidation callback.

This callback is a little nasty - it needs to be a NonNullableConsumer
on Forge, but that class isn't available on Fabric. For now, we make the
lookup function (and thus the generic peripheral provider) generic on
some <T extends Runnable> type, then specialise that on the Forge side.
Hopefully we can clean this up when NeoForge reworks capabilities.
2023-10-17 21:59:16 +01:00
Jonathan Coates
bd327e37eb
Fix common jar not actually being published
Or rather, being published to the wrong place. The java-convention
plugin sets the group, but that was applied after the publishing one - I
was hoping it'd read that property lazy, but clearly not!
2023-10-11 19:15:36 +01:00
Jonathan Coates
bdce9a8170
Replace several Guava classes with Java stdlib
Wow, some of this is /old/. All the Maps.newHashMap stuff dates back to
Java 6, so must originally be CCTweaks code?!

We're unlikely to drop our Guava dependency (we use too much other
stuff), but we should make the most of the stdlib where possible.
2023-10-11 09:59:55 +01:00
Weblate
6635edd35c Translations for French
Translations for German

Translations for German

Co-authored-by: Sammy <SammyKoch@pm.me>
Co-authored-by: SquidDev <git@squiddev.cc>
2023-10-09 22:33:03 +00:00
Jonathan Coates
93ad40efbb
Ensure the terminal exists when creating a monitor peripheral
Previously we had the invariant that if we had a server monitor, we also
had a terminal. When a monitor shrank into a place, we deleted the
monitor, and then recreated it when a peripheral was requested.

As of ab785a0906 this has changed
slightly, and we now just delete the terminal (keeping the ServerMonitor
around). However, we didn't adjust the peripheral code accordingly,
meaning we didn't recreate the /terminal/ when a peripheral was
requested.

The fix for this is very simple - most of the rest of this commit is
some additional code for ensuring monitor invariants hold, so we can
write tests with a little more confidence.

I'm not 100% sold on this approach. It's tricky having a double layer of
nullable state (ServerMonitor, and then the terminal). However, I think
this is reasonable - the ServerMonitor is a reference to the multiblock,
and the Terminal is part of the multiblock's state.

Even after all the refactors, monitor code is still nastier than I'd
like :/.

Fixes #1608
2023-10-09 22:09:01 +01:00
Jonathan Coates
905d4cb091
Fix crash when joining a dedicated server
We can't use FriendlyByte.readCollection to read to a
pre-allocated/array-backed NonNullList, as that doesn't implement
List.add. Instead, we just need to do a normal loop.

We add a couple of tests to round-trip our recipe specs. Unfortunately
we can't test the recipes themselves as our own registries aren't set
up, so this'll have to do for now.
2023-10-08 15:22:32 +01:00
Jonathan Coates
ab785a0906
Fix monitors being warped after a resize
Oh, this was a really nasty bug to reproduce. I'm not sure why - it's
very simple - I guess I've only just seen screenshots of it, and never
sat down to try myself. Reminder to actually report your bugs folks!

In this case:

 1. Place down three down three monitors and then a computer.
 2. Display something on the monitor (monitor left paint a) is my go-to.
 3. Break the middle monitor.

We'd expect the left most monitor to be cleared, however it actually
preserves the monitor contents, resizing (and skewing it) to fit on its
new size!

This is because we clear the server monitor, but never sync that over to
the client, so the client monitor retains the old contents. To fix that,
instead of nulling out the server monitor, we null out the underlying
Terminal. This causes the change to be synced, fixing the bug.
2023-10-03 18:20:44 +01:00
Spongecade
747a5a53b4
Update Minecraft wiki links to new domain (#1601) 2023-10-03 15:55:20 +00:00