Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/performance-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ body:
- type: markdown
attributes:
value: |
**Read [What a measurement here means](https://github.com/OneLiteFeatherNET/Falco/blob/main/docs/explanation/what-a-measurement-here-means.md)
**Read [What a measurement here means](https://docs.onelitefeather.net/falco/explanation/what-a-measurement-here-means)
before filing this.** Every number this project publishes comes from a JMH benchmark in
`falco-benchmarks`, and the conditions are published next to it. A number without its
methodology cannot be compared with one that has it, so this form asks for the methodology
Expand Down Expand Up @@ -126,7 +126,7 @@ body:
attributes:
label: "Before submitting"
options:
- label: "I read [What a measurement here means](https://github.com/OneLiteFeatherNET/Falco/blob/main/docs/explanation/what-a-measurement-here-means.md)."
- label: "I read [What a measurement here means](https://docs.onelitefeather.net/falco/explanation/what-a-measurement-here-means)."
required: true
- label: "My run used the configured forks and iterations, not a single smoke run."
required: true
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ What is shared, and how is it guarded?
_Delete this section if the change makes no claim about performance._

Numbers without their methodology are not accepted; see
[Benchmarking](../docs/explanation/what-the-benchmarks-establish.md).
[Benchmarking](https://docs.onelitefeather.net/falco/explanation/what-the-benchmarks-establish).

- [ ] The figures below come from JMH with the configured forks and iterations, not a smoke run
- [ ] I have quoted the error margins alongside the means, and named the machine, the JVM and the
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Falco is an Anvil chunk loader, a light engine and an `Instance` implementation
all three modules are experimental: every public type carries `@ApiStatus.Experimental`, and
signatures and behaviour may still change in a minor release. This file is the entry point and
nothing more — what the build is made of, which conventions it enforces and what a push to `main`
publishes is [Contributing](docs/contributing/README.md) in the
publishes is [Contributing](https://docs.onelitefeather.net/falco/contributing) in the
documentation.

## Building
Expand All @@ -22,7 +22,7 @@ fails the build, so a green `./gradlew build` is also the documentation check.
the internal `mycelium-bom` through `https://repo.onelitefeather.dev/onelitefeather`, which answers
`401` to an anonymous request, so `./gradlew build` fails without them. This affects only work on
Falco itself. The names the credentials are read under are in
[Installation](docs/how-to/build-falco-from-source.md).
[Installation](https://docs.onelitefeather.net/falco/how-to/build-falco-from-source).

**Consuming Falco needs no credentials from this release onwards, and did need them before it.** The
jars were always served without authentication, but a POM is resolved as well as a jar, and up to
Expand All @@ -36,7 +36,7 @@ and the public `releases` endpoint declared, the 1.8.5 import resolves and bring

Running a single test class, building the benchmark jar and starting the two demo servers are
further commands, listed under
[Working on this](docs/contributing/README.md#working-on-this).
[Working on this](https://docs.onelitefeather.net/falco/contributing#working-on-this).

## Commits

Expand All @@ -62,7 +62,7 @@ Opening one fills `.github/pull_request_template.md`, whose checklist is the sho
what review looks for: the pull-request title is the Conventional Commit, a test was written first
and watched fail, the concurrency section is filled in or deleted, the measurements section is
filled in or deleted. What each of those four asks for, and why, is
[Opening a pull request](docs/contributing/README.md#opening-a-pull-request).
[Opening a pull request](https://docs.onelitefeather.net/falco/contributing#opening-a-pull-request).
`.github/workflows/build-pr.yml` then runs the same build on the branch that `./gradlew build` runs
locally.

Expand Down
470 changes: 56 additions & 414 deletions README.md

Large diffs are not rendered by default.

102 changes: 51 additions & 51 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,70 +9,70 @@
## How-to guides

* [How-to guides](how-to/README.md)
* [Add Falco to your build](how-to/add-falco-to-your-build.md)
* [Load an Anvil world](how-to/load-an-anvil-world.md)
* [Compute light for a loaded world](how-to/compute-light-for-a-loaded-world.md)
* [Keep chunk light up to date automatically](how-to/keep-chunk-light-up-to-date-automatically.md)
* [Use FalcoInstance instead of InstanceContainer](how-to/use-falcoinstance-instead-of-instancecontainer.md)
* [Migrate a world from an older version](how-to/migrate-a-world-from-an-older-version.md)
* [Replace the version and unknown-entry policies](how-to/replace-the-version-and-unknown-entry-policies.md)
* [Build Falco from source](how-to/build-falco-from-source.md)
* [Run the JMH benchmark suite](how-to/run-the-jmh-benchmark-suite.md)
* [Reproduce a published measurement](how-to/reproduce-a-published-measurement.md)
* [Add Falco to your build](how-to/add-falco-to-your-build.md)
* [Load an Anvil world](how-to/load-an-anvil-world.md)
* [Compute light for a loaded world](how-to/compute-light-for-a-loaded-world.md)
* [Keep chunk light up to date automatically](how-to/keep-chunk-light-up-to-date-automatically.md)
* [Use FalcoInstance instead of InstanceContainer](how-to/use-falcoinstance-instead-of-instancecontainer.md)
* [Migrate a world from an older version](how-to/migrate-a-world-from-an-older-version.md)
* [Replace the version and unknown-entry policies](how-to/replace-the-version-and-unknown-entry-policies.md)
* [Build Falco from source](how-to/build-falco-from-source.md)
* [Run the JMH benchmark suite](how-to/run-the-jmh-benchmark-suite.md)
* [Reproduce a published measurement](how-to/reproduce-a-published-measurement.md)

## Reference

* [Reference](reference/README.md)
* [Modules and coordinates](reference/modules-and-coordinates.md)
* [Supported versions](reference/supported-versions.md)
* [Measured results](reference/measured-results.md)
* [API surface and stability](reference/api-surface-and-stability.md)
* [Replaceable policies](reference/replaceable-policies.md)
* [Migration modes](reference/migration-modes.md)
* [Exceptions and faults](reference/exceptions-and-faults.md)
* [Logging](reference/logging.md)
* [Benchmark catalogue](reference/benchmark-catalogue.md)
* [Modules and coordinates](reference/modules-and-coordinates.md)
* [Supported versions](reference/supported-versions.md)
* [Measured results](reference/measured-results.md)
* [API surface and stability](reference/api-surface-and-stability.md)
* [Replaceable policies](reference/replaceable-policies.md)
* [Migration modes](reference/migration-modes.md)
* [Exceptions and faults](reference/exceptions-and-faults.md)
* [Logging](reference/logging.md)
* [Benchmark catalogue](reference/benchmark-catalogue.md)

## Explanation

* [Explanation](explanation/README.md)
* [Choosing between Falco and the built-in loader](explanation/choosing-between-falco-and-the-built-in-loader.md)
* [Scope and non-goals](explanation/scope-and-non-goals.md)
* [When light computation actually runs](explanation/when-light-computation-actually-runs.md)
* [Measurement](explanation/what-a-measurement-here-means.md)
* [Choosing between FalcoInstance and InstanceContainer](explanation/choosing-between-falcoinstance-and-instancecontainer.md)
* [How the Anvil loader is built](explanation/how-the-anvil-loader-is-built.md)
* [How the light engine works](explanation/how-the-light-engine-works.md)
* [Why the concurrency design is what it is](explanation/how-the-concurrency-design-works.md)
* [How world migration works](explanation/how-world-migration-works.md)
* [The chunk version guard](explanation/the-chunk-version-guard.md)
* [Why Falco has its own Anvil chunk loader](explanation/why-a-second-anvil-loader.md)
* [Why Falco has its own light engine](explanation/why-a-custom-light-engine.md)
* [Why falco-instance exists](explanation/why-falco-instance-exists.md)
* [Comparing the light engine with Minestom's](explanation/comparing-the-light-engine-with-minestoms.md)
* [What the benchmarks establish](explanation/what-the-benchmarks-establish.md)
* [Choosing between Falco and the built-in loader](explanation/choosing-between-falco-and-the-built-in-loader.md)
* [Scope and non-goals](explanation/scope-and-non-goals.md)
* [When light computation actually runs](explanation/when-light-computation-actually-runs.md)
* [Measurement](explanation/what-a-measurement-here-means.md)
* [Choosing between FalcoInstance and InstanceContainer](explanation/choosing-between-falcoinstance-and-instancecontainer.md)
* [How the Anvil loader is built](explanation/how-the-anvil-loader-is-built.md)
* [How the light engine works](explanation/how-the-light-engine-works.md)
* [Why the concurrency design is what it is](explanation/how-the-concurrency-design-works.md)
* [How world migration works](explanation/how-world-migration-works.md)
* [The chunk version guard](explanation/the-chunk-version-guard.md)
* [Why Falco has its own Anvil chunk loader](explanation/why-a-second-anvil-loader.md)
* [Why Falco has its own light engine](explanation/why-a-custom-light-engine.md)
* [Why falco-instance exists](explanation/why-falco-instance-exists.md)
* [Comparing the light engine with Minestom's](explanation/comparing-the-light-engine-with-minestoms.md)
* [What the benchmarks establish](explanation/what-the-benchmarks-establish.md)

## Project record

* [Project record](project-record/README.md)
* [Status — Anvil chunk loader and light engine](project-record/project-status.md)
* [Research notes](project-record/research/README.md)
* [Research: could `falco-instance` be faster, and how would anyone know?](project-record/research/instance-performance-research.md)
* [Research: exception hierarchy for the Anvil package](project-record/research/exception-hierarchy.md)
* [Research: a fluent construction API for the three modules](project-record/research/fluent-api.md)
* [Research: a multithreaded `InstanceContainer` replacement](project-record/research/instance-container.md)
* [Research: a faster, lower-memory light engine](project-record/research/light-engine.md)
* [Research: shared instances and batch integration for a foreign instance](project-record/research/shared-instances-and-batches.md)
* [Status — Anvil chunk loader and light engine](project-record/project-status.md)
* [Research notes](project-record/research/README.md)
* [Research: could `falco-instance` be faster, and how would anyone know?](project-record/research/instance-performance-research.md)
* [Research: exception hierarchy for the Anvil package](project-record/research/exception-hierarchy.md)
* [Research: a fluent construction API for the three modules](project-record/research/fluent-api.md)
* [Research: a multithreaded `InstanceContainer` replacement](project-record/research/instance-container.md)
* [Research: a faster, lower-memory light engine](project-record/research/light-engine.md)
* [Research: shared instances and batch integration for a foreign instance](project-record/research/shared-instances-and-batches.md)

## Contributing

* [Contributing](contributing/README.md)
* [Build setup](contributing/build-setup.md)
* [Dependency management](contributing/dependency-management.md)
* [Versioning and releases](contributing/versioning-and-releases.md)
* [Publishing](contributing/publishing.md)
* [Testing and Javadoc](contributing/testing-and-javadoc.md)
* [Benchmarks and demo](contributing/benchmarks-and-demo.md)
* [Architecture rules](contributing/architecture-rules.md)
* [Documentation conventions](contributing/documentation-conventions.md)
* [Benchmark baselines](benchmarks/README.md)
* [Build setup](contributing/build-setup.md)
* [Dependency management](contributing/dependency-management.md)
* [Versioning and releases](contributing/versioning-and-releases.md)
* [Publishing](contributing/publishing.md)
* [Testing and Javadoc](contributing/testing-and-javadoc.md)
* [Benchmarks and demo](contributing/benchmarks-and-demo.md)
* [Architecture rules](contributing/architecture-rules.md)
* [Documentation conventions](contributing/documentation-conventions.md)
* [Benchmark baselines](benchmarks/README.md)
3 changes: 3 additions & 0 deletions docs/explanation/why-falco-instance-exists.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ call legal.
the setter `public` itself and holds its blocks in a `BlockStorage` of its own, so nothing about the
storage is inherited any more. The barrier is unchanged — it is answered one class lower down.

Before `1.0.0` this was a real choice for a server. The lighting chunk and the Falco chunk both extended
`DynamicChunk`, a class has one superclass, and a server had to pick one of the two.

The sentence that used to stand here said that `FalcoChunk` adds nothing beyond re-exposing those
members, and that Minestom's block storage was never the part that needed replacing. Both were true
of the first version and neither is true now. What changed the answer was a measurement rather than
Expand Down
49 changes: 49 additions & 0 deletions docs/how-to/keep-chunk-light-up-to-date-automatically.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,55 @@ that already has a pool can hand that one over instead.
source is retracted before the light is spread again. How this works, and why the update is identical to a
full recalculation, is explained in [Explanation How the light engine works](../explanation/how-the-light-engine-works.md#incremental-updates-and-what-they-keep).

## Tune the scheduler

The builder carries the settings that matter under load. Every one has a default; the values below
are the ones you would most often set:

```java
ChunkLightScheduler scheduler = ChunkLightScheduler.builder(lighting)
.executor(ChunkLightScheduler.defaultExecutor())
.maxAreaSize(4) // chunks per side of one lighting area
.maxCachedChunks(256) // opacity tables kept between passes
.skyLight(ChunkLightScheduler.SkyLight.FROM_DIMENSION)
.onFailure(throwable -> log.error("lighting failed", throwable))
.build();
```

`maxAreaSize` decides where one lighting area ends: a larger value means fewer seams between areas, and
a smaller one means less work per pass. `SkyLight.FROM_DIMENSION` computes sky light only when the
dimension has skylight. `onFailure` receives what a failed pass threw, so a lighting failure is logged
instead of disappearing.

## Tell the scheduler about changes Falco did not make

When something outside Falco changes the world, say so, and the chunk is relit on the next tick:

```java
scheduler.markChanged(instance, 0, 0); // this chunk needs relighting
scheduler.markChanged(instance, 0, 0, 8, 40, 8); // this position did
scheduler.markDirty(instance, 0, 0); // relight without an incremental path
```

## Use it on an InstanceContainer

On a `FalcoInstance` the supplier is all you need. On an `InstanceContainer`, build the chunks yourself,
hang a `ChunkLightListener` on each one, and tick the scheduler from a repeating task:

```java
container.setChunkSupplier((instance, x, z) -> {
FalcoChunk chunk = new FalcoChunk(instance, x, z);
chunk.addLifecycleListener(new ChunkLightListener(scheduler));
return chunk;
});
MinecraftServer.getSchedulerManager().buildTask(() -> scheduler.onTick(container, System.currentTimeMillis()))
.repeat(TaskSchedule.tick(1))
.schedule();
```

Use `FalcoInstance` unless you need the container's own write path; see
[Use FalcoInstance instead of InstanceContainer](use-falcoinstance-instead-of-instancecontainer.md).

## Check it worked

Place a block, let one tick pass, and the surrounding light is correct without any call of your own.
Expand Down
35 changes: 35 additions & 0 deletions docs/how-to/load-an-anvil-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,41 @@ The three-argument constructor sets how many region files stay open. The default
FalcoAnvilLoader loader = new FalcoAnvilLoader(worldRoot, dimension, 256);
```

## Tune the builder

The two-argument constructor is enough for most servers. The builder is there when the defaults do not
fit:

```java
FalcoAnvilLoader loader = FalcoAnvilLoader.builder()
.openRegionLimit(64) // region files kept open at once
.compressionLevel(2) // 1..9, the trade between write time and file size
.saveParallelism(4) // threads a saveChunks call may use
.dataVersion(4189) // what a written chunk claims to be
.diagnostics(new AnvilDiagnostics())
.exceptionHandler(throwable -> log.warn("chunk load failed", throwable))
.build(Path.of("worlds", "lobby"), DimensionType.OVERWORLD.key());
```

`diagnostics()` matters on a live server. A world written by another version, or by a mod, can contain
blocks and biomes this loader does not know. The loader substitutes them and counts the substitutions
rather than failing:

```java
AnvilDiagnostics diagnostics = loader.diagnostics();
diagnostics.reportUnknownBlock("mod:strange_block"); // true the first time, false after
```

## See what the loader resolved

Three accessors tell you what the loader is doing right now:

- `regionDirectory()` gives the directory it resolved.
- `legacyLayout()` is true when it fell back to the pre-26.1 layout.
- `openRegionCount()` is how many region files are open at this moment.

`close()` flushes every open region file. `ownsLoader(true)` on a `FalcoInstance` calls it for you.

## Check it worked

The loader writes an opening log line naming the resolved region directory and the policies it
Expand Down
11 changes: 11 additions & 0 deletions docs/how-to/use-falcoinstance-instead-of-instancecontainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,17 @@ of the four Minestom sites that branch on `instanceof InstanceContainer` — see
The view keeps its own generator, chunk supplier and auto-load setting. The order of the calls and the
limits of sharing are explained in [Explanation Why falco-instance exists](../explanation/why-falco-instance-exists.md#shared-instances-refused-by-the-compiler-which-is-the-good-outcome).

## Set a generator

Generation uses the usual Minestom API:

```java
instance.setGenerator(unit -> unit.modifier().fillHeight(0, 40, Block.STONE));
```

The generator is handed copies of the section palettes, and they are moved over only when it returns.
A generator that fails halfway therefore leaves the chunk as it was, instead of half built and published.

## Read a chunk without materialising its sections

8. Read through `chunk.storage()` and not through `getSections()`, `getSection(int)` or `sections()`. The
Expand Down
7 changes: 7 additions & 0 deletions docs/reference/measured-results.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ defects found and fixed, and what is still open — is [Project Status](../proje
> the `±` covers is defined once in [What a measurement here means](../explanation/what-a-measurement-here-means.md); how to re-run
> any of it is in [What the benchmarks establish](../explanation/what-the-benchmarks-establish.md).
>
> **The Minestom version is part of every timing, and it is not recorded for any of them.** The rows
> that quote a factor compare Falco with the loader and engine Minestom ships, so the Minestom under test
> is part of the measurement. The run commit is not recorded either, so it cannot be recovered later.
> Every figure on this page was taken before this repository moved to Minestom `2026.08.28-26.2`, and none
> has been re-run against it. Read the comparison rows as unverified on 26.2 until a baseline is taken
> there. The object counts in [Counted](#counted) are not affected, because they count objects on a heap.
>
> [Counted](#counted) is the exception and is separated for it: object and byte counts are
> deterministic, carry no `±`, and are unaffected by what else the machine was doing. They are the
> only figures on this page that survive being taken on a busy machine.
Expand Down
Loading
Loading