feat: update paper plugin sender, fix protocollib classpath access

This commit is contained in:
ineanto 2025-04-02 18:23:19 +02:00
parent 9adf5f34f5
commit 875a04a850
Signed by: ineanto
GPG key ID: E511F9CAA2F9CE84
14 changed files with 84 additions and 210 deletions

View file

@ -1,7 +1,7 @@
plugins {
id("java")
id("com.gradleup.shadow") version "8.3.2"
id("xyz.jpenilla.run-paper") version "2.3.0"
id("xyz.jpenilla.run-paper") version "2.3.1"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.10"
}
@ -31,11 +31,11 @@ repositories {
dependencies {
paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
compileOnly("com.comphenix.protocol:ProtocolLib:5.4.0-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.5")
compileOnly("net.kyori:adventure-api:4.17.0")
compileOnly("xyz.xenondevs.invui:invui-core:$invuiVersion")
compileOnly("net.wesjd:anvilgui:1.10.4-SNAPSHOT")
compileOnly("com.comphenix.protocol:ProtocolLib:5.4.0-SNAPSHOT")
implementation("de.rapha149.signgui:signgui:2.5.0")
implementation("com.github.jsixface:yamlconfig:1.2")
@ -62,7 +62,7 @@ tasks {
relocate("net.wesjd", "xyz.ineanto.nicko.libs.anvilgui")
relocate("com.github.jsixface", "xyz.ineanto.nicko.libs.yaml")
relocate("me.clip", "xyz.ineanto.nicko.libs.placeholderapi")
relocate("com.fasterxml.jackson", "xyz.ineanto.nicko.libs.jacksonpr")
relocate("com.fasterxml.jackson", "xyz.ineanto.nicko.libs.jackson")
relocate("com.mysql", "xyz.ineanto.nicko.libs.mysql")
relocate("org.mariadb.jdbc", "xyz.ineanto.nicko.libs.mariadb")
relocate("redis.clients", "xyz.ineanto.nicko.libs.redis")
@ -95,10 +95,10 @@ tasks {
runServer {
downloadPlugins {
url("https://download.luckperms.net/1568/bukkit/loader/LuckPerms-Bukkit-5.4.151.jar")
url("https://download.luckperms.net/1575/bukkit/loader/LuckPerms-Bukkit-5.4.158.jar")
// 1.20.5 - latest testing
url("https://ci.dmulloy2.net/job/ProtocolLib/lastSuccessfulBuild/artifact/build/libs/ProtocolLib.jar")
//url("https://ci.dmulloy2.net/job/ProtocolLib/lastSuccessfulBuild/artifact/build/libs/ProtocolLib.jar")
}
minecraftVersion("1.21.4")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

View file

@ -6,7 +6,7 @@ import org.bukkit.event.player.PlayerTeleportEvent;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.event.custom.PlayerDisguiseEvent;
import xyz.ineanto.nicko.event.custom.PlayerResetDisguiseEvent;
import xyz.ineanto.nicko.packet.InternalPacketSender;
import xyz.ineanto.nicko.packet.PaperPacketSender;
import xyz.ineanto.nicko.packet.PacketSender;
import xyz.ineanto.nicko.profile.NickoProfile;
import xyz.ineanto.nicko.storage.PlayerDataStore;
@ -24,7 +24,7 @@ public class AppearanceManager {
public AppearanceManager(Player player) {
this.player = player;
this.packetSender = new InternalPacketSender(player, getNickoProfile());
this.packetSender = new PaperPacketSender(player, getNickoProfile());
}
public ActionResult reset() {
@ -51,10 +51,14 @@ public class AppearanceManager {
final NickoProfile profile = getNickoProfile();
final String displayName = profile.getName() == null ? player.getName() : profile.getName();
final ActionResult result = packetSender.sendGameProfileUpdate(displayName, skinChange, reset);
final ActionResult result = packetSender.updatePlayerProfile(displayName);
if (result.isError()) {
return reset();
if (skinChange) {
final ActionResult propertiesUpdateResult = packetSender.updatePlayerProfileProperties();
if (propertiesUpdateResult.isError()) {
return reset();
}
}
// Call the event.
@ -63,8 +67,8 @@ public class AppearanceManager {
packetSender.sendEntityMetadataUpdate();
packetSender.sendTabListUpdate(displayName);
respawnPlayer();
packetSender.sendEntityRespawn();
//respawnPlayer();
//packetSender.sendEntityRespawn();
return result;
}

View file

@ -8,6 +8,7 @@ import org.bukkit.entity.Player;
import xyz.ineanto.nicko.language.LanguageKey;
import xyz.ineanto.nicko.language.PlayerLanguage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -16,12 +17,13 @@ import java.util.concurrent.atomic.AtomicReference;
public class SignPrompt implements Prompt {
private final Player player;
private final PlayerLanguage playerLanguage;
private List<String> lines = List.of(
"VVVVVVVVVVVVVVV",
null,
null,
"ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ"
private final ArrayList<String> lines = new ArrayList<>(
List.of(
"VVVVVVVVVVVVVVV",
"",
"",
"ΛΛΛΛΛΛΛΛΛΛΛΛΛΛΛ"
)
);
private final AtomicReference<Optional<String>> name = new AtomicReference<>();
@ -73,6 +75,7 @@ public class SignPrompt implements Prompt {
try {
final SignGUI gui = SignGUI.builder()
.setLines(lines.toArray(new String[0]))
.setLine(2, null)
.setType(Material.OAK_SIGN)
.setHandler((_, result) -> {
final String internalLine2 = result.getLineWithoutColor(2);
@ -89,6 +92,7 @@ public class SignPrompt implements Prompt {
gui.open(player);
return reference.get();
} catch (SignGUIVersionException exception) {
exception.printStackTrace();
return Optional.empty();
}
}

View file

@ -9,7 +9,7 @@ public enum Language implements Serializable {
FRENCH("fr", "Français"),
CUSTOM("cm", "Server Custom");
public static final Version VERSION = new Version(1, 2, 0);
public static final Version VERSION = new Version(1, 3, 0);
private final String code;
private transient final String name;

View file

@ -10,8 +10,7 @@ public class LanguageKey {
public static final String PERMISSION = ERROR_KEY + "permission";
public static final String CACHE = ERROR_KEY + "cache";
public static final String MOJANG_NAME = ERROR_KEY + "mojang_name";
public static final String MOJANG_SKIN = ERROR_KEY + "mojang_skin";
public static final String MOJANG = ERROR_KEY + "mojang";
}
public static class Event {

View file

@ -3,8 +3,8 @@ package xyz.ineanto.nicko.loader;
import io.papermc.paper.plugin.loader.PluginClasspathBuilder;
import io.papermc.paper.plugin.loader.PluginLoader;
import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
public class NickoPluginLoader implements PluginLoader {
@ -15,7 +15,6 @@ public class NickoPluginLoader implements PluginLoader {
resolver.addRepository(new RemoteRepository.Builder("xenondevs", "default", "https://repo.xenondevs.xyz/releases/").build());
resolver.addRepository(new RemoteRepository.Builder("codemc", "default", "https://repo.codemc.io/repository/maven-snapshots/").build());
resolver.addDependency(new Dependency(new DefaultArtifact("xyz.xenondevs.invui:invui:pom:1.44"), null));
//resolver.addDependency(new Dependency(new DefaultArtifact("net.wesjd:anvilgui:1.10.4-SNAPSHOT"), null));
pluginClasspathBuilder.addLibrary(resolver);
}

View file

@ -1,7 +1,11 @@
package xyz.ineanto.nicko.mojang;
import com.destroystokyo.paper.profile.ProfileProperty;
import com.google.gson.JsonObject;
import java.util.Collection;
import java.util.Collections;
public record MojangSkin(String value, String signature) {
public static MojangSkin buildFromJson(JsonObject object) {
final JsonObject properties = object.get("properties").getAsJsonArray().get(0).getAsJsonObject();
@ -9,4 +13,8 @@ public record MojangSkin(String value, String signature) {
final String signature = properties.get("signature").getAsString();
return new MojangSkin(value, signature);
}
public Collection<ProfileProperty> asProfileProperties() {
return Collections.singleton(new ProfileProperty("textures", value, signature));
}
}

View file

@ -5,7 +5,9 @@ import xyz.ineanto.nicko.appearance.ActionResult;
public interface PacketSender {
void sendEntityRespawn();
ActionResult sendGameProfileUpdate(String name, boolean skinChange, boolean reset);
ActionResult updatePlayerProfile(String name);
ActionResult updatePlayerProfileProperties();
void sendEntityMetadataUpdate();

View file

@ -1,8 +1,7 @@
package xyz.ineanto.nicko.packet;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.Optionull;
import net.minecraft.network.chat.MutableComponent;
@ -39,11 +38,11 @@ import java.util.concurrent.ExecutionException;
* I want you to really stare at this code.
* You made me do this.
*/
public class InternalPacketSender implements PacketSender {
public class PaperPacketSender implements PacketSender {
private final Player player;
private final NickoProfile profile;
public InternalPacketSender(Player player, NickoProfile profile) {
public PaperPacketSender(Player player, NickoProfile profile) {
this.player = player;
this.profile = profile;
}
@ -74,41 +73,40 @@ public class InternalPacketSender implements PacketSender {
}
@Override
public ActionResult sendGameProfileUpdate(String name, boolean skinChange, boolean reset) {
final GameProfile gameProfile = new GameProfile(player.getUniqueId(), name);
// TODO (Ineanto, 31/10/2024): Could this be refactored to get rid of the boolean?
if (skinChange) {
Optional<MojangSkin> skin;
try {
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
final Optional<String> uuid = mojangAPI.getUUID(profile.getSkin());
if (uuid.isPresent()) {
skin = reset ? mojangAPI.getSkinWithoutCaching(uuid.get()) : mojangAPI.getSkin(uuid.get());
if (skin.isPresent()) {
final MojangSkin skinResult = skin.get();
final PropertyMap properties = gameProfile.getProperties();
properties.get("textures").clear();
properties.put("textures", new Property("textures", skinResult.value(), skinResult.signature()));
((CraftPlayer) player).getHandle().gameProfile = gameProfile;
} else {
return ActionResult.error(LanguageKey.Error.MOJANG_SKIN);
}
} else {
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
}
return ActionResult.ok();
} catch (ExecutionException e) {
return ActionResult.error(LanguageKey.Error.CACHE);
} catch (IOException e) {
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
} catch (InterruptedException e) {
return ActionResult.error("Unknown error");
}
}
public ActionResult updatePlayerProfile(String name) {
final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), name);
// Copy previous properties to preserve skin
playerProfile.setProperties(playerProfile.getProperties());
player.setPlayerProfile(playerProfile);
return ActionResult.ok();
}
@Override
public ActionResult updatePlayerProfileProperties() {
final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), profile.getName() == null ? player.getName() : profile.getName());
try {
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
final Optional<String> uuid = mojangAPI.getUUID(profile.getSkin());
if (uuid.isEmpty()) {
return ActionResult.error(LanguageKey.Error.MOJANG);
}
final Optional<MojangSkin> skin = mojangAPI.getSkin(uuid.get());
if (skin.isEmpty()) {
return ActionResult.error(LanguageKey.Error.MOJANG);
}
final MojangSkin skinResult = skin.get();
playerProfile.setProperties(skinResult.asProfileProperties());
player.setPlayerProfile(playerProfile);
return ActionResult.ok();
} catch (ExecutionException | IOException e) {
return ActionResult.error(LanguageKey.Error.CACHE);
}
}
@Override
public void sendEntityMetadataUpdate() {
final SynchedEntityData.DataValue<?> dataValueComponent =

View file

@ -1,139 +0,0 @@
package xyz.ineanto.nicko.packet;
import com.comphenix.protocol.wrappers.*;
import com.google.common.collect.Multimap;
import it.unimi.dsi.fastutil.ints.IntList;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.appearance.ActionResult;
import xyz.ineanto.nicko.language.LanguageKey;
import xyz.ineanto.nicko.mojang.MojangAPI;
import xyz.ineanto.nicko.mojang.MojangSkin;
import xyz.ineanto.nicko.packet.wrapper.*;
import xyz.ineanto.nicko.profile.NickoProfile;
import java.io.IOException;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
public class WrapperPacketSender implements PacketSender {
private final Player player;
private final NickoProfile profile;
private WrappedGameProfile gameProfile;
public WrapperPacketSender(Player player, NickoProfile profile) {
this.player = player;
this.profile = profile;
}
@Override
public void sendEntityRespawn() {
if (!profile.hasData()) return;
final WrapperPlayServerEntityDestroy destroy = new WrapperPlayServerEntityDestroy();
final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity();
destroy.setEntityIds(IntList.of(player.getEntityId()));
spawn.setEntityId(player.getEntityId());
spawn.setLocation(player.getLocation());
spawn.setPlayerId(player.getUniqueId());
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
destroy.sendPacket(receiver);
spawn.sendPacket(receiver);
});
}
@Override
public ActionResult sendGameProfileUpdate(String name, boolean skinChange, boolean reset) {
this.gameProfile = WrappedGameProfile.fromPlayer(player).withName(name);
// TODO (Ineanto, 31/10/2024): Could get refactored to omit this boolean?
if (skinChange) {
Optional<MojangSkin> skin;
try {
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
final Optional<String> uuid = mojangAPI.getUUID(profile.getSkin());
if (uuid.isPresent()) {
skin = reset ? mojangAPI.getSkinWithoutCaching(uuid.get()) : mojangAPI.getSkin(uuid.get());
if (skin.isPresent()) {
final MojangSkin skinResult = skin.get();
final Multimap<String, WrappedSignedProperty> properties = gameProfile.getProperties();
properties.get("textures").clear();
properties.put("textures", new WrappedSignedProperty("textures", skinResult.value(), skinResult.signature()));
} else {
return ActionResult.error(LanguageKey.Error.MOJANG_SKIN);
}
} else {
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
}
return ActionResult.ok();
} catch (ExecutionException e) {
return ActionResult.error(LanguageKey.Error.CACHE);
} catch (IOException e) {
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
} catch (InterruptedException e) {
return ActionResult.error("Unknown error");
}
}
return ActionResult.ok();
}
@Override
public void sendEntityMetadataUpdate() {
final WrappedDataWatcher entityWatcher = WrappedDataWatcher.getEntityWatcher(player);
entityWatcher.setObject(17, (byte) 0x7f, true);
}
@Override
public void sendPlayerRespawn() {
final World world = player.getWorld();
final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn();
respawn.setDimension(world);
respawn.setSeed(world.getSeed());
respawn.setGameMode(player.getGameMode());
respawn.setPreviousGameMode(player.getGameMode());
respawn.setCopyMetadata(true);
respawn.sendPacket(player);
}
@Override
public void sendTabListUpdate(String displayName) {
if (gameProfile == null) {
Nicko.getInstance().getLogger().warning("Hello. I sincerely hope you're doing great out there.");
Nicko.getInstance().getLogger().warning("If you see this message, I've failed at my task and I'm a terrible programmer.");
Nicko.getInstance().getLogger().warning("Report this issue on https://git.ineanto.xyz/ineanto/nicko, thank you!");
return;
}
final WrapperPlayerServerPlayerInfo add = new WrapperPlayerServerPlayerInfo();
final WrapperPlayerServerPlayerInfoRemove remove = new WrapperPlayerServerPlayerInfoRemove();
final EnumSet<EnumWrappers.PlayerInfoAction> actions = EnumSet.of(
EnumWrappers.PlayerInfoAction.ADD_PLAYER,
EnumWrappers.PlayerInfoAction.INITIALIZE_CHAT,
EnumWrappers.PlayerInfoAction.UPDATE_LISTED,
EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME,
EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE,
EnumWrappers.PlayerInfoAction.UPDATE_LATENCY);
remove.setUUIDs(List.of(player.getUniqueId()));
remove.broadcastPacket();
add.setActions(actions);
add.setData(List.of(new PlayerInfoData(
player.getUniqueId(),
player.getPing(),
true,
EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()),
gameProfile,
WrappedChatComponent.fromText(displayName),
WrappedRemoteChatSessionData.fromPlayer(player)
)));
add.broadcastPacket();
}
}

View file

@ -1,7 +1,7 @@
# Nicko ${version} - Language File:
# Specifies the configuration version, don't change.
version: "1.2.0"
version: "1.3.0"
prefix: "<b><gradient:#01a97c:#8ffd54>NICKO</gradient></b>"
whoosh: "<b><gradient:#01a97c:#8ffd54>WHOOSH!</gradient></b>"
@ -10,8 +10,7 @@ oops: "<b><color:#ff4640>OOPS!</color></b>"
error:
permission: "<gray>You're missing the permission to do that.</gray>"
invalid_username: "<gray>This is an invalid Minecraft username.</gray>"
mojang_name: "<gray>There's is not Minecraft account with this username.</gray>"
mojang_skin: "<gray>This Minecraft account has no skin.</gray>"
mojang: "<gray>Something went wrong while fetching data from Mojang.</gray>"
cache: "<gray>Unable to get data from the cache.</gray>"
event:

View file

@ -1,7 +1,7 @@
# Nicko ${version} - Fichier de langue:
# Précise la version de la configuration, ne pas changer.
version: "1.2.0"
version: "1.3.0"
prefix: "<b><gradient:#01a97c:#8ffd54>NICKO</gradient></b>"
whoosh: "<b><gradient:#01a97c:#8ffd54>WHOOSH!</gradient></b>"
@ -10,8 +10,7 @@ oops: "<b><color:#ff4640>OOPS!</color></b>"
error:
permission: "<gray>Vous n'avez pas la permission de faire cela.<gray>"
invalid_username: "<gray>Nom d'utilisateur Minecraft invalide.<gray>"
mojang_name: "<gray>Aucun compte Minecraft associé à ce nom d'utilisateur.<gray>"
mojang_skin: "<gray>Ce compte Minecraft n'a pas de skin.<gray>"
mojang: "<gray>Une erreur est surevenue en récupérant les information depuis Mojang.<gray>"
cache: "<gray>Impossible de récupérer les données depuis le cache.<gray>"
event:

View file

@ -9,10 +9,11 @@ softdepend: [ PlaceholderAPI ]
depend:
- ProtocolLib
# Suppose we require ProtocolLib to be loaded for our plugin
AnvilGUI:
join-classpath: true
required: true
dependencies:
server:
ProtocolLib:
load: BEFORE
join-classpath: true
permissions:
nicko.*: