feat: switching to pe

This commit is contained in:
ineanto 2025-06-27 10:49:54 +02:00
parent 37e42fb169
commit 073d96bf99
Signed by: ineanto
GPG key ID: E511F9CAA2F9CE84
10 changed files with 110 additions and 506 deletions

View file

@ -26,6 +26,7 @@ repositories {
maven("https://repo.xenondevs.xyz/releases") maven("https://repo.xenondevs.xyz/releases")
maven("https://repo.papermc.io/repository/maven-public/") maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.codemc.io/repository/maven-snapshots/") maven("https://repo.codemc.io/repository/maven-snapshots/")
maven("https://repo.codemc.io/repository/maven-releases/")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
} }
@ -36,7 +37,8 @@ dependencies {
compileOnly("net.kyori:adventure-api:4.21.0") compileOnly("net.kyori:adventure-api:4.21.0")
compileOnly("xyz.xenondevs.invui:invui-core:$invuiVersion") compileOnly("xyz.xenondevs.invui:invui-core:$invuiVersion")
compileOnly("net.wesjd:anvilgui:1.10.4-SNAPSHOT") compileOnly("net.wesjd:anvilgui:1.10.4-SNAPSHOT")
compileOnly("com.comphenix.protocol:ProtocolLib:5.4.0-SNAPSHOT") compileOnly("com.github.retrooper:packetevents-spigot:2.8.0")
implementation("de.rapha149.signgui:signgui:2.5.0") implementation("de.rapha149.signgui:signgui:2.5.0")
implementation("com.github.jsixface:yamlconfig:1.2") implementation("com.github.jsixface:yamlconfig:1.2")

View file

@ -1,6 +1,7 @@
package xyz.ineanto.nicko; package xyz.ineanto.nicko;
import com.comphenix.protocol.utility.MinecraftVersion; import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
@ -47,9 +48,9 @@ public class Nicko extends JavaPlugin {
dataStore = new PlayerDataStore(mojangAPI, getNickoConfig()); dataStore = new PlayerDataStore(mojangAPI, getNickoConfig());
if (!MinecraftVersion.v1_21_5.atOrAbove()) { if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_20)) {
getLogger().severe("This version (" + MinecraftVersion.getCurrentVersion().getVersion() + ") is not supported by Nicko!"); 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 latest Minecraft version. (Currently 1.21.5)"); 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); dataStore.getStorage().setError(true);
Bukkit.getPluginManager().disablePlugin(this); Bukkit.getPluginManager().disablePlugin(this);
} }

View file

@ -0,0 +1,102 @@
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.wrapper.PacketWrapper;
import org.bukkit.Bukkit;
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.Optional;
import java.util.concurrent.ExecutionException;
public class PacketEventsPacketSender implements PacketSender {
private final Player player;
private final NickoProfile profile;
public PacketEventsPacketSender(Player player, NickoProfile profile) {
this.player = player;
this.profile = profile;
}
@Override
public void sendEntityRespawn() {
if (!profile.hasData()) return;
// TODO (Ineanto, 27/06/2025): Create/Delete packets here
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
// TODO (Ineanto, 27/06/2025): Send packets
//sendPacket(destroy, player);
//sendPacket(create, 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() {
// TODO (Ineanto, 27/06/2025): Player profile
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() {
// TODO (Ineanto, 27/06/2025): Entity Metadata packet
//sendPacket(data, player);
}
@Override
public void sendPlayerRespawn() {
// TODO (Ineanto, 27/06/2025): Respawn packet
//sendPacket(respawn, player);
}
@Override
public void sendTabListUpdate(String displayName) {
// TODO (Ineanto, 27/06/2025): TabList packet
Bukkit.getOnlinePlayers().forEach(onlinePlayer -> {
//sendPacket(remove, onlinePlayer);
//sendPacket(update, onlinePlayer);
});
}
private void sendPacket(PacketWrapper<?> packet, Player player) {
PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet);
}
}

View file

