Compare commits
22 commits
master
...
packeteven
Author | SHA1 | Date | |
---|---|---|---|
794d560363 | |||
42f9476970 | |||
c20df8bc48 | |||
b7ac5862eb | |||
9b6e7e0f8e | |||
5f32720617 | |||
18a533667e | |||
d7663ab463 | |||
ae9b8143aa | |||
942c6e3609 | |||
1a499fe7cc | |||
a1e9684c08 | |||
3f697c3e23 | |||
1613a85461 | |||
1a45ca186a | |||
436fc91556 | |||
03a8eb9b7d | |||
deb76e0150 | |||
11b0f680bc | |||
cc61a89ec1 | |||
ab2ffcbd37 | |||
073d96bf99 |
21 changed files with 458 additions and 715 deletions
.gitignoreCHANGELOG.logREADME.mdbuild.gradle.kts
src/main
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -26,4 +26,7 @@ bin/
|
|||
!**/src/test/**/bin/
|
||||
|
||||
### Server ###
|
||||
run/
|
||||
run/
|
||||
|
||||
### Private ###
|
||||
TODO
|
|
@ -1,4 +1,7 @@
|
|||
1.3.0-RC1: Update n°13 (XX/XX/25)
|
||||
As of 1.3.0-RC1, Nicko will now run on every Minecraft version.
|
||||
However, I will NOT provide support if the plugin breaks.
|
||||
|
||||
[FEATURES]
|
||||
- Players are now able to mark disguises as favorites.
|
||||
|
||||
|
|
|
@ -12,10 +12,7 @@ https://www.spigotmc.org/resources/nicko.113868/
|
|||
|
||||
## Known bugs:
|
||||
|
||||
- Players who have operator (OP) status lose access to the Operator Items tab in creative mode
|
||||
after disguising **(1.20 and up)**.
|
||||
- When disguising and only changing their display name, players will have the new default
|
||||
skins **(1.20 and up)**.
|
||||
N/A
|
||||
|
||||
---
|
||||
|
||||
|
@ -25,5 +22,5 @@ https://www.spigotmc.org/resources/nicko.113868/
|
|||
|---------------|----------------------------------------------------------------------------|
|
||||
| 1.7 and lower | Unsupported |
|
||||
| 1.8 - 1.12.2 | Use [NickReloaded](https://www.spigotmc.org/resources/nickreloaded.46335/) |
|
||||
| 1.13 to 1.19 | Unsupported |
|
||||
| 1.20 - 1.21 | Use Nicko |
|
||||
| 1.13 to 1.19 | Use Nicko (without support) |
|
||||
| 1.20 - 1.21 | Use Nicko (with support) |
|
||||
|
|
|
@ -2,11 +2,10 @@ plugins {
|
|||
id("java")
|
||||
id("com.gradleup.shadow") version "8.3.2"
|
||||
id("xyz.jpenilla.run-paper") version "2.3.0"
|
||||
id("io.papermc.paperweight.userdev") version "2.0.0-beta.17"
|
||||
}
|
||||
|
||||
group = "xyz.ineanto"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
|
||||
val invuiVersion: String = "1.44"
|
||||
|
||||
|
@ -26,17 +25,19 @@ repositories {
|
|||
maven("https://repo.xenondevs.xyz/releases")
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
maven("https://repo.codemc.io/repository/maven-snapshots/")
|
||||
maven("https://repo.codemc.io/repository/maven-releases/")
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT")
|
||||
compileOnly("io.papermc.paper:paper-api:1.21.6-R0.1-SNAPSHOT")
|
||||
|
||||
compileOnly("me.clip:placeholderapi:2.11.5")
|
||||
compileOnly("net.kyori:adventure-api:4.21.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("com.github.retrooper:packetevents-spigot:2.8.0")
|
||||
|
||||
implementation("de.rapha149.signgui:signgui:2.5.0")
|
||||
implementation("com.github.jsixface:yamlconfig:1.2")
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
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 io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
@ -38,20 +40,28 @@ public class Nicko extends JavaPlugin {
|
|||
private PlayerNameStore nameStore;
|
||||
private RandomNameFetcher nameFetcher;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
|
||||
PacketEvents.getAPI().getSettings().checkForUpdates(false).kickOnPacketException(true);
|
||||
PacketEvents.getAPI().load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
plugin = this;
|
||||
|
||||
PacketEvents.getAPI().init();
|
||||
|
||||
configurationManager = new ConfigurationManager(getDataFolder());
|
||||
configurationManager.saveDefaultConfig();
|
||||
|
||||
dataStore = new PlayerDataStore(mojangAPI, getNickoConfig());
|
||||
|
||||
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
|
||||
getLogger().severe("This version (" + MinecraftVersion.getCurrentVersion().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)");
|
||||
dataStore.getStorage().setError(true);
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_20)) {
|
||||
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()) {
|
||||
|
|
|
@ -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() {
|
||||
|
@ -33,11 +33,16 @@ public class AppearanceManager {
|
|||
final PlayerResetDisguiseEvent event = new PlayerResetDisguiseEvent(player);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
|
||||
final String playerName = nameStore.getStoredName(player);
|
||||
profile.setName(playerName);
|
||||
profile.setSkin(playerName);
|
||||
final ActionResult resetSkinUpdate = update(true);
|
||||
|
||||
profile.setName(null);
|
||||
profile.setSkin(null);
|
||||
dataStore.getCache().cache(player.getUniqueId(), profile);
|
||||
|
||||
return ActionResult.error();
|
||||
return resetSkinUpdate;
|
||||
}
|
||||
|
||||
public ActionResult update(boolean skinChange) {
|
||||
|
@ -50,19 +55,38 @@ public class AppearanceManager {
|
|||
final ActionResult propertiesUpdateResult = packetSender.updatePlayerProfileProperties();
|
||||
|
||||
if (propertiesUpdateResult.isError()) {
|
||||
return reset();
|
||||
return resetWithoutUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// Not needed, but still broken using PE.
|
||||
//packetSender.sendPlayerRespawn();
|
||||
|
||||
// Call the event.
|
||||
final PlayerDisguiseEvent event = new PlayerDisguiseEvent(player, profile.getSkin(), profile.getName());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
|
||||
packetSender.sendEntityMetadataUpdate();
|
||||
packetSender.sendTabListUpdate(displayName);
|
||||
|
||||
packetSender.sendEntityRespawn();
|
||||
return result;
|
||||
}
|
||||
|
||||
private ActionResult resetWithoutUpdate() {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
|
||||
// Call the event.
|
||||
final PlayerResetDisguiseEvent event = new PlayerResetDisguiseEvent(player);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
|
||||
profile.setName(null);
|
||||
profile.setSkin(null);
|
||||
dataStore.getCache().cache(player.getUniqueId(), profile);
|
||||
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
private NickoProfile getNickoProfile() {
|
||||
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
|
||||
return optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone());
|
||||
|
|
|
@ -47,7 +47,7 @@ public class FavoritesGUI {
|
|||
items = Collections.emptyList();
|
||||
} else {
|
||||
items = favorites.stream()
|
||||
.map((appearance) -> new FavoriteAppearanceEntryItem(player, appearance).get())
|
||||
.map((appearance) -> new FavoriteAppearanceEntryItem(playerLanguage, appearance))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
|
|
@ -2,34 +2,34 @@ package xyz.ineanto.nicko.gui.items.favorites;
|
|||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.appearance.Appearance;
|
||||
import xyz.ineanto.nicko.appearance.AppearanceManager;
|
||||
import xyz.ineanto.nicko.gui.items.ItemDefaults;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.AsyncItem;
|
||||
import xyz.xenondevs.invui.util.MojangApiUtils;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
import java.io.IOException;
|
||||
public class FavoriteAppearanceEntryItem extends AsyncItem {
|
||||
private final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
|
||||
|
||||
public class FavoriteAppearanceEntryItem {
|
||||
private final AppearanceManager appearanceManager;
|
||||
private final PlayerLanguage playerLanguage;
|
||||
private final Appearance appearance;
|
||||
|
||||
public FavoriteAppearanceEntryItem(Player player, Appearance appearance) {
|
||||
this.appearanceManager = new AppearanceManager(player);
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
this.appearance = appearance;
|
||||
}
|
||||
|
||||
public AsyncItem get() {
|
||||
// TODO (Ineanto, 26/06/2025): handle click
|
||||
final ItemBuilder temporaryItemBuilder = new ItemBuilder(Material.PAINTING);
|
||||
return new AsyncItem(playerLanguage.translateItem(temporaryItemBuilder, LanguageKey.GUI.LOADING),
|
||||
public FavoriteAppearanceEntryItem(PlayerLanguage playerLanguage, Appearance appearance) {
|
||||
super(new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.PAINTING);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.LOADING);
|
||||
}, (_ -> true)).getItemProvider(),
|
||||
() -> {
|
||||
try {
|
||||
// TODO (Ineanto, 08/06/2025): set a default skin if the entry contains only a name
|
||||
|
@ -37,10 +37,38 @@ public class FavoriteAppearanceEntryItem {
|
|||
final String skin = (appearance.skin() == null ? "N/A" : appearance.skin());
|
||||
final SkullBuilder skull = new SkullBuilder(skin);
|
||||
return playerLanguage.translateItem(skull, LanguageKey.GUI.Favorites.ENTRY, name, skin);
|
||||
} catch (MojangApiUtils.MojangApiException | IOException e) {
|
||||
} catch (Exception e) {
|
||||
Nicko.getInstance().getLogger().warning("Unable to get Head texture for specified UUID (" + appearance.skin() + ")! (GUI/Favorites/Entry)");
|
||||
return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Favorites.ENTRY, "N/A", "N/A");
|
||||
}
|
||||
});
|
||||
this.playerLanguage = playerLanguage;
|
||||
this.appearance = appearance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) {
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
event.getView().close();
|
||||
final NickoProfile profile = dataStore.getData(player.getUniqueId()).orElse(NickoProfile.EMPTY_PROFILE);
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(player);
|
||||
|
||||
profile.setName(appearance.name());
|
||||
profile.setSkin(appearance.skin());
|
||||
dataStore.updateCache(player.getUniqueId(), profile);
|
||||
|
||||
final ActionResult result = appearanceManager.update(true);
|
||||
if (!result.isError()) {
|
||||
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Set.OK));
|
||||
|
||||
} else {
|
||||
player.sendMessage(playerLanguage.translateWithOops(
|
||||
LanguageKey.Event.Appearance.Set.ERROR,
|
||||
result.getErrorKey()
|
||||
)
|
||||
);
|
||||
appearanceManager.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public class RandomSkinItem {
|
|||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.WIND_CHARGE);
|
||||
final ItemBuilder builder = new ItemBuilder(Material.COBWEB);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.RANDOM_SKIN);
|
||||
}, (event) -> {
|
||||
final Player player = event.getPlayer();
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.bukkit.Material;
|
|||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.appearance.AppearanceManager;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
|
@ -37,10 +38,11 @@ public class ResetItem {
|
|||
}
|
||||
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(player);
|
||||
if (!appearanceManager.reset().isError()) {
|
||||
final ActionResult reset = appearanceManager.reset();
|
||||
if (!reset.isError()) {
|
||||
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Remove.OK));
|
||||
} else {
|
||||
player.sendMessage(playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Remove.ERROR));
|
||||
player.sendMessage(playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Remove.ERROR, reset.getErrorKey()));
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_VILLAGER_NO, 1, 1f);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
package xyz.ineanto.nicko.packet;
|
||||
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
|
||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.github.retrooper.packetevents.protocol.player.TextureProperty;
|
||||
import com.github.retrooper.packetevents.protocol.player.UserProfile;
|
||||
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.*;
|
||||
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
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.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;
|
||||
|
||||
public record PacketEventsPacketSender(Player player, NickoProfile profile) implements PacketSender {
|
||||
@Override
|
||||
public void sendEntityRespawn() {
|
||||
if (!profile.hasData()) return;
|
||||
|
||||
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 -> {
|
||||
sendPacket(destroy, player);
|
||||
sendPacket(spawn, player);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult updatePlayerProfile(String name) {
|
||||
final PlayerProfile previousProfile = player.getPlayerProfile();
|
||||
final PlayerProfile newProfile = Bukkit.getServer().createProfile(player.getUniqueId(), name);
|
||||
|
||||
// Copy previous properties to preserve skin
|
||||
newProfile.setProperties(previousProfile.getProperties());
|
||||
player.setPlayerProfile(newProfile);
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult updatePlayerProfileProperties() {
|
||||
final PlayerProfile playerProfile = player.getPlayerProfile();
|
||||
|
||||
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() {
|
||||
// This was surprisingly easy to write?
|
||||
final EntityData<Byte> entityData = new EntityData<>(17, EntityDataTypes.BYTE, (byte) 0x7f);
|
||||
final WrapperPlayServerEntityMetadata metadata = new WrapperPlayServerEntityMetadata(player.getEntityId(), List.of(entityData));
|
||||
|
||||
sendPacket(metadata, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlayerRespawn() {
|
||||
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
|
||||
public void sendTabListUpdate(String displayName) {
|
||||
final EnumSet<WrapperPlayServerPlayerInfoUpdate.Action> actions = EnumSet.of(
|
||||
WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER,
|
||||
WrapperPlayServerPlayerInfoUpdate.Action.INITIALIZE_CHAT,
|
||||
WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_LISTED,
|
||||
WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_DISPLAY_NAME,
|
||||
WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_GAME_MODE,
|
||||
WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_LATENCY
|
||||
);
|
||||
|
||||
final List<WrapperPlayServerPlayerInfoUpdate.PlayerInfo> entries = List.of(
|
||||
new WrapperPlayServerPlayerInfoUpdate.PlayerInfo(
|
||||
toUserProfile(player.getPlayerProfile()),
|
||||
true,
|
||||
player.getPing(),
|
||||
SpigotConversionUtil.fromBukkitGameMode(player.getGameMode()),
|
||||
Component.text(displayName),
|
||||
null, // Welcome back fucked chat encryption
|
||||
player.getPlayerListOrder(),
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
final WrapperPlayServerPlayerInfoRemove remove = new WrapperPlayServerPlayerInfoRemove(player.getUniqueId());
|
||||
final WrapperPlayServerPlayerInfoUpdate update = new WrapperPlayServerPlayerInfoUpdate(actions, entries);
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(onlinePlayer -> {
|
||||
sendPacket(remove, onlinePlayer);
|
||||
sendPacket(update, onlinePlayer);
|
||||
});
|
||||
}
|
||||
|
||||
private UserProfile toUserProfile(PlayerProfile playerProfile) {
|
||||
return new UserProfile(
|
||||
playerProfile.getId(),
|
||||
playerProfile.getName(),
|
||||
playerProfile.getProperties()
|
||||
.stream()
|
||||
.map(profileProperty -> new TextureProperty(
|
||||
profileProperty.getName(),
|
||||
profileProperty.getValue(),
|
||||
profileProperty.getSignature()
|
||||
))
|
||||
.toList()
|
||||
);
|
||||
}
|
||||
|
||||
private void sendPacket(PacketWrapper<?> packet, Player player) {
|
||||
PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
//}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -169,7 +169,7 @@ gui:
|
|||
- "<grey>Clicking on any disguise as this mode is</grey>"
|
||||
- "<grey>active will remove it from your favorites.</grey>"
|
||||
entry:
|
||||
name: "Favorite"
|
||||
name: "<gold>Favorite</gold>"
|
||||
lore:
|
||||
- "<gray>Name:</gray> <green>{0}</green>"
|
||||
- "<gray>Skin:</gray> <green>{1}</green>"
|
||||
- "<gray>Name:</gray> <yellow>{0}</yellow>"
|
||||
- "<gray>Skin:</gray> <yellow>{1}</yellow>"
|
|
@ -26,7 +26,7 @@ event:
|
|||
error: "<gray>Impossible d''appliquer le précédent déguisement ! ({0})</gray>"
|
||||
ok: "<gray>Votre précédent déguisement a été appliqué.<gray>"
|
||||
remove:
|
||||
error: "<gray>Impossible de retirer votre déguisement.</gray>"
|
||||
error: "<gray>Impossible de retirer votre déguisement ({0}).</gray>"
|
||||
missing: "<gray>Vous n''avez pas de déguisement.</gray>"
|
||||
ok: "<gray>Déguisement retiré.</gray>"
|
||||
admin:
|
||||
|
@ -171,7 +171,7 @@ gui:
|
|||
- "<grey>Cliquer sur un déguisement lorsque le mode est</grey>"
|
||||
- "<grey>actif le supprimera de vos favoris.</grey>"
|
||||
entry:
|
||||
name: "Favori"
|
||||
name: "<gold>Favori</gold>"
|
||||
lore:
|
||||
- "<gray>Nom:</gray> <green>{0}</green>"
|
||||
- "<gray>Skin:</gray> <green>{1}</green>"
|
||||
- "<gray>Nom:</gray> <yellow>{0}</yellow>"
|
||||
- "<gray>Skin:</gray> <yellow>{1}</yellow>"
|
Loading…
Add table
Add a link
Reference in a new issue