From 24f4ab7f37685f06f34b19bb0b55c54c4685e3b2 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Wed, 11 Jun 2025 15:35:27 +0200 Subject: [PATCH 1/3] Fix RPlayer not showing when same player is present Add DevLobby20 to LobbySystem build.gradle.kts --- LobbySystem/build.gradle.kts | 9 ++ .../src/de/steamwar/entity/RPlayer.java | 83 ++++++++++++++++++- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/LobbySystem/build.gradle.kts b/LobbySystem/build.gradle.kts index aa511a0c..8662545c 100644 --- a/LobbySystem/build.gradle.kts +++ b/LobbySystem/build.gradle.kts @@ -34,3 +34,12 @@ dependencies { compileOnly(libs.nms20) compileOnly(libs.worldedit15) } + +tasks.register("DevLobby20") { + group = "run" + description = "Run a 1.20 Dev Lobby" + dependsOn(":SpigotCore:shadowJar") + dependsOn(":LobbySystem:jar") + template = "Lobby20" + worldName = "Lobby" +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java index b4aaa3ee..6205f5a6 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java @@ -19,22 +19,38 @@ package de.steamwar.entity; -import de.steamwar.Reflection; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import de.steamwar.Reflection; import de.steamwar.core.BountifulWrapper; import de.steamwar.core.Core; import de.steamwar.core.FlatteningWrapper; import de.steamwar.core.ProtocolWrapper; import lombok.Getter; +import lombok.SneakyThrows; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Collectors; @Getter public class RPlayer extends REntity { @@ -61,17 +77,76 @@ public class RPlayer extends REntity { private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(skinPartsIndex(), Byte.class); + private final UUID actualUUID; private final String name; public RPlayer(REntityServer server, UUID uuid, String name, Location location) { - super(server, EntityType.PLAYER, uuid, location,0); + super(server, EntityType.PLAYER, UUID.randomUUID(), location,0); + this.actualUUID = uuid; this.name = name; server.addEntity(this); } + private static final Map skinData = new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > 100; + } + }; + + @SneakyThrows + public static Property fetchSkinData(UUID uuid) { + if (skinData.containsKey(uuid)) { + return skinData.get(uuid); + } + + String url = "https://sessionserver.mojang.com/session/minecraft/profile/" + uuid.toString().replace("-", "") + "?unsigned=false"; + + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setReadTimeout(5000); + connection.setConnectTimeout(5000); + connection.setRequestProperty("User-Agent", "SkinFetcher"); + + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + return null; + } + + InputStream is = connection.getInputStream(); + String json = new BufferedReader(new InputStreamReader(is)) + .lines().collect(Collectors.joining("\n")); + + JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); + JsonArray properties = obj.getAsJsonArray("properties"); + for (JsonElement propElement : properties) { + JsonObject prop = propElement.getAsJsonObject(); + if (prop.get("name").getAsString().equals("textures")) { + Property property = new Property( + prop.get("name").getAsString(), + prop.get("value").getAsString(), + prop.get("signature").getAsString() + ); + skinData.put(uuid, property); + return property; + } + } + + throw new IOException("Failed to fetch skin profile"); + } + + private GameProfile getGameProfile() { + Property property = fetchSkinData(actualUUID); + if (property != null) { + GameProfile gameProfile = new GameProfile(uuid, name); + gameProfile.getProperties().put("textures", property); + return gameProfile; + } else { + return new GameProfile(actualUUID, name); + } + } + @Override void list(Consumer packetSink) { - packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(uuid, name), GameMode.CREATIVE)); + packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, getGameProfile(), GameMode.CREATIVE)); } @Override @@ -88,7 +163,7 @@ public class RPlayer extends REntity { @Override void delist(Consumer packetSink) { - packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(uuid, name), GameMode.CREATIVE)); + packetSink.accept(ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, getGameProfile(), GameMode.CREATIVE)); } private static final Class namedSpawnPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket"); From 3d562cf74394b43696b69719f0416ff31e30a432 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Wed, 11 Jun 2025 17:05:47 +0200 Subject: [PATCH 2/3] Add skin cache to VelocityCore --- .../common/PlayerSkinRequestPacket.java | 36 +++++ .../common/PlayerSkinResponsePacket.java | 38 ++++++ .../src/de/steamwar/core/Promise.java | 47 +++++++ .../src/de/steamwar/entity/RPlayer.java | 70 ++-------- .../steamwar/network/CoreNetworkHandler.java | 11 ++ .../de/steamwar/network/NetworkSender.java | 43 +++++- .../steamwar/velocitycore/VelocityCore.java | 2 +- .../network/handlers/PlayerSkinHandler.java | 125 ++++++++++++++++++ 8 files changed, 310 insertions(+), 62 deletions(-) create mode 100644 CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinRequestPacket.java create mode 100644 CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinResponsePacket.java create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java create mode 100644 VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java diff --git a/CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinRequestPacket.java b/CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinRequestPacket.java new file mode 100644 index 00000000..4916e0f1 --- /dev/null +++ b/CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinRequestPacket.java @@ -0,0 +1,36 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.network.packets.common; + +import de.steamwar.network.packets.NetworkPacket; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.util.UUID; + +@AllArgsConstructor +@Getter +@ToString +public class PlayerSkinRequestPacket extends NetworkPacket { + + private static final long serialVersionUID = 277267302555671765L; + private UUID uuid; +} diff --git a/CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinResponsePacket.java b/CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinResponsePacket.java new file mode 100644 index 00000000..5c3767ca --- /dev/null +++ b/CommonCore/Network/src/de/steamwar/network/packets/common/PlayerSkinResponsePacket.java @@ -0,0 +1,38 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.network.packets.common; + +import de.steamwar.network.packets.NetworkPacket; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.util.UUID; + +@AllArgsConstructor +@Getter +@ToString +public class PlayerSkinResponsePacket extends NetworkPacket { + + private static final long serialVersionUID = 5792855362547625112L; + private UUID uuid; + private String skin; + private String signature; +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java new file mode 100644 index 00000000..393932f3 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java @@ -0,0 +1,47 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.core; + +import lombok.NoArgsConstructor; + +import java.util.concurrent.atomic.AtomicBoolean; + +@NoArgsConstructor +public class Promise { + + private AtomicBoolean hasValue = new AtomicBoolean(false); + private E value; + + public void setValue(E value) { + this.value = value; + hasValue.set(true); + } + + public E getValue() { + if (hasValue.get()) { + return value; + } + + while (hasValue.get()) { + Thread.yield(); + } + return value; + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java index 6205f5a6..5efdd3f9 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java @@ -19,38 +19,23 @@ package de.steamwar.entity; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import de.steamwar.Reflection; -import de.steamwar.core.BountifulWrapper; -import de.steamwar.core.Core; -import de.steamwar.core.FlatteningWrapper; -import de.steamwar.core.ProtocolWrapper; +import de.steamwar.core.*; +import de.steamwar.network.NetworkSender; +import de.steamwar.network.packets.common.PlayerSkinRequestPacket; import lombok.Getter; -import lombok.SneakyThrows; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; -import java.util.stream.Collectors; @Getter public class RPlayer extends REntity { @@ -87,54 +72,19 @@ public class RPlayer extends REntity { server.addEntity(this); } - private static final Map skinData = new LinkedHashMap() { + public static final Map> SKIN_DATA_PROMISES = new LinkedHashMap>() { @Override - protected boolean removeEldestEntry(Map.Entry eldest) { + protected boolean removeEldestEntry(Map.Entry> eldest) { return size() > 100; } }; - @SneakyThrows - public static Property fetchSkinData(UUID uuid) { - if (skinData.containsKey(uuid)) { - return skinData.get(uuid); - } - - String url = "https://sessionserver.mojang.com/session/minecraft/profile/" + uuid.toString().replace("-", "") + "?unsigned=false"; - - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - connection.setReadTimeout(5000); - connection.setConnectTimeout(5000); - connection.setRequestProperty("User-Agent", "SkinFetcher"); - - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - return null; - } - - InputStream is = connection.getInputStream(); - String json = new BufferedReader(new InputStreamReader(is)) - .lines().collect(Collectors.joining("\n")); - - JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); - JsonArray properties = obj.getAsJsonArray("properties"); - for (JsonElement propElement : properties) { - JsonObject prop = propElement.getAsJsonObject(); - if (prop.get("name").getAsString().equals("textures")) { - Property property = new Property( - prop.get("name").getAsString(), - prop.get("value").getAsString(), - prop.get("signature").getAsString() - ); - skinData.put(uuid, property); - return property; - } - } - - throw new IOException("Failed to fetch skin profile"); - } - private GameProfile getGameProfile() { - Property property = fetchSkinData(actualUUID); + Property property = SKIN_DATA_PROMISES.computeIfAbsent(uuid, __ -> { + Promise future = new Promise<>(); + NetworkSender.sendOrQueue(new PlayerSkinRequestPacket(uuid)); + return future; + }).getValue(); if (property != null) { GameProfile gameProfile = new GameProfile(uuid, name); gameProfile.getProperties().put("textures", property); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java index bf74682a..407e9e41 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java @@ -19,9 +19,13 @@ package de.steamwar.network; +import com.mojang.authlib.properties.Property; import de.steamwar.core.BountifulWrapper; +import de.steamwar.core.Promise; +import de.steamwar.entity.RPlayer; import de.steamwar.network.handlers.InventoryHandler; import de.steamwar.network.packets.PacketHandler; +import de.steamwar.network.packets.common.PlayerSkinResponsePacket; import de.steamwar.network.packets.server.*; import de.steamwar.sql.BauweltMember; import de.steamwar.sql.SteamwarUser; @@ -68,4 +72,11 @@ public class CoreNetworkHandler extends PacketHandler { public void handleLocaleChange(LocaleInvalidationPacket packet) { SteamwarUser.invalidate(packet.getPlayerId()); } + + @Handler + public void handlePlayerSkinResponse(PlayerSkinResponsePacket packet) { + Promise propertyPromise = RPlayer.SKIN_DATA_PROMISES.get(packet.getUuid()); + if (propertyPromise == null) return; + propertyPromise.setValue(new Property("textures", packet.getSkin(), packet.getSignature())); + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java index 10a387d8..2dda8b92 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java @@ -24,8 +24,49 @@ import de.steamwar.network.packets.NetworkPacket; import lombok.SneakyThrows; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; -public class NetworkSender { +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class NetworkSender implements Listener { + + private static AtomicInteger numberOfPlayers = new AtomicInteger(0); + private static List queued = new ArrayList<>(); + + static { + Bukkit.getPluginManager().registerEvents(new NetworkSender(), Core.getInstance()); + } + + private NetworkSender() { + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + numberOfPlayers.incrementAndGet(); + if (numberOfPlayers.get() > 1) return; + Bukkit.getScheduler().runTaskLater(Core.getInstance(), () -> { + queued.forEach(NetworkSender::send); + queued.clear(); + }, 1); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + numberOfPlayers.decrementAndGet(); + } + + public static void sendOrQueue(NetworkPacket packet) { + if (numberOfPlayers.get() > 0) { + send(packet); + } else { + queued.add(packet); + } + } public static void send(NetworkPacket packet) { Bukkit.getOnlinePlayers().stream().findAny().ifPresent(player -> send(packet, player)); diff --git a/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java b/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java index 517d3342..841b657d 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java +++ b/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java @@ -225,7 +225,7 @@ public class VelocityCore implements ReloadablePlugin { for(PacketHandler handler : new PacketHandler[] { new EloPlayerHandler(), new EloSchemHandler(), new ExecuteCommandHandler(), new FightInfoHandler(), - new ImALobbyHandler(), new InventoryCallbackHandler(), new PrepareSchemHandler() + new ImALobbyHandler(), new InventoryCallbackHandler(), new PrepareSchemHandler(), new PlayerSkinHandler() }) handler.register(); diff --git a/VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java b/VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java new file mode 100644 index 00000000..0baf15ba --- /dev/null +++ b/VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java @@ -0,0 +1,125 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.velocitycore.network.handlers; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.PostLoginEvent; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.util.GameProfile; +import de.steamwar.network.packets.PacketHandler; +import de.steamwar.network.packets.common.PlayerSkinRequestPacket; +import de.steamwar.network.packets.common.PlayerSkinResponsePacket; +import de.steamwar.persistent.Storage; +import de.steamwar.velocitycore.VelocityCore; +import de.steamwar.velocitycore.network.NetworkSender; +import de.steamwar.velocitycore.network.ServerMetaInfo; +import lombok.SneakyThrows; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class PlayerSkinHandler extends PacketHandler { + + private final int maxCacheSize = 1000; + + public PlayerSkinHandler() { + VelocityCore.getProxy().getEventManager().register(VelocityCore.get(), this); + } + + private Map skins = new LinkedHashMap<>() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > maxCacheSize; + } + }; + private Map signatures = new LinkedHashMap<>() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > maxCacheSize; + } + }; + + @Handler + @SneakyThrows + public void handle(PlayerSkinRequestPacket packet) { + if (skins.containsKey(packet.getUuid()) && signatures.containsKey(packet.getUuid())) { + NetworkSender.send(((ServerMetaInfo) packet.getMetaInfos()).sender().getServer(), new PlayerSkinResponsePacket(packet.getUuid(), skins.get(packet.getUuid()), signatures.get(packet.getUuid()))); + return; + } + + String url = "https://sessionserver.mojang.com/session/minecraft/profile/" + packet.getUuid().toString().replace("-", "") + "?unsigned=false"; + + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setReadTimeout(5000); + connection.setConnectTimeout(5000); + connection.setRequestProperty("User-Agent", "SkinFetcher"); + + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + return; + } + + InputStream is = connection.getInputStream(); + String json = new BufferedReader(new InputStreamReader(is)) + .lines().collect(Collectors.joining("\n")); + + JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); + JsonArray properties = obj.getAsJsonArray("properties"); + for (JsonElement propElement : properties) { + JsonObject prop = propElement.getAsJsonObject(); + if (prop.get("name").getAsString().equals("textures")) { + String skin = prop.get("value").getAsString(); + String signature = prop.get("signature").getAsString(); + skins.put(packet.getUuid(), skin); + signatures.put(packet.getUuid(), signature); + NetworkSender.send(((ServerMetaInfo) packet.getMetaInfos()).sender().getServer(), new PlayerSkinResponsePacket(packet.getUuid(), skin, signature)); + return; + } + } + } + + @Subscribe + public void onPostLogin(PostLoginEvent event) { + Player player = event.getPlayer(); + GameProfile gameProfile = player.getGameProfile(); + GameProfile.Property property = gameProfile.getProperties().stream().filter(p -> p.getName().equals("textures")).findFirst().orElse(null); + if (property == null) return; + skins.put(player.getUniqueId(), property.getValue()); + signatures.put(player.getUniqueId(), property.getSignature()); + + Set uuidSet = skins.keySet(); + VelocityCore.getProxy().getAllServers().forEach(server -> { + for (UUID uuid : uuidSet) { + NetworkSender.send(server, new PlayerSkinResponsePacket(uuid, skins.get(uuid), signatures.get(uuid))); + } + }); + } +} From c3a4e7ed858081afd94902bfaf1b6ccde2c03fb2 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 12 Jun 2025 21:28:49 +0200 Subject: [PATCH 3/3] Improve network code --- .../src/de/steamwar/core/Promise.java | 47 ------------------- .../src/de/steamwar/entity/RPlayer.java | 20 ++++---- .../steamwar/network/CoreNetworkHandler.java | 7 ++- .../de/steamwar/network/NetworkSender.java | 15 ++---- .../network/handlers/PlayerSkinHandler.java | 28 +++++------ 5 files changed, 30 insertions(+), 87 deletions(-) delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java deleted file mode 100644 index 393932f3..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Promise.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2020 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.core; - -import lombok.NoArgsConstructor; - -import java.util.concurrent.atomic.AtomicBoolean; - -@NoArgsConstructor -public class Promise { - - private AtomicBoolean hasValue = new AtomicBoolean(false); - private E value; - - public void setValue(E value) { - this.value = value; - hasValue.set(true); - } - - public E getValue() { - if (hasValue.get()) { - return value; - } - - while (hasValue.get()) { - Thread.yield(); - } - return value; - } -} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java index 5efdd3f9..2575a778 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java @@ -22,7 +22,10 @@ package de.steamwar.entity; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import de.steamwar.Reflection; -import de.steamwar.core.*; +import de.steamwar.core.BountifulWrapper; +import de.steamwar.core.Core; +import de.steamwar.core.FlatteningWrapper; +import de.steamwar.core.ProtocolWrapper; import de.steamwar.network.NetworkSender; import de.steamwar.network.packets.common.PlayerSkinRequestPacket; import lombok.Getter; @@ -72,22 +75,21 @@ public class RPlayer extends REntity { server.addEntity(this); } - public static final Map> SKIN_DATA_PROMISES = new LinkedHashMap>() { + public static final Map SKIN_DATA_PROMISES = new LinkedHashMap() { @Override - protected boolean removeEldestEntry(Map.Entry> eldest) { + protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 100; } }; private GameProfile getGameProfile() { - Property property = SKIN_DATA_PROMISES.computeIfAbsent(uuid, __ -> { - Promise future = new Promise<>(); + Property skinData = SKIN_DATA_PROMISES.computeIfAbsent(uuid, __ -> { NetworkSender.sendOrQueue(new PlayerSkinRequestPacket(uuid)); - return future; - }).getValue(); - if (property != null) { + return new Property("textures", null, null); + }); + if (skinData.getValue() != null) { GameProfile gameProfile = new GameProfile(uuid, name); - gameProfile.getProperties().put("textures", property); + gameProfile.getProperties().put("textures", skinData); return gameProfile; } else { return new GameProfile(actualUUID, name); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java index 407e9e41..50ee9fcb 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/CoreNetworkHandler.java @@ -21,7 +21,6 @@ package de.steamwar.network; import com.mojang.authlib.properties.Property; import de.steamwar.core.BountifulWrapper; -import de.steamwar.core.Promise; import de.steamwar.entity.RPlayer; import de.steamwar.network.handlers.InventoryHandler; import de.steamwar.network.packets.PacketHandler; @@ -75,8 +74,8 @@ public class CoreNetworkHandler extends PacketHandler { @Handler public void handlePlayerSkinResponse(PlayerSkinResponsePacket packet) { - Promise propertyPromise = RPlayer.SKIN_DATA_PROMISES.get(packet.getUuid()); - if (propertyPromise == null) return; - propertyPromise.setValue(new Property("textures", packet.getSkin(), packet.getSignature())); + Property property = RPlayer.SKIN_DATA_PROMISES.get(packet.getUuid()); + if (property == null) return; + RPlayer.SKIN_DATA_PROMISES.put(packet.getUuid(), new Property("textures", packet.getSkin(), packet.getSignature())); } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java index 2dda8b92..8000ff23 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/network/NetworkSender.java @@ -27,15 +27,12 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; public class NetworkSender implements Listener { - private static AtomicInteger numberOfPlayers = new AtomicInteger(0); private static List queued = new ArrayList<>(); static { @@ -47,21 +44,17 @@ public class NetworkSender implements Listener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { - numberOfPlayers.incrementAndGet(); - if (numberOfPlayers.get() > 1) return; + if (!Bukkit.getOnlinePlayers().isEmpty()) { + return; + } Bukkit.getScheduler().runTaskLater(Core.getInstance(), () -> { queued.forEach(NetworkSender::send); queued.clear(); }, 1); } - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - numberOfPlayers.decrementAndGet(); - } - public static void sendOrQueue(NetworkPacket packet) { - if (numberOfPlayers.get() > 0) { + if (!Bukkit.getOnlinePlayers().isEmpty()) { send(packet); } else { queued.add(packet); diff --git a/VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java b/VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java index 0baf15ba..0515212e 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java +++ b/VelocityCore/src/de/steamwar/velocitycore/network/handlers/PlayerSkinHandler.java @@ -30,10 +30,11 @@ import com.velocitypowered.api.util.GameProfile; import de.steamwar.network.packets.PacketHandler; import de.steamwar.network.packets.common.PlayerSkinRequestPacket; import de.steamwar.network.packets.common.PlayerSkinResponsePacket; -import de.steamwar.persistent.Storage; import de.steamwar.velocitycore.VelocityCore; import de.steamwar.velocitycore.network.NetworkSender; import de.steamwar.velocitycore.network.ServerMetaInfo; +import lombok.AllArgsConstructor; +import lombok.Data; import lombok.SneakyThrows; import java.io.BufferedReader; @@ -55,15 +56,9 @@ public class PlayerSkinHandler extends PacketHandler { VelocityCore.getProxy().getEventManager().register(VelocityCore.get(), this); } - private Map skins = new LinkedHashMap<>() { + private Map skins = new LinkedHashMap<>() { @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > maxCacheSize; - } - }; - private Map signatures = new LinkedHashMap<>() { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { + protected boolean removeEldestEntry(Map.Entry eldest) { return size() > maxCacheSize; } }; @@ -71,8 +66,9 @@ public class PlayerSkinHandler extends PacketHandler { @Handler @SneakyThrows public void handle(PlayerSkinRequestPacket packet) { - if (skins.containsKey(packet.getUuid()) && signatures.containsKey(packet.getUuid())) { - NetworkSender.send(((ServerMetaInfo) packet.getMetaInfos()).sender().getServer(), new PlayerSkinResponsePacket(packet.getUuid(), skins.get(packet.getUuid()), signatures.get(packet.getUuid()))); + if (skins.containsKey(packet.getUuid())) { + SkinData skinData = skins.get(packet.getUuid()); + NetworkSender.send(((ServerMetaInfo) packet.getMetaInfos()).sender().getServer(), new PlayerSkinResponsePacket(packet.getUuid(), skinData.skin, skinData.signature)); return; } @@ -98,8 +94,7 @@ public class PlayerSkinHandler extends PacketHandler { if (prop.get("name").getAsString().equals("textures")) { String skin = prop.get("value").getAsString(); String signature = prop.get("signature").getAsString(); - skins.put(packet.getUuid(), skin); - signatures.put(packet.getUuid(), signature); + skins.put(packet.getUuid(), new SkinData(skin, signature)); NetworkSender.send(((ServerMetaInfo) packet.getMetaInfos()).sender().getServer(), new PlayerSkinResponsePacket(packet.getUuid(), skin, signature)); return; } @@ -112,14 +107,15 @@ public class PlayerSkinHandler extends PacketHandler { GameProfile gameProfile = player.getGameProfile(); GameProfile.Property property = gameProfile.getProperties().stream().filter(p -> p.getName().equals("textures")).findFirst().orElse(null); if (property == null) return; - skins.put(player.getUniqueId(), property.getValue()); - signatures.put(player.getUniqueId(), property.getSignature()); + skins.put(player.getUniqueId(), new SkinData(property.getValue(), property.getSignature())); Set uuidSet = skins.keySet(); VelocityCore.getProxy().getAllServers().forEach(server -> { for (UUID uuid : uuidSet) { - NetworkSender.send(server, new PlayerSkinResponsePacket(uuid, skins.get(uuid), signatures.get(uuid))); + NetworkSender.send(server, new PlayerSkinResponsePacket(uuid, property.getValue(), property.getSignature())); } }); } + + public record SkinData(String skin, String signature) {} }