Skip to content

Commit

Permalink
fix(spigot): advancements handler initialization on 1.20.2 (#330)
Browse files Browse the repository at this point in the history
Backport of 5878840 to v3
  • Loading branch information
diogotcorreia committed Oct 16, 2023
1 parent e55e079 commit 65e0c32
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class ProtocolLibListener implements PacketListener, PacketInterceptor {
private final String MERCHANT_RECIPE_DEMAND_FIELD;

private final SignPacketHandler signPacketHandler = new SignPacketHandler();
private final AdvancementsPacketHandler advancementsPacketHandler;
private final AdvancementsPacketHandler advancementsPacketHandler = AdvancementsPacketHandler.newInstance();
private final EntitiesPacketHandler entitiesPacketHandler = new EntitiesPacketHandler();

private final SpigotMLP main;
Expand Down Expand Up @@ -119,8 +119,6 @@ public ProtocolLibListener(SpigotMLP main, HandlerFunction.HandlerType... allowe
PLAYER_ACTIVE_CONTAINER_FIELD = Arrays.stream(MinecraftReflection.getEntityHumanClass().getDeclaredFields())
.filter(field -> field.getType() == containerClass && !field.getName().equals("defaultContainer")).findAny().orElse(null);

this.advancementsPacketHandler = getMCVersion() >= 12 ? new AdvancementsPacketHandler() : null;

setupPacketHandlers();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,28 @@
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.rexcantor64.triton.Triton;
import com.rexcantor64.triton.api.language.Localized;
import com.rexcantor64.triton.player.SpigotLanguagePlayer;
import com.rexcantor64.triton.utils.NMSUtils;
import com.rexcantor64.triton.wrappers.WrappedAdvancementDisplay;
import com.rexcantor64.triton.wrappers.WrappedAdvancementHolder;
import lombok.val;
import net.md_5.bungee.chat.ComponentSerializer;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Map;
import java.util.Optional;

import static com.rexcantor64.triton.packetinterceptor.protocollib.HandlerFunction.asAsync;

public class AdvancementsPacketHandler extends PacketHandler {

private final Class<?> SERIALIZED_ADVANCEMENT_CLASS;
private final FieldAccessor ADVANCEMENT_DISPLAY_FIELD;
private final FieldAccessor ENTITY_PLAYER_ADVANCEMENT_DATA_PLAYER_FIELD;
Expand All @@ -31,9 +36,21 @@ public class AdvancementsPacketHandler extends PacketHandler {
private final MethodAccessor MINECRAFT_SERVER_GET_ADVANCEMENT_DATA_METHOD;
private final MethodAccessor ADVANCEMENT_DATA_PLAYER_LOAD_FROM_ADVANCEMENT_DATA_WORLD_METHOD;

public AdvancementsPacketHandler() {
SERIALIZED_ADVANCEMENT_CLASS = MinecraftReflection.getMinecraftClass("advancements.Advancement$SerializedAdvancement", "Advancement$SerializedAdvancement");
ADVANCEMENT_DISPLAY_FIELD = Accessors.getFieldAccessor(SERIALIZED_ADVANCEMENT_CLASS, WrappedAdvancementDisplay.getWrappedClass(), true);
private AdvancementsPacketHandler() {
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
SERIALIZED_ADVANCEMENT_CLASS = MinecraftReflection.getMinecraftClass(
"advancements.Advancement$SerializedAdvancement",
"Advancement$SerializedAdvancement"
);
ADVANCEMENT_DISPLAY_FIELD = Accessors.getFieldAccessor(
SERIALIZED_ADVANCEMENT_CLASS,
WrappedAdvancementDisplay.getWrappedClass(),
true
);
} else {
SERIALIZED_ADVANCEMENT_CLASS = null;
ADVANCEMENT_DISPLAY_FIELD = null;
}
val advancementDataPlayerClass = MinecraftReflection.getMinecraftClass("server.AdvancementDataPlayer", "AdvancementDataPlayer");
ENTITY_PLAYER_ADVANCEMENT_DATA_PLAYER_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityPlayerClass(), advancementDataPlayerClass, true);
ADVANCEMENT_DATA_PLAYER_REFRESH_METHOD = Accessors.getMethodAccessor(advancementDataPlayerClass, "b", MinecraftReflection.getEntityPlayerClass());
Expand All @@ -55,6 +72,25 @@ public AdvancementsPacketHandler() {
}
}

/**
* Build a new instance of {@link AdvancementsPacketHandler} if supported by the current
* Minecraft version (1.12 and above).
*
* @return A new instance of this class.
* @since 4.0.0
*/
public static @Nullable AdvancementsPacketHandler newInstance() {
try {
if (MinecraftVersion.COLOR_UPDATE.atOrAbove()) {
// MC 1.12+
return new AdvancementsPacketHandler();
}
} catch (Exception e) {
Triton.get().getLogger().logError(e, "Failed to hook into advancements packets. Advancements translation will not work.");
}
return null;
}

/**
* @return Whether the plugin should attempt to translate advancements
*/
Expand All @@ -69,7 +105,7 @@ private boolean areAdvancementsRefreshDisabled() {
return areAdvancementsDisabled() || !getMain().getConfig().isAdvancementsRefresh();
}

private void handleAdvancements(PacketEvent packet, SpigotLanguagePlayer languagePlayer) {
private void handleAdvancementsPre1_20_2(PacketEvent packet, SpigotLanguagePlayer languagePlayer) {
if (areAdvancementsDisabled()) return;

val serializedAdvancementMap = packet.getPacket().getMaps(MinecraftKey.getConverter(), Converters.passthrough(SERIALIZED_ADVANCEMENT_CLASS)).readSafely(0);
Expand All @@ -79,18 +115,44 @@ private void handleAdvancements(PacketEvent packet, SpigotLanguagePlayer languag
if (advancementDisplayHandle == null) continue;

val advancementDisplay = WrappedAdvancementDisplay.fromHandle(advancementDisplayHandle).shallowClone();
translateAdvancementDisplay(advancementDisplay, languagePlayer);
if (advancementDisplay.hasChangedAndReset()) {
ADVANCEMENT_DISPLAY_FIELD.set(serializedAdvancement, advancementDisplay.getHandle());
}
}
}

val originalTitle = ComponentSerializer.parse(advancementDisplay.getTitle().getJson());
val originalDescription = ComponentSerializer.parse(advancementDisplay.getDescription().getJson());
private void handleAdvancementsPost1_20_2(PacketEvent packet, SpigotLanguagePlayer languagePlayer) {
if (areAdvancementsDisabled()) return;

val translatedTitle = getLanguageParser().parseComponent(languagePlayer, getMain().getConf().getAdvancementsSyntax(), originalTitle);
val translatedDescription = getLanguageParser().parseComponent(languagePlayer, getMain().getConf().getAdvancementsSyntax(), originalDescription);
val advancementHolders = packet.getPacket().getLists(WrappedAdvancementHolder.CONVERTER).readSafely(0);

for (WrappedAdvancementHolder advancementHolder : advancementHolders) {
val advancement = advancementHolder.getAdvancement();
val advancementDisplayOpt = advancement.getAdvancementDisplay();
if (!advancementDisplayOpt.isPresent()) {
continue;
}

val advancementDisplay = advancementDisplayOpt.get().shallowClone();
translateAdvancementDisplay(advancementDisplay, languagePlayer);
if (advancementDisplay.hasChangedAndReset()) {
val advancementClone = advancement.shallowClone();
advancementClone.setAdvancementDisplay(Optional.of(advancementDisplay));
advancementHolder.setAdvancement(advancementClone);
}
}
}

advancementDisplay.setTitle(WrappedChatComponent.fromJson(ComponentSerializer.toString(translatedTitle)));
advancementDisplay.setDescription(WrappedChatComponent.fromJson(ComponentSerializer.toString(translatedDescription)));
private void translateAdvancementDisplay(WrappedAdvancementDisplay advancementDisplay, Localized locale) {
val originalTitle = ComponentSerializer.parse(advancementDisplay.getTitle().getJson());
val originalDescription = ComponentSerializer.parse(advancementDisplay.getDescription().getJson());

ADVANCEMENT_DISPLAY_FIELD.set(serializedAdvancement, advancementDisplay.getHandle());
}
val translatedTitle = getLanguageParser().parseComponent(locale, getMain().getConf().getAdvancementsSyntax(), originalTitle);
val translatedDescription = getLanguageParser().parseComponent(locale, getMain().getConf().getAdvancementsSyntax(), originalDescription);

advancementDisplay.setTitle(WrappedChatComponent.fromJson(ComponentSerializer.toString(translatedTitle)));
advancementDisplay.setDescription(WrappedChatComponent.fromJson(ComponentSerializer.toString(translatedDescription)));
}

/**
Expand Down Expand Up @@ -126,6 +188,11 @@ public void refreshAdvancements(SpigotLanguagePlayer languagePlayer) {

@Override
public void registerPacketTypes(Map<PacketType, HandlerFunction> registry) {
registry.put(PacketType.Play.Server.ADVANCEMENTS, asAsync(this::handleAdvancements));
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
// MC 1.20.2+
registry.put(PacketType.Play.Server.ADVANCEMENTS, asAsync(this::handleAdvancementsPost1_20_2));
} else {
registry.put(PacketType.Play.Server.ADVANCEMENTS, asAsync(this::handleAdvancementsPre1_20_2));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.rexcantor64.triton.wrappers;

import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
import com.comphenix.protocol.wrappers.Converters;

import java.util.Map;
import java.util.Optional;

/**
* Custom ProtocolLib Wrapper of NMS' Advancement
* Does not work with versions lower than 1.20.2, but it is not used then
* Tested only with 1.20.2
*/
public class WrappedAdvancement extends AbstractWrapper {

private static Class<?> ADVANCEMENT = MinecraftReflection.getMinecraftClass("advancements.Advancement", "Advancement");
private static Class<?> ADVANCEMENT_REWARDS_CLASS = MinecraftReflection.getMinecraftClass("advancements.AdvancementRewards", "AdvancementRewards");
private static Class<?> ADVANCEMENT_REQUIREMENTS_CLASS = MinecraftReflection.getMinecraftClass("advancements.AdvancementRequirements", "AdvancementRequirements");
private static FuzzyReflection FUZZY_REFLECTION = FuzzyReflection.fromClass(ADVANCEMENT, true);
private static FieldAccessor MINECRAFT_KEY = Accessors.getFieldAccessor(FUZZY_REFLECTION.getParameterizedField(Optional.class, MinecraftReflection.getMinecraftKeyClass()));
private static FieldAccessor ADVANCEMENT_DISPLAY = Accessors.getFieldAccessor(FUZZY_REFLECTION.getParameterizedField(Optional.class, WrappedAdvancementDisplay.getWrappedClass()));
private static FieldAccessor ADVANCEMENT_REWARDS = Accessors.getFieldAccessor(ADVANCEMENT, ADVANCEMENT_REWARDS_CLASS, true);
private static FieldAccessor CRITERION_MAP = Accessors.getFieldAccessor(ADVANCEMENT, Map.class, true);
private static FieldAccessor ADVANCEMENT_REQUIREMENTS = Accessors.getFieldAccessor(ADVANCEMENT, ADVANCEMENT_REQUIREMENTS_CLASS, true);
private static FieldAccessor SEND_TELEMETRY_EVENT_BOOL = Accessors.getFieldAccessor(ADVANCEMENT, boolean.class, true);
private static FieldAccessor CHAT = Accessors.getFieldAccessor(FUZZY_REFLECTION.getParameterizedField(Optional.class, MinecraftReflection.getIChatBaseComponentClass()));
private static ConstructorAccessor ADVANCEMENT_CONSTRUTOR = Accessors.getConstructorAccessor(
ADVANCEMENT,
Optional.class,
Optional.class,
ADVANCEMENT_REWARDS_CLASS,
Map.class,
ADVANCEMENT_REQUIREMENTS_CLASS,
boolean.class,
Optional.class
);

public static final EquivalentConverter<Optional<WrappedAdvancementDisplay>> DISPLAY_CONVERT = Converters.optional(WrappedAdvancementDisplay.CONVERTER);

public static final EquivalentConverter<WrappedAdvancement> CONVERTER = Converters.ignoreNull(Converters.handle(WrappedAdvancement::getHandle, WrappedAdvancement::fromHandle, WrappedAdvancement.class));

/**
* Construct a new AdvancementDisplay wrapper.
*/
private WrappedAdvancement(Object handle) {
super(getWrappedClass());
setHandle(handle);
}

public Optional<WrappedAdvancementDisplay> getAdvancementDisplay() {
return DISPLAY_CONVERT.getSpecific(ADVANCEMENT_DISPLAY.get(handle));
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public void setAdvancementDisplay(Optional<WrappedAdvancementDisplay> advancementDisplay) {
ADVANCEMENT_DISPLAY.set(handle, DISPLAY_CONVERT.getGeneric(advancementDisplay));
}

public WrappedAdvancement shallowClone() {
Object newInstance = ADVANCEMENT_CONSTRUTOR.invoke(
MINECRAFT_KEY.get(handle),
ADVANCEMENT_DISPLAY.get(handle),
ADVANCEMENT_REWARDS.get(handle),
CRITERION_MAP.get(handle),
ADVANCEMENT_REQUIREMENTS.get(handle),
SEND_TELEMETRY_EVENT_BOOL.get(handle),
CHAT.get(handle)
);

return new WrappedAdvancement(newInstance);
}

/**
* Construct a wrapped advancement display from a native NMS object.
*
* @param handle - the native object.
* @return The wrapped advancement display object.
*/
public static WrappedAdvancement fromHandle(Object handle) {
return new WrappedAdvancement(handle);
}

public static Class<?> getWrappedClass() {
return ADVANCEMENT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedChatComponent;

/**
Expand Down Expand Up @@ -42,6 +43,10 @@ public class WrappedAdvancementDisplay extends AbstractWrapper {

private static EquivalentConverter<WrappedChatComponent> CHAT_CONVERT = BukkitConverters.getWrappedChatComponentConverter();

public static final EquivalentConverter<WrappedAdvancementDisplay> CONVERTER = Converters.ignoreNull(Converters.handle(WrappedAdvancementDisplay::getHandle, WrappedAdvancementDisplay::fromHandle, WrappedAdvancementDisplay.class));

private boolean hasChanged = false;

/**
* Construct a new AdvancementDisplay wrapper.
*/
Expand All @@ -60,10 +65,18 @@ public WrappedChatComponent getDescription() {

public void setTitle(WrappedChatComponent title) {
TITLE.set(handle, CHAT_CONVERT.getGeneric(title));
hasChanged = true;
}

public void setDescription(WrappedChatComponent description) {
DESCRIPTION.set(handle, CHAT_CONVERT.getGeneric(description));
hasChanged = true;
}

public boolean hasChangedAndReset() {
boolean changed = hasChanged;
hasChanged = false;
return changed;
}

public WrappedAdvancementDisplay shallowClone() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.rexcantor64.triton.wrappers;

import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
import com.comphenix.protocol.wrappers.Converters;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

/**
* Custom ProtocolLib Wrapper of NMS' AdvancementHolder (added to NMS in 1.20.2)
* Tested with 1.20.2
*/
public class WrappedAdvancementHolder extends AbstractWrapper {

private static Class<?> ADVANCEMENT_HOLDER = MinecraftReflection.getMinecraftClass("advancements.AdvancementHolder");
private static Class<?> ADVANCEMENT_CLASS = MinecraftReflection.getMinecraftClass("advancements.Advancement", "Advancement");
private static FieldAccessor ADVANCEMENT = Accessors.getFieldAccessor(ADVANCEMENT_HOLDER, ADVANCEMENT_CLASS, true);

public static final EquivalentConverter<WrappedAdvancementHolder> CONVERTER = Converters.ignoreNull(Converters.handle(WrappedAdvancementHolder::getHandle, WrappedAdvancementHolder::fromHandle, WrappedAdvancementHolder.class));

/**
* Construct a new AdvancementDisplay wrapper.
*/
private WrappedAdvancementHolder(Object handle) {
super(getWrappedClass());
setHandle(handle);
}

public WrappedAdvancement getAdvancement() {
return WrappedAdvancement.CONVERTER.getSpecific(ADVANCEMENT.get(handle));
}

public void setAdvancement(WrappedAdvancement advancement) {
ADVANCEMENT.set(handle, WrappedAdvancement.CONVERTER.getGeneric(advancement));
}

/**
* Construct a wrapped advancement display from a native NMS object.
*
* @param handle - the native object.
* @return The wrapped advancement display object.
*/
@Contract("_ -> new")
public static @NotNull WrappedAdvancementHolder fromHandle(Object handle) {
return new WrappedAdvancementHolder(handle);
}

public static Class<?> getWrappedClass() {
return ADVANCEMENT_HOLDER;
}
}

0 comments on commit 65e0c32

Please sign in to comment.