13
0
geforkt von Mirrors/Velocity

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' id 'maven-publish'
} }
apply from: '../gradle/checkerframework.gradle'
sourceSets { sourceSets {
ap { ap {
compileClasspath += main.compileClasspath + main.output compileClasspath += main.compileClasspath + main.output
@ -17,7 +19,7 @@ dependencies {
compile 'com.moandjiezana.toml:toml4j:0.7.2' compile 'com.moandjiezana.toml:toml4j:0.7.2'
compile "org.slf4j:slf4j-api:${slf4jVersion}" compile "org.slf4j:slf4j-api:${slf4jVersion}"
compile 'com.google.inject:guice:4.2.0' 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-api:${junitVersion}"
testCompile "org.junit.jupiter:junit-jupiter-engine:${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.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -29,8 +30,8 @@ public class SerializedPluginDescription {
this.version = Strings.emptyToNull(version); this.version = Strings.emptyToNull(version);
this.description = Strings.emptyToNull(description); this.description = Strings.emptyToNull(description);
this.url = Strings.emptyToNull(url); this.url = Strings.emptyToNull(url);
this.authors = authors == null || authors.isEmpty() ? null : authors; this.authors = authors == null || authors.isEmpty() ? ImmutableList.of() : authors;
this.dependencies = dependencies == null || dependencies.isEmpty() ? null : dependencies; this.dependencies = dependencies == null || dependencies.isEmpty() ? ImmutableList.of() : dependencies;
this.main = Preconditions.checkNotNull(main, "main"); this.main = Preconditions.checkNotNull(main, "main");
} }
@ -63,12 +64,12 @@ public class SerializedPluginDescription {
return url; return url;
} }
public @Nullable List<String> getAuthors() { public List<String> getAuthors() {
return authors; return authors == null ? ImmutableList.of() : authors;
} }
public @Nullable List<Dependency> getDependencies() { public List<Dependency> getDependencies() {
return dependencies; return dependencies == null ? ImmutableList.of() : dependencies;
} }
public String getMain() { public String getMain() {

Datei anzeigen

@ -15,7 +15,7 @@ public interface Command {
* @param source the source of this command * @param source the source of this command
* @param args the arguments for 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}. * 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 * @param currentArgs the current, partial arguments for this command
* @return tab complete suggestions * @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(); return ImmutableList.of();
} }
@ -38,7 +38,7 @@ public interface Command {
* @param args the arguments for this command * @param args the arguments for this command
* @return whether the source has permission * @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; return true;
} }
} }

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.command; package com.velocitypowered.api.command;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Represents an interface to register a command executor with the proxy. * 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 command the command to register
* @param aliases the alias to use * @param aliases the alias to use
*/ */
void register(@NonNull Command command, String... aliases); void register(Command command, String... aliases);
/** /**
* Unregisters a command. * Unregisters a command.
* @param alias the command alias to unregister * @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}. * Attempts to execute a command from the specified {@code cmdLine}.
@ -25,5 +23,5 @@ public interface CommandManager {
* @param cmdLine the command to run * @param cmdLine the command to run
* @return true if the command was found and executed, false if it was not * @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 com.velocitypowered.api.permission.PermissionSubject;
import net.kyori.text.Component; import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Represents something that can be used to run a {@link Command}. * 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. * Sends the specified {@code component} to the invoker.
* @param component the text component to send * @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; 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 * Represents an interface to perform direct dispatch of an event. This makes integration easier to achieve with platforms
* such as RxJava. * such as RxJava.
*/ */
@FunctionalInterface @FunctionalInterface
public interface EventHandler<E> { public interface EventHandler<E> {
void execute(@NonNull E event); void execute(E event);
} }

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.event; package com.velocitypowered.api.event;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
/** /**
@ -13,7 +11,7 @@ public interface EventManager {
* @param plugin the plugin to associate with the listener * @param plugin the plugin to associate with the listener
* @param listener the listener to register * @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}. * 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 handler the handler to register
* @param <E> the event type to handle * @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); register(plugin, eventClass, PostOrder.NORMAL, handler);
} }
@ -34,7 +32,7 @@ public interface EventManager {
* @param handler the handler to register * @param handler the handler to register
* @param <E> the event type to handle * @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 * 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 * @param event the event to fire
* @return a {@link CompletableFuture} representing the posted event * @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. * Posts the specified event to the event bus and discards the result.
* @param event the event to fire * @param event the event to fire
*/ */
default void fireAndForget(@NonNull Object event) { default void fireAndForget(Object event) {
fire(event); fire(event);
} }
@ -56,14 +54,14 @@ public interface EventManager {
* Unregisters all listeners for the specified {@code plugin}. * Unregisters all listeners for the specified {@code plugin}.
* @param plugin the plugin to deregister listeners for * @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. * Unregisters a specific listener for a specific plugin.
* @param plugin the plugin associated with the listener * @param plugin the plugin associated with the listener
* @param listener the listener to deregister * @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. * Unregisters a specific event handler for a specific plugin.
@ -71,5 +69,5 @@ public interface EventManager {
* @param handler the handler to register * @param handler the handler to register
* @param <E> the event type to handle * @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. * Sets the result of this event. The result must be non-null.
* @param result the new result * @param result the new result
*/ */
void setResult(@NonNull R result); void setResult(R result);
/** /**
* Represents a result for an event. * Represents a result for an event.
@ -106,7 +106,7 @@ public interface ResultedEvent<R extends ResultedEvent.Result> {
return ALLOWED; return ALLOWED;
} }
public static ComponentResult denied(@NonNull Component reason) { public static ComponentResult denied(Component reason) {
Preconditions.checkNotNull(reason, "reason"); Preconditions.checkNotNull(reason, "reason");
return new ComponentResult(false, 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.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.InboundConnection; 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. * This event is fired when a handshake is established between a client and Velocity.
*/ */
public final class ConnectionHandshakeEvent { 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"); 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.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player; 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 * 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. * data retrieval operations, may behave in undefined ways.
*/ */
public final class DisconnectEvent { 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"); 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.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player; 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. * 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 final Player player;
private ComponentResult result; private ComponentResult result;
public LoginEvent(@NonNull Player player) { public LoginEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player"); this.player = Preconditions.checkNotNull(player, "player");
this.result = ComponentResult.allowed(); this.result = ComponentResult.allowed();
} }
@ -27,7 +26,7 @@ public final class LoginEvent implements ResultedEvent<ResultedEvent.ComponentRe
} }
@Override @Override
public void setResult(@NonNull ComponentResult result) { public void setResult(ComponentResult result) {
this.result = Preconditions.checkNotNull(result, "result"); this.result = Preconditions.checkNotNull(result, "result");
} }

Datei anzeigen

@ -36,7 +36,7 @@ public final class PluginMessageEvent implements ResultedEvent<PluginMessageEven
} }
@Override @Override
public void setResult(@NonNull ForwardResult result) { public void setResult(ForwardResult result) {
this.result = Preconditions.checkNotNull(result, "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.event.ResultedEvent;
import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.InboundConnection;
import net.kyori.text.Component; import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; 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 static final PreLoginComponentResult FORCE_OFFLINEMODE = new PreLoginComponentResult(Result.FORCE_OFFLINE, null);
private final Result result; private final Result result;
private final Optional<Component> reason; private final @Nullable Component reason;
private PreLoginComponentResult(Result result, @Nullable Component reason) { private PreLoginComponentResult(Result result, @Nullable Component reason) {
this.result = result; this.result = result;
this.reason = Optional.ofNullable(reason); this.reason = reason;
} }
@Override @Override
@ -75,7 +74,7 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
} }
public Optional<Component> getReason() { public Optional<Component> getReason() {
return reason; return Optional.ofNullable(reason);
} }
public boolean isOnlineModeAllowed() { public boolean isOnlineModeAllowed() {
@ -88,19 +87,16 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
@Override @Override
public String toString() { public String toString() {
if (isForceOfflineMode()) { switch (result) {
return "allowed with force offline mode"; 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.PermissionFunction;
import com.velocitypowered.api.permission.PermissionProvider; import com.velocitypowered.api.permission.PermissionProvider;
import com.velocitypowered.api.permission.PermissionSubject; import com.velocitypowered.api.permission.PermissionSubject;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
@ -22,7 +21,7 @@ public final class PermissionsSetupEvent {
this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider"); this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider");
} }
public @NonNull PermissionSubject getSubject() { public PermissionSubject getSubject() {
return this.subject; return this.subject;
} }
@ -33,11 +32,11 @@ public final class PermissionsSetupEvent {
* @param subject the subject * @param subject the subject
* @return the obtained permission function * @return the obtained permission function
*/ */
public @NonNull PermissionFunction createFunction(PermissionSubject subject) { public PermissionFunction createFunction(PermissionSubject subject) {
return this.provider.createFunction(subject); return this.provider.createFunction(subject);
} }
public @NonNull PermissionProvider getProvider() { public PermissionProvider getProvider() {
return this.provider; return this.provider;
} }

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional; import java.util.Optional;
@ -33,7 +32,7 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
} }
@Override @Override
public void setResult(@NonNull ServerResult result) { public void setResult(ServerResult result) {
this.result = Preconditions.checkNotNull(result, "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}. * Represents the result of the {@link ServerPreConnectEvent}.
*/ */
public static class ServerResult implements ResultedEvent.Result { 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 @Nullable RegisteredServer server;
private final RegisteredServer server;
private ServerResult(boolean allowed, @Nullable RegisteredServer server) { private ServerResult(@Nullable RegisteredServer server) {
this.allowed = allowed;
this.server = server; this.server = server;
} }
@Override @Override
public boolean isAllowed() { public boolean isAllowed() {
return allowed; return server != null;
} }
public Optional<RegisteredServer> getServer() { public Optional<RegisteredServer> getServer() {
@ -75,10 +72,10 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
@Override @Override
public String toString() { public String toString() {
if (!allowed) { if (server != null) {
return "denied"; return "allowed: connect to " + server.getServerInfo().getName();
} }
return "allowed: connect to " + server.getServerInfo().getName(); return "denied";
} }
public static ServerResult denied() { public static ServerResult denied() {
@ -87,7 +84,7 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
public static ServerResult allowed(RegisteredServer server) { public static ServerResult allowed(RegisteredServer server) {
Preconditions.checkNotNull(server, "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; package com.velocitypowered.api.permission;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Function that calculates the permission settings for a given * Function that calculates the permission settings for a given
* {@link PermissionSubject}. * {@link PermissionSubject}.
@ -29,5 +27,5 @@ public interface PermissionFunction {
* @param permission the permission * @param permission the permission
* @return the value the permission is set to * @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; package com.velocitypowered.api.permission;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Provides {@link PermissionFunction}s for {@link PermissionSubject}s. * Provides {@link PermissionFunction}s for {@link PermissionSubject}s.
*/ */
@ -13,5 +11,5 @@ public interface PermissionProvider {
* @param subject the subject * @param subject the subject
* @return the function * @return the function
*/ */
@NonNull PermissionFunction createFunction(@NonNull PermissionSubject subject); PermissionFunction createFunction(PermissionSubject subject);
} }

Datei anzeigen

@ -1,7 +1,5 @@
package com.velocitypowered.api.permission; package com.velocitypowered.api.permission;
import org.checkerframework.checker.nullness.qual.NonNull;
/** /**
* Represents a object that has a set of queryable permissions. * Represents a object that has a set of queryable permissions.
*/ */
@ -12,7 +10,7 @@ public interface PermissionSubject {
* @param permission the permission to check for * @param permission the permission to check for
* @return whether or not the subject has the permission * @return whether or not the subject has the permission
*/ */
default boolean hasPermission(@NonNull String permission) { default boolean hasPermission(String permission) {
return getPermissionValue(permission).asBoolean(); return getPermissionValue(permission).asBoolean();
} }
@ -22,5 +20,5 @@ public interface PermissionSubject {
* @param permission the permission * @param permission the permission
* @return the value the permission is set to * @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 * @param val the boolean value
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or <code>false</code>, respectively. * @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; return val ? TRUE : FALSE;
} }
@ -51,7 +51,7 @@ public enum Tristate {
* @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value * @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value
* is <code>null</code>, <code>true</code> or <code>false</code>, respectively. * 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; return val == null ? UNDEFINED : val ? TRUE : FALSE;
} }

Datei anzeigen

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

Datei anzeigen

@ -1,16 +1,12 @@
package com.velocitypowered.api.plugin; package com.velocitypowered.api.plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Logger;
/** /**
* The class that manages plugins. This manager can retrieve * Manages plugins loaded on the proxy. This manager can retrieve {@link PluginContainer}s from plugin instances
* {@link PluginContainer}s from {@link Plugin} instances, getting * and inject arbitrary JAR files into the plugin classpath with {@link #addToClasspath(Object, Path)}.
* {@link Logger}s, etc.
*/ */
public interface PluginManager { public interface PluginManager {
/** /**
@ -19,7 +15,7 @@ public interface PluginManager {
* @param instance the instance * @param instance the instance
* @return the container * @return the container
*/ */
@NonNull Optional<PluginContainer> fromInstance(@NonNull Object instance); Optional<PluginContainer> fromInstance(Object instance);
/** /**
* Retrieves a {@link PluginContainer} based on its ID. * Retrieves a {@link PluginContainer} based on its ID.
@ -27,22 +23,22 @@ public interface PluginManager {
* @param id the plugin ID * @param id the plugin ID
* @return the plugin, if available * @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. * Gets a {@link Collection} of all {@link PluginContainer}s.
* *
* @return the plugins * @return the plugins
*/ */
@NonNull Collection<PluginContainer> getPlugins(); Collection<PluginContainer> getPlugins();
/** /**
* Checks if a plugin is loaded based on its ID. * 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 * @return {@code true} if loaded
*/ */
boolean isLoaded(@NonNull String id); boolean isLoaded(String id);
/** /**
* Adds the specified {@code path} to the plugin classpath. * 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 * @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 * @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; package com.velocitypowered.api.plugin.meta;
import javax.annotation.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@ -13,7 +14,8 @@ import static com.google.common.base.Strings.emptyToNull;
*/ */
public final class PluginDependency { public final class PluginDependency {
private final String id; private final String id;
@Nullable private final String version; @Nullable
private final String version;
private final boolean optional; private final boolean optional;
@ -53,7 +55,7 @@ public final class PluginDependency {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
PluginDependency that = (PluginDependency) o; PluginDependency that = (PluginDependency) o;

Datei anzeigen

@ -13,7 +13,6 @@ import com.velocitypowered.api.util.title.Title;
import java.util.List; import java.util.List;
import net.kyori.text.Component; import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ -62,7 +61,7 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
* Sends a chat message to the player's client. * Sends a chat message to the player's client.
* @param component the chat message to send * @param component the chat message to send
*/ */
default void sendMessage(@NonNull Component component) { default void sendMessage(Component component) {
sendMessage(component, MessagePosition.CHAT); sendMessage(component, MessagePosition.CHAT);
} }
@ -71,14 +70,14 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
* @param component the chat message to send * @param component the chat message to send
* @param position the position for the message * @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. * Creates a new connection request so that the player can connect to another server.
* @param server the server to connect to * @param server the server to connect to
* @return a new connection request * @return a new connection request
*/ */
ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server); ConnectionRequestBuilder createConnectionRequest(RegisteredServer server);
/** /**
* Gets the player's profile properties. * 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.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.scheduler.Scheduler; import com.velocitypowered.api.scheduler.Scheduler;
import com.velocitypowered.api.util.ProxyVersion;
import net.kyori.text.Component; import net.kyori.text.Component;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -131,4 +132,10 @@ public interface ProxyServer {
* @return the proxy config * @return the proxy config
* */ * */
ProxyConfig getConfiguration(); 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.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects; import java.util.Objects;
@ -27,7 +28,7 @@ public final class LegacyChannelIdentifier implements ChannelIdentifier {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
LegacyChannelIdentifier that = (LegacyChannelIdentifier) o; 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.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -54,11 +55,11 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
@Override @Override
public String toString() { public String toString() {
return getId() + " (modern)"; return namespace + ":" + name + " (modern)";
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
MinecraftChannelIdentifier that = (MinecraftChannelIdentifier) o; 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.proxy.Player;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import net.kyori.text.Component; import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
@ -47,5 +48,5 @@ public interface TabList {
// Necessary because the TabListEntry implementation isn't in the api // Necessary because the TabListEntry implementation isn't in the api
@Deprecated @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; package com.velocitypowered.api.proxy.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import net.kyori.text.Component; import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional; import java.util.Optional;
@ -16,7 +14,7 @@ public interface TabListEntry {
* Returns the parent {@link TabList} of this {@code this} {@link TabListEntry}. * Returns the parent {@link TabList} of this {@code this} {@link TabListEntry}.
* @return parent {@link TabList} * @return parent {@link TabList}
*/ */
@NonNull TabList getTabList(); TabList getTabList();
/** /**
* Returns the {@link GameProfile} of the entry, which uniquely identifies the entry * 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. * as the player head in the tab list.
* @return {@link GameProfile} of the entry * @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 * 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. * {@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 * @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}. * 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 * @param displayName to show in the {@link TabList} for {@code this} entry
* @return {@code this}, for chaining * @return {@code this}, for chaining
*/ */
@NonNull TabListEntry setDisplayName(@Nullable Component displayName); TabListEntry setDisplayName(@Nullable Component displayName);
/** /**
* Returns the latency for {@code this} entry. * Returns the latency for {@code this} entry.
@ -63,7 +61,7 @@ public interface TabListEntry {
* @param latency to changed to * @param latency to changed to
* @return {@code this}, for chaining * @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. * Gets the game mode {@code this} entry has been set to.
@ -99,9 +97,9 @@ public interface TabListEntry {
* @see TabListEntry * @see TabListEntry
*/ */
class Builder { class Builder {
private TabList tabList; private @Nullable TabList tabList;
private GameProfile profile; private @Nullable GameProfile profile;
private Component displayName; private @Nullable Component displayName;
private int latency = 0; private int latency = 0;
private int gameMode = 0; private int gameMode = 0;
@ -167,9 +165,12 @@ public interface TabListEntry {
* @return the constructed {@link TabListEntry} * @return the constructed {@link TabListEntry}
*/ */
public TabListEntry build() { public TabListEntry build() {
Preconditions.checkState(tabList != null, "The Tablist must be set when building a TabListEntry"); if (tabList == null) {
Preconditions.checkState(profile != null, "The GameProfile must be set when building a TabListEntry"); 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); return tabList.buildEntry(profile, displayName, latency, gameMode);
} }
} }

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,5 +1,8 @@
package com.velocitypowered.api.scheduler; package com.velocitypowered.api.scheduler;
import org.checkerframework.common.value.qual.IntRange;
import org.checkerframework.common.value.qual.IntRangeFromNonNegative;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -24,7 +27,7 @@ public interface Scheduler {
* @param unit the unit of time for {@code time} * @param unit the unit of time for {@code time}
* @return this builder, for chaining * @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. * 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} * @param unit the unit of time for {@code time}
* @return this builder, for chaining * @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. * Clears the delay on this task.

Datei anzeigen

@ -1,7 +1,7 @@
package com.velocitypowered.api.util; package com.velocitypowered.api.util;
import com.google.common.base.Preconditions; 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 javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -25,7 +25,7 @@ public final class Favicon {
* of functions. * of functions.
* @param base64Url the url for use with this favicon * @param base64Url the url for use with this favicon
*/ */
public Favicon(@NonNull String base64Url) { public Favicon(String base64Url) {
this.base64Url = Preconditions.checkNotNull(base64Url, "base64Url"); this.base64Url = Preconditions.checkNotNull(base64Url, "base64Url");
} }
@ -38,7 +38,7 @@ public final class Favicon {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
Favicon favicon = (Favicon) o; Favicon favicon = (Favicon) o;
@ -62,7 +62,7 @@ public final class Favicon {
* @param image the image to use for the favicon * @param image the image to use for the favicon
* @return the created {@link Favicon} instance * @return the created {@link Favicon} instance
*/ */
public static Favicon create(@NonNull BufferedImage image) { public static Favicon create(BufferedImage image) {
Preconditions.checkNotNull(image, "image"); Preconditions.checkNotNull(image, "image");
Preconditions.checkArgument(image.getWidth() == 64 && image.getHeight() == 64, "Image does not have" + Preconditions.checkArgument(image.getWidth() == 64 && image.getHeight() == 64, "Image does not have" +
" 64x64 dimensions (found %sx%s)", image.getWidth(), image.getHeight()); " 64x64 dimensions (found %sx%s)", image.getWidth(), image.getHeight());
@ -81,7 +81,7 @@ public final class Favicon {
* @return the created {@link Favicon} instance * @return the created {@link Favicon} instance
* @throws IOException if the file could not be read from the path * @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)) { try (InputStream stream = Files.newInputStream(path)) {
return create(ImageIO.read(stream)); 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.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -15,7 +14,7 @@ public final class GameProfile {
private final String name; private final String name;
private final List<Property> properties; 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.id = Preconditions.checkNotNull(id, "id");
this.name = Preconditions.checkNotNull(name, "name"); this.name = Preconditions.checkNotNull(name, "name");
this.properties = ImmutableList.copyOf(properties); this.properties = ImmutableList.copyOf(properties);
@ -42,7 +41,7 @@ public final class GameProfile {
* @param username the username to use * @param username the username to use
* @return the new offline-mode game profile * @return the new offline-mode game profile
*/ */
public static GameProfile forOfflinePlayer(@NonNull String username) { public static GameProfile forOfflinePlayer(String username) {
Preconditions.checkNotNull(username, "username"); Preconditions.checkNotNull(username, "username");
String id = UuidUtils.toUndashed(UuidUtils.generateOfflinePlayerUuid(username)); String id = UuidUtils.toUndashed(UuidUtils.generateOfflinePlayerUuid(username));
return new GameProfile(id, username, ImmutableList.of()); return new GameProfile(id, username, ImmutableList.of());
@ -62,7 +61,7 @@ public final class GameProfile {
private final String value; private final String value;
private final String signature; 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.name = Preconditions.checkNotNull(name, "name");
this.value = Preconditions.checkNotNull(value, "value"); this.value = Preconditions.checkNotNull(value, "value");
this.signature = Preconditions.checkNotNull(signature, "signature"); 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.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Objects; import java.util.Objects;
@ -21,7 +20,7 @@ public final class UuidUtils {
* @param string the string to convert * @param string the string to convert
* @return the UUID object * @return the UUID object
*/ */
public static @NonNull UUID fromUndashed(final @NonNull String string) { public static UUID fromUndashed(final String string) {
Objects.requireNonNull(string, "string"); Objects.requireNonNull(string, "string");
Preconditions.checkArgument(string.length() == 32, "Length is incorrect"); Preconditions.checkArgument(string.length() == 32, "Length is incorrect");
return new UUID( return new UUID(
@ -35,7 +34,7 @@ public final class UuidUtils {
* @param uuid the UUID to convert * @param uuid the UUID to convert
* @return the undashed UUID * @return the undashed UUID
*/ */
public static @NonNull String toUndashed(final @NonNull UUID uuid) { public static String toUndashed(final UUID uuid) {
Preconditions.checkNotNull(uuid, "uuid"); Preconditions.checkNotNull(uuid, "uuid");
return Strings.padStart(Long.toHexString(uuid.getMostSignificantBits()), 16, '0') + return Strings.padStart(Long.toHexString(uuid.getMostSignificantBits()), 16, '0') +
Strings.padStart(Long.toHexString(uuid.getLeastSignificantBits()), 16, '0'); Strings.padStart(Long.toHexString(uuid.getLeastSignificantBits()), 16, '0');
@ -46,7 +45,7 @@ public final class UuidUtils {
* @param username the username to use * @param username the username to use
* @return the offline mode UUID * @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)); 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. * Represents a "full" title, including all components. This class is immutable.
*/ */
public final class TextTitle implements Title { public final class TextTitle implements Title {
private final Component title; private final @Nullable Component title;
private final Component subtitle; private final @Nullable Component subtitle;
private final int stay; private final int stay;
private final int fadeIn; private final int fadeIn;
private final int fadeOut; private final int fadeOut;
@ -94,7 +94,7 @@ public final class TextTitle implements Title {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
TextTitle textTitle = (TextTitle) o; TextTitle textTitle = (TextTitle) o;
@ -193,12 +193,12 @@ public final class TextTitle implements Title {
return this; return this;
} }
public Component getTitle() { public Optional<Component> getTitle() {
return title; return Optional.ofNullable(title);
} }
public Component getSubtitle() { public Optional<Component> getSubtitle() {
return subtitle; return Optional.ofNullable(subtitle);
} }
public int getStay() { public int getStay() {

Datei anzeigen

@ -16,6 +16,7 @@ allprojects {
log4jVersion = '2.11.1' log4jVersion = '2.11.1'
nettyVersion = '4.1.30.Final' nettyVersion = '4.1.30.Final'
guavaVersion = '25.1-jre' guavaVersion = '25.1-jre'
checkerFrameworkVersion = '2.5.6'
getCurrentBranchName = { getCurrentBranchName = {
new ByteArrayOutputStream().withStream { os -> 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' id 'java'
} }
apply from: '../gradle/checkerframework.gradle'
dependencies { dependencies {
compile "com.google.guava:guava:${guavaVersion}" compile "com.google.guava:guava:${guavaVersion}"
compile "io.netty:netty-buffer:${nettyVersion}" 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-api:${junitVersion}"
testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
} }

Datei anzeigen

@ -1,88 +1,64 @@
package com.velocitypowered.natives.util; 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.List;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Supplier; import java.util.function.Supplier;
public class NativeCodeLoader<T> implements Supplier<T> { public final class NativeCodeLoader<T> implements Supplier<T> {
private final List<Variant<T>> variants; private final Variant<T> selected;
private volatile Variant<T> selected;
public NativeCodeLoader(List<Variant<T>> variants) { NativeCodeLoader(List<Variant<T>> variants) {
this.variants = ImmutableList.copyOf(variants); this.selected = getVariant(variants);
} }
@Override @Override
public T get() { public T get() {
return tryLoad().object; return selected.object;
} }
private Variant<T> tryLoad() { private static <T> Variant<T> getVariant(List<Variant<T>> variants) {
if (selected != null) { for (Variant<T> variant : variants) {
return selected; T got = variant.get();
} if (got == null) {
continue;
synchronized (this) {
if (selected != null) {
return selected;
} }
return variant;
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");
} }
throw new IllegalArgumentException("Can't find any suitable variants");
} }
public String getLoadedVariant() { public String getLoadedVariant() {
return tryLoad().name; return selected.name;
} }
static class Variant<T> { static class Variant<T> {
private volatile boolean available; private Status status;
private final Runnable setup; private final Runnable setup;
private final String name; private final String name;
private final T object; private final T object;
private volatile boolean hasBeenSetup = false;
Variant(BooleanSupplier available, Runnable setup, String name, T object) { Variant(BooleanSupplier possiblyAvailable, Runnable setup, String name, T object) {
this.available = available.getAsBoolean(); this.status = possiblyAvailable.getAsBoolean() ? Status.POSSIBLY_AVAILABLE : Status.NOT_AVAILABLE;
this.setup = setup; this.setup = setup;
this.name = name; this.name = name;
this.object = object; this.object = object;
} }
public T get() { public @Nullable T get() {
if (!available) { if (status == Status.NOT_AVAILABLE || status == Status.SETUP_FAILURE) {
return null; return null;
} }
// Make sure setup happens only once // Make sure setup happens only once
if (!hasBeenSetup) { if (status == Status.POSSIBLY_AVAILABLE) {
synchronized (this) { try {
// We change availability if need be below, may as well check it again here for sanity. setup.run();
if (!available) { status = Status.SETUP;
return null; } catch (Exception e) {
} status = Status.SETUP_FAILURE;
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;
}
}
} }
} }
@ -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"); 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"); System.getProperty("os.arch").equals("amd64");
static final BooleanSupplier ALWAYS = () -> true; static final BooleanSupplier ALWAYS = () -> true;
} }

Datei anzeigen

@ -8,6 +8,7 @@ import com.velocitypowered.natives.encryption.JavaVelocityCipher;
import com.velocitypowered.natives.encryption.VelocityCipherFactory; import com.velocitypowered.natives.encryption.VelocityCipherFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
@ -21,7 +22,12 @@ public class Natives {
return () -> { return () -> {
try { try {
Path tempFile = Files.createTempFile("native-", path.substring(path.lastIndexOf('.'))); 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(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try { try {
Files.deleteIfExists(tempFile); Files.deleteIfExists(tempFile);

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,9 +1,11 @@
package com.velocitypowered.proxy.command; package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.Command;
import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.*; import java.util.*;
@ -11,7 +13,7 @@ public class VelocityCommandManager implements CommandManager {
private final Map<String, Command> commands = new HashMap<>(); private final Map<String, Command> commands = new HashMap<>();
@Override @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(aliases, "aliases");
Preconditions.checkNotNull(command, "executor"); Preconditions.checkNotNull(command, "executor");
for (int i = 0, length = aliases.length; i < length; i++) { for (int i = 0, length = aliases.length; i < length; i++) {
@ -22,13 +24,13 @@ public class VelocityCommandManager implements CommandManager {
} }
@Override @Override
public void unregister(final String alias) { public void unregister(@NonNull final String alias) {
Preconditions.checkNotNull(alias, "name"); Preconditions.checkNotNull(alias, "name");
this.commands.remove(alias.toLowerCase(Locale.ENGLISH)); this.commands.remove(alias.toLowerCase(Locale.ENGLISH));
} }
@Override @Override
public boolean execute(CommandSource source, String cmdLine) { public boolean execute(@NonNull CommandSource source, @NonNull String cmdLine) {
Preconditions.checkNotNull(source, "invoker"); Preconditions.checkNotNull(source, "invoker");
Preconditions.checkNotNull(cmdLine, "cmdLine"); Preconditions.checkNotNull(cmdLine, "cmdLine");
@ -38,6 +40,7 @@ public class VelocityCommandManager implements CommandManager {
} }
String alias = split[0]; String alias = split[0];
@SuppressWarnings("nullness")
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length); String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
Command command = commands.get(alias.toLowerCase(Locale.ENGLISH)); Command command = commands.get(alias.toLowerCase(Locale.ENGLISH));
if (command == null) { if (command == null) {
@ -60,13 +63,13 @@ public class VelocityCommandManager implements CommandManager {
return commands.containsKey(command); 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(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine"); Preconditions.checkNotNull(cmdLine, "cmdLine");
String[] split = cmdLine.split(" ", -1); String[] split = cmdLine.split(" ", -1);
if (split.length == 0) { if (split.length == 0) {
return Optional.empty(); return ImmutableList.of();
} }
String alias = split[0]; String alias = split[0];
@ -78,21 +81,22 @@ public class VelocityCommandManager implements CommandManager {
availableCommands.add("/" + entry.getKey()); availableCommands.add("/" + entry.getKey());
} }
} }
return Optional.of(availableCommands); return availableCommands;
} }
@SuppressWarnings("nullness")
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length); String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
Command command = commands.get(alias.toLowerCase(Locale.ENGLISH)); Command command = commands.get(alias.toLowerCase(Locale.ENGLISH));
if (command == null) { if (command == null) {
return Optional.empty(); return ImmutableList.of();
} }
try { try {
if (!command.hasPermission(source, actualArgs)) { 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) { } catch (Exception e) {
throw new RuntimeException("Unable to invoke suggestions for command " + alias + " for " + source, 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 // Get a key name for config
ConfigKey key = field.getAnnotation(ConfigKey.class); 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. // Check if field is table.
Table table = field.getAnnotation(Table.class); Table table = field.getAnnotation(Table.class);
@ -130,7 +130,7 @@ public abstract class AnnotatedConfig {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, ?> map = (Map<String, ?>) field.get(dumpable); Map<String, ?> map = (Map<String, ?>) field.get(dumpable);
for (Entry<String, ?> entry : map.entrySet()) { 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 lines.add(""); // Add empty line
continue; continue;
@ -193,7 +193,7 @@ public abstract class AnnotatedConfig {
return value != null ? value.toString() : "null"; 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))) { if(key.contains(".") && !(key.indexOf('"') == 0 && key.lastIndexOf('"') == (key.length() - 1))) {
return '"' + key + '"'; return '"' + key + '"';
} }

Datei anzeigen

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

Datei anzeigen

@ -16,6 +16,7 @@ import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -35,10 +36,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
private final Channel channel; private final Channel channel;
private SocketAddress remoteAddress; private SocketAddress remoteAddress;
private StateRegistry state; private StateRegistry state;
private MinecraftSessionHandler sessionHandler; private @Nullable MinecraftSessionHandler sessionHandler;
private int protocolVersion; private int protocolVersion;
private int nextProtocolVersion; private int nextProtocolVersion;
private MinecraftConnectionAssociation association; private @Nullable MinecraftConnectionAssociation association;
private boolean isLegacyForge; private boolean isLegacyForge;
private final VelocityServer server; private final VelocityServer server;
private boolean canSendLegacyFMLResetPacket = false; private boolean canSendLegacyFMLResetPacket = false;
@ -74,6 +75,12 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 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 (msg instanceof MinecraftPacket) {
if (sessionHandler.beforeHandle()) { if (sessionHandler.beforeHandle()) {
return; return;
@ -197,6 +204,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
} }
} }
@Nullable
public MinecraftSessionHandler getSessionHandler() { public MinecraftSessionHandler getSessionHandler() {
return sessionHandler; return sessionHandler;
} }
@ -243,6 +251,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
channel.pipeline().addBefore(FRAME_ENCODER, CIPHER_ENCODER, new MinecraftCipherEncoder(encryptionCipher)); channel.pipeline().addBefore(FRAME_ENCODER, CIPHER_ENCODER, new MinecraftCipherEncoder(encryptionCipher));
} }
@Nullable
public MinecraftConnectionAssociation getAssociation() { public MinecraftConnectionAssociation getAssociation() {
return association; 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 String VELOCITY_IP_FORWARDING_CHANNEL = "velocity:player_info";
public static final int FORWARDING_VERSION = 1; 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.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
import com.velocitypowered.proxy.connection.forge.ForgeConstants; import com.velocitypowered.proxy.connection.forge.ForgeConstants;
@ -22,7 +23,12 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) { BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) {
this.server = server; this.server = server;
this.serverConn = serverConn; 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 @Override
@ -74,6 +80,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(PluginMessage packet) { public boolean handle(PluginMessage packet) {
MinecraftConnection smc = serverConn.getConnection();
if (smc == null) {
return true;
}
if (!canForwardPluginMessage(packet)) { if (!canForwardPluginMessage(packet)) {
return true; return true;
} }
@ -105,9 +116,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
server.getEventManager().fire(event) server.getEventManager().fire(event)
.thenAcceptAsync(pme -> { .thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed()) { if (pme.getResult().isAllowed()) {
serverConn.getPlayer().getConnection().write(packet); smc.write(packet);
} }
}, serverConn.getConnection().eventLoop()); }, smc.eventLoop());
return true; return true;
} }
@ -152,16 +163,18 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
} }
private boolean canForwardPluginMessage(PluginMessage message) { private boolean canForwardPluginMessage(PluginMessage message) {
ClientPlaySessionHandler playerHandler = MinecraftConnection mc = serverConn.getConnection();
(ClientPlaySessionHandler) serverConn.getPlayer().getConnection().getSessionHandler(); if (mc == null) {
return false;
}
boolean isMCOrFMLMessage; boolean isMCOrFMLMessage;
if (serverConn.getConnection().getProtocolVersion() <= ProtocolConstants.MINECRAFT_1_12_2) { if (mc.getProtocolVersion() <= ProtocolConstants.MINECRAFT_1_12_2) {
String channel = message.getChannel(); String channel = message.getChannel();
isMCOrFMLMessage = channel.startsWith("MC|") || channel.startsWith(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL); isMCOrFMLMessage = channel.startsWith("MC|") || channel.startsWith(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL);
} else { } else {
isMCOrFMLMessage = message.getChannel().startsWith("minecraft:"); isMCOrFMLMessage = message.getChannel().startsWith("minecraft:");
} }
return isMCOrFMLMessage || playerHandler.getClientPluginMsgChannels().contains(message.getChannel()) || return isMCOrFMLMessage || playerSessionHandler.getClientPluginMsgChannels().contains(message.getChannel()) ||
server.getChannelRegistrar().registered(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.VelocityServer;
import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.VelocityConstants;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
@ -38,6 +39,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
this.resultFuture = resultFuture; this.resultFuture = resultFuture;
} }
private MinecraftConnection ensureMinecraftConnection() {
MinecraftConnection mc = serverConn.getConnection();
if (mc == null) {
throw new IllegalStateException("Not connected to backend server!");
}
return mc;
}
@Override @Override
public boolean handle(EncryptionRequest packet) { public boolean handle(EncryptionRequest packet) {
throw new IllegalStateException("Backend server is online-mode!"); throw new IllegalStateException("Backend server is online-mode!");
@ -45,6 +54,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(LoginPluginMessage packet) { public boolean handle(LoginPluginMessage packet) {
MinecraftConnection mc = ensureMinecraftConnection();
VelocityConfiguration configuration = server.getConfiguration(); VelocityConfiguration configuration = server.getConfiguration();
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && packet.getChannel() if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && packet.getChannel()
.equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) { .equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) {
@ -54,7 +64,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
response.setData(createForwardingData(configuration.getForwardingSecret(), response.setData(createForwardingData(configuration.getForwardingSecret(),
serverConn.getPlayer().getRemoteAddress().getHostString(), serverConn.getPlayer().getRemoteAddress().getHostString(),
serverConn.getPlayer().getProfile())); serverConn.getPlayer().getProfile()));
serverConn.getConnection().write(response); mc.write(response);
informationForwarded = true; informationForwarded = true;
} else { } else {
// Don't understand // Don't understand
@ -62,7 +72,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
response.setSuccess(false); response.setSuccess(false);
response.setId(packet.getId()); response.setId(packet.getId());
response.setData(Unpooled.EMPTY_BUFFER); response.setData(Unpooled.EMPTY_BUFFER);
serverConn.getConnection().write(response); mc.write(response);
} }
return true; return true;
} }
@ -76,7 +86,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(SetCompression packet) { public boolean handle(SetCompression packet) {
serverConn.getConnection().setCompressionThreshold(packet.getThreshold()); ensureMinecraftConnection().setCompressionThreshold(packet.getThreshold());
return true; return true;
} }
@ -90,7 +100,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
} }
// The player has been logged on to the backend server. // 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(); VelocityServerConnection existingConnection = serverConn.getPlayer().getConnectedServer();
if (existingConnection == null) { if (existingConnection == null) {
// Strap on the play session handler // Strap on the play session handler
@ -104,14 +115,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
existingConnection.disconnect(); existingConnection.disconnect();
} }
serverConn.getConnection().getChannel().config().setAutoRead(false); smc.getChannel().config().setAutoRead(false);
server.getEventManager().fire(new ServerConnectedEvent(serverConn.getPlayer(), serverConn.getServer())) server.getEventManager().fire(new ServerConnectedEvent(serverConn.getPlayer(), serverConn.getServer()))
.whenCompleteAsync((x, error) -> { .whenCompleteAsync((x, error) -> {
resultFuture.complete(ConnectionRequestResults.SUCCESSFUL); resultFuture.complete(ConnectionRequestResults.SUCCESSFUL);
serverConn.getConnection().setSessionHandler(new BackendPlaySessionHandler(server, serverConn)); smc.setSessionHandler(new BackendPlaySessionHandler(server, serverConn));
serverConn.getPlayer().setConnectedServer(serverConn); serverConn.getPlayer().setConnectedServer(serverConn);
serverConn.getConnection().getChannel().config().setAutoRead(true); smc.getChannel().config().setAutoRead(true);
}, serverConn.getConnection().eventLoop()); }, smc.eventLoop());
return true; return true;
} }

Datei anzeigen

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

Datei anzeigen

@ -1,10 +1,12 @@
package com.velocitypowered.proxy.connection.client; package com.velocitypowered.proxy.connection.client;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.PlayerChatEvent; import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.forge.ForgeConstants; import com.velocitypowered.proxy.connection.forge.ForgeConstants;
@ -19,6 +21,7 @@ import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor; import net.kyori.text.format.TextColor;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.*; import java.util.*;
@ -36,7 +39,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
private final Set<String> clientPluginMsgChannels = new HashSet<>(); private final Set<String> clientPluginMsgChannels = new HashSet<>();
private final Queue<PluginMessage> loginPluginMessages = new ArrayDeque<>(); private final Queue<PluginMessage> loginPluginMessages = new ArrayDeque<>();
private final VelocityServer server; private final VelocityServer server;
private TabCompleteRequest outstandingTabComplete; private @Nullable TabCompleteRequest outstandingTabComplete;
public ClientPlaySessionHandler(VelocityServer server, ConnectedPlayer player) { public ClientPlaySessionHandler(VelocityServer server, ConnectedPlayer player) {
this.player = player; this.player = player;
@ -53,8 +56,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
public boolean handle(KeepAlive packet) { public boolean handle(KeepAlive packet) {
VelocityServerConnection serverConnection = player.getConnectedServer(); VelocityServerConnection serverConnection = player.getConnectedServer();
if (serverConnection != null && packet.getRandomId() == serverConnection.getLastPingId()) { 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()); player.setPing(System.currentTimeMillis() - serverConnection.getLastPingSent());
serverConnection.getConnection().write(packet); smc.write(packet);
serverConnection.resetLastPingId(); serverConnection.resetLastPingId();
} }
return true; return true;
@ -84,15 +92,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (serverConnection == null) { if (serverConnection == null) {
return true; return true;
} }
MinecraftConnection smc = serverConnection.getConnection();
if (smc == null) {
return true;
}
PlayerChatEvent event = new PlayerChatEvent(player, msg); PlayerChatEvent event = new PlayerChatEvent(player, msg);
server.getEventManager().fire(event) server.getEventManager().fire(event)
.thenAcceptAsync(pme -> { .thenAcceptAsync(pme -> {
if (pme.getResult().equals(PlayerChatEvent.ChatResult.allowed())){ PlayerChatEvent.ChatResult chatResult = pme.getResult();
serverConnection.getConnection().write(packet); if (chatResult.isAllowed()) {
} else if (pme.getResult().isAllowed() && pme.getResult().getMessage().isPresent()){ Optional<String> eventMsg = pme.getResult().getMessage();
serverConnection.getConnection().write(Chat.createServerbound(pme.getResult().getMessage().get())); if (eventMsg.isPresent()) {
smc.write(Chat.createServerbound(eventMsg.get()));
} else {
smc.write(packet);
}
} }
}, serverConnection.getConnection().eventLoop()); }, smc.eventLoop());
} }
return true; return true;
} }
@ -105,10 +121,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (spacePos > 0) { if (spacePos > 0) {
String cmd = packet.getCommand().substring(1, spacePos); String cmd = packet.getCommand().substring(1, spacePos);
if (server.getCommandManager().hasCommand(cmd)) { if (server.getCommandManager().hasCommand(cmd)) {
Optional<List<String>> suggestions = server.getCommandManager().offerSuggestions(player, packet.getCommand().substring(1)); List<String> suggestions = server.getCommandManager().offerSuggestions(player, packet.getCommand().substring(1));
if (suggestions.isPresent()) { if (suggestions.size() > 0) {
TabCompleteResponse resp = new TabCompleteResponse(); TabCompleteResponse resp = new TabCompleteResponse();
resp.getOffers().addAll(suggestions.get()); resp.getOffers().addAll(suggestions);
player.getConnection().write(resp); player.getConnection().write(resp);
return true; return true;
} }
@ -121,58 +137,68 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(PluginMessage packet) { public boolean handle(PluginMessage packet) {
if (PluginMessageUtil.isMCRegister(packet)) { VelocityServerConnection serverConn = player.getConnectedServer();
List<String> actuallyRegistered = new ArrayList<>(); MinecraftConnection backendConn = serverConn != null ? serverConn.getConnection() : null;
List<String> channels = PluginMessageUtil.getChannels(packet); if (serverConn != null && backendConn != null) {
for (String channel : channels) { if (PluginMessageUtil.isMCRegister(packet)) {
if (clientPluginMsgChannels.size() >= MAX_PLUGIN_CHANNELS && List<String> actuallyRegistered = new ArrayList<>();
!clientPluginMsgChannels.contains(channel)) { List<String> channels = PluginMessageUtil.getChannels(packet);
throw new IllegalStateException("Too many plugin message channels registered"); 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) { if (actuallyRegistered.size() > 0) {
PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(player.getConnectedServer() PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(backendConn
.getConnection().getProtocolVersion(), actuallyRegistered); .getProtocolVersion(), actuallyRegistered);
player.getConnectedServer().getConnection().write(newRegisterPacket); backendConn.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)));
} }
return true;
// Always forward the FML handshake to the remote server. } else if (PluginMessageUtil.isMCUnregister(packet)) {
player.getConnectedServer().getConnection().write(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 { } else {
// The client is trying to send messages too early. This is primarily caused by mods, but it's further ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
// aggravated by Velocity. To work around these issues, we will queue any non-FML handshake messages to if (id == null) {
// be sent once the JoinGame packet has been received by the proxy. backendConn.write(packet);
loginPluginMessages.add(packet); } else {
} PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, packet.getData());
} else { server.getEventManager().fire(event)
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel()); .thenAcceptAsync(pme -> {
if (id == null) { backendConn.write(packet);
player.getConnectedServer().getConnection().write(packet); }, backendConn.eventLoop());
} 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());
} }
} }
return true; return true;
} }
@ -184,9 +210,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
return; return;
} }
// If we don't want to handle this packet, just forward it on. MinecraftConnection smc = serverConnection.getConnection();
if (serverConnection.hasCompletedJoin()) { if (smc != null && serverConnection.hasCompletedJoin()) {
serverConnection.getConnection().write(packet); smc.write(packet);
} }
} }
@ -198,8 +224,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
return; return;
} }
if (serverConnection.hasCompletedJoin()) { MinecraftConnection smc = serverConnection.getConnection();
serverConnection.getConnection().write(buf.retain()); if (smc != null && serverConnection.hasCompletedJoin()) {
smc.write(buf.retain());
} }
} }
@ -222,11 +249,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
VelocityServerConnection server = player.getConnectedServer(); VelocityServerConnection server = player.getConnectedServer();
if (server != null) { if (server != null) {
boolean writable = player.getConnection().getChannel().isWritable(); 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) { 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) { if (!spawned) {
// Nothing special to do with regards to spawning the player // Nothing special to do with regards to spawning the player
spawned = true; spawned = true;
@ -272,7 +311,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
serverBossBars.clear(); serverBossBars.clear();
// Tell the server about this client's plugin message channels. // 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); Collection<String> toRegister = new HashSet<>(clientPluginMsgChannels);
if (serverVersion >= ProtocolConstants.MINECRAFT_1_13) { if (serverVersion >= ProtocolConstants.MINECRAFT_1_13) {
toRegister.addAll(server.getChannelRegistrar().getModernChannelIds()); toRegister.addAll(server.getChannelRegistrar().getModernChannelIds());
@ -280,14 +319,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
toRegister.addAll(server.getChannelRegistrar().getIdsForLegacyConnections()); toRegister.addAll(server.getChannelRegistrar().getIdsForLegacyConnections());
} }
if (!toRegister.isEmpty()) { if (!toRegister.isEmpty()) {
player.getConnectedServer().getConnection().delayedWrite(PluginMessageUtil.constructChannelsPacket( serverMc.delayedWrite(PluginMessageUtil.constructChannelsPacket(serverVersion, toRegister));
serverVersion, toRegister));
} }
// If we had plugin messages queued during login/FML handshake, send them now. // If we had plugin messages queued during login/FML handshake, send them now.
PluginMessage pm; PluginMessage pm;
while ((pm = loginPluginMessages.poll()) != null) { while ((pm = loginPluginMessages.poll()) != null) {
player.getConnectedServer().getConnection().delayedWrite(pm); serverMc.delayedWrite(pm);
} }
// Clear any title from the previous server. // Clear any title from the previous server.
@ -295,9 +333,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Flush everything // Flush everything
player.getConnection().flush(); player.getConnection().flush();
player.getConnectedServer().getConnection().flush(); serverMc.flush();
player.getConnectedServer().setHasCompletedJoin(true); serverConn.setHasCompletedJoin(true);
if (player.getConnectedServer().isLegacyForge()) { if (serverConn.isLegacyForge()) {
// We only need to indicate we can send a reset packet if we complete a handshake, that is, // We only need to indicate we can send a reset packet if we complete a handshake, that is,
// logged onto a Forge server. // logged onto a Forge server.
// //
@ -326,8 +364,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (!outstandingTabComplete.isAssumeCommand()) { if (!outstandingTabComplete.isAssumeCommand()) {
String command = outstandingTabComplete.getCommand().substring(1); String command = outstandingTabComplete.getCommand().substring(1);
try { try {
Optional<List<String>> offers = server.getCommandManager().offerSuggestions(player, command); response.getOffers().addAll(server.getCommandManager().offerSuggestions(player, command));
offers.ifPresent(strings -> response.getOffers().addAll(strings));
} catch (Exception e) { } catch (Exception e) {
logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(), logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(),
command, e); 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.PlayerSettings;
import com.velocitypowered.api.proxy.player.SkinParts; import com.velocitypowered.api.proxy.player.SkinParts;
import com.velocitypowered.proxy.protocol.packet.ClientSettings; import com.velocitypowered.proxy.protocol.packet.ClientSettings;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale; import java.util.Locale;
@ -11,7 +12,7 @@ public class ClientSettingsWrapper implements PlayerSettings {
private final ClientSettings settings; private final ClientSettings settings;
private final SkinParts parts; private final SkinParts parts;
private Locale locale = null; private @Nullable Locale locale;
ClientSettingsWrapper(ClientSettings settings) { ClientSettingsWrapper(ClientSettings settings) {
this.settings = 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 static final Logger logger = LogManager.getLogger(ConnectedPlayer.class);
private final MinecraftConnection connection; private final MinecraftConnection connection;
private final InetSocketAddress virtualHost; private @Nullable final InetSocketAddress virtualHost;
private GameProfile profile; private GameProfile profile;
private PermissionFunction permissionFunction = null; private PermissionFunction permissionFunction;
private int tryIndex = 0; private int tryIndex = 0;
private long ping = -1; private long ping = -1;
private VelocityServerConnection connectedServer; private @Nullable VelocityServerConnection connectedServer;
private VelocityServerConnection connectionInFlight; private @Nullable VelocityServerConnection connectionInFlight;
private PlayerSettings settings; private @Nullable PlayerSettings settings;
private ModInfo modInfo; private @Nullable ModInfo modInfo;
private final VelocityTabList tabList; private final VelocityTabList tabList;
private final VelocityServer server; private final VelocityServer server;
@MonotonicNonNull @MonotonicNonNull
private List<String> serversToTry = null; 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.server = server;
this.tabList = new VelocityTabList(connection); this.tabList = new VelocityTabList(connection);
this.profile = profile; this.profile = profile;
this.connection = connection; this.connection = connection;
this.virtualHost = virtualHost; this.virtualHost = virtualHost;
this.permissionFunction = (permission) -> Tristate.UNDEFINED;
} }
@Override @Override
@ -123,8 +124,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} }
void setPlayerSettings(ClientSettings settings) { void setPlayerSettings(ClientSettings settings) {
this.settings = new ClientSettingsWrapper(settings); ClientSettingsWrapper cs = new ClientSettingsWrapper(settings);
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, this.settings)); this.settings = cs;
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, cs));
} }
public Optional<ModInfo> getModInfo() { public Optional<ModInfo> getModInfo() {
@ -133,7 +135,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
void setModInfo(ModInfo modInfo) { void setModInfo(ModInfo modInfo) {
this.modInfo = modInfo; this.modInfo = modInfo;
server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, this.modInfo)); server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, modInfo));
} }
@Override @Override
@ -161,7 +163,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} }
@Override @Override
public void sendMessage(@NonNull Component component, @NonNull MessagePosition position) { public void sendMessage(Component component, MessagePosition position) {
Preconditions.checkNotNull(component, "component"); Preconditions.checkNotNull(component, "component");
Preconditions.checkNotNull(position, "position"); Preconditions.checkNotNull(position, "position");
@ -193,7 +195,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} }
@Override @Override
public ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server) { public ConnectionRequestBuilder createConnectionRequest(RegisteredServer server) {
return new ConnectionRequestBuilderImpl(server); return new ConnectionRequestBuilderImpl(server);
} }
@ -243,16 +245,19 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
connection.delayedWrite(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion())); connection.delayedWrite(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion()));
} }
if (tt.getTitle().isPresent()) { Optional<Component> titleText = tt.getTitle();
if (titleText.isPresent()) {
TitlePacket titlePkt = new TitlePacket(); TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_TITLE); titlePkt.setAction(TitlePacket.SET_TITLE);
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getTitle().get())); titlePkt.setComponent(ComponentSerializers.JSON.serialize(titleText.get()));
connection.delayedWrite(titlePkt); connection.delayedWrite(titlePkt);
} }
if (tt.getSubtitle().isPresent()) {
Optional<Component> subtitleText = tt.getSubtitle();
if (subtitleText.isPresent()) {
TitlePacket titlePkt = new TitlePacket(); TitlePacket titlePkt = new TitlePacket();
titlePkt.setAction(TitlePacket.SET_SUBTITLE); titlePkt.setAction(TitlePacket.SET_SUBTITLE);
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getSubtitle().get())); titlePkt.setComponent(ComponentSerializers.JSON.serialize(subtitleText.get()));
connection.delayedWrite(titlePkt); connection.delayedWrite(titlePkt);
} }
@ -270,16 +275,23 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} }
@Nullable
public VelocityServerConnection getConnectedServer() { public VelocityServerConnection getConnectedServer() {
return connectedServer; return connectedServer;
} }
public void handleConnectionException(RegisteredServer server, Throwable throwable) { public void handleConnectionException(RegisteredServer server, Throwable throwable) {
Throwable wrapped = throwable; if (throwable == null) {
if (throwable instanceof CompletionException) { throw new NullPointerException("throwable");
wrapped = throwable.getCause();
} }
Throwable wrapped = throwable;
if (throwable instanceof CompletionException) {
Throwable cause = throwable.getCause();
if (cause != null) {
wrapped = cause;
}
}
String error = ThrowableUtils.briefDescription(wrapped); String error = ThrowableUtils.briefDescription(wrapped);
String userMessage; String userMessage;
if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) { if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) {
@ -366,7 +378,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
String toTryName = serversToTry.get(tryIndex); String toTryName = serversToTry.get(tryIndex);
tryIndex++; tryIndex++;
return server.getServers().getServer(toTryName); return server.getServer(toTryName);
} }
private Optional<ConnectionRequestBuilder.Status> checkServer(RegisteredServer server) { private Optional<ConnectionRequestBuilder.Status> checkServer(RegisteredServer server) {
@ -390,13 +402,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
ServerPreConnectEvent event = new ServerPreConnectEvent(this, request.getServer()); ServerPreConnectEvent event = new ServerPreConnectEvent(this, request.getServer());
return server.getEventManager().fire(event) return server.getEventManager().fire(event)
.thenCompose((newEvent) -> { .thenCompose((newEvent) -> {
if (!newEvent.getResult().isAllowed()) { Optional<RegisteredServer> connectTo = newEvent.getResult().getServer();
if (!connectTo.isPresent()) {
return CompletableFuture.completedFuture( return CompletableFuture.completedFuture(
ConnectionRequestResults.plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED) ConnectionRequestResults.plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED)
); );
} }
RegisteredServer rs = newEvent.getResult().getServer().get(); RegisteredServer rs = connectTo.get();
Optional<ConnectionRequestBuilder.Status> lastCheck = checkServer(rs); Optional<ConnectionRequestBuilder.Status> lastCheck = checkServer(rs);
if (lastCheck.isPresent()) { if (lastCheck.isPresent()) {
return CompletableFuture.completedFuture(ConnectionRequestResults.plainResult(lastCheck.get())); return CompletableFuture.completedFuture(ConnectionRequestResults.plainResult(lastCheck.get()));
@ -406,9 +419,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} }
public void setConnectedServer(VelocityServerConnection serverConnection) { 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; this.tryIndex = 0;
} }
if (serverConnection == connectionInFlight) {
connectionInFlight = null;
}
this.connectedServer = serverConnection; this.connectedServer = serverConnection;
} }
@ -426,6 +443,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
connection.closeWith(Disconnect.create(reason)); 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() { void teardown() {
if (connectionInFlight != null) { if (connectionInFlight != null) {
connectionInFlight.disconnect(); connectionInFlight.disconnect();
@ -439,11 +470,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override @Override
public String toString() { public String toString() {
return "[connected player] " + getProfile().getName() + " (" + getRemoteAddress() + ")"; return "[connected player] " + profile.getName() + " (" + getRemoteAddress() + ")";
} }
@Override @Override
public @NonNull Tristate getPermissionValue(@NonNull String permission) { public Tristate getPermissionValue(String permission) {
return permissionFunction.getPermissionValue(permission); return permissionFunction.getPermissionValue(permission);
} }
@ -461,7 +492,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override @Override
public void spoofChatInput(String input) { 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"); 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 { 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.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.kyori.text.Component;
import net.kyori.text.TextComponent; import net.kyori.text.TextComponent;
import net.kyori.text.format.TextColor; import net.kyori.text.format.TextColor;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
@ -38,6 +40,8 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
public class LoginSessionHandler implements MinecraftSessionHandler { public class LoginSessionHandler implements MinecraftSessionHandler {
private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class); private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class);
@ -47,10 +51,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
private final VelocityServer server; private final VelocityServer server;
private final MinecraftConnection inbound; private final MinecraftConnection inbound;
private final InboundConnection apiInbound; private final InboundConnection apiInbound;
private ServerLogin login; private @MonotonicNonNull ServerLogin login;
private byte[] verify; private byte[] verify = EMPTY_BYTE_ARRAY;
private int playerInfoId; private int playerInfoId;
private ConnectedPlayer connectedPlayer; private @MonotonicNonNull ConnectedPlayer connectedPlayer;
public LoginSessionHandler(VelocityServer server, MinecraftConnection inbound, InboundConnection apiInbound) { public LoginSessionHandler(VelocityServer server, MinecraftConnection inbound, InboundConnection apiInbound) {
this.server = Preconditions.checkNotNull(server, "server"); this.server = Preconditions.checkNotNull(server, "server");
@ -62,12 +66,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
public boolean handle(ServerLogin packet) { public boolean handle(ServerLogin packet) {
this.login = packet; this.login = packet;
if (inbound.getProtocolVersion() >= ProtocolConstants.MINECRAFT_1_13) { if (inbound.getProtocolVersion() >= ProtocolConstants.MINECRAFT_1_13) {
LoginPluginMessage message = new LoginPluginMessage();
playerInfoId = ThreadLocalRandom.current().nextInt(); playerInfoId = ThreadLocalRandom.current().nextInt();
message.setId(playerInfoId); inbound.write(new LoginPluginMessage(playerInfoId, VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL,
message.setChannel(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL); Unpooled.EMPTY_BUFFER));
message.setData(Unpooled.EMPTY_BUFFER);
inbound.write(message);
} else { } else {
beginPreLogin(); beginPreLogin();
} }
@ -92,6 +93,15 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(EncryptionResponse packet) { 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 { try {
KeyPair serverKeyPair = server.getServerKeyPair(); KeyPair serverKeyPair = server.getServerKeyPair();
byte[] decryptedVerifyToken = EncryptionUtils.decryptRsa(serverKeyPair, packet.getVerifyToken()); byte[] decryptedVerifyToken = EncryptionUtils.decryptRsa(serverKeyPair, packet.getVerifyToken());
@ -149,6 +159,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
} }
private void beginPreLogin() { 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()); PreLoginEvent event = new PreLoginEvent(apiInbound, login.getUsername());
server.getEventManager().fire(event) server.getEventManager().fire(event)
.thenRunAsync(() -> { .thenRunAsync(() -> {
@ -157,9 +171,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
return; return;
} }
PreLoginComponentResult result = event.getResult(); 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. // 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; return;
} }
@ -212,13 +227,13 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
// The player was disconnected // The player was disconnected
return; 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()); }, inbound.eventLoop());
}); });

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -53,16 +53,14 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Comm
.completer((reader, parsedLine, list) -> { .completer((reader, parsedLine, list) -> {
try { try {
boolean isCommand = parsedLine.line().indexOf(' ') == -1; boolean isCommand = parsedLine.line().indexOf(' ') == -1;
Optional<List<String>> o = this.server.getCommandManager().offerSuggestions(this, parsedLine.line()); List<String> offers = this.server.getCommandManager().offerSuggestions(this, parsedLine.line());
o.ifPresent(offers -> { for (String offer : offers) {
for (String offer : offers) { if (isCommand) {
if (isCommand) { list.add(new Candidate(offer.substring(1)));
list.add(new Candidate(offer.substring(1))); } else {
} else { list.add(new Candidate(offer));
list.add(new Candidate(offer));
}
} }
}); }
} catch (Exception e) { } catch (Exception e) {
logger.error("An error occurred while trying to perform tab completion.", 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 io.netty.channel.WriteBufferWaterMark;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.initialization.qual.UnderInitialization;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.HashSet; import java.util.HashSet;
@ -27,16 +28,15 @@ public final class ConnectionManager {
private final VelocityServer server; private final VelocityServer server;
public final ServerChannelInitializerHolder serverChannelInitializer; public final ServerChannelInitializerHolder serverChannelInitializer;
public ConnectionManager(final VelocityServer server) { public ConnectionManager(VelocityServer server) {
this.server = server; this.server = server;
this.transportType = TransportType.bestType(); this.transportType = TransportType.bestType();
this.bossGroup = this.transportType.createEventLoopGroup(TransportType.Type.BOSS); this.bossGroup = this.transportType.createEventLoopGroup(TransportType.Type.BOSS);
this.workerGroup = this.transportType.createEventLoopGroup(TransportType.Type.WORKER); this.workerGroup = this.transportType.createEventLoopGroup(TransportType.Type.WORKER);
this.serverChannelInitializer = new ServerChannelInitializerHolder(new ServerChannelInitializer(this.server)); 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()); 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; package com.velocitypowered.proxy.network.http;
import com.google.common.base.VerifyException;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel; import io.netty.channel.Channel;
@ -61,6 +62,9 @@ public class NettyHttpClient {
.addListener(future -> { .addListener(future -> {
if (future.isSuccess()) { if (future.isSuccess()) {
Channel channel = (Channel) future.getNow(); Channel channel = (Channel) future.getNow();
if (channel == null) {
throw new VerifyException("Null channel retrieved from pool!");
}
channel.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply)); channel.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply));
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url.getPath() + "?" + url.getQuery()); 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) { public PluginClassLoader(URL[] urls) {
super(urls); super(urls);
}
public void addToClassloaders() {
loaders.add(this); loaders.add(this);
} }
public void addPath(Path path) { void addPath(Path path) {
try { try {
addURL(path.toUri().toURL()); addURL(path.toUri().toURL());
} catch (MalformedURLException e) { } catch (MalformedURLException e) {

Datei anzeigen

@ -12,7 +12,6 @@ import com.velocitypowered.api.plugin.PluginManager;
import net.kyori.event.EventSubscriber; import net.kyori.event.EventSubscriber;
import net.kyori.event.PostResult; import net.kyori.event.PostResult;
import net.kyori.event.SimpleEventBus; import net.kyori.event.SimpleEventBus;
import net.kyori.event.method.EventExecutor;
import net.kyori.event.method.MethodScanner; import net.kyori.event.method.MethodScanner;
import net.kyori.event.method.MethodSubscriptionAdapter; import net.kyori.event.method.MethodSubscriptionAdapter;
import net.kyori.event.method.SimpleMethodSubscriptionAdapter; 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.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -38,20 +39,24 @@ public class VelocityEventManager implements EventManager {
.synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new)); .synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new));
private final ListMultimap<Object, EventHandler<?>> registeredHandlersByPlugin = Multimaps private final ListMultimap<Object, EventHandler<?>> registeredHandlersByPlugin = Multimaps
.synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new)); .synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new));
private final VelocityEventBus bus = new VelocityEventBus( private final SimpleEventBus<Object> bus;
new ASMEventExecutorFactory<>(new PluginClassLoader(new URL[0])), private final MethodSubscriptionAdapter<Object> methodAdapter;
new VelocityMethodScanner());
private final ExecutorService service; private final ExecutorService service;
private final PluginManager pluginManager; private final PluginManager pluginManager;
public VelocityEventManager(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.pluginManager = pluginManager;
this.service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder() this.service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder()
.setNameFormat("Velocity Event Executor - #%d").setDaemon(true).build()); .setNameFormat("Velocity Event Executor - #%d").setDaemon(true).build());
} }
@Override @Override
public void register(@NonNull Object plugin, @NonNull Object listener) { public void register(Object plugin, Object listener) {
Preconditions.checkNotNull(plugin, "plugin"); Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkNotNull(listener, "listener"); Preconditions.checkNotNull(listener, "listener");
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded"); 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."); throw new IllegalArgumentException("Trying to register the plugin main instance. Velocity already takes care of this for you.");
} }
registeredListenersByPlugin.put(plugin, listener); registeredListenersByPlugin.put(plugin, listener);
bus.register(listener); methodAdapter.register(listener);
} }
@Override @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(plugin, "plugin");
Preconditions.checkNotNull(eventClass, "eventClass"); Preconditions.checkNotNull(eventClass, "eventClass");
Preconditions.checkNotNull(postOrder, "postOrder"); Preconditions.checkNotNull(postOrder, "postOrder");
@ -72,8 +78,10 @@ public class VelocityEventManager implements EventManager {
} }
@Override @Override
public <E> @NonNull CompletableFuture<E> fire(@NonNull E event) { public <E> CompletableFuture<E> fire(E event) {
Preconditions.checkNotNull(event, "event"); if (event == null) {
throw new NullPointerException("event");
}
if (!bus.hasSubscribers(event.getClass())) { if (!bus.hasSubscribers(event.getClass())) {
// Optimization: nobody's listening. // Optimization: nobody's listening.
return CompletableFuture.completedFuture(event); return CompletableFuture.completedFuture(event);
@ -99,30 +107,30 @@ public class VelocityEventManager implements EventManager {
} }
@Override @Override
public void unregisterListeners(@NonNull Object plugin) { public void unregisterListeners(Object plugin) {
Preconditions.checkNotNull(plugin, "plugin"); Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded"); Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
Collection<Object> listeners = registeredListenersByPlugin.removeAll(plugin); Collection<Object> listeners = registeredListenersByPlugin.removeAll(plugin);
listeners.forEach(bus::unregister); listeners.forEach(methodAdapter::unregister);
Collection<EventHandler<?>> handlers = registeredHandlersByPlugin.removeAll(plugin); Collection<EventHandler<?>> handlers = registeredHandlersByPlugin.removeAll(plugin);
handlers.forEach(bus::unregister); handlers.forEach(handler -> bus.unregister(new KyoriToVelocityHandler<>(handler, PostOrder.LAST)));
} }
@Override @Override
public void unregisterListener(@NonNull Object plugin, @NonNull Object listener) { public void unregisterListener(Object plugin, Object listener) {
Preconditions.checkNotNull(plugin, "plugin"); Preconditions.checkNotNull(plugin, "plugin");
Preconditions.checkNotNull(listener, "listener"); Preconditions.checkNotNull(listener, "listener");
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded"); Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
registeredListenersByPlugin.remove(plugin, listener); registeredListenersByPlugin.remove(plugin, listener);
bus.unregister(listener); methodAdapter.unregister(listener);
} }
@Override @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(plugin, "plugin");
Preconditions.checkNotNull(handler, "listener"); Preconditions.checkNotNull(handler, "listener");
registeredHandlersByPlugin.remove(plugin, handler); registeredHandlersByPlugin.remove(plugin, handler);
bus.unregister(handler); bus.unregister(new KyoriToVelocityHandler<>(handler, PostOrder.LAST));
} }
public boolean shutdown() throws InterruptedException { public boolean shutdown() throws InterruptedException {
@ -130,27 +138,6 @@ public class VelocityEventManager implements EventManager {
return service.awaitTermination(10, TimeUnit.SECONDS); 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> { private static class VelocityMethodScanner implements MethodScanner<Object> {
@Override @Override
public boolean shouldRegister(@NonNull Object listener, @NonNull Method method) { public boolean shouldRegister(@NonNull Object listener, @NonNull Method method) {
@ -159,7 +146,11 @@ public class VelocityEventManager implements EventManager {
@Override @Override
public int postOrder(@NonNull Object listener, @NonNull Method method) { 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 @Override
@ -190,5 +181,18 @@ public class VelocityEventManager implements EventManager {
public EventHandler<E> getHandler() { public EventHandler<E> getHandler() {
return handler; 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"); this.server = checkNotNull(server, "server");
} }
private void registerPlugin(@NonNull PluginContainer plugin) { private void registerPlugin(PluginContainer plugin) {
plugins.put(plugin.getDescription().getId(), 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"); checkNotNull(directory, "directory");
checkArgument(Files.isDirectory(directory), "provided path isn't a directory"); checkArgument(Files.isDirectory(directory), "provided path isn't a directory");
@ -86,7 +89,7 @@ public class VelocityPluginManager implements PluginManager {
} }
@Override @Override
public @NonNull Optional<PluginContainer> fromInstance(@NonNull Object instance) { public Optional<PluginContainer> fromInstance(Object instance) {
checkNotNull(instance, "instance"); checkNotNull(instance, "instance");
if (instance instanceof PluginContainer) { if (instance instanceof PluginContainer) {
@ -97,23 +100,23 @@ public class VelocityPluginManager implements PluginManager {
} }
@Override @Override
public @NonNull Optional<PluginContainer> getPlugin(@NonNull String id) { public Optional<PluginContainer> getPlugin(String id) {
checkNotNull(id, "id"); checkNotNull(id, "id");
return Optional.ofNullable(plugins.get(id)); return Optional.ofNullable(plugins.get(id));
} }
@Override @Override
public @NonNull Collection<PluginContainer> getPlugins() { public Collection<PluginContainer> getPlugins() {
return Collections.unmodifiableCollection(plugins.values()); return Collections.unmodifiableCollection(plugins.values());
} }
@Override @Override
public boolean isLoaded(@NonNull String id) { public boolean isLoaded(String id) {
return plugins.containsKey(id); return plugins.containsKey(id);
} }
@Override @Override
public void addToClasspath(@NonNull Object plugin, @NonNull Path path) { public void addToClasspath(Object plugin, Path path) {
checkNotNull(plugin, "instance"); checkNotNull(plugin, "instance");
checkNotNull(path, "path"); checkNotNull(path, "path");
checkArgument(pluginInstances.containsKey(plugin), "plugin is not loaded"); 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.PluginClassLoader;
import com.velocitypowered.proxy.plugin.loader.java.JavaVelocityPluginDescription; import com.velocitypowered.proxy.plugin.loader.java.JavaVelocityPluginDescription;
import com.velocitypowered.proxy.plugin.loader.java.VelocityPluginModule; import com.velocitypowered.proxy.plugin.loader.java.VelocityPluginModule;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -51,6 +52,7 @@ public class JavaPluginLoader implements PluginLoader {
PluginClassLoader loader = new PluginClassLoader( PluginClassLoader loader = new PluginClassLoader(
new URL[] {source.toUri().toURL() } new URL[] {source.toUri().toURL() }
); );
loader.addToClassloaders();
Class mainClass = loader.loadClass(pd.getMain()); Class mainClass = loader.loadClass(pd.getMain());
return createDescription(pd, source, mainClass); return createDescription(pd, source, mainClass);
@ -72,6 +74,10 @@ public class JavaPluginLoader implements PluginLoader {
Injector injector = Guice.createInjector(new VelocityPluginModule(server, javaDescription, baseDirectory)); Injector injector = Guice.createInjector(new VelocityPluginModule(server, javaDescription, baseDirectory));
Object instance = injector.getInstance(javaDescription.getMainClass()); 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); return new VelocityPluginContainer(description, instance);
} }
@ -93,10 +99,8 @@ public class JavaPluginLoader implements PluginLoader {
private VelocityPluginDescription createDescription(SerializedPluginDescription description, Path source, Class mainClass) { private VelocityPluginDescription createDescription(SerializedPluginDescription description, Path source, Class mainClass) {
Set<PluginDependency> dependencies = new HashSet<>(); Set<PluginDependency> dependencies = new HashSet<>();
if (description.getDependencies() != null) { for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) { dependencies.add(toDependencyMeta(dependency));
dependencies.add(toDependencyMeta(dependency));
}
} }
return new JavaVelocityPluginDescription( return new JavaVelocityPluginDescription(

Datei anzeigen

@ -33,7 +33,7 @@ public class VelocityPluginDescription implements PluginDescription {
this.description = Strings.emptyToNull(description); this.description = Strings.emptyToNull(description);
this.url = Strings.emptyToNull(url); this.url = Strings.emptyToNull(url);
this.authors = authors == null ? ImmutableList.of() : ImmutableList.copyOf(authors); 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; this.source = source;
} }

Datei anzeigen

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

Datei anzeigen

@ -48,6 +48,10 @@ public enum ProtocolConstants { ;
public enum Direction { public enum Direction {
SERVERBOUND, 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; package com.velocitypowered.proxy.protocol;
import com.google.common.base.Strings;
import com.google.common.primitives.ImmutableIntArray; import com.google.common.primitives.ImmutableIntArray;
import com.velocitypowered.proxy.protocol.packet.*; import com.velocitypowered.proxy.protocol.packet.*;
import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects; import java.util.Objects;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -36,6 +36,9 @@ public enum StateRegistry {
}, },
PLAY { PLAY {
{ {
SERVERBOUND.fallback = false;
CLIENTBOUND.fallback = false;
SERVERBOUND.register(TabCompleteRequest.class, TabCompleteRequest::new, SERVERBOUND.register(TabCompleteRequest.class, TabCompleteRequest::new,
map(0x14, MINECRAFT_1_8, false), map(0x14, MINECRAFT_1_8, false),
map(0x01, MINECRAFT_1_9, 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 STATUS_ID = 1;
public static final int LOGIN_ID = 2; public static final int LOGIN_ID = 2;
public final PacketRegistry CLIENTBOUND = new PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND, this); public final PacketRegistry CLIENTBOUND = new PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND);
public final PacketRegistry SERVERBOUND = new PacketRegistry(ProtocolConstants.Direction.SERVERBOUND, this); public final PacketRegistry SERVERBOUND = new PacketRegistry(ProtocolConstants.Direction.SERVERBOUND);
public static class PacketRegistry { public static class PacketRegistry {
private static final IntObjectMap<ImmutableIntArray> LINKED_PROTOCOL_VERSIONS = new IntObjectHashMap<>(); private static final IntObjectMap<ImmutableIntArray> LINKED_PROTOCOL_VERSIONS = new IntObjectHashMap<>();
@ -165,19 +168,18 @@ public enum StateRegistry {
} }
private final ProtocolConstants.Direction direction; private final ProtocolConstants.Direction direction;
private final StateRegistry state;
private final IntObjectMap<ProtocolVersion> versions = new IntObjectHashMap<>(16); 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.direction = direction;
this.state = state;
ProtocolConstants.SUPPORTED_VERSIONS.forEach(version -> versions.put(version, new ProtocolVersion(version))); ProtocolConstants.SUPPORTED_VERSIONS.forEach(version -> versions.put(version, new ProtocolVersion(version)));
} }
public ProtocolVersion getVersion(final int version) { public ProtocolVersion getVersion(final int version) {
ProtocolVersion result = versions.get(version); ProtocolVersion result = versions.get(version);
if (result == null) { if (result == null) {
if (state != PLAY) { if (fallback) {
return getVersion(MINIMUM_GENERIC_VERSION); return getVersion(MINIMUM_GENERIC_VERSION);
} }
throw new IllegalArgumentException("Could not find data for protocol version " + 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); 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); final Supplier<? extends MinecraftPacket> supplier = this.packetIdToSupplier.get(id);
if (supplier == null) { if (supplier == null) {
return null; return null;
@ -242,23 +244,6 @@ public enum StateRegistry {
} }
return id; 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 @Override
public boolean equals(Object o) { public boolean equals(@Nullable Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
PacketMapping that = (PacketMapping) o; PacketMapping that = (PacketMapping) o;

Datei anzeigen

@ -13,14 +13,14 @@ import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List; import java.util.List;
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> { public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
private StateRegistry state;
private final ProtocolConstants.Direction direction; private final ProtocolConstants.Direction direction;
private StateRegistry state;
private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion; private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion;
public MinecraftDecoder(ProtocolConstants.Direction direction) { public MinecraftDecoder(ProtocolConstants.Direction direction) {
this.state = StateRegistry.HANDSHAKE;
this.direction = Preconditions.checkNotNull(direction, "direction"); 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 @Override
@ -51,24 +51,12 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
} }
} }
public StateRegistry.PacketRegistry.ProtocolVersion getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(int protocolVersion) { public void setProtocolVersion(int protocolVersion) {
this.protocolVersion = (this.direction == ProtocolConstants.Direction.CLIENTBOUND ? this.state.CLIENTBOUND : this.state.SERVERBOUND).getVersion(protocolVersion); this.protocolVersion = direction.getProtocol(state, protocolVersion);
}
public StateRegistry getState() {
return state;
} }
public void setState(StateRegistry state) { public void setState(StateRegistry state) {
this.state = state; this.state = state;
this.setProtocolVersion(protocolVersion.id); 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; import io.netty.handler.codec.MessageToByteEncoder;
public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> { public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
private StateRegistry state;
private final ProtocolConstants.Direction direction; private final ProtocolConstants.Direction direction;
private StateRegistry state;
private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion; private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion;
public MinecraftEncoder(ProtocolConstants.Direction direction) { public MinecraftEncoder(ProtocolConstants.Direction direction) {
this.state = StateRegistry.HANDSHAKE;
this.direction = Preconditions.checkNotNull(direction, "direction"); 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 @Override
@ -27,24 +27,12 @@ public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
msg.encode(out, direction, protocolVersion.id); msg.encode(out, direction, protocolVersion.id);
} }
public StateRegistry.PacketRegistry.ProtocolVersion getProtocolVersion() {
return protocolVersion;
}
public void setProtocolVersion(final int protocolVersion) { public void setProtocolVersion(final int protocolVersion) {
this.protocolVersion = (this.direction == ProtocolConstants.Direction.CLIENTBOUND ? this.state.CLIENTBOUND : this.state.SERVERBOUND).getVersion(protocolVersion); this.protocolVersion = direction.getProtocol(state, protocolVersion);
}
public StateRegistry getState() {
return state;
} }
public void setState(StateRegistry state) { public void setState(StateRegistry state) {
this.state = state; this.state = state;
this.setProtocolVersion(protocolVersion.id); 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.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID; 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_NAME = 3;
public static final int UPDATE_STYLE = 4; public static final int UPDATE_STYLE = 4;
public static final int UPDATE_PROPERTIES = 5; public static final int UPDATE_PROPERTIES = 5;
private UUID uuid; private @Nullable UUID uuid;
private int action; private int action;
private String name; private @Nullable String name;
private float percent; private float percent;
private int color; private int color;
private int overlay; private int overlay;
private short flags; private short flags;
public UUID getUuid() { public UUID getUuid() {
if (uuid == null) {
throw new IllegalStateException("No boss bar UUID specified");
}
return uuid; return uuid;
} }
@ -39,7 +43,7 @@ public class BossBar implements MinecraftPacket {
this.action = action; this.action = action;
} }
public String getName() { public @Nullable String getName() {
return name; return name;
} }
@ -124,10 +128,16 @@ public class BossBar implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { 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.writeUuid(buf, uuid);
ProtocolUtils.writeVarInt(buf, action); ProtocolUtils.writeVarInt(buf, action);
switch (action) { switch (action) {
case ADD: case ADD:
if (name == null) {
throw new IllegalStateException("No name specified!");
}
ProtocolUtils.writeString(buf, name); ProtocolUtils.writeString(buf, name);
buf.writeFloat(percent); buf.writeFloat(percent);
ProtocolUtils.writeVarInt(buf, color); ProtocolUtils.writeVarInt(buf, color);
@ -140,6 +150,9 @@ public class BossBar implements MinecraftPacket {
buf.writeFloat(percent); buf.writeFloat(percent);
break; break;
case UPDATE_NAME: case UPDATE_NAME:
if (name == null) {
throw new IllegalStateException("No name specified!");
}
ProtocolUtils.writeString(buf, name); ProtocolUtils.writeString(buf, name);
break; break;
case UPDATE_STYLE: case UPDATE_STYLE:

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.proxy.protocol.packet; package com.velocitypowered.proxy.protocol.packet;
import com.google.common.base.Preconditions;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants.Direction; 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; import static com.velocitypowered.proxy.protocol.ProtocolUtils.writeString;
public class HeaderAndFooter implements MinecraftPacket { public class HeaderAndFooter implements MinecraftPacket {
private static final String EMPTY_COMPONENT = "{\"translate\":\"\"}";
private static final HeaderAndFooter RESET = new HeaderAndFooter("{\"translate\":\"\"}", "{\"translate\":\"\"}"); private static final HeaderAndFooter RESET = new HeaderAndFooter();
private String header; private String header;
private String footer; private String footer;
public HeaderAndFooter() { public HeaderAndFooter() {
this(EMPTY_COMPONENT, EMPTY_COMPONENT);
} }
public HeaderAndFooter(String header, String footer) { public HeaderAndFooter(String header, String footer) {
this.header = header; this.header = Preconditions.checkNotNull(header, "header");
this.footer = footer; this.footer = Preconditions.checkNotNull(footer, "footer");
} }
public String getHeader() { public String getHeader() {
return header; return header;
} }
public void setHeader(String header) {
this.header = header;
}
public String getFooter() { public String getFooter() {
return footer; return footer;
} }
public void setFooter(String footer) {
this.footer = footer;
}
@Override @Override
public void decode(ByteBuf buf, Direction direction, int protocolVersion) { public void decode(ByteBuf buf, Direction direction, int protocolVersion) {
throw new UnsupportedOperationException("Decode is not implemented"); 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.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class JoinGame implements MinecraftPacket { public class JoinGame implements MinecraftPacket {
private int entityId; private int entityId;
@ -12,7 +13,7 @@ public class JoinGame implements MinecraftPacket {
private int dimension; private int dimension;
private short difficulty; private short difficulty;
private short maxPlayers; private short maxPlayers;
private String levelType; private @Nullable String levelType;
private boolean reducedDebugInfo; private boolean reducedDebugInfo;
public int getEntityId() { public int getEntityId() {
@ -56,6 +57,9 @@ public class JoinGame implements MinecraftPacket {
} }
public String getLevelType() { public String getLevelType() {
if (levelType == null) {
throw new IllegalStateException("No level type specified.");
}
return levelType; return levelType;
} }
@ -110,6 +114,9 @@ public class JoinGame implements MinecraftPacket {
} }
buf.writeByte(difficulty); buf.writeByte(difficulty);
buf.writeByte(maxPlayers); buf.writeByte(maxPlayers);
if (levelType == null) {
throw new IllegalStateException("No level type specified.");
}
ProtocolUtils.writeString(buf, levelType); ProtocolUtils.writeString(buf, levelType);
buf.writeBoolean(reducedDebugInfo); buf.writeBoolean(reducedDebugInfo);
} }

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,20 +1,27 @@
package com.velocitypowered.proxy.protocol.packet; package com.velocitypowered.proxy.protocol.packet;
import com.google.common.base.Preconditions;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ServerLogin implements MinecraftPacket { public class ServerLogin implements MinecraftPacket {
private String username; private @Nullable String username;
public String getUsername() { public ServerLogin() {}
return username;
public ServerLogin(String username) {
this.username = Preconditions.checkNotNull(username, "username");
} }
public void setUsername(String username) { public String getUsername() {
this.username = username; if (username == null) {
throw new IllegalStateException("No username found!");
}
return username;
} }
@Override @Override
@ -31,6 +38,9 @@ public class ServerLogin implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (username == null) {
throw new IllegalStateException("No username found!");
}
ProtocolUtils.writeString(buf, username); 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.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID; import java.util.UUID;
public class ServerLoginSuccess implements MinecraftPacket { public class ServerLoginSuccess implements MinecraftPacket {
private UUID uuid; private @Nullable UUID uuid;
private String username; private @Nullable String username;
public UUID getUuid() { public UUID getUuid() {
if (uuid == null) {
throw new IllegalStateException("No UUID specified!");
}
return uuid; return uuid;
} }
@ -21,6 +25,9 @@ public class ServerLoginSuccess implements MinecraftPacket {
} }
public String getUsername() { public String getUsername() {
if (username == null) {
throw new IllegalStateException("No username specified!");
}
return username; return username;
} }
@ -44,7 +51,13 @@ public class ServerLoginSuccess implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (uuid == null) {
throw new IllegalStateException("No UUID specified!");
}
ProtocolUtils.writeString(buf, uuid.toString()); ProtocolUtils.writeString(buf, uuid.toString());
if (username == null) {
throw new IllegalStateException("No username specified!");
}
ProtocolUtils.writeString(buf, username); 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.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class StatusResponse implements MinecraftPacket { public class StatusResponse implements MinecraftPacket {
private String status; private @Nullable String status;
public String getStatus() { public StatusResponse() {}
return status;
public StatusResponse(String status) {
this.status = status;
} }
public void setStatus(String status) { public String getStatus() {
this.status = status; if (status == null) {
throw new IllegalStateException("Status is not specified");
}
return status;
} }
@Override @Override
@ -31,6 +37,9 @@ public class StatusResponse implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (status == null) {
throw new IllegalStateException("Status is not specified");
}
ProtocolUtils.writeString(buf, status); 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.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_9; import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_9;
public class TabCompleteRequest implements MinecraftPacket { public class TabCompleteRequest implements MinecraftPacket {
private String command; private @Nullable String command;
private boolean assumeCommand; private boolean assumeCommand;
private boolean hasPosition; private boolean hasPosition;
private long position; private long position;
public String getCommand() { public String getCommand() {
if (command == null) {
throw new IllegalStateException("Command is not specified");
}
return command; return command;
} }
@ -70,6 +74,9 @@ public class TabCompleteRequest implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
if (command == null) {
throw new IllegalStateException("Command is not specified");
}
ProtocolUtils.writeString(buf, command); ProtocolUtils.writeString(buf, command);
if (protocolVersion >= MINECRAFT_1_9) { if (protocolVersion >= MINECRAFT_1_9) {
buf.writeBoolean(assumeCommand); 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.ProtocolConstants;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class TitlePacket implements MinecraftPacket { public class TitlePacket implements MinecraftPacket {
public static final int SET_TITLE = 0; public static final int SET_TITLE = 0;
@ -18,7 +19,7 @@ public class TitlePacket implements MinecraftPacket {
public static final int RESET_OLD = 4; public static final int RESET_OLD = 4;
private int action; private int action;
private String component; private @Nullable String component;
private int fadeIn; private int fadeIn;
private int stay; private int stay;
private int fadeOut; private int fadeOut;
@ -37,6 +38,9 @@ public class TitlePacket implements MinecraftPacket {
case SET_TITLE: case SET_TITLE:
case SET_SUBTITLE: case SET_SUBTITLE:
case SET_ACTION_BAR: case SET_ACTION_BAR:
if (component == null) {
throw new IllegalStateException("No component found for " + action);
}
ProtocolUtils.writeString(buf, component); ProtocolUtils.writeString(buf, component);
break; break;
case SET_TIMES: case SET_TIMES:
@ -52,6 +56,9 @@ public class TitlePacket implements MinecraftPacket {
switch (action) { switch (action) {
case SET_TITLE: case SET_TITLE:
case SET_SUBTITLE: case SET_SUBTITLE:
if (component == null) {
throw new IllegalStateException("No component found for " + action);
}
ProtocolUtils.writeString(buf, component); ProtocolUtils.writeString(buf, component);
break; break;
case SET_TIMES_OLD: case SET_TIMES_OLD:
@ -74,11 +81,11 @@ public class TitlePacket implements MinecraftPacket {
this.action = action; this.action = action;
} }
public String getComponent() { public @Nullable String getComponent() {
return component; return component;
} }
public void setComponent(String component) { public void setComponent(@Nullable String component) {
this.component = component; this.component = component;
} }

Datei anzeigen

@ -11,6 +11,7 @@ import com.velocitypowered.api.scheduler.Scheduler;
import com.velocitypowered.api.scheduler.TaskStatus; import com.velocitypowered.api.scheduler.TaskStatus;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -92,6 +93,7 @@ public class VelocityScheduler implements Scheduler {
public ScheduledTask schedule() { public ScheduledTask schedule() {
VelocityTask task = new VelocityTask(plugin, runnable, delay, repeat); VelocityTask task = new VelocityTask(plugin, runnable, delay, repeat);
tasksByPlugin.put(plugin, task); tasksByPlugin.put(plugin, task);
task.schedule();
return task; return task;
} }
} }
@ -99,12 +101,19 @@ public class VelocityScheduler implements Scheduler {
private class VelocityTask implements Runnable, ScheduledTask { private class VelocityTask implements Runnable, ScheduledTask {
private final Object plugin; private final Object plugin;
private final Runnable runnable; private final Runnable runnable;
private ScheduledFuture<?> future; private final long delay;
private volatile Thread currentTaskThread; private final long repeat;
private @Nullable ScheduledFuture<?> future;
private volatile @Nullable Thread currentTaskThread;
private VelocityTask(Object plugin, Runnable runnable, long delay, long repeat) { private VelocityTask(Object plugin, Runnable runnable, long delay, long repeat) {
this.plugin = plugin; this.plugin = plugin;
this.runnable = runnable; this.runnable = runnable;
this.delay = delay;
this.repeat = repeat;
}
public void schedule() {
if (repeat == 0) { if (repeat == 0) {
this.future = timerExecutionService.schedule(this, delay, TimeUnit.MILLISECONDS); this.future = timerExecutionService.schedule(this, delay, TimeUnit.MILLISECONDS);
} else { } else {
@ -155,14 +164,10 @@ public class VelocityScheduler implements Scheduler {
try { try {
runnable.run(); runnable.run();
} catch (Exception e) { } catch (Exception e) {
// Since we can't catch InterruptedException separately... Log.logger.error("Exception in task {} by plugin {}", runnable, plugin);
if (e instanceof InterruptedException) { } finally {
onFinish(); currentTaskThread = null;
} else {
Log.logger.error("Exception in task {} by plugin {}", runnable, plugin);
}
} }
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.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Locale; import java.util.Locale;
@ -13,10 +14,10 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
public class ServerMap { public class ServerMap {
private final VelocityServer server; private final @Nullable VelocityServer server;
private final Map<String, RegisteredServer> servers = new ConcurrentHashMap<>(); private final Map<String, RegisteredServer> servers = new ConcurrentHashMap<>();
public ServerMap(VelocityServer server) { public ServerMap(@Nullable VelocityServer server) {
this.server = server; this.server = server;
} }
@ -49,7 +50,9 @@ public class ServerMap {
Preconditions.checkNotNull(serverInfo, "serverInfo"); Preconditions.checkNotNull(serverInfo, "serverInfo");
String lowerName = serverInfo.getName().toLowerCase(Locale.US); String lowerName = serverInfo.getName().toLowerCase(Locale.US);
RegisteredServer rs = servers.get(lowerName); 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.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()); 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