-
-
Notifications
You must be signed in to change notification settings - Fork 0
Explanation Scope and non goals
What Falco deliberately does not do, per module, stated plainly. Each of these is a reason to keep using another loader, another engine or another tool — and knowing them up front prevents a class of bug report.
Falco is four independent modules with no dependency between them in either direction. None of them
is a world-management layer: falco-anvil handles the region/ directory of a single dimension and
nothing else, falco-light computes light and stores nothing, falco-instance cleans up after
itself and claims no speed advantage, and falco-migration translates chunks forward and refuses
everything else.
Stated plainly, because each of these is a reason to keep using another loader or another tool.
-
No
entities/orpoi/region handling. Only theregion/directory is read and written. Entity and point-of-interest region files of a vanilla world are ignored and are neither migrated nor kept in sync. The MinestomAnvilLoaderdoes not handle them either. -
No DataFixer and no DataVersion migration.
MinecraftServer.DATA_VERSIONis written into every saved chunk (FalcoAnvilLoader.dataVersion, written byFalcoAnvilLoader#snapshot), and the storedDataVersionof a chunk being read is now inspected — but only far enough to reject one below the floor described in The version floor. A chunk that clears the floor is read with the current schema regardless of how old itsDataVersionactually is; nothing here runs a DataFixer or reinterprets older data under its own schema. Convert worlds with the vanilla client or another tool first. -
No LZ4 (compression type 4) and no custom compression (type 127).
ChunkCompression.fromIdaccepts gzip (1), zlib (2) and uncompressed (3), with the external bit0x80masked off; every other id raisesThe compression scheme <id> is not supported. Only gzip (1), zlib (2) and none (3) can be read(ChunkCompression#fromId). Unsupported compression therefore fails with an explicit error instead of being misread as another scheme. Saving always uses zlib (FalcoAnvilLoader#saveChunk). -
No corruption recovery and no header rebuilding. A header shorter than 8192 bytes, or a
location table with overlapping sector ranges, fails the open
(
RegionFile#readHeader,SectorAllocator#reserve). There is no scan-and-repair mode, no orphaned-sector reclamation and no defragmentation; freed sectors are reused but the file is never shrunk (SectorAllocator#free). -
No
level.dathandling.loadInstanceandsaveInstanceare not overridden, so world metadata (seed, spawn, game rules, world age) is neither read nor written.LastUpdateis written as a constant0(FalcoAnvilLoader#snapshot). -
No preservation of unknown chunk-level tags.
snapshotwrites a fixed key set (FalcoAnvilLoader#snapshot), soHeightmaps,structures,block_ticks,fluid_ticksand everything else present in a vanilla chunk are lost when that chunk is saved. Heightmaps are not restored on load either. Use this loader for worlds the server owns, not as an editor for vanilla worlds you intend to open in the client again. -
No configurable compression level.
compressionLevelis assignedChunkCompression.DEFAULT_LEVELin the constructor and is never read from anywhere else (FalcoAnvilLoader). There is no constructor parameter and no setter for it, so a server that wants level 6 on disk cannot ask this loader for it. The level is a deliberate default and currently a fixed one. -
No loading of partially generated chunks. A chunk whose
Statusis notminecraft:fullis reported as absent —loadChunkreturnsnullafter a throttled warning (FalcoAnvilLoader#isFullyGenerated).InstanceContainerthen generates a replacement (instance/InstanceContainer.java:336-343), which the next save writes over the partial chunk. This is the one place where the loader accepts the overwrite it otherwise exists to prevent, and it does so on purpose: a partially generated chunk has no meaningful contents to preserve, and Minestom'sAnvilLoaderskips it as well (instance/anvil/AnvilLoader.java:133-142). A world mid-generation is still not something to point either loader at. -
No light computation. Stored
SkyLightandBlockLightarrays are read into the section and written back out unchanged (FalcoAnvilLoader.DecodedSection#applyTo,FalcoAnvilLoader#encodeSection). A chunk stored without light stays dark until something else lights it; the How the light engine works is that something, and it is a separate module with no dependency in either direction. -
No durability guarantee per save, and no crash consistency. A save writes the payload and the
8-byte header entry through the channel and returns; nothing is forced to the device.
RegionFile.flushischannel.force(false)and it is called at exactly two places — when a region file is evicted or unloaded, and when the loader is closed (RegionFile#flush,FalcoAnvilLoader#closeQuietly,FalcoAnvilLoader#close). A power loss between asaveChunkand that point loses the save, and because the payload and the header entry are two writes with no barrier between them, it can also leave an entry pointing at sectors whose bytes never reached the device. The region lock orders writers against each other; it orders nothing against the page cache. There is no journal, no write-ahead log and no torn-write detection on read beyond the length and sector-range checks. This is not a regression against the built-in loader — Minestom opens its file"rw"and itsRegionFile.close()closes without syncing (instance/anvil/RegionFile.java) — but neither loader is safe against an unclean shutdown, and a server that needs that needs a snapshot on the storage layer. -
No cross-loader coordination. Two
FalcoAnvilLoaderinstances over the same region directory share no sector allocator, no region cache and no lock. The per-entry version counters and the region lock coordinate threads inside one loader, not processes and not two loaders. One loader per dimension directory, and nothing else writing that directory while the server runs.
-
Sky light is never compared with Minestom's. Every comparison on this page runs against
BlockLight.buildInternalQueueandLightCompute.compute, which are the built-in block light path.LightEngineEquivalenceTestcompares block light only, andLightEngineComparisonBenchmark.falco/.minestommeasure block light only. Falco's sky light is checked against Falco's own full recalculation — that is whatSkyLightUpdateTestandSkyLightTestdo — and against nothing else. No claim of byte identity with the built-in engine is made for sky light on this page, and none may be quoted from it. -
No
Lightimplementation. The engine deliberately does not implementnet.minestom.server.instance.light.Light. It writes its result throughsetinstead, which avoids depending on the internal calculation methods of that interface. -
The exchange covers one ring of chunks.
calculateWithNeighbourssettles the chunk and the eight positions around it, and an area settles its own chunks against a ring. That is enough for the chunks being written, but the ring itself is not settled against its own neighbours further out. -
The ring of an area holds no diagonals. It is built from the face neighbours of every chunk of
the area, so a source in a chunk that touches the area only at a corner is missing from the result.
calculateWithNeighboursreads its diagonals and does not have this gap, which is why it was not deprecated in favour of the area. - An area rebuilds every opacity table on every pass, for each of its chunks and each of its ring chunks, whether or not anything in them moved. Now that the light itself is kept between passes, this is the largest remaining cost of a pass.
- An area split at the cap leaves a seam for one tick. Each part reads the other as its ring, so the following pass settles it.
-
Section.clone()discards foreign light. Should an adapter be built later, note thatSection.clone()callsLight.sky()/Light.block()outright, so any custom implementation is silently replaced on copy.LightingChunk.copy()would have to be overridden. -
LightPropagatorandChunkLightPropagatorare thread confined. Both keep their level buffer and queue in instance fields and clear them per run (LightPropagator), so one instance per worker thread and never a shared one.ChunkLightServiceis the exception and is safe to share, because it builds a propagator inside every call. Nothing enforces the distinction at compile time; getting it wrong produces wrong light rather than an exception, which is the failure mode this engine has already been bitten by once. -
One
ChunkLightSchedulerserves one instance. A second instance handed to the same scheduler is refused with anIllegalStateException, because the dirty set is keyed by chunk coordinates alone and every instance of a server ticks with the same timestamp (ChunkLightScheduler). A server with many worlds needs one scheduler per world, and the memory bound is therefore per world and not global. -
The kept light is capped and the cap is not adaptive.
ChunkLightArea.DEFAULT_MAX_CACHED_CHUNKSis 128 andChunkLightScheduler.DEFAULT_MAX_AREA_SIZEis 16, both plain constants with no measurement behind either value. Dropping an entry costs a full propagation, which is correct but not free; no benchmark in this repository sweeps either constant. -
Nothing here reports what a real server pays. Every measurement on this page is a per-section or
per-chunk average time from a JMH harness, and
Mode.AverageTimereports a mean. A server's felt lighting cost is a tail — the tick that took too long — and no percentile is measured anywhere in the suite. The comparison rows are valid against their own counterparts and against nothing else.
-
No
SharedInstanceonto aFalcoInstance. Structural, see above. -
Foreign chunk types are refused. A
ChunkSupplierproducing anything but aFalcoChunkis rejected with a message naming the cause, because such a chunk would be accepted everywhere except the unload path and would then report itself as loaded forever. -
FalcoChunk#tickwalks its tickable map without a lock. A concurrentsetBlockthat rehashes the map while the tick thread walks it can yield garbage or spin. Inherited rather than introduced —DynamicChunkhas the identical race — and Minestom'sChunk#tickcontract says the method "doesn't necessary have to be thread-safe". Open, and listed as such in Project Status. -
No throughput measurement exists. Benchmark classes for the instance and the chunk are in
falco-benchmarks, but none has been run as a baseline, so there is no timing to quote.
- No downgrade. The engine lifts a chunk forward. Going back is a projection with a substitution policy the upgrade path has no use for, and it is not built.
-
Nothing below Minecraft 1.13.
DataVersionunder1519predates the flattening and holds numeric block ids these types do not speak. Such a chunk is declined, not attempted. - No entities, and no Bedrock. The engine covers blocks, biomes and block entities. Bedrock is a different storage format entirely and is out of scope; see Project Status.
- No command line tool yet. The engine and the loader option exist; a standalone converter does not.
- It cannot invent a rule it does not have. Migration only fixes renames someone has written down. An unknown block with no rule still becomes air, and still gets exactly one log line.
Related: Explanation Choosing between Falco and the built-in loader · Explanation Why falco-instance exists · Explanation What a measurement here means · Project Status for what is open rather than out of scope
Every published table lives on Reference Measured results, which owns them; a correction is made
there and nowhere else. What the ± after a JMH mean covers is defined once, in
Explanation What a measurement here means.
Wiki home · Repository · README and quick start · API documentation · Issues · Licence: AGPL-3.0
Getting started
How-to guides
- How-to Add Falco to your build
- How-to Load an Anvil world
- How-to Compute light for a loaded world
- How-to Keep chunk light up to date automatically
- How-to Use FalcoInstance instead of InstanceContainer
- How-to Migrate a world from an older version
four more
Reference
six more
Background
- Explanation Choosing between Falco and the built-in loader
- Explanation Scope and non-goals
- Explanation When light computation actually runs
- Explanation What a measurement here means
nine more
- Explanation Choosing between FalcoInstance and InstanceContainer
- Explanation How the Anvil loader is built
- Explanation How the light engine works
- Explanation How the concurrency design works
- Explanation How world migration works
- Explanation The chunk version guard
- Explanation Why a second Anvil loader
- Explanation Why a custom light engine
- Explanation Why falco-instance exists
- Explanation Comparing the light engine with Minestoms
- Explanation What the benchmarks establish
Project record
Working on Falco