Skip to content

Features

Martijn Muijsers edited this page Jul 3, 2026 · 10 revisions

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.
    • 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()
  • Collections
    Use an optimal collection type (such as one from it.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.seenBy into a list (for iteration) and a packed boolean array (for set functionality) in which each Connection is indexed by Connection.packedSeenByWordIndex and Connection.packedSeenByWordMask.
      • Make ServerEntity.trackedPlayers a list.
    • 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 of optimize-despawn.patch
  • 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 duplicated Bukkit.isPushable() check, already checked inside the Bukkit.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 before canSee, because the latter is more expensive.
      Leaf: Reduce-canSee-work.patch
    • LivingEntity.tryAddFrost() (original by 2No2Name)
      Sort the checks in LivingEntity.tryAddFrost() in ascending order of cost.
      Leaf: Check-frozen-ticks-before-landing-block.patch
    • Raid.updateBossbar() (original by jellysquid3)
      Performs the code in Raid.updateBossbar() at most once per tick (during tick()), instead of every time it is called.
      Leaf: Update-boss-bar-within-tick.patch
    • RespawnAnchorBlock.explode() (original by Dreeam)
      Compute inWater as late as possible.
      Leaf: part of Optimize-respawn-anchor-explosion.patch
    • SetLookAndInteract.create() (original by Taiyou)
      Check type before computing distance, because the latter is more expensive.
      Leaf: part of Optimize-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
    • Skip negligible planar movement multiplication
      Skip calling Entity.getBlockSpeedFactor() from Entity.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 of Entity.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
  • 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 - Pre-check for removeWhenFarAway()
      • AbstractFish.canRemoveWhenFarAway
      • Axolotl.canRemoveWhenFarAway
      • Cat.canRemoveWhenFarAway
      • Chicken.canRemoveWhenFarAway
      • 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
  • Faster implementation for subclass
    Override a superclass method with a faster implementation specific to the subclass.
    • LevelChunk.getSectionIndex()(original by hayanesuru)
      Leaf: part of optimize-LevelChunk-getBlockStateFinal.patch
  • Intrusive values
    Store properties of some types directly as a field, instead of in some external data structure.
    • CraftPlayer.invertedVisibilityEntities
      For each entry in CraftPlayer.invertedVisibilityEntities, store a corresponding inverse entry in CraftEntity.packedVisibilityInversions, an efficient data structure in which each player is indexed by CraftPlayer.packedVisibilityInversionsWordIndex and CraftPlayer.packedVisibilityInversionsWordMask.
      This allows for fast lookups from CraftPlayer.canSee().
    • EntityDataSerializers.SERIALIZERS
      Make EntityDataSerializers.SERIALIZERS a simple list, and store EntityDataSerializers.getSerializedId(EntityDataSerializer) in AbstractEntityDataSerializer.indexInList.
    • Global block state palette id
      Store Block.BLOCK_STATE_REGISTRY.getId(BlockState) in BlockState.indexInRegistry.
    • Registry elements
      • Make Registry.getId(T) available as IdAwareRegistryValue.getIdInRegistry() (which is stored in FieldIdAwareRegistryValue.idInRegistry).
      • Store Registry.asHolderIdMap().IdMap.getId(Holder) in Holder.Reference.idInRegistry.
  • 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 of optimize-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 of Optimize-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
  • 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
  • Optimize Mob.checkDespawn
    Optimize the contents and calling of Mob.checkDespawn method in various ways.
  • Pre-compute
    Pre-compute values instead of repeatedly computing them whenever needed.
    • BiomeManager.getFiddle() (original by hayanesuru)
      Leaf: part of cache-biome-for-mob-spawning-and-advancements.patch
    • Common BlockState predicates (original by hayanesuru)
      Pre-compute some commonly tested predicates on BlockStates.
      Leaf:
      • part of Cache-block-state-tags.patch
      • optimize-canHoldAnyFluid.patch
      • part of optimize-getOnPos.patch
      • optimize-isStateClimbable.patch
    • Enchantment.matchingSlot()
      Leaf: replaces Remove-stream-in-matchingSlot.patch
    • Registry tags
      Pre-compute the tags for each registry element and store them in Holder.tagsBitSet, an efficient data structure in which each tag is indexed by TagKey.indexInRegistryWordIndex and TagKey.indexInRegistryWordIndex.
  • Reduce allocations
    Reduce object allocations. This also saves time during garbage collection.
    • RandomSource (original by foss-mc)
      Re-use RandomSource instances where it doesn't affect game mechanics.
      Leaf: Reduce-RandomSource-instances.patch
  • 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.0f returns 16777215.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.

Clone this wiki locally