### LDLib2 RPC Packet Handler and Sender Example (Java) Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/rpc_packet Demonstrates how to define an RPC packet handler using the @RPCPacket annotation and how to send packets to the server or all players using RPCSender and RPCPacketDistributor. ```java import com.ldbc.ldlib.api.RPCSender; import com.ldbc.ldlib.api.RPCPacket; import com.ldbc.ldlib.api.RPCPacketDistributor; import com.ldbc.ldlib.LDLib2; public class RpcExample { // annotate your packet method anywhere you want @RPCPacket("rpcPacketTest") public static void rpcPacketTest(RPCSender sender, String message, boolean message2) { if (sender.isServer()) { LDLib2.LOGGER.info("Received RPC packet from server: {}, {}", message, message2); } else { LDLib2.LOGGER.info("Received RPC packet from client: {}, {}", message, message2); } } // Example of sending packets (this would typically be in another method or class) public void sendPackets() { // send packet to the remote/server RPCPacketDistributor.rpcToServer("rpcPacketTest", "Hello from client!", true); // send packet to all players RPCPacketDistributor.rpcToAllPlayers("rpcPacketTest", "Hello from server!", false); } } ``` -------------------------------- ### Implement ISyncPersistRPCBlockEntity for BlockEntity Management Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/blockentity This Java code demonstrates how to implement the ISyncPersistRPCBlockEntity interface to automatically handle synchronization and persistence for a BlockEntity. It utilizes annotations like @Persisted, @DescSynced, and @UpdateListener to define synchronized fields and callbacks. The example also shows how to define and call RPC methods. ```java public class MyBlockEntity extends BlockEntity implements ISyncPersistRPCBlockEntity { @Getter private final FieldManagedStorage syncStorage = new FieldManagedStorage(this); @Persisted @DescSynced @UpdateListener(methodName = "onIntValueChanged") private int intValue = 10; @Persisted @DescSynced @DropSaved @RequireRerender private ItemStack itemStack = ItemStack.EMPTY; public MyBlockEntity(BlockPos pWorldPosition, BlockState pBlockState) { super(...); } private void onIntValueChanged(int oldValue, int newValue) { LDLib2.LOGGER.info("Int value changed from {} to {}", oldValue, newValue); } @RPCMethod public void rpcMsg(String msg) { if (level.isClient) { // receive LDLib2.LOGGER.info("Received RPC from server: {}", message); } else { // send rpcToTracking("rpcMsg", msg) } } ``` -------------------------------- ### Saving BlockEntity Data to Drop Items with @DropSaved Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @DropSaved annotation is used exclusively for BlockEntities to mark fields that should be saved to the dropped item when the block is broken. This requires additional setup within the Block class to handle saving and loading the data components. The example demonstrates saving and loading custom data. ```java public class MyBlock extends Block { @Override public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { if (!level.isClientSide) { if (level.getBlockEntity(pos) instanceof IPersistManagedHolder persistManagedHolder) { // you can use other DataComponents if you want. Optional.ofNullable(stack.get(DataComponents.CUSTOM_DATA)).ifPresent(customData -> { persistManagedHolder.loadManagedPersistentData(customData.copyTag()); }); } } } @Override protected List getDrops(BlockState state, LootParams.Builder params) { var opt = Optional.ofNullable(params.getOptionalParameter(LootContextParams.BLOCK_ENTITY)); if (opt.isPresent() && opt.get() instanceof IPersistManagedHolder persistManagedHolder) { var drop = new ItemStack(this); var tag = new CompoundTag(); persistManagedHolder.saveManagedPersistentData(tag, true); drop.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); // you can move this part to LootTable if you want. return List.of(drop); } return super.getDrops(state, params); } @Override public ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, Player player) { // if you want to clone an item with drop data, don't forget it if (level.getBlockEntity(pos) instanceof IPersistManagedHolder persistManagedHolder) { var clone = new ItemStack(this); var tag = new CompoundTag(); persistManagedHolder.saveManagedPersistentData(tag, true); clone.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); return clone; } return super.getCloneItemStack(state, target, level, pos, player); } } public class MyBlockEntity extends BlockEntity implements ISyncPersistRPCBlockEntity { @Persisted private int intValue = 10; @Persisted @DropSaved private ItemStack itemStack = ItemStack.EMPTY; } ``` -------------------------------- ### Vanilla/Forge BlockEntity Data Handling vs. LDLib2 Implementation Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync Compares the traditional vanilla/Forge approach for saving, loading, and synchronizing BlockEntity data with the simplified, annotation-driven method provided by LDLib2. The vanilla approach requires manual NBT handling and explicit sync calls, while LDLib2 automates these processes with annotations like @Persisted and @DescSynced. ```java public class ExampleBE extends BlockEntity { private int energy = 0; private String owner = ""; @Override public void saveAdditional(CompoundTag tag) { super.saveAdditional(tag); tag.putInt("Energy", energy); tag.putString("Owner", owner); } @Override public void load(CompoundTag tag) { super.load(tag); energy = tag.getInt("Energy"); owner = tag.getString("Owner"); } @Override public CompoundTag getUpdateTag() { CompoundTag tag = new CompoundTag(); saveAdditional(tag); return tag; } @Override public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) { load(pkt.getTag()); } protected void syncAndSave() { if (!level.isClientSide) { setChanged(); level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); } } public void setEnergy(int newEnergy) { if (this.energy != newEnergy) { this.energy = newEnergy; syncAndSave(); } } public void setOwner(String newOwner) { if (this.energy != newOwner) { this.energy = newOwner; syncAndSave(); } } } ``` ```java public class ExampleBE extends BlockEntity implements ISyncPersistRPCBlockEntity { @Getter private final FieldManagedStorage syncStorage = new FieldManagedStorage(this); // your fields @Persisted @DescSynced public int energy = 0; @Persisted @DescSynced public String owner = ""; } ``` -------------------------------- ### Vanilla/Forge Codec Implementation vs. LDLib2 Annotation-Driven Approach Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync Compares the traditional vanilla/Forge style of implementing Codecs with the simpler, annotation-driven approach provided by LDLib2. The LDLib2 version significantly reduces boilerplate code by using the @Persisted annotation. ```java public class MyObject implements INBTSerializable { public final static Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( ResourceLocation.CODEC.fieldOf("rl").forGetter(MyObject::getResourceLocation), Direction.CODEC.fieldOf("enum").forGetter(MyObject::getEnumValue), ItemStack.OPTIONAL_CODEC.fieldOf("item").forGetter(MyObject::getItemstack) ).apply(instance, MyObject::new)); private ResourceLocation resourceLocation = LDLib2.id("test"); private Direction enumValue = Direction.NORTH; private ItemStack itemstack = ItemStack.EMPTY; public MyObject(ResourceLocation resourceLocation, Direction enumValue, ItemStack itemstack) { this.resourceLocation = resourceLocation; this.enumValue = enumValue; this.itemstack = itemstack; } public ResourceLocation getResourceLocation() { return resourceLocation; } public Direction getEnumValue() { return enumValue; } public ItemStack getItemstack() { return itemstack; } // for INBTSerializable @Override public CompoundTag serializeNBT(HolderLookup.Provider provider) { var tag = new CompoundTag(); tag.putString("rl", resourceLocation.toString()); tag.putString("enum", enumValue.toString()); tag.put("item", ItemStack.OPTIONAL_CODEC.encodeStart(provider.createSerializationContext(NbtOps.INSTANCE), itemstack).getOrThrow()); return tag; } @Override public void deserializeNBT(HolderLookup.Provider provider, CompoundTag nbt) { resourceLocation = ResourceLocation.parse(nbt.getString("rl")); enumValue = Direction.byName(nbt.getString("enum")); itemstack = ItemStack.OPTIONAL_CODEC.parse(provider.createSerializationContext(NbtOps.INSTANCE), nbt.get("item")).getOrThrow(); } } ``` ```java public class MyObject implements IPersistedSerializable { public final static Codec CODEC = PersistedParser.createCodec(MyObject::new); @Persisted(key = "rl") private ResourceLocation resourceLocation = LDLib2.id("test"); @Persisted(key = "enum") private Direction enumValue = Direction.NORTH; @Persisted(key = "item") private ItemStack itemstack = ItemStack.EMPTY; // IPersistedSerializable is inherited from INBTSerializable you don't need to implement it manually } ``` -------------------------------- ### LDLib2 Automatic Codec Generation with PersistedParser Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync Demonstrates how LDLib2's PersistedParser can automatically generate a Codec for a class. This approach eliminates the need for manual field definition, getter mapping, and error handling, significantly reducing boilerplate code. ```java PersistedParser.createCodec(MyObject::new) ``` -------------------------------- ### Create Codec from Annotations with PersistedParser Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/PersistedParser Shows how to automatically generate a Codec for a class using PersistedParser.createCodec based on the @Persisted annotations within the class. This simplifies the creation of codecs for data classes. ```java public class MyObject implements IPersistedSerializable { public final static Codec CODEC = PersistedParser.createCodec(MyObject::new); @Persisted(key = "rl") private ResourceLocation resourceLocation = LDLib2.id("test"); @Persisted(key = "enum") private Direction enumValue = Direction.NORTH; @Persisted(key = "item") private ItemStack itemstack = ItemStack.EMPTY; } ``` ```java public class MyObject { public final static Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( ResourceLocation.CODEC.fieldOf("rl").forGetter(MyObject::getResourceLocation), Direction.CODEC.fieldOf("enum").forGetter(MyObject::getEnumValue), ItemStack.OPTIONAL_CODEC.fieldOf("item").forGetter(MyObject::getItemstack) ).apply(instance, MyObject::new)); private ResourceLocation resourceLocation = LDLib2.id("test"); private Direction enumValue = Direction.NORTH; private ItemStack itemstack = ItemStack.EMPTY; public MyObject(ResourceLocation resourceLocation, Direction enumValue, ItemStack itemstack) { this.resourceLocation = resourceLocation; this.enumValue = enumValue; this.itemstack = itemstack; } public ResourceLocation getResourceLocation() { return resourceLocation; } public Direction getEnumValue() { return enumValue; } public ItemStack getItemstack() { return itemstack; } } ``` -------------------------------- ### Register Direct Type Accessor with LDLib Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/types_support Demonstrates how to register a custom direct type accessor for types like Quaternionf and ItemStack using LDLib's AccessorRegistries. This involves providing codecs for persistence and synchronization, and optionally defining custom mark handling for change detection. ```java AccessorRegistries.registerAccessor(CustomDirectAccessor.builder(Quaternionf.class) .codec(ExtraCodecs.QUATERNIONF) .streamCodec(ByteBufCodecs.QUATERNIONF) .copyMark(Quaternionf::new) .build()); AccessorRegistries.registerAccessor(CustomDirectAccessor.builder(ItemStack.class) .codec(ItemStack.OPTIONAL_CODEC) .streamCodec(ItemStack.OPTIONAL_STREAM_CODEC) .customMark(ItemStack::copy, ItemStack::matches) .build()); ``` -------------------------------- ### Manual Chunk Rendering Update Scheduling (Java) Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations This snippet demonstrates the manual implementation equivalent to using @RequireRerender. It involves adding a listener to the 'color' field and manually calling scheduleRenderUpdate() when the field changes. The scheduleRenderUpdate() method handles notifying the level to rerender the chunk. ```java public class MyBlockEntity extends BlockEntity implements ISyncPersistRPCBlockEntity { @Persisted @DescSync private int color = -1; public MyBlockEntity(BlockPos pos, BlockState state) { super(...) ... addSyncUpdateListener("color", this::onColorUpdated); // add a listener } private Consumer onColorUpdated(ManagedKey managedKey, Object currentValue) { return newValue -> scheduleRenderUpdate(); } public void scheduleRenderUpdate() { var level = getLevel(); if (level != null) { if (level.isClientSide) { var state = getBlockState(); level.sendBlockUpdated(getBlockPos(), state, state, 1 << 3); // notify chunk rerender } } } } ``` -------------------------------- ### Serialize and Deserialize Objects with PersistedParser Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/PersistedParser Demonstrates how to serialize an object with @Persisted and @Configurable annotations into a JSON string and then deserialize it back into an object. This process uses JsonOps and a provider for handling the data. ```java @EqualsAndHashCode public class TestData implements IPersistedSerializable { @Persisted private float numberFloat = 0.0f; @Configurable private boolean booleanValue = false; @Persisted(key = "key") private String stringValue = "default"; public TestData(float initialValue) { this.numberFloat = initialValue; } } var instance = new TestData(100f); var output = PersistedParser.serialize(JsonOps.INSTANCE, instance, provider).result().get(); System.out.println(output); var newInstance = new TestData(0f); var data = PersistedParser.deserialize(JsonOps.INSTANCE, output, newInstance, provider); System.out.println(newInstance.equals(instance)); ``` -------------------------------- ### Implement Remote Procedure Calls with @RPCMethod Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @RPCMethod annotation enables sending RPC packets between server and remote clients. Methods annotated with @RPCMethod can have custom parameters, as long as they support synchronization. This is useful for broadcasting events or sending specific data across the network. The RPCSender parameter can provide information about the sender. ```java @RPCMethod public void rpcTestA(RPCSender sender, String message) { if (sender.isServer()) { LDLib2.LOGGER.info("Received RPC from server: {}", message); } else { LDLib2.LOGGER.info("Received RPC from client: {}", message); } } @RPCMethod public void rpcTestB(ItemStack item) { LDLib2.LOGGER.info("Received RPC: {}", item); } // methods to send rpc public void sendMsgToPlayer(ServerPlayer player, String msg) { rpcToServer(player, "rpcTestA", msg) } public void sendMsgToAllTrackingPlayers(ServerPlayer player, String msg) { rpcToTracking("rpcTestA", msg) } public void sendMsgToServer(ItemStack item) { rpcToServer("rpcTestB", item) } ``` ```java @RPCMethod public void rpcTest(String msg) { if (level.isClient) { // receive LDLib2.LOGGER.info("Received RPC from server: {}", message); } else { // send rpcToTracking("rpcTest", msg) } } ``` -------------------------------- ### Using @RequireRerender Annotation for Chunk Rendering Updates (Java) Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations This snippet shows how to use the @RequireRerender annotation on a persisted and synced field within a BlockEntity. When the 'color' field is updated, it automatically triggers a chunk rendering update. The BlockEntity must implement ISyncPersistRPCBlockEntity and inherit from IBlockEntityManaged. ```java public class MyBlockEntity extends BlockEntity implements ISyncPersistRPCBlockEntity { @Persisted @DescSync @RequireRerender private int color = -1; } ``` -------------------------------- ### LDLib @Persisted Annotation for NBT/JSON Persistence Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @Persisted annotation allows fields to be written to and read from BlockEntities' NBT data. The 'key()' parameter specifies the tag name in NBT, defaulting to the field name. The 'subPersisted()' option enables wrapping of non-null instances, supporting serialization via INBTSerializable or as a map. ```java @Persisted(key = "fluidAmount") int value = 100; @Persisted boolean isWater = true; ``` ```json { "fluidAmount": 100, "isWater": true } ``` ```java @Persisted(subPersisted = true) // @Persisted is also fine here, because INBTSerializable is also supported as a read-only field. private final INBTSerializable stackHandler = new ItemStackHandler(5); @Persisted(subPersisted = true) private final TestContainer testContainer = new TestContainer(); public static class TestContainer { @Persisted private Vector3f vector3fValue = new Vector3f(0, 0, 0); @Persisted private int[] intArray = new int[]{1, 2, 3}; } ``` ```json { "stackHandler": { "Size": 5, "Items": [], }, "testContainer": { "vector3fValue": [0, 0, 0], "intArray": [1, 2, 3], } } ``` -------------------------------- ### LDLib @LazyManaged Annotation for Manual Dirty Marking Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @LazyManaged annotation marks a field for lazy management, requiring manual notification of changes using 'markDirty()'. This is useful for fields that are updated infrequently or in batches. It can be combined with @DescSynced and @Persisted. ```java @DescSynced @Persisted int a; @DescSynced @Persisted @LayzManaged int b; public void setA(int value) { this.a = value; // will be sync/persist automatically, in general } public void setB(int value) { this.b = value; markDirty("b"); // mannually notify chagned } ``` -------------------------------- ### Skipping Persisted Values with @SkipPersistedValue Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @SkipPersistedValue annotation provides control over which fields annotated with @Persisted are serialized during persistence. It references a method that returns a boolean, allowing developers to skip serialization for specific values, such as initial or unchanged values, to reduce output size. ```java @Persisted int intField = 10; @SkipPersistedValue(field = "intField") public boolean skipIntFieldPersisted(int value) { // 10 is the initial value of this class, there is no need to store it. return value == 10; } ``` -------------------------------- ### Remote Update Listener with @UpdateListener Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @UpdateListener annotation marks a method to be called on the remote side when a field is updated from the server. It takes the old and new values of the field as parameters. This is useful for reacting to synchronized data changes. ```java @DescSynced @UpdateListener(methodName = "onIntValueChanged") private int intValue = 10; private void onIntValueChanged(int oldValue, int newValue) { LDLib2.LOGGER.info("Int value changed from {} to {}", oldValue, newValue); } ``` -------------------------------- ### Conditional Synchronization with @ConditionalSynced Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @ConditionalSynced annotation allows for fine-grained control over whether a field annotated with @DescSynced should be synchronized. It specifies a method that returns a boolean to determine if the sync should occur. This enables conditional syncing based on field values or other logic. ```java @Configurable @ConditionalSynced(methodName = "shouldSync") int intField = 10; public boolean shouldSync(int value) { return value > 0; } ``` -------------------------------- ### LDLib @DescSynced Annotation Usage Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @DescSynced annotation indicates that a field's value should be synchronized from the server side to the client side (specifically, 'remote'). It can be applied to fields of various types, including primitive integers and custom objects like ItemStack. ```java @DescSynced int a; @DescSynced private ItemStack b = ItemStack.EMPTY; @DescSynced private List c = new ArrayList<>(); ``` -------------------------------- ### Configure Read-Only Managed Fields with @ReadOnlyManaged Source: https://low-drag-mc.github.io/LowDragMC-Doc/ldlib/2/sync/annotations The @ReadOnlyManaged annotation marks fields as read-only and managed by the user. It's crucial for types that are non-null and immutable, requiring custom serialization and deserialization methods. This annotation allows for defining how to serialize an object to a unique ID and deserialize it back, ensuring proper data synchronization. ```java boolean methodName(); Tag methodName(@Nonnull T obj); T methodName(@Nonnull Tag tag) ``` ```java @Persisted @DescSync @ReadOnlyManaged(serializeMethod = "testGroupSerialize", deserializeMethod = "testGroupDeserialize") private final List groupList = new ArrayList<>(); public static class TestGroup implements IPersistedSerializable { @Persisted private Range rangeValue = Range.of(0, 1); @Persisted private Direction enumValue = Direction.NORTH; @Persisted private Vector3i vector3iValue = new Vector3i(0, 0, 0); } public IntTag testGroupSerialize(List groups) { return IntTag.valueOf(groups.size()); } public List testGroupDeserialize(IntTag tag) { var groups = new ArrayList(); for (int i = 0; i < tag.getAsInt(); i++) { groups.add(new TestGroup()); } return groups; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.