-
Notifications
You must be signed in to change notification settings - Fork 20
Features
All included features are listed below.
Features originating from other projects are carefully verified and updated as part of Gale.
-
Cache
Lazily compute values and cache them while they remain valid.-
BiomeManager.getBiome() (original by hayanesuru)
Caches biomes requested withLevel.getBiome(), excluding Bukkit API calls. This caches results for mob spawning, villager breeding, advancement triggers and more.
Leaf: part ofcache-biome-for-mob-spawning-and-advancements.patch - Enchantment.hashCode()
-
Identifier.hashCode() and Identifier.toString() (original by OverwriteMC)
Leaf:Cache-identifier-toString-and-hash.patch - MapDecorationType.hashCode()
-
NoiseBasedChunkGenerator.getSeaLevel() (original by jaskarth)
This method is potentially called for every block in the chunk, so this will save a lot of lookups.
Leaf:Cache-world-generator-sea-level.patch - PaintingVariant.hashCode()
-
SpecialDates.isHalloween()
Pre-compute the epoch milliseconds for the next Halloween, for faster comparison.
Leaf:Predict-Halloween.patch - TagKey.hashCode()
-
BiomeManager.getBiome() (original by hayanesuru)
-
Collections
Use an optimal collection type (such as one fromit.unimi.dsi.fastutil, or a linked map for fast iteration), potentially initialized with a specific capacity.-
ChunkMap.entityMap
Using a linked map is nearly equivalent to the non-linked map, but slightly faster in practice due to the frequency of full iterations.
Leaf:Use-linked-map-for-entity-trackers.patch -
ChunkMap.TrackedEntity.seenBy
Store seenBy in a fast-access format-
Split
ChunkMap.TrackedEntity.seenByinto a list (for iteration) and a packed boolean array (for set functionality) in which eachConnectionis indexed byConnection.packedSeenByWordIndexandConnection.packedSeenByWordMask. -
Make
ServerEntity.trackedPlayersa list.
-
Split
-
MobCounts.counts (use array) (original by ishland)
Leaf:Store-mob-counts-in-an-array.patch -
Sensing
Most mobs only ever target at most 1 entity (typically a nearby player or a mob farm bait entity)
so we create their sensing cache with an initial capacity of 2 instead of 16.
Leaf:Initialize-line-of-sight-cache-with-low-capacity.patch -
ServerHandshakePacketListenerImpl.throttleTracker
Leaf:Replace-throttle-tracker-map-with-optimized-collecti.patch -
SynchedEntityData.packDirty() (original by hayanesuru)
Leaf:Optimize-SynchedEntityData-packDirty.patch -
WorldConfiguration.Entities.Spawning.despawnRanges (use EnumMap) (original by hayanesuru)
Leaf: part ofoptimize-despawn.patch
-
ChunkMap.entityMap
-
Do less work
Evaluate the cheapest conditions first, and avoid doing unnecessary work.-
Check ticks frozen difference only once (original by hayanesuru)
Leaf:Only-update-frozen-ticks-if-changed.patch -
EntitySelector.pushableBy() (original by OverwriteMC)
Avoid duplicatedBukkit.isPushable()check, already checked inside theBukkit.canCollideWithBukkit().
Leaf:Optimize-pushable-selector.patch -
Exit Bukkit event call early if no listeners (original by lilingfengdev)
Leaf:Skip-event-if-no-listeners.patch -
Level.checkEntityCollision()
Perform other checks beforecanSee, because the latter is more expensive.
Leaf:Reduce-canSee-work.patch -
LivingEntity.tryAddFrost() (original by 2No2Name)
Sort the checks inLivingEntity.tryAddFrost()in ascending order of cost.
Leaf:Check-frozen-ticks-before-landing-block.patch -
Raid.updateBossbar() (original by jellysquid3)
Performs the code inRaid.updateBossbar()at most once per tick (duringtick()), instead of every time it is called.
Leaf:Update-boss-bar-within-tick.patch -
RespawnAnchorBlock.explode() (original by Dreeam)
ComputeinWateras late as possible.
Leaf: part ofOptimize-respawn-anchor-explosion.patch -
SecondaryPoiSensor.doTick() (skip secondary POI sensor if absent) (original by 2No2Name)
Leaf: part ofSkip-secondary-POI-sensor-if-absent.patch -
SetLookAndInteract.create() (original by Taiyou)
Check type before computing distance, because the latter is more expensive.
Leaf: part ofOptimize-SetLookAndInteract-and-NearestVisibleLiving.patch -
Skip client-side code
Do not perform actions server-side if the result is only used client-side.-
Leashable.getLeashHolder() (original by MrlingXD)
Leaf:Optimize-getLeashHolder.patch
-
Leashable.getLeashHolder() (original by MrlingXD)
-
Skip negligible planar movement multiplication
Skip callingEntity.getBlockSpeedFactor()fromEntity.move()when planar delta movement is negligible (within 1.0E-6 threshold).
Leaf:Skip-negligible-planar-movement-multiplication.patch -
Skip PlayerCommandSendEvent if no listeners (original by BillyGalbreath)
Leaf:Skip-PlayerCommandSendEvent-if-there-are-no-listener.patch -
Skip self check in CraftPlayer.canSee() if called from ChunkMap.updatePlayer() (original by MrPowerGamerBR)
Skip the self check, as it was already checked at that point.
Leaf:SparklyPaper-Optimize-canSee-checks.patch -
Skip unnecessary Entity.move() code if bounding box and position are unchanged (original by ishland)
Run a simplified version ofEntity.move()if the bounding box was not changed and the movement delta is zero.
Leaf:Skip-entity-move-if-movement-is-zero.patch -
TargetingConditions.test() (original by PaulBGD)
Check targeting range before computing visibility distance, because the latter is more expensive.
Leaf:Check-targeting-range-before-getting-visibility.patch
-
Check ticks frozen difference only once (original by hayanesuru)
-
Event-driven
Update (and keep cached) values or take certain actions when the underlying conditions change, instead of calculating the value or considering the action every time it is requested.-
Supporting Optimize Mob.checkDespawn
- AbstractFish.canRemoveWhenFarAway
- Axolotl.canRemoveWhenFarAway
- Cat.canRemoveWhenFarAway
- Chicken.canRemoveWhenFarAway
- Mob.despawnRanges
- Mob.despawnRangeShape
- Ocelot.canRemoveWhenFarAway
- Piglin.canRemoveWhenFarAway
- Raider.canRemoveWhenFarAway
- ZombieVillager.canRemoveWhenFarAway
- Entity.playerPassengerCount
- Level.affectsSpawningSelectorPlayerCount
- LivingEntity.isAlive
- Mob.isLeashed
- Mob.isPersistent
- Mob.mustDespawnBecauseOfPeacefulDifficulty
- Mob.requiresCustomPersistence
- Mob.shouldDespawnInPeaceful
- Player.affectsSpawningSelector
-
Supporting Optimize Mob.checkDespawn
-
Faster implementation for subclass
Override a superclass method with a faster implementation specific to the subclass.-
LevelChunk.getSectionIndex()(original by hayanesuru)
Leaf: part ofoptimize-LevelChunk-getBlockStateFinal.patch
-
LevelChunk.getSectionIndex()(original by hayanesuru)
-
Intrusive values
Store properties of some types directly as a field, instead of in some external data structure.-
CraftPlayer.invertedVisibilityEntities
For each entry inCraftPlayer.invertedVisibilityEntities, store a corresponding inverse entry inCraftEntity.packedVisibilityInversions, an efficient data structure in which each player is indexed byCraftPlayer.packedVisibilityInversionsWordIndexandCraftPlayer.packedVisibilityInversionsWordMask.
This allows for fast lookups fromCraftPlayer.canSee(). -
EntityDataSerializers.SERIALIZERS
MakeEntityDataSerializers.SERIALIZERSa simple list, and storeEntityDataSerializers.getSerializedId(EntityDataSerializer)inAbstractEntityDataSerializer.indexInList. -
Global block state palette id
StoreBlock.BLOCK_STATE_REGISTRY.getId(BlockState)inBlockState.indexInRegistry. -
Registry elements
-
Make
Registry.getId(T)available asIdAwareRegistryValue.getIdInRegistry()(which is stored inFieldIdAwareRegistryValue.idInRegistry). -
Store
Registry.asHolderIdMap().IdMap.getId(Holder)inHolder.Reference.idInRegistry.
-
Make
-
CraftPlayer.invertedVisibilityEntities
-
Local code optimization
Make small, localized procedures faster.-
Add equality check to ItemStack.isSameItem...()
Leaf:Optimize-matching-item-checks.patch -
DedicatedServer.getScaledTrackingDistance() (original by hayanesuru)
Leaf:Optimize-getScaledTrackingDistance.patch -
EntityBasedExplosionDamageCalculator.getBlockExplosionResistance() (reduce allocations) (original by 2No2Name)
Leaf:Reduce-lambda-and-Optional-allocation-in-EntityBased.patch -
EntityGetter.getNearestPlayer() (original by Taiyou)
Leaf:Slightly-optimise-getNearestPlayer.patch -
GateBehavior.tickOrStop() (remove stream) (original by Taiyou)
Leaf:Remove-stream-in-GateBehavior.patch - Inline player UUID comparison
-
LevelChunk.getBlockStateFinal()(original by hayanesuru)
Leaf:optimize-LevelChunk-getBlockStateFinal.patch -
LivingEntity.tickEffects() (don't iterate over empty effect collection) (original by hayanesuru)
Leaf: part ofoptimize-tickEffects.patch -
MobEffectUtil.getDigSpeedAmplification() (original by Taiyou)
Leaf:Optimise-MobEffectUtil-getDigSpeedAmplification.patch -
MobSensor.checkForMobsNearby() (remove stream) (original by Taiyou)
Leaf:Remove-stream-in-MobSensor.patch -
NearestVisibleLivingEntities.findClosest() (add empty check and size variable) (original by Taiyou)
Leaf: part ofOptimize-SetLookAndInteract-and-NearestVisibleLiving.patch -
new Advancement() (skip cloning criteria) (original by etil2jz)
Leaf:Skip-cloning-advancement-criteria.patch -
PatchedDataComponentMap.equals() (original by HaHaWTH)
Leaf:Optimize-PatchedDataComponentMap-equals.patch -
ServerLevel.destroyBlockProgress() (allocate zero or one block destruction packets) (original by vytskalt)
Leaf:Reduce-block-destruction-packet-allocations.patch
-
Add equality check to ItemStack.isSameItem...()
-
Math
Optimize mathematical expressions by hardware operation efficiency. For example, multiplication is faster than division on all hardware.-
CubePointRange.getDouble() (replace division by multiplication) (original by 2No2Name)
Leaf:Replace-division-by-multiplication-in-CubePointRange.patch
-
CubePointRange.getDouble() (replace division by multiplication) (original by 2No2Name)
-
Optimize Mob.checkDespawn
Optimize the contents and calling ofMob.checkDespawnmethod in various ways. -
Pre-compute
Pre-compute values instead of repeatedly computing them whenever needed.-
BiomeManager.getFiddle() (original by hayanesuru)
Leaf: part ofcache-biome-for-mob-spawning-and-advancements.patch -
Common BlockState predicates (original by hayanesuru)
Pre-compute some commonly tested predicates onBlockStates.
Leaf:- part of
Cache-block-state-tags.patch optimize-canHoldAnyFluid.patch- part of
optimize-getOnPos.patch optimize-isStateClimbable.patch
- part of
-
CubePointRange.size
ReplaceCubePointRange.partsbyCubePointRange.size.
Leaf: replacesRemove-stream-in-matchingSlot.patch -
Enchantment.matchingSlot()
Leaf:Replace-parts-by-size-in-CubePointRange.patch -
Registry tags
Pre-compute the tags for each registry element and store them inHolder.tagsBitSet, an efficient data structure in which each tag is indexed byTagKey.indexInRegistryWordIndexandTagKey.indexInRegistryWordIndex.
-
BiomeManager.getFiddle() (original by hayanesuru)
-
Reduce allocations
Reduce object allocations. This also saves time during garbage collection.-
RandomSource (original by foss-mc)
Re-useRandomSourceinstances where it doesn't affect game mechanics.
Leaf:Reduce-RandomSource-instances.patch
-
RandomSource (original by foss-mc)
-
Skip enderman teleport if requires main thread chunk load (original by PaulBGD)
Leaf:Reduce-enderman-teleport-chunk-lookups.patch
Differences with Paper
Although Gale will normally have the same observable behavior as Paper, it does make some internal changes.
Obviously, Gale modifies internal code (replaces existing code with faster alternatives). Some "plugins" do not just use the plugin API, but rely deeply on Paper internals, which are not even supported by Paper itself. These plugins typically break every Minecraft version, and they may break on Gale. We really try to support these plugins regardless, but there is a limit. As an example, we could never support a plugin that doesn't kill an entity via the API, but instead simply deletes it from the world using Minecraft internals without even marking it as dead or calling any events.
There also exist some actual behavior differences, that really should not affect anyone. We explain these below for transparency. However, note that we are very open to removing these behavioral differences when they actually do affect any practical scenario.
-
Exceptions when calling synchronous events with no listeners asynchronously
While rarely used, plugins can manually fire Bukkit events. Obviously, it is not allowed to fire a synchronous event asynchronously. Paper throws an Exception if a misbehaving plugin does so anyway. Paper throws an Exception even when the event has no listeners (and so it would never have caused a problem). Gale only throws the Exception if it would have caused a problem. Even if a badly written plugin somehow decides to rely on Paper repeatedly throwing these Exceptions when the plugin makes forbidden calls, there is no imaginable way that this could actually matter for events with no listeners. -
Floating-point operations
Floating-point operations are not precise. For example,16777217 + 1.0f - 1.0freturns16777215.0f. Any change in an expression may cause the result to slightly change. However, these differences happen all the time, and no plugins or behaviors could ever rely on floating-point errors not existing. Notable exception: of course, world generation on Gale is guaranteed to be the exact same as on Paper. -
Random-based synchronous chunk loading
Some events in Minecraft target a random location, with no regard for whether that location is loaded or not. In some cases, this can lead to an ad-hoc synchronous chunk load, which really hurts performance. For events that no farm could ever depend on, Gale may choose to limit the choice to chunks that have already been loaded. This can prevent some heavy lag spikes without any effect on gameplay.