feat: init pe

This commit is contained in:
ineanto 2025-06-27 14:54:35 +02:00
parent 073d96bf99
commit ab2ffcbd37
Signed by: ineanto
GPG key ID: E511F9CAA2F9CE84
5 changed files with 234 additions and 189 deletions

View file

@ -2,6 +2,7 @@ package xyz.ineanto.nicko;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.plugin.java.JavaPlugin;
@ -39,20 +40,29 @@ public class Nicko extends JavaPlugin {
private PlayerNameStore nameStore;
private RandomNameFetcher nameFetcher;
@Override
public void onLoad() {
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().load();
}
@Override
public void onEnable() {
plugin = this;
PacketEvents.getAPI().init();
PacketEvents.getAPI().getSettings().checkForUpdates(false).kickOnPacketException(true);
configurationManager = new ConfigurationManager(getDataFolder());
configurationManager.saveDefaultConfig();
dataStore = new PlayerDataStore(mojangAPI, getNickoConfig());
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_20)) {
getLogger().severe("This version (" + PacketEvents.getAPI().getServerManager().getVersion() + ") is not supported by Nicko!");
getLogger().severe("As of version 1.2.0, Nicko only supports the two latest major Minecraft versions. (Currently 1.20 to 1.21.5)");
dataStore.getStorage().setError(true);
Bukkit.getPluginManager().disablePlugin(this);
getLogger().severe("This version (" + PacketEvents.getAPI().getServerManager().getVersion() + ") is not officially supported by Nicko!");
getLogger().severe("As of version 1.3.0, Nicko only supports the two latest major Minecraft versions. (Currently 1.20 to 1.21.5)");
getLogger().severe("Do NOT complain about it not working, you've been warned!");
}
if (!Bukkit.getOnlineMode()) {

View file

@ -5,8 +5,8 @@ import org.bukkit.entity.Player;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.event.custom.PlayerDisguiseEvent;
import xyz.ineanto.nicko.event.custom.PlayerResetDisguiseEvent;
import xyz.ineanto.nicko.packet.PacketEventsPacketSender;
import xyz.ineanto.nicko.packet.PacketSender;
import xyz.ineanto.nicko.packet.PaperPacketSender;
import xyz.ineanto.nicko.profile.NickoProfile;
import xyz.ineanto.nicko.storage.PlayerDataStore;
import xyz.ineanto.nicko.storage.name.PlayerNameStore;
@ -23,7 +23,7 @@ public class AppearanceManager {
public AppearanceManager(Player player) {
this.player = player;
this.packetSender = new PaperPacketSender(player, getNickoProfile());
this.packetSender = new PacketEventsPacketSender(player, getNickoProfile());
}
public ActionResult reset() {

View file

@ -1,10 +1,17 @@
package xyz.ineanto.nicko.packet;
import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.world.Difficulty;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import xyz.ineanto.nicko.Nicko;
import xyz.ineanto.nicko.appearance.ActionResult;
@ -15,6 +22,7 @@ import xyz.ineanto.nicko.profile.NickoProfile;
import java.io.IOException;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ExecutionException;
public class PacketEventsPacketSender implements PacketSender {
@ -30,27 +38,39 @@ public class PacketEventsPacketSender implements PacketSender {
public void sendEntityRespawn() {
if (!profile.hasData()) return;
// TODO (Ineanto, 27/06/2025): Create/Delete packets here
final WrapperPlayServerDestroyEntities destroy = new WrapperPlayServerDestroyEntities(player.getEntityId());
final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity(
new Random().nextInt(9999),
Optional.of(player.getUniqueId()),
EntityTypes.PLAYER,
new Vector3d(player.getX(), player.getY(), player.getZ()),
player.getPitch(),
player.getYaw(),
player.getBodyYaw(),
0,
Optional.empty()
);
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
// TODO (Ineanto, 27/06/2025): Send packets
//sendPacket(destroy, player);
//sendPacket(create, player);
sendPacket(destroy, player);
sendPacket(spawn, player);
});
}
@Override
public ActionResult updatePlayerProfile(String name) {
final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), name);
final PlayerProfile previousProfile = player.getPlayerProfile();
final PlayerProfile newProfile = Bukkit.getServer().createProfile(player.getUniqueId(), name);
// Copy previous properties to preserve skin
playerProfile.setProperties(playerProfile.getProperties());
player.setPlayerProfile(playerProfile);
newProfile.setProperties(previousProfile.getProperties());
player.setPlayerProfile(newProfile);
return ActionResult.ok();
}
@Override
public ActionResult updatePlayerProfileProperties() {
// TODO (Ineanto, 27/06/2025): Player profile
final PlayerProfile playerProfile = player.getPlayerProfile();
try {
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
@ -66,8 +86,8 @@ public class PacketEventsPacketSender implements PacketSender {
}
final MojangSkin skinResult = skin.get();
//playerProfile.setProperties(skinResult.asProfileProperties());
//player.setPlayerProfile(playerProfile);
playerProfile.setProperties(skinResult.asProfileProperties());
player.setPlayerProfile(playerProfile);
return ActionResult.ok();
} catch (ExecutionException | IOException e) {
return ActionResult.error(LanguageKey.Error.CACHE);
@ -82,8 +102,24 @@ public class PacketEventsPacketSender implements PacketSender {
@Override
public void sendPlayerRespawn() {
// TODO (Ineanto, 27/06/2025): Respawn packet
//sendPacket(respawn, player);
final World world = player.getWorld();
final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn(
SpigotConversionUtil.typeFromBukkitWorld(world),
world.getName(),
Difficulty.getById(world.getDifficulty().ordinal()),
world.getSeed(),
SpigotConversionUtil.fromBukkitGameMode(player.getGameMode()),
SpigotConversionUtil.fromBukkitGameMode(player.getPreviousGameMode()),
false,
false,
true,
null,
null,
null
);
sendPacket(respawn, player);
}
@Override

View file

@ -1,167 +1,167 @@
package xyz.ineanto.nicko.packet;
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;
import net.minecraft.network.chat.RemoteChatSession;
import net.minecraft.network.chat.contents.PlainTextContents;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.entity.CraftPlayer;
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.profile.NickoProfile;
import java.io.IOException;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ExecutionException;
/**
* Look at this Mojang.
* I want you to really stare at this code.
* You made me do this.
*/
public class PaperPacketSender implements PacketSender {
private final Player player;
private final NickoProfile profile;
public PaperPacketSender(Player player, NickoProfile profile) {
this.player = player;
this.profile = profile;
}
@Override
public void sendEntityRespawn() {
if (!profile.hasData()) return;
final ClientboundRemoveEntitiesPacket destroy = new ClientboundRemoveEntitiesPacket(IntList.of(player.getEntityId()));
final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket(
new Random().nextInt(9999),
player.getUniqueId(),
player.getX(),
player.getY(),
player.getZ(),
player.getPitch(),
player.getYaw(),
EntityType.PLAYER,
0,
Vec3.ZERO,
player.getBodyYaw()
);
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
sendPacket(destroy, player);
sendPacket(add, player);
});
}
@Override
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 =
new SynchedEntityData.DataItem<>(
new EntityDataAccessor<>(17, EntityDataSerializers.BYTE),
(byte) 0x7f
).value();
final ClientboundSetEntityDataPacket data = new ClientboundSetEntityDataPacket(player.getEntityId(), List.of(dataValueComponent));
sendPacket(data, player);
}
@Override
public void sendPlayerRespawn() {
final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
final ServerLevel level = serverPlayer.serverLevel();
final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(level), (byte) 0x03);
sendPacket(respawn, player);
}
@Override
public void sendTabListUpdate(String displayName) {
final ServerPlayer serverPlayer = (((CraftPlayer) player)).getHandle();
final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = EnumSet.of(
ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER,
ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT,
ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED,
ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME,
ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE,
ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
final List<ClientboundPlayerInfoUpdatePacket.Entry> entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry(
serverPlayer.getUUID(),
serverPlayer.gameProfile,
true,
serverPlayer.connection.latency(),
serverPlayer.gameMode.getGameModeForPlayer(),
MutableComponent.create(new PlainTextContents.LiteralContents(displayName)),
true,
serverPlayer.getTabListOrder(),
Optionull.map(serverPlayer.getChatSession(), RemoteChatSession::asData)
));
final ClientboundPlayerInfoUpdatePacket update = new ClientboundPlayerInfoUpdatePacket(actions, entries);
final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId()));
Bukkit.getOnlinePlayers().forEach(onlinePlayer -> {
sendPacket(remove, onlinePlayer);
sendPacket(update, onlinePlayer);
});
}
private void sendPacket(Packet<?> packet, Player player) {
(((CraftPlayer) player).getHandle()).connection.send(packet);
}
}
//package xyz.ineanto.nicko.packet;
//
//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;
//import net.minecraft.network.chat.RemoteChatSession;
//import net.minecraft.network.chat.contents.PlainTextContents;
//import net.minecraft.network.protocol.Packet;
//import net.minecraft.network.protocol.game.*;
//import net.minecraft.network.syncher.EntityDataAccessor;
//import net.minecraft.network.syncher.EntityDataSerializers;
//import net.minecraft.network.syncher.SynchedEntityData;
//import net.minecraft.server.level.ServerLevel;
//import net.minecraft.server.level.ServerPlayer;
//import net.minecraft.world.entity.EntityType;
//import net.minecraft.world.phys.Vec3;
//import org.bukkit.Bukkit;
//import org.bukkit.craftbukkit.entity.CraftPlayer;
//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.profile.NickoProfile;
//
//import java.io.IOException;
//import java.util.EnumSet;
//import java.util.List;
//import java.util.Optional;
//import java.util.Random;
//import java.util.concurrent.ExecutionException;
//
///**
// * Look at this Mojang.
// * I want you to really stare at this code.
// * You made me do this.
// */
//public class PaperPacketSender implements PacketSender {
// private final Player player;
// private final NickoProfile profile;
//
// public PaperPacketSender(Player player, NickoProfile profile) {
// this.player = player;
// this.profile = profile;
// }
//
// @Override
// public void sendEntityRespawn() {
// if (!profile.hasData()) return;
//
// final ClientboundRemoveEntitiesPacket destroy = new ClientboundRemoveEntitiesPacket(IntList.of(player.getEntityId()));
// final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket(
// new Random().nextInt(9999),
// player.getUniqueId(),
// player.getX(),
// player.getY(),
// player.getZ(),
// player.getPitch(),
// player.getYaw(),
// EntityType.PLAYER,
// 0,
// Vec3.ZERO,
// player.getBodyYaw()
// );
//
// Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
// sendPacket(destroy, player);
// sendPacket(add, player);
// });
// }
//
// @Override
// 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 =
// new SynchedEntityData.DataItem<>(
// new EntityDataAccessor<>(17, EntityDataSerializers.BYTE),
// (byte) 0x7f
// ).value();
//
// final ClientboundSetEntityDataPacket data = new ClientboundSetEntityDataPacket(player.getEntityId(), List.of(dataValueComponent));
// sendPacket(data, player);
// }
//
// @Override
// public void sendPlayerRespawn() {
// final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
// final ServerLevel level = serverPlayer.serverLevel();
//
// final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(level), (byte) 0x03);
// sendPacket(respawn, player);
// }
//
// @Override
// public void sendTabListUpdate(String displayName) {
// final ServerPlayer serverPlayer = (((CraftPlayer) player)).getHandle();
//
// final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = EnumSet.of(
// ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER,
// ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT,
// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED,
// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME,
// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE,
// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
//
// final List<ClientboundPlayerInfoUpdatePacket.Entry> entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry(
// serverPlayer.getUUID(),
// serverPlayer.gameProfile,
// true,
// serverPlayer.connection.latency(),
// serverPlayer.gameMode.getGameModeForPlayer(),
// MutableComponent.create(new PlainTextContents.LiteralContents(displayName)),
// true,
// serverPlayer.getTabListOrder(),
// Optionull.map(serverPlayer.getChatSession(), RemoteChatSession::asData)
// ));
//
// final ClientboundPlayerInfoUpdatePacket update = new ClientboundPlayerInfoUpdatePacket(actions, entries);
// final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId()));
//
// Bukkit.getOnlinePlayers().forEach(onlinePlayer -> {
// sendPacket(remove, onlinePlayer);
// sendPacket(update, onlinePlayer);
// });
// }
//
// private void sendPacket(Packet<?> packet, Player player) {
// (((CraftPlayer) player).getHandle()).connection.send(packet);
// }
//}