2021-06-11 14:02:28 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2021-11-27 09:11:43 +01:00
From: MiniDigger <admin@benndorf.dev>
2021-06-11 14:02:28 +02:00
Date: Mon, 20 Jan 2020 21:38:15 +0100
Subject: [PATCH] Implement Player Client Options API
2022-11-20 00:53:20 +01:00
== AT ==
public net.minecraft.world.entity.player.Player DATA_PLAYER_MODE_CUSTOMISATION
2024-11-26 18:46:12 +01:00
public net.minecraft.server.level.ServerPlayer particleStatus
2021-06-11 14:02:28 +02:00
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 0000000000000000000000000000000000000000..b6f4400df3d8ec7e06a996de54f8cabba57885e1
--- /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/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
2024-11-26 18:46:12 +01:00
index 0c68c0a9ec9b353b353eff0c36af2993df5f59b3..eebf44c7124c4f48b6d48562a00633b1e8ff9b00 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
2024-10-23 16:04:01 +02:00
@@ -420,7 +420,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
2024-04-24 08:05:14 +02:00
this.stats = server.getPlayerList().getPlayerStats(this);
2023-10-14 20:32:00 +02:00
this.advancements = server.getPlayerList().getPlayerAdvancements(this);
2024-06-13 22:14:13 +02:00
// this.moveTo(this.adjustSpawnLocation(world, world.getSharedSpawnPos()).getBottomCenter(), 0.0F, 0.0F); // Paper - Don't move existing players to world spawn
2023-10-14 20:32:00 +02:00
- this.updateOptions(clientOptions);
+ this.updateOptionsNoEvents(clientOptions); // Paper - don't call options events on login
2024-04-24 08:05:14 +02:00
this.object = null;
2023-10-14 20:32:00 +02:00
2024-07-17 19:24:53 +02:00
// CraftBukkit start
2024-11-26 18:46:12 +01:00
@@ -2405,6 +2405,19 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
2022-10-27 01:09:03 +02:00
}
2023-09-22 05:29:51 +02:00
public void updateOptions(ClientInformation clientOptions) {
2024-11-26 18:46:12 +01:00
+ // Paper start - settings event
+ new com.destroystokyo.paper.event.player.PlayerClientOptionsChangeEvent(this.getBukkitEntity(), Util.make(new java.util.IdentityHashMap<>(), map -> {
+ map.put(com.destroystokyo.paper.ClientOption.LOCALE, clientOptions.language());
+ map.put(com.destroystokyo.paper.ClientOption.VIEW_DISTANCE, clientOptions.viewDistance());
+ map.put(com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY, com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(clientOptions.chatVisibility().name()));
+ map.put(com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED, clientOptions.chatColors());
+ map.put(com.destroystokyo.paper.ClientOption.SKIN_PARTS, new com.destroystokyo.paper.PaperSkinParts(clientOptions.modelCustomisation()));
+ map.put(com.destroystokyo.paper.ClientOption.MAIN_HAND, clientOptions.mainHand() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT);
+ map.put(com.destroystokyo.paper.ClientOption.TEXT_FILTERING_ENABLED, clientOptions.textFilteringEnabled());
+ map.put(com.destroystokyo.paper.ClientOption.ALLOW_SERVER_LISTINGS, clientOptions.allowsListing());
+ map.put(com.destroystokyo.paper.ClientOption.PARTICLE_VISIBILITY, com.destroystokyo.paper.ClientOption.ParticleVisibility.valueOf(clientOptions.particleStatus().name()));
+ })).callEvent();
+ // Paper end - settings event
2021-06-11 14:02:28 +02:00
// CraftBukkit start
2023-10-27 01:34:58 +02:00
if (this.getMainArm() != clientOptions.mainHand()) {
PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(this.getBukkitEntity(), this.getMainArm() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT);
2024-11-26 18:46:12 +01:00
@@ -2415,6 +2428,11 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
2024-06-14 23:07:44 +02:00
this.server.server.getPluginManager().callEvent(event);
2023-10-14 20:32:00 +02:00
}
// CraftBukkit end
+ // Paper start - don't call options events on login
2024-06-13 22:14:13 +02:00
+ this.updateOptionsNoEvents(clientOptions);
2023-10-14 20:32:00 +02:00
+ }
+ public void updateOptionsNoEvents(ClientInformation clientOptions) {
+ // Paper end
this.language = clientOptions.language();
2024-02-09 21:30:50 +01:00
this.adventure$locale = java.util.Objects.requireNonNullElse(net.kyori.adventure.translation.Translator.parseLocale(this.language), java.util.Locale.US); // Paper
2023-10-14 20:32:00 +02:00
this.requestedViewDistance = clientOptions.viewDistance();
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
2024-11-29 17:12:17 +01:00
index b3b13f1baea0b170fd4f1546689aad40f53d3c27..59e291d7552e20b960faa7d679cf69981458c139 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
2024-11-26 18:46:12 +01:00
@@ -658,6 +658,30 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
2022-09-26 10:02:51 +02:00
connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message);
}
2021-06-11 14:02:28 +02:00
}
+
+ @Override
2021-06-14 03:06:38 +02:00
+ public <T> T getClientOption(com.destroystokyo.paper.ClientOption<T> type) {
2022-10-27 01:09:03 +02:00
+ if (com.destroystokyo.paper.ClientOption.SKIN_PARTS == type) {
2024-11-26 18:46:12 +01:00
+ return type.getType().cast(new com.destroystokyo.paper.PaperSkinParts(this.getHandle().getEntityData().get(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION)));
2022-10-27 01:09:03 +02:00
+ } else if (com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED == type) {
2024-11-26 18:46:12 +01:00
+ return type.getType().cast(this.getHandle().canChatInColor());
2022-10-27 01:09:03 +02:00
+ } else if (com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY == type) {
2024-11-29 17:12:17 +01:00
+ return type.getType().cast(com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(this.getHandle().getChatVisibility().name()));
2022-10-27 01:09:03 +02:00
+ } else if (com.destroystokyo.paper.ClientOption.LOCALE == type) {
2024-11-26 18:46:12 +01:00
+ return type.getType().cast(this.getLocale());
2022-10-27 01:09:03 +02:00
+ } else if (com.destroystokyo.paper.ClientOption.MAIN_HAND == type) {
2024-11-26 18:46:12 +01:00
+ return type.getType().cast(this.getMainHand());
2022-10-27 01:09:03 +02:00
+ } else if (com.destroystokyo.paper.ClientOption.VIEW_DISTANCE == type) {
2024-11-26 18:46:12 +01:00
+ return type.getType().cast(this.getClientViewDistance());
2022-10-27 01:09:03 +02:00
+ } else if (com.destroystokyo.paper.ClientOption.TEXT_FILTERING_ENABLED == type) {
2024-11-26 18:46:12 +01:00
+ return type.getType().cast(this.getHandle().isTextFilteringEnabled());
+ } else if (com.destroystokyo.paper.ClientOption.ALLOW_SERVER_LISTINGS == type) {
+ return type.getType().cast(this.getHandle().allowsListing());
+ } else if (com.destroystokyo.paper.ClientOption.PARTICLE_VISIBILITY == type) {
+ return type.getType().cast(com.destroystokyo.paper.ClientOption.ParticleVisibility.valueOf(this.getHandle().particleStatus.name()));
2021-06-11 14:02:28 +02:00
+ }
+ throw new RuntimeException("Unknown settings type");
+ }
// Paper end
2021-06-14 03:06:38 +02:00
@Override
2021-08-14 06:11:12 +02:00
diff --git a/src/test/java/io/papermc/paper/world/TranslationKeyTest.java b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
new file mode 100644
2024-11-26 18:46:12 +01:00
index 0000000000000000000000000000000000000000..01e0936ea8ce5bcacafd9e89a1c0dfd2c172024d
2021-08-14 06:11:12 +02:00
--- /dev/null
+++ b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
2024-11-26 18:46:12 +01:00
@@ -0,0 +1,25 @@
2021-08-14 06:11:12 +02:00
+package io.papermc.paper.world;
+
+import com.destroystokyo.paper.ClientOption;
2024-11-26 18:46:12 +01:00
+import net.minecraft.server.level.ParticleStatus;
2021-08-14 06:11:12 +02:00
+import net.minecraft.world.entity.player.ChatVisiblity;
2023-09-24 09:16:58 +02:00
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
2021-08-14 06:11:12 +02:00
+
+public class TranslationKeyTest {
+
+ @Test
+ public void testChatVisibilityKeys() {
+ for (ClientOption.ChatVisibility chatVisibility : ClientOption.ChatVisibility.values()) {
+ if (chatVisibility == ClientOption.ChatVisibility.UNKNOWN) continue;
2023-09-25 01:05:05 +02:00
+ Assertions.assertEquals(ChatVisiblity.valueOf(chatVisibility.name()).getKey(), chatVisibility.translationKey(), chatVisibility + "'s translation key doesn't match");
2021-08-14 06:11:12 +02:00
+ }
+ }
2024-11-26 18:46:12 +01:00
+
+ @Test
+ public void testParticleVisibilityKeys() {
+ for (ClientOption.ParticleVisibility particleVisibility : ClientOption.ParticleVisibility.values()) {
+ Assertions.assertEquals(ParticleStatus.valueOf(particleVisibility.name()).getKey(), particleVisibility.translationKey(), particleVisibility + "'s translation key doesn't match");
+ }
+ }
2021-08-14 06:11:12 +02:00
+}