@ -1,82 +0,0 @@
package xyz.ineanto.nicko.packet.debug;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.wrappers.MinecraftKey;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import xyz.ineanto.nicko.Nicko;
import java.util.Arrays;
import java.util.Optional;
public class RespawnPacketListener implements PacketListener {
@Override
public void onPacketSending(PacketEvent event) {
try {
final Optional<World> world = getWorld(event);
if (world.isEmpty()) {
Bukkit.broadcast(Component.text("did not find the world the player was in"));
return;
}
Bukkit.broadcast(Component.text("found " + world.get().getName() + "!"));
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public void onPacketReceiving(PacketEvent event) {
}
@Override
public ListeningWhitelist getSendingWhitelist() {
return ListeningWhitelist.newBuilder().types(PacketType.Play.Server.RESPAWN).build();
}
@Override
public ListeningWhitelist getReceivingWhitelist() {
return ListeningWhitelist.EMPTY_WHITELIST;
}
@Override
public Plugin getPlugin() {
return Nicko.getInstance();
}
private boolean keysEquals(MinecraftKey wrappedKey, NamespacedKey bukkitKey) {
// compare bukkit minecraft key and NMS wrapped minecraft key
return wrappedKey.getPrefix().equals(bukkitKey.getNamespace()) && wrappedKey.getKey().equals(bukkitKey.getKey());
}
public Optional<World> getWorld(PacketEvent event) throws Throwable {
// access CommonPlayerSpawnInfo, first field of that type in the Respawn / Login packets
final Object packetHandle = event.getPacket().getHandle();
final Object commonSpawnData = packetHandle.getClass().getRecordComponents()[0].getAccessor().invoke(packetHandle);
Arrays.stream(commonSpawnData.getClass().getRecordComponents()).forEach(component -> {
component.getAccessor().setAccessible(true);
System.out.println("-=-=-=-=-=-=-=-=-=-=-=-=-=-");
System.out.println(component.getName());
System.out.println(component.getType().getSimpleName());
component.getAccessor().setAccessible(false);
}
);
// get the key of the level the player is joining. Second field in the object. First of type ResourceKey
final MinecraftKey key = MinecraftKey.fromHandle(commonSpawnData.getClass().getRecordComponents()[1]);
for (World world : Bukkit.getWorlds()) {
if (keysEquals(key, world.getKey())) {
return Optional.of(world);
}
}
return Optional.empty();
}
}

View file

@ -1,73 +0,0 @@
/**
* PacketWrapper - ProtocolLib wrappers for Minecraft packets
* Copyright (C) dmulloy2 <http://dmulloy2.net>
* Copyright (C) Kristian S. Strangeland
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package xyz.ineanto.nicko.packet.wrapper;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import com.google.common.base.Objects;
import org.bukkit.entity.Player;
public abstract class AbstractPacket {
protected PacketContainer handle;
/**
* Constructs a new strongly typed wrapper for the given packet.
*
* @param handle - handle to the raw packet data.
* @param type - the packet type.
*/
protected AbstractPacket(PacketContainer handle, PacketType type) {
// Make sure we're given a valid packet
if (handle == null)
throw new IllegalArgumentException("Packet handle cannot be NULL.");
if (!Objects.equal(handle.getType(), type))
throw new IllegalArgumentException(handle.getHandle()
+ " is not a packet of type " + type);
this.handle = handle;
}
/**
* Retrieve a handle to the raw packet data.
*
* @return Raw packet data.
*/
public PacketContainer getHandle() {
return handle;
}
/**
* Send the current packet to the given receiver.
*
* @param receiver - the receiver.
* @throws RuntimeException If the packet cannot be sent.
*/
public void sendPacket(Player receiver) {
ProtocolLibrary.getProtocolManager().sendServerPacket(receiver,
getHandle());
}
/**
* Send the current packet to all online players.
*/
public void broadcastPacket() {
ProtocolLibrary.getProtocolManager().broadcastServerPacket(getHandle());
}
}

View file

@ -1,32 +0,0 @@
package xyz.ineanto.nicko.packet.wrapper;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.Converters;
import it.unimi.dsi.fastutil.ints.IntList;
/**
* Sent by the server to the client to remove one or more entities.
*/
public class WrapperPlayServerEntityDestroy extends AbstractPacket {
/**
* The packet type that is wrapped by this wrapper.
*/
public static final PacketType TYPE = PacketType.Play.Server.ENTITY_DESTROY;
public WrapperPlayServerEntityDestroy() {
super(new PacketContainer(TYPE), TYPE);
handle.getModifier().writeDefaults();
}
/**
* Sets the list of entity ids to remove
*
* @param value New value for field 'entityIds'
*/
public void setEntityIds(IntList value) {
this.handle.getModifier().withType(IntList.class, Converters.passthrough(IntList.class)).writeSafely(0, value);
}
}

View file

@ -1,147 +0,0 @@
package xyz.ineanto.nicko.packet.wrapper;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.InternalStructure;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.google.common.hash.Hashing;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.World;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent;
import java.util.Arrays;
/**
* PacketPlayServerRespawn Wrapper class (1.20.X to 1.21.X)
* <p>
* In 1.20.2, all the fields were merged inside a
* single "CommonPlayerSpawnInfo" record.
*
* @author inenato (w/ additional help from lukalt), based on work from dmulloy2 and Kristian S. Strangeland
*/
public class WrapperPlayServerRespawn extends AbstractPacket {
public static final PacketType TYPE = PacketType.Play.Server.RESPAWN;
private InternalStructure spawnInfoStructure = null;
public WrapperPlayServerRespawn() {
super(new PacketContainer(TYPE), TYPE);
handle.getModifier().writeDefaults();
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
spawnInfoStructure = handle.getStructures().read(0);
}
}
public void setDimension(World value) {
final MinecraftVersion v1_20_5 = new MinecraftVersion(1, 20, 5);
if (!MinecraftVersion.getCurrentVersion().isAtLeast(v1_20_5)) {
// 1.20 - 1.20.4
final StructureModifier<InternalStructure> structureModifier = spawnInfoStructure == null ?
handle.getStructures() : spawnInfoStructure.getStructures();
final StructureModifier<World> worldStructureModifier = spawnInfoStructure == null ?
handle.getWorldKeys() : spawnInfoStructure.getWorldKeys();
final InternalStructure dimensionType = structureModifier.read(0);
dimensionType.getMinecraftKeys().writeSafely(0, new MinecraftKey("minecraft", "dimension_type"));
dimensionType.getMinecraftKeys().writeSafely(1, new MinecraftKey("minecraft", "overworld"));
structureModifier.writeSafely(0, dimensionType);
worldStructureModifier.writeSafely(0, value);
} else {
// 1.20.5 to 1.21.1
try {
final Class<?> spawnInfoClass = MinecraftReflection.getMinecraftClass("network.protocol.game.CommonPlayerSpawnInfo");
Class<?>[] componentTypes = Arrays.stream(spawnInfoClass.getRecordComponents())
.map(RecordComponent::getType)
.toArray(Class<?>[]::new);
final Constructor<?> spawnInfoConstructor = spawnInfoClass.getDeclaredConstructor(componentTypes);
/**
* Holder<DimensionType> dimensionType,
* ResourceKey<Level> dimension,
* long seed,
* GameType gameType,
* GameType previousGameType,
* boolean isDebug,
* boolean isFlat,
* Optional<GlobalPos> lastDeathLocation,
* int portalCooldown
*/
final World world = Bukkit.getWorld("world");
FuzzyReflection.fromClass(spawnInfoClass).getConstructor(
FuzzyMethodContract
.newBuilder()
.build()
);
final Object spawnInfo = spawnInfoConstructor.newInstance(
BukkitConverters.getDimensionConverter().getGeneric(world),
BukkitConverters.getWorldKeyConverter().getGeneric(world),
world.getSeed(),
EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.NativeGameMode.fromBukkit(GameMode.SURVIVAL)),
EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.NativeGameMode.fromBukkit(GameMode.SURVIVAL)),
false,
false,
BukkitConverters.getSectionPositionConverter()
);
final Field commonSpawnDataField = Accessors.getFieldAccessor(TYPE.getPacketClass(), spawnInfoClass, true).getField();
commonSpawnDataField.set(spawnInfoStructure.getHandle(), spawnInfo);
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException |
InstantiationException e) {
throw new RuntimeException();
}
}
}
public void setGameMode(GameMode value) {
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
// 1.20 to 1.20.1
handle.getGameModes().writeSafely(0, EnumWrappers.NativeGameMode.fromBukkit(value));
return;
}
spawnInfoStructure.getGameModes().writeSafely(0, EnumWrappers.NativeGameMode.fromBukkit(value));
}
public void setPreviousGameMode(GameMode value) {
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
// 1.20 to 1.20.1
handle.getGameModes().writeSafely(1, EnumWrappers.NativeGameMode.fromBukkit(value));
return;
}
spawnInfoStructure.getGameModes().writeSafely(1, EnumWrappers.NativeGameMode.fromBukkit(value));
}
public void setCopyMetadata(boolean value) {
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) return;
// 1.20 to 1.20.1
handle.getBooleans().writeSafely(0, value);
}
public void setSeed(long value) {
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
// 1.20 to 1.20.1
handle.getLongs().writeSafely(0, Hashing.sha256().hashLong(value).asLong());
}
}
}

