ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/ping/ServerPingRecord.java

301 lines
8.0 KiB
Java
Raw Normal View History

2023-03-23 19:43:22 +01:00
package com.comphenix.protocol.wrappers.ping;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Semaphore;
2023-03-23 19:43:22 +01:00
import com.comphenix.protocol.events.InternalStructure;
import com.comphenix.protocol.reflect.EquivalentConverter;
2023-03-23 19:43:22 +01:00
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.*;
import com.google.common.collect.ImmutableList;
2023-03-23 19:43:22 +01:00
import org.bukkit.Bukkit;
public final class ServerPingRecord implements ServerPingImpl {
2023-03-23 19:43:22 +01:00
private static Class<?> SERVER_PING;
private static Class<?> PLAYER_SAMPLE_CLASS;
private static Class<?> SERVER_DATA_CLASS;
private static WrappedChatComponent DEFAULT_DESCRIPTION;
private static ConstructorAccessor PING_CTOR;
private static EquivalentConverter<List<WrappedGameProfile>> PROFILE_LIST_CONVERTER;
private static boolean initialized = false;
private static final Object lock = new Object();
2023-03-23 19:43:22 +01:00
private static void initialize() {
if (initialized) {
return;
}
synchronized (lock) {
// may have been initialized while waiting for the lock
if (initialized) {
return;
}
2023-03-23 19:43:22 +01:00
try {
SERVER_PING = MinecraftReflection.getServerPingClass();
PLAYER_SAMPLE_CLASS = MinecraftReflection.getServerPingPlayerSampleClass();
SERVER_DATA_CLASS = MinecraftReflection.getServerPingServerDataClass();
2023-03-23 19:43:22 +01:00
PING_CTOR = Accessors.getConstructorAccessor(SERVER_PING.getConstructors()[0]);
2023-03-23 19:43:22 +01:00
DATA_WRAPPER = AutoWrapper.wrap(ServerData.class, SERVER_DATA_CLASS);
SAMPLE_WRAPPER = AutoWrapper.wrap(PlayerSample.class, PLAYER_SAMPLE_CLASS);
FAVICON_WRAPPER = AutoWrapper.wrap(Favicon.class, MinecraftReflection.getMinecraftClass("network.protocol.status.ServerPing$a"));
2023-03-23 19:43:22 +01:00
PROFILE_LIST_CONVERTER = BukkitConverters.getListConverter(BukkitConverters.getWrappedGameProfileConverter());
DEFAULT_DESCRIPTION = WrappedChatComponent.fromLegacyText("A Minecraft Server");
} catch (Exception ex) {
throw new RuntimeException("Failed to initialize Server Ping", ex);
} finally {
initialized = true;
}
2023-03-23 19:43:22 +01:00
}
}
public static final class PlayerSample {
2023-03-23 19:43:22 +01:00
public int max;
public int online;
public Object sample;
public PlayerSample(int max, int online, Object sample) {
this.max = max;
this.online = online;
this.sample = sample;
}
public PlayerSample() {
this(0, 0, null);
}
2023-03-23 19:43:22 +01:00
}
public static final class ServerData {
2023-03-23 19:43:22 +01:00
public String name;
public int protocol;
public ServerData(String name, int protocol) {
this.name = name;
this.protocol = protocol;
}
public ServerData() {
this("", 0);
}
2023-03-23 19:43:22 +01:00
}
static final byte[] EMPTY_FAVICON = new byte[0];
public static final class Favicon {
2023-03-23 19:43:22 +01:00
public byte[] iconBytes;
public Favicon(byte[] iconBytes) {
this.iconBytes = iconBytes;
}
public Favicon() {
this(EMPTY_FAVICON);
}
2023-03-23 19:43:22 +01:00
}
private static AutoWrapper<PlayerSample> SAMPLE_WRAPPER;
private static AutoWrapper<ServerData> DATA_WRAPPER;
private static AutoWrapper<Favicon> FAVICON_WRAPPER;
2023-03-26 20:58:46 +02:00
private WrappedChatComponent description;
2023-03-23 19:43:22 +01:00
private PlayerSample playerSample;
private ServerData serverData;
private Favicon favicon;
private boolean enforceSafeChat;
private boolean playersVisible = true;
private static ServerData defaultData() {
String name = MinecraftVersion.getCurrentVersion().toString();
int protocol = MinecraftProtocolVersion.getCurrentVersion();
return new ServerData(name, protocol);
2023-03-23 19:43:22 +01:00
}
private static PlayerSample defaultSample() {
int max = Bukkit.getMaxPlayers();
int online = Bukkit.getOnlinePlayers().size();
return new PlayerSample(max, online, null);
2023-03-23 19:43:22 +01:00
}
private static Favicon defaultFavicon() {
return new Favicon();
2023-03-23 19:43:22 +01:00
}
public ServerPingRecord(Object handle) {
initialize();
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
InternalStructure structure = new InternalStructure(handle, modifier);
this.description = structure.getChatComponents().readSafely(0);
StructureModifier<Optional<Object>> optionals = structure.getOptionals(Converters.passthrough(Object.class));
Optional<Object> sampleHandle = optionals.readSafely(0);
this.playerSample = sampleHandle.isPresent() ? SAMPLE_WRAPPER.wrap(sampleHandle.get()) : defaultSample();
2023-03-23 19:43:22 +01:00
Optional<Object> dataHandle = optionals.readSafely(1);
this.serverData = dataHandle.isPresent() ? DATA_WRAPPER.wrap(dataHandle.get()) : defaultData();
2023-03-23 19:43:22 +01:00
Optional<Object> faviconHandle = optionals.readSafely(2);
this.favicon = faviconHandle.isPresent() ? FAVICON_WRAPPER.wrap(faviconHandle.get()) : defaultFavicon();
2023-03-23 19:43:22 +01:00
this.enforceSafeChat = structure.getBooleans().readSafely(0);
}
public ServerPingRecord() {
initialize();
this.description = DEFAULT_DESCRIPTION;
this.playerSample = defaultSample();
this.serverData = defaultData();
2023-03-23 19:43:22 +01:00
this.favicon = defaultFavicon();
}
@Override
2023-03-26 20:58:46 +02:00
public WrappedChatComponent getMotD() {
2023-03-23 19:43:22 +01:00
return description;
}
@Override
2023-03-26 20:58:46 +02:00
public void setMotD(WrappedChatComponent description) {
2023-03-23 19:43:22 +01:00
this.description = description;
}
@Override
public int getPlayersMaximum() {
return playerSample.max;
}
@Override
public void setPlayersMaximum(int maxPlayers) {
playerSample.max = maxPlayers;
}
@Override
public int getPlayersOnline() {
return playerSample.online;
}
@Override
public void setPlayersOnline(int onlineCount) {
playerSample.online = onlineCount;
}
@Override
public ImmutableList<WrappedGameProfile> getPlayers() {
if (playerSample.sample == null) {
return ImmutableList.of();
}
List<WrappedGameProfile> list = PROFILE_LIST_CONVERTER.getSpecific(playerSample.sample);
if (list == null) {
return ImmutableList.of();
}
return ImmutableList.copyOf(list);
2023-03-23 19:43:22 +01:00
}
@Override
public void setPlayers(Iterable<? extends WrappedGameProfile> playerSample) {
if (playerSample == null) {
this.playerSample.sample = null;
return;
}
List<WrappedGameProfile> list = Converters.toList(playerSample);
this.playerSample.sample = PROFILE_LIST_CONVERTER.getGeneric(list);
2023-03-23 19:43:22 +01:00
}
@Override
public String getVersionName() {
return serverData.name;
}
@Override
public void setVersionName(String versionName) {
serverData.name = versionName;
}
@Override
public int getVersionProtocol() {
return serverData.protocol;
}
@Override
public void setVersionProtocol(int protocolVersion) {
serverData.protocol = protocolVersion;
}
@Override
public String getFavicon() {
return new String(favicon.iconBytes, StandardCharsets.UTF_8);
}
@Override
public void setFavicon(String favicon) {
this.favicon.iconBytes = favicon.getBytes(StandardCharsets.UTF_8);
}
@Override
public boolean isEnforceSecureChat() {
return enforceSafeChat;
}
@Override
public void setEnforceSecureChat(boolean safeChat) {
this.enforceSafeChat = safeChat;
}
@Override
public void resetPlayers() {
this.playerSample = defaultSample();
}
@Override
public void resetVersion() {
this.serverData = defaultData();
}
@Override
public boolean arePlayersVisible() {
return playersVisible;
}
@Override
public void setPlayersVisible(boolean visible) {
this.playersVisible = visible;
}
@Override
public Object getHandle() {
2023-03-26 20:58:46 +02:00
WrappedChatComponent wrappedDescription = description != null ? description : DEFAULT_DESCRIPTION;
Object descHandle = wrappedDescription.getHandle();
Optional<Object> playersHandle = Optional.ofNullable(playerSample != null ? SAMPLE_WRAPPER.unwrap(playerSample) : null);
Optional<Object> versionHandle = Optional.ofNullable(serverData != null ? DATA_WRAPPER.unwrap(serverData) : null);
Optional<Object> favHandle = Optional.ofNullable(favicon != null ? FAVICON_WRAPPER.unwrap(favicon) : null);
2023-03-23 19:43:22 +01:00
return PING_CTOR.invoke(descHandle, playersHandle, versionHandle, favHandle, enforceSafeChat);
2023-03-23 19:43:22 +01:00
}
}