diff --git a/Spigot-API-Patches/0197-Add-Player-Client-Options-API.patch b/Spigot-API-Patches/0197-Add-Player-Client-Options-API.patch new file mode 100644 index 0000000000..5b98894115 --- /dev/null +++ b/Spigot-API-Patches/0197-Add-Player-Client-Options-API.patch @@ -0,0 +1,205 @@ +From fae45f9c98f07be9e341c9a04d47d885d5b503c7 Mon Sep 17 00:00:00 2001 +From: MiniDigger +Date: Mon, 20 Jan 2020 21:38:34 +0100 +Subject: [PATCH] Add Player Client Options API + + +diff --git a/src/main/java/com/destroystokyo/paper/ClientOption.java b/src/main/java/com/destroystokyo/paper/ClientOption.java +new file mode 100644 +index 00000000..9dad814c +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/ClientOption.java +@@ -0,0 +1,33 @@ ++package com.destroystokyo.paper; ++ ++import org.jetbrains.annotations.NotNull; ++ ++import org.bukkit.inventory.MainHand; ++ ++public final class ClientOption { ++ ++ public static final ClientOption SKIN_PARTS = new ClientOption<>(SkinParts.class); ++ public static final ClientOption CHAT_COLORS_ENABLED = new ClientOption<>(Boolean.class); ++ public static final ClientOption CHAT_VISIBILITY = new ClientOption<>(ChatVisibility.class); ++ public static final ClientOption LOCALE = new ClientOption<>(String.class); ++ public static final ClientOption MAIN_HAND = new ClientOption<>(MainHand.class); ++ public static final ClientOption VIEW_DISTANCE = new ClientOption<>(Integer.class); ++ ++ private final Class type; ++ ++ private ClientOption(@NotNull Class type) { ++ this.type = type; ++ } ++ ++ @NotNull ++ public Class getType() { ++ return type; ++ } ++ ++ public enum ChatVisibility { ++ FULL, ++ SYSTEM, ++ HIDDEN, ++ UNKNOWN ++ } ++} +diff --git a/src/main/java/com/destroystokyo/paper/SkinParts.java b/src/main/java/com/destroystokyo/paper/SkinParts.java +new file mode 100644 +index 00000000..4a0c3940 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/SkinParts.java +@@ -0,0 +1,20 @@ ++package com.destroystokyo.paper; ++ ++public interface SkinParts { ++ ++ boolean hasCapeEnabled(); ++ ++ boolean hasJacketEnabled(); ++ ++ boolean hasLeftSleeveEnabled(); ++ ++ boolean hasRightSleeveEnabled(); ++ ++ boolean hasLeftPantsEnabled(); ++ ++ boolean hasRightPantsEnabled(); ++ ++ boolean hasHatsEnabled(); ++ ++ int getRaw(); ++} +diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerClientOptionsChangeEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerClientOptionsChangeEvent.java +new file mode 100644 +index 00000000..f7f171c4 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerClientOptionsChangeEvent.java +@@ -0,0 +1,100 @@ ++package com.destroystokyo.paper.event.player; ++ ++import com.destroystokyo.paper.ClientOption; ++import com.destroystokyo.paper.ClientOption.ChatVisibility; ++import com.destroystokyo.paper.SkinParts; ++ ++import org.jetbrains.annotations.NotNull; ++ ++import org.bukkit.entity.Player; ++import org.bukkit.event.HandlerList; ++import org.bukkit.event.player.PlayerEvent; ++import org.bukkit.inventory.MainHand; ++ ++/** ++ * Called when the player changes his client settings ++ */ ++public class PlayerClientOptionsChangeEvent extends PlayerEvent { ++ ++ private static final HandlerList handlers = new HandlerList(); ++ ++ private final String locale; ++ private final int viewDistance; ++ private final ChatVisibility chatVisibility; ++ private final boolean chatColors; ++ private final SkinParts skinparts; ++ private final MainHand mainHand; ++ ++ public PlayerClientOptionsChangeEvent(@NotNull Player player, @NotNull String locale, int viewDistance, @NotNull ChatVisibility chatVisibility, boolean chatColors, @NotNull SkinParts skinParts, @NotNull MainHand mainHand) { ++ super(player); ++ this.locale = locale; ++ this.viewDistance = viewDistance; ++ this.chatVisibility = chatVisibility; ++ this.chatColors = chatColors; ++ this.skinparts = skinParts; ++ this.mainHand = mainHand; ++ } ++ ++ @NotNull ++ public String getLocale() { ++ return locale; ++ } ++ ++ public boolean hasLocaleChanged() { ++ return !locale.equals(player.getClientOption(ClientOption.LOCALE)); ++ } ++ ++ public int getViewDistance() { ++ return viewDistance; ++ } ++ ++ public boolean hasViewDistanceChanged() { ++ return viewDistance != player.getClientOption(ClientOption.VIEW_DISTANCE); ++ } ++ ++ @NotNull ++ public ChatVisibility getChatVisibility() { ++ return chatVisibility; ++ } ++ ++ public boolean hasChatVisibilityChanged() { ++ return chatVisibility != player.getClientOption(ClientOption.CHAT_VISIBILITY); ++ } ++ ++ public boolean hasChatColorsEnabled() { ++ return chatColors; ++ } ++ ++ public boolean hasChatColorsEnabledChanged() { ++ return chatColors != player.getClientOption(ClientOption.CHAT_COLORS_ENABLED); ++ } ++ ++ @NotNull ++ public SkinParts getSkinParts() { ++ return skinparts; ++ } ++ ++ public boolean hasSkinPartsChanged() { ++ return skinparts.getRaw() != player.getClientOption(ClientOption.SKIN_PARTS).getRaw(); ++ } ++ ++ @NotNull ++ public MainHand getMainHand() { ++ return mainHand; ++ } ++ ++ public boolean hasMainHandChanged() { ++ return mainHand != player.getClientOption(ClientOption.MAIN_HAND); ++ } ++ ++ @Override ++ @NotNull ++ public HandlerList getHandlers() { ++ return handlers; ++ } ++ ++ @NotNull ++ public static HandlerList getHandlerList() { ++ return handlers; ++ } ++} +diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java +index 3600b4c8..787bb144 100644 +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -1,6 +1,7 @@ + package org.bukkit.entity; + + import java.net.InetSocketAddress; ++import com.destroystokyo.paper.ClientOption; // Paper + import com.destroystokyo.paper.Title; // Paper + import com.destroystokyo.paper.profile.PlayerProfile; // Paper + import java.util.Date; // Paper +@@ -1723,6 +1724,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM + * Reset the cooldown counter to 0, effectively starting the cooldown period. + */ + void resetCooldown(); ++ ++ /** ++ * @return the client option value of the player ++ */ ++ @NotNull ++ T getClientOption(@NotNull ClientOption option); + // Paper end + + // Spigot start +-- +2.25.1 + diff --git a/Spigot-Server-Patches/0483-Implement-Player-Client-Options-API.patch b/Spigot-Server-Patches/0483-Implement-Player-Client-Options-API.patch new file mode 100644 index 0000000000..7a042ca3e2 --- /dev/null +++ b/Spigot-Server-Patches/0483-Implement-Player-Client-Options-API.patch @@ -0,0 +1,204 @@ +From a750c430e5f3beecb38bd4d76d8c7bc715ab99e1 Mon Sep 17 00:00:00 2001 +From: MiniDigger +Date: Mon, 20 Jan 2020 21:38:15 +0100 +Subject: [PATCH] Implement Player Client Options API + + +diff --git a/src/main/java/com/destroystokyo/paper/PaperSkinParts.java b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java +new file mode 100644 +index 000000000..b6f4400df +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java +@@ -0,0 +1,74 @@ ++package com.destroystokyo.paper; ++ ++import com.google.common.base.Objects; ++ ++import java.util.StringJoiner; ++ ++public class PaperSkinParts implements SkinParts { ++ ++ private final int raw; ++ ++ public PaperSkinParts(int raw) { ++ this.raw = raw; ++ } ++ ++ public boolean hasCapeEnabled() { ++ return (raw & 1) == 1; ++ } ++ ++ public boolean hasJacketEnabled() { ++ return (raw >> 1 & 1) == 1; ++ } ++ ++ public boolean hasLeftSleeveEnabled() { ++ return (raw >> 2 & 1) == 1; ++ } ++ ++ public boolean hasRightSleeveEnabled() { ++ return (raw >> 3 & 1) == 1; ++ } ++ ++ public boolean hasLeftPantsEnabled() { ++ return (raw >> 4 & 1) == 1; ++ } ++ ++ public boolean hasRightPantsEnabled() { ++ return (raw >> 5 & 1) == 1; ++ } ++ ++ public boolean hasHatsEnabled() { ++ return (raw >> 6 & 1) == 1; ++ } ++ ++ @Override ++ public int getRaw() { ++ return raw; ++ } ++ ++ @Override ++ public boolean equals(Object o) { ++ if (this == o) return true; ++ if (o == null || getClass() != o.getClass()) return false; ++ PaperSkinParts that = (PaperSkinParts) o; ++ return raw == that.raw; ++ } ++ ++ @Override ++ public int hashCode() { ++ return Objects.hashCode(raw); ++ } ++ ++ @Override ++ public String toString() { ++ return new StringJoiner(", ", PaperSkinParts.class.getSimpleName() + "[", "]") ++ .add("raw=" + raw) ++ .add("cape=" + hasCapeEnabled()) ++ .add("jacket=" + hasJacketEnabled()) ++ .add("leftSleeve=" + hasLeftSleeveEnabled()) ++ .add("rightSleeve=" + hasRightSleeveEnabled()) ++ .add("leftPants=" + hasLeftPantsEnabled()) ++ .add("rightPants=" + hasRightPantsEnabled()) ++ .add("hats=" + hasHatsEnabled()) ++ .toString(); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java +index 7afcde608..8a3f2b5e4 100644 +--- a/src/main/java/net/minecraft/server/EntityHuman.java ++++ b/src/main/java/net/minecraft/server/EntityHuman.java +@@ -35,7 +35,7 @@ public abstract class EntityHuman extends EntityLiving { + private static final Map b = ImmutableMap.builder().put(EntityPose.STANDING, EntityHuman.bp).put(EntityPose.SLEEPING, EntityHuman.ap).put(EntityPose.FALL_FLYING, EntitySize.b(0.6F, 0.6F)).put(EntityPose.SWIMMING, EntitySize.b(0.6F, 0.6F)).put(EntityPose.SPIN_ATTACK, EntitySize.b(0.6F, 0.6F)).put(EntityPose.CROUCHING, EntitySize.b(0.6F, 1.5F)).put(EntityPose.DYING, EntitySize.c(0.2F, 0.2F)).build(); + private static final DataWatcherObject c = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.c); + private static final DataWatcherObject d = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.b); +- protected static final DataWatcherObject bq = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.a); ++ protected static final DataWatcherObject bq = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.a); public static DataWatcherObject getSkinPartsWatcher() { return bq; } // Paper - OBFHELPER + protected static final DataWatcherObject br = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.a); + protected static final DataWatcherObject bs = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.p); + protected static final DataWatcherObject bt = DataWatcher.a(EntityHuman.class, DataWatcherRegistry.p); +diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java +index cd8df622f..202068314 100644 +--- a/src/main/java/net/minecraft/server/EntityPlayer.java ++++ b/src/main/java/net/minecraft/server/EntityPlayer.java +@@ -2,6 +2,7 @@ package net.minecraft.server; + + import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent; + import com.google.common.collect.Lists; ++import com.destroystokyo.paper.event.player.PlayerClientOptionsChangeEvent; // Paper + import com.mojang.authlib.GameProfile; + import com.mojang.datafixers.util.Either; + import io.netty.util.concurrent.Future; +@@ -59,7 +60,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + public int lastSentExp = -99999999; + public int invulnerableTicks = 60; + private EnumChatVisibility ch; +- private boolean ci = true; ++ private boolean ci = true; public boolean hasChatColorsEnabled() { return this.ci; } // Paper - OBFHELPER + private long cj = SystemUtils.getMonotonicMillis(); + private Entity spectatedEntity; private void setSpectatorTargetField(Entity e) { this.spectatedEntity = e; } // Paper - OBFHELPER + public boolean worldChangeInvuln; +@@ -1582,6 +1583,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + } + + public void a(PacketPlayInSettings packetplayinsettings) { ++ new PlayerClientOptionsChangeEvent(getBukkitEntity(), packetplayinsettings.getLocale(), packetplayinsettings.viewDistance, com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(packetplayinsettings.getChatVisibility().name()), packetplayinsettings.hasChatColorsEnabled(), new com.destroystokyo.paper.PaperSkinParts(packetplayinsettings.getSkinParts()), packetplayinsettings.getMainHand() == EnumMainHand.LEFT ? MainHand.LEFT : MainHand.RIGHT).callEvent(); // Paper - settings event + // CraftBukkit start + if (getMainHand() != packetplayinsettings.getMainHand()) { + PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(getBukkitEntity(), getMainHand() == EnumMainHand.LEFT ? MainHand.LEFT : MainHand.RIGHT); +diff --git a/src/main/java/net/minecraft/server/PacketPlayInSettings.java b/src/main/java/net/minecraft/server/PacketPlayInSettings.java +index 8faebf9ef..4da637138 100644 +--- a/src/main/java/net/minecraft/server/PacketPlayInSettings.java ++++ b/src/main/java/net/minecraft/server/PacketPlayInSettings.java +@@ -37,18 +37,22 @@ public class PacketPlayInSettings implements Packet { + packetlistenerplayin.a(this); + } + ++ public String getLocale() { return b(); } // Paper - OBFHELPER + public String b() { + return this.a; + } + ++ public EnumChatVisibility getChatVisibility() { return d(); } // Paper - OBFHELPER + public EnumChatVisibility d() { + return this.c; + } + ++ public boolean hasChatColorsEnabled() { return e(); } // Paper - OBFHELPER + public boolean e() { + return this.d; + } + ++ public int getSkinParts() { return f(); } // Paper - OBFHELPER + public int f() { + return this.e; + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 33cac5fcb..b76379a17 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1,5 +1,8 @@ + package org.bukkit.craftbukkit.entity; + ++import com.destroystokyo.paper.ClientOption.ChatVisibility; ++import com.destroystokyo.paper.PaperSkinParts; ++import com.destroystokyo.paper.ClientOption; + import com.destroystokyo.paper.Title; + import com.destroystokyo.paper.profile.CraftPlayerProfile; + import com.destroystokyo.paper.profile.PlayerProfile; +@@ -36,6 +39,7 @@ import net.minecraft.server.BlockPosition; + import net.minecraft.server.ChatComponentText; + import net.minecraft.server.Container; + import net.minecraft.server.Entity; ++import net.minecraft.server.EntityHuman; + import net.minecraft.server.EntityLiving; + import net.minecraft.server.EntityPlayer; + import net.minecraft.server.EnumChatFormat; +@@ -1969,6 +1973,24 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + public void setViewDistance(int viewDistance) { + throw new NotImplementedException("Per-Player View Distance APIs need further understanding to properly implement"); // TODO + } ++ ++ @Override ++ public T getClientOption(ClientOption type) { ++ if(ClientOption.SKIN_PARTS.equals(type)) { ++ return type.getType().cast(new PaperSkinParts(getHandle().getDataWatcher().get(EntityHuman.getSkinPartsWatcher()))); ++ } else if(ClientOption.CHAT_COLORS_ENABLED.equals(type)) { ++ return type.getType().cast(getHandle().hasChatColorsEnabled()); ++ } else if(ClientOption.CHAT_VISIBILITY.equals(type)) { ++ return type.getType().cast(getHandle().getChatFlags() == null ? ChatVisibility.UNKNOWN : ChatVisibility.valueOf(getHandle().getChatFlags().name())); ++ } else if(ClientOption.LOCALE.equals(type)) { ++ return type.getType().cast(getLocale()); ++ } else if(ClientOption.MAIN_HAND.equals(type)) { ++ return type.getType().cast(getMainHand()); ++ } else if(ClientOption.VIEW_DISTANCE.equals(type)) { ++ return type.getType().cast(getClientViewDistance()); ++ } ++ throw new RuntimeException("Unknown settings type"); ++ } + // Paper end + + // Spigot start +-- +2.25.1 +