View file

@ -1,103 +0,0 @@
package xyz.ineanto.nicko.packet.wrapper;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import javax.annotation.Nonnull;
import java.util.UUID;
/**
* This packet is sent by the server when a player comes into visible range, not when a player joins.
*/
public class WrapperPlayServerSpawnEntity extends AbstractPacket {
/**
* The packet type that is wrapped by this wrapper.
*/
public static final PacketType TYPE = PacketType.Play.Server.SPAWN_ENTITY;
/**
* Constructors a new wrapper for the specified packet
*/
public WrapperPlayServerSpawnEntity() {
super(new PacketContainer(TYPE), TYPE);
handle.getModifier().writeDefaults();
}
/**
* Sets the entity id of the player
*
* @param value New value for field 'entityId'
*/
public void setEntityId(int value) {
this.handle.getIntegers().writeSafely(0, value);
this.handle.getEntityTypeModifier().writeSafely(0, EntityType.PLAYER);
}
/**
* Sets the unique id of the player
*
* @param value New value for field 'playerId'
*/
public void setPlayerId(UUID value) {
this.handle.getUUIDs().writeSafely(0, value);
}
/**
* Sets the value of field 'x'
*
* @param value New value for field 'x'
*/
public void setX(double value) {
this.handle.getDoubles().writeSafely(0, value);
}
/**
* Sets the value of field 'y'
*
* @param value New value for field 'y'
*/
public void setY(double value) {
this.handle.getDoubles().writeSafely(1, value);
}
/**
* Sets the value of field 'z'
*
* @param value New value for field 'z'
*/
public void setZ(double value) {
this.handle.getDoubles().write(2, value);
}
/**
* Sets the discrete rotation around the y-axis (yaw)
*
* @param value New value for field 'yRot'
*/
public void setYRotRaw(byte value) {
this.handle.getBytes().writeSafely(0, value);
}
/**
* Sets the discrete rotation around the x-axis (pitch)
*
* @param value New value for field 'xRot'
*/
public void setXRotRaw(byte value) {
this.handle.getBytes().writeSafely(1, value);
}
public void setLocation(@Nonnull Location location) {
setX(location.getX());
setY(location.getY());
setZ(location.getZ());
setYRotRaw(degreesToAngle(location.getYaw()));
setXRotRaw(degreesToAngle(location.getPitch()));
}
private byte degreesToAngle(float degree) {
return (byte)((int)(degree * 256.0F / 360.0F));
}
}

