Skip to content

Commit

Permalink
Add support for force-syncing default item component values
Browse files Browse the repository at this point in the history
  • Loading branch information
Patbox committed Dec 13, 2024
1 parent af4e41c commit 16ee7fb
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
import eu.pb4.polymer.core.api.item.PolymerItem;
import eu.pb4.polymer.core.impl.interfaces.EntityAttachedPacket;
import eu.pb4.polymer.core.impl.networking.PolymerServerProtocol;
import eu.pb4.polymer.core.mixin.block.packet.ServerChunkLoadingManagerAccessor;
import eu.pb4.polymer.core.mixin.entity.EntityAccessor;
import eu.pb4.polymer.core.mixin.entity.EntityTrackerAccessor;
import eu.pb4.polymer.core.mixin.entity.PlayerListS2CPacketAccessor;
import eu.pb4.polymer.rsm.api.RegistrySyncUtils;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
Expand Down Expand Up @@ -165,6 +167,28 @@ public static void sendEntityType(ServerPlayerEntity player, int entityId, Entit
PolymerServerProtocol.sendEntityInfo(player.networkHandler, entityId, entityType);
}

public static void refreshEntity(ServerPlayerEntity player, Entity entity) {
if (entity.getWorld() instanceof ServerWorld world) {
var tracker = ((ServerChunkLoadingManagerAccessor) world.getChunkManager().chunkLoadingManager).polymer$getEntityTrackers().get(entity.getId());
if (tracker != null) {
tracker.stopTracking(player);
tracker.updateTrackedStatus(player);
}
}
}

public static void refreshEntity(Entity entity) {
if (entity.getWorld() instanceof ServerWorld world) {
var tracker = ((ServerChunkLoadingManagerAccessor) world.getChunkManager().chunkLoadingManager).polymer$getEntityTrackers().get(entity.getId());
if (tracker != null) {
for (var player : ((EntityTrackerAccessor) tracker).getListeners()) {
((EntityTrackerAccessor) tracker).getEntry().stopTracking(player.getPlayer());
((EntityTrackerAccessor) tracker).getEntry().startTracking(player.getPlayer());
}
}
}
}

