13
0
geforkt von Mirrors/Velocity
Dieser Commit ist enthalten in:
Andrew Steinborn 2018-09-21 15:58:47 -04:00 committet von GitHub
Ursprung 1e04d27bb7
Commit ee917682e0
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
9 geänderte Dateien mit 510 neuen und 5 gelöschten Zeilen

Datei anzeigen

@ -6,6 +6,7 @@ import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
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.MessagePosition; import com.velocitypowered.api.util.MessagePosition;
import com.velocitypowered.api.util.title.Title;
import net.kyori.text.Component; import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -83,6 +84,12 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
*/ */
void disconnect(Component reason); void disconnect(Component reason);
/**
* Sends the specified title to the client.
* @param title the title to send
*/
void sendTitle(Title title);
/** /**
* Sends chat input onto the players current server as if they typed it * Sends chat input onto the players current server as if they typed it
* into the client chat box. * into the client chat box.

Datei anzeigen

@ -0,0 +1,236 @@
package com.velocitypowered.api.util.title;
import com.google.common.base.Preconditions;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.Optional;
/**
* Represents a "full" title, including all components. This class is immutable.
*/
public class TextTitle implements Title {
private final Component title;
private final Component subtitle;
private final int stay;
private final int fadeIn;
private final int fadeOut;
private final boolean resetBeforeSend;
private TextTitle(Builder builder) {
this.title = builder.title;
this.subtitle = builder.subtitle;
this.stay = builder.stay;
this.fadeIn = builder.fadeIn;
this.fadeOut = builder.fadeOut;
this.resetBeforeSend = builder.resetBeforeSend;
}
/**
* Returns the main title this title has, if any.
* @return the main title of this title
*/
public Optional<Component> getTitle() {
return Optional.ofNullable(title);
}
/**
* Returns the subtitle this title has, if any.
* @return the subtitle
*/
public Optional<Component> getSubtitle() {
return Optional.ofNullable(subtitle);
}
/**
* Returns the number of ticks this title will stay up.
* @return how long the title will stay, in ticks
*/
public int getStay() {
return stay;
}
/**
* Returns the number of ticks over which this title will fade in.
* @return how long the title will fade in, in ticks
*/
public int getFadeIn() {
return fadeIn;
}
/**
* Returns the number of ticks over which this title will fade out.
* @return how long the title will fade out, in ticks
*/
public int getFadeOut() {
return fadeOut;
}
/**
* Returns whether or not a reset packet will be sent before this title is sent. By default, unless explicitly
* disabled, this is enabled by default.
* @return whether or not a reset packet will be sent before this title is sent
*/
public boolean isResetBeforeSend() {
return resetBeforeSend;
}
/**
* Determines whether or not this title has times set on it. If none are set, it will update the previous title
* set on the client.
* @return whether or not this title has times set on it
*/
public boolean areTimesSet() {
return stay != 0 || fadeIn != 0 || fadeOut != 0;
}
/**
* Creates a new builder from the contents of this title so that it may be changed.
* @return a builder instance with the contents of this title
*/
public Builder toBuilder() {
return new Builder(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TextTitle textTitle = (TextTitle) o;
return stay == textTitle.stay &&
fadeIn == textTitle.fadeIn &&
fadeOut == textTitle.fadeOut &&
resetBeforeSend == textTitle.resetBeforeSend &&
Objects.equals(title, textTitle.title) &&
Objects.equals(subtitle, textTitle.subtitle);
}
@Override
public String toString() {
return "TextTitle{" +
"title=" + title +
", subtitle=" + subtitle +
", stay=" + stay +
", fadeIn=" + fadeIn +
", fadeOut=" + fadeOut +
", resetBeforeSend=" + resetBeforeSend +
'}';
}
@Override
public int hashCode() {
return Objects.hash(title, subtitle, stay, fadeIn, fadeOut, resetBeforeSend);
}
/**
* Creates a new builder for constructing titles.
* @return a builder for constructing titles
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
private @Nullable Component title;
private @Nullable Component subtitle;
private int stay;
private int fadeIn;
private int fadeOut;
private boolean resetBeforeSend = true;
private Builder() {}
private Builder(TextTitle copy) {
this.title = copy.title;
this.subtitle = copy.subtitle;
this.stay = copy.stay;
this.fadeIn = copy.fadeIn;
this.fadeOut = copy.fadeOut;
this.resetBeforeSend = copy.resetBeforeSend;
}
public Builder title(Component title) {
this.title = Preconditions.checkNotNull(title, "title");
return this;
}
public Builder clearTitle() {
this.title = null;
return this;
}
public Builder subtitle(Component subtitle) {
this.subtitle = Preconditions.checkNotNull(subtitle, "subtitle");
return this;
}
public Builder clearSubtitle() {
this.subtitle = null;
return this;
}
public Builder stay(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.stay = ticks;
return this;
}
public Builder fadeIn(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.fadeIn = ticks;
return this;
}
public Builder fadeOut(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.fadeOut = ticks;
return this;
}
public Builder resetBeforeSend(boolean b) {
this.resetBeforeSend = b;
return this;
}
public Component getTitle() {
return title;
}
public Component getSubtitle() {
return subtitle;
}
public int getStay() {
return stay;
}
public int getFadeIn() {
return fadeIn;
}
public int getFadeOut() {
return fadeOut;
}
public boolean isResetBeforeSend() {
return resetBeforeSend;
}
public TextTitle build() {
return new TextTitle(this);
}
@Override
public String toString() {
return "Builder{" +
"title=" + title +
", subtitle=" + subtitle +
", stay=" + stay +
", fadeIn=" + fadeIn +
", fadeOut=" + fadeOut +
", resetBeforeSend=" + resetBeforeSend +
'}';
}
}
}

Datei anzeigen

@ -0,0 +1,7 @@
package com.velocitypowered.api.util.title;
/**
* Represents a title that can be sent to a Minecraft client.
*/
public interface Title {
}

Datei anzeigen

@ -0,0 +1,50 @@
package com.velocitypowered.api.util.title;
/**
* Provides special-purpose titles.
*/
public class Titles {
private Titles() {
throw new AssertionError();
}
private static final Title RESET = new Title() {
@Override
public String toString() {
return "reset title";
}
};
private static final Title HIDE = new Title() {
@Override
public String toString() {
return "hide title";
}
};
/**
* Returns a title that, when sent to the client, will cause all title data to be reset and any existing title to be
* hidden.
* @return the reset title
*/
public static Title reset() {
return RESET;
}
/**
* Returns a title that, when sent to the client, will cause any existing title to be hidden. The title may be
* restored by a {@link TextTitle} with no title or subtitle (only a time).
* @return the hide title
*/
public static Title hide() {
return HIDE;
}
/**
* Returns a builder for {@link TextTitle}s.
* @return a builder for text titles
*/
public static TextTitle.Builder text() {
return TextTitle.builder();
}
}

Datei anzeigen

@ -0,0 +1,4 @@
/**
* Provides data structures for creating and manipulating titles.
*/
package com.velocitypowered.api.util.title;

Datei anzeigen

@ -219,6 +219,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
player.getConnectedServer().getMinecraftConnection().delayedWrite(pm); player.getConnectedServer().getMinecraftConnection().delayedWrite(pm);
} }
// Clear any title from the previous server.
player.getConnection().delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion()));
// Flush everything // Flush everything
player.getConnection().flush(); player.getConnection().flush();
player.getConnectedServer().getMinecraftConnection().flush(); player.getConnectedServer().getMinecraftConnection().flush();

