1818package com .viaversion .viaversion .protocols .v1_21to1_21_2 .rewriter ;
1919
2020import com .viaversion .nbt .tag .CompoundTag ;
21+ import com .viaversion .viaversion .api .connection .UserConnection ;
2122import com .viaversion .viaversion .api .data .entity .EntityTracker ;
2223import com .viaversion .viaversion .api .minecraft .RegistryEntry ;
2324import com .viaversion .viaversion .api .minecraft .entities .EntityType ;
2425import com .viaversion .viaversion .api .minecraft .entities .EntityTypes1_21_2 ;
26+ import com .viaversion .viaversion .api .minecraft .entitydata .EntityData ;
27+ import com .viaversion .viaversion .api .minecraft .item .Item ;
2528import com .viaversion .viaversion .api .protocol .packet .PacketWrapper ;
2629import com .viaversion .viaversion .api .protocol .remapper .PacketHandlers ;
2730import com .viaversion .viaversion .api .type .Types ;
3639import com .viaversion .viaversion .protocols .v1_21to1_21_2 .storage .BundleStateTracker ;
3740import com .viaversion .viaversion .protocols .v1_21to1_21_2 .storage .ChunkLoadTracker ;
3841import com .viaversion .viaversion .protocols .v1_21to1_21_2 .storage .ClientVehicleStorage ;
42+ import com .viaversion .viaversion .protocols .v1_21to1_21_2 .storage .EntityTracker1_21_2 ;
3943import com .viaversion .viaversion .protocols .v1_21to1_21_2 .storage .PlayerPositionStorage ;
4044import com .viaversion .viaversion .rewriter .EntityRewriter ;
4145import com .viaversion .viaversion .rewriter .RegistryDataRewriter ;
46+ import java .util .List ;
47+ import java .util .UUID ;
4248import java .util .concurrent .ThreadLocalRandom ;
4349
4450public final class EntityPacketRewriter1_21_2 extends EntityRewriter <ClientboundPacket1_21 , Protocol1_21To1_21_2 > {
@@ -65,6 +71,32 @@ public void registerPackets() {
6571 registerSetEntityData (ClientboundPackets1_21 .SET_ENTITY_DATA , Types1_21 .ENTITY_DATA_LIST , Types1_21_2 .ENTITY_DATA_LIST );
6672 registerRemoveEntities (ClientboundPackets1_21 .REMOVE_ENTITIES );
6773
74+ protocol .appendClientbound (ClientboundPackets1_21 .ADD_ENTITY , wrapper -> {
75+ final int entityType = wrapper .get (Types .VAR_INT , 1 );
76+
77+ final EntityType type = typeFromId (entityType );
78+ if (type == null || !type .isOrHasParent (EntityTypes1_21_2 .ABSTRACT_BOAT )) {
79+ return ;
80+ }
81+
82+ final int entityId = wrapper .get (Types .VAR_INT , 0 );
83+ final UUID uuid = wrapper .get (Types .UUID , 0 );
84+
85+ final double x = wrapper .get (Types .DOUBLE , 0 );
86+ final double y = wrapper .get (Types .DOUBLE , 1 );
87+ final double z = wrapper .get (Types .DOUBLE , 2 );
88+
89+ final float pitch = wrapper .get (Types .BYTE , 0 ) * 256.0F / 360.0F ;
90+ final float yaw = wrapper .get (Types .BYTE , 1 ) * 256.0F / 360.0F ;
91+
92+ final int data = wrapper .get (Types .VAR_INT , 2 );
93+
94+ final EntityTracker1_21_2 tracker = tracker (wrapper .user ());
95+ final EntityTracker1_21_2 .BoatEntity entity = tracker .trackBoatEntity (entityId , uuid , data );
96+ entity .setPosition (x , y , z );
97+ entity .setRotation (yaw , pitch );
98+ });
99+
68100 protocol .registerFinishConfiguration (ClientboundConfigurationPackets1_21 .FINISH_CONFIGURATION , wrapper -> {
69101 final PacketWrapper instrumentsPacket = wrapper .create (ClientboundConfigurationPackets1_21 .REGISTRY_DATA );
70102 instrumentsPacket .write (Types .STRING , "minecraft:instrument" );
@@ -193,6 +225,12 @@ public void register() {
193225 wrapper .user ().remove (ClientVehicleStorage .class );
194226 }
195227
228+ final EntityTracker1_21_2 tracker = tracker (wrapper .user ());
229+ final EntityTracker1_21_2 .BoatEntity entity = tracker .trackedBoatEntity (vehicleId );
230+ if (entity != null ) {
231+ entity .setPassengers (passengerIds );
232+ }
233+
196234 final int clientEntityId = tracker (wrapper .user ()).clientEntityId ();
197235 for (final int passenger : passengerIds ) {
198236 if (passenger == clientEntityId ) {
@@ -243,23 +281,34 @@ public void register() {
243281 });
244282
245283 protocol .registerClientbound (ClientboundPackets1_21 .TELEPORT_ENTITY , ClientboundPackets1_21_2 .ENTITY_POSITION_SYNC , wrapper -> {
246- wrapper .passthrough (Types .VAR_INT ); // Entity ID
284+ final int entityId = wrapper .passthrough (Types .VAR_INT ); // Entity ID
247285
248- wrapper .passthrough (Types .DOUBLE ); // X
249- wrapper .passthrough (Types .DOUBLE ); // Y
250- wrapper .passthrough (Types .DOUBLE ); // Z
286+ final double x = wrapper .passthrough (Types .DOUBLE ); // X
287+ final double y = wrapper .passthrough (Types .DOUBLE ); // Y
288+ final double z = wrapper .passthrough (Types .DOUBLE ); // Z
251289
252290 // Unused...
253291 wrapper .write (Types .DOUBLE , 0D ); // Delta movement X
254292 wrapper .write (Types .DOUBLE , 0D ); // Delta movement Y
255293 wrapper .write (Types .DOUBLE , 0D ); // Delta movement Z
256294
257295 // Unpack y and x rot
258- final byte yaw = wrapper .read (Types .BYTE );
259- final byte pitch = wrapper .read (Types .BYTE );
260- wrapper .write (Types .FLOAT , yaw * 360F / 256F );
261- wrapper .write (Types .FLOAT , pitch * 360F / 256F );
296+ final float yaw = wrapper .read (Types .BYTE ) * 360F / 256F ;
297+ final float pitch = wrapper .read (Types .BYTE ) * 360F / 256F ;
298+ wrapper .write (Types .FLOAT , yaw );
299+ wrapper .write (Types .FLOAT , pitch );
300+
301+ final EntityTracker1_21_2 tracker = tracker (wrapper .user ());
302+ final EntityTracker1_21_2 .BoatEntity trackedEntity = tracker .trackedBoatEntity (entityId );
303+ if (trackedEntity == null ) {
304+ return ;
305+ }
306+ trackedEntity .setPosition (x , y , z );
307+ trackedEntity .setRotation (yaw , pitch );
262308 });
309+ protocol .registerClientbound (ClientboundPackets1_21 .MOVE_ENTITY_POS , wrapper -> storeEntityPositionRotation (wrapper , true , false ));
310+ protocol .registerClientbound (ClientboundPackets1_21 .MOVE_ENTITY_POS_ROT , wrapper -> storeEntityPositionRotation (wrapper , true , true ));
311+ protocol .registerClientbound (ClientboundPackets1_21 .MOVE_ENTITY_ROT , wrapper -> storeEntityPositionRotation (wrapper , false , true ));
263312
264313 protocol .registerServerbound (ServerboundPackets1_21_2 .MOVE_PLAYER_POS , wrapper -> {
265314 wrapper .passthrough (Types .DOUBLE ); // X
@@ -326,6 +375,27 @@ private void readOnGround(final PacketWrapper wrapper) {
326375 wrapper .write (Types .BOOLEAN , (data & 1 ) != 0 ); // On ground, ignoring horizontal collision data
327376 }
328377
378+ private void storeEntityPositionRotation (final PacketWrapper wrapper , final boolean position , final boolean rotation ) {
379+ final int entityId = wrapper .passthrough (Types .VAR_INT ); // Entity id
380+
381+ final EntityTracker1_21_2 tracker = tracker (wrapper .user ());
382+ final EntityTracker1_21_2 .BoatEntity trackedEntity = tracker .trackedBoatEntity (entityId );
383+ if (trackedEntity == null ) {
384+ return ;
385+ }
386+ if (position ) {
387+ final double x = wrapper .passthrough (Types .SHORT ) / 4096.0 ; // Delta X
388+ final double y = wrapper .passthrough (Types .SHORT ) / 4096.0 ; // Delta Y
389+ final double z = wrapper .passthrough (Types .SHORT ) / 4096.0 ; // Delta Z
390+ trackedEntity .setPosition (trackedEntity .x () + x , trackedEntity .y () + y , trackedEntity .z () + z );
391+ }
392+ if (rotation ) {
393+ final float yaw = wrapper .passthrough (Types .BYTE ) * 360.0F / 256.0F ;
394+ final float pitch = wrapper .passthrough (Types .BYTE ) * 360.0F / 256.0F ;
395+ trackedEntity .setRotation (yaw , pitch );
396+ }
397+ }
398+
329399 @ Override
330400 protected void registerRewrites () {
331401 filter ().mapDataType (Types1_21_2 .ENTITY_DATA_TYPES ::byId );
@@ -341,14 +411,161 @@ protected void registerRewrites() {
341411 );
342412 registerBlockStateHandler (EntityTypes1_21_2 .ABSTRACT_MINECART , 11 );
343413
344- filter ().type (EntityTypes1_21_2 .ABSTRACT_BOAT ).removeIndex (11 ); // Goodbye boat type
414+ filter ().type (EntityTypes1_21_2 .ABSTRACT_BOAT ).handler ((event , data ) -> {
415+ final int dataIndex = event .index ();
416+ // Boat type - now set as own entity type
417+ // Idea is to remove the old entity, then add a new one and re-apply entity data and passengers
418+ if (dataIndex > 11 ) {
419+ event .setIndex (dataIndex - 1 );
420+ return ;
421+ }
422+ if (dataIndex != 11 ) {
423+ return ;
424+ }
425+ event .cancel ();
426+
427+ final EntityTracker1_21_2 tracker = tracker (event .user ());
428+ final EntityTracker1_21_2 .BoatEntity entity = tracker .trackedBoatEntity (event .entityId ());
429+ if (entity == null ) {
430+ return ;
431+ }
432+
433+ final boolean isBundling = event .user ().get (BundleStateTracker .class ).isBundling ();
434+ if (!isBundling ) {
435+ final PacketWrapper bundleStart = PacketWrapper .create (ClientboundPackets1_21_2 .BUNDLE_DELIMITER , event .user ());
436+ bundleStart .send (Protocol1_21To1_21_2 .class );
437+ }
438+
439+ // Remove old entity
440+ final PacketWrapper removeEntityPacket = PacketWrapper .create (ClientboundPackets1_21_2 .REMOVE_ENTITIES , event .user ());
441+ removeEntityPacket .write (Types .VAR_INT_ARRAY_PRIMITIVE , new int [] { event .entityId () });
442+ removeEntityPacket .send (Protocol1_21To1_21_2 .class );
443+
444+ // Detect correct boat entity type from entity data
445+ final int boatType = (int ) data .getValue ();
446+ EntityType entityType ;
447+ if (tracker .entityType (event .entityId ()).isOrHasParent (EntityTypes1_21_2 .ABSTRACT_CHEST_BOAT )) {
448+ entityType = entityTypeFromChestBoatType (boatType );
449+ } else {
450+ entityType = entityTypeFromBoatType (boatType );
451+ }
452+
453+ // Spawn new entity
454+ final PacketWrapper spawnEntityPacket = PacketWrapper .create (ClientboundPackets1_21_2 .ADD_ENTITY , event .user ());
455+ spawnEntityPacket .write (Types .VAR_INT , event .entityId ()); // Entity ID
456+ spawnEntityPacket .write (Types .UUID , entity .uuid ()); // Entity UUID
457+ spawnEntityPacket .write (Types .VAR_INT , entityType .getId ()); // Entity type
458+ spawnEntityPacket .write (Types .DOUBLE , entity .x ()); // X
459+ spawnEntityPacket .write (Types .DOUBLE , entity .y ()); // Y
460+ spawnEntityPacket .write (Types .DOUBLE , entity .z ()); // Z
461+ spawnEntityPacket .write (Types .BYTE , (byte ) Math .floor (entity .pitch () * 256.0F / 360.0F )); // Pitch
462+ spawnEntityPacket .write (Types .BYTE , (byte ) Math .floor (entity .yaw () * 256.0F / 360.0F )); // Yaw
463+ spawnEntityPacket .write (Types .BYTE , (byte ) 0 ); // Head yaw
464+ spawnEntityPacket .write (Types .VAR_INT , entity .data ()); // Data
465+ spawnEntityPacket .write (Types .SHORT , (short ) 0 ); // Velocity X
466+ spawnEntityPacket .write (Types .SHORT , (short ) 0 ); // Velocity Y
467+ spawnEntityPacket .write (Types .SHORT , (short ) 0 ); // Velocity Z
468+ spawnEntityPacket .send (Protocol1_21To1_21_2 .class );
469+
470+ // Update tracked entity in storage with new entity type
471+ tracker .updateBoatType (event .entityId (), entityType );
472+
473+ // Re-apply entity data previously set
474+ final PacketWrapper setEntityDataPacket = PacketWrapper .create (ClientboundPackets1_21_2 .SET_ENTITY_DATA , event .user ());
475+ setEntityDataPacket .write (Types .VAR_INT , event .entityId ());
476+ setEntityDataPacket .write (Types1_21_2 .ENTITY_DATA_LIST , entity .entityData ());
477+ setEntityDataPacket .send (Protocol1_21To1_21_2 .class );
478+
479+ // Re-attach all passengers
480+ if (entity .passengers () != null ) {
481+ final PacketWrapper setPassengersPacket = PacketWrapper .create (ClientboundPackets1_21_2 .SET_PASSENGERS , event .user ());
482+ setPassengersPacket .write (Types .VAR_INT , event .entityId ());
483+ setPassengersPacket .write (Types .VAR_INT_ARRAY_PRIMITIVE , entity .passengers ());
484+ setPassengersPacket .send (Protocol1_21To1_21_2 .class );
485+ }
486+
487+ if (!isBundling ) {
488+ final PacketWrapper bundleEnd = PacketWrapper .create (ClientboundPackets1_21_2 .BUNDLE_DELIMITER , event .user ());
489+ bundleEnd .send (Protocol1_21To1_21_2 .class );
490+ }
491+ });
345492
346493 filter ().type (EntityTypes1_21_2 .SALMON ).addIndex (17 ); // Data type
347494 filter ().type (EntityTypes1_21_2 .AGEABLE_WATER_CREATURE ).addIndex (16 ); // Baby
348495
349496 filter ().type (EntityTypes1_21_2 .ABSTRACT_ARROW ).addIndex (10 ); // In ground
350497 }
351498
499+ @ Override
500+ public void handleEntityData (final int entityId , final List <EntityData > dataList , final UserConnection connection ) {
501+ super .handleEntityData (entityId , dataList , connection );
502+
503+ final EntityTracker1_21_2 tracker = tracker (connection );
504+ final EntityType entityType = tracker .entityType (entityId );
505+ if (entityType != null && !entityType .isOrHasParent (EntityTypes1_21_2 .ABSTRACT_BOAT )) {
506+ return ;
507+ }
508+
509+ final List <EntityData > entityData = tracker .trackedBoatEntity (entityId ).entityData ();
510+ entityData .removeIf (first -> dataList .stream ().anyMatch (second -> first .id () == second .id ()));
511+ for (final EntityData data : dataList ) {
512+ final Object value = data .value ();
513+ if (value instanceof Item item ) {
514+ entityData .add (new EntityData (data .id (), data .dataType (), item .copy ()));
515+ } else {
516+ entityData .add (new EntityData (data .id (), data .dataType (), value ));
517+ }
518+ }
519+ }
520+
521+ private EntityType entityTypeFromBoatType (final int boatType ) {
522+ if (boatType == 0 ) {
523+ return EntityTypes1_21_2 .OAK_BOAT ;
524+ } else if (boatType == 1 ) {
525+ return EntityTypes1_21_2 .SPRUCE_BOAT ;
526+ } else if (boatType == 2 ) {
527+ return EntityTypes1_21_2 .BIRCH_BOAT ;
528+ } else if (boatType == 3 ) {
529+ return EntityTypes1_21_2 .JUNGLE_BOAT ;
530+ } else if (boatType == 4 ) {
531+ return EntityTypes1_21_2 .ACACIA_BOAT ;
532+ } else if (boatType == 5 ) {
533+ return EntityTypes1_21_2 .CHERRY_BOAT ;
534+ } else if (boatType == 6 ) {
535+ return EntityTypes1_21_2 .DARK_OAK_BOAT ;
536+ } else if (boatType == 7 ) {
537+ return EntityTypes1_21_2 .MANGROVE_BOAT ;
538+ } else if (boatType == 8 ) {
539+ return EntityTypes1_21_2 .BAMBOO_RAFT ;
540+ } else {
541+ return EntityTypes1_21_2 .OAK_BOAT ; // Fallback
542+ }
543+ }
544+
545+ private EntityType entityTypeFromChestBoatType (final int chestBoatType ) {
546+ if (chestBoatType == 0 ) {
547+ return EntityTypes1_21_2 .OAK_CHEST_BOAT ;
548+ } else if (chestBoatType == 1 ) {
549+ return EntityTypes1_21_2 .SPRUCE_CHEST_BOAT ;
550+ } else if (chestBoatType == 2 ) {
551+ return EntityTypes1_21_2 .BIRCH_CHEST_BOAT ;
552+ } else if (chestBoatType == 3 ) {
553+ return EntityTypes1_21_2 .JUNGLE_CHEST_BOAT ;
554+ } else if (chestBoatType == 4 ) {
555+ return EntityTypes1_21_2 .ACACIA_CHEST_BOAT ;
556+ } else if (chestBoatType == 5 ) {
557+ return EntityTypes1_21_2 .CHERRY_CHEST_BOAT ;
558+ } else if (chestBoatType == 6 ) {
559+ return EntityTypes1_21_2 .DARK_OAK_CHEST_BOAT ;
560+ } else if (chestBoatType == 7 ) {
561+ return EntityTypes1_21_2 .MANGROVE_CHEST_BOAT ;
562+ } else if (chestBoatType == 8 ) {
563+ return EntityTypes1_21_2 .BAMBOO_CHEST_RAFT ;
564+ } else {
565+ return EntityTypes1_21_2 .OAK_CHEST_BOAT ; // Fallback
566+ }
567+ }
568+
352569 @ Override
353570 public EntityType typeFromId (final int type ) {
354571 return EntityTypes1_21_2 .getTypeFromId (type );
0 commit comments