public static boolean isPolymerEntityInteraction(ServerPlayerEntity player, Hand hand, ItemStack stack, ServerWorld world, Entity entity, ActionResult actionResult) {
if (entity instanceof PolymerEntity polymerEntity && polymerEntity.isPolymerEntityInteraction(player, hand, stack, world, actionResult)) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import eu.pb4.polymer.core.impl.PolymerImpl;
import eu.pb4.polymer.core.impl.TransformingComponent;
import eu.pb4.polymer.core.impl.compat.polymc.PolyMcUtils;
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
import net.minecraft.component.ComponentType;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.EnchantmentEffectComponentTypes;
Expand All @@ -36,6 +37,7 @@
import net.minecraft.text.Text;
import net.minecraft.util.*;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
import xyz.nucleoid.packettweaker.PacketContext;

import java.util.*;
Expand Down Expand Up @@ -77,6 +79,9 @@ public final class PolymerItemUtils {

public static final BooleanEvent<PolymerItemInteractionListener> POLYMER_ITEM_INTERACTION_CHECK = new BooleanEvent<>();

private static final IdentityHashMap<Item, List<ComponentType<?>>> FORCE_SYNCED_COMPONENTS = new IdentityHashMap<>();


private static final ComponentType<?>[] COMPONENTS_TO_COPY = {DataComponentTypes.CAN_BREAK, DataComponentTypes.CAN_PLACE_ON,
DataComponentTypes.BLOCK_ENTITY_DATA, DataComponentTypes.TRIM,
DataComponentTypes.TOOL,
Expand Down Expand Up @@ -128,6 +133,8 @@ public final class PolymerItemUtils {
HideableTooltip.of(DataComponentTypes.DYED_COLOR, DyedColorComponent::withShowInTooltip)
);



private PolymerItemUtils() {
}

Expand Down Expand Up @@ -530,6 +537,28 @@ public static boolean isPolymerItemInteraction(ServerPlayerEntity player, ItemSt
return POLYMER_ITEM_INTERACTION_CHECK.invoke((x) -> x.isPolymerItemInteraction(player, hand, stack, world, actionResult));
}

/**
* This method allows to define Data Component Types, which need to be always synced to clients,
* even if they have the default value for sent ItemStack.
* This can be used with combination with Fabric's DefaultItemComponentEvents to synchronize modified components values to clients without the mod.
*
* @param item item this effect should apply to
* @param types Component types that need to be always synced to client
*/
public static void syncDefaultComponent(Item item, ComponentType<?>... types) {
var list = FORCE_SYNCED_COMPONENTS.computeIfAbsent(item, (i) -> new ReferenceArrayList<>());
for (var type : types) {
if (!list.contains(type)) {
list.add(type);
}
}
}

@UnmodifiableView
public static List<ComponentType<?>> getSyncedDefaultComponents(Item item) {
return FORCE_SYNCED_COMPONENTS.getOrDefault(item, List.of());
}

@FunctionalInterface
public interface ItemModificationEventHandler {
ItemStack modifyItem(ItemStack original, ItemStack client, PacketContext context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package eu.pb4.polymer.core.mixin.entity;

import net.minecraft.server.network.EntityTrackerEntry;
import net.minecraft.server.network.PlayerAssociatedNetworkHandler;
import net.minecraft.server.world.ServerChunkLoadingManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

import java.util.Set;

@Mixin(ServerChunkLoadingManager.EntityTracker.class)
public interface EntityTrackerAccessor {
@Accessor
Set<PlayerAssociatedNetworkHandler> getListeners();

@Accessor
EntityTrackerEntry getEntry();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package eu.pb4.polymer.core.mixin.item;

import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import net.minecraft.component.ComponentChanges;
import net.minecraft.component.ComponentType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;

import java.util.Optional;

@Mixin(ComponentChanges.class)
public interface ComponentChangesAccessor {
@Invoker("<init>")
static ComponentChanges createComponentChanges(Reference2ObjectMap<ComponentType<?>, Optional<?>> changedComponents) {
throw new UnsupportedOperationException();
}

@Accessor
Reference2ObjectMap<ComponentType<?>, Optional<?>> getChangedComponents();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
import com.llamalad7.mixinextras.sugar.Local;
import eu.pb4.polymer.common.api.PolymerCommonUtils;
import eu.pb4.polymer.core.api.item.PolymerItemUtils;
import eu.pb4.polymer.core.api.utils.PolymerUtils;
import eu.pb4.polymer.core.mixin.item.ComponentChangesAccessor;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import net.minecraft.component.ComponentType;
import net.minecraft.item.ItemStack;
import net.minecraft.network.RegistryByteBuf;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import xyz.nucleoid.packettweaker.PacketContext;

import java.util.Optional;

@Mixin(targets = "net/minecraft/item/ItemStack$1", priority = 500)
public abstract class ItemStackPacketCodecMixin {

Expand All @@ -21,6 +25,25 @@ public abstract class ItemStackPacketCodecMixin {
var player = PacketContext.get();
return PolymerItemUtils.getPolymerItemStack(itemStack, player);
}

@ModifyArg(method = "encode(Lnet/minecraft/network/RegistryByteBuf;Lnet/minecraft/item/ItemStack;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/network/codec/PacketCodec;encode(Ljava/lang/Object;Ljava/lang/Object;)V", ordinal = 1), index = 1)
private Object polymer$addSyncedDefaults(Object object, @Local(argsOnly = true) ItemStack stack) {
var changedDefaults = PolymerItemUtils.getSyncedDefaultComponents(stack.getItem());
if (changedDefaults.isEmpty()) {
return object;
}
var original = ((ComponentChangesAccessor) object).getChangedComponents();
var changes = new Reference2ObjectOpenHashMap<ComponentType<?>, Optional<?>>(changedDefaults.size() + original.size());
changes.putAll(original);
for (var type : changedDefaults) {
if (!changes.containsKey(type)) {
changes.put(type, Optional.ofNullable(stack.getItem().getComponents().get(type)));
}
}

return ComponentChangesAccessor.createComponentChanges(changes);
}
@ModifyReturnValue(method = "decode(Lnet/minecraft/network/RegistryByteBuf;)Lnet/minecraft/item/ItemStack;", at = @At(value = "RETURN", ordinal = 1))
private ItemStack polymerCore$decodeItemStackServer(ItemStack stack, @Local(argsOnly = true) RegistryByteBuf buf) {
return PolymerCommonUtils.isServerNetworkingThread() ? PolymerItemUtils.getRealItemStack(stack, buf.getRegistryManager()) : stack;
Expand Down
2 changes: 2 additions & 0 deletions polymer-core/src/main/resources/polymer-core.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"entity.EntityAttributesS2CPacketMixin",
"entity.EntitySpawnS2CPacketMixin",
"entity.EntityStatusEffectS2CPacketMixin",
"entity.EntityTrackerAccessor",
"entity.EntityTrackerEntryMixin",
"entity.EntityTrackerMixin",
"entity.EntityTrackerUpdateS2CPacketMixin",
Expand All @@ -53,6 +54,7 @@
"entity.PlayerListS2CPacketAccessor",
"entity.RemoveEntityStatusEffectS2CPacketMixin",
"entity.ServerWorldAccessor",
"item.ComponentChangesAccessor",
"item.ItemGroupMixin",
"item.ItemGroupsMixin",
"item.ItemStackMixin",
Expand Down
15 changes: 15 additions & 0 deletions polymer-core/src/testmod/java/eu/pb4/polymertest/TestMod.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package eu.pb4.polymertest;

import com.mojang.serialization.Codec;
import eu.pb4.polymer.common.api.PolymerCommonUtils;
import eu.pb4.polymer.core.api.block.BlockMapper;
import eu.pb4.polymer.core.api.block.SimplePolymerBlock;
import eu.pb4.polymer.core.api.entity.PolymerEntityUtils;
import eu.pb4.polymer.core.api.item.*;
import eu.pb4.polymer.core.api.other.PolymerComponent;
import eu.pb4.polymer.core.api.other.PolymerStat;
import eu.pb4.polymer.core.api.other.SimplePolymerPotion;
import eu.pb4.polymer.core.api.utils.PolymerSyncUtils;
Expand All @@ -22,11 +24,14 @@
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.item.v1.DefaultItemComponentEvents;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.fabricmc.fabric.api.object.builder.v1.trade.TradeOfferHelper;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.*;
import net.minecraft.component.ComponentType;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.AttributeModifierSlot;
import net.minecraft.component.type.AttributeModifiersComponent;
import net.minecraft.component.type.ConsumableComponent;
Expand Down Expand Up @@ -192,6 +197,8 @@ public ActionResult useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity
public static final Item TEST_ENTITY_EGG = registerItem(Identifier.of("test", "spawn_egg"), (s) -> new PolymerSpawnEggItem(ENTITY, Items.COW_SPAWN_EGG, s));
public static Item TEST_FOOD;
public static final Item TEST_FOOD_2 = registerItem(Identifier.of("test", "food2"), (s) -> new SimplePolymerItem(s.food(new FoodComponent.Builder().nutrition(1).saturationModifier(2).build()), Items.CAKE));
public static final ComponentType<String> TEST = register(Registries.DATA_COMPONENT_TYPE, Identifier.of("test", "test"),
ComponentType.<String>builder().codec(Codec.STRING).build());

//public static final SoundEvent GHOST_HURT = new PolymerSoundEvent(PolymerResourcePackUtils.getMainUuid(), Identifier.of("polymertest", "ghosthurt"), 16, true, SoundEvents.ENTITY_GHAST_HURT);

Expand Down Expand Up @@ -498,6 +505,14 @@ public void onInitialize() {
local.set(Boolean.TRUE);
long localTime = System.currentTimeMillis();

DefaultItemComponentEvents.MODIFY.register(x -> x.modify(Items.DIAMOND, b -> b.add(DataComponentTypes.MAX_STACK_SIZE, 99)));
DefaultItemComponentEvents.MODIFY.register(x -> x.modify(Items.CHAINMAIL_HELMET, b -> b.add(DataComponentTypes.EQUIPPABLE, null)));
DefaultItemComponentEvents.MODIFY.register(x -> x.modify(Items.CHAINMAIL_CHESTPLATE, b -> b.add(DataComponentTypes.EQUIPPABLE, null)));
PolymerItemUtils.syncDefaultComponent(Items.DIAMOND, DataComponentTypes.MAX_STACK_SIZE);
PolymerItemUtils.syncDefaultComponent(Items.CHAINMAIL_HELMET, DataComponentTypes.EQUIPPABLE);

PolymerComponent.registerDataComponent(TEST);

if (PolymerImpl.IS_CLIENT) {
InternalClientRegistry.decodeState(-1);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package eu.pb4.polymer.virtualentity.mixin.accessors;

import net.minecraft.server.network.EntityTrackerEntry;
import net.minecraft.server.network.PlayerAssociatedNetworkHandler;
import net.minecraft.server.world.ServerChunkLoadingManager;
import org.spongepowered.asm.mixin.Mixin;
Expand All @@ -11,4 +12,7 @@
public interface EntityTrackerAccessor {
@Accessor
Set<PlayerAssociatedNetworkHandler> getListeners();

@Accessor
EntityTrackerEntry getEntry();
}

0 comments on commit 16ee7fb

Please sign in to comment.