Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-16 21:10:30 +01:00
Checker Framework integration (#126)
Dieser Commit ist enthalten in:
Ursprung
dccf688da8
Commit
32829c5637
35
CONTRIBUTING.md
Normale Datei
35
CONTRIBUTING.md
Normale Datei
@ -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.
|
@ -4,6 +4,8 @@ plugins {
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
apply from: '../gradle/checkerframework.gradle'
|
||||
|
||||
sourceSets {
|
||||
ap {
|
||||
compileClasspath += main.compileClasspath + main.output
|
||||
@ -17,7 +19,7 @@ dependencies {
|
||||
compile 'com.moandjiezana.toml:toml4j:0.7.2'
|
||||
compile "org.slf4j:slf4j-api:${slf4jVersion}"
|
||||
compile 'com.google.inject:guice:4.2.0'
|
||||
compile 'org.checkerframework:checker-qual:2.5.4'
|
||||
compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
|
||||
|
||||
testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
|
||||
|
@ -2,6 +2,7 @@ package com.velocitypowered.api.plugin.ap;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
@ -29,8 +30,8 @@ public class SerializedPluginDescription {
|
||||
this.version = Strings.emptyToNull(version);
|
||||
this.description = Strings.emptyToNull(description);
|
||||
this.url = Strings.emptyToNull(url);
|
||||
this.authors = authors == null || authors.isEmpty() ? null : authors;
|
||||
this.dependencies = dependencies == null || dependencies.isEmpty() ? null : dependencies;
|
||||
this.authors = authors == null || authors.isEmpty() ? ImmutableList.of() : authors;
|
||||
this.dependencies = dependencies == null || dependencies.isEmpty() ? ImmutableList.of() : dependencies;
|
||||
this.main = Preconditions.checkNotNull(main, "main");
|
||||
}
|
||||
|
||||
@ -63,12 +64,12 @@ public class SerializedPluginDescription {
|
||||
return url;
|
||||
}
|
||||
|
||||
public @Nullable List<String> getAuthors() {
|
||||
return authors;
|
||||
public List<String> getAuthors() {
|
||||
return authors == null ? ImmutableList.of() : authors;
|
||||
}
|
||||
|
||||
public @Nullable List<Dependency> getDependencies() {
|
||||
return dependencies;
|
||||
public List<Dependency> getDependencies() {
|
||||
return dependencies == null ? ImmutableList.of() : dependencies;
|
||||
}
|
||||
|
||||
public String getMain() {
|
||||
|
@ -15,7 +15,7 @@ public interface Command {
|
||||
* @param source the source of this command
|
||||
* @param args the arguments for this command
|
||||
*/
|
||||
void execute(@NonNull CommandSource source, @NonNull String[] args);
|
||||
void execute(CommandSource source, String @NonNull [] args);
|
||||
|
||||
/**
|
||||
* Provides tab complete suggestions for a command for a specified {@link CommandSource}.
|
||||
@ -23,7 +23,7 @@ public interface Command {
|
||||
* @param currentArgs the current, partial arguments for this command
|
||||
* @return tab complete suggestions
|
||||
*/
|
||||
default List<String> suggest(@NonNull CommandSource source, @NonNull String[] currentArgs) {
|
||||
default List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public interface Command {
|
||||
* @param args the arguments for this command
|
||||
* @return whether the source has permission
|
||||
*/
|
||||
default boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
default boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.velocitypowered.api.command;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Represents an interface to register a command executor with the proxy.
|
||||
*/
|
||||
@ -11,13 +9,13 @@ public interface CommandManager {
|
||||
* @param command the command to register
|
||||
* @param aliases the alias to use
|
||||
*/
|
||||
void register(@NonNull Command command, String... aliases);
|
||||
void register(Command command, String... aliases);
|
||||
|
||||
/**
|
||||
* Unregisters a command.
|
||||
* @param alias the command alias to unregister
|
||||
*/
|
||||
void unregister(@NonNull String alias);
|
||||
void unregister(String alias);
|
||||
|
||||
/**
|
||||
* Attempts to execute a command from the specified {@code cmdLine}.
|
||||
@ -25,5 +23,5 @@ public interface CommandManager {
|
||||
* @param cmdLine the command to run
|
||||
* @return true if the command was found and executed, false if it was not
|
||||
*/
|
||||
boolean execute(@NonNull CommandSource source, @NonNull String cmdLine);
|
||||
boolean execute(CommandSource source, String cmdLine);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.velocitypowered.api.command;
|
||||
|
||||
import com.velocitypowered.api.permission.PermissionSubject;
|
||||
import net.kyori.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Represents something that can be used to run a {@link Command}.
|
||||
@ -12,5 +11,5 @@ public interface CommandSource extends PermissionSubject {
|
||||
* Sends the specified {@code component} to the invoker.
|
||||
* @param component the text component to send
|
||||
*/
|
||||
void sendMessage(@NonNull Component component);
|
||||
void sendMessage(Component component);
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
package com.velocitypowered.api.event;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Represents an interface to perform direct dispatch of an event. This makes integration easier to achieve with platforms
|
||||
* such as RxJava.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EventHandler<E> {
|
||||
void execute(@NonNull E event);
|
||||
void execute(E event);
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.velocitypowered.api.event;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@ -13,7 +11,7 @@ public interface EventManager {
|
||||
* @param plugin the plugin to associate with the listener
|
||||
* @param listener the listener to register
|
||||
*/
|
||||
void register(@NonNull Object plugin, @NonNull Object listener);
|
||||
void register(Object plugin, Object listener);
|
||||
|
||||
/**
|
||||
* Requests that the specified {@code handler} listen for events and associate it with the {@code plugin}.
|
||||
@ -22,7 +20,7 @@ public interface EventManager {
|
||||
* @param handler the handler to register
|
||||
* @param <E> the event type to handle
|
||||
*/
|
||||
default <E> void register(@NonNull Object plugin, @NonNull Class<E> eventClass, @NonNull EventHandler<E> handler) {
|
||||
default <E> void register(Object plugin, Class<E> eventClass, EventHandler<E> handler) {
|
||||
register(plugin, eventClass, PostOrder.NORMAL, handler);
|
||||
}
|
||||
|
||||
@ -34,7 +32,7 @@ public interface EventManager {
|
||||
* @param handler the handler to register
|
||||
* @param <E> the event type to handle
|
||||
*/
|
||||
<E> void register(@NonNull Object plugin, @NonNull Class<E> eventClass, @NonNull PostOrder postOrder, @NonNull EventHandler<E> handler);
|
||||
<E> void register(Object plugin, Class<E> eventClass, PostOrder postOrder, EventHandler<E> handler);
|
||||
|
||||
/**
|
||||
* Fires the specified event to the event bus asynchronously. This allows Velocity to continue servicing connections
|
||||
@ -42,13 +40,13 @@ public interface EventManager {
|
||||
* @param event the event to fire
|
||||
* @return a {@link CompletableFuture} representing the posted event
|
||||
*/
|
||||
@NonNull <E> CompletableFuture<E> fire(@NonNull E event);
|
||||
<E> CompletableFuture<E> fire(E event);
|
||||
|
||||
/**
|
||||
* Posts the specified event to the event bus and discards the result.
|
||||
* @param event the event to fire
|
||||
*/
|
||||
default void fireAndForget(@NonNull Object event) {
|
||||
default void fireAndForget(Object event) {
|
||||
fire(event);
|
||||
}
|
||||
|
||||
@ -56,14 +54,14 @@ public interface EventManager {
|
||||
* Unregisters all listeners for the specified {@code plugin}.
|
||||
* @param plugin the plugin to deregister listeners for
|
||||
*/
|
||||
void unregisterListeners(@NonNull Object plugin);
|
||||
void unregisterListeners(Object plugin);
|
||||
|
||||
/**
|
||||
* Unregisters a specific listener for a specific plugin.
|
||||
* @param plugin the plugin associated with the listener
|
||||
* @param listener the listener to deregister
|
||||
*/
|
||||
void unregisterListener(@NonNull Object plugin, @NonNull Object listener);
|
||||
void unregisterListener(Object plugin, Object listener);
|
||||
|
||||
/**
|
||||
* Unregisters a specific event handler for a specific plugin.
|
||||
@ -71,5 +69,5 @@ public interface EventManager {
|
||||
* @param handler the handler to register
|
||||
* @param <E> the event type to handle
|
||||
*/
|
||||
<E> void unregister(@NonNull Object plugin, @NonNull EventHandler<E> handler);
|
||||
<E> void unregister(Object plugin, EventHandler<E> handler);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public interface ResultedEvent<R extends ResultedEvent.Result> {
|
||||
* Sets the result of this event. The result must be non-null.
|
||||
* @param result the new result
|
||||
*/
|
||||
void setResult(@NonNull R result);
|
||||
void setResult(R result);
|
||||
|
||||
/**
|
||||
* Represents a result for an event.
|
||||
@ -106,7 +106,7 @@ public interface ResultedEvent<R extends ResultedEvent.Result> {
|
||||
return ALLOWED;
|
||||
}
|
||||
|
||||
public static ComponentResult denied(@NonNull Component reason) {
|
||||
public static ComponentResult denied(Component reason) {
|
||||
Preconditions.checkNotNull(reason, "reason");
|
||||
return new ComponentResult(false, reason);
|
||||
}
|
||||
|
@ -2,15 +2,14 @@ package com.velocitypowered.api.event.connection;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* This event is fired when a handshake is established between a client and Velocity.
|
||||
*/
|
||||
public final class ConnectionHandshakeEvent {
|
||||
private final @NonNull InboundConnection connection;
|
||||
private final InboundConnection connection;
|
||||
|
||||
public ConnectionHandshakeEvent(@NonNull InboundConnection connection) {
|
||||
public ConnectionHandshakeEvent(InboundConnection connection) {
|
||||
this.connection = Preconditions.checkNotNull(connection, "connection");
|
||||
}
|
||||
|
||||
|
@ -2,16 +2,15 @@ package com.velocitypowered.api.event.connection;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* This event is fired when a player disconnects from the proxy. Operations on the provided player, aside from basic
|
||||
* data retrieval operations, may behave in undefined ways.
|
||||
*/
|
||||
public final class DisconnectEvent {
|
||||
private @NonNull final Player player;
|
||||
private final Player player;
|
||||
|
||||
public DisconnectEvent(@NonNull Player player) {
|
||||
public DisconnectEvent(Player player) {
|
||||
this.player = Preconditions.checkNotNull(player, "player");
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ package com.velocitypowered.api.event.connection;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.event.ResultedEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* This event is fired once the player has been authenticated but before they connect to a server on the proxy.
|
||||
@ -12,7 +11,7 @@ public final class LoginEvent implements ResultedEvent<ResultedEvent.ComponentRe
|
||||
private final Player player;
|
||||
private ComponentResult result;
|
||||
|
||||
public LoginEvent(@NonNull Player player) {
|
||||
public LoginEvent(Player player) {
|
||||
this.player = Preconditions.checkNotNull(player, "player");
|
||||
this.result = ComponentResult.allowed();
|
||||
}
|
||||
@ -27,7 +26,7 @@ public final class LoginEvent implements ResultedEvent<ResultedEvent.ComponentRe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(@NonNull ComponentResult result) {
|
||||
public void setResult(ComponentResult result) {
|
||||
this.result = Preconditions.checkNotNull(result, "result");
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ public final class PluginMessageEvent implements ResultedEvent<PluginMessageEven
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(@NonNull ForwardResult result) {
|
||||
public void setResult(ForwardResult result) {
|
||||
this.result = Preconditions.checkNotNull(result, "result");
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.event.ResultedEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
@ -62,11 +61,11 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
|
||||
private static final PreLoginComponentResult FORCE_OFFLINEMODE = new PreLoginComponentResult(Result.FORCE_OFFLINE, null);
|
||||
|
||||
private final Result result;
|
||||
private final Optional<Component> reason;
|
||||
private final @Nullable Component reason;
|
||||
|
||||
private PreLoginComponentResult(Result result, @Nullable Component reason) {
|
||||
this.result = result;
|
||||
this.reason = Optional.ofNullable(reason);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -75,7 +74,7 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
|
||||
}
|
||||
|
||||
public Optional<Component> getReason() {
|
||||
return reason;
|
||||
return Optional.ofNullable(reason);
|
||||
}
|
||||
|
||||
public boolean isOnlineModeAllowed() {
|
||||
@ -88,19 +87,16 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isForceOfflineMode()) {
|
||||
return "allowed with force offline mode";
|
||||
switch (result) {
|
||||
case ALLOWED:
|
||||
return "allowed";
|
||||
case FORCE_OFFLINE:
|
||||
return "allowed with force offline mode";
|
||||
case FORCE_ONLINE:
|
||||
return "allowed with online mode";
|
||||
default:
|
||||
return "denied";
|
||||
}
|
||||
if (isOnlineModeAllowed()) {
|
||||
return "allowed with online mode";
|
||||
}
|
||||
if (isAllowed()) {
|
||||
return "allowed";
|
||||
}
|
||||
if (reason.isPresent()) {
|
||||
return "denied: " + ComponentSerializers.PLAIN.serialize(reason.get());
|
||||
}
|
||||
return "denied";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.permission.PermissionFunction;
|
||||
import com.velocitypowered.api.permission.PermissionProvider;
|
||||
import com.velocitypowered.api.permission.PermissionSubject;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
@ -22,7 +21,7 @@ public final class PermissionsSetupEvent {
|
||||
this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider");
|
||||
}
|
||||
|
||||
public @NonNull PermissionSubject getSubject() {
|
||||
public PermissionSubject getSubject() {
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
@ -33,11 +32,11 @@ public final class PermissionsSetupEvent {
|
||||
* @param subject the subject
|
||||
* @return the obtained permission function
|
||||
*/
|
||||
public @NonNull PermissionFunction createFunction(PermissionSubject subject) {
|
||||
public PermissionFunction createFunction(PermissionSubject subject) {
|
||||
return this.provider.createFunction(subject);
|
||||
}
|
||||
|
||||
public @NonNull PermissionProvider getProvider() {
|
||||
public PermissionProvider getProvider() {
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ public final class GameProfileRequestEvent {
|
||||
private final InboundConnection connection;
|
||||
private final GameProfile originalProfile;
|
||||
private final boolean onlineMode;
|
||||
private GameProfile gameProfile;
|
||||
private @Nullable GameProfile gameProfile;
|
||||
|
||||
public GameProfileRequestEvent(InboundConnection connection, GameProfile originalProfile, boolean onlineMode) {
|
||||
this.connection = Preconditions.checkNotNull(connection, "connection");
|
||||
|
@ -36,7 +36,7 @@ public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.Chat
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(@NonNull ChatResult result) {
|
||||
public void setResult(ChatResult result) {
|
||||
this.result = Preconditions.checkNotNull(result, "result");
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ public final class PlayerSettingsChangedEvent {
|
||||
return player;
|
||||
}
|
||||
|
||||
//New settings
|
||||
public PlayerSettings getPlayerSettings() {
|
||||
return playerSettings;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.event.ResultedEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
@ -33,7 +32,7 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(@NonNull ServerResult result) {
|
||||
public void setResult(ServerResult result) {
|
||||
this.result = Preconditions.checkNotNull(result, "result");
|
||||
}
|
||||
|
||||
@ -54,19 +53,17 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
|
||||
* Represents the result of the {@link ServerPreConnectEvent}.
|
||||
*/
|
||||
public static class ServerResult implements ResultedEvent.Result {
|
||||
private static final ServerResult DENIED = new ServerResult(false, null);
|
||||
private static final ServerResult DENIED = new ServerResult(null);
|
||||
|
||||
private final boolean allowed;
|
||||
private final RegisteredServer server;
|
||||
private final @Nullable RegisteredServer server;
|
||||
|
||||
private ServerResult(boolean allowed, @Nullable RegisteredServer server) {
|
||||
this.allowed = allowed;
|
||||
private ServerResult(@Nullable RegisteredServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowed() {
|
||||
return allowed;
|
||||
return server != null;
|
||||
}
|
||||
|
||||
public Optional<RegisteredServer> getServer() {
|
||||
@ -75,10 +72,10 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!allowed) {
|
||||
return "denied";
|
||||
if (server != null) {
|
||||
return "allowed: connect to " + server.getServerInfo().getName();
|
||||
}
|
||||
return "allowed: connect to " + server.getServerInfo().getName();
|
||||
return "denied";
|
||||
}
|
||||
|
||||
public static ServerResult denied() {
|
||||
@ -87,7 +84,7 @@ public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConne
|
||||
|
||||
public static ServerResult allowed(RegisteredServer server) {
|
||||
Preconditions.checkNotNull(server, "server");
|
||||
return new ServerResult(true, server);
|
||||
return new ServerResult(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.velocitypowered.api.permission;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Function that calculates the permission settings for a given
|
||||
* {@link PermissionSubject}.
|
||||
@ -29,5 +27,5 @@ public interface PermissionFunction {
|
||||
* @param permission the permission
|
||||
* @return the value the permission is set to
|
||||
*/
|
||||
@NonNull Tristate getPermissionValue(@NonNull String permission);
|
||||
Tristate getPermissionValue(String permission);
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.velocitypowered.api.permission;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Provides {@link PermissionFunction}s for {@link PermissionSubject}s.
|
||||
*/
|
||||
@ -13,5 +11,5 @@ public interface PermissionProvider {
|
||||
* @param subject the subject
|
||||
* @return the function
|
||||
*/
|
||||
@NonNull PermissionFunction createFunction(@NonNull PermissionSubject subject);
|
||||
PermissionFunction createFunction(PermissionSubject subject);
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.velocitypowered.api.permission;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Represents a object that has a set of queryable permissions.
|
||||
*/
|
||||
@ -12,7 +10,7 @@ public interface PermissionSubject {
|
||||
* @param permission the permission to check for
|
||||
* @return whether or not the subject has the permission
|
||||
*/
|
||||
default boolean hasPermission(@NonNull String permission) {
|
||||
default boolean hasPermission(String permission) {
|
||||
return getPermissionValue(permission).asBoolean();
|
||||
}
|
||||
|
||||
@ -22,5 +20,5 @@ public interface PermissionSubject {
|
||||
* @param permission the permission
|
||||
* @return the value the permission is set to
|
||||
*/
|
||||
@NonNull Tristate getPermissionValue(@NonNull String permission);
|
||||
Tristate getPermissionValue(String permission);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ public enum Tristate {
|
||||
* @param val the boolean value
|
||||
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or <code>false</code>, respectively.
|
||||
*/
|
||||
public static @NonNull Tristate fromBoolean(boolean val) {
|
||||
public static Tristate fromBoolean(boolean val) {
|
||||
return val ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ public enum Tristate {
|
||||
* @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value
|
||||
* is <code>null</code>, <code>true</code> or <code>false</code>, respectively.
|
||||
*/
|
||||
public static @NonNull Tristate fromNullableBoolean(@Nullable Boolean val) {
|
||||
public static Tristate fromNullableBoolean(@Nullable Boolean val) {
|
||||
return val == null ? UNDEFINED : val ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.velocitypowered.api.plugin;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@ -13,7 +11,7 @@ public interface PluginContainer {
|
||||
*
|
||||
* @return the plugin's description
|
||||
*/
|
||||
@NonNull PluginDescription getDescription();
|
||||
PluginDescription getDescription();
|
||||
|
||||
/**
|
||||
* Returns the created plugin if it is available.
|
||||
|
@ -1,16 +1,12 @@
|
||||
package com.velocitypowered.api.plugin;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* The class that manages plugins. This manager can retrieve
|
||||
* {@link PluginContainer}s from {@link Plugin} instances, getting
|
||||
* {@link Logger}s, etc.
|
||||
* Manages plugins loaded on the proxy. This manager can retrieve {@link PluginContainer}s from plugin instances
|
||||
* and inject arbitrary JAR files into the plugin classpath with {@link #addToClasspath(Object, Path)}.
|
||||
*/
|
||||
public interface PluginManager {
|
||||
/**
|
||||
@ -19,7 +15,7 @@ public interface PluginManager {
|
||||
* @param instance the instance
|
||||
* @return the container
|
||||
*/
|
||||
@NonNull Optional<PluginContainer> fromInstance(@NonNull Object instance);
|
||||
Optional<PluginContainer> fromInstance(Object instance);
|
||||
|
||||
/**
|
||||
* Retrieves a {@link PluginContainer} based on its ID.
|
||||
@ -27,22 +23,22 @@ public interface PluginManager {
|
||||
* @param id the plugin ID
|
||||
* @return the plugin, if available
|
||||
*/
|
||||
@NonNull Optional<PluginContainer> getPlugin(@NonNull String id);
|
||||
Optional<PluginContainer> getPlugin(String id);
|
||||
|
||||
/**
|
||||
* Gets a {@link Collection} of all {@link PluginContainer}s.
|
||||
*
|
||||
* @return the plugins
|
||||
*/
|
||||
@NonNull Collection<PluginContainer> getPlugins();
|
||||
Collection<PluginContainer> getPlugins();
|
||||
|
||||
/**
|
||||
* Checks if a plugin is loaded based on its ID.
|
||||
*
|
||||
* @param id the id of the {@link Plugin}
|
||||
* @param id the id of the plugin
|
||||
* @return {@code true} if loaded
|
||||
*/
|
||||
boolean isLoaded(@NonNull String id);
|
||||
boolean isLoaded(String id);
|
||||
|
||||
/**
|
||||
* Adds the specified {@code path} to the plugin classpath.
|
||||
@ -51,5 +47,5 @@ public interface PluginManager {
|
||||
* @param path the path to the JAR you want to inject into the classpath
|
||||
* @throws UnsupportedOperationException if the operation is not applicable to this plugin
|
||||
*/
|
||||
void addToClasspath(@NonNull Object plugin, @NonNull Path path);
|
||||
void addToClasspath(Object plugin, Path path);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.velocitypowered.api.plugin.meta;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -13,7 +14,8 @@ import static com.google.common.base.Strings.emptyToNull;
|
||||
*/
|
||||
public final class PluginDependency {
|
||||
private final String id;
|
||||
@Nullable private final String version;
|
||||
@Nullable
|
||||
private final String version;
|
||||
|
||||
private final boolean optional;
|
||||
|
||||
@ -53,7 +55,7 @@ public final class PluginDependency {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PluginDependency that = (PluginDependency) o;
|
||||
|
@ -13,7 +13,6 @@ import com.velocitypowered.api.util.title.Title;
|
||||
import java.util.List;
|
||||
|
||||
import net.kyori.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@ -62,7 +61,7 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
|
||||
* Sends a chat message to the player's client.
|
||||
* @param component the chat message to send
|
||||
*/
|
||||
default void sendMessage(@NonNull Component component) {
|
||||
default void sendMessage(Component component) {
|
||||
sendMessage(component, MessagePosition.CHAT);
|
||||
}
|
||||
|
||||
@ -71,14 +70,14 @@ public interface Player extends CommandSource, InboundConnection, ChannelMessage
|
||||
* @param component the chat message to send
|
||||
* @param position the position for the message
|
||||
*/
|
||||
void sendMessage(@NonNull Component component, @NonNull MessagePosition position);
|
||||
void sendMessage(Component component, MessagePosition position);
|
||||
|
||||
/**
|
||||
* Creates a new connection request so that the player can connect to another server.
|
||||
* @param server the server to connect to
|
||||
* @return a new connection request
|
||||
*/
|
||||
ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server);
|
||||
ConnectionRequestBuilder createConnectionRequest(RegisteredServer server);
|
||||
|
||||
/**
|
||||
* Gets the player's profile properties.
|
||||
|
@ -9,6 +9,7 @@ import com.velocitypowered.api.proxy.messages.ChannelRegistrar;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
import com.velocitypowered.api.scheduler.Scheduler;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
import net.kyori.text.Component;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
@ -131,4 +132,10 @@ public interface ProxyServer {
|
||||
* @return the proxy config
|
||||
* */
|
||||
ProxyConfig getConfiguration();
|
||||
|
||||
/**
|
||||
* Returns the version of the proxy.
|
||||
* @return the proxy version
|
||||
*/
|
||||
ProxyVersion getVersion();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.velocitypowered.api.proxy.messages;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -27,7 +28,7 @@ public final class LegacyChannelIdentifier implements ChannelIdentifier {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
LegacyChannelIdentifier that = (LegacyChannelIdentifier) o;
|
||||
|
@ -2,6 +2,7 @@ package com.velocitypowered.api.proxy.messages;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
@ -54,11 +55,11 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getId() + " (modern)";
|
||||
return namespace + ":" + name + " (modern)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
MinecraftChannelIdentifier that = (MinecraftChannelIdentifier) o;
|
||||
|
@ -3,6 +3,7 @@ package com.velocitypowered.api.proxy.player;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import net.kyori.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
@ -47,5 +48,5 @@ public interface TabList {
|
||||
|
||||
// Necessary because the TabListEntry implementation isn't in the api
|
||||
@Deprecated
|
||||
TabListEntry buildEntry(GameProfile profile, Component displayName, int latency, int gameMode);
|
||||
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, int gameMode);
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package com.velocitypowered.api.proxy.player;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import net.kyori.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
@ -16,7 +14,7 @@ public interface TabListEntry {
|
||||
* Returns the parent {@link TabList} of this {@code this} {@link TabListEntry}.
|
||||
* @return parent {@link TabList}
|
||||
*/
|
||||
@NonNull TabList getTabList();
|
||||
TabList getTabList();
|
||||
|
||||
/**
|
||||
* Returns the {@link GameProfile} of the entry, which uniquely identifies the entry
|
||||
@ -24,14 +22,14 @@ public interface TabListEntry {
|
||||
* as the player head in the tab list.
|
||||
* @return {@link GameProfile} of the entry
|
||||
*/
|
||||
@NonNull GameProfile getProfile();
|
||||
GameProfile getProfile();
|
||||
|
||||
/**
|
||||
* Returns {@link Optional} text {@link Component}, which if present is the text displayed for
|
||||
* {@code this} entry in the {@link TabList}, otherwise {@link GameProfile#getName()} is shown.
|
||||
* @return {@link Optional} text {@link Component} of name displayed in the tab list
|
||||
*/
|
||||
@NonNull Optional<Component> getDisplayName();
|
||||
Optional<Component> getDisplayName();
|
||||
|
||||
/**
|
||||
* Sets the text {@link Component} to be displayed for {@code this} {@link TabListEntry}.
|
||||
@ -39,7 +37,7 @@ public interface TabListEntry {
|
||||
* @param displayName to show in the {@link TabList} for {@code this} entry
|
||||
* @return {@code this}, for chaining
|
||||
*/
|
||||
@NonNull TabListEntry setDisplayName(@Nullable Component displayName);
|
||||
TabListEntry setDisplayName(@Nullable Component displayName);
|
||||
|
||||
/**
|
||||
* Returns the latency for {@code this} entry.
|
||||
@ -63,7 +61,7 @@ public interface TabListEntry {
|
||||
* @param latency to changed to
|
||||
* @return {@code this}, for chaining
|
||||
*/
|
||||
@NonNull TabListEntry setLatency(int latency);
|
||||
TabListEntry setLatency(int latency);
|
||||
|
||||
/**
|
||||
* Gets the game mode {@code this} entry has been set to.
|
||||
@ -99,9 +97,9 @@ public interface TabListEntry {
|
||||
* @see TabListEntry
|
||||
*/
|
||||
class Builder {
|
||||
private TabList tabList;
|
||||
private GameProfile profile;
|
||||
private Component displayName;
|
||||
private @Nullable TabList tabList;
|
||||
private @Nullable GameProfile profile;
|
||||
private @Nullable Component displayName;
|
||||
private int latency = 0;
|
||||
private int gameMode = 0;
|
||||
|
||||
@ -167,9 +165,12 @@ public interface TabListEntry {
|
||||
* @return the constructed {@link TabListEntry}
|
||||
*/
|
||||
public TabListEntry build() {
|
||||
Preconditions.checkState(tabList != null, "The Tablist must be set when building a TabListEntry");
|
||||
Preconditions.checkState(profile != null, "The GameProfile must be set when building a TabListEntry");
|
||||
|
||||
if (tabList == null) {
|
||||
throw new IllegalStateException("The Tablist must be set when building a TabListEntry");
|
||||
}
|
||||
if (profile == null) {
|
||||
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
|
||||
}
|
||||
return tabList.buildEntry(profile, displayName, latency, gameMode);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.velocitypowered.api.proxy.server;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Objects;
|
||||
@ -10,24 +10,24 @@ import java.util.Objects;
|
||||
* ServerInfo represents a server that a player can connect to. This object is immutable and safe for concurrent access.
|
||||
*/
|
||||
public final class ServerInfo {
|
||||
private final @NonNull String name;
|
||||
private final @NonNull InetSocketAddress address;
|
||||
private final String name;
|
||||
private final InetSocketAddress address;
|
||||
|
||||
/**
|
||||
* Creates a new ServerInfo object.
|
||||
* @param name the name for the server
|
||||
* @param address the address of the server to connect to
|
||||
*/
|
||||
public ServerInfo(@NonNull String name, @NonNull InetSocketAddress address) {
|
||||
public ServerInfo(String name, InetSocketAddress address) {
|
||||
this.name = Preconditions.checkNotNull(name, "name");
|
||||
this.address = Preconditions.checkNotNull(address, "address");
|
||||
}
|
||||
|
||||
public final @NonNull String getName() {
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public final @NonNull InetSocketAddress getAddress() {
|
||||
public final InetSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ public final class ServerInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
public final boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ServerInfo that = (ServerInfo) o;
|
||||
|
@ -14,7 +14,7 @@ import java.util.*;
|
||||
*/
|
||||
public final class ServerPing {
|
||||
private final Version version;
|
||||
private final Players players;
|
||||
private final @Nullable Players players;
|
||||
private final Component description;
|
||||
private final @Nullable Favicon favicon;
|
||||
private final @Nullable ModInfo modinfo;
|
||||
@ -89,14 +89,14 @@ public final class ServerPing {
|
||||
* A builder for {@link ServerPing} objects.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private Version version;
|
||||
private Version version = new Version(0, "Unknown");
|
||||
private int onlinePlayers;
|
||||
private int maximumPlayers;
|
||||
private final List<SamplePlayer> samplePlayers = new ArrayList<>();
|
||||
private String modType;
|
||||
private String modType = "FML";
|
||||
private final List<ModInfo.Mod> mods = new ArrayList<>();
|
||||
private Component description;
|
||||
private Favicon favicon;
|
||||
private @Nullable Component description;
|
||||
private @Nullable Favicon favicon;
|
||||
private boolean nullOutPlayers;
|
||||
private boolean nullOutModinfo;
|
||||
|
||||
@ -165,6 +165,12 @@ public final class ServerPing {
|
||||
}
|
||||
|
||||
public ServerPing build() {
|
||||
if (this.version == null) {
|
||||
throw new IllegalStateException("version not specified");
|
||||
}
|
||||
if (this.description == null) {
|
||||
throw new IllegalStateException("no server description supplied");
|
||||
}
|
||||
return new ServerPing(version, nullOutPlayers ? null : new Players(onlinePlayers, maximumPlayers, samplePlayers),
|
||||
description, favicon, nullOutModinfo ? null : new ModInfo(modType, mods));
|
||||
}
|
||||
@ -185,12 +191,12 @@ public final class ServerPing {
|
||||
return samplePlayers;
|
||||
}
|
||||
|
||||
public Component getDescription() {
|
||||
return description;
|
||||
public Optional<Component> getDescription() {
|
||||
return Optional.ofNullable(description);
|
||||
}
|
||||
|
||||
public Favicon getFavicon() {
|
||||
return favicon;
|
||||
public Optional<Favicon> getFavicon() {
|
||||
return Optional.ofNullable(favicon);
|
||||
}
|
||||
|
||||
public String getModType() {
|
||||
|
@ -1,5 +1,8 @@
|
||||
package com.velocitypowered.api.scheduler;
|
||||
|
||||
import org.checkerframework.common.value.qual.IntRange;
|
||||
import org.checkerframework.common.value.qual.IntRangeFromNonNegative;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -24,7 +27,7 @@ public interface Scheduler {
|
||||
* @param unit the unit of time for {@code time}
|
||||
* @return this builder, for chaining
|
||||
*/
|
||||
TaskBuilder delay(long time, TimeUnit unit);
|
||||
TaskBuilder delay(@IntRange(from = 0) long time, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Specifies that the task should continue running after waiting for the specified amount, until it is cancelled.
|
||||
@ -32,7 +35,7 @@ public interface Scheduler {
|
||||
* @param unit the unit of time for {@code time}
|
||||
* @return this builder, for chaining
|
||||
*/
|
||||
TaskBuilder repeat(long time, TimeUnit unit);
|
||||
TaskBuilder repeat(@IntRange(from = 0) long time, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Clears the delay on this task.
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.velocitypowered.api.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@ -25,7 +25,7 @@ public final class Favicon {
|
||||
* of functions.
|
||||
* @param base64Url the url for use with this favicon
|
||||
*/
|
||||
public Favicon(@NonNull String base64Url) {
|
||||
public Favicon(String base64Url) {
|
||||
this.base64Url = Preconditions.checkNotNull(base64Url, "base64Url");
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public final class Favicon {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Favicon favicon = (Favicon) o;
|
||||
@ -62,7 +62,7 @@ public final class Favicon {
|
||||
* @param image the image to use for the favicon
|
||||
* @return the created {@link Favicon} instance
|
||||
*/
|
||||
public static Favicon create(@NonNull BufferedImage image) {
|
||||
public static Favicon create(BufferedImage image) {
|
||||
Preconditions.checkNotNull(image, "image");
|
||||
Preconditions.checkArgument(image.getWidth() == 64 && image.getHeight() == 64, "Image does not have" +
|
||||
" 64x64 dimensions (found %sx%s)", image.getWidth(), image.getHeight());
|
||||
@ -81,7 +81,7 @@ public final class Favicon {
|
||||
* @return the created {@link Favicon} instance
|
||||
* @throws IOException if the file could not be read from the path
|
||||
*/
|
||||
public static Favicon create(@NonNull Path path) throws IOException {
|
||||
public static Favicon create(Path path) throws IOException {
|
||||
try (InputStream stream = Files.newInputStream(path)) {
|
||||
return create(ImageIO.read(stream));
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@ -15,7 +14,7 @@ public final class GameProfile {
|
||||
private final String name;
|
||||
private final List<Property> properties;
|
||||
|
||||
public GameProfile(@NonNull String id, @NonNull String name, @NonNull List<Property> properties) {
|
||||
public GameProfile(String id, String name, List<Property> properties) {
|
||||
this.id = Preconditions.checkNotNull(id, "id");
|
||||
this.name = Preconditions.checkNotNull(name, "name");
|
||||
this.properties = ImmutableList.copyOf(properties);
|
||||
@ -42,7 +41,7 @@ public final class GameProfile {
|
||||
* @param username the username to use
|
||||
* @return the new offline-mode game profile
|
||||
*/
|
||||
public static GameProfile forOfflinePlayer(@NonNull String username) {
|
||||
public static GameProfile forOfflinePlayer(String username) {
|
||||
Preconditions.checkNotNull(username, "username");
|
||||
String id = UuidUtils.toUndashed(UuidUtils.generateOfflinePlayerUuid(username));
|
||||
return new GameProfile(id, username, ImmutableList.of());
|
||||
@ -62,7 +61,7 @@ public final class GameProfile {
|
||||
private final String value;
|
||||
private final String signature;
|
||||
|
||||
public Property(@NonNull String name, @NonNull String value, @NonNull String signature) {
|
||||
public Property(String name, String value, String signature) {
|
||||
this.name = Preconditions.checkNotNull(name, "name");
|
||||
this.value = Preconditions.checkNotNull(value, "value");
|
||||
this.signature = Preconditions.checkNotNull(signature, "signature");
|
||||
|
57
api/src/main/java/com/velocitypowered/api/util/ProxyVersion.java
Normale Datei
57
api/src/main/java/com/velocitypowered/api/util/ProxyVersion.java
Normale Datei
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
@ -21,7 +20,7 @@ public final class UuidUtils {
|
||||
* @param string the string to convert
|
||||
* @return the UUID object
|
||||
*/
|
||||
public static @NonNull UUID fromUndashed(final @NonNull String string) {
|
||||
public static UUID fromUndashed(final String string) {
|
||||
Objects.requireNonNull(string, "string");
|
||||
Preconditions.checkArgument(string.length() == 32, "Length is incorrect");
|
||||
return new UUID(
|
||||
@ -35,7 +34,7 @@ public final class UuidUtils {
|
||||
* @param uuid the UUID to convert
|
||||
* @return the undashed UUID
|
||||
*/
|
||||
public static @NonNull String toUndashed(final @NonNull UUID uuid) {
|
||||
public static String toUndashed(final UUID uuid) {
|
||||
Preconditions.checkNotNull(uuid, "uuid");
|
||||
return Strings.padStart(Long.toHexString(uuid.getMostSignificantBits()), 16, '0') +
|
||||
Strings.padStart(Long.toHexString(uuid.getLeastSignificantBits()), 16, '0');
|
||||
@ -46,7 +45,7 @@ public final class UuidUtils {
|
||||
* @param username the username to use
|
||||
* @return the offline mode UUID
|
||||
*/
|
||||
public static @NonNull UUID generateOfflinePlayerUuid(@NonNull String username) {
|
||||
public static UUID generateOfflinePlayerUuid(String username) {
|
||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ import java.util.Optional;
|
||||
* Represents a "full" title, including all components. This class is immutable.
|
||||
*/
|
||||
public final class TextTitle implements Title {
|
||||
private final Component title;
|
||||
private final Component subtitle;
|
||||
private final @Nullable Component title;
|
||||
private final @Nullable Component subtitle;
|
||||
private final int stay;
|
||||
private final int fadeIn;
|
||||
private final int fadeOut;
|
||||
@ -94,7 +94,7 @@ public final class TextTitle implements Title {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TextTitle textTitle = (TextTitle) o;
|
||||
@ -193,12 +193,12 @@ public final class TextTitle implements Title {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Component getTitle() {
|
||||
return title;
|
||||
public Optional<Component> getTitle() {
|
||||
return Optional.ofNullable(title);
|
||||
}
|
||||
|
||||
public Component getSubtitle() {
|
||||
return subtitle;
|
||||
public Optional<Component> getSubtitle() {
|
||||
return Optional.ofNullable(subtitle);
|
||||
}
|
||||
|
||||
public int getStay() {
|
||||
|
@ -16,6 +16,7 @@ allprojects {
|
||||
log4jVersion = '2.11.1'
|
||||
nettyVersion = '4.1.30.Final'
|
||||
guavaVersion = '25.1-jre'
|
||||
checkerFrameworkVersion = '2.5.6'
|
||||
|
||||
getCurrentBranchName = {
|
||||
new ByteArrayOutputStream().withStream { os ->
|
||||
|
70
gradle/checkerframework.gradle
Normale Datei
70
gradle/checkerframework.gradle
Normale Datei
@ -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}"
|
||||
]
|
||||
}
|
||||
}
|
@ -2,9 +2,13 @@ plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
apply from: '../gradle/checkerframework.gradle'
|
||||
|
||||
dependencies {
|
||||
compile "com.google.guava:guava:${guavaVersion}"
|
||||
compile "io.netty:netty-buffer:${nettyVersion}"
|
||||
compile "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
|
||||
|
||||
testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
|
||||
testCompile "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
|
||||
}
|
@ -1,88 +1,64 @@
|
||||
package com.velocitypowered.natives.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class NativeCodeLoader<T> implements Supplier<T> {
|
||||
private final List<Variant<T>> variants;
|
||||
private volatile Variant<T> selected;
|
||||
public final class NativeCodeLoader<T> implements Supplier<T> {
|
||||
private final Variant<T> selected;
|
||||
|
||||
public NativeCodeLoader(List<Variant<T>> variants) {
|
||||
this.variants = ImmutableList.copyOf(variants);
|
||||
NativeCodeLoader(List<Variant<T>> variants) {
|
||||
this.selected = getVariant(variants);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return tryLoad().object;
|
||||
return selected.object;
|
||||
}
|
||||
|
||||
private Variant<T> tryLoad() {
|
||||
if (selected != null) {
|
||||
return selected;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (selected != null) {
|
||||
return selected;
|
||||
private static <T> Variant<T> getVariant(List<Variant<T>> variants) {
|
||||
for (Variant<T> variant : variants) {
|
||||
T got = variant.get();
|
||||
if (got == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Variant<T> variant : variants) {
|
||||
T got = variant.get();
|
||||
if (got == null) {
|
||||
continue;
|
||||
}
|
||||
selected = variant;
|
||||
return selected;
|
||||
}
|
||||
throw new IllegalArgumentException("Can't find any suitable variants");
|
||||
return variant;
|
||||
}
|
||||
throw new IllegalArgumentException("Can't find any suitable variants");
|
||||
}
|
||||
|
||||
public String getLoadedVariant() {
|
||||
return tryLoad().name;
|
||||
return selected.name;
|
||||
}
|
||||
|
||||
static class Variant<T> {
|
||||
private volatile boolean available;
|
||||
private Status status;
|
||||
private final Runnable setup;
|
||||
private final String name;
|
||||
private final T object;
|
||||
private volatile boolean hasBeenSetup = false;
|
||||
|
||||
Variant(BooleanSupplier available, Runnable setup, String name, T object) {
|
||||
this.available = available.getAsBoolean();
|
||||
Variant(BooleanSupplier possiblyAvailable, Runnable setup, String name, T object) {
|
||||
this.status = possiblyAvailable.getAsBoolean() ? Status.POSSIBLY_AVAILABLE : Status.NOT_AVAILABLE;
|
||||
this.setup = setup;
|
||||
this.name = name;
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
if (!available) {
|
||||
public @Nullable T get() {
|
||||
if (status == Status.NOT_AVAILABLE || status == Status.SETUP_FAILURE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Make sure setup happens only once
|
||||
if (!hasBeenSetup) {
|
||||
synchronized (this) {
|
||||
// We change availability if need be below, may as well check it again here for sanity.
|
||||
if (!available) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Okay, now try the setup if we haven't done so yet.
|
||||
if (!hasBeenSetup) {
|
||||
try {
|
||||
setup.run();
|
||||
hasBeenSetup = true;
|
||||
return object;
|
||||
} catch (Exception e) {
|
||||
available = false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (status == Status.POSSIBLY_AVAILABLE) {
|
||||
try {
|
||||
setup.run();
|
||||
status = Status.SETUP;
|
||||
} catch (Exception e) {
|
||||
status = Status.SETUP_FAILURE;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,9 +66,16 @@ public class NativeCodeLoader<T> implements Supplier<T> {
|
||||
}
|
||||
}
|
||||
|
||||
static final BooleanSupplier MACOS = () -> System.getProperty("os.name").equalsIgnoreCase("Mac OS X") &&
|
||||
private enum Status {
|
||||
NOT_AVAILABLE,
|
||||
POSSIBLY_AVAILABLE,
|
||||
SETUP,
|
||||
SETUP_FAILURE
|
||||
}
|
||||
|
||||
static final BooleanSupplier MACOS = () -> System.getProperty("os.name", "").equalsIgnoreCase("Mac OS X") &&
|
||||
System.getProperty("os.arch").equals("x86_64");
|
||||
static final BooleanSupplier LINUX = () -> System.getProperties().getProperty("os.name").equalsIgnoreCase("Linux") &&
|
||||
static final BooleanSupplier LINUX = () -> System.getProperties().getProperty("os.name", "").equalsIgnoreCase("Linux") &&
|
||||
System.getProperty("os.arch").equals("amd64");
|
||||
static final BooleanSupplier ALWAYS = () -> true;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import com.velocitypowered.natives.encryption.JavaVelocityCipher;
|
||||
import com.velocitypowered.natives.encryption.VelocityCipherFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
@ -21,7 +22,12 @@ public class Natives {
|
||||
return () -> {
|
||||
try {
|
||||
Path tempFile = Files.createTempFile("native-", path.substring(path.lastIndexOf('.')));
|
||||
Files.copy(Natives.class.getResourceAsStream(path), tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
InputStream nativeLib = Natives.class.getResourceAsStream(path);
|
||||
if (nativeLib == null) {
|
||||
throw new IllegalStateException("Native library " + path + " not found.");
|
||||
}
|
||||
|
||||
Files.copy(nativeLib, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
Files.deleteIfExists(tempFile);
|
||||
|
@ -4,6 +4,8 @@ plugins {
|
||||
id 'de.sebastianboegl.shadow.transformer.log4j' version '2.1.1'
|
||||
}
|
||||
|
||||
apply from: '../gradle/checkerframework.gradle'
|
||||
|
||||
compileJava {
|
||||
options.compilerArgs += ['-proc:none']
|
||||
}
|
||||
|
@ -8,8 +8,6 @@ import java.text.DecimalFormat;
|
||||
public class Velocity {
|
||||
private static final Logger logger = LogManager.getLogger(Velocity.class);
|
||||
|
||||
private static long startTime;
|
||||
|
||||
static {
|
||||
// We use BufferedImage for favicons, and on macOS this puts the Java application in the dock. How inconvenient.
|
||||
// Force AWT to work with its head chopped off.
|
||||
@ -17,8 +15,7 @@ public class Velocity {
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
startTime = System.currentTimeMillis();
|
||||
logger.info("Booting up Velocity {}...", Velocity.class.getPackage().getImplementationVersion());
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
VelocityServer server = new VelocityServer();
|
||||
server.start();
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.velocitypowered.proxy;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.Gson;
|
||||
@ -7,12 +8,14 @@ import com.google.gson.GsonBuilder;
|
||||
import com.velocitypowered.api.event.EventManager;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.plugin.PluginContainer;
|
||||
import com.velocitypowered.api.plugin.PluginManager;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
import com.velocitypowered.api.util.Favicon;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
import com.velocitypowered.proxy.command.ServerCommand;
|
||||
import com.velocitypowered.proxy.command.ShutdownCommand;
|
||||
import com.velocitypowered.proxy.command.VelocityCommand;
|
||||
@ -39,6 +42,7 @@ import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.serializer.GsonComponentSerializer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.*;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Files;
|
||||
@ -58,36 +62,54 @@ public class VelocityServer implements ProxyServer {
|
||||
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
|
||||
.create();
|
||||
|
||||
private final ConnectionManager cm = new ConnectionManager(this);
|
||||
private VelocityConfiguration configuration;
|
||||
private NettyHttpClient httpClient;
|
||||
private KeyPair serverKeyPair;
|
||||
private final ServerMap servers = new ServerMap(this);
|
||||
private @MonotonicNonNull ConnectionManager cm;
|
||||
private @MonotonicNonNull VelocityConfiguration configuration;
|
||||
private @MonotonicNonNull NettyHttpClient httpClient;
|
||||
private @MonotonicNonNull KeyPair serverKeyPair;
|
||||
private @MonotonicNonNull ServerMap servers;
|
||||
private final VelocityCommandManager commandManager = new VelocityCommandManager();
|
||||
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
||||
private boolean shutdown = false;
|
||||
private final VelocityPluginManager pluginManager = new VelocityPluginManager(this);
|
||||
private @MonotonicNonNull VelocityPluginManager pluginManager;
|
||||
|
||||
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
|
||||
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
|
||||
private final VelocityConsole console = new VelocityConsole(this);
|
||||
private Ratelimiter ipAttemptLimiter;
|
||||
private VelocityEventManager eventManager;
|
||||
private VelocityScheduler scheduler;
|
||||
private VelocityChannelRegistrar channelRegistrar;
|
||||
|
||||
VelocityServer() {
|
||||
commandManager.register(new VelocityCommand(), "velocity");
|
||||
commandManager.register(new ServerCommand(this), "server");
|
||||
commandManager.register(new ShutdownCommand(this), "shutdown", "end");
|
||||
}
|
||||
private @MonotonicNonNull VelocityConsole console;
|
||||
private @MonotonicNonNull Ratelimiter ipAttemptLimiter;
|
||||
private @MonotonicNonNull VelocityEventManager eventManager;
|
||||
private @MonotonicNonNull VelocityScheduler scheduler;
|
||||
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
|
||||
|
||||
public KeyPair getServerKeyPair() {
|
||||
if (serverKeyPair == null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return serverKeyPair;
|
||||
}
|
||||
|
||||
public VelocityConfiguration getConfiguration() {
|
||||
return configuration;
|
||||
VelocityConfiguration cfg = this.configuration;
|
||||
if (cfg == null) {
|
||||
throw new IllegalStateException("Configuration not initialized!");
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyVersion getVersion() {
|
||||
Package pkg = VelocityServer.class.getPackage();
|
||||
String implName, implVersion, implVendor;
|
||||
if (pkg != null) {
|
||||
implName = MoreObjects.firstNonNull(pkg.getImplementationTitle(), "Velocity");
|
||||
implVersion = MoreObjects.firstNonNull(pkg.getImplementationVersion(), "<unknown>");
|
||||
implVendor = MoreObjects.firstNonNull(pkg.getImplementationVendor(), "Velocity Contributors");
|
||||
} else {
|
||||
implName = "Velocity";
|
||||
implVersion = "<unknown>";
|
||||
implVendor = "Velocity Contributors";
|
||||
}
|
||||
|
||||
return new ProxyVersion(implName, implVendor, implVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -95,7 +117,25 @@ public class VelocityServer implements ProxyServer {
|
||||
return commandManager;
|
||||
}
|
||||
|
||||
@EnsuresNonNull({"serverKeyPair", "servers", "pluginManager", "eventManager", "scheduler", "console", "cm", "configuration"})
|
||||
public void start() {
|
||||
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
|
||||
|
||||
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
|
||||
pluginManager = new VelocityPluginManager(this);
|
||||
eventManager = new VelocityEventManager(pluginManager);
|
||||
scheduler = new VelocityScheduler(pluginManager);
|
||||
console = new VelocityConsole(this);
|
||||
cm = new ConnectionManager(this);
|
||||
servers = new ServerMap(this);
|
||||
|
||||
cm.logChannelInformation();
|
||||
|
||||
// Initialize commands first
|
||||
commandManager.register(new VelocityCommand(this), "velocity");
|
||||
commandManager.register(new ServerCommand(this), "server");
|
||||
commandManager.register(new ShutdownCommand(this), "shutdown", "end");
|
||||
|
||||
try {
|
||||
Path configPath = Paths.get("velocity.toml");
|
||||
configuration = VelocityConfiguration.read(configPath);
|
||||
@ -118,22 +158,13 @@ public class VelocityServer implements ProxyServer {
|
||||
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
|
||||
}
|
||||
|
||||
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
|
||||
ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit());
|
||||
httpClient = new NettyHttpClient(this);
|
||||
eventManager = new VelocityEventManager(pluginManager);
|
||||
scheduler = new VelocityScheduler(pluginManager);
|
||||
channelRegistrar = new VelocityChannelRegistrar();
|
||||
loadPlugins();
|
||||
|
||||
try {
|
||||
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
|
||||
// to fully initialize before we accept any connections to the server.
|
||||
eventManager.fire(new ProxyInitializeEvent()).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
// Ignore, we don't care. InterruptedException is unlikely to happen (and if it does, you've got bigger
|
||||
// issues) and there is almost no chance ExecutionException will be thrown.
|
||||
}
|
||||
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
|
||||
// to fully initialize before we accept any connections to the server.
|
||||
eventManager.fire(new ProxyInitializeEvent()).join();
|
||||
|
||||
// init console permissions after plugins are loaded
|
||||
console.setupPermissions();
|
||||
@ -145,6 +176,7 @@ public class VelocityServer implements ProxyServer {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresNonNull({"pluginManager", "eventManager"})
|
||||
private void loadPlugins() {
|
||||
logger.info("Loading plugins...");
|
||||
|
||||
@ -166,18 +198,20 @@ public class VelocityServer implements ProxyServer {
|
||||
}
|
||||
|
||||
// Register the plugin main classes so that we may proceed with firing the proxy initialize event
|
||||
pluginManager.getPlugins().forEach(container -> {
|
||||
container.getInstance().ifPresent(plugin -> eventManager.register(plugin, plugin));
|
||||
});
|
||||
for (PluginContainer plugin : pluginManager.getPlugins()) {
|
||||
Optional<?> instance = plugin.getInstance();
|
||||
if (instance.isPresent()) {
|
||||
eventManager.register(instance.get(), instance.get());
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
|
||||
}
|
||||
|
||||
public ServerMap getServers() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
public Bootstrap initializeGenericBootstrap() {
|
||||
if (cm == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return this.cm.createWorker();
|
||||
}
|
||||
|
||||
@ -186,6 +220,10 @@ public class VelocityServer implements ProxyServer {
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
if (!shutdownInProgress.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
@ -210,10 +248,16 @@ public class VelocityServer implements ProxyServer {
|
||||
}
|
||||
|
||||
public NettyHttpClient getHttpClient() {
|
||||
if (httpClient == null) {
|
||||
throw new IllegalStateException("HTTP client not initialized");
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
public Ratelimiter getIpAttemptLimiter() {
|
||||
if (ipAttemptLimiter == null) {
|
||||
throw new IllegalStateException("Ratelimiter not initialized");
|
||||
}
|
||||
return ipAttemptLimiter;
|
||||
}
|
||||
|
||||
@ -268,41 +312,65 @@ public class VelocityServer implements ProxyServer {
|
||||
@Override
|
||||
public Optional<RegisteredServer> getServer(String name) {
|
||||
Preconditions.checkNotNull(name, "name");
|
||||
if (servers == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return servers.getServer(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RegisteredServer> getAllServers() {
|
||||
if (servers == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return servers.getAllServers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisteredServer registerServer(ServerInfo server) {
|
||||
if (servers == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return servers.register(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterServer(ServerInfo server) {
|
||||
if (servers == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
servers.unregister(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VelocityConsole getConsoleCommandSource() {
|
||||
if (console == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return console;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginManager getPluginManager() {
|
||||
if (pluginManager == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return pluginManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventManager getEventManager() {
|
||||
if (eventManager == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return eventManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VelocityScheduler getScheduler() {
|
||||
if (scheduler == null) {
|
||||
throw new IllegalStateException("Server did not initialize properly.");
|
||||
}
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@ -313,6 +381,9 @@ public class VelocityServer implements ProxyServer {
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getBoundAddress() {
|
||||
if (configuration == null) {
|
||||
throw new IllegalStateException("No configuration"); // even though you'll never get the chance... heh, heh
|
||||
}
|
||||
return configuration.getBind();
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public class ServerCommand implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSource source, String[] args) {
|
||||
public void execute(CommandSource source, String @NonNull [] args) {
|
||||
if (!(source instanceof Player)) {
|
||||
source.sendMessage(TextComponent.of("Only players may run this command.", TextColor.RED));
|
||||
return;
|
||||
@ -76,7 +76,7 @@ public class ServerCommand implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(CommandSource source, String[] currentArgs) {
|
||||
public List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
|
||||
if (currentArgs.length == 0) {
|
||||
return server.getAllServers().stream()
|
||||
.map(rs -> rs.getServerInfo().getName())
|
||||
@ -92,7 +92,7 @@ public class ServerCommand implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
||||
return source.getPermissionValue("velocity.command.server") != Tristate.FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public class ShutdownCommand implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSource source, String[] args) {
|
||||
public void execute(CommandSource source, String @NonNull [] args) {
|
||||
if (source != server.getConsoleCommandSource()) {
|
||||
source.sendMessage(TextComponent.of("You are not allowed to use this command.", TextColor.RED));
|
||||
return;
|
||||
@ -24,7 +24,7 @@ public class ShutdownCommand implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
||||
return source == server.getConsoleCommandSource();
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package com.velocitypowered.proxy.command;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.velocitypowered.api.command.Command;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.permission.Tristate;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.event.ClickEvent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
@ -20,9 +20,13 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class VelocityCommand implements Command {
|
||||
private final Map<String, Command> subcommands = ImmutableMap.<String, Command>builder()
|
||||
.put("version", Info.INSTANCE)
|
||||
.build();
|
||||
private final Map<String, Command> subcommands;
|
||||
|
||||
public VelocityCommand(ProxyServer server) {
|
||||
this.subcommands = ImmutableMap.<String, Command>builder()
|
||||
.put("version", new Info(server))
|
||||
.build();
|
||||
}
|
||||
|
||||
private void usage(CommandSource source) {
|
||||
String commandText = "/velocity <" + String.join("|", subcommands.keySet()) + ">";
|
||||
@ -30,7 +34,7 @@ public class VelocityCommand implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSource source, String[] args) {
|
||||
public void execute(CommandSource source, String @NonNull [] args) {
|
||||
if (args.length == 0) {
|
||||
usage(source);
|
||||
return;
|
||||
@ -41,11 +45,13 @@ public class VelocityCommand implements Command {
|
||||
usage(source);
|
||||
return;
|
||||
}
|
||||
command.execute(source, Arrays.copyOfRange(args, 1, args.length));
|
||||
@SuppressWarnings("nullness")
|
||||
String[] actualArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
command.execute(source, actualArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(@NonNull CommandSource source, @NonNull String[] currentArgs) {
|
||||
public List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
|
||||
if (currentArgs.length == 0) {
|
||||
return ImmutableList.copyOf(subcommands.keySet());
|
||||
}
|
||||
@ -60,11 +66,13 @@ public class VelocityCommand implements Command {
|
||||
if (command == null) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
return command.suggest(source, Arrays.copyOfRange(currentArgs, 1, currentArgs.length));
|
||||
@SuppressWarnings("nullness")
|
||||
String[] actualArgs = Arrays.copyOfRange(currentArgs, 1, currentArgs.length);
|
||||
return command.suggest(source, actualArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
||||
if (args.length == 0) {
|
||||
return true;
|
||||
}
|
||||
@ -72,29 +80,33 @@ public class VelocityCommand implements Command {
|
||||
if (command == null) {
|
||||
return true;
|
||||
}
|
||||
return command.hasPermission(source, Arrays.copyOfRange(args, 1, args.length));
|
||||
@SuppressWarnings("nullness")
|
||||
String[] actualArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
return command.hasPermission(source, actualArgs);
|
||||
}
|
||||
|
||||
private static class Info implements Command {
|
||||
static final Info INSTANCE = new Info();
|
||||
private Info() {}
|
||||
private final ProxyServer server;
|
||||
|
||||
private Info(ProxyServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
String implName = MoreObjects.firstNonNull(VelocityServer.class.getPackage().getImplementationTitle(), "Velocity");
|
||||
String implVersion = MoreObjects.firstNonNull(VelocityServer.class.getPackage().getImplementationVersion(), "<unknown>");
|
||||
String implVendor = MoreObjects.firstNonNull(VelocityServer.class.getPackage().getImplementationVendor(), "Velocity Contributors");
|
||||
TextComponent velocity = TextComponent.builder(implName + " ")
|
||||
public void execute(CommandSource source, String @NonNull [] args) {
|
||||
ProxyVersion version = server.getVersion();
|
||||
|
||||
TextComponent velocity = TextComponent.builder(version.getName() + " ")
|
||||
.decoration(TextDecoration.BOLD, true)
|
||||
.color(TextColor.DARK_AQUA)
|
||||
.append(TextComponent.of(implVersion).decoration(TextDecoration.BOLD, false))
|
||||
.append(TextComponent.of(version.getVersion()).decoration(TextDecoration.BOLD, false))
|
||||
.build();
|
||||
TextComponent copyright = TextComponent.of("Copyright 2018 " + implVendor + ". " + implName + " is freely licensed under the terms of the " +
|
||||
TextComponent copyright = TextComponent.of("Copyright 2018 " + version.getVendor() + ". " + version.getName() + " is freely licensed under the terms of the " +
|
||||
"MIT License.");
|
||||
source.sendMessage(velocity);
|
||||
source.sendMessage(copyright);
|
||||
|
||||
if (implName.equals("Velocity")) {
|
||||
if (version.getName().equals("Velocity")) {
|
||||
TextComponent velocityWebsite = TextComponent.builder()
|
||||
.content("Visit the ")
|
||||
.append(TextComponent.builder("Velocity website")
|
||||
@ -112,7 +124,7 @@ public class VelocityCommand implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
||||
return source.getPermissionValue("velocity.command.info") != Tristate.FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package com.velocitypowered.proxy.command;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.api.command.Command;
|
||||
import com.velocitypowered.api.command.CommandManager;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -11,7 +13,7 @@ public class VelocityCommandManager implements CommandManager {
|
||||
private final Map<String, Command> commands = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void register(final Command command, final String... aliases) {
|
||||
public void register(@NonNull final Command command, final String... aliases) {
|
||||
Preconditions.checkNotNull(aliases, "aliases");
|
||||
Preconditions.checkNotNull(command, "executor");
|
||||
for (int i = 0, length = aliases.length; i < length; i++) {
|
||||
@ -22,13 +24,13 @@ public class VelocityCommandManager implements CommandManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(final String alias) {
|
||||
public void unregister(@NonNull final String alias) {
|
||||
Preconditions.checkNotNull(alias, "name");
|
||||
this.commands.remove(alias.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSource source, String cmdLine) {
|
||||
public boolean execute(@NonNull CommandSource source, @NonNull String cmdLine) {
|
||||
Preconditions.checkNotNull(source, "invoker");
|
||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||
|
||||
@ -38,6 +40,7 @@ public class VelocityCommandManager implements CommandManager {
|
||||
}
|
||||
|
||||
String alias = split[0];
|
||||
@SuppressWarnings("nullness")
|
||||
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
|
||||
Command command = commands.get(alias.toLowerCase(Locale.ENGLISH));
|
||||
if (command == null) {
|
||||
@ -60,13 +63,13 @@ public class VelocityCommandManager implements CommandManager {
|
||||
return commands.containsKey(command);
|
||||
}
|
||||
|
||||
public Optional<List<String>> offerSuggestions(CommandSource source, String cmdLine) {
|
||||
public List<String> offerSuggestions(CommandSource source, String cmdLine) {
|
||||
Preconditions.checkNotNull(source, "source");
|
||||
Preconditions.checkNotNull(cmdLine, "cmdLine");
|
||||
|
||||
String[] split = cmdLine.split(" ", -1);
|
||||
if (split.length == 0) {
|
||||
return Optional.empty();
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
String alias = split[0];
|
||||
@ -78,21 +81,22 @@ public class VelocityCommandManager implements CommandManager {
|
||||
availableCommands.add("/" + entry.getKey());
|
||||
}
|
||||
}
|
||||
return Optional.of(availableCommands);
|
||||
return availableCommands;
|
||||
}
|
||||
|
||||
@SuppressWarnings("nullness")
|
||||
String[] actualArgs = Arrays.copyOfRange(split, 1, split.length);
|
||||
Command command = commands.get(alias.toLowerCase(Locale.ENGLISH));
|
||||
if (command == null) {
|
||||
return Optional.empty();
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
try {
|
||||
if (!command.hasPermission(source, actualArgs)) {
|
||||
return Optional.empty();
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
return Optional.of(command.suggest(source, actualArgs));
|
||||
return command.suggest(source, actualArgs);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to invoke suggestions for command " + alias + " for " + source, e);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public abstract class AnnotatedConfig {
|
||||
|
||||
// Get a key name for config
|
||||
ConfigKey key = field.getAnnotation(ConfigKey.class);
|
||||
String name = key == null ? field.getName() : key.value(); // Use field name if @ConfigKey annotation is not present
|
||||
String name = safeKey(key == null ? field.getName() : key.value()); // Use field name if @ConfigKey annotation is not present
|
||||
|
||||
// Check if field is table.
|
||||
Table table = field.getAnnotation(Table.class);
|
||||
@ -130,7 +130,7 @@ public abstract class AnnotatedConfig {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> map = (Map<String, ?>) field.get(dumpable);
|
||||
for (Entry<String, ?> entry : map.entrySet()) {
|
||||
lines.add(entry.getKey() + " = " + serialize(entry.getValue())); // Save map data
|
||||
lines.add(safeKey(entry.getKey()) + " = " + serialize(entry.getValue())); // Save map data
|
||||
}
|
||||
lines.add(""); // Add empty line
|
||||
continue;
|
||||
@ -193,7 +193,7 @@ public abstract class AnnotatedConfig {
|
||||
return value != null ? value.toString() : "null";
|
||||
}
|
||||
|
||||
private String safeKey(String key) {
|
||||
private static String safeKey(String key) {
|
||||
if(key.contains(".") && !(key.indexOf('"') == 0 && key.lastIndexOf('"') == (key.length() - 1))) {
|
||||
return '"' + key + '"';
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import com.velocitypowered.proxy.util.AddressUtil;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
@ -78,10 +80,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi
|
||||
private final Query query;
|
||||
|
||||
@Ignore
|
||||
private Component motdAsComponent;
|
||||
private @MonotonicNonNull Component motdAsComponent;
|
||||
|
||||
@Ignore
|
||||
private Favicon favicon;
|
||||
private @Nullable Favicon favicon;
|
||||
|
||||
public VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced, Query query) {
|
||||
this.servers = servers;
|
||||
|
@ -16,6 +16,7 @@ import io.netty.handler.codec.haproxy.HAProxyMessage;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@ -35,10 +36,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
||||
private final Channel channel;
|
||||
private SocketAddress remoteAddress;
|
||||
private StateRegistry state;
|
||||
private MinecraftSessionHandler sessionHandler;
|
||||
private @Nullable MinecraftSessionHandler sessionHandler;
|
||||
private int protocolVersion;
|
||||
private int nextProtocolVersion;
|
||||
private MinecraftConnectionAssociation association;
|
||||
private @Nullable MinecraftConnectionAssociation association;
|
||||
private boolean isLegacyForge;
|
||||
private final VelocityServer server;
|
||||
private boolean canSendLegacyFMLResetPacket = false;
|
||||
@ -74,6 +75,12 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
if (sessionHandler == null) {
|
||||
// No session handler available, do nothing
|
||||
ReferenceCountUtil.release(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg instanceof MinecraftPacket) {
|
||||
if (sessionHandler.beforeHandle()) {
|
||||
return;
|
||||
@ -197,6 +204,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MinecraftSessionHandler getSessionHandler() {
|
||||
return sessionHandler;
|
||||
}
|
||||
@ -243,6 +251,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
||||
channel.pipeline().addBefore(FRAME_ENCODER, CIPHER_ENCODER, new MinecraftCipherEncoder(encryptionCipher));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MinecraftConnectionAssociation getAssociation() {
|
||||
return association;
|
||||
}
|
||||
|
@ -7,4 +7,6 @@ public class VelocityConstants {
|
||||
|
||||
public static final String VELOCITY_IP_FORWARDING_CHANNEL = "velocity:player_info";
|
||||
public static final int FORWARDING_VERSION = 1;
|
||||
|
||||
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.event.player.ServerConnectedEvent;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
|
||||
import com.velocitypowered.proxy.connection.forge.ForgeConstants;
|
||||
@ -22,7 +23,12 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) {
|
||||
this.server = server;
|
||||
this.serverConn = serverConn;
|
||||
this.playerSessionHandler = (ClientPlaySessionHandler) serverConn.getPlayer().getConnection().getSessionHandler();
|
||||
|
||||
MinecraftSessionHandler psh = serverConn.getPlayer().getConnection().getSessionHandler();
|
||||
if (!(psh instanceof ClientPlaySessionHandler)) {
|
||||
throw new IllegalStateException("Initializing BackendPlaySessionHandler with no backing client play session handler!");
|
||||
}
|
||||
this.playerSessionHandler = (ClientPlaySessionHandler) psh;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,6 +80,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(PluginMessage packet) {
|
||||
MinecraftConnection smc = serverConn.getConnection();
|
||||
if (smc == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!canForwardPluginMessage(packet)) {
|
||||
return true;
|
||||
}
|
||||
@ -105,9 +116,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
server.getEventManager().fire(event)
|
||||
.thenAcceptAsync(pme -> {
|
||||
if (pme.getResult().isAllowed()) {
|
||||
serverConn.getPlayer().getConnection().write(packet);
|
||||
smc.write(packet);
|
||||
}
|
||||
}, serverConn.getConnection().eventLoop());
|
||||
}, smc.eventLoop());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -152,16 +163,18 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
|
||||
private boolean canForwardPluginMessage(PluginMessage message) {
|
||||
ClientPlaySessionHandler playerHandler =
|
||||
(ClientPlaySessionHandler) serverConn.getPlayer().getConnection().getSessionHandler();
|
||||
MinecraftConnection mc = serverConn.getConnection();
|
||||
if (mc == null) {
|
||||
return false;
|
||||
}
|
||||
boolean isMCOrFMLMessage;
|
||||
if (serverConn.getConnection().getProtocolVersion() <= ProtocolConstants.MINECRAFT_1_12_2) {
|
||||
if (mc.getProtocolVersion() <= ProtocolConstants.MINECRAFT_1_12_2) {
|
||||
String channel = message.getChannel();
|
||||
isMCOrFMLMessage = channel.startsWith("MC|") || channel.startsWith(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL);
|
||||
} else {
|
||||
isMCOrFMLMessage = message.getChannel().startsWith("minecraft:");
|
||||
}
|
||||
return isMCOrFMLMessage || playerHandler.getClientPluginMsgChannels().contains(message.getChannel()) ||
|
||||
return isMCOrFMLMessage || playerSessionHandler.getClientPluginMsgChannels().contains(message.getChannel()) ||
|
||||
server.getChannelRegistrar().registered(message.getChannel());
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import com.velocitypowered.api.util.GameProfile;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.VelocityConstants;
|
||||
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
|
||||
@ -38,6 +39,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
this.resultFuture = resultFuture;
|
||||
}
|
||||
|
||||
private MinecraftConnection ensureMinecraftConnection() {
|
||||
MinecraftConnection mc = serverConn.getConnection();
|
||||
if (mc == null) {
|
||||
throw new IllegalStateException("Not connected to backend server!");
|
||||
}
|
||||
return mc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(EncryptionRequest packet) {
|
||||
throw new IllegalStateException("Backend server is online-mode!");
|
||||
@ -45,6 +54,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(LoginPluginMessage packet) {
|
||||
MinecraftConnection mc = ensureMinecraftConnection();
|
||||
VelocityConfiguration configuration = server.getConfiguration();
|
||||
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && packet.getChannel()
|
||||
.equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) {
|
||||
@ -54,7 +64,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
response.setData(createForwardingData(configuration.getForwardingSecret(),
|
||||
serverConn.getPlayer().getRemoteAddress().getHostString(),
|
||||
serverConn.getPlayer().getProfile()));
|
||||
serverConn.getConnection().write(response);
|
||||
mc.write(response);
|
||||
informationForwarded = true;
|
||||
} else {
|
||||
// Don't understand
|
||||
@ -62,7 +72,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
response.setSuccess(false);
|
||||
response.setId(packet.getId());
|
||||
response.setData(Unpooled.EMPTY_BUFFER);
|
||||
serverConn.getConnection().write(response);
|
||||
mc.write(response);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -76,7 +86,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(SetCompression packet) {
|
||||
serverConn.getConnection().setCompressionThreshold(packet.getThreshold());
|
||||
ensureMinecraftConnection().setCompressionThreshold(packet.getThreshold());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -90,7 +100,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
|
||||
// The player has been logged on to the backend server.
|
||||
serverConn.getConnection().setState(StateRegistry.PLAY);
|
||||
MinecraftConnection smc = ensureMinecraftConnection();
|
||||
smc.setState(StateRegistry.PLAY);
|
||||
VelocityServerConnection existingConnection = serverConn.getPlayer().getConnectedServer();
|
||||
if (existingConnection == null) {
|
||||
// Strap on the play session handler
|
||||
@ -104,14 +115,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
existingConnection.disconnect();
|
||||
}
|
||||
|
||||
serverConn.getConnection().getChannel().config().setAutoRead(false);
|
||||
smc.getChannel().config().setAutoRead(false);
|
||||
server.getEventManager().fire(new ServerConnectedEvent(serverConn.getPlayer(), serverConn.getServer()))
|
||||
.whenCompleteAsync((x, error) -> {
|
||||
resultFuture.complete(ConnectionRequestResults.SUCCESSFUL);
|
||||
serverConn.getConnection().setSessionHandler(new BackendPlaySessionHandler(server, serverConn));
|
||||
smc.setSessionHandler(new BackendPlaySessionHandler(server, serverConn));
|
||||
serverConn.getPlayer().setConnectedServer(serverConn);
|
||||
serverConn.getConnection().getChannel().config().setAutoRead(true);
|
||||
}, serverConn.getConnection().eventLoop());
|
||||
smc.getChannel().config().setAutoRead(true);
|
||||
}, smc.eventLoop());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.velocitypowered.proxy.connection.backend;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Verify;
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
@ -24,10 +26,12 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static com.velocitypowered.proxy.VelocityServer.GSON;
|
||||
import static com.velocitypowered.proxy.network.Connections.*;
|
||||
|
||||
@ -35,7 +39,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
|
||||
private final VelocityRegisteredServer registeredServer;
|
||||
private final ConnectedPlayer proxyPlayer;
|
||||
private final VelocityServer server;
|
||||
private MinecraftConnection connection;
|
||||
private @Nullable MinecraftConnection connection;
|
||||
private boolean legacyForge = false;
|
||||
private boolean hasCompletedJoin = false;
|
||||
private boolean gracefulDisconnect = false;
|
||||
@ -72,6 +76,11 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
|
||||
if (future.isSuccess()) {
|
||||
connection = future.channel().pipeline().get(MinecraftConnection.class);
|
||||
|
||||
// This is guaranteed not to be null, but Checker Framework is whining about it anyway
|
||||
if (connection == null) {
|
||||
throw new VerifyException("MinecraftConnection not injected into pipeline");
|
||||
}
|
||||
|
||||
// Kick off the connection process
|
||||
connection.setSessionHandler(new LoginSessionHandler(server, VelocityServerConnection.this, result));
|
||||
startHandshake();
|
||||
@ -93,6 +102,11 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
|
||||
}
|
||||
|
||||
private void startHandshake() {
|
||||
MinecraftConnection mc = connection;
|
||||
if (mc == null) {
|
||||
throw new IllegalStateException("No connection established!");
|
||||
}
|
||||
|
||||
PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode();
|
||||
|
||||
// Initiate a handshake.
|
||||
@ -107,17 +121,15 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
|
||||
handshake.setServerAddress(registeredServer.getServerInfo().getAddress().getHostString());
|
||||
}
|
||||
handshake.setPort(registeredServer.getServerInfo().getAddress().getPort());
|
||||
connection.write(handshake);
|
||||
mc.write(handshake);
|
||||
|
||||
int protocolVersion = proxyPlayer.getConnection().getNextProtocolVersion();
|
||||
connection.setProtocolVersion(protocolVersion);
|
||||
connection.setState(StateRegistry.LOGIN);
|
||||
|
||||
ServerLogin login = new ServerLogin();
|
||||
login.setUsername(proxyPlayer.getUsername());
|
||||
connection.write(login);
|
||||
mc.setProtocolVersion(protocolVersion);
|
||||
mc.setState(StateRegistry.LOGIN);
|
||||
mc.write(new ServerLogin(proxyPlayer.getUsername()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MinecraftConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
@ -154,10 +166,16 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
|
||||
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
|
||||
Preconditions.checkNotNull(identifier, "identifier");
|
||||
Preconditions.checkNotNull(data, "data");
|
||||
|
||||
MinecraftConnection mc = connection;
|
||||
if (mc == null) {
|
||||
throw new IllegalStateException("Not connected to a server!");
|
||||
}
|
||||
|
||||
PluginMessage message = new PluginMessage();
|
||||
message.setChannel(identifier.getId());
|
||||
message.setData(data);
|
||||
connection.write(message);
|
||||
mc.write(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package com.velocitypowered.proxy.connection.client;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.event.player.PlayerChatEvent;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.util.ModInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||
import com.velocitypowered.proxy.connection.forge.ForgeConstants;
|
||||
@ -19,6 +21,7 @@ import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@ -36,7 +39,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
private final Set<String> clientPluginMsgChannels = new HashSet<>();
|
||||
private final Queue<PluginMessage> loginPluginMessages = new ArrayDeque<>();
|
||||
private final VelocityServer server;
|
||||
private TabCompleteRequest outstandingTabComplete;
|
||||
private @Nullable TabCompleteRequest outstandingTabComplete;
|
||||
|
||||
public ClientPlaySessionHandler(VelocityServer server, ConnectedPlayer player) {
|
||||
this.player = player;
|
||||
@ -53,8 +56,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
public boolean handle(KeepAlive packet) {
|
||||
VelocityServerConnection serverConnection = player.getConnectedServer();
|
||||
if (serverConnection != null && packet.getRandomId() == serverConnection.getLastPingId()) {
|
||||
MinecraftConnection smc = serverConnection.getConnection();
|
||||
if (smc == null) {
|
||||
// eat the packet
|
||||
return true;
|
||||
}
|
||||
player.setPing(System.currentTimeMillis() - serverConnection.getLastPingSent());
|
||||
serverConnection.getConnection().write(packet);
|
||||
smc.write(packet);
|
||||
serverConnection.resetLastPingId();
|
||||
}
|
||||
return true;
|
||||
@ -84,15 +92,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
if (serverConnection == null) {
|
||||
return true;
|
||||
}
|
||||
MinecraftConnection smc = serverConnection.getConnection();
|
||||
if (smc == null) {
|
||||
return true;
|
||||
}
|
||||
PlayerChatEvent event = new PlayerChatEvent(player, msg);
|
||||
server.getEventManager().fire(event)
|
||||
.thenAcceptAsync(pme -> {
|
||||
if (pme.getResult().equals(PlayerChatEvent.ChatResult.allowed())){
|
||||
serverConnection.getConnection().write(packet);
|
||||
} else if (pme.getResult().isAllowed() && pme.getResult().getMessage().isPresent()){
|
||||
serverConnection.getConnection().write(Chat.createServerbound(pme.getResult().getMessage().get()));
|
||||
PlayerChatEvent.ChatResult chatResult = pme.getResult();
|
||||
if (chatResult.isAllowed()) {
|
||||
Optional<String> eventMsg = pme.getResult().getMessage();
|
||||
if (eventMsg.isPresent()) {
|
||||
smc.write(Chat.createServerbound(eventMsg.get()));
|
||||
} else {
|
||||
smc.write(packet);
|
||||
}
|
||||
}
|
||||
}, serverConnection.getConnection().eventLoop());
|
||||
}, smc.eventLoop());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -105,10 +121,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
if (spacePos > 0) {
|
||||
String cmd = packet.getCommand().substring(1, spacePos);
|
||||
if (server.getCommandManager().hasCommand(cmd)) {
|
||||
Optional<List<String>> suggestions = server.getCommandManager().offerSuggestions(player, packet.getCommand().substring(1));
|
||||
if (suggestions.isPresent()) {
|
||||
List<String> suggestions = server.getCommandManager().offerSuggestions(player, packet.getCommand().substring(1));
|
||||
if (suggestions.size() > 0) {
|
||||
TabCompleteResponse resp = new TabCompleteResponse();
|
||||
resp.getOffers().addAll(suggestions.get());
|
||||
resp.getOffers().addAll(suggestions);
|
||||
player.getConnection().write(resp);
|
||||
return true;
|
||||
}
|
||||
@ -121,58 +137,68 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(PluginMessage packet) {
|
||||
if (PluginMessageUtil.isMCRegister(packet)) {
|
||||
List<String> actuallyRegistered = new ArrayList<>();
|
||||
List<String> channels = PluginMessageUtil.getChannels(packet);
|
||||
for (String channel : channels) {
|
||||
if (clientPluginMsgChannels.size() >= MAX_PLUGIN_CHANNELS &&
|
||||
!clientPluginMsgChannels.contains(channel)) {
|
||||
throw new IllegalStateException("Too many plugin message channels registered");
|
||||
VelocityServerConnection serverConn = player.getConnectedServer();
|
||||
MinecraftConnection backendConn = serverConn != null ? serverConn.getConnection() : null;
|
||||
if (serverConn != null && backendConn != null) {
|
||||
if (PluginMessageUtil.isMCRegister(packet)) {
|
||||
List<String> actuallyRegistered = new ArrayList<>();
|
||||
List<String> channels = PluginMessageUtil.getChannels(packet);
|
||||
for (String channel : channels) {
|
||||
if (clientPluginMsgChannels.size() >= MAX_PLUGIN_CHANNELS &&
|
||||
!clientPluginMsgChannels.contains(channel)) {
|
||||
throw new IllegalStateException("Too many plugin message channels registered");
|
||||
}
|
||||
if (clientPluginMsgChannels.add(channel)) {
|
||||
actuallyRegistered.add(channel);
|
||||
}
|
||||
}
|
||||
if (clientPluginMsgChannels.add(channel)) {
|
||||
actuallyRegistered.add(channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (actuallyRegistered.size() > 0) {
|
||||
PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(player.getConnectedServer()
|
||||
.getConnection().getProtocolVersion(), actuallyRegistered);
|
||||
player.getConnectedServer().getConnection().write(newRegisterPacket);
|
||||
}
|
||||
} else if (PluginMessageUtil.isMCUnregister(packet)) {
|
||||
List<String> channels = PluginMessageUtil.getChannels(packet);
|
||||
clientPluginMsgChannels.removeAll(channels);
|
||||
player.getConnectedServer().getConnection().write(packet);
|
||||
} else if (PluginMessageUtil.isMCBrand(packet)) {
|
||||
player.getConnectedServer().getConnection().write(PluginMessageUtil.rewriteMCBrand(packet));
|
||||
} else if (player.getConnectedServer().isLegacyForge() && !player.getConnectedServer().hasCompletedJoin()) {
|
||||
if (packet.getChannel().equals(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) {
|
||||
if (!player.getModInfo().isPresent()) {
|
||||
ForgeUtil.readModList(packet).ifPresent(mods -> player.setModInfo(new ModInfo("FML", mods)));
|
||||
if (actuallyRegistered.size() > 0) {
|
||||
PluginMessage newRegisterPacket = PluginMessageUtil.constructChannelsPacket(backendConn
|
||||
.getProtocolVersion(), actuallyRegistered);
|
||||
backendConn.write(newRegisterPacket);
|
||||
}
|
||||
|
||||
// Always forward the FML handshake to the remote server.
|
||||
player.getConnectedServer().getConnection().write(packet);
|
||||
return true;
|
||||
} else if (PluginMessageUtil.isMCUnregister(packet)) {
|
||||
List<String> channels = PluginMessageUtil.getChannels(packet);
|
||||
clientPluginMsgChannels.removeAll(channels);
|
||||
backendConn.write(packet);
|
||||
return true;
|
||||
} else if (PluginMessageUtil.isMCBrand(packet)) {
|
||||
backendConn.write(PluginMessageUtil.rewriteMCBrand(packet));
|
||||
return true;
|
||||
} else if (backendConn.isLegacyForge() && !serverConn.hasCompletedJoin()) {
|
||||
if (packet.getChannel().equals(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL)) {
|
||||
if (!player.getModInfo().isPresent()) {
|
||||
List<ModInfo.Mod> mods = ForgeUtil.readModList(packet);
|
||||
if (!mods.isEmpty()) {
|
||||
player.setModInfo(new ModInfo("FML", mods));
|
||||
}
|
||||
}
|
||||
|
||||
// Always forward the FML handshake to the remote server.
|
||||
backendConn.write(packet);
|
||||
} else {
|
||||
// The client is trying to send messages too early. This is primarily caused by mods, but it's further
|
||||
// aggravated by Velocity. To work around these issues, we will queue any non-FML handshake messages to
|
||||
// be sent once the JoinGame packet has been received by the proxy.
|
||||
loginPluginMessages.add(packet);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// The client is trying to send messages too early. This is primarily caused by mods, but it's further
|
||||
// aggravated by Velocity. To work around these issues, we will queue any non-FML handshake messages to
|
||||
// be sent once the JoinGame packet has been received by the proxy.
|
||||
loginPluginMessages.add(packet);
|
||||
}
|
||||
} else {
|
||||
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||
if (id == null) {
|
||||
player.getConnectedServer().getConnection().write(packet);
|
||||
} else {
|
||||
PluginMessageEvent event = new PluginMessageEvent(player, player.getConnectedServer(), id, packet.getData());
|
||||
server.getEventManager().fire(event)
|
||||
.thenAcceptAsync(pme -> {
|
||||
if (pme.getResult().isAllowed()) {
|
||||
player.getConnectedServer().getConnection().write(packet);
|
||||
}
|
||||
}, player.getConnectedServer().getConnection().eventLoop());
|
||||
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||
if (id == null) {
|
||||
backendConn.write(packet);
|
||||
} else {
|
||||
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, packet.getData());
|
||||
server.getEventManager().fire(event)
|
||||
.thenAcceptAsync(pme -> {
|
||||
backendConn.write(packet);
|
||||
}, backendConn.eventLoop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -184,9 +210,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't want to handle this packet, just forward it on.
|
||||
if (serverConnection.hasCompletedJoin()) {
|
||||
serverConnection.getConnection().write(packet);
|
||||
MinecraftConnection smc = serverConnection.getConnection();
|
||||
if (smc != null && serverConnection.hasCompletedJoin()) {
|
||||
smc.write(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,8 +224,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (serverConnection.hasCompletedJoin()) {
|
||||
serverConnection.getConnection().write(buf.retain());
|
||||
MinecraftConnection smc = serverConnection.getConnection();
|
||||
if (smc != null && serverConnection.hasCompletedJoin()) {
|
||||
smc.write(buf.retain());
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,11 +249,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
VelocityServerConnection server = player.getConnectedServer();
|
||||
if (server != null) {
|
||||
boolean writable = player.getConnection().getChannel().isWritable();
|
||||
server.getConnection().getChannel().config().setAutoRead(writable);
|
||||
MinecraftConnection smc = server.getConnection();
|
||||
if (smc != null) {
|
||||
smc.getChannel().config().setAutoRead(writable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleBackendJoinGame(JoinGame joinGame) {
|
||||
VelocityServerConnection serverConn = player.getConnectedServer();
|
||||
if (serverConn == null) {
|
||||
throw new IllegalStateException("No server connection for " + player + ", but JoinGame packet received");
|
||||
}
|
||||
MinecraftConnection serverMc = serverConn.getConnection();
|
||||
if (serverMc == null) {
|
||||
throw new IllegalStateException("Server connection for " + player + " is disconnected, but JoinGame packet received");
|
||||
}
|
||||
|
||||
if (!spawned) {
|
||||
// Nothing special to do with regards to spawning the player
|
||||
spawned = true;
|
||||
@ -272,7 +311,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
serverBossBars.clear();
|
||||
|
||||
// Tell the server about this client's plugin message channels.
|
||||
int serverVersion = player.getConnectedServer().getConnection().getProtocolVersion();
|
||||
int serverVersion = serverMc.getProtocolVersion();
|
||||
Collection<String> toRegister = new HashSet<>(clientPluginMsgChannels);
|
||||
if (serverVersion >= ProtocolConstants.MINECRAFT_1_13) {
|
||||
toRegister.addAll(server.getChannelRegistrar().getModernChannelIds());
|
||||
@ -280,14 +319,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
toRegister.addAll(server.getChannelRegistrar().getIdsForLegacyConnections());
|
||||
}
|
||||
if (!toRegister.isEmpty()) {
|
||||
player.getConnectedServer().getConnection().delayedWrite(PluginMessageUtil.constructChannelsPacket(
|
||||
serverVersion, toRegister));
|
||||
serverMc.delayedWrite(PluginMessageUtil.constructChannelsPacket(serverVersion, toRegister));
|
||||
}
|
||||
|
||||
// If we had plugin messages queued during login/FML handshake, send them now.
|
||||
PluginMessage pm;
|
||||
while ((pm = loginPluginMessages.poll()) != null) {
|
||||
player.getConnectedServer().getConnection().delayedWrite(pm);
|
||||
serverMc.delayedWrite(pm);
|
||||
}
|
||||
|
||||
// Clear any title from the previous server.
|
||||
@ -295,9 +333,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
// Flush everything
|
||||
player.getConnection().flush();
|
||||
player.getConnectedServer().getConnection().flush();
|
||||
player.getConnectedServer().setHasCompletedJoin(true);
|
||||
if (player.getConnectedServer().isLegacyForge()) {
|
||||
serverMc.flush();
|
||||
serverConn.setHasCompletedJoin(true);
|
||||
if (serverConn.isLegacyForge()) {
|
||||
// We only need to indicate we can send a reset packet if we complete a handshake, that is,
|
||||
// logged onto a Forge server.
|
||||
//
|
||||
@ -326,8 +364,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
if (!outstandingTabComplete.isAssumeCommand()) {
|
||||
String command = outstandingTabComplete.getCommand().substring(1);
|
||||
try {
|
||||
Optional<List<String>> offers = server.getCommandManager().offerSuggestions(player, command);
|
||||
offers.ifPresent(strings -> response.getOffers().addAll(strings));
|
||||
response.getOffers().addAll(server.getCommandManager().offerSuggestions(player, command));
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to provide tab list completions for {} for command '{}'", player.getUsername(),
|
||||
command, e);
|
||||
|
@ -3,6 +3,7 @@ package com.velocitypowered.proxy.connection.client;
|
||||
import com.velocitypowered.api.proxy.player.PlayerSettings;
|
||||
import com.velocitypowered.api.proxy.player.SkinParts;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@ -11,7 +12,7 @@ public class ClientSettingsWrapper implements PlayerSettings {
|
||||
|
||||
private final ClientSettings settings;
|
||||
private final SkinParts parts;
|
||||
private Locale locale = null;
|
||||
private @Nullable Locale locale;
|
||||
|
||||
ClientSettingsWrapper(ClientSettings settings) {
|
||||
this.settings = settings;
|
||||
|
@ -63,27 +63,28 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class);
|
||||
|
||||
private final MinecraftConnection connection;
|
||||
private final InetSocketAddress virtualHost;
|
||||
private @Nullable final InetSocketAddress virtualHost;
|
||||
private GameProfile profile;
|
||||
private PermissionFunction permissionFunction = null;
|
||||
private PermissionFunction permissionFunction;
|
||||
private int tryIndex = 0;
|
||||
private long ping = -1;
|
||||
private VelocityServerConnection connectedServer;
|
||||
private VelocityServerConnection connectionInFlight;
|
||||
private PlayerSettings settings;
|
||||
private ModInfo modInfo;
|
||||
private @Nullable VelocityServerConnection connectedServer;
|
||||
private @Nullable VelocityServerConnection connectionInFlight;
|
||||
private @Nullable PlayerSettings settings;
|
||||
private @Nullable ModInfo modInfo;
|
||||
private final VelocityTabList tabList;
|
||||
private final VelocityServer server;
|
||||
|
||||
@MonotonicNonNull
|
||||
private List<String> serversToTry = null;
|
||||
|
||||
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, InetSocketAddress virtualHost) {
|
||||
|
||||
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, @Nullable InetSocketAddress virtualHost) {
|
||||
this.server = server;
|
||||
this.tabList = new VelocityTabList(connection);
|
||||
this.profile = profile;
|
||||
this.connection = connection;
|
||||
this.virtualHost = virtualHost;
|
||||
this.permissionFunction = (permission) -> Tristate.UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,8 +124,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
}
|
||||
|
||||
void setPlayerSettings(ClientSettings settings) {
|
||||
this.settings = new ClientSettingsWrapper(settings);
|
||||
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, this.settings));
|
||||
ClientSettingsWrapper cs = new ClientSettingsWrapper(settings);
|
||||
this.settings = cs;
|
||||
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, cs));
|
||||
}
|
||||
|
||||
public Optional<ModInfo> getModInfo() {
|
||||
@ -133,7 +135,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
|
||||
void setModInfo(ModInfo modInfo) {
|
||||
this.modInfo = modInfo;
|
||||
server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, this.modInfo));
|
||||
server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, modInfo));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -161,7 +163,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NonNull Component component, @NonNull MessagePosition position) {
|
||||
public void sendMessage(Component component, MessagePosition position) {
|
||||
Preconditions.checkNotNull(component, "component");
|
||||
Preconditions.checkNotNull(position, "position");
|
||||
|
||||
@ -193,7 +195,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionRequestBuilder createConnectionRequest(@NonNull RegisteredServer server) {
|
||||
public ConnectionRequestBuilder createConnectionRequest(RegisteredServer server) {
|
||||
return new ConnectionRequestBuilderImpl(server);
|
||||
}
|
||||
|
||||
@ -243,16 +245,19 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
connection.delayedWrite(TitlePacket.resetForProtocolVersion(connection.getProtocolVersion()));
|
||||
}
|
||||
|
||||
if (tt.getTitle().isPresent()) {
|
||||
Optional<Component> titleText = tt.getTitle();
|
||||
if (titleText.isPresent()) {
|
||||
TitlePacket titlePkt = new TitlePacket();
|
||||
titlePkt.setAction(TitlePacket.SET_TITLE);
|
||||
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getTitle().get()));
|
||||
titlePkt.setComponent(ComponentSerializers.JSON.serialize(titleText.get()));
|
||||
connection.delayedWrite(titlePkt);
|
||||
}
|
||||
if (tt.getSubtitle().isPresent()) {
|
||||
|
||||
Optional<Component> subtitleText = tt.getSubtitle();
|
||||
if (subtitleText.isPresent()) {
|
||||
TitlePacket titlePkt = new TitlePacket();
|
||||
titlePkt.setAction(TitlePacket.SET_SUBTITLE);
|
||||
titlePkt.setComponent(ComponentSerializers.JSON.serialize(tt.getSubtitle().get()));
|
||||
titlePkt.setComponent(ComponentSerializers.JSON.serialize(subtitleText.get()));
|
||||
connection.delayedWrite(titlePkt);
|
||||
}
|
||||
|
||||
@ -270,16 +275,23 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VelocityServerConnection getConnectedServer() {
|
||||
return connectedServer;
|
||||
}
|
||||
|
||||
public void handleConnectionException(RegisteredServer server, Throwable throwable) {
|
||||
Throwable wrapped = throwable;
|
||||
if (throwable instanceof CompletionException) {
|
||||
wrapped = throwable.getCause();
|
||||
if (throwable == null) {
|
||||
throw new NullPointerException("throwable");
|
||||
}
|
||||
|
||||
Throwable wrapped = throwable;
|
||||
if (throwable instanceof CompletionException) {
|
||||
Throwable cause = throwable.getCause();
|
||||
if (cause != null) {
|
||||
wrapped = cause;
|
||||
}
|
||||
}
|
||||
String error = ThrowableUtils.briefDescription(wrapped);
|
||||
String userMessage;
|
||||
if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) {
|
||||
@ -366,7 +378,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
|
||||
String toTryName = serversToTry.get(tryIndex);
|
||||
tryIndex++;
|
||||
return server.getServers().getServer(toTryName);
|
||||
return server.getServer(toTryName);
|
||||
}
|
||||
|
||||
private Optional<ConnectionRequestBuilder.Status> checkServer(RegisteredServer server) {
|
||||
@ -390,13 +402,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
ServerPreConnectEvent event = new ServerPreConnectEvent(this, request.getServer());
|
||||
return server.getEventManager().fire(event)
|
||||
.thenCompose((newEvent) -> {
|
||||
if (!newEvent.getResult().isAllowed()) {
|
||||
Optional<RegisteredServer> connectTo = newEvent.getResult().getServer();
|
||||
if (!connectTo.isPresent()) {
|
||||
return CompletableFuture.completedFuture(
|
||||
ConnectionRequestResults.plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED)
|
||||
);
|
||||
}
|
||||
|
||||
RegisteredServer rs = newEvent.getResult().getServer().get();
|
||||
RegisteredServer rs = connectTo.get();
|
||||
Optional<ConnectionRequestBuilder.Status> lastCheck = checkServer(rs);
|
||||
if (lastCheck.isPresent()) {
|
||||
return CompletableFuture.completedFuture(ConnectionRequestResults.plainResult(lastCheck.get()));
|
||||
@ -406,9 +419,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
}
|
||||
|
||||
public void setConnectedServer(VelocityServerConnection serverConnection) {
|
||||
if (this.connectedServer != null && !serverConnection.getServerInfo().equals(connectedServer.getServerInfo())) {
|
||||
VelocityServerConnection oldConnection = this.connectedServer;
|
||||
if (oldConnection != null && !serverConnection.getServerInfo().equals(oldConnection.getServerInfo())) {
|
||||
this.tryIndex = 0;
|
||||
}
|
||||
if (serverConnection == connectionInFlight) {
|
||||
connectionInFlight = null;
|
||||
}
|
||||
this.connectedServer = serverConnection;
|
||||
}
|
||||
|
||||
@ -426,6 +443,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
connection.closeWith(Disconnect.create(reason));
|
||||
}
|
||||
|
||||
private MinecraftConnection ensureBackendConnection() {
|
||||
VelocityServerConnection sc = this.connectedServer;
|
||||
if (sc == null) {
|
||||
throw new IllegalStateException("No backend connection");
|
||||
}
|
||||
|
||||
MinecraftConnection mc = sc.getConnection();
|
||||
if (mc == null) {
|
||||
throw new IllegalStateException("Backend connection is not connected to a server");
|
||||
}
|
||||
|
||||
return mc;
|
||||
}
|
||||
|
||||
void teardown() {
|
||||
if (connectionInFlight != null) {
|
||||
connectionInFlight.disconnect();
|
||||
@ -439,11 +470,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[connected player] " + getProfile().getName() + " (" + getRemoteAddress() + ")";
|
||||
return "[connected player] " + profile.getName() + " (" + getRemoteAddress() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Tristate getPermissionValue(@NonNull String permission) {
|
||||
public Tristate getPermissionValue(String permission) {
|
||||
return permissionFunction.getPermissionValue(permission);
|
||||
}
|
||||
|
||||
@ -461,7 +492,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
@Override
|
||||
public void spoofChatInput(String input) {
|
||||
Preconditions.checkArgument(input.length() <= Chat.MAX_SERVERBOUND_MESSAGE_LENGTH, "input cannot be greater than " + Chat.MAX_SERVERBOUND_MESSAGE_LENGTH + " characters in length");
|
||||
connectedServer.getConnection().write(Chat.createServerbound(input));
|
||||
ensureBackendConnection().write(Chat.createServerbound(input));
|
||||
}
|
||||
|
||||
private class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {
|
||||
|
@ -22,10 +22,12 @@ import com.velocitypowered.proxy.util.EncryptionUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.TextComponent;
|
||||
import net.kyori.text.format.TextColor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
@ -38,6 +40,8 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
|
||||
|
||||
public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class);
|
||||
@ -47,10 +51,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
private final VelocityServer server;
|
||||
private final MinecraftConnection inbound;
|
||||
private final InboundConnection apiInbound;
|
||||
private ServerLogin login;
|
||||
private byte[] verify;
|
||||
private @MonotonicNonNull ServerLogin login;
|
||||
private byte[] verify = EMPTY_BYTE_ARRAY;
|
||||
private int playerInfoId;
|
||||
private ConnectedPlayer connectedPlayer;
|
||||
private @MonotonicNonNull ConnectedPlayer connectedPlayer;
|
||||
|
||||
public LoginSessionHandler(VelocityServer server, MinecraftConnection inbound, InboundConnection apiInbound) {
|
||||
this.server = Preconditions.checkNotNull(server, "server");
|
||||
@ -62,12 +66,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
public boolean handle(ServerLogin packet) {
|
||||
this.login = packet;
|
||||
if (inbound.getProtocolVersion() >= ProtocolConstants.MINECRAFT_1_13) {
|
||||
LoginPluginMessage message = new LoginPluginMessage();
|
||||
playerInfoId = ThreadLocalRandom.current().nextInt();
|
||||
message.setId(playerInfoId);
|
||||
message.setChannel(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL);
|
||||
message.setData(Unpooled.EMPTY_BUFFER);
|
||||
inbound.write(message);
|
||||
inbound.write(new LoginPluginMessage(playerInfoId, VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL,
|
||||
Unpooled.EMPTY_BUFFER));
|
||||
} else {
|
||||
beginPreLogin();
|
||||
}
|
||||
@ -92,6 +93,15 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(EncryptionResponse packet) {
|
||||
ServerLogin login = this.login;
|
||||
if (login == null) {
|
||||
throw new IllegalStateException("No ServerLogin packet received yet.");
|
||||
}
|
||||
|
||||
if (verify.length == 0) {
|
||||
throw new IllegalStateException("No EncryptionRequest packet sent yet.");
|
||||
}
|
||||
|
||||
try {
|
||||
KeyPair serverKeyPair = server.getServerKeyPair();
|
||||
byte[] decryptedVerifyToken = EncryptionUtils.decryptRsa(serverKeyPair, packet.getVerifyToken());
|
||||
@ -149,6 +159,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
|
||||
private void beginPreLogin() {
|
||||
ServerLogin login = this.login;
|
||||
if (login == null) {
|
||||
throw new IllegalStateException("No ServerLogin packet received yet.");
|
||||
}
|
||||
PreLoginEvent event = new PreLoginEvent(apiInbound, login.getUsername());
|
||||
server.getEventManager().fire(event)
|
||||
.thenRunAsync(() -> {
|
||||
@ -157,9 +171,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
return;
|
||||
}
|
||||
PreLoginComponentResult result = event.getResult();
|
||||
if (!result.isAllowed()) {
|
||||
Optional<Component> disconnectReason = result.getReason();
|
||||
if (disconnectReason.isPresent()) {
|
||||
// The component is guaranteed to be provided if the connection was denied.
|
||||
inbound.closeWith(Disconnect.create(event.getResult().getReason().get()));
|
||||
inbound.closeWith(Disconnect.create(disconnectReason.get()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -212,13 +227,13 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
// The player was disconnected
|
||||
return;
|
||||
}
|
||||
if (!event.getResult().isAllowed()) {
|
||||
// The component is guaranteed to be provided if the connection was denied.
|
||||
player.disconnect(event.getResult().getReason().get());
|
||||
return;
|
||||
}
|
||||
|
||||
handleProxyLogin(player);
|
||||
Optional<Component> reason = event.getResult().getReason();
|
||||
if (reason.isPresent()) {
|
||||
player.disconnect(reason.get());
|
||||
} else {
|
||||
handleProxyLogin(player);
|
||||
}
|
||||
}, inbound.eventLoop());
|
||||
});
|
||||
|
||||
|
@ -49,9 +49,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
|
||||
ProxyPingEvent event = new ProxyPingEvent(inboundWrapper, initialPing);
|
||||
server.getEventManager().fire(event)
|
||||
.thenRunAsync(() -> {
|
||||
StatusResponse response = new StatusResponse();
|
||||
response.setStatus(VelocityServer.GSON.toJson(event.getPing()));
|
||||
connection.write(response);
|
||||
connection.write(new StatusResponse(VelocityServer.GSON.toJson(event.getPing())));
|
||||
}, connection.eventLoop());
|
||||
return true;
|
||||
}
|
||||
|
@ -9,15 +9,13 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ForgeUtil {
|
||||
|
||||
private ForgeUtil() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static Optional<List<ModInfo.Mod>> readModList(PluginMessage message) {
|
||||
public static List<ModInfo.Mod> readModList(PluginMessage message) {
|
||||
Preconditions.checkNotNull(message, "message");
|
||||
Preconditions.checkArgument(message.getChannel().equals(ForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL),
|
||||
"message is not a FML HS plugin message");
|
||||
@ -36,10 +34,10 @@ public class ForgeUtil {
|
||||
mods.add(new ModInfo.Mod(id, version));
|
||||
}
|
||||
|
||||
return Optional.of(mods.build());
|
||||
return mods.build();
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
return ImmutableList.of();
|
||||
} finally {
|
||||
byteBuf.release();
|
||||
}
|
||||
|
@ -53,16 +53,14 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Comm
|
||||
.completer((reader, parsedLine, list) -> {
|
||||
try {
|
||||
boolean isCommand = parsedLine.line().indexOf(' ') == -1;
|
||||
Optional<List<String>> o = this.server.getCommandManager().offerSuggestions(this, parsedLine.line());
|
||||
o.ifPresent(offers -> {
|
||||
for (String offer : offers) {
|
||||
if (isCommand) {
|
||||
list.add(new Candidate(offer.substring(1)));
|
||||
} else {
|
||||
list.add(new Candidate(offer));
|
||||
}
|
||||
List<String> offers = this.server.getCommandManager().offerSuggestions(this, parsedLine.line());
|
||||
for (String offer : offers) {
|
||||
if (isCommand) {
|
||||
list.add(new Candidate(offer.substring(1)));
|
||||
} else {
|
||||
list.add(new Candidate(offer));
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An error occurred while trying to perform tab completion.", e);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.WriteBufferWaterMark;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashSet;
|
||||
@ -27,16 +28,15 @@ public final class ConnectionManager {
|
||||
private final VelocityServer server;
|
||||
public final ServerChannelInitializerHolder serverChannelInitializer;
|
||||
|
||||
public ConnectionManager(final VelocityServer server) {
|
||||
public ConnectionManager(VelocityServer server) {
|
||||
this.server = server;
|
||||
this.transportType = TransportType.bestType();
|
||||
this.bossGroup = this.transportType.createEventLoopGroup(TransportType.Type.BOSS);
|
||||
this.workerGroup = this.transportType.createEventLoopGroup(TransportType.Type.WORKER);
|
||||
this.serverChannelInitializer = new ServerChannelInitializerHolder(new ServerChannelInitializer(this.server));
|
||||
this.logChannelInformation();
|
||||
}
|
||||
|
||||
private void logChannelInformation() {
|
||||
public void logChannelInformation() {
|
||||
LOGGER.info("Connections will use {} channels, {} compression, {} ciphers", this.transportType, Natives.compressor.getLoadedVariant(), Natives.cipher.getLoadedVariant());
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.velocitypowered.proxy.network.http;
|
||||
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
@ -61,6 +62,9 @@ public class NettyHttpClient {
|
||||
.addListener(future -> {
|
||||
if (future.isSuccess()) {
|
||||
Channel channel = (Channel) future.getNow();
|
||||
if (channel == null) {
|
||||
throw new VerifyException("Null channel retrieved from pool!");
|
||||
}
|
||||
channel.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply));
|
||||
|
||||
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, url.getPath() + "?" + url.getQuery());
|
||||
|
@ -16,10 +16,13 @@ public class PluginClassLoader extends URLClassLoader {
|
||||
|
||||
public PluginClassLoader(URL[] urls) {
|
||||
super(urls);
|
||||
}
|
||||
|
||||
public void addToClassloaders() {
|
||||
loaders.add(this);
|
||||
}
|
||||
|
||||
public void addPath(Path path) {
|
||||
void addPath(Path path) {
|
||||
try {
|
||||
addURL(path.toUri().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
|
@ -12,7 +12,6 @@ import com.velocitypowered.api.plugin.PluginManager;
|
||||
import net.kyori.event.EventSubscriber;
|
||||
import net.kyori.event.PostResult;
|
||||
import net.kyori.event.SimpleEventBus;
|
||||
import net.kyori.event.method.EventExecutor;
|
||||
import net.kyori.event.method.MethodScanner;
|
||||
import net.kyori.event.method.MethodSubscriptionAdapter;
|
||||
import net.kyori.event.method.SimpleMethodSubscriptionAdapter;
|
||||
@ -20,12 +19,14 @@ import net.kyori.event.method.asm.ASMEventExecutorFactory;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -38,20 +39,24 @@ public class VelocityEventManager implements EventManager {
|
||||
.synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new));
|
||||
private final ListMultimap<Object, EventHandler<?>> registeredHandlersByPlugin = Multimaps
|
||||
.synchronizedListMultimap(Multimaps.newListMultimap(new IdentityHashMap<>(), ArrayList::new));
|
||||
private final VelocityEventBus bus = new VelocityEventBus(
|
||||
new ASMEventExecutorFactory<>(new PluginClassLoader(new URL[0])),
|
||||
new VelocityMethodScanner());
|
||||
private final SimpleEventBus<Object> bus;
|
||||
private final MethodSubscriptionAdapter<Object> methodAdapter;
|
||||
private final ExecutorService service;
|
||||
private final PluginManager pluginManager;
|
||||
|
||||
public VelocityEventManager(PluginManager pluginManager) {
|
||||
PluginClassLoader cl = new PluginClassLoader(new URL[0]);
|
||||
cl.addToClassloaders();
|
||||
this.bus = new SimpleEventBus<>(Object.class);
|
||||
this.methodAdapter = new SimpleMethodSubscriptionAdapter<>(bus, new ASMEventExecutorFactory<>(cl),
|
||||
new VelocityMethodScanner());
|
||||
this.pluginManager = pluginManager;
|
||||
this.service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder()
|
||||
.setNameFormat("Velocity Event Executor - #%d").setDaemon(true).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(@NonNull Object plugin, @NonNull Object listener) {
|
||||
public void register(Object plugin, Object listener) {
|
||||
Preconditions.checkNotNull(plugin, "plugin");
|
||||
Preconditions.checkNotNull(listener, "listener");
|
||||
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
|
||||
@ -59,11 +64,12 @@ public class VelocityEventManager implements EventManager {
|
||||
throw new IllegalArgumentException("Trying to register the plugin main instance. Velocity already takes care of this for you.");
|
||||
}
|
||||
registeredListenersByPlugin.put(plugin, listener);
|
||||
bus.register(listener);
|
||||
methodAdapter.register(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> void register(@NonNull Object plugin, @NonNull Class<E> eventClass, @NonNull PostOrder postOrder, @NonNull EventHandler<E> handler) {
|
||||
@SuppressWarnings("type.argument.type.incompatible")
|
||||
public <E> void register(Object plugin, Class<E> eventClass, PostOrder postOrder, EventHandler<E> handler) {
|
||||
Preconditions.checkNotNull(plugin, "plugin");
|
||||
Preconditions.checkNotNull(eventClass, "eventClass");
|
||||
Preconditions.checkNotNull(postOrder, "postOrder");
|
||||
@ -72,8 +78,10 @@ public class VelocityEventManager implements EventManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> @NonNull CompletableFuture<E> fire(@NonNull E event) {
|
||||
Preconditions.checkNotNull(event, "event");
|
||||
public <E> CompletableFuture<E> fire(E event) {
|
||||
if (event == null) {
|
||||
throw new NullPointerException("event");
|
||||
}
|
||||
if (!bus.hasSubscribers(event.getClass())) {
|
||||
// Optimization: nobody's listening.
|
||||
return CompletableFuture.completedFuture(event);
|
||||
@ -99,30 +107,30 @@ public class VelocityEventManager implements EventManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListeners(@NonNull Object plugin) {
|
||||
public void unregisterListeners(Object plugin) {
|
||||
Preconditions.checkNotNull(plugin, "plugin");
|
||||
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
|
||||
Collection<Object> listeners = registeredListenersByPlugin.removeAll(plugin);
|
||||
listeners.forEach(bus::unregister);
|
||||
listeners.forEach(methodAdapter::unregister);
|
||||
Collection<EventHandler<?>> handlers = registeredHandlersByPlugin.removeAll(plugin);
|
||||
handlers.forEach(bus::unregister);
|
||||
handlers.forEach(handler -> bus.unregister(new KyoriToVelocityHandler<>(handler, PostOrder.LAST)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListener(@NonNull Object plugin, @NonNull Object listener) {
|
||||
public void unregisterListener(Object plugin, Object listener) {
|
||||
Preconditions.checkNotNull(plugin, "plugin");
|
||||
Preconditions.checkNotNull(listener, "listener");
|
||||
Preconditions.checkArgument(pluginManager.fromInstance(plugin).isPresent(), "Specified plugin is not loaded");
|
||||
registeredListenersByPlugin.remove(plugin, listener);
|
||||
bus.unregister(listener);
|
||||
methodAdapter.unregister(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E> void unregister(@NonNull Object plugin, @NonNull EventHandler<E> handler) {
|
||||
public <E> void unregister(Object plugin, EventHandler<E> handler) {
|
||||
Preconditions.checkNotNull(plugin, "plugin");
|
||||
Preconditions.checkNotNull(handler, "listener");
|
||||
registeredHandlersByPlugin.remove(plugin, handler);
|
||||
bus.unregister(handler);
|
||||
bus.unregister(new KyoriToVelocityHandler<>(handler, PostOrder.LAST));
|
||||
}
|
||||
|
||||
public boolean shutdown() throws InterruptedException {
|
||||
@ -130,27 +138,6 @@ public class VelocityEventManager implements EventManager {
|
||||
return service.awaitTermination(10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private static class VelocityEventBus extends SimpleEventBus<Object> {
|
||||
private final MethodSubscriptionAdapter<Object> methodAdapter;
|
||||
|
||||
VelocityEventBus(EventExecutor.@NonNull Factory<Object, Object> factory, @NonNull MethodScanner<Object> methodScanner) {
|
||||
super(Object.class);
|
||||
this.methodAdapter = new SimpleMethodSubscriptionAdapter<>(this, factory, methodScanner);
|
||||
}
|
||||
|
||||
void register(Object listener) {
|
||||
this.methodAdapter.register(listener);
|
||||
}
|
||||
|
||||
void unregister(Object listener) {
|
||||
this.methodAdapter.unregister(listener);
|
||||
}
|
||||
|
||||
void unregister(EventHandler<?> handler) {
|
||||
this.unregister(s -> s instanceof KyoriToVelocityHandler && ((KyoriToVelocityHandler<?>) s).getHandler().equals(handler));
|
||||
}
|
||||
}
|
||||
|
||||
private static class VelocityMethodScanner implements MethodScanner<Object> {
|
||||
@Override
|
||||
public boolean shouldRegister(@NonNull Object listener, @NonNull Method method) {
|
||||
@ -159,7 +146,11 @@ public class VelocityEventManager implements EventManager {
|
||||
|
||||
@Override
|
||||
public int postOrder(@NonNull Object listener, @NonNull Method method) {
|
||||
return method.getAnnotation(Subscribe.class).order().ordinal();
|
||||
Subscribe annotation = method.getAnnotation(Subscribe.class);
|
||||
if (annotation == null) {
|
||||
throw new IllegalStateException("Trying to determine post order for listener without @Subscribe annotation");
|
||||
}
|
||||
return annotation.order().ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -190,5 +181,18 @@ public class VelocityEventManager implements EventManager {
|
||||
public EventHandler<E> getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
KyoriToVelocityHandler<?> that = (KyoriToVelocityHandler<?>) o;
|
||||
return Objects.equals(handler, that.handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,12 +31,15 @@ public class VelocityPluginManager implements PluginManager {
|
||||
this.server = checkNotNull(server, "server");
|
||||
}
|
||||
|
||||
private void registerPlugin(@NonNull PluginContainer plugin) {
|
||||
private void registerPlugin(PluginContainer plugin) {
|
||||
plugins.put(plugin.getDescription().getId(), plugin);
|
||||
plugin.getInstance().ifPresent(instance -> pluginInstances.put(instance, plugin));
|
||||
Optional<?> instance = plugin.getInstance();
|
||||
if (instance.isPresent()) {
|
||||
pluginInstances.put(instance.get(), plugin);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadPlugins(@NonNull Path directory) throws IOException {
|
||||
public void loadPlugins(Path directory) throws IOException {
|
||||
checkNotNull(directory, "directory");
|
||||
checkArgument(Files.isDirectory(directory), "provided path isn't a directory");
|
||||
|
||||
@ -86,7 +89,7 @@ public class VelocityPluginManager implements PluginManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Optional<PluginContainer> fromInstance(@NonNull Object instance) {
|
||||
public Optional<PluginContainer> fromInstance(Object instance) {
|
||||
checkNotNull(instance, "instance");
|
||||
|
||||
if (instance instanceof PluginContainer) {
|
||||
@ -97,23 +100,23 @@ public class VelocityPluginManager implements PluginManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Optional<PluginContainer> getPlugin(@NonNull String id) {
|
||||
public Optional<PluginContainer> getPlugin(String id) {
|
||||
checkNotNull(id, "id");
|
||||
return Optional.ofNullable(plugins.get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Collection<PluginContainer> getPlugins() {
|
||||
public Collection<PluginContainer> getPlugins() {
|
||||
return Collections.unmodifiableCollection(plugins.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded(@NonNull String id) {
|
||||
public boolean isLoaded(String id) {
|
||||
return plugins.containsKey(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToClasspath(@NonNull Object plugin, @NonNull Path path) {
|
||||
public void addToClasspath(Object plugin, Path path) {
|
||||
checkNotNull(plugin, "instance");
|
||||
checkNotNull(path, "path");
|
||||
checkArgument(pluginInstances.containsKey(plugin), "plugin is not loaded");
|
||||
|
@ -12,6 +12,7 @@ import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.plugin.PluginClassLoader;
|
||||
import com.velocitypowered.proxy.plugin.loader.java.JavaVelocityPluginDescription;
|
||||
import com.velocitypowered.proxy.plugin.loader.java.VelocityPluginModule;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@ -51,6 +52,7 @@ public class JavaPluginLoader implements PluginLoader {
|
||||
PluginClassLoader loader = new PluginClassLoader(
|
||||
new URL[] {source.toUri().toURL() }
|
||||
);
|
||||
loader.addToClassloaders();
|
||||
|
||||
Class mainClass = loader.loadClass(pd.getMain());
|
||||
return createDescription(pd, source, mainClass);
|
||||
@ -72,6 +74,10 @@ public class JavaPluginLoader implements PluginLoader {
|
||||
Injector injector = Guice.createInjector(new VelocityPluginModule(server, javaDescription, baseDirectory));
|
||||
Object instance = injector.getInstance(javaDescription.getMainClass());
|
||||
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("Got nothing from injector for plugin " + javaDescription.getId());
|
||||
}
|
||||
|
||||
return new VelocityPluginContainer(description, instance);
|
||||
}
|
||||
|
||||
@ -93,10 +99,8 @@ public class JavaPluginLoader implements PluginLoader {
|
||||
private VelocityPluginDescription createDescription(SerializedPluginDescription description, Path source, Class mainClass) {
|
||||
Set<PluginDependency> dependencies = new HashSet<>();
|
||||
|
||||
if (description.getDependencies() != null) {
|
||||
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
|
||||
dependencies.add(toDependencyMeta(dependency));
|
||||
}
|
||||
for (SerializedPluginDescription.Dependency dependency : description.getDependencies()) {
|
||||
dependencies.add(toDependencyMeta(dependency));
|
||||
}
|
||||
|
||||
return new JavaVelocityPluginDescription(
|
||||
|
@ -33,7 +33,7 @@ public class VelocityPluginDescription implements PluginDescription {
|
||||
this.description = Strings.emptyToNull(description);
|
||||
this.url = Strings.emptyToNull(url);
|
||||
this.authors = authors == null ? ImmutableList.of() : ImmutableList.copyOf(authors);
|
||||
this.dependencies = Maps.uniqueIndex(dependencies, PluginDependency::getId);
|
||||
this.dependencies = Maps.uniqueIndex(dependencies, d -> d == null ? null : d.getId());
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
|
@ -8,14 +8,15 @@ import com.google.common.graph.GraphBuilder;
|
||||
import com.google.common.graph.MutableGraph;
|
||||
import com.velocitypowered.api.plugin.PluginDescription;
|
||||
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class PluginDependencyUtils {
|
||||
public static List<PluginDescription> sortCandidates(List<PluginDescription> candidates) {
|
||||
public static List<PluginDescription> sortCandidates(List<@NonNull PluginDescription> candidates) {
|
||||
// Create our graph, we're going to be using this for Kahn's algorithm.
|
||||
MutableGraph<PluginDescription> graph = GraphBuilder.directed().allowsSelfLoops(false).build();
|
||||
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(candidates, PluginDescription::getId);
|
||||
Map<String, PluginDescription> candidateMap = Maps.uniqueIndex(candidates, d -> d == null ? null : d.getId());
|
||||
|
||||
// Add edges
|
||||
for (PluginDescription description : candidates) {
|
||||
@ -36,7 +37,7 @@ public class PluginDependencyUtils {
|
||||
// Actually run Kahn's algorithm
|
||||
List<PluginDescription> sorted = new ArrayList<>();
|
||||
while (!noEdges.isEmpty()) {
|
||||
PluginDescription candidate = noEdges.poll();
|
||||
PluginDescription candidate = noEdges.remove();
|
||||
sorted.add(candidate);
|
||||
|
||||
for (PluginDescription node : ImmutableSet.copyOf(graph.adjacentNodes(candidate))) {
|
||||
|
@ -48,6 +48,10 @@ public enum ProtocolConstants { ;
|
||||
|
||||
public enum Direction {
|
||||
SERVERBOUND,
|
||||
CLIENTBOUND
|
||||
CLIENTBOUND;
|
||||
|
||||
public StateRegistry.PacketRegistry.ProtocolVersion getProtocol(StateRegistry state, int protocolVersion) {
|
||||
return (this == SERVERBOUND ? state.SERVERBOUND : state.CLIENTBOUND).getVersion(protocolVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package com.velocitypowered.proxy.protocol;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.primitives.ImmutableIntArray;
|
||||
import com.velocitypowered.proxy.protocol.packet.*;
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
import io.netty.util.collection.IntObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
@ -36,6 +36,9 @@ public enum StateRegistry {
|
||||
},
|
||||
PLAY {
|
||||
{
|
||||
SERVERBOUND.fallback = false;
|
||||
CLIENTBOUND.fallback = false;
|
||||
|
||||
SERVERBOUND.register(TabCompleteRequest.class, TabCompleteRequest::new,
|
||||
map(0x14, MINECRAFT_1_8, false),
|
||||
map(0x01, MINECRAFT_1_9, false),
|
||||
@ -150,8 +153,8 @@ public enum StateRegistry {
|
||||
|
||||
public static final int STATUS_ID = 1;
|
||||
public static final int LOGIN_ID = 2;
|
||||
public final PacketRegistry CLIENTBOUND = new PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND, this);
|
||||
public final PacketRegistry SERVERBOUND = new PacketRegistry(ProtocolConstants.Direction.SERVERBOUND, this);
|
||||
public final PacketRegistry CLIENTBOUND = new PacketRegistry(ProtocolConstants.Direction.CLIENTBOUND);
|
||||
public final PacketRegistry SERVERBOUND = new PacketRegistry(ProtocolConstants.Direction.SERVERBOUND);
|
||||
|
||||
public static class PacketRegistry {
|
||||
private static final IntObjectMap<ImmutableIntArray> LINKED_PROTOCOL_VERSIONS = new IntObjectHashMap<>();
|
||||
@ -165,19 +168,18 @@ public enum StateRegistry {
|
||||
}
|
||||
|
||||
private final ProtocolConstants.Direction direction;
|
||||
private final StateRegistry state;
|
||||
private final IntObjectMap<ProtocolVersion> versions = new IntObjectHashMap<>(16);
|
||||
private boolean fallback = true;
|
||||
|
||||
public PacketRegistry(Direction direction, StateRegistry state) {
|
||||
public PacketRegistry(Direction direction) {
|
||||
this.direction = direction;
|
||||
this.state = state;
|
||||
ProtocolConstants.SUPPORTED_VERSIONS.forEach(version -> versions.put(version, new ProtocolVersion(version)));
|
||||
}
|
||||
|
||||
public ProtocolVersion getVersion(final int version) {
|
||||
ProtocolVersion result = versions.get(version);
|
||||
if (result == null) {
|
||||
if (state != PLAY) {
|
||||
if (fallback) {
|
||||
return getVersion(MINIMUM_GENERIC_VERSION);
|
||||
}
|
||||
throw new IllegalArgumentException("Could not find data for protocol version " + version);
|
||||
@ -224,7 +226,7 @@ public enum StateRegistry {
|
||||
this.packetClassToId.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
public MinecraftPacket createPacket(final int id) {
|
||||
public @Nullable MinecraftPacket createPacket(final int id) {
|
||||
final Supplier<? extends MinecraftPacket> supplier = this.packetIdToSupplier.get(id);
|
||||
if (supplier == null) {
|
||||
return null;
|
||||
@ -242,23 +244,6 @@ public enum StateRegistry {
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder mappingAsString = new StringBuilder("{");
|
||||
for (Object2IntMap.Entry<Class<? extends MinecraftPacket>> entry : packetClassToId.object2IntEntrySet()) {
|
||||
mappingAsString.append(entry.getKey().getSimpleName()).append(" -> ")
|
||||
.append("0x")
|
||||
.append(Strings.padStart(Integer.toHexString(entry.getIntValue()), 2, '0'))
|
||||
.append(", ");
|
||||
}
|
||||
mappingAsString.setLength(mappingAsString.length() - 2);
|
||||
mappingAsString.append("}");
|
||||
return "ProtocolVersion{" +
|
||||
"id=" + id +
|
||||
", packetClassToId=" + mappingAsString.toString() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,7 +268,7 @@ public enum StateRegistry {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PacketMapping that = (PacketMapping) o;
|
||||
|
@ -13,14 +13,14 @@ import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import java.util.List;
|
||||
|
||||
public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
private StateRegistry state;
|
||||
private final ProtocolConstants.Direction direction;
|
||||
private StateRegistry state;
|
||||
private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion;
|
||||
|
||||
public MinecraftDecoder(ProtocolConstants.Direction direction) {
|
||||
this.state = StateRegistry.HANDSHAKE;
|
||||
this.direction = Preconditions.checkNotNull(direction, "direction");
|
||||
this.setProtocolVersion(ProtocolConstants.MINIMUM_GENERIC_VERSION);
|
||||
this.protocolVersion = direction.getProtocol(StateRegistry.HANDSHAKE, ProtocolConstants.MINIMUM_GENERIC_VERSION);
|
||||
this.state = StateRegistry.HANDSHAKE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -51,24 +51,12 @@ public class MinecraftDecoder extends MessageToMessageDecoder<ByteBuf> {
|
||||
}
|
||||
}
|
||||
|
||||
public StateRegistry.PacketRegistry.ProtocolVersion getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = (this.direction == ProtocolConstants.Direction.CLIENTBOUND ? this.state.CLIENTBOUND : this.state.SERVERBOUND).getVersion(protocolVersion);
|
||||
}
|
||||
|
||||
public StateRegistry getState() {
|
||||
return state;
|
||||
this.protocolVersion = direction.getProtocol(state, protocolVersion);
|
||||
}
|
||||
|
||||
public void setState(StateRegistry state) {
|
||||
this.state = state;
|
||||
this.setProtocolVersion(protocolVersion.id);
|
||||
}
|
||||
|
||||
public ProtocolConstants.Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
@ -10,14 +10,14 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
|
||||
private StateRegistry state;
|
||||
private final ProtocolConstants.Direction direction;
|
||||
private StateRegistry state;
|
||||
private StateRegistry.PacketRegistry.ProtocolVersion protocolVersion;
|
||||
|
||||
public MinecraftEncoder(ProtocolConstants.Direction direction) {
|
||||
this.state = StateRegistry.HANDSHAKE;
|
||||
this.direction = Preconditions.checkNotNull(direction, "direction");
|
||||
this.setProtocolVersion(ProtocolConstants.MINIMUM_GENERIC_VERSION);
|
||||
this.protocolVersion = direction.getProtocol(StateRegistry.HANDSHAKE, ProtocolConstants.MINIMUM_GENERIC_VERSION);
|
||||
this.state = StateRegistry.HANDSHAKE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -27,24 +27,12 @@ public class MinecraftEncoder extends MessageToByteEncoder<MinecraftPacket> {
|
||||
msg.encode(out, direction, protocolVersion.id);
|
||||
}
|
||||
|
||||
public StateRegistry.PacketRegistry.ProtocolVersion getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(final int protocolVersion) {
|
||||
this.protocolVersion = (this.direction == ProtocolConstants.Direction.CLIENTBOUND ? this.state.CLIENTBOUND : this.state.SERVERBOUND).getVersion(protocolVersion);
|
||||
}
|
||||
|
||||
public StateRegistry getState() {
|
||||
return state;
|
||||
this.protocolVersion = direction.getProtocol(state, protocolVersion);
|
||||
}
|
||||
|
||||
public void setState(StateRegistry state) {
|
||||
this.state = state;
|
||||
this.setProtocolVersion(protocolVersion.id);
|
||||
}
|
||||
|
||||
public ProtocolConstants.Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@ -15,15 +16,18 @@ public class BossBar implements MinecraftPacket {
|
||||
public static final int UPDATE_NAME = 3;
|
||||
public static final int UPDATE_STYLE = 4;
|
||||
public static final int UPDATE_PROPERTIES = 5;
|
||||
private UUID uuid;
|
||||
private @Nullable UUID uuid;
|
||||
private int action;
|
||||
private String name;
|
||||
private @Nullable String name;
|
||||
private float percent;
|
||||
private int color;
|
||||
private int overlay;
|
||||
private short flags;
|
||||
|
||||
public UUID getUuid() {
|
||||
if (uuid == null) {
|
||||
throw new IllegalStateException("No boss bar UUID specified");
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@ -39,7 +43,7 @@ public class BossBar implements MinecraftPacket {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
public @Nullable String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -124,10 +128,16 @@ public class BossBar implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (uuid == null) {
|
||||
throw new IllegalStateException("No boss bar UUID specified");
|
||||
}
|
||||
ProtocolUtils.writeUuid(buf, uuid);
|
||||
ProtocolUtils.writeVarInt(buf, action);
|
||||
switch (action) {
|
||||
case ADD:
|
||||
if (name == null) {
|
||||
throw new IllegalStateException("No name specified!");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, name);
|
||||
buf.writeFloat(percent);
|
||||
ProtocolUtils.writeVarInt(buf, color);
|
||||
@ -140,6 +150,9 @@ public class BossBar implements MinecraftPacket {
|
||||
buf.writeFloat(percent);
|
||||
break;
|
||||
case UPDATE_NAME:
|
||||
if (name == null) {
|
||||
throw new IllegalStateException("No name specified!");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, name);
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
|
@ -8,12 +8,13 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class Chat implements MinecraftPacket {
|
||||
public static final byte CHAT = (byte) 0;
|
||||
public static final int MAX_SERVERBOUND_MESSAGE_LENGTH = 256;
|
||||
|
||||
private String message;
|
||||
private @Nullable String message;
|
||||
private byte type;
|
||||
|
||||
public Chat() {
|
||||
@ -25,6 +26,9 @@ public class Chat implements MinecraftPacket {
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
if (message == null) {
|
||||
throw new IllegalStateException("Message is not specified");
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
@ -58,6 +62,9 @@ public class Chat implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (message == null) {
|
||||
throw new IllegalStateException("Message is not specified");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, message);
|
||||
if (direction == ProtocolConstants.Direction.CLIENTBOUND) {
|
||||
buf.writeByte(type);
|
||||
|
@ -5,10 +5,11 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class ClientSettings implements MinecraftPacket {
|
||||
|
||||
private String locale;
|
||||
private @Nullable String locale;
|
||||
private byte viewDistance;
|
||||
private int chatVisibility;
|
||||
private boolean chatColors;
|
||||
@ -28,6 +29,9 @@ public class ClientSettings implements MinecraftPacket {
|
||||
}
|
||||
|
||||
public String getLocale() {
|
||||
if (locale == null) {
|
||||
throw new IllegalStateException("No locale specified");
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
@ -102,6 +106,9 @@ public class ClientSettings implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (locale == null) {
|
||||
throw new IllegalStateException("No locale specified");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, locale);
|
||||
buf.writeByte(viewDistance);
|
||||
ProtocolUtils.writeVarInt(buf, chatVisibility);
|
||||
|
@ -8,22 +8,26 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class Disconnect implements MinecraftPacket {
|
||||
private String reason;
|
||||
private @Nullable String reason;
|
||||
|
||||
public Disconnect() {
|
||||
}
|
||||
|
||||
public Disconnect(String reason) {
|
||||
this.reason = reason;
|
||||
this.reason = Preconditions.checkNotNull(reason, "reason");
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
if (reason == null) {
|
||||
throw new IllegalStateException("No reason specified");
|
||||
}
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
public void setReason(@Nullable String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@ -41,6 +45,9 @@ public class Disconnect implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (reason == null) {
|
||||
throw new IllegalStateException("No reason specified.");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, reason);
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,12 @@ import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.velocitypowered.proxy.connection.VelocityConstants.*;
|
||||
|
||||
public class EncryptionRequest implements MinecraftPacket {
|
||||
private String serverId = "";
|
||||
private byte[] publicKey;
|
||||
private byte[] verifyToken;
|
||||
private byte[] publicKey = EMPTY_BYTE_ARRAY;
|
||||
private byte[] verifyToken = EMPTY_BYTE_ARRAY;
|
||||
|
||||
public byte[] getPublicKey() {
|
||||
return publicKey;
|
||||
|
@ -8,9 +8,11 @@ import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.velocitypowered.proxy.connection.VelocityConstants.*;
|
||||
|
||||
public class EncryptionResponse implements MinecraftPacket {
|
||||
private byte[] sharedSecret;
|
||||
private byte[] verifyToken;
|
||||
private byte[] sharedSecret = EMPTY_BYTE_ARRAY;
|
||||
private byte[] verifyToken = EMPTY_BYTE_ARRAY;
|
||||
|
||||
public byte[] getSharedSecret() {
|
||||
return sharedSecret;
|
||||
|
@ -8,7 +8,7 @@ import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class Handshake implements MinecraftPacket {
|
||||
private int protocolVersion;
|
||||
private String serverAddress;
|
||||
private String serverAddress = "";
|
||||
private int port;
|
||||
private int nextStatus;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants.Direction;
|
||||
@ -11,36 +12,29 @@ import net.kyori.text.serializer.ComponentSerializers;
|
||||
import static com.velocitypowered.proxy.protocol.ProtocolUtils.writeString;
|
||||
|
||||
public class HeaderAndFooter implements MinecraftPacket {
|
||||
|
||||
private static final HeaderAndFooter RESET = new HeaderAndFooter("{\"translate\":\"\"}", "{\"translate\":\"\"}");
|
||||
private static final String EMPTY_COMPONENT = "{\"translate\":\"\"}";
|
||||
private static final HeaderAndFooter RESET = new HeaderAndFooter();
|
||||
|
||||
private String header;
|
||||
private String footer;
|
||||
|
||||
public HeaderAndFooter() {
|
||||
this(EMPTY_COMPONENT, EMPTY_COMPONENT);
|
||||
}
|
||||
|
||||
public HeaderAndFooter(String header, String footer) {
|
||||
this.header = header;
|
||||
this.footer = footer;
|
||||
this.header = Preconditions.checkNotNull(header, "header");
|
||||
this.footer = Preconditions.checkNotNull(footer, "footer");
|
||||
}
|
||||
|
||||
public String getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setHeader(String header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public String getFooter() {
|
||||
return footer;
|
||||
}
|
||||
|
||||
public void setFooter(String footer) {
|
||||
this.footer = footer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, Direction direction, int protocolVersion) {
|
||||
throw new UnsupportedOperationException("Decode is not implemented");
|
||||
|
@ -5,6 +5,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class JoinGame implements MinecraftPacket {
|
||||
private int entityId;
|
||||
@ -12,7 +13,7 @@ public class JoinGame implements MinecraftPacket {
|
||||
private int dimension;
|
||||
private short difficulty;
|
||||
private short maxPlayers;
|
||||
private String levelType;
|
||||
private @Nullable String levelType;
|
||||
private boolean reducedDebugInfo;
|
||||
|
||||
public int getEntityId() {
|
||||
@ -56,6 +57,9 @@ public class JoinGame implements MinecraftPacket {
|
||||
}
|
||||
|
||||
public String getLevelType() {
|
||||
if (levelType == null) {
|
||||
throw new IllegalStateException("No level type specified.");
|
||||
}
|
||||
return levelType;
|
||||
}
|
||||
|
||||
@ -110,6 +114,9 @@ public class JoinGame implements MinecraftPacket {
|
||||
}
|
||||
buf.writeByte(difficulty);
|
||||
buf.writeByte(maxPlayers);
|
||||
if (levelType == null) {
|
||||
throw new IllegalStateException("No level type specified.");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, levelType);
|
||||
buf.writeBoolean(reducedDebugInfo);
|
||||
}
|
||||
|
@ -6,14 +6,11 @@ import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
public class LegacyPingResponse {
|
||||
private static final ServerPing.Players FAKE_PLAYERS = new ServerPing.Players(0, 0, ImmutableList.of());
|
||||
private int protocolVersion;
|
||||
private String serverVersion;
|
||||
private String motd;
|
||||
private int playersOnline;
|
||||
private int playersMax;
|
||||
|
||||
public LegacyPingResponse() {
|
||||
}
|
||||
private final int protocolVersion;
|
||||
private final String serverVersion;
|
||||
private final String motd;
|
||||
private final int playersOnline;
|
||||
private final int playersMax;
|
||||
|
||||
public LegacyPingResponse(int protocolVersion, String serverVersion, String motd, int playersOnline, int playersMax) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
@ -27,42 +24,22 @@ public class LegacyPingResponse {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(int protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public String getServerVersion() {
|
||||
return serverVersion;
|
||||
}
|
||||
|
||||
public void setServerVersion(String serverVersion) {
|
||||
this.serverVersion = serverVersion;
|
||||
}
|
||||
|
||||
public String getMotd() {
|
||||
return motd;
|
||||
}
|
||||
|
||||
public void setMotd(String motd) {
|
||||
this.motd = motd;
|
||||
}
|
||||
|
||||
public int getPlayersOnline() {
|
||||
return playersOnline;
|
||||
}
|
||||
|
||||
public void setPlayersOnline(int playersOnline) {
|
||||
this.playersOnline = playersOnline;
|
||||
}
|
||||
|
||||
public int getPlayersMax() {
|
||||
return playersMax;
|
||||
}
|
||||
|
||||
public void setPlayersMax(int playersMax) {
|
||||
this.playersMax = playersMax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LegacyPingResponse{" +
|
||||
|
@ -6,36 +6,38 @@ import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class LoginPluginMessage implements MinecraftPacket {
|
||||
private int id;
|
||||
private String channel;
|
||||
private ByteBuf data;
|
||||
private @Nullable String channel;
|
||||
private ByteBuf data = Unpooled.EMPTY_BUFFER;
|
||||
|
||||
public LoginPluginMessage() {
|
||||
|
||||
}
|
||||
|
||||
public LoginPluginMessage(int id, @Nullable String channel, ByteBuf data) {
|
||||
this.id = id;
|
||||
this.channel = channel;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
if (channel == null) {
|
||||
throw new IllegalStateException("Channel is not specified!");
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public ByteBuf getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(ByteBuf data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LoginPluginMessage{" +
|
||||
@ -59,6 +61,9 @@ public class LoginPluginMessage implements MinecraftPacket {
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeVarInt(buf, id);
|
||||
if (channel == null) {
|
||||
throw new IllegalStateException("Channel is not specified!");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, channel);
|
||||
buf.writeBytes(data);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import io.netty.buffer.Unpooled;
|
||||
public class LoginPluginResponse implements MinecraftPacket {
|
||||
private int id;
|
||||
private boolean success;
|
||||
private ByteBuf data;
|
||||
private ByteBuf data = Unpooled.EMPTY_BUFFER;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
@ -9,6 +10,7 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -21,11 +23,11 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
public static final int UPDATE_DISPLAY_NAME = 3;
|
||||
public static final int REMOVE_PLAYER = 4;
|
||||
private int action;
|
||||
private List<Item> items;
|
||||
private final List<Item> items = new ArrayList<>();
|
||||
|
||||
public PlayerListItem(int action, List<Item> items) {
|
||||
this.action = action;
|
||||
this.items = items;
|
||||
this.items.addAll(items);
|
||||
}
|
||||
|
||||
public PlayerListItem() {}
|
||||
@ -41,7 +43,6 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
action = ProtocolUtils.readVarInt(buf);
|
||||
items = new ArrayList<>();
|
||||
int length = ProtocolUtils.readVarInt(buf);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
@ -57,7 +58,8 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
if (hasDisplayName) {
|
||||
item.setDisplayName(ComponentSerializers.JSON.deserialize(ProtocolUtils.readString(buf)));
|
||||
}
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
case UPDATE_GAMEMODE:
|
||||
item.setGameMode(ProtocolUtils.readVarInt(buf));
|
||||
break;
|
||||
@ -81,7 +83,7 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
ProtocolUtils.writeVarInt(buf, action);
|
||||
ProtocolUtils.writeVarInt(buf, items.size());
|
||||
for (Item item: items) {
|
||||
for (Item item : items) {
|
||||
ProtocolUtils.writeUuid(buf, item.getUuid());
|
||||
switch (action) {
|
||||
case ADD_PLAYER:
|
||||
@ -113,7 +115,7 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
return handler.handle(this);
|
||||
}
|
||||
|
||||
private void writeDisplayName(ByteBuf buf, Component displayName) {
|
||||
private void writeDisplayName(ByteBuf buf, @Nullable Component displayName) {
|
||||
buf.writeBoolean(displayName != null);
|
||||
if (displayName != null) {
|
||||
ProtocolUtils.writeString(buf, ComponentSerializers.JSON.serialize(displayName));
|
||||
@ -122,11 +124,11 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
|
||||
public static class Item {
|
||||
private final UUID uuid;
|
||||
private String name;
|
||||
private List<GameProfile.Property> properties;
|
||||
private String name = "";
|
||||
private List<GameProfile.Property> properties = ImmutableList.of();
|
||||
private int gameMode;
|
||||
private int latency;
|
||||
private Component displayName;
|
||||
private @Nullable Component displayName;
|
||||
|
||||
public Item(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
@ -181,11 +183,11 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Component getDisplayName() {
|
||||
public @Nullable Component getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public Item setDisplayName(Component displayName) {
|
||||
public Item setDisplayName(@Nullable Component displayName) {
|
||||
this.displayName = displayName;
|
||||
return this;
|
||||
}
|
||||
|
@ -6,12 +6,18 @@ import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static com.velocitypowered.proxy.connection.VelocityConstants.*;
|
||||
|
||||
public class PluginMessage implements MinecraftPacket {
|
||||
private String channel;
|
||||
private byte[] data;
|
||||
private @Nullable String channel;
|
||||
private byte[] data = EMPTY_BYTE_ARRAY;
|
||||
|
||||
public String getChannel() {
|
||||
if (channel == null) {
|
||||
throw new IllegalStateException("Channel is not specified.");
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
@ -44,6 +50,9 @@ public class PluginMessage implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (channel == null) {
|
||||
throw new IllegalStateException("Channel is not specified.");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, channel);
|
||||
buf.writeBytes(data);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public class Respawn implements MinecraftPacket {
|
||||
private int dimension;
|
||||
private short difficulty;
|
||||
private short gamemode;
|
||||
private String levelType;
|
||||
private String levelType = "";
|
||||
|
||||
public Respawn() {
|
||||
}
|
||||
|
@ -1,20 +1,27 @@
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class ServerLogin implements MinecraftPacket {
|
||||
private String username;
|
||||
private @Nullable String username;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
public ServerLogin() {}
|
||||
|
||||
public ServerLogin(String username) {
|
||||
this.username = Preconditions.checkNotNull(username, "username");
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
public String getUsername() {
|
||||
if (username == null) {
|
||||
throw new IllegalStateException("No username found!");
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -31,6 +38,9 @@ public class ServerLogin implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (username == null) {
|
||||
throw new IllegalStateException("No username found!");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, username);
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,18 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ServerLoginSuccess implements MinecraftPacket {
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
private @Nullable UUID uuid;
|
||||
private @Nullable String username;
|
||||
|
||||
public UUID getUuid() {
|
||||
if (uuid == null) {
|
||||
throw new IllegalStateException("No UUID specified!");
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@ -21,6 +25,9 @@ public class ServerLoginSuccess implements MinecraftPacket {
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
if (username == null) {
|
||||
throw new IllegalStateException("No username specified!");
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
@ -44,7 +51,13 @@ public class ServerLoginSuccess implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (uuid == null) {
|
||||
throw new IllegalStateException("No UUID specified!");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, uuid.toString());
|
||||
if (username == null) {
|
||||
throw new IllegalStateException("No username specified!");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, username);
|
||||
}
|
||||
|
||||
|
@ -5,16 +5,22 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class StatusResponse implements MinecraftPacket {
|
||||
private String status;
|
||||
private @Nullable String status;
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
public StatusResponse() {}
|
||||
|
||||
public StatusResponse(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
public String getStatus() {
|
||||
if (status == null) {
|
||||
throw new IllegalStateException("Status is not specified");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -31,6 +37,9 @@ public class StatusResponse implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (status == null) {
|
||||
throw new IllegalStateException("Status is not specified");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, status);
|
||||
}
|
||||
|
||||
|
@ -5,16 +5,20 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import static com.velocitypowered.proxy.protocol.ProtocolConstants.MINECRAFT_1_9;
|
||||
|
||||
public class TabCompleteRequest implements MinecraftPacket {
|
||||
private String command;
|
||||
private @Nullable String command;
|
||||
private boolean assumeCommand;
|
||||
private boolean hasPosition;
|
||||
private long position;
|
||||
|
||||
public String getCommand() {
|
||||
if (command == null) {
|
||||
throw new IllegalStateException("Command is not specified");
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
@ -70,6 +74,9 @@ public class TabCompleteRequest implements MinecraftPacket {
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||
if (command == null) {
|
||||
throw new IllegalStateException("Command is not specified");
|
||||
}
|
||||
ProtocolUtils.writeString(buf, command);
|
||||
if (protocolVersion >= MINECRAFT_1_9) {
|
||||
buf.writeBoolean(assumeCommand);
|
||||
|
@ -5,6 +5,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class TitlePacket implements MinecraftPacket {
|
||||
public static final int SET_TITLE = 0;
|
||||
@ -18,7 +19,7 @@ public class TitlePacket implements MinecraftPacket {
|
||||
public static final int RESET_OLD = 4;
|
||||
|
||||
private int action;
|
||||
private String component;
|
||||
private @Nullable String component;
|
||||
private int fadeIn;
|
||||
private int stay;
|
||||
private int fadeOut;
|
||||
@ -37,6 +38,9 @@ public class TitlePacket implements MinecraftPacket {
|
||||
case SET_TITLE:
|
||||
case SET_SUBTITLE:
|
||||
case SET_ACTION_BAR:
|
||||
if (component == null) {
|
||||
throw new IllegalStateException("No component found for " + action);
|
||||
}
|
||||
ProtocolUtils.writeString(buf, component);
|
||||
break;
|
||||
case SET_TIMES:
|
||||
@ -52,6 +56,9 @@ public class TitlePacket implements MinecraftPacket {
|
||||
switch (action) {
|
||||
case SET_TITLE:
|
||||
case SET_SUBTITLE:
|
||||
if (component == null) {
|
||||
throw new IllegalStateException("No component found for " + action);
|
||||
}
|
||||
ProtocolUtils.writeString(buf, component);
|
||||
break;
|
||||
case SET_TIMES_OLD:
|
||||
@ -74,11 +81,11 @@ public class TitlePacket implements MinecraftPacket {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getComponent() {
|
||||
public @Nullable String getComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
public void setComponent(String component) {
|
||||
public void setComponent(@Nullable String component) {
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import com.velocitypowered.api.scheduler.Scheduler;
|
||||
import com.velocitypowered.api.scheduler.TaskStatus;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
@ -92,6 +93,7 @@ public class VelocityScheduler implements Scheduler {
|
||||
public ScheduledTask schedule() {
|
||||
VelocityTask task = new VelocityTask(plugin, runnable, delay, repeat);
|
||||
tasksByPlugin.put(plugin, task);
|
||||
task.schedule();
|
||||
return task;
|
||||
}
|
||||
}
|
||||
@ -99,12 +101,19 @@ public class VelocityScheduler implements Scheduler {
|
||||
private class VelocityTask implements Runnable, ScheduledTask {
|
||||
private final Object plugin;
|
||||
private final Runnable runnable;
|
||||
private ScheduledFuture<?> future;
|
||||
private volatile Thread currentTaskThread;
|
||||
private final long delay;
|
||||
private final long repeat;
|
||||
private @Nullable ScheduledFuture<?> future;
|
||||
private volatile @Nullable Thread currentTaskThread;
|
||||
|
||||
private VelocityTask(Object plugin, Runnable runnable, long delay, long repeat) {
|
||||
this.plugin = plugin;
|
||||
this.runnable = runnable;
|
||||
this.delay = delay;
|
||||
this.repeat = repeat;
|
||||
}
|
||||
|
||||
public void schedule() {
|
||||
if (repeat == 0) {
|
||||
this.future = timerExecutionService.schedule(this, delay, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
@ -155,14 +164,10 @@ public class VelocityScheduler implements Scheduler {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
// Since we can't catch InterruptedException separately...
|
||||
if (e instanceof InterruptedException) {
|
||||
onFinish();
|
||||
} else {
|
||||
Log.logger.error("Exception in task {} by plugin {}", runnable, plugin);
|
||||
}
|
||||
Log.logger.error("Exception in task {} by plugin {}", runnable, plugin);
|
||||
} finally {
|
||||
currentTaskThread = null;
|
||||
}
|
||||
currentTaskThread = null;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
@ -13,10 +14,10 @@ import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ServerMap {
|
||||
private final VelocityServer server;
|
||||
private final @Nullable VelocityServer server;
|
||||
private final Map<String, RegisteredServer> servers = new ConcurrentHashMap<>();
|
||||
|
||||
public ServerMap(VelocityServer server) {
|
||||
public ServerMap(@Nullable VelocityServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@ -49,7 +50,9 @@ public class ServerMap {
|
||||
Preconditions.checkNotNull(serverInfo, "serverInfo");
|
||||
String lowerName = serverInfo.getName().toLowerCase(Locale.US);
|
||||
RegisteredServer rs = servers.get(lowerName);
|
||||
Preconditions.checkArgument(rs != null, "Server with name %s is not registered!", serverInfo.getName());
|
||||
if (rs == null) {
|
||||
throw new IllegalArgumentException("Server with name " + serverInfo.getName() + " is not registered!");
|
||||
}
|
||||
Preconditions.checkArgument(rs.getServerInfo().equals(serverInfo), "Trying to remove server %s with differing information", serverInfo.getName());
|
||||
Preconditions.checkState(servers.remove(lowerName, rs), "Server with name %s replaced whilst unregistering", serverInfo.getName());
|
||||
}
|
||||
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren