Skip to content

Commit

Permalink
Add (experimental) support for parsing polymer items in BlockEntity (…
Browse files Browse the repository at this point in the history
…and similar) nbt of other mods. Fix EntityElement not syncing armor updates
  • Loading branch information
Patbox committed Aug 10, 2023
1 parent 576089d commit f715dbf
Show file tree
Hide file tree
Showing 20 changed files with 229 additions and 28 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fabric_version=0.83.0+1.20.1

maven_group = eu.pb4

mod_version = 0.5.7
mod_version = 0.5.8

minecraft_version_supported = ">=1.20-"

Expand Down
1 change: 1 addition & 0 deletions polymer-common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies {
modApi include("xyz.nucleoid:packet-tweaker:${packet_tweaker_version}") {
transitive(false)
}
modApi(include(annotationProcessor("com.llamalad7.mixinextras:mixinextras-fabric:0.2.0-beta.9")))
modCompileOnly "xyz.nucleoid:disguiselib-fabric:1.2.2"
modCompileOnly "org.geysermc.floodgate:api:2.2.0-SNAPSHOT"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import java.util.WeakHashMap;

public final class PolymerCommonUtils {
private PolymerCommonUtils(){}
Expand Down Expand Up @@ -134,19 +136,29 @@ public static World getFakeWorld() {
*/
@Nullable
public static ServerPlayerEntity getPlayerContext() {
ServerPlayerEntity player = getPlayerContextNoClient();
if (player == null && CommonImpl.IS_CLIENT) {
player = ClientUtils.getPlayer();
}

return player;
}

@Nullable
public static ServerPlayerEntity getPlayerContextNoClient() {
ServerPlayerEntity player = PacketContext.get().getTarget();

if (player == null) {
player = CommonImplUtils.getPlayer();

if (player == null && CommonImpl.IS_CLIENT) {
player = ClientUtils.getPlayer();
}
}

return player;
}

public static boolean isNetworkingThread() {
return Thread.currentThread().getName().startsWith("Netty");
}

public static boolean isBedrockPlayer(ServerPlayerEntity player) {
if (CompatStatus.FLOODGATE) {
return FloodGateUtils.isPlayerBroken(player);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@

import java.util.*;


/**
* An server side item group that can be synchronized with polymer clients
* It also has it's own server side functionality
*/
public final class PolymerItemGroupUtils {
public static final PolymerRegistry<ItemGroup> REGISTRY = InternalServerRegistry.ITEM_GROUPS;
private PolymerItemGroupUtils() {}
/**
* Even called on synchronization of ItemGroups
*/
public static final SimpleEvent<ItemGroupEventListener> LIST_EVENT = new SimpleEvent<>();

private PolymerItemGroupUtils() {
}

public static Contents getContentsFor(ServerPlayerEntity player, ItemGroup group) {
return getContentsFor(group, player.getServer().getRegistryManager(), player.getServerWorld().getEnabledFeatures(), CommonImplUtils.permissionCheck(player, "op_items", 2));
}
Expand All @@ -51,14 +54,22 @@ public static List<ItemGroup> getItemGroups(ServerPlayerEntity player) {
var list = new LinkedHashSet<ItemGroup>();

for (var g : ItemGroups.getGroups()) {
if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) {
list.add(g);
try {
if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) {
list.add(g);
}
} catch (Throwable e) {
e.printStackTrace();
}
}

for (var g : InternalServerRegistry.ITEM_GROUPS) {
if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) {
list.add(g);
try {
if (g.getType() == ItemGroup.Type.CATEGORY && ((ItemGroupExtra) g).polymer$isSyncable()) {
list.add(g);
}
} catch (Throwable e) {
e.printStackTrace();
}
}

Expand Down Expand Up @@ -106,8 +117,10 @@ public interface ItemGroupEventListener {

public interface ItemGroupListBuilder {
void add(ItemGroup group);

void remove(ItemGroup group);
}

public record Contents(Collection<ItemStack> main, Collection<ItemStack> search) {}
public record Contents(Collection<ItemStack> main, Collection<ItemStack> search) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.item.*;
import net.minecraft.item.trim.ArmorTrim;
import net.minecraft.item.trim.ArmorTrimMaterial;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
Expand Down Expand Up @@ -87,7 +86,7 @@ public static ItemStack getPolymerItemStack(ItemStack itemStack, TooltipContext
return itemStack;
} else if (itemStack.getItem() instanceof PolymerItem item) {
return item.getPolymerItemStack(itemStack, tooltipContext, player);
} else if (shouldPolymerConvert(itemStack, player)) {
} else if (isPolymerServerItem(itemStack, player)) {
return createItemStack(itemStack, tooltipContext, player);
}

Expand Down Expand Up @@ -180,10 +179,10 @@ public static NbtCompound getPolymerNbt(ItemStack itemStack) {
}

public static boolean isPolymerServerItem(ItemStack itemStack) {
return shouldPolymerConvert(itemStack, null);
return isPolymerServerItem(itemStack, PolymerUtils.getPlayerContext());
}

private static boolean shouldPolymerConvert(ItemStack itemStack, @Nullable ServerPlayerEntity player) {
public static boolean isPolymerServerItem(ItemStack itemStack, @Nullable ServerPlayerEntity player) {
if (getPolymerIdentifier(itemStack) != null) {
return false;
}
Expand Down Expand Up @@ -232,15 +231,15 @@ private static boolean shouldPolymerConvert(ItemStack itemStack, @Nullable Serve

if (itemStack.getNbt().contains("ChargedProjectiles", NbtElement.LIST_TYPE)) {
for (var itemNbt : itemStack.getNbt().getList("ChargedProjectiles", NbtElement.COMPOUND_TYPE)) {
if (shouldPolymerConvert(ItemStack.fromNbt((NbtCompound) itemNbt), player)) {
if (isPolymerServerItem(ItemStack.fromNbt((NbtCompound) itemNbt), player)) {
return true;
}
}
}

if (itemStack.getNbt().contains("Items", NbtElement.LIST_TYPE)) {
for (var itemNbt : itemStack.getNbt().getList("Items", NbtElement.COMPOUND_TYPE)) {
if (shouldPolymerConvert(ItemStack.fromNbt((NbtCompound) itemNbt), player)) {
if (isPolymerServerItem(ItemStack.fromNbt((NbtCompound) itemNbt), player)) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public final class PolymerImpl {
public static final boolean LOG_MORE_ERRORS;
public static final int LIGHT_UPDATE_TICK_DELAY;
public static final boolean FORCE_STRICT_UPDATES;
public static final boolean ITEMSTACK_NBT_HACK;

static {
var serverConfig = CommonImpl.loadConfig("server", ServerConfig.class);
Expand All @@ -39,6 +40,7 @@ public final class PolymerImpl {
SYNC_MODDED_ENTRIES_POLYMC = serverConfig.polyMcSyncModdedEntries && CompatStatus.POLYMC;
LIGHT_UPDATE_TICK_DELAY = serverConfig.lightUpdateTickDelay;
FORCE_STRICT_UPDATES = serverConfig.forceStrictUpdates;
ITEMSTACK_NBT_HACK = serverConfig.itemStackNbtHack;


if (PolymerImpl.IS_CLIENT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ public class ServerConfig {
public String _c3 = "Forcefully enables strict block updates, making client desyncs less likely to happen";
@SerializedName("force_strict_block_updates")
public boolean forceStrictUpdates = false;
public String _c4 = "Enables experimental passing of ItemStack context through nbt, allowing for better mod compat";
@SerializedName("item_stack_nbt_hack")
public boolean itemStackNbtHack = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package eu.pb4.polymer.core.impl.interfaces;

import net.minecraft.item.ItemStack;

public interface ItemStackAwareNbtCompound {
default void polymerCore$setItemStack(ItemStack stack) {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;

@Mixin(targets = "net/minecraft/world/chunk/PalettedContainer$Data")
public class PalettedContainerDataMixin<T> {
@Shadow @Final private Palette<T> palette;

@Redirect(method = "writePacket", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/PaletteStorage;getData()[J"))
private long[] polymer$replaceData(PaletteStorage instance) {
if (this.palette instanceof IdListPalette<T> && this.palette.get(0) instanceof BlockState) {
@Shadow @Final private PaletteStorage storage;

@ModifyArg(method = "writePacket", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketByteBuf;writeLongArray([J)Lnet/minecraft/network/PacketByteBuf;"), require = 0)
private long[] polymer$replaceData(long[] initialReturn) {
if (this.palette instanceof IdListPalette<T> && this.palette.get(0) instanceof BlockState) {
var palette = (IdListPalette<BlockState>) this.palette;
var player = PolymerUtils.getPlayerContext();
if (player == null) {
return instance.getData();
return initialReturn;
}
int bits;

Expand All @@ -38,7 +41,7 @@ public class PalettedContainerDataMixin<T> {
} else {
bits = playerBitCount.intValue();
}

final var instance = new PackedIntegerArray(this.storage.getElementBits(), this.storage.getSize(), initialReturn);
final int size = instance.getSize();
var data = new PackedIntegerArray(bits, size);

Expand All @@ -49,6 +52,6 @@ public class PalettedContainerDataMixin<T> {
return data.getData();
}

return instance.getData();
return initialReturn;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public abstract class ItemGroupMixin implements ClientItemGroupExtension {

@Shadow public abstract void reloadSearchProvider();

@Unique private List<ItemStack> polymer$itemsGroup = new ArrayList<>();
@Unique private List<ItemStack> polymer$itemsSearch = new ArrayList<>();
@Unique private final List<ItemStack> polymer$itemsGroup = new ArrayList<>();
@Unique private final List<ItemStack> polymer$itemsSearch = new ArrayList<>();
@Unique
private int polymerCore$page;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

@Mixin(CommandManager.class)
public abstract class CommandManagerMixin {
@Inject(method = "argument", at = @At("TAIL"), cancellable = true)
@Inject(method = "argument", at = @At("TAIL"))
private static void polymer$handleSuggestions(String name, ArgumentType<?> type, CallbackInfoReturnable<RequiredArgumentBuilder<ServerCommandSource, ?>> cir) {
if (type instanceof ItemStackArgumentType || type instanceof BlockStateArgumentType || type instanceof RegistryEntryArgumentType<?>) {
cir.getReturnValue().suggests(type::listSuggestions);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
package eu.pb4.polymer.core.mixin.item;

import eu.pb4.polymer.core.api.item.PolymerItemUtils;
import eu.pb4.polymer.core.api.utils.PolymerUtils;
import eu.pb4.polymer.core.impl.PolymerImpl;
import eu.pb4.polymer.core.impl.PolymerImplUtils;
import eu.pb4.polymer.core.impl.interfaces.ItemStackAwareNbtCompound;
import eu.pb4.polymer.core.impl.other.PolymerTooltipContext;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.Registries;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import java.util.List;

@Mixin(ItemStack.class)
public class ItemStackMixin {
@Inject(method = "writeNbt", at = @At("TAIL"))
private void polymerCore$magicPerPlayerNbt(NbtCompound nbt, CallbackInfoReturnable<NbtCompound> cir) {
if (PolymerImpl.ITEMSTACK_NBT_HACK) {
var self = (ItemStack) (Object) this;
var player = PolymerUtils.getPlayerContext();
if (PolymerItemUtils.isPolymerServerItem(self, player)) {
((ItemStackAwareNbtCompound) nbt).polymerCore$setItemStack(self.copy());
}
}
}


@Inject(method = "getTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/item/TooltipContext;isAdvanced()Z", ordinal = 2), cancellable = true, locals = LocalCapture.CAPTURE_FAILSOFT)
private void polymer$quitEarly(@Nullable PlayerEntity player, TooltipContext context, CallbackInfoReturnable<List<Text>> cir, List<Text> list) {
if (context instanceof PolymerTooltipContext) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package eu.pb4.polymer.core.mixin.item;

import eu.pb4.polymer.core.impl.interfaces.ItemStackAwareNbtCompound;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Inject;

@Mixin(NbtCompound.class)
public class NbtCompoundFallbackMixin implements ItemStackAwareNbtCompound {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package eu.pb4.polymer.core.mixin.item;

import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.Share;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import eu.pb4.polymer.common.api.PolymerCommonUtils;
import eu.pb4.polymer.core.api.item.PolymerItemUtils;
import eu.pb4.polymer.core.impl.PolymerImpl;
import eu.pb4.polymer.core.impl.interfaces.ItemStackAwareNbtCompound;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtString;
import net.minecraft.registry.Registries;
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.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.io.DataOutput;

@SuppressWarnings("InvalidInjectorMethodSignature")
@Mixin(NbtCompound.class)
public class NbtCompoundMixin implements ItemStackAwareNbtCompound {
@Unique
private ItemStack polymerCore$stack;

@Override
public void polymerCore$setItemStack(ItemStack stack) {
this.polymerCore$stack = stack;
}


@Inject(method = "write(Ljava/io/DataOutput;)V", at = @At("HEAD"))
private void polymerCore$storePlayerContextedItemStack(DataOutput output, CallbackInfo ci, @Share("polymerCore:stack") LocalRef<ItemStack> polymerStack) {
if (this.polymerCore$stack != null && PolymerCommonUtils.isNetworkingThread()) {
var player = PolymerCommonUtils.getPlayerContextNoClient();
if (player != null) {
polymerStack.set(PolymerItemUtils.getPolymerItemStack(this.polymerCore$stack, player));
}
}
}

@ModifyArg(method = "write(Ljava/io/DataOutput;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NbtCompound;write(Ljava/lang/String;Lnet/minecraft/nbt/NbtElement;Ljava/io/DataOutput;)V"))
private NbtElement polymerCore$swapNbt(NbtElement nbtElement, @Local(ordinal = 0) String key, @Share("polymerCore:stack") LocalRef<ItemStack> polymerStack) {
if (PolymerImpl.ITEMSTACK_NBT_HACK && this.polymerCore$stack != null && PolymerCommonUtils.isNetworkingThread()) {
if (key.equals("id") && nbtElement.getType() == NbtElement.STRING_TYPE) {
var stack = polymerStack.get();
return stack != null ? NbtString.of(Registries.ITEM.getId(stack.getItem()).toString()) : nbtElement;
} else if (key.equals("tag") && nbtElement.getType() == NbtElement.COMPOUND_TYPE) {
var stack = polymerStack.get();
return stack != null ? stack.getOrCreateNbt() : nbtElement;
}
}

return nbtElement;
}

@Inject(method = "copy()Lnet/minecraft/nbt/NbtCompound;", at = @At("RETURN"))
private void polymerCore$copyStack(CallbackInfoReturnable<NbtCompound> cir) {
((ItemStackAwareNbtCompound) cir.getReturnValue()).polymerCore$setItemStack(this.polymerCore$stack);
}
}
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 @@ -55,6 +55,8 @@
"item.ItemStackContentMixin",
"item.ItemStackMixin",
"item.MiningToolItemAccessor",
"item.NbtCompoundFallbackMixin",
"item.NbtCompoundMixin",
"item.ServerPlayNetworkHandlerMixin",
"item.StonecutterScreenHandlerMixin",
"item.packet.PacketByteBufMixin",
Expand Down
Loading

0 comments on commit f715dbf

Please sign in to comment.