Datei anzeigen

@ -15,6 +15,9 @@ 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.util.MessagePosition; import com.velocitypowered.api.util.MessagePosition;
import com.velocitypowered.api.util.title.TextTitle;
import com.velocitypowered.api.util.title.Title;
import com.velocitypowered.api.util.title.Titles;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
@ -22,6 +25,7 @@ import com.velocitypowered.proxy.connection.VelocityConstants;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
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.util.ThrowableUtils; import com.velocitypowered.proxy.util.ThrowableUtils;
@ -139,11 +143,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
byte pos = (byte) position.ordinal(); byte pos = (byte) position.ordinal();
String json; String json;
if (position == MessagePosition.ACTION_BAR) { if (position == MessagePosition.ACTION_BAR) {
if (getProtocolVersion() >= ProtocolConstants.MINECRAFT_1_11) {
// We can use the title packet instead.
TitlePacket pkt = new TitlePacket();
pkt.setAction(TitlePacket.SET_ACTION_BAR);
pkt.setComponent(ComponentSerializers.JSON.serialize(component));
connection.write(pkt);
return;
} else {
// Due to issues with action bar packets, we'll need to convert the text message into a legacy message // Due to issues with action bar packets, we'll need to convert the text message into a legacy message
// and then inject the legacy text into a component... yuck! // and then inject the legacy text into a component... yuck!
JsonObject object = new JsonObject(); JsonObject object = new JsonObject();
object.addProperty("text", ComponentSerializers.LEGACY.serialize(component)); object.addProperty("text", ComponentSerializers.LEGACY.serialize(component));
json = VelocityServer.GSON.toJson(object); json = VelocityServer.GSON.toJson(object);
}
} else { } else {
json = ComponentSerializers.JSON.serialize(component); json = ComponentSerializers.JSON.serialize(component);
} }
@ -176,6 +189,48 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
connection.closeWith(Disconnect.create(reason)); connection.closeWith(Disconnect.create(reason));
} }
@Override
public void sendTitle(Title title) {
Preconditions.checkNotNull(title, "title");
if (title.equals(Titles.reset())) {
connection.write(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion()));
} else if (title.equals(Titles.hide())) {
connection.write(TitlePacket.hideForProtocolVersion(connection.getProtocolVersion()));
} else if (title instanceof TextTitle) {
TextTitle tt = (TextTitle) title;
if (tt.isResetBeforeSend()) {
connection.delayedWrite(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion()));
}
if (tt.getTitle().isPresent()) {
TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_TITLE);
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getTitle().get()));
connection.delayedWrite(titlePkt);
}
if (tt.getSubtitle().isPresent()) {
TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_SUBTITLE);
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getSubtitle().get()));
connection.delayedWrite(titlePkt);
}
if (tt.areTimesSet()) {
TitlePacket timesPkt = TitlePacket.timesForProtocolVersion(connection.getProtocolVersion());
timesPkt.setFadeIn(tt.getFadeIn());
timesPkt.setStay(tt.getStay());
timesPkt.setFadeOut(tt.getFadeOut());
connection.delayedWrite(timesPkt);
}
connection.flush();
} else {
throw new IllegalArgumentException("Unknown title class " + title.getClass().getName());
}
}
public VelocityServerConnection getConnectedServer() { public VelocityServerConnection getConnectedServer() {
return connectedServer; return connectedServer;
} }

