3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-16 21:10:30 +01:00

Checker Framework integration (#126)

Dieser Commit ist enthalten in:
Andrew Steinborn 2018-10-27 21:45:42 -04:00 committet von GitHub
Ursprung dccf688da8
Commit 32829c5637
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
106 geänderte Dateien mit 1170 neuen und 708 gelöschten Zeilen

35
CONTRIBUTING.md Normale Datei
Datei anzeigen

@ -0,0 +1,35 @@
Thanks for taking the time to submit a contribution to Velocity! Your support
is greatly appreciated.
In this document, we'll give you some tips on making it more likely your
contribution will be pulled.
# Setting up a development environment
This isn't as difficult as you may be led to believe. All you need to do is
clone the Velocity repository in your favorite IDE and have your backend test
servers set up to run behind Velocity.
# Actually working on the code
It is strongly recommended that you are familiar with the Minecraft protocol,
proficient with using Java, and have familiarity with the libraries used in
Velocity (particularly [Netty](https://netty.io), [Google Guava](https://github.com/google/guava),
and the [Checker Framework annotations](https://checkerframework.org/)).
While you can certainly work with the Velocity codebase without knowing any
of this, it can be risky to proceed.
Velocity does not currently obey any one general code style at the moment.
Plans are [in the works](https://github.com/VelocityPowered/Velocity/issues/125)
to define the code style the project will follow.
# Notes on the build
To reduce bugs and ensure code quality, we run the following tools on all commits
and pull requests:
* [Checker Framework](https://checkerframework.org/): an enhancement to Java's type
system that is designed to help catch bugs. Velocity runs the _Nullness Checker_
and the _Optional Checker_.
* [Checkstyle](http://checkstyle.sourceforge.net/) (not currently in use): ensures
that your code is correctly formatted.

Datei anzeigen

@ -4,6 +4,8 @@ plugins {
id 'maven-publish'
}
apply from: '../gradle/checkerframework.gradle'
sourceSets {
ap {
compileClasspath += main.compileClasspath + main.output
@ -17,7 +19,7 @@ dependencies {
compile 'com.moandjiezana.toml:toml4j:0.7.2'
compile "org.slf4j:slf4j-api:${slf4jVersion}"
compile 'com.google.inject:guice:4.2.0'
compile 'org.checkerframework:checker-qual:2.5.4'
compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"

Datei anzeigen

@ -2,6 +2,7 @@ package com.velocitypowered.api.plugin.ap;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -29,8 +30,8 @@ public class SerializedPluginDescription {
this.version = Strings.emptyToNull(version);
this.description = Strings.emptyToNull(description);
this.url = Strings.emptyToNull(url);
this.authors = authors == null || authors.isEmpty() ? null : authors;
this.dependencies = dependencies == null || dependencies.isEmpty() ? null : dependencies;
this.authors = authors == null || authors.isEmpty() ? ImmutableList.of() : authors;
this.dependencies = dependencies == null || dependencies.isEmpty() ? ImmutableList.of() : dependencies;
this.main = Preconditions.checkNotNull(main, "main");
}
@ -63,12 +64,12 @@ public class SerializedPluginDescription {
return url;
}
public @Nullable List<String> getAuthors() {
return authors;
public List<String> getAuthors() {
return authors == null ? ImmutableList.of() : authors;
}
public @Nullable List<Dependency> getDependencies() {
return dependencies;
public List<Dependency> getDependencies() {
return dependencies == null ? ImmutableList.of() : dependencies;
}
public String getMain() {

Datei anzeigen

@ -15,7 +15,7 @@ public interface Command {
* @param source the source of this command
* @param args the arguments for this command
*/
void execute(@NonNull CommandSource source, @NonNull String[] args);
void execute(CommandSource source, String @NonNull [] args);
/**
* Provides tab complete suggestions for a command for a specified {@link CommandSource}.
@ -23,7 +23,7 @@ public interface Command {
* @param currentArgs the current, partial arguments for this command
* @return tab complete suggestions
*/
default List<String> suggest(@NonNull CommandSource source, @NonNull String[] currentArgs) {
default List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
return ImmutableList.of();
}
@ -38,7 +38,7 @@ public interface Command {
* @param args the arguments for this command
* @return whether the source has permission
*/
default boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
default boolean hasPermission(CommandSource source, String @NonNull [] args) {
return true;
}
}

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.command;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents an interface to register a command executor with the proxy.
*/
@ -11,13 +9,13 @@ public interface CommandManager {
* @param command the command to register
* @param aliases the alias to use
*/
void register(@NonNull Command command, String... aliases);
void register(Command command, String... aliases);
/**
* Unregisters a command.
* @param alias the command alias to unregister
*/
void unregister(@NonNull String alias);
void unregister(String alias);
/**
* Attempts to execute a command from the specified {@code cmdLine}.
@ -25,5 +23,5 @@ public interface CommandManager {
* @param cmdLine the command to run
* @return true if the command was found and executed, false if it was not
*/
boolean execute(@NonNull CommandSource source, @NonNull String cmdLine);
boolean execute(CommandSource source, String cmdLine);
}

Datei anzeigen

@ -2,7 +2,6 @@ package com.velocitypowered.api.command;
import com.velocitypowered.api.permission.PermissionSubject;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents something that can be used to run a {@link Command}.
@ -12,5 +11,5 @@ public interface CommandSource extends PermissionSubject {
* Sends the specified {@code component} to the invoker.
* @param component the text component to send
*/
void sendMessage(@NonNull Component component);
void sendMessage(Component component);
}

Datei anzeigen

@ -1,12 +1,10 @@
package com.velocitypowered.api.event;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents an interface to perform direct dispatch of an event. This makes integration easier to achieve with platforms
* such as RxJava.
*/
@FunctionalInterface
public interface EventHandler<E> {
void execute(@NonNull E event);
void execute(E event);
}

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.event;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.concurrent.CompletableFuture;
/**
@ -13,7 +11,7 @@ public interface EventManager {
* @param plugin the plugin to associate with the listener
* @param listener the listener to register
*/
void register(@NonNull Object plugin, @NonNull Object listener);
void register(Object plugin, Object listener);
/**
* Requests that the specified {@code handler} listen for events and associate it with the {@code plugin}.
@ -22,7 +20,7 @@ public interface EventManager {
* @param handler the handler to register
* @param <E> the event type to handle
*/
default <E> void register(@NonNull Object plugin, @NonNull Class<E> eventClass, @NonNull EventHandler<E> handler) {
default <E> void register(Object plugin, Class<E> eventClass, EventHandler<E> handler) {
register(plugin, eventClass, PostOrder.NORMAL, handler);
}
@ -34,7 +32,7 @@ public interface EventManager {
* @param handler the handler to register
* @param <E> the event type to handle
*/
<E> void register(@NonNull Object plugin, @NonNull Class<E> eventClass, @NonNull PostOrder postOrder, @NonNull EventHandler<E> handler);
<E> void register(Object plugin, Class<E> eventClass, PostOrder postOrder, EventHandler<E> handler);
/**
* Fires the specified event to the event bus asynchronously. This allows Velocity to continue servicing connections
@ -42,13 +40,13 @@ public interface EventManager {
* @param event the event to fire
* @return a {@link CompletableFuture} representing the posted event
*/
@NonNull <E> CompletableFuture<E> fire(@NonNull E event);
<E> CompletableFuture<E> fire(E event);
/**
* Posts the specified event to the event bus and discards the result.
* @param event the event to fire
*/
default void fireAndForget(@NonNull Object event) {
default void fireAndForget(Object event) {
fire(event);
}
@ -56,14 +54,14 @@ public interface EventManager {
* Unregisters all listeners for the specified {@code plugin}.
* @param plugin the plugin to deregister listeners for
*/
void unregisterListeners(@NonNull Object plugin);
void unregisterListeners(Object plugin);
/**
* Unregisters a specific listener for a specific plugin.
* @param plugin the plugin associated with the listener
* @param listener the listener to deregister
*/
void unregisterListener(@NonNull Object plugin, @NonNull Object listener);
void unregisterListener(Object plugin, Object listener);
/**
* Unregisters a specific event handler for a specific plugin.
@ -71,5 +69,5 @@ public interface EventManager {
* @param handler the handler to register
* @param <E> the event type to handle
*/
<E> void unregister(@NonNull Object plugin, @NonNull EventHandler<E> handler);
<E> void unregister(Object plugin, EventHandler<E> handler);
}

Datei anzeigen

@ -22,7 +22,7 @@ public interface ResultedEvent<R extends ResultedEvent.Result> {
* Sets the result of this event. The result must be non-null.
* @param result the new result
*/
void setResult(@NonNull R result);
void setResult(R result);
/**
* Represents a result for an event.
@ -106,7 +106,7 @@ public interface ResultedEvent<R extends ResultedEvent.Result> {
return ALLOWED;
}
public static ComponentResult denied(@NonNull Component reason) {
public static ComponentResult denied(Component reason) {
Preconditions.checkNotNull(reason, "reason");
return new ComponentResult(false, reason);
}

Datei anzeigen

@ -2,15 +2,14 @@ package com.velocitypowered.api.event.connection;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.InboundConnection;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* This event is fired when a handshake is established between a client and Velocity.
*/
public final class ConnectionHandshakeEvent {
private final @NonNull InboundConnection connection;
private final InboundConnection connection;
public ConnectionHandshakeEvent(@NonNull InboundConnection connection) {
public ConnectionHandshakeEvent(InboundConnection connection) {
this.connection = Preconditions.checkNotNull(connection, "connection");
}

Datei anzeigen

@ -2,16 +2,15 @@ package com.velocitypowered.api.event.connection;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* This event is fired when a player disconnects from the proxy. Operations on the provided player, aside from basic
* data retrieval operations, may behave in undefined ways.
*/
public final class DisconnectEvent {
private @NonNull final Player player;
private final Player player;
public DisconnectEvent(@NonNull Player player) {
public DisconnectEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
}

Datei anzeigen

@ -3,7 +3,6 @@ package com.velocitypowered.api.event.connection;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* This event is fired once the player has been authenticated but before they connect to a server on the proxy.
@ -12,7 +11,7 @@ public final class LoginEvent implements ResultedEvent<ResultedEvent.ComponentRe
private final Player player;
private ComponentResult result;
public LoginEvent(@NonNull Player player) {
public LoginEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
this.result = ComponentResult.allowed();
}
@ -27,7 +26,7 @@ public final class LoginEvent implements ResultedEvent<ResultedEvent.ComponentRe
}
@Override
public void setResult(@NonNull ComponentResult result) {
public void setResult(ComponentResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}

Datei anzeigen

@ -36,7 +36,7 @@ public final class PluginMessageEvent implements ResultedEvent<PluginMessageEven
}
@Override
public void setResult(@NonNull ForwardResult result) {
public void setResult(ForwardResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}

Datei anzeigen

@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.InboundConnection;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -62,11 +61,11 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
private static final PreLoginComponentResult FORCE_OFFLINEMODE = new PreLoginComponentResult(Result.FORCE_OFFLINE, null);
private final Result result;
private final Optional<Component> reason;
private final @Nullable Component reason;
private PreLoginComponentResult(Result result, @Nullable Component reason) {
this.result = result;
this.reason = Optional.ofNullable(reason);
this.reason = reason;
}
@Override
@ -75,7 +74,7 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
}
public Optional<Component> getReason() {
return reason;
return Optional.ofNullable(reason);
}
public boolean isOnlineModeAllowed() {
@ -88,19 +87,16 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
@Override
public String toString() {
if (isForceOfflineMode()) {
return "allowed with force offline mode";
switch (result) {
case ALLOWED:
return "allowed";
case FORCE_OFFLINE:
return "allowed with force offline mode";
case FORCE_ONLINE:
return "allowed with online mode";
default:
return "denied";
}
if (isOnlineModeAllowed()) {
return "allowed with online mode";
}
if (isAllowed()) {
return "allowed";
}
if (reason.isPresent()) {
return "denied: " + ComponentSerializers.PLAIN.serialize(reason.get());
}
return "denied";
}
/**

Datei anzeigen

@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.permission.PermissionProvider;
import com.velocitypowered.api.permission.PermissionSubject;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
@ -22,7 +21,7 @@ public final class PermissionsSetupEvent {
this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider");
}
public @NonNull PermissionSubject getSubject() {
public PermissionSubject getSubject() {
return this.subject;
}
@ -33,11 +32,11 @@ public final class PermissionsSetupEvent {
* @param subject the subject
* @return the obtained permission function
*/
public @NonNull PermissionFunction createFunction(PermissionSubject subject) {
public PermissionFunction createFunction(PermissionSubject subject) {
return this.provider.createFunction(subject);
}
public @NonNull PermissionProvider getProvider() {
public PermissionProvider getProvider() {
return this.provider;
}

Datei anzeigen

@ -14,7 +14,7 @@ public final class GameProfileRequestEvent {
private final InboundConnection connection;
private final GameProfile originalProfile;
private final boolean onlineMode;
private GameProfile gameProfile;
private @Nullable GameProfile gameProfile;
public GameProfileRequestEvent(InboundConnection connection, GameProfile originalProfile, boolean onlineMode) {
this.connection = Preconditions.checkNotNull(connection, "connection");

Datei anzeigen

@ -36,7 +36,7 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
}
@Override
public void setResult(@NonNull ChatResult result) {
public void setResult(ChatResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}

Datei anzeigen

@ -17,7 +17,6 @@ public final class PlayerSettingsChangedEvent {
return player;
}
//New settings
public PlayerSettings getPlayerSettings() {
return playerSettings;
}

Datei anzeigen

@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
@ -33,7 +32,7 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
}
@Override
public void setResult(@NonNull ServerResult result) {
public void setResult(ServerResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
@ -54,19 +53,17 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
* Represents the result of the {@link ServerPreConnectEvent}.
*/
public static class ServerResult implements ResultedEvent.Result {
private static final ServerResult DENIED = new ServerResult(false, null);
private static final ServerResult DENIED = new ServerResult(null);
private final boolean allowed;
private final RegisteredServer server;
private final @Nullable RegisteredServer server;
private ServerResult(boolean allowed, @Nullable RegisteredServer server) {
this.allowed = allowed;
private ServerResult(@Nullable RegisteredServer server) {
this.server = server;
}
@Override
public boolean isAllowed() {
return allowed;
return server != null;
}
public Optional<RegisteredServer> getServer() {
@ -75,10 +72,10 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
@Override
public String toString() {
if (!allowed) {
return "denied";
if (server != null) {
return "allowed: connect to " + server.getServerInfo().getName();
}
return "allowed: connect to " + server.getServerInfo().getName();
return "denied";
}
public static ServerResult denied() {
@ -87,7 +84,7 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
public static ServerResult allowed(RegisteredServer server) {
Preconditions.checkNotNull(server, "server");
return new ServerResult(true, server);
return new ServerResult(server);
}
}
}

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.permission;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Function that calculates the permission settings for a given
* {@link PermissionSubject}.
@ -29,5 +27,5 @@ public interface PermissionFunction {
* @param permission the permission
* @return the value the permission is set to
*/
@NonNull Tristate getPermissionValue(@NonNull String permission);
Tristate getPermissionValue(String permission);
}

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.permission;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Provides {@link PermissionFunction}s for {@link PermissionSubject}s.
*/
@ -13,5 +11,5 @@ public interface PermissionProvider {
* @param subject the subject
* @return the function
*/
@NonNull PermissionFunction createFunction(@NonNull PermissionSubject subject);
PermissionFunction createFunction(PermissionSubject subject);
}

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.permission;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Represents a object that has a set of queryable permissions.
*/
@ -12,7 +10,7 @@ public interface PermissionSubject {
* @param permission the permission to check for
* @return whether or not the subject has the permission
*/
default boolean hasPermission(@NonNull String permission) {
default boolean hasPermission(String permission) {
return getPermissionValue(permission).asBoolean();
}
@ -22,5 +20,5 @@ public interface PermissionSubject {
* @param permission the permission
* @return the value the permission is set to
*/
@NonNull Tristate getPermissionValue(@NonNull String permission);
Tristate getPermissionValue(String permission);
}

Datei anzeigen

@ -37,7 +37,7 @@ public enum Tristate {
* @param val the boolean value
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or <code>false</code>, respectively.
*/
public static @NonNull Tristate fromBoolean(boolean val) {
public static Tristate fromBoolean(boolean val) {
return val ? TRUE : FALSE;
}
@ -51,7 +51,7 @@ public enum Tristate {
* @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value
* is <code>null</code>, <code>true</code> or <code>false</code>, respectively.
*/
public static @NonNull Tristate fromNullableBoolean(@Nullable Boolean val) {
public static Tristate fromNullableBoolean(@Nullable Boolean val) {
return val == null ? UNDEFINED : val ? TRUE : FALSE;
}

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
/**
@ -13,7 +11,7 @@ public interface PluginContainer {
*
* @return the plugin's description
*/
@NonNull PluginDescription getDescription();
PluginDescription getDescription();
/**
* Returns the created plugin if it is available.

Datei anzeigen

@ -1,16 +1,12 @@
package com.velocitypowered.api.plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Optional;
import java.util.logging.Logger;
/**
* The class that manages plugins. This manager can retrieve
* {@link PluginContainer}s from {@link Plugin} instances, getting
* {@link Logger}s, etc.
* Manages plugins loaded on the proxy. This manager can retrieve {@link PluginContainer}s from plugin instances
* and inject arbitrary JAR files into the plugin classpath with {@link #addToClasspath(Object, Path)}.
*/
public interface PluginManager {
/**
@ -19,7 +15,7 @@ public interface PluginManager {
* @param instance the instance
* @return the container
*/
@NonNull Optional<PluginContainer> fromInstance(@NonNull Object instance);
Optional<PluginContainer> fromInstance(Object instance);
/**
* Retrieves a {@link PluginContainer} based on its ID.
@ -27,22 +23,22 @@ public interface PluginManager {
* @param id the plugin ID
* @return the plugin, if available
*/
@NonNull Optional<PluginContainer> getPlugin(@NonNull String id);
Optional<PluginContainer> getPlugin(String id);
/**
* Gets a {@link Collection} of all {@link PluginContainer}s.
*
* @return the plugins
*/
@NonNull Collection<PluginContainer> getPlugins();
Collection<PluginContainer> getPlugins();
/**
* Checks if a plugin is loaded based on its ID.
*
* @param id the id of the {@link Plugin}
* @param id the id of the plugin
* @return {@code true} if loaded
*/
boolean isLoaded(@NonNull String id);
boolean isLoaded(String id);
/**
* Adds the specified {@code path} to the plugin classpath.
@ -51,5 +47,5 @@ public interface PluginManager {
* @param path the path to the JAR you want to inject into the classpath
* @throws UnsupportedOperationException if the operation is not applicable to this plugin
*/
void addToClasspath(@NonNull Object plugin, @NonNull Path path);
void addToClasspath(Object plugin, Path path);
}

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.api.plugin.meta;
import javax.annotation.Nullable;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.Optional;
@ -13,7 +14,8 @@ import static com.google.common.base.Strings.emptyToNull;
*/
public final class PluginDependency {
private final String id;
@Nullable private final String version;
@Nullable
private final String version;
private final boolean optional;
@ -53,7 +55,7 @@ public final class PluginDependency {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PluginDependency that = (PluginDependency) o;

Datei anzeigen

@ -13,7 +13,6 @@ import com.velocitypowered.api.util.title.Title;
import java.util.List;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
import java.util.UUID;
@ -62,7 +61,7 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
* Sends a chat message to the player's client.
* @param component the chat message to send
*/
default void sendMessage(@NonNull Component component) {
default void sendMessage(Component component) {
sendMessage(component, MessagePosition.CHAT);
}
@ -71,14 +70,14 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
* @param component the chat message to send
* @param position the position for the message
*/
void sendMessage(@NonNull Component component, @NonNull MessagePosition position);
void sendMessage(Component component, MessagePosition position);
/**
* Creates a new connection request so that the player can connect to another server.
* @param server the server to connect to
* @return a new connection request
*/
ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server);
ConnectionRequestBuilder createConnectionRequest(RegisteredServer server);
/**
* Gets the player's profile properties.

Datei anzeigen

@ -9,6 +9,7 @@ import com.velocitypowered.api.proxy.messages.ChannelRegistrar;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.scheduler.Scheduler;
import com.velocitypowered.api.util.ProxyVersion;
import net.kyori.text.Component;
import java.net.InetSocketAddress;
@ -131,4 +132,10 @@ public interface ProxyServer {
* @return the proxy config
* */
ProxyConfig getConfiguration();
/**
* Returns the version of the proxy.
* @return the proxy version
*/
ProxyVersion getVersion();
}

Datei anzeigen

@ -2,6 +2,7 @@ package com.velocitypowered.api.proxy.messages;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
@ -27,7 +28,7 @@ public final class LegacyChannelIdentifier implements ChannelIdentifier {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LegacyChannelIdentifier that = (LegacyChannelIdentifier) o;

Datei anzeigen

@ -2,6 +2,7 @@ package com.velocitypowered.api.proxy.messages;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.regex.Pattern;
@ -54,11 +55,11 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
@Override
public String toString() {
return getId() + " (modern)";
return namespace + ":" + name + " (modern)";
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MinecraftChannelIdentifier that = (MinecraftChannelIdentifier) o;

Datei anzeigen

@ -3,6 +3,7 @@ package com.velocitypowered.api.proxy.player;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.util.GameProfile;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection;
import java.util.Optional;
@ -47,5 +48,5 @@ public interface TabList {
// Necessary because the TabListEntry implementation isn't in the api
@Deprecated
TabListEntry buildEntry(GameProfile profile, Component displayName, int latency, int gameMode);
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, int gameMode);
}

Datei anzeigen

@ -1,9 +1,7 @@
package com.velocitypowered.api.proxy.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.util.GameProfile;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
@ -16,7 +14,7 @@ public interface TabListEntry {
* Returns the parent {@link TabList} of this {@code this} {@link TabListEntry}.
* @return parent {@link TabList}
*/
@NonNull TabList getTabList();
TabList getTabList();
/**
* Returns the {@link GameProfile} of the entry, which uniquely identifies the entry
@ -24,14 +22,14 @@ public interface TabListEntry {
* as the player head in the tab list.
* @return {@link GameProfile} of the entry
*/
@NonNull GameProfile getProfile();
GameProfile getProfile();
/**
* Returns {@link Optional} text {@link Component}, which if present is the text displayed for
* {@code this} entry in the {@link TabList}, otherwise {@link GameProfile#getName()} is shown.
* @return {@link Optional} text {@link Component} of name displayed in the tab list
*/
@NonNull Optional<Component> getDisplayName();
Optional<Component> getDisplayName();
/**
* Sets the text {@link Component} to be displayed for {@code this} {@link TabListEntry}.
@ -39,7 +37,7 @@ public interface TabListEntry {
* @param displayName to show in the {@link TabList} for {@code this} entry
* @return {@code this}, for chaining
*/
@NonNull TabListEntry setDisplayName(@Nullable Component displayName);
TabListEntry setDisplayName(@Nullable Component displayName);
/**
* Returns the latency for {@code this} entry.
@ -63,7 +61,7 @@ public interface TabListEntry {
* @param latency to changed to
* @return {@code this}, for chaining
*/
@NonNull TabListEntry setLatency(int latency);
TabListEntry setLatency(int latency);
/**
* Gets the game mode {@code this} entry has been set to.
@ -99,9 +97,9 @@ public interface TabListEntry {
* @see TabListEntry
*/
class Builder {
private TabList tabList;
private GameProfile profile;
private Component displayName;
private @Nullable TabList tabList;
private @Nullable GameProfile profile;
private @Nullable Component displayName;
private int latency = 0;
private int gameMode = 0;
@ -167,9 +165,12 @@ public interface TabListEntry {
* @return the constructed {@link TabListEntry}
*/
public TabListEntry build() {
Preconditions.checkState(tabList != null, "The Tablist must be set when building a TabListEntry");
Preconditions.checkState(profile != null, "The GameProfile must be set when building a TabListEntry");
if (tabList == null) {
throw new IllegalStateException("The Tablist must be set when building a TabListEntry");
}
if (profile == null) {
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
}
return tabList.buildEntry(profile, displayName, latency, gameMode);
}
}

Datei anzeigen

@ -1,7 +1,7 @@
package com.velocitypowered.api.proxy.server;
import com.google.common.base.Preconditions;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.net.InetSocketAddress;
import java.util.Objects;
@ -10,24 +10,24 @@ import java.util.Objects;
* ServerInfo represents a server that a player can connect to. This object is immutable and safe for concurrent access.
*/
public final class ServerInfo {
private final @NonNull String name;
private final @NonNull InetSocketAddress address;
private final String name;
private final InetSocketAddress address;
/**
* Creates a new ServerInfo object.
* @param name the name for the server
* @param address the address of the server to connect to
*/
public ServerInfo(@NonNull String name, @NonNull InetSocketAddress address) {
public ServerInfo(String name, InetSocketAddress address) {
this.name = Preconditions.checkNotNull(name, "name");
this.address = Preconditions.checkNotNull(address, "address");
}
public final @NonNull String getName() {
public final String getName() {
return name;
}
public final @NonNull InetSocketAddress getAddress() {
public final InetSocketAddress getAddress() {
return address;
}
@ -40,7 +40,7 @@ public final class ServerInfo {
}
@Override
public final boolean equals(Object o) {
public final boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServerInfo that = (ServerInfo) o;

Datei anzeigen

@ -14,7 +14,7 @@ import java.util.*;
*/
public final class ServerPing {
private final Version version;
private final Players players;
private final @Nullable Players players;
private final Component description;
private final @Nullable Favicon favicon;
private final @Nullable ModInfo modinfo;
@ -89,14 +89,14 @@ public final class ServerPing {
* A builder for {@link ServerPing} objects.
*/
public static final class Builder {
private Version version;
private Version version = new Version(0, "Unknown");
private int onlinePlayers;
private int maximumPlayers;
private final List<SamplePlayer> samplePlayers = new ArrayList<>();
private String modType;
private String modType = "FML";
private final List<ModInfo.Mod> mods = new ArrayList<>();
private Component description;
private Favicon favicon;
private @Nullable Component description;
private @Nullable Favicon favicon;
private boolean nullOutPlayers;
private boolean nullOutModinfo;
@ -165,6 +165,12 @@ public final class ServerPing {
}
public ServerPing build() {
if (this.version == null) {
throw new IllegalStateException("version not specified");
}
if (this.description == null) {
throw new IllegalStateException("no server description supplied");
}
return new ServerPing(version, nullOutPlayers ? null : new Players(onlinePlayers, maximumPlayers, samplePlayers),
description, favicon, nullOutModinfo ? null : new ModInfo(modType, mods));
}
@ -185,12 +191,12 @@ public final class ServerPing {
return samplePlayers;
}
public Component getDescription() {
return description;
public Optional<Component> getDescription() {
return Optional.ofNullable(description);
}
public Favicon getFavicon() {
return favicon;
public Optional<Favicon> getFavicon() {
return Optional.ofNullable(favicon);
}
public String getModType() {

Datei anzeigen

@ -1,5 +1,8 @@
package com.velocitypowered.api.scheduler;
import org.checkerframework.common.value.qual.IntRange;
import org.checkerframework.common.value.qual.IntRangeFromNonNegative;
import java.util.concurrent.TimeUnit;
/**
@ -24,7 +27,7 @@ public interface Scheduler {
* @param unit the unit of time for {@code time}
* @return this builder, for chaining
*/
TaskBuilder delay(long time, TimeUnit unit);
TaskBuilder delay(@IntRange(from = 0) long time, TimeUnit unit);
/**
* Specifies that the task should continue running after waiting for the specified amount, until it is cancelled.
@ -32,7 +35,7 @@ public interface Scheduler {
* @param unit the unit of time for {@code time}
* @return this builder, for chaining
*/
TaskBuilder repeat(long time, TimeUnit unit);
TaskBuilder repeat(@IntRange(from = 0) long time, TimeUnit unit);
/**
* Clears the delay on this task.

Datei anzeigen

@ -1,7 +1,7 @@
package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@ -25,7 +25,7 @@ public final class Favicon {
* of functions.
* @param base64Url the url for use with this favicon
*/
public Favicon(@NonNull String base64Url) {
public Favicon(String base64Url) {
this.base64Url = Preconditions.checkNotNull(base64Url, "base64Url");
}
@ -38,7 +38,7 @@ public final class Favicon {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Favicon favicon = (Favicon) o;
@ -62,7 +62,7 @@ public final class Favicon {
* @param image the image to use for the favicon
* @return the created {@link Favicon} instance
*/
public static Favicon create(@NonNull BufferedImage image) {
public static Favicon create(BufferedImage image) {
Preconditions.checkNotNull(image, "image");
Preconditions.checkArgument(image.getWidth() == 64 && image.getHeight() == 64, "Image does not have" +
" 64x64 dimensions (found %sx%s)", image.getWidth(), image.getHeight());
@ -81,7 +81,7 @@ public final class Favicon {
* @return the created {@link Favicon} instance
* @throws IOException if the file could not be read from the path
*/
public static Favicon create(@NonNull Path path) throws IOException {
public static Favicon create(Path path) throws IOException {
try (InputStream stream = Files.newInputStream(path)) {
return create(ImageIO.read(stream));
}

Datei anzeigen

@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.UUID;
@ -15,7 +14,7 @@ public final class GameProfile {
private final String name;
private final List<Property> properties;
public GameProfile(@NonNull String id, @NonNull String name, @NonNull List<Property> properties) {
public GameProfile(String id, String name, List<Property> properties) {
this.id = Preconditions.checkNotNull(id, "id");
this.name = Preconditions.checkNotNull(name, "name");
this.properties = ImmutableList.copyOf(properties);
@ -42,7 +41,7 @@ public final class GameProfile {
* @param username the username to use
* @return the new offline-mode game profile
*/
public static GameProfile forOfflinePlayer(@NonNull String username) {
public static GameProfile forOfflinePlayer(String username) {
Preconditions.checkNotNull(username, "username");
String id = UuidUtils.toUndashed(UuidUtils.generateOfflinePlayerUuid(username));
return new GameProfile(id, username, ImmutableList.of());
@ -62,7 +61,7 @@ public final class GameProfile {
private final String value;
private final String signature;
public Property(@NonNull String name, @NonNull String value, @NonNull String signature) {
public Property(String name, String value, String signature) {
this.name = Preconditions.checkNotNull(name, "name");
this.value = Preconditions.checkNotNull(value, "value");
this.signature = Preconditions.checkNotNull(signature, "signature");

Datei anzeigen

@ -0,0 +1,57 @@
package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
/**
* Provides a version object for the proxy.
*/
public final class ProxyVersion {
private final String name;
private final String vendor;
private final String version;
public ProxyVersion(String name, String vendor, String version) {
this.name = Preconditions.checkNotNull(name, "name");
this.vendor = Preconditions.checkNotNull(vendor, "vendor");
this.version = Preconditions.checkNotNull(version, "version");
}
public String getName() {
return name;
}
public String getVendor() {
return vendor;
}
public String getVersion() {
return version;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProxyVersion that = (ProxyVersion) o;
return Objects.equals(name, that.name) &&
Objects.equals(vendor, that.vendor) &&
Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(name, vendor, version);
}
@Override
public String toString() {
return "ProxyVersion{" +
"name='" + name + '\'' +
", vendor='" + vendor + '\'' +
", version='" + version + '\'' +
'}';
}
}

Datei anzeigen

@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@ -21,7 +20,7 @@ public final class UuidUtils {
* @param string the string to convert
* @return the UUID object
*/
public static @NonNull UUID fromUndashed(final @NonNull String string) {
public static UUID fromUndashed(final String string) {
Objects.requireNonNull(string, "string");
Preconditions.checkArgument(string.length() == 32, "Length is incorrect");
return new UUID(
@ -35,7 +34,7 @@ public final class UuidUtils {
* @param uuid the UUID to convert
* @return the undashed UUID
*/
public static @NonNull String toUndashed(final @NonNull UUID uuid) {
public static String toUndashed(final UUID uuid) {
Preconditions.checkNotNull(uuid, "uuid");
return Strings.padStart(Long.toHexString(uuid.getMostSignificantBits()), 16, '0') +
Strings.padStart(Long.toHexString(uuid.getLeastSignificantBits()), 16, '0');
@ -46,7 +45,7 @@ public final class UuidUtils {
* @param username the username to use
* @return the offline mode UUID
*/
public static @NonNull UUID generateOfflinePlayerUuid(@NonNull String username) {
public static UUID generateOfflinePlayerUuid(String username) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
}
}

Datei anzeigen

@ -11,8 +11,8 @@ import java.util.Optional;
* Represents a "full" title, including all components. This class is immutable.
*/
public final class TextTitle implements Title {
private final Component title;
private final Component subtitle;
private final @Nullable Component title;
private final @Nullable Component subtitle;
private final int stay;
private final int fadeIn;
private final int fadeOut;
@ -94,7 +94,7 @@ public final class TextTitle implements Title {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TextTitle textTitle = (TextTitle) o;
@ -193,12 +193,12 @@ public final class TextTitle implements Title {
return this;
}
public Component getTitle() {
return title;
public Optional<Component> getTitle() {
return Optional.ofNullable(title);
}
public Component getSubtitle() {
return subtitle;
public Optional<Component> getSubtitle() {
return Optional.ofNullable(subtitle);
}
public int getStay() {

Datei anzeigen

@ -16,6 +16,7 @@ allprojects {
log4jVersion = '2.11.1'
nettyVersion = '4.1.30.Final'
guavaVersion = '25.1-jre'
checkerFrameworkVersion = '2.5.6'
getCurrentBranchName = {
new ByteArrayOutputStream().withStream { os ->

Datei anzeigen

@ -0,0 +1,70 @@
///////////////////////////////////////////////////////////////////////////
/// Checker Framework pluggable type-checking
///
repositories {
mavenCentral()
}
configurations {
checkerFrameworkCheckerJar {
description = 'the Checker Framework, including the Type Annotations compiler'
}
checkerFrameworkAnnotatedJDK {
description = 'a copy of JDK classes with Checker Framework type qualifers inserted'
}
}
// By default, use Checker Framework from Maven Central.
// Pass -PcfLocal to use a locally-built version of the Checker Framework.
dependencies {
if (!rootProject.hasProperty('cfLocal')) {
checkerFrameworkAnnotatedJDK "org.checkerframework:jdk8:${checkerFrameworkVersion}"
checkerFrameworkCheckerJar "org.checkerframework:checker:${checkerFrameworkVersion}"
implementation "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
} else if (System.getenv("CHECKERFRAMEWORK") == null) {
throw new GradleException("Environment variable CHECKERFRAMEWORK is not set")
} else if (! file(System.getenv("CHECKERFRAMEWORK")).exists()) {
throw new GradleException("Environment variable CHECKERFRAMEWORK is set to non-existent directory " + System.getenv("CHECKERFRAMEWORK"));
} else {
ext.checkerframeworkdist = "$System.env.CHECKERFRAMEWORK/checker/dist"
checkerFrameworkAnnotatedJDK fileTree(dir: "${ext.checkerframeworkdist}", include: "jdk8.jar")
checkerFrameworkCheckerJar fileTree(dir: "${ext.checkerframeworkdist}", include: 'checker.jar')
implementation fileTree(dir: "${ext.checkerframeworkdist}", include: 'checker-qual.jar')
}
}
// // To type-check all projects.
// allprojects {
// tasks.withType(JavaCompile).all { JavaCompile compile ->
// compile.doFirst {
// compile.options.compilerArgs = [
// '-processor', 'org.checkerframework.checker.formatter.FormatterChecker,org.checkerframework.checker.index.IndexChecker,org.checkerframework.checker.lock.LockChecker,org.checkerframework.checker.nullness.NullnessChecker,org.checkerframework.checker.signature.SignatureChecker',
// '-Xmaxerrs', '10000',
// '-Awarns', // -Awarns turns Checker Framework errors into warnings
// '-AcheckPurityAnnotations',
// '-processorpath', "${configurations.checkerFrameworkCheckerJar.asPath}",
// "-Xbootclasspath/p:${configurations.checkerFrameworkAnnotatedJDK.asPath}",
// "-Astubs=$System.env.CHECKERFRAMEWORK/checker/resources/javadoc.astub" // TODO: does not work when downloading from Maven Central
// ]
// }
// }
// }
// To typecheck only the current project's main source set (in a multi-project
// build), use this instead:
compileJava {
doFirst {
options.compilerArgs = [
'-processor', 'org.checkerframework.checker.nullness.NullnessChecker',
'-processor', 'org.checkerframework.checker.optional.OptionalChecker',
'-Xmaxerrs', '10000',
'-Xmaxwarns', '10000',
// '-Awarns', // -Awarns turns Checker Framework errors into warnings
//'-AcheckPurityAnnotations', // Disabled for Velocity, wish we could do better
'-processorpath', "${configurations.checkerFrameworkCheckerJar.asPath}",
"-Xbootclasspath/p:${configurations.checkerFrameworkAnnotatedJDK.asPath}"
]
}
}

Datei anzeigen

@ -2,9 +2,13 @@ plugins {
id 'java'
}
apply from: '../gradle/checkerframework.gradle'
dependencies {
compile "com.google.guava:guava:${guavaVersion}"
compile "io.netty:netty-buffer:${nettyVersion}"
compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
}

Datei anzeigen

@ -1,88 +1,64 @@
package com.velocitypowered.natives.util;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
public class NativeCodeLoader<T> implements Supplier<T> {
private final List<Variant<T>> variants;
private volatile Variant<T> selected;
public final class NativeCodeLoader<T> implements Supplier<T> {
private final Variant<T> selected;
public NativeCodeLoader(List<Variant<T>> variants) {
this.variants = ImmutableList.copyOf(variants);
NativeCodeLoader(List<Variant<T>> variants) {
this.selected = getVariant(variants);
}
@Override
public T get() {
return tryLoad().object;
return selected.object;
}
private Variant<T> tryLoad() {
if (selected != null) {
return selected;
}
synchronized (this) {
if (selected != null) {
return selected;
private static <T> Variant<T> getVariant(List<Variant<T>> variants) {
for (Variant<T> variant : variants) {
T got = variant.get();
if (got == null) {
continue;
}
for (Variant<T> variant : variants) {
T got = variant.get();
if (got == null) {
continue;
}
selected = variant;
return selected;
}
throw new IllegalArgumentException("Can't find any suitable variants");
return variant;
}
throw new IllegalArgumentException("Can't find any suitable variants");
}
public String getLoadedVariant() {
return tryLoad().name;
return selected.name;
}
static class Variant<T> {
private volatile boolean available;
private Status status;
private final Runnable setup;
private final String name;
private final T object;
private volatile boolean hasBeenSetup = false;
Variant(BooleanSupplier available, Runnable setup, String name, T object) {
this.available = available.getAsBoolean();
Variant(BooleanSupplier possiblyAvailable, Runnable setup, String name, T object) {
this.status = possiblyAvailable.getAsBoolean() ? Status.POSSIBLY_AVAILABLE : Status.NOT_AVAILABLE;
this.setup = setup;
this.name = name;
this.object = object;
}
public T get() {
if (!available) {
public @Nullable T get() {
if (status == Status.NOT_AVAILABLE || status == Status.SETUP_FAILURE) {
return null;
}
// Make sure setup happens only once
if (!hasBeenSetup) {
synchronized (this) {
// We change availability if need be below, may as well check it again here for sanity.
if (!available) {
return null;
}
// Okay, now try the setup if we haven't done so yet.
if (!hasBeenSetup) {
try {
setup.run();
hasBeenSetup = true;
return object;
} catch (Exception e) {
available = false;
return null;
}
}
if (status == Status.POSSIBLY_AVAILABLE) {
try {
setup.run();
status = Status.SETUP;
} catch (Exception e) {
status = Status.SETUP_FAILURE;
return null;
}
}
@ -90,9 +66,16 @@ public class NativeCodeLoader<T> implements Supplier<T> {
}
}
static final BooleanSupplier MACOS = () -> System.getProperty("os.name").equalsIgnoreCase("Mac OS X") &&
private enum Status {
NOT_AVAILABLE,
POSSIBLY_AVAILABLE,
SETUP,
SETUP_FAILURE
}
static final BooleanSupplier MACOS = () -> System.getProperty("os.name", "").equalsIgnoreCase("Mac OS X") &&
System.getProperty("os.arch").equals("x86_64");
static final BooleanSupplier LINUX = () -> System.getProperties().getProperty("os.name").equalsIgnoreCase("Linux") &&
static final BooleanSupplier LINUX = () -> System.getProperties().getProperty("os.name", "").equalsIgnoreCase("Linux") &&
System.getProperty("os.arch").equals("amd64");
static final BooleanSupplier ALWAYS = () -> true;
}

Datei anzeigen

@ -8,6 +8,7 @@ import com.velocitypowered.natives.encryption.JavaVelocityCipher;
import com.velocitypowered.natives.encryption.VelocityCipherFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@ -21,7 +22,12 @@ public class Natives {
return () -> {
try {
Path tempFile = Files.createTempFile("native-", path.substring(path.lastIndexOf('.')));
Files.copy(Natives.class.getResourceAsStream(path), tempFile, StandardCopyOption.REPLACE_EXISTING);
InputStream nativeLib = Natives.class.getResourceAsStream(path);
if (nativeLib == null) {
throw new IllegalStateException("Native library " + path + " not found.");
}
Files.copy(nativeLib, tempFile, StandardCopyOption.REPLACE_EXISTING);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
Files.deleteIfExists(tempFile);

Datei anzeigen

@ -4,6 +4,8 @@ plugins {
id 'de.sebastianboegl.shadow.transformer.log4j' version '2.1.1'
}
apply from: '../gradle/checkerframework.gradle'
compileJava {
options.compilerArgs += ['-proc:none']
}

Datei anzeigen

@ -8,8 +8,6 @@ import java.text.DecimalFormat;
public class Velocity {
private static final Logger logger = LogManager.getLogger(Velocity.class);
private static long startTime;
static {
// We use BufferedImage for favicons, and on macOS this puts the Java application in the dock. How inconvenient.
// Force AWT to work with its head chopped off.
@ -17,8 +15,7 @@ public class Velocity {
}
public static void main(String... args) {
startTime = System.currentTimeMillis();
logger.info("Booting up Velocity {}...", Velocity.class.getPackage().getImplementationVersion());
long startTime = System.currentTimeMillis();
VelocityServer server = new VelocityServer();
server.start();

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.proxy;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
@ -7,12 +8,14 @@ import com.google.gson.GsonBuilder;
import com.velocitypowered.api.event.EventManager;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.api.util.ProxyVersion;
import com.velocitypowered.proxy.command.ServerCommand;
import com.velocitypowered.proxy.command.ShutdownCommand;
import com.velocitypowered.proxy.command.VelocityCommand;
@ -39,6 +42,7 @@ import net.kyori.text.TextComponent;
import net.kyori.text.serializer.GsonComponentSerializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.*;
import java.net.InetSocketAddress;
import java.nio.file.Files;
@ -58,36 +62,54 @@ public class VelocityServer implements ProxyServer {
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
.create();
private final ConnectionManager cm = new ConnectionManager(this);
private VelocityConfiguration configuration;
private NettyHttpClient httpClient;
private KeyPair serverKeyPair;
private final ServerMap servers = new ServerMap(this);
private @MonotonicNonNull ConnectionManager cm;
private @MonotonicNonNull VelocityConfiguration configuration;
private @MonotonicNonNull NettyHttpClient httpClient;
private @MonotonicNonNull KeyPair serverKeyPair;
private @MonotonicNonNull ServerMap servers;
private final VelocityCommandManager commandManager = new VelocityCommandManager();
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
private boolean shutdown = false;
private final VelocityPluginManager pluginManager = new VelocityPluginManager(this);
private @MonotonicNonNull VelocityPluginManager pluginManager;
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
private final VelocityConsole console = new VelocityConsole(this);
private Ratelimiter ipAttemptLimiter;
private VelocityEventManager eventManager;
private VelocityScheduler scheduler;
private VelocityChannelRegistrar channelRegistrar;
VelocityServer() {
commandManager.register(new VelocityCommand(), "velocity");
commandManager.register(new ServerCommand(this), "server");
commandManager.register(new ShutdownCommand(this), "shutdown", "end");
}
private @MonotonicNonNull VelocityConsole console;
private @MonotonicNonNull Ratelimiter ipAttemptLimiter;
private @MonotonicNonNull VelocityEventManager eventManager;
private @MonotonicNonNull VelocityScheduler scheduler;
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
public KeyPair getServerKeyPair() {
if (serverKeyPair == null) {
throw new AssertionError();
}
return serverKeyPair;
}
public VelocityConfiguration getConfiguration() {
return configuration;
VelocityConfiguration cfg = this.configuration;
if (cfg == null) {
throw new IllegalStateException("Configuration not initialized!");
}
return cfg;
}
@Override
public ProxyVersion getVersion() {
Package pkg = VelocityServer.class.getPackage();
String implName, implVersion, implVendor;
if (pkg != null) {
implName = MoreObjects.firstNonNull(pkg.getImplementationTitle(), "Velocity");
implVersion = MoreObjects.firstNonNull(pkg.getImplementationVersion(), "<unknown>");
implVendor = MoreObjects.firstNonNull(pkg.getImplementationVendor(), "Velocity Contributors");
} else {
implName = "Velocity";
implVersion = "<unknown>";
implVendor = "Velocity Contributors";
}
return new ProxyVersion(implName, implVendor, implVersion);
}
@Override
@ -95,7 +117,25 @@ public class VelocityServer implements ProxyServer {
return commandManager;
}
@EnsuresNonNull({"serverKeyPair", "servers", "pluginManager", "eventManager", "scheduler", "console", "cm", "configuration"})
public void start() {
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
pluginManager = new VelocityPluginManager(this);
eventManager = new VelocityEventManager(pluginManager);
scheduler = new VelocityScheduler(pluginManager);
console = new VelocityConsole(this);
cm = new ConnectionManager(this);
servers = new ServerMap(this);
cm.logChannelInformation();
// Initialize commands first
commandManager.register(new VelocityCommand(this), "velocity");
commandManager.register(new ServerCommand(this), "server");
commandManager.register(new ShutdownCommand(this), "shutdown", "end");
try {
Path configPath = Paths.get("velocity.toml");
configuration = VelocityConfiguration.read(configPath);
@ -118,22 +158,13 @@ public class VelocityServer implements ProxyServer {
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
}
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit());
httpClient = new NettyHttpClient(this);
eventManager = new VelocityEventManager(pluginManager);
scheduler = new VelocityScheduler(pluginManager);
channelRegistrar = new VelocityChannelRegistrar();
loadPlugins();
try {
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
// to fully initialize before we accept any connections to the server.
eventManager.fire(new ProxyInitializeEvent()).get();
} catch (InterruptedException | ExecutionException e) {
// Ignore, we don't care. InterruptedException is unlikely to happen (and if it does, you've got bigger
// issues) and there is almost no chance ExecutionException will be thrown.
}
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
// to fully initialize before we accept any connections to the server.
eventManager.fire(new ProxyInitializeEvent()).join();
// init console permissions after plugins are loaded
console.setupPermissions();
@ -145,6 +176,7 @@ public class VelocityServer implements ProxyServer {
}
}
@RequiresNonNull({"pluginManager", "eventManager"})
private void loadPlugins() {
logger.info("Loading plugins...");
@ -166,18 +198,20 @@ public class VelocityServer implements ProxyServer {
}
// Register the plugin main classes so that we may proceed with firing the proxy initialize event
pluginManager.getPlugins().forEach(container -> {
container.getInstance().ifPresent(plugin -> eventManager.register(plugin, plugin));
});
for (PluginContainer plugin : pluginManager.getPlugins()) {
Optional<?> instance = plugin.getInstance();
if (instance.isPresent()) {
eventManager.register(instance.get(), instance.get());
}
}
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
}
public ServerMap getServers() {
return servers;
}
public Bootstrap initializeGenericBootstrap() {
if (cm == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return this.cm.createWorker();
}
@ -186,6 +220,10 @@ public class VelocityServer implements ProxyServer {
}
public void shutdown() {
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
throw new AssertionError();
}
if (!shutdownInProgress.compareAndSet(false, true)) {
return;
}
@ -210,10 +248,16 @@ public class VelocityServer implements ProxyServer {
}
public NettyHttpClient getHttpClient() {
if (httpClient == null) {
throw new IllegalStateException("HTTP client not initialized");
}
return httpClient;
}
public Ratelimiter getIpAttemptLimiter() {
if (ipAttemptLimiter == null) {
throw new IllegalStateException("Ratelimiter not initialized");
}
return ipAttemptLimiter;
}
@ -268,41 +312,65 @@ public class VelocityServer implements ProxyServer {
@Override
public Optional<RegisteredServer> getServer(String name) {
Preconditions.checkNotNull(name, "name");
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.getServer(name);
}
@Override
public Collection<RegisteredServer> getAllServers() {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.getAllServers();
}
@Override
public RegisteredServer registerServer(ServerInfo server) {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.register(server);
}
@Override
public void unregisterServer(ServerInfo server) {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
servers.unregister(server);
}
@Override
public VelocityConsole getConsoleCommandSource() {
if (console == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return console;
}
@Override
public PluginManager getPluginManager() {
if (pluginManager == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return pluginManager;
}
@Override
public EventManager getEventManager() {
if (eventManager == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return eventManager;
}
@Override
public VelocityScheduler getScheduler() {
if (scheduler == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return scheduler;
}
@ -313,6 +381,9 @@ public class VelocityServer implements ProxyServer {
@Override
public InetSocketAddress getBoundAddress() {
if (configuration == null) {
throw new IllegalStateException("No configuration"); // even though you'll never get the chance... heh, heh
}
return configuration.getBind();
}
}

Datei anzeigen

@ -27,7 +27,7 @@ public class ServerCommand implements Command {
}
@Override
public void execute(CommandSource source, String[] args) {
public void execute(CommandSource source, String @NonNull [] args) {
if (!(source instanceof Player)) {
source.sendMessage(TextComponent.of("Only players may run this command.", TextColor.RED));
return;
@ -76,7 +76,7 @@ public class ServerCommand implements Command {
}
@Override
public List<String> suggest(CommandSource source, String[] currentArgs) {
public List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
if (currentArgs.length == 0) {
return server.getAllServers().stream()
.map(rs -> rs.getServerInfo().getName())
@ -92,7 +92,7 @@ public class ServerCommand implements Command {
}
@Override
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
return source.getPermissionValue("velocity.command.server") != Tristate.FALSE;
}
}

Datei anzeigen

@ -15,7 +15,7 @@ public class ShutdownCommand implements Command {
}
@Override
public void execute(CommandSource source, String[] args) {
public void execute(CommandSource source, String @NonNull [] args) {
if (source != server.getConsoleCommandSource()) {
source.sendMessage(TextComponent.of("You are not allowed to use this command.", TextColor.RED));
return;
@ -24,7 +24,7 @@ public class ShutdownCommand implements Command {
}
@Override
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
return source == server.getConsoleCommandSource();
}
}

Datei anzeigen

@ -1,12 +1,12 @@
package com.velocitypowered.proxy.command;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.velocitypowered.api.command.Command;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.permission.Tristate;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.util.ProxyVersion;
import net.kyori.text.TextComponent;
import net.kyori.text.event.ClickEvent;
import net.kyori.text.format.TextColor;
@ -20,9 +20,13 @@ import java.util.Map;
import java.util.stream.Collectors;
public class VelocityCommand implements Command {
private final Map<String, Command> subcommands = ImmutableMap.<String, Command>builder()
.put("version", Info.INSTANCE)
.build();
private final Map<String, Command> subcommands;
public VelocityCommand(ProxyServer server) {
this.subcommands = ImmutableMap.<String, Command>builder()
.put("version", new Info(server))
.build();
}
private void usage(CommandSource source) {
String commandText = "/velocity <" + String.join("|", subcommands.keySet()) + ">";
@ -30,7 +34,7 @@ public class VelocityCommand implements Command {
}
@Override
public void execute(CommandSource source, String[] args) {
public void execute(CommandSource source, String @NonNull [] args) {
if (args.length == 0) {
usage(source);
return;
@ -41,11 +45,13 @@ public class VelocityCommand implements Command {
usage(source);
return;
}
command.execute(source, Arrays.copyOfRange(args, 1, args.length));
@SuppressWarnings("nullness")
String[] actualArgs = Arrays.copyOfRange(args, 1, args.length);
command.execute(source, actualArgs);
}
@Override
public List<String> suggest(@NonNull CommandSource source, @NonNull String[] currentArgs) {
public List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
if (currentArgs.length == 0) {
return ImmutableList.copyOf(subcommands.keySet());
}
@ -60,11 +66,13 @@ public class VelocityCommand implements Command {
if (command == null) {
return ImmutableList.of();
}
return command.suggest(source, Arrays.copyOfRange(currentArgs, 1, currentArgs.length));
@SuppressWarnings("nullness")
String[] actualArgs = Arrays.copyOfRange(currentArgs, 1, currentArgs.length);
return command.suggest(source, actualArgs);
}
@Override
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
if (args.length == 0) {
return true;
}
@ -72,29 +80,33 @@ public class VelocityCommand implements Command {
if (command == null) {
return true;
}
return command.hasPermission(source, Arrays.copyOfRange(args, 1, args.length));
@SuppressWarnings("nullness")
String[] actualArgs = Arrays.copyOfRange(args, 1, args.length);
return command.hasPermission(source, actualArgs);
}
private static class Info implements Command {
static final Info INSTANCE = new Info();
private Info() {}
private final ProxyServer server;
private Info(ProxyServer server) {
this.server = server;
}
@Override
public void execute(@NonNull CommandSource source, @NonNull String[] args) {
String implName = MoreObjects.firstNonNull(VelocityServer.class.getPackage().getImplementationTitle(), "Velocity");
String implVersion = MoreObjects.firstNonNull(VelocityServer.class.getPackage().getImplementationVersion(), "<unknown>");
String implVendor = MoreObjects.firstNonNull(VelocityServer.class.getPackage().getImplementationVendor(), "Velocity Contributors");
TextComponent velocity = TextComponent.builder(implName + " ")
public void execute(CommandSource source, String @NonNull [] args) {
ProxyVersion version = server.getVersion();
TextComponent velocity = TextComponent.builder(version.getName() + " ")
.decoration(TextDecoration.BOLD, true)
.color(TextColor.DARK_AQUA)
.append(TextComponent.of(implVersion).decoration(TextDecoration.BOLD, false))
.append(TextComponent.of(version.getVersion()).decoration(TextDecoration.BOLD, false))
.build();
TextComponent copyright = TextComponent.of("Copyright 2018 " + implVendor + ". " + implName + " is freely licensed under the terms of the " +
TextComponent copyright = TextComponent.of("Copyright 2018 " + version.getVendor() + ". " + version.getName() + " is freely licensed under the terms of the " +
"MIT License.");
source.sendMessage(velocity);
source.sendMessage(copyright);
if (implName.equals("Velocity")) {
if (version.getName().equals("Velocity")) {
TextComponent velocityWebsite = TextComponent.builder()
.content("Visit the ")
.append(TextComponent.builder("Velocity website")
@ -112,7 +124,7 @@ public class VelocityCommand implements Command {
}
@Override
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
return source.getPermissionValue("velocity.command.info") != Tristate.FALSE;
}
}

Datei anzeigen

@ -1,9 +1,11 @@
package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.command.Command;
import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.command.CommandSource;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.*;
@ -11,7 +13,7 @@ public class VelocityCommandManager implements CommandManager {
private final Map<String, Command> commands = new HashMap<>();
@Override
public void register(final Command command, final String... aliases) {
public void register(@NonNull final Command command, final String... aliases) {
Preconditions.checkNotNull(aliases, "aliases");
Preconditions.checkNotNull(command, "executor");
for (int i = 0, length = aliases.length; i < length; i++) {
@ -22,13 +24,13 @@ public class VelocityCommandManager implements CommandManager {
}
@Override
public void unregister(final String alias) {
public void unregister(@NonNull final String alias) {
Preconditions.checkNotNull(alias, "name");
this.commands.remove(alias.toLowerCase(Locale.ENGLISH));
}
@Override
public boolean execute(CommandSource source, String cmdLine) {
public boolean execute(@NonNull CommandSource source, @NonNull String cmdLine) {
Preconditions.checkNotNull(source, "invoker");
Preconditions.checkNotNull(cmdLine, "cmdLine");
@ -38,6 +40,7 @@ public class VelocityCommandManager implements CommandManager {
}
String alias = split[0];
@SuppressWarnings("nullness")
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
Command command = commands.get(alias.toLowerCase(Locale.ENGLISH));
if (command == null) {
@ -60,13 +63,13 @@ public class VelocityCommandManager implements CommandManager {
return commands.containsKey(command);
}
public Optional<List<String>> offerSuggestions(CommandSource source, String cmdLine) {
public List<String> offerSuggestions(CommandSource source, String cmdLine) {
Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine");
String[] split = cmdLine.split(" ", -1);
if (split.length == 0) {
return Optional.empty();
return ImmutableList.of();
}
String alias = split[0];
@ -78,21 +81,22 @@ public class VelocityCommandManager implements CommandManager {
availableCommands.add("/" + entry.getKey());
}
}
return Optional.of(availableCommands);
return availableCommands;
}
@SuppressWarnings("nullness")
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
Command command = commands.get(alias.toLowerCase(Locale.ENGLISH));
if (command == null) {
return Optional.empty();
return ImmutableList.of();
}
try {
if (!command.hasPermission(source, actualArgs)) {
return Optional.empty();
return ImmutableList.of();
}
return Optional.of(command.suggest(source, actualArgs));
return command.suggest(source, actualArgs);
} catch (Exception e) {
throw new RuntimeException("Unable to invoke suggestions for command " + alias + " for " + source, e);
}

Datei anzeigen

@ -116,7 +116,7 @@ public abstract class AnnotatedConfig {
// Get a key name for config
ConfigKey key = field.getAnnotation(ConfigKey.class);
String name = key == null ? field.getName() : key.value(); // Use field name if @ConfigKey annotation is not present
String name = safeKey(key == null ? field.getName() : key.value()); // Use field name if @ConfigKey annotation is not present
// Check if field is table.
Table table = field.getAnnotation(Table.class);
@ -130,7 +130,7 @@ public abstract class AnnotatedConfig {
@SuppressWarnings("unchecked")
Map<String, ?> map = (Map<String, ?>) field.get(dumpable);
for (Entry<String, ?> entry : map.entrySet()) {
lines.add(entry.getKey() + " = " + serialize(entry.getValue())); // Save map data
lines.add(safeKey(entry.getKey()) + " = " + serialize(entry.getValue())); // Save map data
}
lines.add(""); // Add empty line
continue;
@ -193,7 +193,7 @@ public abstract class AnnotatedConfig {
return value != null ? value.toString() : "null";
}
private String safeKey(String key) {
private static String safeKey(String key) {
if(key.contains(".") && !(key.indexOf('"') == 0 && key.lastIndexOf('"') == (key.length() - 1))) {
return '"' + key + '"';
}

Datei anzeigen

@ -10,6 +10,8 @@ import com.velocitypowered.proxy.util.AddressUtil;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
import java.io.Reader;
@ -78,10 +80,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
private final Query query;
@Ignore
private Component motdAsComponent;
private @MonotonicNonNull Component motdAsComponent;
@Ignore
private Favicon favicon;
private @Nullable Favicon favicon;
public VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced, Query query) {
this.servers = servers;

Datei anzeigen

@ -16,6 +16,7 @@ import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.util.ReferenceCountUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@ -35,10 +36,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
private final Channel channel;
private SocketAddress remoteAddress;
private StateRegistry state;
private MinecraftSessionHandler sessionHandler;
private @Nullable MinecraftSessionHandler sessionHandler;
private int protocolVersion;
private int nextProtocolVersion;
private MinecraftConnectionAssociation association;
private @Nullable MinecraftConnectionAssociation association;
private boolean isLegacyForge;
private final VelocityServer server;
private boolean canSendLegacyFMLResetPacket = false;
@ -74,6 +75,12 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (sessionHandler == null) {
// No session handler available, do nothing
ReferenceCountUtil.release(msg);
return;
}
if (msg instanceof MinecraftPacket) {
if (sessionHandler.beforeHandle()) {
return;
@ -197,6 +204,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
}
}
@Nullable
public MinecraftSessionHandler getSessionHandler() {
return sessionHandler;
}
@ -243,6 +251,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
channel.pipeline().addBefore(FRAME_ENCODER, CIPHER_ENCODER, new MinecraftCipherEncoder(encryptionCipher));
}
@Nullable
public MinecraftConnectionAssociation getAssociation() {
return association;
}

Datei anzeigen

@ -7,4 +7,6 @@ public class VelocityConstants {
public static final String VELOCITY_IP_FORWARDING_CHANNEL = "velocity:player_info";
public static final int FORWARDING_VERSION = 1;
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
}

Datei anzeigen

@ -4,6 +4,7 @@ import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
import com.velocitypowered.proxy.connection.forge.ForgeConstants;
@ -22,7 +23,12 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) {
this.server = server;
this.serverConn = serverConn;
this.playerSessionHandler = (ClientPlaySessionHandler) serverConn.getPlayer().getConnection().getSessionHandler();
MinecraftSessionHandler psh = serverConn.getPlayer().getConnection().getSessionHandler();
if (!(psh instanceof ClientPlaySessionHandler)) {
throw new IllegalStateException("Initializing BackendPlaySessionHandler with no backing client play session handler!");
}
this.playerSessionHandler = (ClientPlaySessionHandler) psh;
}
@Override
@ -74,6 +80,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(PluginMessage packet) {
MinecraftConnection smc = serverConn.getConnection();
if (smc == null) {
return true;
}
if (!canForwardPluginMessage(packet)) {
return true;
}
@ -105,9 +116,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
server.getEventManager().fire(event)
.thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed()) {
serverConn.getPlayer().getConnection().write(packet);
smc.write(packet);
}
}, serverConn.getConnection().eventLoop());
}, smc.eventLoop());
return true;
}
@ -152,16 +163,18 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
}
private boolean canForwardPluginMessage(PluginMessage message) {
ClientPlaySessionHandler playerHandler =
(ClientPlaySessionHandler) serverConn.getPlayer().getConnection().getSessionHandler();
MinecraftConnection mc = serverConn.getConnection();
if (mc == null) {
return false;
}
boolean isMCOrFMLMessage;
if (serverConn.getConnection().getProtocolVersion() <= ProtocolConstants.MINECRAFT_1_12_2) {
if (mc.getProtocolVersion() <= ProtocolConstants.MINECRAFT_1_12_2) {
String channel = message.getChannel();
isMCOrFMLMessage = channel.startsWith("MC|") || channel.startsWith(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL);
} else {
isMCOrFMLMessage = message.getChannel().startsWith("minecraft:");
}
return isMCOrFMLMessage || playerHandler.getClientPluginMsgChannels().contains(message.getChannel()) ||
return isMCOrFMLMessage || playerSessionHandler.getClientPluginMsgChannels().contains(message.getChannel()) ||
server.getChannelRegistrar().registered(message.getChannel());
}
}

Datei anzeigen

@ -6,6 +6,7 @@ import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.VelocityConstants;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
@ -38,6 +39,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
this.resultFuture = resultFuture;
}
private MinecraftConnection ensureMinecraftConnection() {
MinecraftConnection mc = serverConn.getConnection();
if (mc == null) {
throw new IllegalStateException("Not connected to backend server!");
}
return mc;
}
@Override
public boolean handle(EncryptionRequest packet) {
throw new IllegalStateException("Backend server is online-mode!");
@ -45,6 +54,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(LoginPluginMessage packet) {
MinecraftConnection mc = ensureMinecraftConnection();
VelocityConfiguration configuration = server.getConfiguration();
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && packet.getChannel()
.equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) {
@ -54,7 +64,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
response.setData(createForwardingData(configuration.getForwardingSecret(),
serverConn.getPlayer().getRemoteAddress().getHostString(),
serverConn.getPlayer().getProfile()));
serverConn.getConnection().write(response);
mc.write(response);
informationForwarded = true;
} else {
// Don't understand
@ -62,7 +72,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
response.setSuccess(false);
response.setId(packet.getId());
response.setData(Unpooled.EMPTY_BUFFER);
serverConn.getConnection().write(response);
mc.write(response);
}
return true;
}
@ -76,7 +86,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(SetCompression packet) {
serverConn.getConnection().setCompressionThreshold(packet.getThreshold());
ensureMinecraftConnection().setCompressionThreshold(packet.getThreshold());
return true;
}
@ -90,7 +100,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
}
// The player has been logged on to the backend server.
serverConn.getConnection().setState(StateRegistry.PLAY);
MinecraftConnection smc = ensureMinecraftConnection();
smc.setState(StateRegistry.PLAY);
VelocityServerConnection existingConnection = serverConn.getPlayer().getConnectedServer();
if (existingConnection == null) {
// Strap on the play session handler
@ -104,14 +115,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
existingConnection.disconnect();
}
serverConn.getConnection().getChannel().config().setAutoRead(false);
smc.getChannel().config().setAutoRead(false);
server.getEventManager().fire(new ServerConnectedEvent(serverConn.getPlayer(), serverConn.getServer()))
.whenCompleteAsync((x, error) -> {
resultFuture.complete(ConnectionRequestResults.SUCCESSFUL);
serverConn.getConnection().setSessionHandler(new BackendPlaySessionHandler(server, serverConn));
smc.setSessionHandler(new BackendPlaySessionHandler(server, serverConn));
serverConn.getPlayer().setConnectedServer(serverConn);
serverConn.getConnection().getChannel().config().setAutoRead(true);
}, serverConn.getConnection().eventLoop());
smc.getChannel().config().setAutoRead(true);
}, smc.eventLoop());
return true;
}

Datei anzeigen

@ -1,6 +1,8 @@
package com.velocitypowered.proxy.connection.backend;
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.base.VerifyException;
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
@ -24,10 +26,12 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Verify.verify;
import static com.velocitypowered.proxy.VelocityServer.GSON;
import static com.velocitypowered.proxy.network.Connections.*;
@ -35,7 +39,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
private final VelocityRegisteredServer registeredServer;
private final ConnectedPlayer proxyPlayer;
private final VelocityServer server;
private MinecraftConnection connection;
private @Nullable MinecraftConnection connection;
private boolean legacyForge = false;
private boolean hasCompletedJoin = false;
private boolean gracefulDisconnect = false;
@ -72,6 +76,11 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
if (future.isSuccess()) {
connection = future.channel().pipeline().get(MinecraftConnection.class);
// This is guaranteed not to be null, but Checker Framework is whining about it anyway
if (connection == null) {
throw new VerifyException("MinecraftConnection not injected into pipeline");
}
// Kick off the connection process
connection.setSessionHandler(new LoginSessionHandler(server, VelocityServerConnection.this, result));
startHandshake();
@ -93,6 +102,11 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
}
private void startHandshake() {
MinecraftConnection mc = connection;
if (mc == null) {
throw new IllegalStateException("No connection established!");
}
PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode();
// Initiate a handshake.
@ -107,17 +121,15 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString());
}
handshake.setPort(registeredServer.getServerInfo().getAddress().getPort());
connection.write(handshake);
mc.write(handshake);
int protocolVersion = proxyPlayer.getConnection().getNextProtocolVersion();
connection.setProtocolVersion(protocolVersion);
connection.setState(StateRegistry.LOGIN);
ServerLogin login = new ServerLogin();
login.setUsername(proxyPlayer.getUsername());
connection.write(login);
mc.setProtocolVersion(protocolVersion);
mc.setState(StateRegistry.LOGIN);
mc.write(new ServerLogin(proxyPlayer.getUsername()));
}
@Nullable
public MinecraftConnection getConnection() {
return connection;
}
@ -154,10 +166,16 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data");
MinecraftConnection mc = connection;
if (mc == null) {
throw new IllegalStateException("Not connected to a server!");
}
PluginMessage message = new PluginMessage();
message.setChannel(identifier.getId());
message.setData(data);
connection.write(message);
mc.write(message);
return true;
}

Datei anzeigen

@ -1,10 +1,12 @@
package com.velocitypowered.proxy.connection.client;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.forge.ForgeConstants;
@ -19,6 +21,7 @@ import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.*;
@ -36,7 +39,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
private final Set<String> clientPluginMsgChannels = new HashSet<>();
private final Queue<PluginMessage> loginPluginMessages = new ArrayDeque<>();
private final VelocityServer server;
private TabCompleteRequest outstandingTabComplete;
private @Nullable TabCompleteRequest outstandingTabComplete;
public ClientPlaySessionHandler(VelocityServer server, ConnectedPlayer player) {
this.player = player;
@ -53,8 +56,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
public boolean handle(KeepAlive packet) {
VelocityServerConnection serverConnection = player.getConnectedServer();
if (serverConnection != null && packet.getRandomId() == serverConnection.getLastPingId()) {
MinecraftConnection smc = serverConnection.getConnection();
if (smc == null) {
// eat the packet
return true;
}
player.setPing(System.currentTimeMillis() - serverConnection.getLastPingSent());
serverConnection.getConnection().write(packet);
smc.write(packet);
serverConnection.resetLastPingId();
}
return true;
@ -84,15 +92,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (serverConnection == null) {
return true;
}
MinecraftConnection smc = serverConnection.getConnection();
if (smc == null) {
return true;
}
PlayerChatEvent event = new PlayerChatEvent(player, msg);
server.getEventManager().fire(event)
.thenAcceptAsync(pme -> {
if (pme.getResult().equals(PlayerChatEvent.ChatResult.allowed())){
serverConnection.getConnection().write(packet);
} else if (pme.getResult().isAllowed() && pme.getResult().getMessage().isPresent()){
serverConnection.getConnection().write(Chat.createServerbound(pme.getResult().getMessage().get()));
PlayerChatEvent.ChatResult chatResult = pme.getResult();
if (chatResult.isAllowed()) {
Optional<String> eventMsg = pme.getResult().getMessage();
if (eventMsg.isPresent()) {
smc.write(Chat.createServerbound(eventMsg.get()));
} else {
smc.write(packet);
}
}
}, serverConnection.getConnection().eventLoop());
}, smc.eventLoop());
}
return true;
}
@ -105,10 +121,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (spacePos > 0) {
String cmd = packet.getCommand().substring(1, spacePos);
if (server.getCommandManager().hasCommand(cmd)) {
Optional<List<String>> suggestions = server.getCommandManager().offerSuggestions(player, packet.getCommand().substring(1));
if (suggestions.isPresent()) {
List<String> suggestions = server.getCommandManager().offerSuggestions(player, packet.getCommand().substring(1));
if (suggestions.size() > 0) {
TabCompleteResponse resp = new TabCompleteResponse();
resp.getOffers().addAll(suggestions.get());
resp.getOffers().addAll(suggestions);
player.getConnection().write(resp);
return true;
}
@ -121,58 +137,68 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(PluginMessage packet) {
if (PluginMessageUtil.isMCRegister(packet)) {
List<String> actuallyRegistered = new ArrayList<>();
List<String> channels = PluginMessageUtil.getChannels(packet);
for (String channel : channels) {
if (clientPluginMsgChannels.size() >= MAX_PLUGIN_CHANNELS &&
!clientPluginMsgChannels.contains(channel)) {
throw new IllegalStateException("Too many plugin message channels registered");
VelocityServerConnection serverConn = player.getConnectedServer();
MinecraftConnection backendConn = serverConn != null ? serverConn.getConnection() : null;
if (serverConn != null && backendConn != null) {
if (PluginMessageUtil.isMCRegister(packet)) {
List<String> actuallyRegistered = new ArrayList<>();
List<String> channels = PluginMessageUtil.getChannels(packet);
for (String channel : channels) {
if (clientPluginMsgChannels.size() >= MAX_PLUGIN_CHANNELS &&
!clientPluginMsgChannels.contains(channel)) {
throw new IllegalStateException("Too many plugin message channels registered");
}
if (clientPluginMsgChannels.add(channel)) {
actuallyRegistered.add(channel);
}
}
if (clientPluginMsgChannels.add(channel)) {
actuallyRegistered.add(channel);
}
}
if (actuallyRegistered.size() > 0) {
PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(player.getConnectedServer()
.getConnection().getProtocolVersion(), actuallyRegistered);
player.getConnectedServer().getConnection().write(newRegisterPacket);
}
} else if (PluginMessageUtil.isMCUnregister(packet)) {
List<String> channels = PluginMessageUtil.getChannels(packet);
clientPluginMsgChannels.removeAll(channels);
player.getConnectedServer().getConnection().write(packet);
} else if (PluginMessageUtil.isMCBrand(packet)) {
player.getConnectedServer().getConnection().write(PluginMessageUtil.rewriteMCBrand(packet));
} else if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) {
if (packet.getChannel().equals(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) {
if (!player.getModInfo().isPresent()) {
ForgeUtil.readModList(packet).ifPresent(mods -> player.setModInfo(new ModInfo("FML", mods)));
if (actuallyRegistered.size() > 0) {
PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(backendConn
.getProtocolVersion(), actuallyRegistered);
backendConn.write(newRegisterPacket);
}
// Always forward the FML handshake to the remote server.
player.getConnectedServer().getConnection().write(packet);
return true;
} else if (PluginMessageUtil.isMCUnregister(packet)) {
List<String> channels = PluginMessageUtil.getChannels(packet);
clientPluginMsgChannels.removeAll(channels);
backendConn.write(packet);
return true;
} else if (PluginMessageUtil.isMCBrand(packet)) {
backendConn.write(PluginMessageUtil.rewriteMCBrand(packet));
return true;
} else if (backendConn.isLegacyForge() && !serverConn.hasCompletedJoin()) {
if (packet.getChannel().equals(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) {
if (!player.getModInfo().isPresent()) {
List<ModInfo.Mod> mods = ForgeUtil.readModList(packet);
if (!mods.isEmpty()) {
player.setModInfo(new ModInfo("FML", mods));
}
}
// Always forward the FML handshake to the remote server.
backendConn.write(packet);
} else {
// The client is trying to send messages too early. This is primarily caused by mods, but it's further
// aggravated by Velocity. To work around these issues, we will queue any non-FML handshake messages to
// be sent once the JoinGame packet has been received by the proxy.
loginPluginMessages.add(packet);
}
return true;
} else {
// The client is trying to send messages too early. This is primarily caused by mods, but it's further
// aggravated by Velocity. To work around these issues, we will queue any non-FML handshake messages to
// be sent once the JoinGame packet has been received by the proxy.
loginPluginMessages.add(packet);
}
} else {
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
if (id == null) {
player.getConnectedServer().getConnection().write(packet);
} else {
PluginMessageEvent event = new PluginMessageEvent(player, player.getConnectedServer(), id, packet.getData());
server.getEventManager().fire(event)
.thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed()) {
player.getConnectedServer().getConnection().write(packet);
}
}, player.getConnectedServer().getConnection().eventLoop());
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
if (id == null) {
backendConn.write(packet);
} else {
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, packet.getData());
server.getEventManager().fire(event)
.thenAcceptAsync(pme -> {
backendConn.write(packet);
}, backendConn.eventLoop());
}
}
}
return true;
}
@ -184,9 +210,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
return;
}
// If we don't want to handle this packet, just forward it on.
if (serverConnection.hasCompletedJoin()) {
serverConnection.getConnection().write(packet);
MinecraftConnection smc = serverConnection.getConnection();
if (smc != null && serverConnection.hasCompletedJoin()) {
smc.write(packet);
}
}
@ -198,8 +224,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
return;
}
if (serverConnection.hasCompletedJoin()) {
serverConnection.getConnection().write(buf.retain());
MinecraftConnection smc = serverConnection.getConnection();
if (smc != null && serverConnection.hasCompletedJoin()) {
smc.write(buf.retain());
}
}
@ -222,11 +249,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
VelocityServerConnection server = player.getConnectedServer();
if (server != null) {
boolean writable = player.getConnection().getChannel().isWritable();
server.getConnection().getChannel().config().setAutoRead(writable);
MinecraftConnection smc = server.getConnection();
if (smc != null) {
smc.getChannel().config().setAutoRead(writable);
}
}
}
public void handleBackendJoinGame(JoinGame joinGame) {
VelocityServerConnection serverConn = player.getConnectedServer();
if (serverConn == null) {
throw new IllegalStateException("No server connection for " + player + ", but JoinGame packet received");
}
MinecraftConnection serverMc = serverConn.getConnection();
if (serverMc == null) {
throw new IllegalStateException("Server connection for " + player + " is disconnected, but JoinGame packet received");
}
if (!spawned) {
// Nothing special to do with regards to spawning the player
spawned = true;
@ -272,7 +311,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
serverBossBars.clear();
// Tell the server about this client's plugin message channels.
int serverVersion = player.getConnectedServer().getConnection().getProtocolVersion();
int serverVersion = serverMc.getProtocolVersion();
Collection<String> toRegister = new HashSet<>(clientPluginMsgChannels);
if (serverVersion >= ProtocolConstants.MINECRAFT_1_13) {
toRegister.addAll(server.getChannelRegistrar().getModernChannelIds());
@ -280,14 +319,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
toRegister.addAll(server.getChannelRegistrar().getIdsForLegacyConnections());
}
if (!toRegister.isEmpty()) {
player.getConnectedServer().getConnection().delayedWrite(PluginMessageUtil.constructChannelsPacket(
serverVersion, toRegister));
serverMc.delayedWrite(PluginMessageUtil.constructChannelsPacket(serverVersion, toRegister));
}
// If we had plugin messages queued during login/FML handshake, send them now.
PluginMessage pm;
while ((pm = loginPluginMessages.poll()) != null) {
player.getConnectedServer().getConnection().delayedWrite(pm);
serverMc.delayedWrite(pm);
}
// Clear any title from the previous server.
@ -295,9 +333,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Flush everything
player.getConnection().flush();
player.getConnectedServer().getConnection().flush();
player.getConnectedServer().setHasCompletedJoin(true);
if (player.getConnectedServer().isLegacyForge()) {
serverMc.flush();
serverConn.setHasCompletedJoin(true);
if (serverConn.isLegacyForge()) {
// We only need to indicate we can send a reset packet if we complete a handshake, that is,
// logged onto a Forge server.
//
@ -326,8 +364,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (!outstandingTabComplete.isAssumeCommand()) {
String command = outstandingTabComplete.getCommand().substring(1);
try {
Optional<List<String>> offers = server.getCommandManager().offerSuggestions(player, command);
offers.ifPresent(strings -> response.getOffers().addAll(strings));
response.getOffers().addAll(server.getCommandManager().offerSuggestions(player, command));
} catch (Exception e) {
logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(),
command, e);

Datei anzeigen

@ -3,6 +3,7 @@ package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.player.SkinParts;
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale;
@ -11,7 +12,7 @@ public class ClientSettingsWrapper implements PlayerSettings {
private final ClientSettings settings;
private final SkinParts parts;
private Locale locale = null;
private @Nullable Locale locale;
ClientSettingsWrapper(ClientSettings settings) {
this.settings = settings;

Datei anzeigen

@ -63,27 +63,28 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class);
private final MinecraftConnection connection;
private final InetSocketAddress virtualHost;
private @Nullable final InetSocketAddress virtualHost;
private GameProfile profile;
private PermissionFunction permissionFunction = null;
private PermissionFunction permissionFunction;
private int tryIndex = 0;
private long ping = -1;
private VelocityServerConnection connectedServer;
private VelocityServerConnection connectionInFlight;
private PlayerSettings settings;
private ModInfo modInfo;
private @Nullable VelocityServerConnection connectedServer;
private @Nullable VelocityServerConnection connectionInFlight;
private @Nullable PlayerSettings settings;
private @Nullable ModInfo modInfo;
private final VelocityTabList tabList;
private final VelocityServer server;
@MonotonicNonNull
private List<String> serversToTry = null;
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, InetSocketAddress virtualHost) {
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, @Nullable InetSocketAddress virtualHost) {
this.server = server;
this.tabList = new VelocityTabList(connection);
this.profile = profile;
this.connection = connection;
this.virtualHost = virtualHost;
this.permissionFunction = (permission) -> Tristate.UNDEFINED;
}
@Override
@ -123,8 +124,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
void setPlayerSettings(ClientSettings settings) {
this.settings = new ClientSettingsWrapper(settings);
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, this.settings));
ClientSettingsWrapper cs = new ClientSettingsWrapper(settings);
this.settings = cs;
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, cs));
}
public Optional<ModInfo> getModInfo() {
@ -133,7 +135,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
void setModInfo(ModInfo modInfo) {
this.modInfo = modInfo;
server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, this.modInfo));
server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, modInfo));
}
@Override
@ -161,7 +163,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public void sendMessage(@NonNull Component component, @NonNull MessagePosition position) {
public void sendMessage(Component component, MessagePosition position) {
Preconditions.checkNotNull(component, "component");
Preconditions.checkNotNull(position, "position");
@ -193,7 +195,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server) {
public ConnectionRequestBuilder createConnectionRequest(RegisteredServer server) {
return new ConnectionRequestBuilderImpl(server);
}
@ -243,16 +245,19 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
connection.delayedWrite(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion()));
}
if (tt.getTitle().isPresent()) {
Optional<Component> titleText = tt.getTitle();
if (titleText.isPresent()) {
TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_TITLE);
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getTitle().get()));
titlePkt.setComponent(ComponentSerializers.JSON.serialize(titleText.get()));
connection.delayedWrite(titlePkt);
}
if (tt.getSubtitle().isPresent()) {
Optional<Component> subtitleText = tt.getSubtitle();
if (subtitleText.isPresent()) {
TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_SUBTITLE);
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getSubtitle().get()));
titlePkt.setComponent(ComponentSerializers.JSON.serialize(subtitleText.get()));
connection.delayedWrite(titlePkt);
}
@ -270,16 +275,23 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Nullable
public VelocityServerConnection getConnectedServer() {
return connectedServer;
}
public void handleConnectionException(RegisteredServer server, Throwable throwable) {
Throwable wrapped = throwable;
if (throwable instanceof CompletionException) {
wrapped = throwable.getCause();
if (throwable == null) {
throw new NullPointerException("throwable");
}
Throwable wrapped = throwable;
if (throwable instanceof CompletionException) {
Throwable cause = throwable.getCause();
if (cause != null) {
wrapped = cause;
}
}
String error = ThrowableUtils.briefDescription(wrapped);
String userMessage;
if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) {
@ -366,7 +378,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
String toTryName = serversToTry.get(tryIndex);
tryIndex++;
return server.getServers().getServer(toTryName);
return server.getServer(toTryName);
}
private Optional<ConnectionRequestBuilder.Status> checkServer(RegisteredServer server) {
@ -390,13 +402,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
ServerPreConnectEvent event = new ServerPreConnectEvent(this, request.getServer());
return server.getEventManager().fire(event)
.thenCompose((newEvent) -> {
if (!newEvent.getResult().isAllowed()) {
Optional<RegisteredServer> connectTo = newEvent.getResult().getServer();
if (!connectTo.isPresent()) {
return CompletableFuture.completedFuture(
ConnectionRequestResults.plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED)
);
}
RegisteredServer rs = newEvent.getResult().getServer().get();
RegisteredServer rs = connectTo.get();
Optional<ConnectionRequestBuilder.Status> lastCheck = checkServer(rs);
if (lastCheck.isPresent()) {
return CompletableFuture.completedFuture(ConnectionRequestResults.plainResult(lastCheck.get()));
@ -406,9 +419,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
public void setConnectedServer(VelocityServerConnection serverConnection) {
if (this.connectedServer != null && !serverConnection.getServerInfo().equals(connectedServer.getServerInfo())) {
VelocityServerConnection oldConnection = this.connectedServer;
if (oldConnection != null && !serverConnection.getServerInfo().equals(oldConnection.getServerInfo())) {
this.tryIndex = 0;
}
if (serverConnection == connectionInFlight) {
connectionInFlight = null;
}
this.connectedServer = serverConnection;
}
@ -426,6 +443,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
connection.closeWith(Disconnect.create(reason));
}
private MinecraftConnection ensureBackendConnection() {
VelocityServerConnection sc = this.connectedServer;
if (sc == null) {
throw new IllegalStateException("No backend connection");
}
MinecraftConnection mc = sc.getConnection();
if (mc == null) {
throw new IllegalStateException("Backend connection is not connected to a server");
}
return mc;
}
void teardown() {
if (connectionInFlight != null) {
connectionInFlight.disconnect();
@ -439,11 +470,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override
public String toString() {
return "[connected player] " + getProfile().getName() + " (" + getRemoteAddress() + ")";
return "[connected player] " + profile.getName() + " (" + getRemoteAddress() + ")";
}
@Override
public @NonNull Tristate getPermissionValue(@NonNull String permission) {
public Tristate getPermissionValue(String permission) {
return permissionFunction.getPermissionValue(permission);
}
@ -461,7 +492,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override
public void spoofChatInput(String input) {
Preconditions.checkArgument(input.length() <= Chat.MAX_SERVERBOUND_MESSAGE_LENGTH, "input cannot be greater than " + Chat.MAX_SERVERBOUND_MESSAGE_LENGTH + " characters in length");
connectedServer.getConnection().write(Chat.createServerbound(input));
ensureBackendConnection().write(Chat.createServerbound(input));
}
private class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {

Datei anzeigen

@ -22,10 +22,12 @@ import com.velocitypowered.proxy.util.EncryptionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import net.kyori.text.Component;
import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
@ -38,6 +40,8 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
public class LoginSessionHandler implements MinecraftSessionHandler {
private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class);
@ -47,10 +51,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
private final VelocityServer server;
private final MinecraftConnection inbound;
private final InboundConnection apiInbound;
private ServerLogin login;
private byte[] verify;
private @MonotonicNonNull ServerLogin login;
private byte[] verify = EMPTY_BYTE_ARRAY;
private int playerInfoId;
private ConnectedPlayer connectedPlayer;
private @MonotonicNonNull ConnectedPlayer connectedPlayer;
public LoginSessionHandler(VelocityServer server, MinecraftConnection inbound, InboundConnection apiInbound) {
this.server = Preconditions.checkNotNull(server, "server");
@ -62,12 +66,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
public boolean handle(ServerLogin packet) {
this.login = packet;
if (inbound.getProtocolVersion() >= ProtocolConstants.MINECRAFT_1_13) {
LoginPluginMessage message = new LoginPluginMessage();
playerInfoId = ThreadLocalRandom.current().nextInt();
message.setId(playerInfoId);
message.setChannel(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL);
message.setData(Unpooled.EMPTY_BUFFER);
inbound.write(message);
inbound.write(new LoginPluginMessage(playerInfoId, VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL,
Unpooled.EMPTY_BUFFER));
} else {
beginPreLogin();
}
@ -92,6 +93,15 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(EncryptionResponse packet) {
ServerLogin login = this.login;
if (login == null) {
throw new IllegalStateException("No ServerLogin packet received yet.");
}
if (verify.length == 0) {
throw new IllegalStateException("No EncryptionRequest packet sent yet.");
}
try {
KeyPair serverKeyPair = server.getServerKeyPair();
byte[] decryptedVerifyToken = EncryptionUtils.decryptRsa(serverKeyPair, packet.getVerifyToken());
@ -149,6 +159,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
}
private void beginPreLogin() {
ServerLogin login = this.login;
if (login == null) {
throw new IllegalStateException("No ServerLogin packet received yet.");
}
PreLoginEvent event = new PreLoginEvent(apiInbound, login.getUsername());
server.getEventManager().fire(event)
.thenRunAsync(() -> {
@ -157,9 +171,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
return;
}
PreLoginComponentResult result = event.getResult();
if (!result.isAllowed()) {
Optional<Component> disconnectReason = result.getReason();
if (disconnectReason.isPresent()) {
// The component is guaranteed to be provided if the connection was denied.
inbound.closeWith(Disconnect.create(event.getResult().getReason().get()));
inbound.closeWith(Disconnect.create(disconnectReason.get()));
return;
}
@ -212,13 +227,13 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
// The player was disconnected
return;
}
if (!event.getResult().isAllowed()) {
// The component is guaranteed to be provided if the connection was denied.
player.disconnect(event.getResult().getReason().get());
return;
}
handleProxyLogin(player);
Optional<Component> reason = event.getResult().getReason();
if (reason.isPresent()) {
player.disconnect(reason.get());
} else {
handleProxyLogin(player);
}
}, inbound.eventLoop());
});

Datei anzeigen

@ -49,9 +49,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
ProxyPingEvent event = new ProxyPingEvent(inboundWrapper, initialPing);
server.getEventManager().fire(event)
.thenRunAsync(() -> {
StatusResponse response = new StatusResponse();
response.setStatus(VelocityServer.GSON.toJson(event.getPing()));
connection.write(response);
connection.write(new StatusResponse(VelocityServer.GSON.toJson(event.getPing())));
}, connection.eventLoop());
return true;
}

Datei anzeigen

@ -9,15 +9,13 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.List;
import java.util.Optional;
public class ForgeUtil {
private ForgeUtil() {
throw new AssertionError();
}
public static Optional<List<ModInfo.Mod>> readModList(PluginMessage message) {
public static List<ModInfo.Mod> readModList(PluginMessage message) {
Preconditions.checkNotNull(message, "message");
Preconditions.checkArgument(message.getChannel().equals(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL),
"message is not a FML HS plugin message");
@ -36,10 +34,10 @@ public class ForgeUtil {
mods.add(new ModInfo.Mod(id, version));
}
return Optional.of(mods.build());
return mods.build();
}
return Optional.empty();
return ImmutableList.of();
} finally {
byteBuf.release();
}

Datei anzeigen

@ -53,16 +53,14 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Comm
.completer((reader, parsedLine, list) -> {
try {
boolean isCommand = parsedLine.line().indexOf(' ') == -1;
Optional<List<String>> o = this.server.getCommandManager().offerSuggestions(this, parsedLine.line());
o.ifPresent(offers -> {
for (String offer : offers) {
if (isCommand) {
list.add(new Candidate(offer.substring(1)));
} else {
list.add(new Candidate(offer));
}
List<String> offers = this.server.getCommandManager().offerSuggestions(this, parsedLine.line());
for (String offer : offers) {
if (isCommand) {
list.add(new Candidate(offer.substring(1)));
} else {
list.add(new Candidate(offer));
}
});
}
} catch (Exception e) {
logger.error("An error occurred while trying to perform tab completion.", e);
}

Datei anzeigen

@ -12,6 +12,7 @@ import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import java.net.InetSocketAddress;
import java.util.HashSet;
@ -27,16 +28,15 @@ public final class ConnectionManager {
private final VelocityServer server;
public final ServerChannelInitializerHolder serverChannelInitializer;
public ConnectionManager(final VelocityServer server) {
public ConnectionManager(VelocityServer server) {
this.server = server;
this.transportType = TransportType.bestType();
this.bossGroup = this.transportType.createEventLoopGroup(TransportType.Type.BOSS);
this.workerGroup = this.transportType.createEventLoopGroup(TransportType.Type.WORKER);
this.serverChannelInitializer = new ServerChannelInitializerHolder(new ServerChannelInitializer(this.server));
this.logChannelInformation();
}
private void logChannelInformation() {
public void logChannelInformation() {
LOGGER.info("Connections will use {} channels, {} compression, {} ciphers", this.transportType, Natives.compressor.getLoadedVariant(), Natives.cipher.getLoadedVariant());
}

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.proxy.network.http;
import com.google.common.base.VerifyException;
import com.velocitypowered.proxy.VelocityServer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
@ -61,6 +62,9 @@ public class NettyHttpClient {
.addListener(future -> {
if (future.isSuccess()) {
Channel channel = (Channel) future.getNow();
if (channel == null) {
throw new VerifyException("Null channel retrieved from pool!");
}
channel.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply));
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url.getPath() + "?" + url.getQuery());

Datei anzeigen

@ -16,10 +16,13 @@ public class PluginClassLoader extends URLClassLoader {
public PluginClassLoader(URL[] urls) {
super(urls);
}
public void addToClassloaders() {
loaders.add(this);
}
public void addPath(Path path) {
void addPath(Path path) {
try {
addURL(path.toUri().toURL());
} catch (MalformedURLException e) {

Datei anzeigen

@ -12,7 +12,6 @@ import com.velocitypowered.api.plugin.PluginManager;
import net.kyori.event.EventSubscriber;
import net.kyori.event.PostResult;
import net.kyori.event.SimpleEventBus;
import net.kyori.event.method.EventExecutor;
import net.kyori.event.method.MethodScanner;
import net.kyori.event.method.MethodSubscriptionAdapter;
import net.kyori.event.method.SimpleMethodSubscriptionAdapter;
@ -20,12 +19,14 @@ import net.kyori.event.method.asm.ASMEventExecutorFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -38,20 +39,24 @@ public class VelocityEventManager implements EventManager {
.synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new));
private final ListMultimap<Object, EventHandler<?>> registeredHandlersByPlugin = Multimaps
.synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new));
private final VelocityEventBus bus = new VelocityEventBus(
new ASMEventExecutorFactory<>(new PluginClassLoader(new URL[0])),
new VelocityMethodScanner());
private final SimpleEventBus<Object> bus;
private final MethodSubscriptionAdapter<Object> methodAdapter;
private final ExecutorService service;
private final PluginManager pluginManager;
public VelocityEventManager(PluginManager pluginManager) {
PluginClassLoader cl = new PluginClassLoader(new URL[0]);
cl.addToClassloaders();
this.bus = new SimpleEventBus<>(Object.class);
this.methodAdapter = new SimpleMethodSubscriptionAdapter<>(bus, new ASMEventExecutorFactory<>(cl),
new VelocityMethodScanner());
this.pluginManager = pluginManager;
this.service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder()
.setNameFormat("Velocity Event Executor - #%d").setDaemon(true).build());
}
@Override
public void register(@NonNull Object plugin, @NonNull Object listener) {
public void register(Object plugin, Object listener) {
Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkNotNull(listener, "listener");
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
@ -59,11 +64,12 @@ public class VelocityEventManager implements EventManager {
throw new IllegalArgumentException("Trying to register the plugin main instance. Velocity already takes care of this for you.");
}
registeredListenersByPlugin.put(plugin, listener);
bus.register(listener);
methodAdapter.register(listener);
}
@Override
public <E> void register(@NonNull Object plugin, @NonNull Class<E> eventClass, @NonNull PostOrder postOrder, @NonNull EventHandler<E> handler) {
@SuppressWarnings("type.argument.type.incompatible")
public <E> void register(Object plugin, Class<E> eventClass, PostOrder postOrder, EventHandler<E> handler) {
Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkNotNull(eventClass, "eventClass");
Preconditions.checkNotNull(postOrder, "postOrder");
@ -72,8 +78,10 @@ public class VelocityEventManager implements EventManager {
}
@Override
public <E> @NonNull CompletableFuture<E> fire(@NonNull E event) {
Preconditions.checkNotNull(event, "event");
public <E> CompletableFuture<E> fire(E event) {
if (event == null) {
throw new NullPointerException("event");
}
if (!bus.hasSubscribers(event.getClass())) {
// Optimization: nobody's listening.
return CompletableFuture.completedFuture(event);
@ -99,30 +107,30 @@ public class VelocityEventManager implements EventManager {
}
@Override
public void unregisterListeners(@NonNull Object plugin) {
public void unregisterListeners(Object plugin) {
Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
Collection<Object> listeners = registeredListenersByPlugin.removeAll(plugin);
listeners.forEach(bus::unregister);
listeners.forEach(methodAdapter::unregister);
Collection<EventHandler<?>> handlers = registeredHandlersByPlugin.removeAll(plugin);
handlers.forEach(bus::unregister);
handlers.forEach(handler -> bus.unregister(new KyoriToVelocityHandler<>(handler, PostOrder.LAST)));
}
@Override
public void unregisterListener(@NonNull Object plugin, @NonNull Object listener) {
public void unregisterListener(Object plugin, Object listener) {
Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkNotNull(listener, "listener");
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
registeredListenersByPlugin.remove(plugin, listener);
bus.unregister(listener);
methodAdapter.unregister(listener);
}
@Override
public <E> void unregister(@NonNull Object plugin, @NonNull EventHandler<E> handler) {
public <E> void unregister(Object plugin, EventHandler<E> handler) {
Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkNotNull(handler, "listener");
registeredHandlersByPlugin.remove(plugin, handler);
bus.unregister(handler);
bus.unregister(new KyoriToVelocityHandler<>(handler, PostOrder.LAST));
}
public boolean shutdown() throws InterruptedException {
@ -130,27 +138,6 @@ public class VelocityEventManager implements EventManager {
return service.awaitTermination(10, TimeUnit.SECONDS);
}
private static class VelocityEventBus extends SimpleEventBus<Object> {
private final MethodSubscriptionAdapter<Object> methodAdapter;
VelocityEventBus(EventExecutor.@NonNull Factory<Object, Object> factory, @NonNull MethodScanner<Object> methodScanner) {
super(Object.class);
this.methodAdapter = new SimpleMethodSubscriptionAdapter<>(this, factory, methodScanner);
}
void register(Object listener) {
this.methodAdapter.register(listener);
}
void unregister(Object listener) {
this.methodAdapter.unregister(listener);
}
void unregister(EventHandler<?> handler) {
this.unregister(s -> s instanceof KyoriToVelocityHandler && ((KyoriToVelocityHandler<?>) s).getHandler().equals(handler));
}
}
private static class VelocityMethodScanner implements MethodScanner<Object> {
@Override
public boolean shouldRegister(@NonNull Object listener, @NonNull Method method) {
@ -159,7 +146,11 @@ public class VelocityEventManager implements EventManager {
@Override
public int postOrder(@NonNull Object listener, @NonNull Method method) {
return method.getAnnotation(Subscribe.class).order().ordinal();
Subscribe annotation = method.getAnnotation(Subscribe.class);
if (annotation == null) {
throw new IllegalStateException("Trying to determine post order for listener without @Subscribe annotation");
}
return annotation.order().ordinal();
}
@Override
@ -190,5 +181,18 @@ public class VelocityEventManager implements EventManager {
public EventHandler<E> getHandler() {
return handler;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KyoriToVelocityHandler<?> that = (KyoriToVelocityHandler<?>) o;
return Objects.equals(handler, that.handler);
}
@Override
public int hashCode() {
return Objects.hash(handler);
}
}
}

Datei anzeigen

@ -31,12 +31,15 @@ public class VelocityPluginManager implements PluginManager {
this.server = checkNotNull(server, "server");
}
private void registerPlugin(@NonNull PluginContainer plugin) {
private void registerPlugin(PluginContainer plugin) {
plugins.put(plugin.getDescription().getId(), plugin);
plugin.getInstance().ifPresent(instance -> pluginInstances.put(instance, plugin));
Optional<?> instance = plugin.getInstance();
if (instance.isPresent()) {
pluginInstances.put(instance.get(), plugin);
}
}
public void loadPlugins(@NonNull Path directory) throws IOException {
public void loadPlugins(Path directory) throws IOException {
checkNotNull(directory, "directory");
checkArgument(Files.isDirectory(directory), "provided path isn't a directory");
@ -86,7 +89,7 @@ public class VelocityPluginManager implements PluginManager {
}
@Override
public @NonNull Optional<PluginContainer> fromInstance(@NonNull Object instance) {
public Optional<PluginContainer> fromInstance(Object instance) {
checkNotNull(instance, "instance");
if (instance instanceof PluginContainer) {
@ -97,23 +100,23 @@ public class VelocityPluginManager implements PluginManager {
}
@Override
public @NonNull Optional<PluginContainer> getPlugin(@NonNull String id) {
public Optional<PluginContainer> getPlugin(String id) {
checkNotNull(id, "id");
return Optional.ofNullable(plugins.get(id));
}
@Override
public @NonNull Collection<PluginContainer> getPlugins() {
public Collection<PluginContainer> getPlugins() {
return Collections.unmodifiableCollection(plugins.values());
}
@Override
public boolean isLoaded(@NonNull String id) {
public boolean isLoaded(String id) {
return plugins.containsKey(id);
}
@Override
public void addToClasspath(@NonNull Object plugin, @NonNull Path path) {
public void addToClasspath(Object plugin, Path path) {
checkNotNull(plugin, "instance");
checkNotNull(path, "path");
checkArgument(pluginInstances.containsKey(plugin), "plugin is not loaded");

Datei anzeigen

@ -12,6 +12,7 @@ import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.plugin.PluginClassLoader;
import com.velocitypowered.proxy.plugin.loader.java.JavaVelocityPluginDescription;
import com.velocitypowered.proxy.plugin.loader.java.VelocityPluginModule;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.BufferedInputStream;
import java.io.InputStreamReader;
@ -51,6 +52,7 @@ public class JavaPluginLoader implements PluginLoader {
PluginClassLoader loader = new PluginClassLoader(
new URL[] {source.toUri().toURL() }
);
loader.addToClassloaders();
Class mainClass = loader.loadClass(pd.getMain());
return createDescription(pd, source, mainClass);
@ -72,6 +74,10 @@ public class JavaPluginLoader implements PluginLoader {
Injector injector = Guice.createInjector(new VelocityPluginModule(server, javaDescription, baseDirectory));
Object instance = injector.getInstance(javaDescription.getMainClass());
if (instance == null) {
throw new IllegalStateException("Got nothing from injector for plugin " + javaDescription.getId());
}
return new VelocityPluginContainer(description, instance);
}
@ -93,10 +99,8 @@ public class JavaPluginLoader implements PluginLoader {
private VelocityPluginDescription createDescription(SerializedPluginDescription description, Path source, Class mainClass) {
Set<PluginDependency> dependencies = new HashSet<>();
if (description.getDependencies() != null) {
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
dependencies.add(toDependencyMeta(dependency));
}
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
dependencies.add(toDependencyMeta(dependency));
}
return new JavaVelocityPluginDescription(

Datei anzeigen

@ -33,7 +33,7 @@ public class VelocityPluginDescription implements PluginDescription {
this.description = Strings.emptyToNull(description);
this.url = Strings.emptyToNull(url);
this.authors = authors == null ? ImmutableList.of() : ImmutableList.copyOf(authors);
this.dependencies = Maps.uniqueIndex(dependencies, PluginDependency::getId);
this.dependencies = Maps.uniqueIndex(dependencies, d -> d == null ? null : d.getId());
this.source = source;
}

Datei anzeigen

@ -8,14 +8,15 @@ import com.google.common.graph.GraphBuilder;
import com.google.common.graph.MutableGraph;
import com.velocitypowered.api.plugin.PluginDescription;
import com.velocitypowered.api.plugin.meta.PluginDependency;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.*;
public class PluginDependencyUtils {
public static List<PluginDescription> sortCandidates(List<PluginDescription> candidates) {
public static List<PluginDescription> sortCandidates(List<@NonNull PluginDescription> candidates) {
// Create our graph, we're going to be using this for Kahn's algorithm.
MutableGraph<PluginDescription> graph = GraphBuilder.directed().allowsSelfLoops(false).build();
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(candidates, PluginDescription::getId);
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(candidates, d -> d == null ? null : d.getId());
// Add edges
for (PluginDescription description : candidates) {
@ -36,7 +37,7 @@ public class PluginDependencyUtils {
// Actually run Kahn's algorithm
List<PluginDescription> sorted = new ArrayList<>();
while (!noEdges.isEmpty()) {
PluginDescription candidate = noEdges.poll();
PluginDescription candidate = noEdges.remove();
sorted.add(candidate);
for (PluginDescription node : ImmutableSet.copyOf(graph.adjacentNodes(candidate))) {

Datei anzeigen

@ -48,6 +48,10 @@ public enum ProtocolConstants { ;
public enum Direction {
SERVERBOUND,
CLIENTBOUND
CLIENTBOUND;
public StateRegistry.PacketRegistry.ProtocolVersion getProtocol(StateRegistry state, int protocolVersion) {
return (this == SERVERBOUND ? state.SERVERBOUND : state.CLIENTBOUND).getVersion(protocolVersion);
}
}
}

Datei anzeigen

@ -1,12 +1,12 @@
package com.velocitypowered.proxy.protocol;
import com.google.common.base.Strings;
import com.google.common.primitives.ImmutableIntArray;
import com.velocitypowered.proxy.protocol.packet.*;
import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.function.Supplier;
@ -36,6 +36,9 @@ public enum StateRegistry {
},
PLAY {
{
SERVERBOUND.fallback = false;
CLIENTBOUND.fallback = false;
SERVERBOUND.register(TabCompleteRequest.class, TabCompleteRequest::new,
map(0x14, MINECRAFT_1_8, false),
map(0x01, MINECRAFT_1_9, false),
@ -150,8 +153,8 @@ public enum StateRegistry {
public static final int STATUS_ID = 1;
public static final int LOGIN_ID = 2;
public final PacketRegistry CLIENTBOUND = new PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND, this);
public final PacketRegistry SERVERBOUND = new PacketRegistry(ProtocolConstants.Direction.SERVERBOUND, this);
public final PacketRegistry CLIENTBOUND = new PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND);
public final PacketRegistry SERVERBOUND = new PacketRegistry(ProtocolConstants.Direction.SERVERBOUND);
public static class PacketRegistry {
private static final IntObjectMap<ImmutableIntArray> LINKED_PROTOCOL_VERSIONS = new IntObjectHashMap<>();
@ -165,19 +168,18 @@ public enum StateRegistry {
}
private final ProtocolConstants.Direction direction;
private final StateRegistry state;
private final IntObjectMap<ProtocolVersion> versions = new IntObjectHashMap<>(16);
private boolean fallback = true;
public PacketRegistry(Direction direction, StateRegistry state) {
public PacketRegistry(Direction direction) {
this.direction = direction;
this.state = state;
ProtocolConstants.SUPPORTED_VERSIONS.forEach(version -> versions.put(version, new ProtocolVersion(version)));
}
public ProtocolVersion getVersion(final int version) {
ProtocolVersion result = versions.get(version);
if (result == null) {
if (state != PLAY) {
if (fallback) {
return getVersion(MINIMUM_GENERIC_VERSION);
}
throw new IllegalArgumentException("Could not find data for protocol version " + version);
@ -224,7 +226,7 @@ public enum StateRegistry {
this.packetClassToId.defaultReturnValue(Integer.MIN_VALUE);
}
public MinecraftPacket createPacket(final int id) {
public @Nullable MinecraftPacket createPacket(final int id) {
final Supplier<? extends MinecraftPacket> supplier = this.packetIdToSupplier.get(id);
if (supplier == null) {
return null;
@ -242,23 +244,6 @@ public enum StateRegistry {
}
return id;
}
@Override
public String toString() {
StringBuilder mappingAsString = new StringBuilder("{");
for (Object2IntMap.Entry<Class<? extends MinecraftPacket>> entry : packetClassToId.object2IntEntrySet()) {
mappingAsString.append(entry.getKey().getSimpleName()).append(" -> ")
.append("0x")
.append(Strings.padStart(Integer.toHexString(entry.getIntValue()), 2, '0'))
.append(", ");
}
mappingAsString.setLength(mappingAsString.length() - 2);
mappingAsString.append("}");
return "ProtocolVersion{" +
"id=" + id +
", packetClassToId=" + mappingAsString.toString() +
'}';
}
}
}
@ -283,7 +268,7 @@ public enum StateRegistry {
}
@Override
public boolean equals(Object o) {
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PacketMapping that = (PacketMapping) o;

Datei anzeigen

@ -13,14 +13,14 @@ import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
private StateRegistry state;
private final ProtocolConstants.Direction direction;
private StateRegistry state;
private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion;
public MinecraftDecoder(ProtocolConstants.Direction direction) {
this.state = StateRegistry.HANDSHAKE;
this.direction = Preconditions.checkNotNull(direction, "direction");
this.setProtocolVersion(ProtocolConstants.MINIMUM_GENERIC_VERSION);
this.protocolVersion = direction.getProtocol(StateRegistry.HANDSHAKE, ProtocolConstants.MINIMUM_GENERIC_VERSION);
this.state = StateRegistry.HANDSHAKE;
}
@Override
@ -51,24 +51,12 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
}
}
public StateRegistry.PacketRegistry.ProtocolVersion getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(int protocolVersion) {
this.protocolVersion = (this.direction == ProtocolConstants.Direction.CLIENTBOUND ? this.state.CLIENTBOUND : this.state.SERVERBOUND).getVersion(protocolVersion);
}
public StateRegistry getState() {
return state;
this.protocolVersion = direction.getProtocol(state, protocolVersion);
}
public void setState(StateRegistry state) {
this.state = state;
this.setProtocolVersion(protocolVersion.id);
}
public ProtocolConstants.Direction getDirection() {
return direction;
}
}

Datei anzeigen

@ -10,14 +10,14 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
private StateRegistry state;
private final ProtocolConstants.Direction direction;
private StateRegistry state;
private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion;
public MinecraftEncoder(ProtocolConstants.Direction direction) {
this.state = StateRegistry.HANDSHAKE;
this.direction = Preconditions.checkNotNull(direction, "direction");
this.setProtocolVersion(ProtocolConstants.MINIMUM_GENERIC_VERSION);
this.protocolVersion = direction.getProtocol(StateRegistry.HANDSHAKE, ProtocolConstants.MINIMUM_GENERIC_VERSION);
this.state = StateRegistry.HANDSHAKE;
}
@Override
@ -27,24 +27,12 @@ public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
msg.encode(out, direction, protocolVersion.id);
}
public StateRegistry.PacketRegistry.ProtocolVersion getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(final int protocolVersion) {
this.protocolVersion = (this.direction == ProtocolConstants.Direction.CLIENTBOUND ? this.state.CLIENTBOUND : this.state.SERVERBOUND).getVersion(protocolVersion);
}
public StateRegistry getState() {
return state;
this.protocolVersion = direction.getProtocol(state, protocolVersion);
}
public void setState(StateRegistry state) {
this.state = state;
this.setProtocolVersion(protocolVersion.id);
}
public ProtocolConstants.Direction getDirection() {
return direction;
}
}

Datei anzeigen

@ -5,6 +5,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
@ -15,15 +16,18 @@ public class BossBar implements MinecraftPacket {
public static final int UPDATE_NAME = 3;
public static final int UPDATE_STYLE = 4;
public static final int UPDATE_PROPERTIES = 5;
private UUID uuid;
private @Nullable UUID uuid;
private int action;
private String name;
private @Nullable String name;
private float percent;
private int color;
private int overlay;
private short flags;
public UUID getUuid() {
if (uuid == null) {
throw new IllegalStateException("No boss bar UUID specified");
}
return uuid;
}
@ -39,7 +43,7 @@ public class BossBar implements MinecraftPacket {
this.action = action;
}
public String getName() {
public @Nullable String getName() {
return name;
}
@ -124,10 +128,16 @@ public class BossBar implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (uuid == null) {
throw new IllegalStateException("No boss bar UUID specified");
}
ProtocolUtils.writeUuid(buf, uuid);
ProtocolUtils.writeVarInt(buf, action);
switch (action) {
case ADD:
if (name == null) {
throw new IllegalStateException("No name specified!");
}
ProtocolUtils.writeString(buf, name);
buf.writeFloat(percent);
ProtocolUtils.writeVarInt(buf, color);
@ -140,6 +150,9 @@ public class BossBar implements MinecraftPacket {
buf.writeFloat(percent);
break;
case UPDATE_NAME:
if (name == null) {
throw new IllegalStateException("No name specified!");
}
ProtocolUtils.writeString(buf, name);
break;
case UPDATE_STYLE:

Datei anzeigen

@ -8,12 +8,13 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.checkerframework.checker.nullness.qual.Nullable;
public class Chat implements MinecraftPacket {
public static final byte CHAT = (byte) 0;
public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256;
private String message;
private @Nullable String message;
private byte type;
public Chat() {
@ -25,6 +26,9 @@ public class Chat implements MinecraftPacket {
}
public String getMessage() {
if (message == null) {
throw new IllegalStateException("Message is not specified");
}
return message;
}
@ -58,6 +62,9 @@ public class Chat implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (message == null) {
throw new IllegalStateException("Message is not specified");
}
ProtocolUtils.writeString(buf, message);
if (direction == ProtocolConstants.Direction.CLIENTBOUND) {
buf.writeByte(type);

Datei anzeigen

@ -5,10 +5,11 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ClientSettings implements MinecraftPacket {
private String locale;
private @Nullable String locale;
private byte viewDistance;
private int chatVisibility;
private boolean chatColors;
@ -28,6 +29,9 @@ public class ClientSettings implements MinecraftPacket {
}
public String getLocale() {
if (locale == null) {
throw new IllegalStateException("No locale specified");
}
return locale;
}
@ -102,6 +106,9 @@ public class ClientSettings implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (locale == null) {
throw new IllegalStateException("No locale specified");
}
ProtocolUtils.writeString(buf, locale);
buf.writeByte(viewDistance);
ProtocolUtils.writeVarInt(buf, chatVisibility);

Datei anzeigen

@ -8,22 +8,26 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.checkerframework.checker.nullness.qual.Nullable;
public class Disconnect implements MinecraftPacket {
private String reason;
private @Nullable String reason;
public Disconnect() {
}
public Disconnect(String reason) {
this.reason = reason;
this.reason = Preconditions.checkNotNull(reason, "reason");
}
public String getReason() {
if (reason == null) {
throw new IllegalStateException("No reason specified");
}
return reason;
}
public void setReason(String reason) {
public void setReason(@Nullable String reason) {
this.reason = reason;
}
@ -41,6 +45,9 @@ public class Disconnect implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (reason == null) {
throw new IllegalStateException("No reason specified.");
}
ProtocolUtils.writeString(buf, reason);
}

Datei anzeigen

@ -8,10 +8,12 @@ import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import static com.velocitypowered.proxy.connection.VelocityConstants.*;
public class EncryptionRequest implements MinecraftPacket {
private String serverId = "";
private byte[] publicKey;
private byte[] verifyToken;
private byte[] publicKey = EMPTY_BYTE_ARRAY;
private byte[] verifyToken = EMPTY_BYTE_ARRAY;
public byte[] getPublicKey() {
return publicKey;

Datei anzeigen

@ -8,9 +8,11 @@ import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import static com.velocitypowered.proxy.connection.VelocityConstants.*;
public class EncryptionResponse implements MinecraftPacket {
private byte[] sharedSecret;
private byte[] verifyToken;
private byte[] sharedSecret = EMPTY_BYTE_ARRAY;
private byte[] verifyToken = EMPTY_BYTE_ARRAY;
public byte[] getSharedSecret() {
return sharedSecret;

Datei anzeigen

@ -8,7 +8,7 @@ import io.netty.buffer.ByteBuf;
public class Handshake implements MinecraftPacket {
private int protocolVersion;
private String serverAddress;
private String serverAddress = "";
private int port;
private int nextStatus;

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.proxy.protocol.packet;
import com.google.common.base.Preconditions;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants.Direction;
@ -11,36 +12,29 @@ import net.kyori.text.serializer.ComponentSerializers;
import static com.velocitypowered.proxy.protocol.ProtocolUtils.writeString;
public class HeaderAndFooter implements MinecraftPacket {
private static final HeaderAndFooter RESET = new HeaderAndFooter("{\"translate\":\"\"}", "{\"translate\":\"\"}");
private static final String EMPTY_COMPONENT = "{\"translate\":\"\"}";
private static final HeaderAndFooter RESET = new HeaderAndFooter();
private String header;
private String footer;
public HeaderAndFooter() {
this(EMPTY_COMPONENT, EMPTY_COMPONENT);
}
public HeaderAndFooter(String header, String footer) {
this.header = header;
this.footer = footer;
this.header = Preconditions.checkNotNull(header, "header");
this.footer = Preconditions.checkNotNull(footer, "footer");
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
public String getFooter() {
return footer;
}
public void setFooter(String footer) {
this.footer = footer;
}
@Override
public void decode(ByteBuf buf, Direction direction, int protocolVersion) {
throw new UnsupportedOperationException("Decode is not implemented");

Datei anzeigen

@ -5,6 +5,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class JoinGame implements MinecraftPacket {
private int entityId;
@ -12,7 +13,7 @@ public class JoinGame implements MinecraftPacket {
private int dimension;
private short difficulty;
private short maxPlayers;
private String levelType;
private @Nullable String levelType;
private boolean reducedDebugInfo;
public int getEntityId() {
@ -56,6 +57,9 @@ public class JoinGame implements MinecraftPacket {
}
public String getLevelType() {
if (levelType == null) {
throw new IllegalStateException("No level type specified.");
}
return levelType;
}
@ -110,6 +114,9 @@ public class JoinGame implements MinecraftPacket {
}
buf.writeByte(difficulty);
buf.writeByte(maxPlayers);
if (levelType == null) {
throw new IllegalStateException("No level type specified.");
}
ProtocolUtils.writeString(buf, levelType);
buf.writeBoolean(reducedDebugInfo);
}

Datei anzeigen

@ -6,14 +6,11 @@ import net.kyori.text.serializer.ComponentSerializers;
public class LegacyPingResponse {
private static final ServerPing.Players FAKE_PLAYERS = new ServerPing.Players(0, 0, ImmutableList.of());
private int protocolVersion;
private String serverVersion;
private String motd;
private int playersOnline;
private int playersMax;
public LegacyPingResponse() {
}
private final int protocolVersion;
private final String serverVersion;
private final String motd;
private final int playersOnline;
private final int playersMax;
public LegacyPingResponse(int protocolVersion, String serverVersion, String motd, int playersOnline, int playersMax) {
this.protocolVersion = protocolVersion;
@ -27,42 +24,22 @@ public class LegacyPingResponse {
return protocolVersion;
}
public void setProtocolVersion(int protocolVersion) {
this.protocolVersion = protocolVersion;
}
public String getServerVersion() {
return serverVersion;
}
public void setServerVersion(String serverVersion) {
this.serverVersion = serverVersion;
}
public String getMotd() {
return motd;
}
public void setMotd(String motd) {
this.motd = motd;
}
public int getPlayersOnline() {
return playersOnline;
}
public void setPlayersOnline(int playersOnline) {
this.playersOnline = playersOnline;
}
public int getPlayersMax() {
return playersMax;
}
public void setPlayersMax(int playersMax) {
this.playersMax = playersMax;
}
@Override
public String toString() {
return "LegacyPingResponse{" +

Datei anzeigen

@ -6,36 +6,38 @@ import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LoginPluginMessage implements MinecraftPacket {
private int id;
private String channel;
private ByteBuf data;
private @Nullable String channel;
private ByteBuf data = Unpooled.EMPTY_BUFFER;
public LoginPluginMessage() {
}
public LoginPluginMessage(int id, @Nullable String channel, ByteBuf data) {
this.id = id;
this.channel = channel;
this.data = data;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getChannel() {
if (channel == null) {
throw new IllegalStateException("Channel is not specified!");
}
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
public ByteBuf getData() {
return data;
}
public void setData(ByteBuf data) {
this.data = data;
}
@Override
public String toString() {
return "LoginPluginMessage{" +
@ -59,6 +61,9 @@ public class LoginPluginMessage implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
ProtocolUtils.writeVarInt(buf, id);
if (channel == null) {
throw new IllegalStateException("Channel is not specified!");
}
ProtocolUtils.writeString(buf, channel);
buf.writeBytes(data);
}

Datei anzeigen

@ -10,7 +10,7 @@ import io.netty.buffer.Unpooled;
public class LoginPluginResponse implements MinecraftPacket {
private int id;
private boolean success;
private ByteBuf data;
private ByteBuf data = Unpooled.EMPTY_BUFFER;
public int getId() {
return id;

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.proxy.protocol.packet;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.player.TabListEntry;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
@ -9,6 +10,7 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -21,11 +23,11 @@ public class PlayerListItem implements MinecraftPacket {
public static final int UPDATE_DISPLAY_NAME = 3;
public static final int REMOVE_PLAYER = 4;
private int action;
private List<Item> items;
private final List<Item> items = new ArrayList<>();
public PlayerListItem(int action, List<Item> items) {
this.action = action;
this.items = items;
this.items.addAll(items);
}
public PlayerListItem() {}
@ -41,7 +43,6 @@ public class PlayerListItem implements MinecraftPacket {
@Override
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
action = ProtocolUtils.readVarInt(buf);
items = new ArrayList<>();
int length = ProtocolUtils.readVarInt(buf);
for (int i = 0; i < length; i++) {
@ -57,7 +58,8 @@ public class PlayerListItem implements MinecraftPacket {
if (hasDisplayName) {
item.setDisplayName(ComponentSerializers.JSON.deserialize(ProtocolUtils.readString(buf)));
}
} break;
break;
}
case UPDATE_GAMEMODE:
item.setGameMode(ProtocolUtils.readVarInt(buf));
break;
@ -81,7 +83,7 @@ public class PlayerListItem implements MinecraftPacket {
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
ProtocolUtils.writeVarInt(buf, action);
ProtocolUtils.writeVarInt(buf, items.size());
for (Item item: items) {
for (Item item : items) {
ProtocolUtils.writeUuid(buf, item.getUuid());
switch (action) {
case ADD_PLAYER:
@ -113,7 +115,7 @@ public class PlayerListItem implements MinecraftPacket {
return handler.handle(this);
}
private void writeDisplayName(ByteBuf buf, Component displayName) {
private void writeDisplayName(ByteBuf buf, @Nullable Component displayName) {
buf.writeBoolean(displayName != null);
if (displayName != null) {
ProtocolUtils.writeString(buf, ComponentSerializers.JSON.serialize(displayName));
@ -122,11 +124,11 @@ public class PlayerListItem implements MinecraftPacket {
public static class Item {
private final UUID uuid;
private String name;
private List<GameProfile.Property> properties;
private String name = "";
private List<GameProfile.Property> properties = ImmutableList.of();
private int gameMode;
private int latency;
private Component displayName;
private @Nullable Component displayName;
public Item(UUID uuid) {
this.uuid = uuid;
@ -181,11 +183,11 @@ public class PlayerListItem implements MinecraftPacket {
return this;
}
public Component getDisplayName() {
public @Nullable Component getDisplayName() {
return displayName;
}
public Item setDisplayName(Component displayName) {
public Item setDisplayName(@Nullable Component displayName) {
this.displayName = displayName;
return this;
}

Datei anzeigen

@ -6,12 +6,18 @@ import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
import static com.velocitypowered.proxy.connection.VelocityConstants.*;
public class PluginMessage implements MinecraftPacket {
private String channel;
private byte[] data;
private @Nullable String channel;
private byte[] data = EMPTY_BYTE_ARRAY;
public String getChannel() {
if (channel == null) {
throw new IllegalStateException("Channel is not specified.");
}
return channel;
}
@ -44,6 +50,9 @@ public class PluginMessage implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (channel == null) {
throw new IllegalStateException("Channel is not specified.");
}
ProtocolUtils.writeString(buf, channel);
buf.writeBytes(data);
}

Datei anzeigen

@ -10,7 +10,7 @@ public class Respawn implements MinecraftPacket {
private int dimension;
private short difficulty;
private short gamemode;
private String levelType;
private String levelType = "";
public Respawn() {
}

Datei anzeigen

@ -1,20 +1,27 @@
package com.velocitypowered.proxy.protocol.packet;
import com.google.common.base.Preconditions;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ServerLogin implements MinecraftPacket {
private String username;
private @Nullable String username;
public String getUsername() {
return username;
public ServerLogin() {}
public ServerLogin(String username) {
this.username = Preconditions.checkNotNull(username, "username");
}
public void setUsername(String username) {
this.username = username;
public String getUsername() {
if (username == null) {
throw new IllegalStateException("No username found!");
}
return username;
}
@Override
@ -31,6 +38,9 @@ public class ServerLogin implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (username == null) {
throw new IllegalStateException("No username found!");
}
ProtocolUtils.writeString(buf, username);
}

Datei anzeigen

@ -5,14 +5,18 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
public class ServerLoginSuccess implements MinecraftPacket {
private UUID uuid;
private String username;
private @Nullable UUID uuid;
private @Nullable String username;
public UUID getUuid() {
if (uuid == null) {
throw new IllegalStateException("No UUID specified!");
}
return uuid;
}
@ -21,6 +25,9 @@ public class ServerLoginSuccess implements MinecraftPacket {
}
public String getUsername() {
if (username == null) {
throw new IllegalStateException("No username specified!");
}
return username;
}
@ -44,7 +51,13 @@ public class ServerLoginSuccess implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (uuid == null) {
throw new IllegalStateException("No UUID specified!");
}
ProtocolUtils.writeString(buf, uuid.toString());
if (username == null) {
throw new IllegalStateException("No username specified!");
}
ProtocolUtils.writeString(buf, username);
}

Datei anzeigen

@ -5,16 +5,22 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class StatusResponse implements MinecraftPacket {
private String status;
private @Nullable String status;
public String getStatus() {
return status;
public StatusResponse() {}
public StatusResponse(String status) {
this.status = status;
}
public void setStatus(String status) {
this.status = status;
public String getStatus() {
if (status == null) {
throw new IllegalStateException("Status is not specified");
}
return status;
}
@Override
@ -31,6 +37,9 @@ public class StatusResponse implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (status == null) {
throw new IllegalStateException("Status is not specified");
}
ProtocolUtils.writeString(buf, status);
}

Datei anzeigen

@ -5,16 +5,20 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_9;
public class TabCompleteRequest implements MinecraftPacket {
private String command;
private @Nullable String command;
private boolean assumeCommand;
private boolean hasPosition;
private long position;
public String getCommand() {
if (command == null) {
throw new IllegalStateException("Command is not specified");
}
return command;
}
@ -70,6 +74,9 @@ public class TabCompleteRequest implements MinecraftPacket {
@Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (command == null) {
throw new IllegalStateException("Command is not specified");
}
ProtocolUtils.writeString(buf, command);
if (protocolVersion >= MINECRAFT_1_9) {
buf.writeBoolean(assumeCommand);

Datei anzeigen

@ -5,6 +5,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class TitlePacket implements MinecraftPacket {
public static final int SET_TITLE = 0;
@ -18,7 +19,7 @@ public class TitlePacket implements MinecraftPacket {
public static final int RESET_OLD = 4;
private int action;
private String component;
private @Nullable String component;
private int fadeIn;
private int stay;
private int fadeOut;
@ -37,6 +38,9 @@ public class TitlePacket implements MinecraftPacket {
case SET_TITLE:
case SET_SUBTITLE:
case SET_ACTION_BAR:
if (component == null) {
throw new IllegalStateException("No component found for " + action);
}
ProtocolUtils.writeString(buf, component);
break;
case SET_TIMES:
@ -52,6 +56,9 @@ public class TitlePacket implements MinecraftPacket {
switch (action) {
case SET_TITLE:
case SET_SUBTITLE:
if (component == null) {
throw new IllegalStateException("No component found for " + action);
}
ProtocolUtils.writeString(buf, component);
break;
case SET_TIMES_OLD:
@ -74,11 +81,11 @@ public class TitlePacket implements MinecraftPacket {
this.action = action;
}
public String getComponent() {
public @Nullable String getComponent() {
return component;
}
public void setComponent(String component) {
public void setComponent(@Nullable String component) {
this.component = component;
}

Datei anzeigen

@ -11,6 +11,7 @@ import com.velocitypowered.api.scheduler.Scheduler;
import com.velocitypowered.api.scheduler.TaskStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection;
import java.util.HashSet;
@ -92,6 +93,7 @@ public class VelocityScheduler implements Scheduler {
public ScheduledTask schedule() {
VelocityTask task = new VelocityTask(plugin, runnable, delay, repeat);
tasksByPlugin.put(plugin, task);
task.schedule();
return task;
}
}
@ -99,12 +101,19 @@ public class VelocityScheduler implements Scheduler {
private class VelocityTask implements Runnable, ScheduledTask {
private final Object plugin;
private final Runnable runnable;
private ScheduledFuture<?> future;
private volatile Thread currentTaskThread;
private final long delay;
private final long repeat;
private @Nullable ScheduledFuture<?> future;
private volatile @Nullable Thread currentTaskThread;
private VelocityTask(Object plugin, Runnable runnable, long delay, long repeat) {
this.plugin = plugin;
this.runnable = runnable;
this.delay = delay;
this.repeat = repeat;
}
public void schedule() {
if (repeat == 0) {
this.future = timerExecutionService.schedule(this, delay, TimeUnit.MILLISECONDS);
} else {
@ -155,14 +164,10 @@ public class VelocityScheduler implements Scheduler {
try {
runnable.run();
} catch (Exception e) {
// Since we can't catch InterruptedException separately...
if (e instanceof InterruptedException) {
onFinish();
} else {
Log.logger.error("Exception in task {} by plugin {}", runnable, plugin);
}
Log.logger.error("Exception in task {} by plugin {}", runnable, plugin);
} finally {
currentTaskThread = null;
}
currentTaskThread = null;
});
}

Datei anzeigen

@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.VelocityServer;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection;
import java.util.Locale;
@ -13,10 +14,10 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class ServerMap {
private final VelocityServer server;
private final @Nullable VelocityServer server;
private final Map<String, RegisteredServer> servers = new ConcurrentHashMap<>();
public ServerMap(VelocityServer server) {
public ServerMap(@Nullable VelocityServer server) {
this.server = server;
}
@ -49,7 +50,9 @@ public class ServerMap {
Preconditions.checkNotNull(serverInfo, "serverInfo");
String lowerName = serverInfo.getName().toLowerCase(Locale.US);
RegisteredServer rs = servers.get(lowerName);
Preconditions.checkArgument(rs != null, "Server with name %s is not registered!", serverInfo.getName());
if (rs == null) {
throw new IllegalArgumentException("Server with name " + serverInfo.getName() + " is not registered!");
}
Preconditions.checkArgument(rs.getServerInfo().equals(serverInfo), "Trying to remove server %s with differing information", serverInfo.getName());
Preconditions.checkState(servers.remove(lowerName, rs), "Server with name %s replaced whilst unregistering", serverInfo.getName());
}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen