A demonstration plugin showcasing how to eliminate bucket placement desync in Minecraft 1.7 & 1.8 by leveraging Apollo client-side ray trace data.
Why is this implemented as a separate plugin and not directly within Apollo?
- Apollo is designed as a platform-agnostic API. Directly modifying NMS (like replacing bucket logic) is outside its scope to maintain compatibility across different server versions/platforms.
- Apollo provides the
ApolloPlayerUseItemBucketEvent, but this event is triggered via the Bukkit Plugin Message API and always runs on the main thread. To prevent server-side desync, the block placement packet must be intercepted before the server processes it incorrectly. Achieving this requires handling packets at the network level.
This plugin modifies core vanilla bucket mechanics by:
- Replacing the vanilla bucket item implementations in the NMS item registry.
- Canceling default server-side placement logic for buckets.
- Implementing custom placement logic based on client ray trace data.
Potential Issues:
- May conflict with other plugins that modify bucket behavior.
- Could cause compatibility issues with anti-cheat plugins.
Security Implication:
- Could be exploited by modified clients to interact with blocks they shouldn't reach.
- May allow impossible placements if validation is insufficient.
Implementation Note:
The server-sided approach shown here is implemented as a plugin for demonstration. Optimally, this processing should be done directly in the server's packet processing logic (e.g., within the PlayerConnection class) rather than through NMS item replacement.
Use at your own risk. This plugin is intended as a proof-of-concept.
In Minecraft, bucket interactions (water, lava) often appear desynced between the client and server, especially in high-latency environments.
When a player right-clicks with a bucket:
-
Client Side:
- Performs a ray trace from the player's perspective.
- Determines which block is being targeted.
- Sends a
PacketPlayInBlockPlacepacket to the server. - Renders the bucket placement immediately.
-
Server Side:
- Receives the packet from the client.
- Performs its own independent ray trace from the server's view of the player position.
- Places the bucket based on the server's ray trace result.
- Sends block updates back to the client.
The client and server often disagree on which block was clicked due to network latency: Player position differs between client and server due to packet travel time (latency).
The result is that the server rejects or moves your placement, causing visual glitches, failed placements, or blocks appearing in unexpected locations.
This plugin synchronizes the server's placement logic with the client's view by:
- Using Apollo's Packet Enrichment Module to receive the client's ray trace data.
- Replacing vanilla bucket items with custom implementations that respect client data.
The server trusts the client's ray trace result rather than performing its own, eliminating the desync.
- Upon using a bucket, Lunar Client performs a ray trace and captures the exact block/position the player is targeting.
- Before sending the vanilla
PacketPlayInBlockPlacepacket, the client sends a custom Apollo packet containing the ray trace data.
-
Initialization: Upon starting the server, the default
ItemBucketin the NMS registry is replaced with our custom implementation,ApolloItemBucket.Note: The code shown below is for server version
v1_8_R3.private void replaceBucket() { Item customBucket = new ApolloItemBucket(Blocks.AIR) .c("bucket").c(16); Item.REGISTRY.a(325, new MinecraftKey("bucket"), customBucket); Item.REGISTRY.a(326, new MinecraftKey("water_bucket"), new ApolloItemBucket(Blocks.FLOWING_WATER) .c("bucketWater").c(customBucket)); Item.REGISTRY.a(327, new MinecraftKey("lava_bucket"), new ApolloItemBucket(Blocks.FLOWING_LAVA) .c("bucketLava").c(customBucket)); }
-
Data Reception: The server receives the Apollo
PlayerUseItemBucketMessagevia thePacketPlayInCustomPayloadpacket and caches the ray trace data for the player. -
Packet Interception: When the vanilla
PacketPlayInBlockPlacepacket arrives:- The server checks if it's a bucket usage.
- If valid client ray trace data is cached, the default server-side placement packet is cancelled.
- This prevents the server from running its standard logic which would use the potentially incorrect server-side ray trace.
-
Custom Processing: The plugin manually processes the placement on the main thread:
- It simulates firing
PlayerInteractEventusing the client's ray trace location. This ensures other plugins see the interaction at the correct location. - If the event is not cancelled, it calls
PlayerInteractManager#useItem.
- It simulates firing
-
Placement Logic: The
useItemcall eventually triggers our customApolloItemBucketlogic:Note: The NMS code shown below is for version
v1_8_R3.
public class ApolloItemBucket extends ItemBucket {
public ItemStack a(ItemStack item, World world, EntityHuman entity) {
boolean flag = this.block == Blocks.AIR;
- MovingObjectPosition movingobjectposition = this.a(world, entity, flag);
+ // Desync fix start
+ MovingObjectPosition movingobjectposition = null;
+
+ // Try to get the client's ray trace result first
+ BucketPlaceManager bucketManager = DesyncPlugin.getInstance().getBucketPlaceManager();
+ if (bucketManager != null && entity instanceof EntityPlayer) {
+ EntityPlayer player = (EntityPlayer) entity;
+ RayTraceResult clientRayTrace = bucketManager.getAndRemove(player.getUniqueID());
+
+ if (clientRayTrace instanceof BlockHitResult) {
+ BlockHitResult blockHit = (BlockHitResult) clientRayTrace;
+ movingobjectposition = bucketManager.convertToMovingObjectPosition(blockHit);
+ }
+ }
+
+ // Fallback to server-side ray trace if no client data available
+ if (movingobjectposition == null) {
+ movingobjectposition = this.a(world, entity, flag);
+ }
+ // Desync fix end
+
+ // ... the rest of the vanilla implementation ...
}
}- PacketEvents
- Apollo v1.2.2+
- LunarClient Version (TODO)
- Install Apollo, PacketEvents, and this DesyncPlugin in your server's
pluginsfolder. - Configure Apollo:
- Ensure the Packet Enrichment module is enabled in Apollo's
config.yml.
- Ensure the Packet Enrichment module is enabled in Apollo's
--- /plugins/Apollo-Bukkit/config.yml
+++ /plugins/Apollo-Bukkit/config.yml
@@
packet_enrichment:
# Set to 'true' to enable this module, otherwise set 'false'.
- enable: false
+ enable: true
player-use-item-bucket:
# Set to 'true' to have the client send an additional player use item bucket packet to the server, otherwise 'false'.
- send-packet: false
+ send-packet: true
# If 'true', Apollo fires the player use item bucket event on the main thread. Disable this and handle the packet yourself if you require asynchronous or off-thread processing.
fire-apollo-event: false