Datei anzeigen

@ -136,6 +136,12 @@ public enum StateRegistry {
map(0x44, MINECRAFT_1_12, true), map(0x44, MINECRAFT_1_12, true),
map(0x45, MINECRAFT_1_12_1, true), map(0x45, MINECRAFT_1_12_1, true),
map(0x48, MINECRAFT_1_13, true)); map(0x48, MINECRAFT_1_13, true));
CLIENTBOUND.register(TitlePacket.class, TitlePacket::new,
map(0x45, MINECRAFT_1_8, true),
map(0x45, MINECRAFT_1_9, true),
map(0x47, MINECRAFT_1_12, true),
map(0x48, MINECRAFT_1_12_1, true),
map(0x4B, MINECRAFT_1_13, true));
} }
}, },
LOGIN { LOGIN {

Datei anzeigen

@ -0,0 +1,137 @@
package com.velocitypowered.proxy.protocol.packet;
import com.google.common.base.Preconditions;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitlePacket implements MinecraftPacket {
public static final int SET_TITLE = 0;
public static final int SET_SUBTITLE = 1;
public static final int SET_ACTION_BAR = 2;
public static final int SET_TIMES = 3;
public static final int SET_TIMES_OLD = 2;
public static final int HIDE = 4;
public static final int HIDE_OLD = 3;
public static final int RESET = 5;
public static final int RESET_OLD = 4;
private int action;
private String component;
private int fadeIn;
private int stay;
private int fadeOut;
@Override
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
throw new UnsupportedOperationException(); // encode only
}
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
ProtocolUtils.writeVarInt(buf, action);
if (protocolVersion >= ProtocolConstants.MINECRAFT_1_11) {
// 1.11+ shifted the action enum by 1 to handle the action bar
switch (action) {
case SET_TITLE:
case SET_SUBTITLE:
case SET_ACTION_BAR:
ProtocolUtils.writeString(buf, component);
break;
case SET_TIMES:
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
break;
case HIDE:
case RESET:
break;
}
} else {
switch (action) {
case SET_TITLE:
case SET_SUBTITLE:
ProtocolUtils.writeString(buf, component);
break;
case SET_TIMES_OLD:
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
break;
case HIDE_OLD:
case RESET_OLD:
break;
}
}
}
public int getAction() {
return action;
}
public void setAction(int action) {
this.action = action;
}
public String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
public int getFadeIn() {
return fadeIn;
}
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
public int getStay() {
return stay;
}
public void setStay(int stay) {
this.stay = stay;
}
public int getFadeOut() {
return fadeOut;
}
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
public static TitlePacket hideForProtocolVersion(int protocolVersion) {
TitlePacket packet = new TitlePacket();
packet.setAction(protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? TitlePacket.HIDE : TitlePacket.HIDE_OLD);
return packet;
}
public static TitlePacket resetForProtocolVersion(int protocolVersion) {
TitlePacket packet = new TitlePacket();
packet.setAction(protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? TitlePacket.RESET : TitlePacket.RESET_OLD);
return packet;
}
public static TitlePacket timesForProtocolVersion(int protocolVersion) {
TitlePacket packet = new TitlePacket();
packet.setAction(protocolVersion >= ProtocolConstants.MINECRAFT_1_11 ? TitlePacket.SET_TIMES : TitlePacket.SET_TIMES_OLD);
return packet;
}
@Override
public String toString() {
return "TitlePacket{" +
"action=" + action +
", component='" + component + '\'' +
", fadeIn=" + fadeIn +
", stay=" + stay +
", fadeOut=" + fadeOut +
'}';
}
}