13
0
geforkt von Mirrors/Velocity

Merge pull request #112 from lolnetnz/forge-modlist

Add Forge mod list support
Dieser Commit ist enthalten in:
Andrew Steinborn 2018-10-10 18:26:04 -04:00 committet von GitHub
Commit f10a3fa1ac
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
8 geänderte Dateien mit 153 neuen und 70 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,26 @@
package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.util.ModInfo;
/**
* This event is fired when the players ModInfo is changed.
*/
public final class PlayerModInfoEvent {
private final Player player;
private final ModInfo modInfo;
public PlayerModInfoEvent(Player player, ModInfo modInfo) {
this.player = Preconditions.checkNotNull(player, "player");
this.modInfo = Preconditions.checkNotNull(modInfo, "modInfo");
}
public Player getPlayer() {
return player;
}
public ModInfo getModInfo() {
return modInfo;
}
}

Datei anzeigen

@ -8,6 +8,7 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.util.MessagePosition; import com.velocitypowered.api.util.MessagePosition;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.api.util.title.Title; import com.velocitypowered.api.util.title.Title;
import java.util.List; import java.util.List;
@ -45,6 +46,12 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
*/ */
PlayerSettings getPlayerSettings(); PlayerSettings getPlayerSettings();
/**
* Returns the player's mod info if they have a modded client.
* @return an {@link Optional} the mod info. which may be empty
*/
Optional<ModInfo> getModInfo();
/** /**
* Returns the current player's ping * Returns the current player's ping
* @return the player's ping or -1 if ping information is currently unknown * @return the player's ping or -1 if ping information is currently unknown

Datei anzeigen

@ -3,6 +3,7 @@ package com.velocitypowered.api.proxy.server;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.api.util.ModInfo;
import net.kyori.text.Component; import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -16,13 +17,13 @@ public final class ServerPing {
private final Players players; private final Players players;
private final Component description; private final Component description;
private final @Nullable Favicon favicon; private final @Nullable Favicon favicon;
private final ModInfo modinfo; private final @Nullable ModInfo modinfo;
public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon) { public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon) {
this(version, players, description, favicon, ModInfo.DEFAULT); this(version, players, description, favicon, ModInfo.DEFAULT);
} }
public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon, ServerPing.@Nullable ModInfo modinfo) { public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon, @Nullable ModInfo modinfo) {
this.version = Preconditions.checkNotNull(version, "version"); this.version = Preconditions.checkNotNull(version, "version");
this.players = players; this.players = players;
this.description = Preconditions.checkNotNull(description, "description"); this.description = Preconditions.checkNotNull(description, "description");
@ -74,8 +75,8 @@ public final class ServerPing {
builder.favicon = favicon; builder.favicon = favicon;
builder.nullOutModinfo = modinfo == null; builder.nullOutModinfo = modinfo == null;
if (modinfo != null) { if (modinfo != null) {
builder.modType = modinfo.type; builder.modType = modinfo.getType();
builder.mods.addAll(modinfo.modList); builder.mods.addAll(modinfo.getMods());
} }
return builder; return builder;
} }
@ -93,7 +94,7 @@ public final class ServerPing {
private int maximumPlayers; private int maximumPlayers;
private final List<SamplePlayer> samplePlayers = new ArrayList<>(); private final List<SamplePlayer> samplePlayers = new ArrayList<>();
private String modType; private String modType;
private final List<Mod> mods = new ArrayList<>(); private final List<ModInfo.Mod> mods = new ArrayList<>();
private Component description; private Component description;
private Favicon favicon; private Favicon favicon;
private boolean nullOutPlayers; private boolean nullOutPlayers;
@ -128,7 +129,7 @@ public final class ServerPing {
return this; return this;
} }
public Builder mods(Mod... mods) { public Builder mods(ModInfo.Mod... mods) {
this.mods.addAll(Arrays.asList(mods)); this.mods.addAll(Arrays.asList(mods));
return this; return this;
} }
@ -196,7 +197,7 @@ public final class ServerPing {
return modType; return modType;
} }
public List<Mod> getMods() { public List<ModInfo.Mod> getMods() {
return mods; return mods;
} }
@ -301,58 +302,4 @@ public final class ServerPing {
'}'; '}';
} }
} }
public static final class ModInfo {
public static final ModInfo DEFAULT = new ModInfo("FML", ImmutableList.of());
private final String type;
private final List<Mod> modList;
public ModInfo(String type, List<Mod> modList) {
this.type = Preconditions.checkNotNull(type, "type");
this.modList = ImmutableList.copyOf(modList);
}
public String getType() {
return type;
}
public List<Mod> getMods() {
return modList;
}
@Override
public String toString() {
return "ModInfo{" +
"type='" + type + '\'' +
", modList=" + modList +
'}';
}
}
public static final class Mod {
private final String id;
private final String version;
public Mod(String id, String version) {
this.id = Preconditions.checkNotNull(id, "id");
this.version = Preconditions.checkNotNull(version, "version");
}
public String getId() {
return id;
}
public String getVersion() {
return version;
}
@Override
public String toString() {
return "Mod{" +
"id='" + id + '\'' +
", version='" + version + '\'' +
'}';
}
}
} }

Datei anzeigen

@ -0,0 +1,60 @@
package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
public final class ModInfo {
public static final ModInfo DEFAULT = new ModInfo("FML", ImmutableList.of());
private final String type;
private final List<Mod> modList;
public ModInfo(String type, List<Mod> modList) {
this.type = Preconditions.checkNotNull(type, "type");
this.modList = ImmutableList.copyOf(modList);
}
public String getType() {
return type;
}
public List<Mod> getMods() {
return modList;
}
@Override
public String toString() {
return "ModInfo{" +
"type='" + type + '\'' +
", modList=" + modList +
'}';
}
public static final class Mod {
private final String id;
private final String version;
public Mod(String id, String version) {
this.id = Preconditions.checkNotNull(id, "id");
this.version = Preconditions.checkNotNull(version, "version");
}
public String getId() {
return id;
}
public String getVersion() {
return version;
}
@Override
public String toString() {
return "Mod{" +
"id='" + id + '\'' +
", version='" + version + '\'' +
'}';
}
}
}

Datei anzeigen

@ -1,10 +1,9 @@
package com.velocitypowered.proxy.connection.client; package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.PlayerChatEvent; import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.VelocityConstants;
@ -13,7 +12,6 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.packet.*; import com.velocitypowered.proxy.protocol.packet.*;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import com.velocitypowered.proxy.tablist.VelocityTabList;
import com.velocitypowered.proxy.util.ThrowableUtils; import com.velocitypowered.proxy.util.ThrowableUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.text.TextComponent; import net.kyori.text.TextComponent;
@ -147,6 +145,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
player.getConnectedServer().getConnection().write(PluginMessageUtil.rewriteMCBrand(packet)); player.getConnectedServer().getConnection().write(PluginMessageUtil.rewriteMCBrand(packet));
} else if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) { } else if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) {
if (packet.getChannel().equals(VelocityConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) { if (packet.getChannel().equals(VelocityConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) {
if (!player.getModInfo().isPresent()) {
PluginMessageUtil.readModList(packet).ifPresent(mods -> player.setModInfo(new ModInfo("FML", mods)));
}
// Always forward the FML handshake to the remote server. // Always forward the FML handshake to the remote server.
player.getConnectedServer().getConnection().write(packet); player.getConnectedServer().getConnection().write(packet);
} else { } else {

Datei anzeigen

@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.player.KickedFromServerEvent; import com.velocitypowered.api.event.player.KickedFromServerEvent;
import com.velocitypowered.api.event.player.PlayerModInfoEvent;
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent; import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
import com.velocitypowered.api.event.player.ServerPreConnectEvent; import com.velocitypowered.api.event.player.ServerPreConnectEvent;
import com.velocitypowered.api.permission.PermissionFunction; import com.velocitypowered.api.permission.PermissionFunction;
@ -16,8 +17,8 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.util.MessagePosition; import com.velocitypowered.api.util.MessagePosition;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.api.util.title.TextTitle; import com.velocitypowered.api.util.title.TextTitle;
import com.velocitypowered.api.util.title.Title; import com.velocitypowered.api.util.title.Title;
import com.velocitypowered.api.util.title.Titles; import com.velocitypowered.api.util.title.Titles;
@ -31,9 +32,6 @@ import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.packet.*; import com.velocitypowered.proxy.protocol.packet.*;
import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import com.velocitypowered.proxy.protocol.packet.Chat;
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import com.velocitypowered.proxy.tablist.VelocityTabList; import com.velocitypowered.proxy.tablist.VelocityTabList;
import com.velocitypowered.proxy.util.ThrowableUtils; import com.velocitypowered.proxy.util.ThrowableUtils;
import net.kyori.text.Component; import net.kyori.text.Component;
@ -69,6 +67,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
private VelocityServerConnection connectedServer; private VelocityServerConnection connectedServer;
private VelocityServerConnection connectionInFlight; private VelocityServerConnection connectionInFlight;
private PlayerSettings settings; private PlayerSettings settings;
private ModInfo modInfo;
private final VelocityTabList tabList; private final VelocityTabList tabList;
private final VelocityServer server; private final VelocityServer server;
@ -121,6 +120,15 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, this.settings)); server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, this.settings));
} }
public Optional<ModInfo> getModInfo() {
return Optional.ofNullable(modInfo);
}
void setModInfo(ModInfo modInfo) {
this.modInfo = modInfo;
server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, this.modInfo));
}
@Override @Override
public InetSocketAddress getRemoteAddress() { public InetSocketAddress getRemoteAddress() {
return (InetSocketAddress) connection.getRemoteAddress(); return (InetSocketAddress) connection.getRemoteAddress();

Datei anzeigen

@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.event.proxy.ProxyPingEvent; import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.InboundConnection;
import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
@ -42,7 +43,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(), ImmutableList.of()), new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(), ImmutableList.of()),
configuration.getMotdComponent(), configuration.getMotdComponent(),
configuration.getFavicon().orElse(null), configuration.getFavicon().orElse(null),
configuration.isAnnounceForge() ? ServerPing.ModInfo.DEFAULT : null configuration.isAnnounceForge() ? ModInfo.DEFAULT : null
); );
ProxyPingEvent event = new ProxyPingEvent(inboundWrapper, initialPing); ProxyPingEvent event = new ProxyPingEvent(inboundWrapper, initialPing);

Datei anzeigen

@ -2,6 +2,9 @@ package com.velocitypowered.proxy.protocol.util;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.connection.VelocityConstants;
import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.PluginMessage;
@ -11,6 +14,7 @@ import io.netty.buffer.Unpooled;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional;
public class PluginMessageUtil { public class PluginMessageUtil {
public static final String BRAND_CHANNEL_LEGACY = "MC|Brand"; public static final String BRAND_CHANNEL_LEGACY = "MC|Brand";
@ -76,4 +80,32 @@ public class PluginMessageUtil {
newMsg.setData(rewrittenData); newMsg.setData(rewrittenData);
return newMsg; return newMsg;
} }
public static Optional<List<ModInfo.Mod>> readModList(PluginMessage message) {
Preconditions.checkNotNull(message, "message");
Preconditions.checkArgument(message.getChannel().equals(VelocityConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL),
"message is not a FML HS plugin message");
ByteBuf byteBuf = Unpooled.wrappedBuffer(message.getData());
try {
byte discriminator = byteBuf.readByte();
if (discriminator == 2) {
ImmutableList.Builder<ModInfo.Mod> mods = ImmutableList.builder();
int modCount = ProtocolUtils.readVarInt(byteBuf);
for (int index = 0; index < modCount; index++) {
String id = ProtocolUtils.readString(byteBuf);
String version = ProtocolUtils.readString(byteBuf);
mods.add(new ModInfo.Mod(id, version));
}
return Optional.of(mods.build());
}
return Optional.empty();
} finally {
byteBuf.release();
}
}
} }