Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
5 changes: 5 additions & 0 deletions .claude/spear-state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"version": 1,
"phase": "idle",
"lastUpdated": "2026-06-29T22:19:07Z"
}
53 changes: 25 additions & 28 deletions build-data/leaf.at
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
# This file is auto generated, any changes may be overridden!
# See CONTRIBUTING.md on how to add access transformers.
protected io.papermc.paper.configuration.Configurations reloader(Ljava/lang/Class;Ljava/lang/Object;)Lorg/spongepowered/configurate/util/CheckedFunction;
protected io.papermc.paper.configuration.PaperConfigurations createDirectoriesSymlinkAware(Ljava/nio/file/Path;)V
protected io.papermc.paper.configuration.PaperConfigurations defaultOptions(Lorg/spongepowered/configurate/ConfigurationOptions;)Lorg/spongepowered/configurate/ConfigurationOptions;
protected net.minecraft.world.entity.Entity collide(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3;
protected net.minecraft.world.entity.Entity dimensions
protected net.minecraft.world.entity.projectile.Projectile checkLeftOwner()Z
protected net.minecraft.world.level.ServerExplosion blockCache
protected net.minecraft.world.level.ServerExplosion calculateExplodedPositions()Ljava/util/List;
protected net.minecraft.world.level.ServerExplosion directMappedBlockCache
protected net.minecraft.world.level.ServerExplosion getBlockDensity(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)F
protected net.minecraft.world.level.ServerExplosion getOrCacheExplosionBlock(IIIJZ)Lca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache;
protected net.minecraft.world.level.ServerExplosion interactWithBlocks(Ljava/util/List;)V
protected net.minecraft.world.level.ServerExplosion interactsWithBlocks()Z
protected net.minecraft.world.level.ServerExplosion source
protected-f net.minecraft.world.entity.Entity eyeHeight
protected-f net.minecraft.world.level.ServerExplosion center
protected-f net.minecraft.world.level.block.state.BlockBehaviour$BlockStateBase$Cache largeCollisionShape
public net.minecraft.core.Direction VALUES
public net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket chunkData
public net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket <init>(ILjava/util/List;)V
public net.minecraft.server.level.ServerChunkCache fullChunks
public net.minecraft.server.level.ServerLevel entityTickList
public net.minecraft.server.network.ServerLoginPacketListenerImpl LOGGER
public net.minecraft.server.players.PlayerList SEND_PLAYER_INFO_INTERVAL
public net.minecraft.util.Mth SIN
public net.minecraft.util.Util LOGGER
public net.minecraft.world.entity.Entity addMovementThisTick(Lnet/minecraft/world/entity/Entity$Movement;)V
public net.minecraft.world.entity.Entity blockPosition
public net.minecraft.world.entity.Entity markHurt()V
public net.minecraft.world.entity.Entity position
public net.minecraft.world.entity.Entity stuckSpeedMultiplier
public net.minecraft.world.entity.Entity updateInWaterStateAndDoWaterCurrentPushing()V
public net.minecraft.world.entity.Entity$Movement
public net.minecraft.world.entity.LivingEntity canGlide()Z
public net.minecraft.world.entity.LivingEntity noActionTime
public net.minecraft.world.entity.ai.attributes.AttributeMap attributes
Expand All @@ -22,7 +42,10 @@ public net.minecraft.world.entity.decoration.ArmorStand noTickEquipmentDirty
public net.minecraft.world.entity.monster.Shulker MAX_SCALE
public net.minecraft.world.entity.player.Player canGlide()Z
public net.minecraft.world.item.CrossbowItem getShotPitch(Lnet/minecraft/util/RandomSource;I)F
public net.minecraft.world.level.Level neighborUpdater
public net.minecraft.world.level.block.PoweredRailBlock findPoweredRailSignal(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;ZI)Z
public net.minecraft.world.level.block.RedStoneWireBlock shouldConnectTo(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;)Z
public net.minecraft.world.level.block.RedStoneWireBlock turbo
public net.minecraft.world.level.block.ShulkerBoxBlock canOpen(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/ShulkerBoxBlockEntity;)Z
public net.minecraft.world.level.block.entity.FuelValues values
public net.minecraft.world.level.chunk.PaletteResize
Expand All @@ -31,38 +54,12 @@ public net.minecraft.world.level.chunk.storage.RegionFile isOversized(II)Z
public net.minecraft.world.level.chunk.storage.RegionFile recalculateHeader()Z
public net.minecraft.world.level.chunk.storage.RegionFile setOversized(IIZ)V
public net.minecraft.world.level.chunk.storage.RegionFile write(Lnet/minecraft/world/level/ChunkPos;Ljava/nio/ByteBuffer;)V
public net.minecraft.world.level.entity.EntityTickList entities
public net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece LOGGER
public net.minecraft.world.level.material.FlowingFluid canHoldAnyFluid(Lnet/minecraft/world/level/block/state/BlockState;)Z
public net.minecraft.world.level.material.FlowingFluid getLegacyLevel(Lnet/minecraft/world/level/material/FluidState;)I
public net.minecraft.world.level.pathfinder.SwimNodeEvaluator allowBreaching
public net.minecraft.world.level.portal.PortalShape FRAME
public-f net.minecraft.server.level.ServerPlayer gameMode
public-f net.minecraft.world.entity.EntityType dimensions
public-f net.minecraft.world.level.block.state.BlockBehaviour explosionResistance

# Sakura - access transformers
protected io.papermc.paper.configuration.Configurations reloader(Ljava/lang/Class;Ljava/lang/Object;)Lorg/spongepowered/configurate/util/CheckedFunction;
protected io.papermc.paper.configuration.PaperConfigurations createDirectoriesSymlinkAware(Ljava/nio/file/Path;)V
protected io.papermc.paper.configuration.PaperConfigurations defaultOptions(Lorg/spongepowered/configurate/ConfigurationOptions;)Lorg/spongepowered/configurate/ConfigurationOptions;
protected net.minecraft.world.entity.Entity collide(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3;
protected net.minecraft.world.entity.Entity updateInWaterStateAndDoWaterCurrentPushing()V
protected net.minecraft.world.entity.projectile.Projectile checkLeftOwner()Z
protected net.minecraft.world.level.ServerExplosion blockCache
protected net.minecraft.world.level.ServerExplosion calculateExplodedPositions()Ljava/util/List;
protected net.minecraft.world.level.ServerExplosion directMappedBlockCache
protected net.minecraft.world.level.ServerExplosion getBlockDensity(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)F
protected net.minecraft.world.level.ServerExplosion getOrCacheExplosionBlock(IIIJZ)Lca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache;
protected net.minecraft.world.level.ServerExplosion interactWithBlocks(Ljava/util/List;)V
protected net.minecraft.world.level.ServerExplosion interactsWithBlocks()Z
protected net.minecraft.world.level.ServerExplosion source
protected-f net.minecraft.world.entity.Entity eyeHeight
protected-f net.minecraft.world.level.ServerExplosion center
public net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket chunkData
public net.minecraft.server.level.ServerLevel entityTickList
public net.minecraft.world.entity.Entity addMovementThisTick(Lnet/minecraft/world/entity/Entity$Movement;)V
public net.minecraft.world.entity.Entity stuckSpeedMultiplier
public net.minecraft.world.entity.Entity$Movement
public net.minecraft.world.level.Level neighborUpdater
public net.minecraft.world.level.block.RedStoneWireBlock shouldConnectTo(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;)Z
public net.minecraft.world.level.block.RedStoneWireBlock turbo
public net.minecraft.world.level.entity.EntityTickList entities
public net.minecraft.world.level.material.FlowingFluid getLegacyLevel(Lnet/minecraft/world/level/material/FluidState;)I
65 changes: 65 additions & 0 deletions docs/implementation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Implementation Plan

## Layer Dependency Rules

Fuji is a Minecraft server fork — not a layered application. The following conventions apply:

- **Fork Java sources** (`leaf-server/src/main/java/`) may reference any Minecraft server class
- **MC patches** (`minecraft-patches/features/`) inject code directly into Minecraft server classes
- **External dependencies** (EntityCulling, OcclusionCulling) live as fork Java sources

No traditional domain/application/infrastructure layers exist. The "domain" IS the Minecraft server.

## Forbidden Domain Annotations

```yaml
forbidden: []
```

No annotation restrictions apply to this project (it is not a layered Spring/DI application).

## Codebase Structure

```
leaf-server/
src/main/java/
dev/tr7zw/entityculling/ # EntityCulling (custom license)
com/logisticscraft/occlusionculling/ # OcclusionCulling (MIT)
org/dreeam/leaf/config/ # Fork configuration
minecraft-patches/
features/
0350-Raytrace-Entity-Tracker.patch # MC source modifications
```

## Patch System

Patches are applied in numeric order by Paperweight patcher. Each patch modifies decompiled Minecraft server classes. Patch changes are validated by `applyAllPatches` — blob hash mismatches require `rebuildPatches` workflow.

## Key MC Classes Touched

| Class | Patch changes |
|-------|--------------|
| `ChunkMap.java` | Add `!entity.isCulled(player)` to tracking range check |
| `Entity.java` | Add `Cullable` interface, `lastTime` field, culling methods |
| `EntityType.java` | Add `skipRaytraceCheck` field |
| `Player.java` | Add `cullTask` field, CullTask lifecycle |
| `Level.java` | Add block change notification hook |

## Stabilization Architecture

### Block-change processing (REQ-001)
Replace `CompletableFuture.runAsync()` firehose with `AtomicBoolean dirty` flag + periodic reset in existing timer tick. Zero async tasks, O(players) lock-free CAS.

### Thread safety (REQ-002)
`Player.cullTask` → `AtomicReference<CullTask>`. Immutable reference after construction, safe cross-thread read.

### Visibility model (REQ-003, REQ-004)
Fix inverted ray-AABB test. Re-enable short-circuit `isForcedVisible()` by removing `|| true`.

### Error handling (REQ-007)
Replace `catch(Throwable)` + `printStackTrace()` with `Logger.warn()` + rate-limited guard (at most 1 per exception type per 5s).

### Modern Java upgrades (architectural)
- `record Vec3d(double x, y, z)` — immutable vectors
- `sealed interface EntityTrait` — compile-time exhaustiveness for entity filtering
- `ConcurrentHashMap` for `OcclusionCache` — safe MT reads
27 changes: 27 additions & 0 deletions docs/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Fuji — Requirements

## Raytrace Entity Tracker Stabilization

### REQ-001 — Bounded block-change processing
**Unwanted.** IF the raytrace entity tracker is enabled AND block state changes occur in loaded chunks THEN THE SYSTEM SHALL process block-change notifications in O(players) time per tick without spawning unbounded asynchronous tasks.

### REQ-002 — Thread-safe CullTask access
**State-driven.** WHILE the multithreaded entity tracker runs THE SYSTEM SHALL provide safe cross-thread visibility of the CullTask reference via volatile or AtomicReference semantics.

### REQ-003 — Forced-visible grace period
**Event-driven.** WHEN an entity transitions from culled to visible THE SYSTEM SHALL prevent re-culling for a configurable grace period.

### REQ-004 — Corrected ray-AABB intersection
**Ubiquitous.** THE SYSTEM SHALL correctly determine whether a ray intersects an axis-aligned bounding box, returning true when the far intersection distance is positive and the near distance does not exceed the far distance.

### REQ-005 — Config value bounds
**Event-driven.** WHEN the raytrace-entity-tracker configuration section is loaded THE SYSTEM SHALL validate and clamp all numeric parameters to defined safe ranges, logging a warning when clamping occurs.

### REQ-006 — Correct armor-stand skipping
**Event-driven.** WHEN the skip-marker-armor-stand configuration option is enabled THE SYSTEM SHALL skip only armor stands where ArmorStand.isMarker() returns true.

### REQ-007 — Rate-limited error logging
**Unwanted.** IF an exception occurs during a visibility check THEN THE SYSTEM SHALL emit a rate-limited log message through the standard logging framework.

### REQ-008 — License compliance
**Ubiquitous.** THE SYSTEM SHALL include the tr7zw/EntityCulling custom license text in the distributed binary.
Loading