View file

@ -1,37 +0,0 @@
package xyz.ineanto.nicko.packet.wrapper;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import java.util.List;
import java.util.Set;
/**
* Up-to-date version of the Wrapper class
* for the PlayerServerPlayerInfo.
*
* @author ineanto, based on work from dmulloy2 and Kristian S. Strangeland
*/
public class WrapperPlayerServerPlayerInfo extends AbstractPacket {
public static final PacketType TYPE = PacketType.Play.Server.PLAYER_INFO;
public WrapperPlayerServerPlayerInfo() {
super(new PacketContainer(TYPE), TYPE);
handle.getModifier().writeDefaults();
}
public void setActions(Set<EnumWrappers.PlayerInfoAction> value) {
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
handle.getPlayerInfoActions().writeSafely(0, value);
} else {
handle.getPlayerInfoAction().writeSafely(0, value.stream().iterator().next()); // Get the first Value.
}
}
public void setData(List<PlayerInfoData> value) {
handle.getPlayerInfoDataLists().writeSafely(1, value);
}
}

View file

@ -1,27 +0,0 @@
package xyz.ineanto.nicko.packet.wrapper;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import java.util.List;
import java.util.UUID;
/**
* Up-to-date version of the Wrapper class
* for the PlayerServerPlayerInfoRemove.
*
* @author ineanto, based on work from dmulloy2 and Kristian S. Strangeland
*/
public class WrapperPlayerServerPlayerInfoRemove extends AbstractPacket {
public static final PacketType TYPE = PacketType.Play.Server.PLAYER_INFO_REMOVE;
public WrapperPlayerServerPlayerInfoRemove() {
super(new PacketContainer(TYPE), TYPE);
handle.getModifier().writeDefaults();
}
public void setUUIDs(List<UUID> value) {
handle.getUUIDLists().writeSafely(0, value);
}
}