Merge branch 'dev/2.0.0' into 2/new-registry

This commit is contained in:
Andrew Steinborn
2021-05-11 01:53:22 -04:00
389 changed files with 9452 additions and 2533 deletions

View File

@@ -5,9 +5,14 @@ plugins {
id 'checkstyle'
}
apply plugin: 'org.cadixdev.licenser'
apply from: '../gradle/checkstyle.gradle'
apply plugin: 'com.github.johnrengelman.shadow'
license {
header = project.rootProject.file('HEADER.txt')
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
@@ -32,6 +37,7 @@ jar {
attributes 'Implementation-Version': version
attributes 'Implementation-Vendor': "Velocity Contributors"
attributes 'Multi-Release': 'true'
attributes 'Add-Opens': 'java.base/java.lang'
}
}

View File

@@ -1,7 +1,23 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
@@ -9,7 +25,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bstats.MetricsBase;
@@ -88,17 +103,17 @@ public class Metrics {
Metrics metrics = new Metrics(logger, 4752, metricsConfig.isEnabled());
metrics.addCustomChart(
new SingleLineChart("players", server::getPlayerCount)
new SingleLineChart("players", server::countConnectedPlayers)
);
metrics.addCustomChart(
new SingleLineChart("managed_servers", () -> server.getAllServers().size())
new SingleLineChart("managed_servers", () -> server.registeredServers().size())
);
metrics.addCustomChart(
new SimplePie("online_mode",
() -> server.getConfiguration().isOnlineMode() ? "online" : "offline")
() -> server.configuration().isOnlineMode() ? "online" : "offline")
);
metrics.addCustomChart(new SimplePie("velocity_version",
() -> server.getVersion().getVersion()));
() -> server.version().getVersion()));
metrics.addCustomChart(new DrilldownPie("java_version", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>();

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy;
import java.io.IOException;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy;
import io.netty.util.ResourceLeakDetector;
@@ -67,7 +84,7 @@ public class Velocity {
double bootTime = (System.currentTimeMillis() - startTime) / 1000d;
logger.info("Done ({}s)!", new DecimalFormat("#.##").format(bootTime));
server.getConsoleCommandSource().start();
server.consoleCommandSource().start();
// If we don't have a console available (because SimpleTerminalConsole returned), then we still
// need to wait, otherwise the JVM will reap us as no non-daemon threads will be active once the

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy;
import com.google.common.base.MoreObjects;
@@ -6,9 +23,9 @@ import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.velocitypowered.api.event.EventManager;
import com.velocitypowered.api.event.lifecycle.ProxyInitializeEvent;
import com.velocitypowered.api.event.lifecycle.ProxyReloadEvent;
import com.velocitypowered.api.event.lifecycle.ProxyShutdownEvent;
import com.velocitypowered.api.event.lifecycle.ProxyInitializeEventImpl;
import com.velocitypowered.api.event.lifecycle.ProxyReloadEventImpl;
import com.velocitypowered.api.event.lifecycle.ProxyShutdownEventImpl;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginManager;
@@ -37,6 +54,7 @@ import com.velocitypowered.proxy.scheduler.VelocityScheduler;
import com.velocitypowered.proxy.server.ServerMap;
import com.velocitypowered.proxy.util.AddressUtil;
import com.velocitypowered.proxy.util.EncryptionUtils;
import com.velocitypowered.proxy.util.FileSystemUtils;
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
import com.velocitypowered.proxy.util.bossbar.AdventureBossBarManager;
import com.velocitypowered.proxy.util.ratelimit.Ratelimiter;
@@ -61,6 +79,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@@ -73,7 +92,11 @@ import java.util.function.IntFunction;
import java.util.stream.Collectors;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.kyori.adventure.translation.TranslationRegistry;
import net.kyori.adventure.util.UTF8ResourceBundleControl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.asynchttpclient.AsyncHttpClient;
@@ -138,12 +161,12 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
}
@Override
public VelocityConfiguration getConfiguration() {
public VelocityConfiguration configuration() {
return this.configuration;
}
@Override
public ProxyVersion getVersion() {
public ProxyVersion version() {
Package pkg = VelocityServer.class.getPackage();
String implName;
String implVersion;
@@ -162,7 +185,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
}
@Override
public VelocityCommandManager getCommandManager() {
public VelocityCommandManager commandManager() {
return commandManager;
}
@@ -173,9 +196,11 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
@EnsuresNonNull({"serverKeyPair", "servers", "pluginManager", "eventManager", "scheduler",
"console", "cm", "configuration"})
void start() {
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
logger.info("Booting up {} {}...", version().getName(), version().getVersion());
console.setupStreams();
registerTranslations();
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
cm.logChannelInformation();
@@ -197,7 +222,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
// 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();
eventManager.fire(new ProxyInitializeEventImpl()).join();
// init console permissions after plugins are loaded
console.setupPermissions();
@@ -219,6 +244,47 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics());
}
private void registerTranslations() {
final TranslationRegistry translationRegistry = TranslationRegistry
.create(Key.key("velocity", "translations"));
translationRegistry.defaultLocale(Locale.US);
try {
FileSystemUtils.visitResources(VelocityServer.class, path -> {
logger.info("Loading localizations...");
try {
Files.walk(path).forEach(file -> {
if (!Files.isRegularFile(file)) {
return;
}
String filename = com.google.common.io.Files
.getNameWithoutExtension(file.getFileName().toString());
String localeName = filename.replace("messages_", "")
.replace("messages", "")
.replace('_', '-');
Locale locale;
if (localeName.isEmpty()) {
locale = Locale.US;
} else {
locale = Locale.forLanguageTag(localeName);
}
translationRegistry.registerAll(locale,
ResourceBundle.getBundle("com/velocitypowered/proxy/l10n/messages",
locale, UTF8ResourceBundleControl.get()), false);
});
} catch (IOException e) {
logger.error("Encountered an I/O error whilst loading translations", e);
}
}, "com", "velocitypowered", "proxy", "l10n");
} catch (IOException e) {
logger.error("Encountered an I/O error whilst loading translations", e);
return;
}
GlobalTranslator.get().addSource(translationRegistry);
}
@SuppressFBWarnings("DM_EXIT")
private void doStartupConfigLoad() {
try {
@@ -260,19 +326,19 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
}
// Register the plugin main classes so that we can fire the proxy initialize event
for (PluginContainer plugin : pluginManager.getPlugins()) {
Optional<?> instance = plugin.getInstance();
for (PluginContainer plugin : pluginManager.plugins()) {
Optional<?> instance = plugin.instance();
if (instance.isPresent()) {
try {
eventManager.register(instance.get(), instance.get());
eventManager.registerInternally(plugin, instance.get());
} catch (Exception e) {
logger.error("Unable to register plugin listener for {}",
plugin.getDescription().getName().orElse(plugin.getDescription().getId()), e);
plugin.description().name().orElse(plugin.description().id()), e);
}
}
}
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
logger.info("Loaded {} plugins", pluginManager.plugins().size());
}
public Bootstrap createBootstrap(@Nullable EventLoopGroup group, SocketAddress target) {
@@ -310,15 +376,15 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
Optional<RegisteredServer> rs = servers.getServer(entry.getKey());
if (!rs.isPresent()) {
servers.register(newInfo);
} else if (!rs.get().getServerInfo().equals(newInfo)) {
for (Player player : rs.get().getPlayersConnected()) {
} else if (!rs.get().serverInfo().equals(newInfo)) {
for (Player player : rs.get().connectedPlayers()) {
if (!(player instanceof ConnectedPlayer)) {
throw new IllegalStateException("ConnectedPlayer not found for player " + player
+ " in server " + rs.get().getServerInfo().getName());
+ " in server " + rs.get().serverInfo().name());
}
evacuate.add((ConnectedPlayer) player);
}
servers.unregister(rs.get().getServerInfo());
servers.unregister(rs.get().serverInfo());
servers.register(newInfo);
}
}
@@ -373,7 +439,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
ipAttemptLimiter = Ratelimiters.createWithMilliseconds(newConfiguration.getLoginRatelimit());
this.configuration = newConfiguration;
eventManager.fireAndForget(new ProxyReloadEvent());
eventManager.fireAndForget(new ProxyReloadEventImpl());
return true;
}
@@ -424,7 +490,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
}
try {
eventManager.fire(new ProxyShutdownEvent()).get(10, TimeUnit.SECONDS);
eventManager.fire(new ProxyShutdownEventImpl()).get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
timedOut = true;
} catch (ExecutionException e) {
@@ -501,9 +567,9 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
if (configuration.isOnlineMode() && configuration.isOnlineModeKickExistingPlayers()) {
return true;
}
String lowerName = connection.getUsername().toLowerCase(Locale.US);
String lowerName = connection.username().toLowerCase(Locale.US);
return !(connectionsByName.containsKey(lowerName)
|| connectionsByUuid.containsKey(connection.getUniqueId()));
|| connectionsByUuid.containsKey(connection.id()));
}
/**
@@ -512,25 +578,25 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
* @return {@code true} if we registered the connection, {@code false} if not
*/
public boolean registerConnection(ConnectedPlayer connection) {
String lowerName = connection.getUsername().toLowerCase(Locale.US);
String lowerName = connection.username().toLowerCase(Locale.US);
if (!this.configuration.isOnlineModeKickExistingPlayers()) {
if (connectionsByName.putIfAbsent(lowerName, connection) != null) {
return false;
}
if (connectionsByUuid.putIfAbsent(connection.getUniqueId(), connection) != null) {
if (connectionsByUuid.putIfAbsent(connection.id(), connection) != null) {
connectionsByName.remove(lowerName, connection);
return false;
}
} else {
ConnectedPlayer existing = connectionsByUuid.get(connection.getUniqueId());
ConnectedPlayer existing = connectionsByUuid.get(connection.id());
if (existing != null) {
existing.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login"));
}
// We can now replace the entries as needed.
connectionsByName.put(lowerName, connection);
connectionsByUuid.put(connection.getUniqueId(), connection);
connectionsByUuid.put(connection.id(), connection);
}
return true;
}
@@ -541,8 +607,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
* @param connection the connection to unregister
*/
public void unregisterConnection(ConnectedPlayer connection) {
connectionsByName.remove(connection.getUsername().toLowerCase(Locale.US), connection);
connectionsByUuid.remove(connection.getUniqueId(), connection);
connectionsByName.remove(connection.username().toLowerCase(Locale.US), connection);
connectionsByUuid.remove(connection.id(), connection);
bossBarManager.onDisconnect(connection);
}
@@ -562,7 +628,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
public Collection<Player> matchPlayer(String partialName) {
Objects.requireNonNull(partialName);
return getAllPlayers().stream().filter(p -> p.getUsername()
return connectedPlayers().stream().filter(p -> p.username()
.regionMatches(true, 0, partialName, 0, partialName.length()))
.collect(Collectors.toList());
}
@@ -571,28 +637,28 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
public Collection<RegisteredServer> matchServer(String partialName) {
Objects.requireNonNull(partialName);
return getAllServers().stream().filter(s -> s.getServerInfo().getName()
return registeredServers().stream().filter(s -> s.serverInfo().name()
.regionMatches(true, 0, partialName, 0, partialName.length()))
.collect(Collectors.toList());
}
@Override
public Collection<Player> getAllPlayers() {
public Collection<Player> connectedPlayers() {
return ImmutableList.copyOf(connectionsByUuid.values());
}
@Override
public int getPlayerCount() {
public int countConnectedPlayers() {
return connectionsByUuid.size();
}
@Override
public Optional<RegisteredServer> getServer(String name) {
public Optional<RegisteredServer> server(String name) {
return servers.getServer(name);
}
@Override
public Collection<RegisteredServer> getAllServers() {
public Collection<RegisteredServer> registeredServers() {
return servers.getAllServers();
}
@@ -607,44 +673,35 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
}
@Override
public VelocityConsole getConsoleCommandSource() {
public VelocityConsole consoleCommandSource() {
return console;
}
@Override
public PluginManager getPluginManager() {
public PluginManager pluginManager() {
return pluginManager;
}
@Override
public EventManager getEventManager() {
public EventManager eventManager() {
return eventManager;
}
@Override
public VelocityScheduler getScheduler() {
public VelocityScheduler scheduler() {
return scheduler;
}
@Override
public VelocityChannelRegistrar getChannelRegistrar() {
public VelocityChannelRegistrar channelRegistrar() {
return channelRegistrar;
}
@Override
public SocketAddress getBoundAddress() {
if (configuration == null) {
throw new IllegalStateException(
"No configuration"); // even though you'll never get the chance... heh, heh
}
return configuration.getBind();
}
@Override
public @NonNull Iterable<? extends Audience> audiences() {
Collection<Audience> audiences = new ArrayList<>(this.getPlayerCount() + 1);
Collection<Audience> audiences = new ArrayList<>(this.countConnectedPlayers() + 1);
audiences.add(this.console);
audiences.addAll(this.getAllPlayers());
audiences.addAll(this.connectedPlayers());
return audiences;
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command;
import com.mojang.brigadier.context.CommandContext;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command;
import com.mojang.brigadier.context.CommandContext;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions;
@@ -17,6 +34,7 @@ import com.velocitypowered.api.command.RawCommand;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
import com.velocitypowered.api.event.command.CommandExecuteEventImpl;
import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.util.BrigadierUtils;
import java.util.Iterator;
@@ -38,13 +56,13 @@ public class VelocityCommandManager implements CommandManager {
}
@Override
public CommandMeta.Builder metaBuilder(final String alias) {
public CommandMeta.Builder createMetaBuilder(final String alias) {
Preconditions.checkNotNull(alias, "alias");
return new VelocityCommandMeta.Builder(alias);
}
@Override
public CommandMeta.Builder metaBuilder(final BrigadierCommand command) {
public CommandMeta.Builder createMetaBuilder(final BrigadierCommand command) {
Preconditions.checkNotNull(command, "command");
return new VelocityCommandMeta.Builder(command.getNode().getName());
}
@@ -52,7 +70,7 @@ public class VelocityCommandManager implements CommandManager {
@Override
public void register(final BrigadierCommand command) {
Preconditions.checkNotNull(command, "command");
register(metaBuilder(command).build(), command);
register(createMetaBuilder(command).build(), command);
}
@Override
@@ -60,7 +78,7 @@ public class VelocityCommandManager implements CommandManager {
Preconditions.checkNotNull(meta, "meta");
Preconditions.checkNotNull(command, "command");
Iterator<String> aliasIterator = meta.getAliases().iterator();
Iterator<String> aliasIterator = meta.aliases().iterator();
String primaryAlias = aliasIterator.next();
LiteralCommandNode<CommandSource> node = null;
@@ -76,7 +94,7 @@ public class VelocityCommandManager implements CommandManager {
}
if (!(command instanceof BrigadierCommand)) {
for (CommandNode<CommandSource> hint : meta.getHints()) {
for (CommandNode<CommandSource> hint : meta.hints()) {
node.addChild(BrigadierUtils.wrapForHinting(hint, node.getCommand()));
}
}
@@ -100,7 +118,7 @@ public class VelocityCommandManager implements CommandManager {
}
/**
* Fires a {@link CommandExecuteEvent}.
* Fires a {@link CommandExecuteEventImpl}.
*
* @param source the source to execute the command for
* @param cmdLine the command to execute
@@ -110,7 +128,7 @@ public class VelocityCommandManager implements CommandManager {
final String cmdLine) {
Preconditions.checkNotNull(source, "source");
Preconditions.checkNotNull(cmdLine, "cmdLine");
return eventManager.fire(new CommandExecuteEvent(source, cmdLine));
return eventManager.fire(new CommandExecuteEventImpl(source, cmdLine));
}
private boolean executeImmediately0(final CommandSource source, final String cmdLine) {
@@ -142,11 +160,11 @@ public class VelocityCommandManager implements CommandManager {
Preconditions.checkNotNull(cmdLine, "cmdLine");
return callCommandEvent(source, cmdLine).thenApplyAsync(event -> {
CommandResult commandResult = event.getResult();
CommandResult commandResult = event.result();
if (commandResult.isForwardToServer() || !commandResult.isAllowed()) {
return false;
}
return executeImmediately0(source, commandResult.getCommand().orElse(event.getCommand()));
return executeImmediately0(source, commandResult.modifiedCommand().orElse(event.rawCommand()));
}, eventManager.getAsyncExecutor());
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions;
@@ -59,12 +76,12 @@ final class VelocityCommandMeta implements CommandMeta {
}
@Override
public Collection<String> getAliases() {
public Collection<String> aliases() {
return aliases;
}
@Override
public Collection<CommandNode<CommandSource>> getHints() {
public Collection<CommandNode<CommandSource>> hints() {
return hints;
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command;
import com.google.common.base.Preconditions;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command;
import com.mojang.brigadier.context.CommandContext;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command.builtin;
import com.velocitypowered.api.proxy.ProxyServer;
@@ -14,8 +31,8 @@ class BuiltinCommandUtil {
}
static List<RegisteredServer> sortedServerList(ProxyServer proxy) {
List<RegisteredServer> servers = new ArrayList<>(proxy.getAllServers());
servers.sort(Comparator.comparing(RegisteredServer::getServerInfo));
List<RegisteredServer> servers = new ArrayList<>(proxy.registeredServers());
servers.sort(Comparator.comparing(RegisteredServer::serverInfo));
return Collections.unmodifiableList(servers);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command.builtin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.format.NamedTextColor;
public class CommandMessages {
public static final TranslatableComponent PLAYERS_ONLY = Component.translatable(
"velocity.command.players-only", NamedTextColor.RED);
public static final TranslatableComponent SERVER_DOES_NOT_EXIST = Component.translatable(
"velocity.command.server-does-not-exist", NamedTextColor.RED);
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command.builtin;
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
@@ -20,6 +37,7 @@ import java.util.Optional;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.format.NamedTextColor;
public class GlistCommand {
@@ -39,14 +57,14 @@ public class GlistCommand {
LiteralCommandNode<CommandSource> totalNode = LiteralArgumentBuilder
.<CommandSource>literal("glist")
.requires(source ->
source.getPermissionValue("velocity.command.glist") == Tristate.TRUE)
source.evaluatePermission("velocity.command.glist") == Tristate.TRUE)
.executes(this::totalCount)
.build();
ArgumentCommandNode<CommandSource, String> serverNode = RequiredArgumentBuilder
.<CommandSource, String>argument(SERVER_ARG, StringArgumentType.string())
.suggests((context, builder) -> {
for (RegisteredServer server : server.getAllServers()) {
builder.suggest(server.getServerInfo().getName());
for (RegisteredServer server : server.registeredServers()) {
builder.suggest(server.serverInfo().name());
}
builder.suggest("all");
return builder.buildFuture();
@@ -54,18 +72,14 @@ public class GlistCommand {
.executes(this::serverCount)
.build();
totalNode.addChild(serverNode);
server.getCommandManager().register(new BrigadierCommand(totalNode));
server.commandManager().register(new BrigadierCommand(totalNode));
}
private int totalCount(final CommandContext<CommandSource> context) {
final CommandSource source = context.getSource();
sendTotalProxyCount(source);
source.sendMessage(Identity.nil(),
Component.text().content("To view all players on servers, use ")
.color(NamedTextColor.YELLOW)
.append(Component.text("/glist all", NamedTextColor.DARK_AQUA))
.append(Component.text(".", NamedTextColor.YELLOW))
.build());
Component.translatable("velocity.command.glist-view-all", NamedTextColor.YELLOW));
return 1;
}
@@ -78,10 +92,10 @@ public class GlistCommand {
}
sendTotalProxyCount(source);
} else {
Optional<RegisteredServer> registeredServer = server.getServer(serverName);
Optional<RegisteredServer> registeredServer = server.server(serverName);
if (!registeredServer.isPresent()) {
source.sendMessage(Identity.nil(),
Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED));
CommandMessages.SERVER_DOES_NOT_EXIST.args(Component.text(serverName)));
return -1;
}
sendServerPlayers(source, registeredServer.get(), false);
@@ -90,21 +104,22 @@ public class GlistCommand {
}
private void sendTotalProxyCount(CommandSource target) {
target.sendMessage(Identity.nil(), Component.text()
.content("There are ").color(NamedTextColor.YELLOW)
.append(Component.text(server.getAllPlayers().size(), NamedTextColor.GREEN))
.append(Component.text(" player(s) online.", NamedTextColor.YELLOW))
.build());
int online = server.countConnectedPlayers();
TranslatableComponent msg = online == 1
? Component.translatable("velocity.command.glist-player-singular")
: Component.translatable("velocity.command.glist-player-plural");
target.sendMessage(msg.color(NamedTextColor.YELLOW)
.args(Component.text(Integer.toString(online), NamedTextColor.GREEN)));
}
private void sendServerPlayers(CommandSource target, RegisteredServer server, boolean fromAll) {
List<Player> onServer = ImmutableList.copyOf(server.getPlayersConnected());
List<Player> onServer = ImmutableList.copyOf(server.connectedPlayers());
if (onServer.isEmpty() && fromAll) {
return;
}
TextComponent.Builder builder = Component.text()
.append(Component.text("[" + server.getServerInfo().getName() + "] ",
.append(Component.text("[" + server.serverInfo().name() + "] ",
NamedTextColor.DARK_AQUA))
.append(Component.text("(" + onServer.size() + ")", NamedTextColor.GRAY))
.append(Component.text(": "))
@@ -112,7 +127,7 @@ public class GlistCommand {
for (int i = 0; i < onServer.size(); i++) {
Player player = onServer.get(i);
builder.append(Component.text(player.getUsername()));
builder.append(Component.text(player.username()));
if (i + 1 < onServer.size()) {
builder.append(Component.text(", "));

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command.builtin;
import static net.kyori.adventure.text.event.HoverEvent.showText;
@@ -18,6 +35,7 @@ import java.util.stream.Stream;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -36,8 +54,7 @@ public class ServerCommand implements SimpleCommand {
final String[] args = invocation.arguments();
if (!(source instanceof Player)) {
source.sendMessage(Identity.nil(), Component.text("Only players may run this command.",
NamedTextColor.RED));
source.sendMessage(Identity.nil(), CommandMessages.PLAYERS_ONLY);
return;
}
@@ -45,10 +62,10 @@ public class ServerCommand implements SimpleCommand {
if (args.length == 1) {
// Trying to connect to a server.
String serverName = args[0];
Optional<RegisteredServer> toConnect = server.getServer(serverName);
Optional<RegisteredServer> toConnect = server.server(serverName);
if (!toConnect.isPresent()) {
player.sendMessage(Identity.nil(),
Component.text("Server " + serverName + " doesn't exist.", NamedTextColor.RED));
player.sendMessage(Identity.nil(), CommandMessages.SERVER_DOES_NOT_EXIST
.args(Component.text(serverName)));
return;
}
@@ -59,21 +76,25 @@ public class ServerCommand implements SimpleCommand {
}
private void outputServerInformation(Player executor) {
String currentServer = executor.getCurrentServer().map(ServerConnection::getServerInfo)
.map(ServerInfo::getName).orElse("<unknown>");
executor.sendMessage(Identity.nil(), Component.text(
"You are currently connected to " + currentServer + ".", NamedTextColor.YELLOW));
String currentServer = executor.connectedServer().map(ServerConnection::serverInfo)
.map(ServerInfo::name).orElse("<unknown>");
executor.sendMessage(Identity.nil(), Component.translatable(
"velocity.command.server-current-server",
NamedTextColor.YELLOW,
Component.text(currentServer)));
List<RegisteredServer> servers = BuiltinCommandUtil.sortedServerList(server);
if (servers.size() > MAX_SERVERS_TO_LIST) {
executor.sendMessage(Identity.nil(), Component.text(
"Too many servers to list. Tab-complete to show all servers.", NamedTextColor.RED));
executor.sendMessage(Identity.nil(), Component.translatable(
"velocity.command.server-too-many", NamedTextColor.RED));
return;
}
// Assemble the list of servers as components
TextComponent.Builder serverListBuilder = Component.text().content("Available servers: ")
.color(NamedTextColor.YELLOW);
TextComponent.Builder serverListBuilder = Component.text()
.append(Component.translatable("velocity.command.server-available",
NamedTextColor.YELLOW))
.append(Component.space());
for (int i = 0; i < servers.size(); i++) {
RegisteredServer rs = servers.get(i);
serverListBuilder.append(formatServerComponent(currentServer, rs));
@@ -86,20 +107,34 @@ public class ServerCommand implements SimpleCommand {
}
private TextComponent formatServerComponent(String currentPlayerServer, RegisteredServer server) {
ServerInfo serverInfo = server.getServerInfo();
TextComponent serverTextComponent = Component.text(serverInfo.getName());
ServerInfo serverInfo = server.serverInfo();
TextComponent serverTextComponent = Component.text(serverInfo.name());
String playersText = server.getPlayersConnected().size() + " player(s) online";
if (serverInfo.getName().equals(currentPlayerServer)) {
int connectedPlayers = server.connectedPlayers().size();
TranslatableComponent playersTextComponent;
if (connectedPlayers == 1) {
playersTextComponent = Component.translatable("velocity.command.server-tooltip-player-online");
} else {
playersTextComponent = Component.translatable("velocity.command.server-tooltip-players-online");
}
playersTextComponent = playersTextComponent.args(Component.text(connectedPlayers));
if (serverInfo.name().equals(currentPlayerServer)) {
serverTextComponent = serverTextComponent.color(NamedTextColor.GREEN)
.hoverEvent(
showText(Component.text("Currently connected to this server\n" + playersText))
showText(
Component.translatable("velocity.command.server-tooltip-current-server")
.append(Component.newline())
.append(playersTextComponent))
);
} else {
serverTextComponent = serverTextComponent.color(NamedTextColor.GRAY)
.clickEvent(ClickEvent.runCommand("/server " + serverInfo.getName()))
.clickEvent(ClickEvent.runCommand("/server " + serverInfo.name()))
.hoverEvent(
showText(Component.text("Click to connect to this server\n" + playersText))
showText(
Component.translatable("velocity.command.server-tooltip-offer-connect-server")
.append(Component.newline())
.append(playersTextComponent))
);
}
return serverTextComponent;
@@ -108,8 +143,8 @@ public class ServerCommand implements SimpleCommand {
@Override
public List<String> suggest(final SimpleCommand.Invocation invocation) {
final String[] currentArgs = invocation.arguments();
Stream<String> possibilities = server.getAllServers().stream()
.map(rs -> rs.getServerInfo().getName());
Stream<String> possibilities = server.registeredServers().stream()
.map(rs -> rs.serverInfo().name());
if (currentArgs.length == 0) {
return possibilities.collect(Collectors.toList());
@@ -124,6 +159,6 @@ public class ServerCommand implements SimpleCommand {
@Override
public boolean hasPermission(final SimpleCommand.Invocation invocation) {
return invocation.source().getPermissionValue("velocity.command.server") != Tristate.FALSE;
return invocation.source().evaluatePermission("velocity.command.server") != Tristate.FALSE;
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command.builtin;
import com.velocitypowered.api.command.RawCommand;
@@ -24,6 +41,6 @@ public class ShutdownCommand implements RawCommand {
@Override
public boolean hasPermission(final Invocation invocation) {
return invocation.source() == server.getConsoleCommandSource();
return invocation.source() == server.consoleCommandSource();
}
}

View File

@@ -1,9 +1,25 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.command.builtin;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.JsonArray;
@@ -20,7 +36,6 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.ProxyVersion;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.util.InformationUtils;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
@@ -31,10 +46,10 @@ import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -166,24 +181,22 @@ public class VelocityCommand implements SimpleCommand {
public void execute(CommandSource source, String @NonNull [] args) {
try {
if (server.reloadConfiguration()) {
source.sendMessage(Identity.nil(), Component.text(
"Configuration reloaded.", NamedTextColor.GREEN));
source.sendMessage(Component.translatable("velocity.command.reload-success",
NamedTextColor.GREEN));
} else {
source.sendMessage(Identity.nil(), Component.text(
"Unable to reload your configuration. Check the console for more details.",
source.sendMessage(Component.translatable("velocity.command.reload-failure",
NamedTextColor.RED));
}
} catch (Exception e) {
logger.error("Unable to reload configuration", e);
source.sendMessage(Identity.nil(), Component.text(
"Unable to reload your configuration. Check the console for more details.",
source.sendMessage(Component.translatable("velocity.command.reload-failure",
NamedTextColor.RED));
}
}
@Override
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
return source.getPermissionValue("velocity.command.reload") == Tristate.TRUE;
return source.evaluatePermission("velocity.command.reload") == Tristate.TRUE;
}
}
@@ -203,41 +216,43 @@ public class VelocityCommand implements SimpleCommand {
return;
}
ProxyVersion version = server.getVersion();
ProxyVersion version = server.version();
TextComponent velocity = Component.text().content(version.getName() + " ")
Component velocity = Component.text().content(version.getName() + " ")
.decoration(TextDecoration.BOLD, true)
.color(VELOCITY_COLOR)
.append(Component.text(version.getVersion()).decoration(TextDecoration.BOLD, false))
.build();
TextComponent copyright = Component
.text("Copyright 2018-2021 " + version.getVendor() + ". " + version.getName()
+ " is freely licensed under the terms of the MIT License.");
source.sendMessage(Identity.nil(), velocity);
source.sendMessage(Identity.nil(), copyright);
Component copyright = Component
.translatable("velocity.command.version-copyright",
Component.text(version.getVendor()),
Component.text(version.getName()));
source.sendMessage(velocity);
source.sendMessage(copyright);
if (version.getName().equals("Velocity")) {
TextComponent velocityWebsite = Component.text()
.content("Visit the ")
.append(Component.text().content("Velocity website")
TextComponent embellishment = Component.text()
.append(Component.text().content("velocitypowered.com")
.color(NamedTextColor.GREEN)
.decoration(TextDecoration.UNDERLINED, true)
.clickEvent(
ClickEvent.openUrl("https://www.velocitypowered.com"))
.build())
.append(Component.text(" or the "))
.append(Component.text().content("Velocity GitHub")
.append(Component.text(" - "))
.append(Component.text().content("GitHub")
.color(NamedTextColor.GREEN)
.decoration(TextDecoration.UNDERLINED, true)
.clickEvent(ClickEvent.openUrl(
"https://github.com/VelocityPowered/Velocity"))
.build())
.build();
source.sendMessage(Identity.nil(), velocityWebsite);
source.sendMessage(Identity.nil(), embellishment);
}
}
@Override
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
return source.getPermissionValue("velocity.command.info") != Tristate.FALSE;
return source.evaluatePermission("velocity.command.info") != Tristate.FALSE;
}
}
@@ -256,20 +271,21 @@ public class VelocityCommand implements SimpleCommand {
return;
}
List<PluginContainer> plugins = ImmutableList.copyOf(server.getPluginManager().getPlugins());
List<PluginContainer> plugins = ImmutableList.copyOf(server.pluginManager().plugins());
int pluginCount = plugins.size();
if (pluginCount == 0) {
source.sendMessage(Identity.nil(), Component.text(
"No plugins installed.", NamedTextColor.YELLOW));
source.sendMessage(Component.translatable("velocity.command.no-plugins",
NamedTextColor.YELLOW));
return;
}
TextComponent.Builder output = Component.text().content("Plugins: ")
TranslatableComponent.Builder output = Component.translatable()
.key("velocity.command.plugins-list")
.color(NamedTextColor.YELLOW);
for (int i = 0; i < pluginCount; i++) {
PluginContainer plugin = plugins.get(i);
output.append(componentForPlugin(plugin.getDescription()));
output.append(componentForPlugin(plugin.description()));
if (i + 1 < pluginCount) {
output.append(Component.text(", "));
}
@@ -279,37 +295,43 @@ public class VelocityCommand implements SimpleCommand {
}
private TextComponent componentForPlugin(PluginDescription description) {
String pluginInfo = description.getName().orElse(description.getId())
+ description.getVersion().map(v -> " " + v).orElse("");
String pluginInfo = description.name().orElse(description.id())
+ description.version().map(v -> " " + v).orElse("");
TextComponent.Builder hoverText = Component.text().content(pluginInfo);
description.getUrl().ifPresent(url -> {
description.url().ifPresent(url -> {
hoverText.append(Component.newline());
hoverText.append(Component.text("Website: " + url));
hoverText.append(Component.translatable(
"velocity.command.plugin-tooltip-website",
Component.text(url)));
});
if (!description.getAuthors().isEmpty()) {
if (!description.authors().isEmpty()) {
hoverText.append(Component.newline());
if (description.getAuthors().size() == 1) {
hoverText.append(Component.text("Author: " + description.getAuthors().get(0)));
if (description.authors().size() == 1) {
hoverText.append(Component.translatable("velocity.command.plugin-tooltip-author",
Component.text(description.authors().get(0))));
} else {
hoverText.append(Component.text("Authors: " + Joiner.on(", ")
.join(description.getAuthors())));
hoverText.append(
Component.translatable("velocity.command.plugin-tooltip-authors",
Component.text(String.join(", ", description.authors()))
)
);
}
}
description.getDescription().ifPresent(pdesc -> {
description.description().ifPresent(pdesc -> {
hoverText.append(Component.newline());
hoverText.append(Component.newline());
hoverText.append(Component.text(pdesc));
});
return Component.text(description.getId(), NamedTextColor.GRAY)
return Component.text(description.id(), NamedTextColor.GRAY)
.hoverEvent(HoverEvent.showText(hoverText.build()));
}
@Override
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
return source.evaluatePermission("velocity.command.plugins") == Tristate.TRUE;
}
}
@@ -329,39 +351,39 @@ public class VelocityCommand implements SimpleCommand {
return;
}
Collection<RegisteredServer> allServers = ImmutableSet.copyOf(server.getAllServers());
Collection<RegisteredServer> allServers = ImmutableSet.copyOf(server.registeredServers());
JsonObject servers = new JsonObject();
for (RegisteredServer iter : allServers) {
servers.add(iter.getServerInfo().getName(),
servers.add(iter.serverInfo().name(),
InformationUtils.collectServerInfo(iter));
}
JsonArray connectOrder = new JsonArray();
List<String> attemptedConnectionOrder = ImmutableList.copyOf(
server.getConfiguration().getAttemptConnectionOrder());
for (int i = 0; i < attemptedConnectionOrder.size(); i++) {
connectOrder.add(attemptedConnectionOrder.get(i));
server.configuration().getAttemptConnectionOrder());
for (String s : attemptedConnectionOrder) {
connectOrder.add(s);
}
JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.getConfiguration());
JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.configuration());
proxyConfig.add("servers", servers);
proxyConfig.add("connectOrder", connectOrder);
proxyConfig.add("forcedHosts",
InformationUtils.collectForcedHosts(server.getConfiguration()));
InformationUtils.collectForcedHosts(server.configuration()));
JsonObject dump = new JsonObject();
dump.add("versionInfo", InformationUtils.collectProxyInfo(server.getVersion()));
dump.add("versionInfo", InformationUtils.collectProxyInfo(server.version()));
dump.add("platform", InformationUtils.collectEnvironmentInfo());
dump.add("config", proxyConfig);
dump.add("plugins", InformationUtils.collectPluginInfo(server));
source.sendMessage(Component.text().content("Uploading gathered information...").build());
source.sendMessage(Component.translatable("velocity.command.dump-uploading"));
AsyncHttpClient httpClient = ((VelocityServer) server).getAsyncHttpClient();
BoundRequestBuilder request =
httpClient.preparePost("https://dump.velocitypowered.com/documents");
request.setHeader("Content-Type", "text/plain");
request.addHeader("User-Agent", server.getVersion().getName() + "/"
+ server.getVersion().getVersion());
request.addHeader("User-Agent", server.version().getName() + "/"
+ server.version().getVersion());
request.setBody(
InformationUtils.toHumanReadableString(dump).getBytes(StandardCharsets.UTF_8));
@@ -370,12 +392,8 @@ public class VelocityCommand implements SimpleCommand {
try {
Response response = future.get();
if (response.getStatusCode() != 200) {
source.sendMessage(Component.text()
.content("An error occurred while communicating with the Velocity servers. "
+ "The servers may be temporarily unavailable or there is an issue "
+ "with your network settings. You can find more information in the "
+ "log or console of your Velocity server.")
.color(NamedTextColor.RED).build());
source.sendMessage(Component.translatable("velocity.command.dump-send-error",
NamedTextColor.RED));
logger.error("Invalid status code while POST-ing Velocity dump: "
+ response.getStatusCode());
logger.error("Headers: \n--------------BEGIN HEADERS--------------\n"
@@ -390,62 +408,35 @@ public class VelocityCommand implements SimpleCommand {
}
String url = "https://dump.velocitypowered.com/"
+ key.get("key").getAsString() + ".json";
source.sendMessage(Component.text()
.content("Created an anonymised report containing useful information about "
+ "this proxy. If a developer requested it, you may share the "
+ "following link with them:")
source.sendMessage(Component.translatable("velocity.command.dump-success")
.append(Component.newline())
.append(Component.text(">> " + url)
.color(NamedTextColor.GREEN)
.clickEvent(ClickEvent.openUrl(url)))
.append(Component.newline())
.append(Component.text("Note: This link is only valid for a few days")
.color(NamedTextColor.GRAY)
).build());
} catch (InterruptedException e) {
source.sendMessage(Component.text()
.content("Could not complete the request, the command was interrupted."
+ "Please refer to the proxy-log or console for more information.")
.color(NamedTextColor.RED).build());
logger.error("Failed to complete dump command, "
+ "the executor was interrupted: " + e.getMessage());
e.printStackTrace();
} catch (ExecutionException e) {
TextComponent.Builder message = Component.text()
.content("An error occurred while attempting to upload the gathered "
+ "information to the Velocity servers.")
.append(Component.newline())
.color(NamedTextColor.RED);
if (e.getCause() instanceof UnknownHostException
|| e.getCause() instanceof ConnectException) {
message.append(Component.text(
"Likely cause: Invalid system DNS settings or no internet connection"));
}
source.sendMessage(message
.append(Component.newline()
.append(Component.text(
"Error details can be found in the proxy log / console"))
).build());
logger.error("Failed to complete dump command, "
+ "the executor encountered an Exception: " + e.getCause().getMessage());
e.getCause().printStackTrace();
.append(Component.translatable("velocity.command.dump-will-expire",
NamedTextColor.GRAY)));
} catch (JsonParseException e) {
source.sendMessage(Component.text()
.content("An error occurred on the Velocity-servers and the dump could not "
+ "be completed. Please contact the Velocity staff about this problem. "
+ "If you do, provide the details about this error from the Velocity "
+ "console or server log.")
.color(NamedTextColor.RED).build());
source.sendMessage(Component.translatable("velocity.command.dump-server-error"));
logger.error("Invalid response from the Velocity servers: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
Component message = Component.translatable("velocity.command.dump-send-error")
.append(Component.newline())
.color(NamedTextColor.RED);
if (e.getCause() instanceof UnknownHostException
|| e.getCause() instanceof ConnectException) {
message = message.append(Component.translatable("velocity.command.dump-offline"));
}
source.sendMessage(message);
logger.error("Failed to complete dump command", Throwables.getRootCause(e));
}
}, MoreExecutors.directExecutor());
}
@Override
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
return source.evaluatePermission("velocity.command.plugins") == Tristate.TRUE;
}
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.config;
public enum PingPassthroughMode {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.config;
public enum PlayerInfoForwarding {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.config;
import com.electronwill.nightconfig.core.CommentedConfig;
@@ -11,12 +28,10 @@ import com.google.gson.annotations.Expose;
import com.velocitypowered.api.proxy.config.ProxyConfig;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.proxy.util.AddressUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.nio.charset.StandardCharsets;
@@ -27,10 +42,10 @@ import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.apache.logging.log4j.LogManager;
@@ -57,25 +72,23 @@ public class VelocityConfiguration implements ProxyConfig {
@Expose private final Advanced advanced;
@Expose private final Query query;
private final Metrics metrics;
private final Messages messages;
private net.kyori.adventure.text.@MonotonicNonNull Component motdAsComponent;
private @Nullable Favicon favicon;
private VelocityConfiguration(Servers servers, ForcedHosts forcedHosts, Advanced advanced,
Query query, Metrics metrics, Messages messages) {
Query query, Metrics metrics) {
this.servers = servers;
this.forcedHosts = forcedHosts;
this.advanced = advanced;
this.query = query;
this.metrics = metrics;
this.messages = messages;
}
private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode,
boolean preventClientProxyConnections, boolean announceForge,
PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret,
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough, Servers servers,
ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics, Messages messages) {
ForcedHosts forcedHosts, Advanced advanced, Query query, Metrics metrics) {
this.bind = bind;
this.motd = motd;
this.showMaxPlayers = showMaxPlayers;
@@ -91,7 +104,6 @@ public class VelocityConfiguration implements ProxyConfig {
this.advanced = advanced;
this.query = query;
this.metrics = metrics;
this.messages = messages;
}
/**
@@ -358,10 +370,6 @@ public class VelocityConfiguration implements ProxyConfig {
return advanced.isLogCommandExecutions();
}
public Messages getMessages() {
return messages;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
@@ -435,7 +443,6 @@ public class VelocityConfiguration implements ProxyConfig {
CommentedConfig advancedConfig = config.get("advanced");
CommentedConfig queryConfig = config.get("query");
CommentedConfig metricsConfig = config.get("metrics");
CommentedConfig messagesConfig = config.get("messages");
PlayerInfoForwarding forwardingMode = config.getEnumOrElse("player-info-forwarding-mode",
PlayerInfoForwarding.NONE);
PingPassthroughMode pingPassthroughMode = config.getEnumOrElse("ping-passthrough",
@@ -455,8 +462,8 @@ public class VelocityConfiguration implements ProxyConfig {
motd,
maxPlayers,
onlineMode,
announceForge,
preventClientProxyConnections,
announceForge,
forwardingMode,
forwardingSecret,
kickExisting,
@@ -465,8 +472,7 @@ public class VelocityConfiguration implements ProxyConfig {
new ForcedHosts(forcedHostsConfig),
new Advanced(advancedConfig),
new Query(queryConfig),
new Metrics(metricsConfig),
new Messages(messagesConfig, defaultConfig.get("messages"))
new Metrics(metricsConfig)
);
}
@@ -573,9 +579,11 @@ public class VelocityConfiguration implements ProxyConfig {
Map<String, List<String>> forcedHosts = new HashMap<>();
for (UnmodifiableConfig.Entry entry : config.entrySet()) {
if (entry.getValue() instanceof String) {
forcedHosts.put(entry.getKey(), ImmutableList.of(entry.getValue()));
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
ImmutableList.of(entry.getValue()));
} else if (entry.getValue() instanceof List) {
forcedHosts.put(entry.getKey(), ImmutableList.copyOf((List<String>) entry.getValue()));
forcedHosts.put(entry.getKey().toLowerCase(Locale.ROOT),
ImmutableList.copyOf((List<String>) entry.getValue()));
} else {
throw new IllegalStateException(
"Invalid value of type " + entry.getValue().getClass() + " in forced hosts!");
@@ -778,73 +786,4 @@ public class VelocityConfiguration implements ProxyConfig {
return enabled;
}
}
public static class Messages {
private final CommentedConfig toml;
private final CommentedConfig defaultToml;
private final String kickPrefix;
private final String disconnectPrefix;
private final String onlineModeOnly;
private final String noAvailableServers;
private final String alreadyConnected;
private final String movedToNewServerPrefix;
private final String genericConnectionError;
private Messages(CommentedConfig toml, CommentedConfig defaultToml) {
this.toml = toml;
this.defaultToml = defaultToml;
this.kickPrefix = getString("kick-prefix");
this.disconnectPrefix = getString("disconnect-prefix");
this.onlineModeOnly = getString("online-mode-only");
this.noAvailableServers = getString("no-available-servers");
this.alreadyConnected = getString("already-connected");
this.movedToNewServerPrefix = getString("moved-to-new-server-prefix");
this.genericConnectionError = getString("generic-connection-error");
}
private String getString(String path) {
String def = defaultToml.getOrElse(path, "");
if (toml == null) {
return def;
}
return toml.getOrElse(path, def);
}
public Component getKickPrefix(String server) {
return deserialize(String.format(kickPrefix, server));
}
public Component getDisconnectPrefix(String server) {
return deserialize(String.format(disconnectPrefix, server));
}
public Component getOnlineModeOnly() {
return deserialize(onlineModeOnly);
}
public Component getNoAvailableServers() {
return deserialize(noAvailableServers);
}
public Component getAlreadyConnected() {
return deserialize(alreadyConnected);
}
public Component getMovedToNewServerPrefix() {
return deserialize(movedToNewServerPrefix);
}
public Component getGenericConnectionError() {
return deserialize(genericConnectionError);
}
private Component deserialize(String str) {
if (str.startsWith("{")) {
return GsonComponentSerializer.gson().deserialize(str);
}
return LegacyComponentSerializer.legacyAmpersand().deserialize(str);
}
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection;
import com.velocitypowered.api.util.GameProfile;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection;
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection;
import static com.velocitypowered.proxy.network.HandlerNames.CIPHER_DECODER;
@@ -25,7 +42,7 @@ import com.velocitypowered.proxy.network.packet.clientbound.ClientboundSetCompre
import com.velocitypowered.proxy.network.pipeline.MinecraftCipherDecoder;
import com.velocitypowered.proxy.network.pipeline.MinecraftCipherEncoder;
import com.velocitypowered.proxy.network.pipeline.MinecraftCompressDecoder;
import com.velocitypowered.proxy.network.pipeline.MinecraftCompressEncoder;
import com.velocitypowered.proxy.network.pipeline.MinecraftCompressorAndLengthEncoder;
import com.velocitypowered.proxy.network.pipeline.MinecraftDecoder;
import com.velocitypowered.proxy.network.pipeline.MinecraftEncoder;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
@@ -391,18 +408,19 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
} else {
MinecraftCompressDecoder decoder = (MinecraftCompressDecoder) channel.pipeline()
.get(COMPRESSION_DECODER);
MinecraftCompressEncoder encoder = (MinecraftCompressEncoder) channel.pipeline()
.get(COMPRESSION_ENCODER);
MinecraftCompressorAndLengthEncoder encoder =
(MinecraftCompressorAndLengthEncoder) channel.pipeline().get(COMPRESSION_ENCODER);
if (decoder != null && encoder != null) {
decoder.setThreshold(threshold);
encoder.setThreshold(threshold);
} else {
int level = server.getConfiguration().getCompressionLevel();
int level = server.configuration().getCompressionLevel();
VelocityCompressor compressor = Natives.compress.get().create(level);
encoder = new MinecraftCompressEncoder(threshold, compressor);
encoder = new MinecraftCompressorAndLengthEncoder(threshold, compressor);
decoder = new MinecraftCompressDecoder(threshold, compressor);
channel.pipeline().remove(FRAME_ENCODER);
channel.pipeline().addBefore(MINECRAFT_DECODER, COMPRESSION_DECODER, decoder);
channel.pipeline().addBefore(MINECRAFT_ENCODER, COMPRESSION_ENCODER, encoder);
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection;
public interface MinecraftConnectionAssociation {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection;
import com.velocitypowered.proxy.network.packet.Packet;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection;
public class VelocityConstants {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.backend;
import static com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder.getBungeeCordChannel;
@@ -7,10 +24,11 @@ import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.event.command.PlayerAvailableCommandsEvent;
import com.velocitypowered.api.event.command.PlayerAvailableCommandsEventImpl;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.connection.PluginMessageEventImpl;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
@@ -41,17 +59,21 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
private static final Logger logger = LogManager.getLogger(BackendPlaySessionHandler.class);
private static final boolean BACKPRESSURE_LOG = Boolean
.getBoolean("velocity.log-server-backpressure");
private static final int MAXIMUM_PACKETS_TO_FLUSH = Integer
.getInteger("velocity.max-packets-per-flush", 8192);
private final VelocityServer server;
private final VelocityServerConnection serverConn;
private final ClientPlaySessionHandler playerSessionHandler;
private final MinecraftConnection playerConnection;
private final BungeeCordMessageResponder bungeecordMessageResponder;
private boolean exceptionTriggered = false;
private int packetsFlushed;
BackendPlaySessionHandler(VelocityServer server, VelocityServerConnection serverConn) {
this.server = server;
this.serverConn = serverConn;
this.playerConnection = serverConn.getPlayer().getConnection();
this.playerConnection = serverConn.player().getConnection();
MinecraftSessionHandler psh = playerConnection.getSessionHandler();
if (!(psh instanceof ClientPlaySessionHandler)) {
@@ -61,14 +83,14 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
this.playerSessionHandler = (ClientPlaySessionHandler) psh;
this.bungeecordMessageResponder = new BungeeCordMessageResponder(server,
serverConn.getPlayer());
serverConn.player());
}
@Override
public void activated() {
serverConn.getServer().addPlayer(serverConn.getPlayer());
serverConn.target().addPlayer(serverConn.player());
if (server.getConfiguration().isBungeePluginChannelEnabled()) {
if (server.configuration().isBungeePluginChannelEnabled()) {
MinecraftConnection serverMc = serverConn.ensureConnected();
serverMc.write(PluginMessageUtil.constructChannelsPacket(serverMc.getProtocolVersion(),
ImmutableList.of(getBungeeCordChannel(serverMc.getProtocolVersion())),
@@ -95,7 +117,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ClientboundDisconnectPacket packet) {
serverConn.disconnect();
serverConn.getPlayer().handleConnectionException(serverConn.getServer(), packet, true);
serverConn.player().handleConnectionException(serverConn.target(), packet, true);
return true;
}
@@ -115,7 +137,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
return true;
}
if (!serverConn.getPlayer().canForwardPluginMessage(serverConn.ensureConnected()
if (!serverConn.player().canForwardPluginMessage(serverConn.ensureConnected()
.getProtocolVersion(), packet)) {
return true;
}
@@ -123,36 +145,36 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
// We need to specially handle REGISTER and UNREGISTER packets. Later on, we'll write them to
// the client.
if (PluginMessageUtil.isRegister(packet)) {
serverConn.getPlayer().getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
serverConn.player().getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
return false;
} else if (PluginMessageUtil.isUnregister(packet)) {
serverConn.getPlayer().getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
serverConn.player().getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
return false;
}
if (PluginMessageUtil.isMcBrand(packet)) {
AbstractPluginMessagePacket<?> rewritten = PluginMessageUtil.rewriteMinecraftBrand(packet,
server.getVersion(), playerConnection.getProtocolVersion(), ClientboundPluginMessagePacket.FACTORY);
server.version(), playerConnection.getProtocolVersion(), ClientboundPluginMessagePacket.FACTORY);
playerConnection.write(rewritten);
return true;
}
if (serverConn.getPhase().handle(serverConn, serverConn.getPlayer(), packet)) {
if (serverConn.getPhase().handle(serverConn, serverConn.player(), packet)) {
// Handled.
return true;
}
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
PluginChannelId id = server.channelRegistrar().getFromId(packet.getChannel());
if (id == null) {
return false;
}
byte[] copy = ByteBufUtil.getBytes(packet.content());
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), id,
PluginMessageEvent event = new PluginMessageEventImpl(serverConn, serverConn.player(), id,
copy);
server.getEventManager().fire(event)
server.eventManager().fire(event)
.thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed() && !playerConnection.isClosed()) {
if (pme.result().isAllowed() && !playerConnection.isClosed()) {
ClientboundPluginMessagePacket copied = new ClientboundPluginMessagePacket(packet.getChannel(),
Unpooled.wrappedBuffer(copy));
playerConnection.write(copied);
@@ -173,18 +195,18 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ClientboundPlayerListItemPacket packet) {
serverConn.getPlayer().getTabList().processBackendPacket(packet);
serverConn.player().tabList().processBackendPacket(packet);
return false; //Forward packet to player
}
@Override
public boolean handle(ClientboundAvailableCommandsPacket commands) {
RootCommandNode<CommandSource> rootNode = commands.getRootNode();
if (server.getConfiguration().isAnnounceProxyCommands()) {
if (server.configuration().isAnnounceProxyCommands()) {
// Inject commands from the proxy.
RootCommandNode<CommandSource> dispatcherRootNode =
(RootCommandNode<CommandSource>)
filterNode(server.getCommandManager().getDispatcher().getRoot());
filterNode(server.commandManager().getDispatcher().getRoot());
assert dispatcherRootNode != null : "Filtering root node returned null.";
Collection<CommandNode<CommandSource>> proxyNodes = dispatcherRootNode.getChildren();
for (CommandNode<CommandSource> node : proxyNodes) {
@@ -196,8 +218,8 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
}
}
server.getEventManager().fire(
new PlayerAvailableCommandsEvent(serverConn.getPlayer(), rootNode))
server.eventManager().fire(
new PlayerAvailableCommandsEventImpl(serverConn.player(), rootNode))
.thenAcceptAsync(event -> playerConnection.write(commands), playerConnection.eventLoop())
.exceptionally((ex) -> {
logger.error("Exception while handling available commands for {}", playerConnection, ex);
@@ -220,7 +242,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
} else {
if (source.getRequirement() != null) {
try {
if (!source.getRequirement().test(serverConn.getPlayer())) {
if (!source.getRequirement().test(serverConn.player())) {
return null;
}
} catch (Throwable e) {
@@ -256,22 +278,31 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
((AbstractPluginMessagePacket<?>) packet).retain();
}
playerConnection.delayedWrite(packet);
if (++packetsFlushed >= MAXIMUM_PACKETS_TO_FLUSH) {
playerConnection.flush();
packetsFlushed = 0;
}
}
@Override
public void handleUnknown(ByteBuf buf) {
playerConnection.delayedWrite(buf.retain());
if (++packetsFlushed >= MAXIMUM_PACKETS_TO_FLUSH) {
playerConnection.flush();
packetsFlushed = 0;
}
}
@Override
public void readCompleted() {
playerConnection.flush();
packetsFlushed = 0;
}
@Override
public void exception(Throwable throwable) {
exceptionTriggered = true;
serverConn.getPlayer().handleConnectionException(serverConn.getServer(), throwable,
serverConn.player().handleConnectionException(serverConn.target(), throwable,
!(throwable instanceof ReadTimeoutException));
}
@@ -281,14 +312,14 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
@Override
public void disconnected() {
serverConn.getServer().removePlayer(serverConn.getPlayer());
serverConn.target().removePlayer(serverConn.player());
if (!serverConn.isGracefulDisconnect() && !exceptionTriggered) {
if (server.getConfiguration().isFailoverOnUnexpectedServerDisconnect()) {
serverConn.getPlayer().handleConnectionException(serverConn.getServer(),
if (server.configuration().isFailoverOnUnexpectedServerDisconnect()) {
serverConn.player().handleConnectionException(serverConn.target(),
ClientboundDisconnectPacket.create(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR,
ProtocolVersion.MINECRAFT_1_16), true);
} else {
serverConn.getPlayer().disconnect(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
serverConn.player().disconnect(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
}
}
}

View File

@@ -1,9 +1,26 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.connection.Player;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PairedPluginChannelId;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.VelocityServer;
@@ -23,6 +40,7 @@ import java.net.SocketAddress;
import java.util.Optional;
import java.util.StringJoiner;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
@@ -33,10 +51,8 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+ "nothing.")
public class BungeeCordMessageResponder {
private static final MinecraftChannelIdentifier MODERN_CHANNEL = MinecraftChannelIdentifier
.create("bungeecord", "main");
private static final LegacyChannelIdentifier LEGACY_CHANNEL =
new LegacyChannelIdentifier("BungeeCord");
private static final PairedPluginChannelId CHANNEL = PluginChannelId
.withLegacy("BungeeCord", Key.key("bungeecord", "main"));
private final VelocityServer proxy;
private final ConnectedPlayer player;
@@ -47,13 +63,13 @@ public class BungeeCordMessageResponder {
}
public static boolean isBungeeCordMessage(AbstractPluginMessagePacket<?> message) {
return MODERN_CHANNEL.getId().equals(message.getChannel()) || LEGACY_CHANNEL.getId()
.equals(message.getChannel());
return CHANNEL.modernChannelKey().asString().equals(message.getChannel())
|| CHANNEL.legacyChannel().equals(message.getChannel());
}
private void processConnect(ByteBufDataInput in) {
String serverName = in.readUTF();
proxy.getServer(serverName).ifPresent(server -> player.createConnectionRequest(server)
proxy.server(serverName).ifPresent(server -> player.createConnectionRequest(server)
.fireAndForget());
}
@@ -62,7 +78,7 @@ public class BungeeCordMessageResponder {
String serverName = in.readUTF();
Optional<Player> referencedPlayer = proxy.getPlayer(playerName);
Optional<RegisteredServer> referencedServer = proxy.getServer(serverName);
Optional<RegisteredServer> referencedServer = proxy.server(serverName);
if (referencedPlayer.isPresent() && referencedServer.isPresent()) {
referencedPlayer.get().createConnectionRequest(referencedServer.get()).fireAndForget();
}
@@ -73,7 +89,7 @@ public class BungeeCordMessageResponder {
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("IP");
SocketAddress address = player.getRemoteAddress();
SocketAddress address = player.remoteAddress();
if (address instanceof InetSocketAddress) {
InetSocketAddress serverInetAddr = (InetSocketAddress) address;
out.writeUTF(serverInetAddr.getHostString());
@@ -93,12 +109,12 @@ public class BungeeCordMessageResponder {
if (target.equals("ALL")) {
out.writeUTF("PlayerCount");
out.writeUTF("ALL");
out.writeInt(proxy.getPlayerCount());
out.writeInt(proxy.countConnectedPlayers());
} else {
proxy.getServer(target).ifPresent(rs -> {
int playersOnServer = rs.getPlayersConnected().size();
proxy.server(target).ifPresent(rs -> {
int playersOnServer = rs.connectedPlayers().size();
out.writeUTF("PlayerCount");
out.writeUTF(rs.getServerInfo().getName());
out.writeUTF(rs.serverInfo().name());
out.writeInt(playersOnServer);
});
}
@@ -120,18 +136,18 @@ public class BungeeCordMessageResponder {
out.writeUTF("ALL");
StringJoiner joiner = new StringJoiner(", ");
for (Player online : proxy.getAllPlayers()) {
joiner.add(online.getUsername());
for (Player online : proxy.connectedPlayers()) {
joiner.add(online.username());
}
out.writeUTF(joiner.toString());
} else {
proxy.getServer(target).ifPresent(info -> {
proxy.server(target).ifPresent(info -> {
out.writeUTF("PlayerList");
out.writeUTF(info.getServerInfo().getName());
out.writeUTF(info.serverInfo().name());
StringJoiner joiner = new StringJoiner(", ");
for (Player online : info.getPlayersConnected()) {
joiner.add(online.getUsername());
for (Player online : info.connectedPlayers()) {
joiner.add(online.username());
}
out.writeUTF(joiner.toString());
});
@@ -146,8 +162,8 @@ public class BungeeCordMessageResponder {
private void processGetServers() {
StringJoiner joiner = new StringJoiner(", ");
for (RegisteredServer server : proxy.getAllServers()) {
joiner.add(server.getServerInfo().getName());
for (RegisteredServer server : proxy.registeredServers()) {
joiner.add(server.serverInfo().name());
}
ByteBuf buf = Unpooled.buffer();
@@ -185,7 +201,7 @@ public class BungeeCordMessageResponder {
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("GetServer");
out.writeUTF(player.ensureAndGetCurrentServer().getServerInfo().getName());
out.writeUTF(player.ensureAndGetCurrentServer().serverInfo().name());
sendResponseOnConnection(buf);
}
@@ -195,7 +211,7 @@ public class BungeeCordMessageResponder {
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("UUID");
out.writeUTF(UuidUtils.toUndashed(player.getUniqueId()));
out.writeUTF(UuidUtils.toUndashed(player.id()));
sendResponseOnConnection(buf);
}
@@ -206,8 +222,8 @@ public class BungeeCordMessageResponder {
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("UUIDOther");
out.writeUTF(player.getUsername());
out.writeUTF(UuidUtils.toUndashed(player.getUniqueId()));
out.writeUTF(player.username());
out.writeUTF(UuidUtils.toUndashed(player.id()));
sendResponseOnConnection(buf);
});
@@ -219,8 +235,8 @@ public class BungeeCordMessageResponder {
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("IPOther");
out.writeUTF(player.getUsername());
SocketAddress address = player.getRemoteAddress();
out.writeUTF(player.username());
SocketAddress address = player.remoteAddress();
if (address instanceof InetSocketAddress) {
InetSocketAddress serverInetAddr = (InetSocketAddress) address;
out.writeUTF(serverInetAddr.getHostString());
@@ -235,13 +251,13 @@ public class BungeeCordMessageResponder {
}
private void processServerIp(ByteBufDataInput in) {
proxy.getServer(in.readUTF()).ifPresent(info -> {
proxy.server(in.readUTF()).ifPresent(info -> {
ByteBuf buf = Unpooled.buffer();
ByteBufDataOutput out = new ByteBufDataOutput(buf);
out.writeUTF("ServerIP");
out.writeUTF(info.getServerInfo().getName());
SocketAddress address = info.getServerInfo().getAddress();
out.writeUTF(info.serverInfo().name());
SocketAddress address = info.serverInfo().address();
if (address instanceof InetSocketAddress) {
InetSocketAddress serverInetAddr = (InetSocketAddress) address;
out.writeUTF(serverInetAddr.getHostString());
@@ -275,17 +291,17 @@ public class BungeeCordMessageResponder {
ByteBuf toForward = in.unwrap().copy();
if (target.equals("ALL")) {
try {
for (RegisteredServer rs : proxy.getAllServers()) {
((VelocityRegisteredServer) rs).sendPluginMessage(LEGACY_CHANNEL,
for (RegisteredServer rs : proxy.registeredServers()) {
((VelocityRegisteredServer) rs).sendPluginMessage(CHANNEL,
toForward.retainedSlice());
}
} finally {
toForward.release();
}
} else {
Optional<RegisteredServer> server = proxy.getServer(target);
Optional<RegisteredServer> server = proxy.server(target);
if (server.isPresent()) {
((VelocityRegisteredServer) server.get()).sendPluginMessage(LEGACY_CHANNEL, toForward);
((VelocityRegisteredServer) server.get()).sendPluginMessage(CHANNEL, toForward);
} else {
toForward.release();
}
@@ -293,8 +309,8 @@ public class BungeeCordMessageResponder {
}
static String getBungeeCordChannel(ProtocolVersion version) {
return version.gte(ProtocolVersion.MINECRAFT_1_13) ? MODERN_CHANNEL.getId()
: LEGACY_CHANNEL.getId();
return version.gte(ProtocolVersion.MINECRAFT_1_13) ? CHANNEL.modernChannelKey().asString()
: CHANNEL.legacyChannel();
}
// Note: this method will always release the buffer!
@@ -311,7 +327,7 @@ public class BungeeCordMessageResponder {
}
boolean process(AbstractPluginMessagePacket<?> message) {
if (!proxy.getConfiguration().isBungeePluginChannelEnabled()) {
if (!proxy.configuration().isBungeePluginChannelEnabled()) {
return false;
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.api.util.GameProfile;
@@ -33,8 +50,8 @@ import net.kyori.adventure.text.TextComponent;
public class LoginSessionHandler implements MinecraftSessionHandler {
private static final TextComponent MODERN_IP_FORWARDING_FAILURE = Component
.text("Your server did not send a forwarding request to the proxy. Is it set up correctly?");
private static final Component MODERN_IP_FORWARDING_FAILURE = Component
.translatable("velocity.error.modern-forwarding-failed");
private final VelocityServer server;
private final VelocityServerConnection serverConn;
@@ -56,12 +73,12 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ClientboundLoginPluginMessagePacket packet) {
MinecraftConnection mc = serverConn.ensureConnected();
VelocityConfiguration configuration = server.getConfiguration();
VelocityConfiguration configuration = server.configuration();
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN && packet
.getChannel().equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) {
ByteBuf forwardingData = createForwardingData(configuration.getForwardingSecret(),
cleanRemoteAddress(serverConn.getPlayer().getRemoteAddress()),
serverConn.getPlayer().getGameProfile());
cleanRemoteAddress(serverConn.player().remoteAddress()),
serverConn.player().gameProfile());
ServerboundLoginPluginResponsePacket response = new ServerboundLoginPluginResponsePacket(
packet.getId(), true, forwardingData);
mc.write(response);
@@ -76,7 +93,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ClientboundDisconnectPacket packet) {
resultFuture.complete(ConnectionRequestResults.forDisconnect(packet, serverConn.getServer()));
resultFuture.complete(ConnectionRequestResults.forDisconnect(packet, serverConn.target()));
serverConn.disconnect();
return true;
}
@@ -89,10 +106,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ClientboundServerLoginSuccessPacket packet) {
if (server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN
if (server.configuration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN
&& !informationForwarded) {
resultFuture.complete(ConnectionRequestResults.forDisconnect(MODERN_IP_FORWARDING_FAILURE,
serverConn.getServer()));
serverConn.target()));
serverConn.disconnect();
return true;
}
@@ -116,7 +133,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public void disconnected() {
if (server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.LEGACY) {
if (server.configuration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.LEGACY) {
resultFuture.completeExceptionally(
new QuietRuntimeException("The connection to the remote server was unexpectedly closed.\n"
+ "This is usually because the remote server does not have BungeeCord IP forwarding "

View File

@@ -1,10 +1,27 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.backend;
import static com.velocitypowered.proxy.connection.backend.BackendConnectionPhases.IN_TRANSITION;
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeHandshakeBackendPhase.HELLO;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
import com.velocitypowered.api.event.player.ServerConnectedEventImpl;
import com.velocitypowered.api.event.player.ServerPostConnectEventImpl;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.ConnectionTypes;
import com.velocitypowered.proxy.connection.MinecraftConnection;
@@ -68,9 +85,9 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ClientboundJoinGamePacket packet) {
MinecraftConnection smc = serverConn.ensureConnected();
VelocityServerConnection existingConnection = serverConn.getPlayer().getConnectedServer();
VelocityServerConnection existingConnection = serverConn.player().getConnectedServer();
final ConnectedPlayer player = serverConn.getPlayer();
final ConnectedPlayer player = serverConn.player();
if (existingConnection != null) {
// Shut down the existing server connection.
@@ -83,10 +100,10 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
// The goods are in hand! We got JoinGame. Let's transition completely to the new state.
smc.setAutoReading(false);
server.getEventManager()
.fire(new ServerConnectedEvent(player, serverConn.getServer(),
existingConnection != null ? existingConnection.getServer() : null))
.whenCompleteAsync((x, error) -> {
server.eventManager()
.fire(new ServerConnectedEventImpl(player, serverConn.target(),
existingConnection != null ? existingConnection.target() : null))
.thenRunAsync(() -> {
// Make sure we can still transition (player might have disconnected here).
if (!serverConn.isActive()) {
// Connection is obsolete.
@@ -112,17 +129,17 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
smc.setAutoReading(true);
// Now set the connected server.
serverConn.getPlayer().setConnectedServer(serverConn);
serverConn.player().setConnectedServer(serverConn);
// We're done! :)
server.getEventManager().fireAndForget(new ServerPostConnectEvent(player,
existingConnection == null ? null : existingConnection.getServer()));
resultFuture.complete(ConnectionRequestResults.successful(serverConn.getServer()));
server.eventManager().fireAndForget(new ServerPostConnectEventImpl(player,
existingConnection == null ? null : existingConnection.target()));
resultFuture.complete(ConnectionRequestResults.successful(serverConn.target()));
}, smc.eventLoop())
.exceptionally(exc -> {
logger.error("Unable to switch to new server {} for {}",
serverConn.getServerInfo().getName(),
player.getUsername(), exc);
serverConn.serverInfo().name(),
player.username(), exc);
player.disconnect(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
resultFuture.completeExceptionally(exc);
return null;
@@ -141,9 +158,9 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
if (connection.getType() == ConnectionTypes.LEGACY_FORGE
&& !serverConn.getPhase().consideredComplete()) {
resultFuture.complete(ConnectionRequestResults.forUnsafeDisconnect(packet,
serverConn.getServer()));
serverConn.target()));
} else {
resultFuture.complete(ConnectionRequestResults.forDisconnect(packet, serverConn.getServer()));
resultFuture.complete(ConnectionRequestResults.forDisconnect(packet, serverConn.target()));
}
return true;
@@ -151,35 +168,35 @@ public class TransitionSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ClientboundPluginMessagePacket packet) {
if (!serverConn.getPlayer().canForwardPluginMessage(serverConn.ensureConnected()
if (!serverConn.player().canForwardPluginMessage(serverConn.ensureConnected()
.getProtocolVersion(), packet)) {
return true;
}
if (PluginMessageUtil.isRegister(packet)) {
serverConn.getPlayer().getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
serverConn.player().getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
} else if (PluginMessageUtil.isUnregister(packet)) {
serverConn.getPlayer().getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
serverConn.player().getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
}
// We always need to handle plugin messages, for Forge compatibility.
if (serverConn.getPhase().handle(serverConn, serverConn.getPlayer(), packet)) {
if (serverConn.getPhase().handle(serverConn, serverConn.player(), packet)) {
// Handled, but check the server connection phase.
if (serverConn.getPhase() == HELLO) {
VelocityServerConnection existingConnection = serverConn.getPlayer().getConnectedServer();
VelocityServerConnection existingConnection = serverConn.player().getConnectedServer();
if (existingConnection != null && existingConnection.getPhase() != IN_TRANSITION) {
// Indicate that this connection is "in transition"
existingConnection.setConnectionPhase(IN_TRANSITION);
// Tell the player that we're leaving and we just aren't coming back.
existingConnection.getPhase().onDepartForNewServer(existingConnection,
serverConn.getPlayer());
serverConn.player());
}
}
return true;
}
serverConn.getPlayer().getConnection().write(packet.retain());
serverConn.player().getConnection().write(packet.retain());
return true;
}

View File

@@ -1,14 +1,32 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.backend;
import static com.velocitypowered.proxy.VelocityServer.GENERAL_GSON;
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN;
import static com.velocitypowered.proxy.network.HandlerNames.HANDLER;
import static com.velocitypowered.proxy.network.PluginMessageUtil.channelIdForVersion;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.connection.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import com.velocitypowered.api.proxy.player.ConnectionRequestBuilder;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.util.GameProfile.Property;
@@ -74,7 +92,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
CompletableFuture<Impl> result = new CompletableFuture<>();
// Note: we use the event loop for the connection the player is on. This reduces context
// switches.
SocketAddress destinationAddress = registeredServer.getServerInfo().getAddress();
SocketAddress destinationAddress = registeredServer.serverInfo().address();
server.createBootstrap(proxyPlayer.getConnection().eventLoop(), destinationAddress)
.handler(server.getBackendChannelInitializer())
.connect(destinationAddress)
@@ -100,27 +118,27 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
return result;
}
private String getHandshakeRemoteAddress() {
return proxyPlayer.getVirtualHost().map(InetSocketAddress::getHostString).orElse("");
private String playerConnectedHostname() {
return proxyPlayer.connectedHostname().map(InetSocketAddress::getHostString).orElse("");
}
private String createLegacyForwardingAddress(UnaryOperator<List<Property>> propertiesTransform) {
// BungeeCord IP forwarding is simply a special injection after the "address" in the handshake,
// separated by \0 (the null byte). In order, you send the original host, the player's IP, their
// UUID (undashed), and if you are in online-mode, their login properties (from Mojang).
SocketAddress playerRemoteAddress = proxyPlayer.getRemoteAddress();
SocketAddress playerRemoteAddress = proxyPlayer.remoteAddress();
if (!(playerRemoteAddress instanceof InetSocketAddress)) {
return getHandshakeRemoteAddress();
return playerConnectedHostname();
}
StringBuilder data = new StringBuilder()
.append(getHandshakeRemoteAddress())
.append(playerConnectedHostname())
.append('\0')
.append(((InetSocketAddress) proxyPlayer.getRemoteAddress()).getHostString())
.append(((InetSocketAddress) proxyPlayer.remoteAddress()).getHostString())
.append('\0')
.append(proxyPlayer.getGameProfile().getUndashedId())
.append(proxyPlayer.gameProfile().getUndashedId())
.append('\0');
GENERAL_GSON
.toJson(propertiesTransform.apply(proxyPlayer.getGameProfile().getProperties()), data);
.toJson(propertiesTransform.apply(proxyPlayer.gameProfile().getProperties()), data);
return data.toString();
}
@@ -140,36 +158,37 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
private void startHandshake() {
final MinecraftConnection mc = ensureConnected();
PlayerInfoForwarding forwardingMode = server.getConfiguration().getPlayerInfoForwardingMode();
PlayerInfoForwarding forwardingMode = server.configuration().getPlayerInfoForwardingMode();
// Initiate the handshake.
ProtocolVersion protocolVersion = proxyPlayer.getConnection().getProtocolVersion();
ServerboundHandshakePacket handshake = new ServerboundHandshakePacket();
handshake.setNextStatus(StateRegistry.LOGIN_ID);
handshake.setProtocolVersion(protocolVersion);
if (forwardingMode == PlayerInfoForwarding.LEGACY) {
handshake.setServerAddress(createLegacyForwardingAddress());
} else if (forwardingMode == PlayerInfoForwarding.BUNGEEGUARD) {
byte[] secret = server.getConfiguration().getForwardingSecret();
handshake.setServerAddress(createBungeeGuardForwardingAddress(secret));
} else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) {
handshake.setServerAddress(getHandshakeRemoteAddress() + HANDSHAKE_HOSTNAME_TOKEN);
} else {
handshake.setServerAddress(getHandshakeRemoteAddress());
}
SocketAddress destinationAddr = registeredServer.getServerInfo().getAddress();
if (destinationAddr instanceof InetSocketAddress) {
handshake.setPort(((InetSocketAddress) destinationAddr).getPort());
}
String address = getHandshakeAddressField(forwardingMode);
SocketAddress destinationAddr = registeredServer.serverInfo().address();
int port = destinationAddr instanceof InetSocketAddress
? ((InetSocketAddress) destinationAddr).getPort() : 0;
ServerboundHandshakePacket handshake = new ServerboundHandshakePacket(protocolVersion,
address, port, StateRegistry.LOGIN_ID);
mc.delayedWrite(handshake);
mc.setProtocolVersion(protocolVersion);
mc.setState(StateRegistry.LOGIN);
mc.delayedWrite(new ServerboundServerLoginPacket(proxyPlayer.getUsername()));
mc.delayedWrite(new ServerboundServerLoginPacket(proxyPlayer.username()));
mc.flush();
}
private String getHandshakeAddressField(PlayerInfoForwarding forwardingMode) {
if (forwardingMode == PlayerInfoForwarding.LEGACY) {
return createLegacyForwardingAddress();
} else if (forwardingMode == PlayerInfoForwarding.BUNGEEGUARD) {
byte[] secret = server.configuration().getForwardingSecret();
return createBungeeGuardForwardingAddress(secret);
} else if (proxyPlayer.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) {
return playerConnectedHostname() + HANDSHAKE_HOSTNAME_TOKEN;
} else {
return playerConnectedHostname();
}
}
public @Nullable MinecraftConnection getConnection() {
return connection;
}
@@ -187,17 +206,17 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
}
@Override
public VelocityRegisteredServer getServer() {
public VelocityRegisteredServer target() {
return registeredServer;
}
@Override
public ServerInfo getServerInfo() {
return registeredServer.getServerInfo();
public ServerInfo serverInfo() {
return registeredServer.serverInfo();
}
@Override
public ConnectedPlayer getPlayer() {
public ConnectedPlayer player() {
return proxyPlayer;
}
@@ -214,12 +233,12 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
@Override
public String toString() {
return "[server connection] " + proxyPlayer.getGameProfile().getName() + " -> "
+ registeredServer.getServerInfo().getName();
return "[server connection] " + proxyPlayer.gameProfile().getName() + " -> "
+ registeredServer.serverInfo().name();
}
@Override
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
public boolean sendPluginMessage(PluginChannelId identifier, byte[] data) {
return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data));
}
@@ -229,13 +248,14 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
* @param data the data
* @return whether or not the message was sent
*/
public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) {
public boolean sendPluginMessage(PluginChannelId identifier, ByteBuf data) {
Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data");
MinecraftConnection mc = ensureConnected();
ServerboundPluginMessagePacket message = new ServerboundPluginMessagePacket(identifier.getId(), data);
ServerboundPluginMessagePacket message = new ServerboundPluginMessagePacket(
channelIdForVersion(identifier, mc.getProtocolVersion()), data);
mc.write(message);
return true;
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
/**

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
@@ -8,14 +25,14 @@ import static com.velocitypowered.proxy.network.PluginMessageUtil.constructChann
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent;
import com.velocitypowered.api.event.connection.PluginMessageEventImpl;
import com.velocitypowered.api.event.player.PlayerChannelRegisterEventImpl;
import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.event.player.TabCompleteEvent;
import com.velocitypowered.api.event.player.PlayerChatEventImpl;
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEventImpl;
import com.velocitypowered.api.event.player.TabCompleteEventImpl;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.ConnectionTypes;
import com.velocitypowered.proxy.connection.MinecraftConnection;
@@ -53,6 +70,7 @@ import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.apache.logging.log4j.LogManager;
@@ -86,10 +104,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
@Override
public void activated() {
Collection<String> channels = server.getChannelRegistrar().getChannelsForProtocol(player
.getProtocolVersion());
Collection<String> channels = server.channelRegistrar().getChannelsForProtocol(player
.protocolVersion());
if (!channels.isEmpty()) {
AbstractPluginMessagePacket<?> register = constructChannelsPacket(player.getProtocolVersion(),
AbstractPluginMessagePacket<?> register = constructChannelsPacket(player.protocolVersion(),
channels, ClientboundPluginMessagePacket.FACTORY);
player.getConnection().write(register);
player.getKnownChannels().addAll(channels);
@@ -139,29 +157,28 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
String msg = packet.getMessage();
if (msg.startsWith("/")) {
String originalCommand = msg.substring(1);
server.getCommandManager().callCommandEvent(player, msg.substring(1))
server.commandManager().callCommandEvent(player, msg.substring(1))
.thenComposeAsync(event -> processCommandExecuteResult(originalCommand,
event.getResult()))
event.result()))
.whenComplete((ignored, throwable) -> {
if (server.getConfiguration().isLogCommandExecutions()) {
if (server.configuration().isLogCommandExecutions()) {
logger.info("{} -> executed command /{}", player, originalCommand);
}
})
.exceptionally(e -> {
logger.info("Exception occurred while running command for {}",
player.getUsername(), e);
player.sendMessage(Identity.nil(),
Component.text("An error occurred while running this command.",
NamedTextColor.RED));
player.username(), e);
player.sendMessage(Component.translatable("velocity.command.generic-error",
NamedTextColor.RED));
return null;
});
} else {
PlayerChatEvent event = new PlayerChatEvent(player, msg);
server.getEventManager().fire(event)
PlayerChatEvent event = new PlayerChatEventImpl(player, msg);
server.eventManager().fire(event)
.thenAcceptAsync(pme -> {
PlayerChatEvent.ChatResult chatResult = pme.getResult();
PlayerChatEventImpl.ChatResult chatResult = pme.result();
if (chatResult.isAllowed()) {
Optional<String> eventMsg = pme.getResult().getMessage();
Optional<String> eventMsg = pme.result().modifiedMessage();
if (eventMsg.isPresent()) {
smc.write(new ServerboundChatPacket(eventMsg.get()));
} else {
@@ -199,23 +216,28 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
} else if (PluginMessageUtil.isRegister(packet)) {
List<String> channels = PluginMessageUtil.getChannels(packet);
player.getKnownChannels().addAll(channels);
List<ChannelIdentifier> channelIdentifiers = new ArrayList<>();
for (String channel : channels) {
try {
channelIdentifiers.add(MinecraftChannelIdentifier.from(channel));
} catch (IllegalArgumentException e) {
channelIdentifiers.add(new LegacyChannelIdentifier(channel));
List<PluginChannelId> pluginChannelIds = new ArrayList<>();
if (player.protocolVersion().gte(MINECRAFT_1_13)) {
for (String channel : channels) {
pluginChannelIds.add(PluginChannelId.wrap(Key.key(channel)));
}
} else {
for (String channel : channels) {
pluginChannelIds.add(PluginChannelId.withLegacy(channel,
Key.key(PluginMessageUtil.transformLegacyToModernChannel(channel))));
}
}
server.getEventManager().fireAndForget(new PlayerChannelRegisterEvent(player,
ImmutableList.copyOf(channelIdentifiers)));
server.eventManager().fireAndForget(new PlayerChannelRegisterEventImpl(player,
ImmutableList.copyOf(pluginChannelIds)));
backendConn.write(packet.retain());
} else if (PluginMessageUtil.isUnregister(packet)) {
player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
backendConn.write(packet.retain());
} else if (PluginMessageUtil.isMcBrand(packet)) {
backendConn.write(PluginMessageUtil
.rewriteMinecraftBrand(packet, server.getVersion(), player.getProtocolVersion(), ServerboundPluginMessagePacket.FACTORY));
.rewriteMinecraftBrand(packet, server.version(), player.protocolVersion(), ServerboundPluginMessagePacket.FACTORY));
} else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) {
return true;
} else {
@@ -240,14 +262,14 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// appropriately.
loginPluginMessages.add(packet.retain());
} else {
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
PluginChannelId id = server.channelRegistrar().getFromId(packet.getChannel());
if (id == null) {
backendConn.write(packet.retain());
} else {
byte[] copy = ByteBufUtil.getBytes(packet.content());
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, copy);
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
if (pme.getResult().isAllowed()) {
PluginMessageEvent event = new PluginMessageEventImpl(player, serverConn, id, copy);
server.eventManager().fire(event).thenAcceptAsync(pme -> {
if (pme.result().isAllowed()) {
ServerboundPluginMessagePacket message = new ServerboundPluginMessagePacket(packet.getChannel(),
Unpooled.wrappedBuffer(copy));
backendConn.write(message);
@@ -269,7 +291,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ServerboundResourcePackResponsePacket packet) {
server.getEventManager().fireAndForget(new PlayerResourcePackStatusEvent(player,
server.eventManager().fireAndForget(new PlayerResourcePackStatusEventImpl(player,
packet.getStatus()));
return false;
}
@@ -312,7 +334,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
@Override
public void exception(Throwable throwable) {
player.disconnect(server.getConfiguration().getMessages().getGenericConnectionError());
player.disconnect(Component.translatable("velocity.error.player-connection-error",
NamedTextColor.RED));
}
@Override
@@ -353,7 +376,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
player.getPhase().onFirstJoin(player);
} else {
// Clear tab list to avoid duplicate entries
player.getTabList().clearAll();
player.tabList().clearAll();
if (player.getConnection().getType() == ConnectionTypes.LEGACY_FORGE) {
this.doSafeClientServerSwitch(joinGame);
} else {
@@ -385,9 +408,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
}
// Clear any title from the previous server.
if (player.getProtocolVersion().gte(MINECRAFT_1_8)) {
if (player.protocolVersion().gte(MINECRAFT_1_8)) {
player.getConnection()
.delayedWrite(ClientboundTitlePacket.reset(player.getProtocolVersion()));
.delayedWrite(ClientboundTitlePacket.reset(player.protocolVersion()));
}
// Flush everything
@@ -406,7 +429,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// to perform entity ID rewrites, eliminating potential issues from rewriting packets and
// improving compatibility with mods.
int sentOldDim = joinGame.getDimension();
if (player.getProtocolVersion().lt(MINECRAFT_1_16)) {
if (player.protocolVersion().lt(MINECRAFT_1_16)) {
// Before Minecraft 1.16, we could not switch to the same dimension without sending an
// additional respawn. On older versions of Minecraft this forces the client to perform
// garbage collection which adds additional latency.
@@ -458,8 +481,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
}
String commandLabel = command.substring(0, commandEndPosition);
if (!server.getCommandManager().hasCommand(commandLabel)) {
if (player.getProtocolVersion().lt(MINECRAFT_1_13)) {
if (!server.commandManager().hasCommand(commandLabel)) {
if (player.protocolVersion().lt(MINECRAFT_1_13)) {
// Outstanding tab completes are recorded for use with 1.12 clients and below to provide
// additional tab completion support.
outstandingTabComplete = packet;
@@ -467,7 +490,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
return false;
}
server.getCommandManager().offerSuggestions(player, command)
server.commandManager().offerSuggestions(player, command)
.thenAcceptAsync(suggestions -> {
if (suggestions.isEmpty()) {
return;
@@ -496,7 +519,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
}
private boolean handleRegularTabComplete(ServerboundTabCompleteRequestPacket packet) {
if (player.getProtocolVersion().lt(MINECRAFT_1_13)) {
if (player.protocolVersion().lt(MINECRAFT_1_13)) {
// Outstanding tab completes are recorded for use with 1.12 clients and below to provide
// additional tab completion support.
outstandingTabComplete = packet;
@@ -526,9 +549,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
private void finishCommandTabComplete(ServerboundTabCompleteRequestPacket request,
ClientboundTabCompleteResponsePacket response) {
String command = request.getCommand().substring(1);
server.getCommandManager().offerSuggestions(player, command)
server.commandManager().offerSuggestions(player, command)
.thenAcceptAsync(offers -> {
boolean legacy = player.getProtocolVersion().lt(MINECRAFT_1_13);
boolean legacy = player.protocolVersion().lt(MINECRAFT_1_13);
try {
for (String offer : offers) {
offer = legacy && !offer.startsWith("/") ? "/" + offer : offer;
@@ -541,7 +564,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
player.getConnection().write(response);
} catch (Exception e) {
logger.error("Unable to provide tab list completions for {} for command '{}'",
player.getUsername(),
player.username(),
command, e);
}
}, player.getConnection().eventLoop())
@@ -559,10 +582,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
for (Offer offer : response.getOffers()) {
offers.add(offer.getText());
}
server.getEventManager().fire(new TabCompleteEvent(player, request.getCommand(), offers))
server.eventManager().fire(new TabCompleteEventImpl(player, request.getCommand(), offers))
.thenAcceptAsync(e -> {
response.getOffers().clear();
for (String s : e.getSuggestions()) {
for (String s : e.suggestions()) {
response.getOffers().add(new Offer(s));
}
player.getConnection().write(response);
@@ -582,12 +605,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
}
MinecraftConnection smc = player.ensureAndGetCurrentServer().ensureConnected();
String commandToRun = result.getCommand().orElse(originalCommand);
String commandToRun = result.modifiedCommand().orElse(originalCommand);
if (result.isForwardToServer()) {
return CompletableFuture.runAsync(() -> smc.write(new ServerboundChatPacket("/"
+ commandToRun)), smc.eventLoop());
} else {
return server.getCommandManager().executeImmediately(player, commandToRun)
return server.commandManager().executeImmediately(player, commandToRun)
.thenAcceptAsync(hasRun -> {
if (!hasRun) {
smc.write(new ServerboundChatPacket("/" + commandToRun));

View File

@@ -1,14 +1,31 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.player.ClientSettings;
import com.velocitypowered.api.proxy.player.SkinParts;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundClientSettingsPacket;
import java.util.Locale;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ClientSettingsWrapper implements PlayerSettings {
public class ClientSettingsWrapper implements ClientSettings {
static final PlayerSettings DEFAULT = new ClientSettingsWrapper(
static final ClientSettings DEFAULT = new ClientSettingsWrapper(
new ServerboundClientSettingsPacket("en_US", (byte) 10, 0, true, (short) 127, 1));
private final ServerboundClientSettingsPacket settings;

View File

@@ -1,35 +1,55 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import static com.velocitypowered.api.proxy.player.ConnectionRequestBuilder.Status.ALREADY_CONNECTED;
import static com.velocitypowered.proxy.connection.util.ConnectionRequestResults.plainResult;
import static com.velocitypowered.proxy.network.PluginMessageUtil.channelIdForVersion;
import static java.util.concurrent.CompletableFuture.completedFuture;
import com.google.common.base.Preconditions;
import com.google.gson.JsonObject;
import com.velocitypowered.api.event.player.DisconnectEvent;
import com.velocitypowered.api.event.player.DisconnectEvent.LoginStatus;
import com.velocitypowered.api.event.player.DisconnectEventImpl;
import com.velocitypowered.api.event.player.KickedFromServerEvent;
import com.velocitypowered.api.event.player.KickedFromServerEvent.DisconnectPlayer;
import com.velocitypowered.api.event.player.KickedFromServerEvent.Notify;
import com.velocitypowered.api.event.player.KickedFromServerEvent.RedirectPlayer;
import com.velocitypowered.api.event.player.KickedFromServerEvent.ServerKickResult;
import com.velocitypowered.api.event.player.PlayerModInfoEvent;
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
import com.velocitypowered.api.event.player.KickedFromServerEventImpl;
import com.velocitypowered.api.event.player.PlayerClientSettingsChangedEventImpl;
import com.velocitypowered.api.event.player.PlayerModInfoEventImpl;
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
import com.velocitypowered.api.event.player.ServerPreConnectEventImpl;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.permission.PermissionProvider;
import com.velocitypowered.api.permission.Tristate;
import com.velocitypowered.api.proxy.connection.Player;
import com.velocitypowered.api.proxy.connection.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import com.velocitypowered.api.proxy.player.ClientSettings;
import com.velocitypowered.api.proxy.player.ConnectionRequestBuilder;
import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
@@ -42,7 +62,6 @@ import com.velocitypowered.proxy.network.StateRegistry;
import com.velocitypowered.proxy.network.packet.AbstractPluginMessagePacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundChatPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundDisconnectPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundHeaderAndFooterPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundKeepAlivePacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundPluginMessagePacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundResourcePackRequestPacket;
@@ -60,6 +79,7 @@ import java.net.SocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
@@ -75,6 +95,9 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.title.Title;
import net.kyori.adventure.title.Title.Times;
import net.kyori.adventure.translation.GlobalTranslator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@@ -103,7 +126,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
private final boolean onlineMode;
private @Nullable VelocityServerConnection connectedServer;
private @Nullable VelocityServerConnection connectionInFlight;
private @Nullable PlayerSettings settings;
private @Nullable ClientSettings settings;
private @Nullable ModInfo modInfo;
private Component playerListHeader = Component.empty();
private Component playerListFooter = Component.empty();
@@ -138,17 +161,17 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public String getUsername() {
public String username() {
return profile.getName();
}
@Override
public UUID getUniqueId() {
public UUID id() {
return profile.getId();
}
@Override
public Optional<ServerConnection> getCurrentServer() {
public Optional<ServerConnection> connectedServer() {
return Optional.ofNullable(connectedServer);
}
@@ -165,7 +188,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public GameProfile getGameProfile() {
public GameProfile gameProfile() {
return profile;
}
@@ -174,7 +197,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public long getPing() {
public long ping() {
return this.ping;
}
@@ -183,38 +206,38 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public boolean isOnlineMode() {
public boolean onlineMode() {
return onlineMode;
}
@Override
public PlayerSettings getPlayerSettings() {
public ClientSettings clientSettings() {
return settings == null ? ClientSettingsWrapper.DEFAULT : this.settings;
}
void setPlayerSettings(ServerboundClientSettingsPacket settings) {
ClientSettingsWrapper cs = new ClientSettingsWrapper(settings);
this.settings = cs;
server.getEventManager().fireAndForget(new PlayerSettingsChangedEvent(this, cs));
server.eventManager().fireAndForget(new PlayerClientSettingsChangedEventImpl(this, cs));
}
@Override
public Optional<ModInfo> getModInfo() {
public Optional<ModInfo> modInfo() {
return Optional.ofNullable(modInfo);
}
public void setModInfo(ModInfo modInfo) {
this.modInfo = modInfo;
server.getEventManager().fireAndForget(new PlayerModInfoEvent(this, modInfo));
server.eventManager().fireAndForget(new PlayerModInfoEventImpl(this, modInfo));
}
@Override
public SocketAddress getRemoteAddress() {
public SocketAddress remoteAddress() {
return connection.getRemoteAddress();
}
@Override
public Optional<InetSocketAddress> getVirtualHost() {
public Optional<InetSocketAddress> connectedHostname() {
return Optional.ofNullable(virtualHost);
}
@@ -228,18 +251,25 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public ProtocolVersion getProtocolVersion() {
public ProtocolVersion protocolVersion() {
return connection.getProtocolVersion();
}
public Component translateMessage(Component message) {
Locale locale = this.settings == null ? Locale.getDefault() : this.settings.getLocale();
return GlobalTranslator.render(message, locale);
}
@Override
public void sendMessage(@NonNull Identity identity, @NonNull Component message,
@NonNull MessageType type) {
Preconditions.checkNotNull(message, "message");
Preconditions.checkNotNull(type, "type");
Component translated = translateMessage(message);
connection.write(new ClientboundChatPacket(
ProtocolUtils.getJsonChatSerializer(this.getProtocolVersion()).serialize(message),
ProtocolUtils.getJsonChatSerializer(this.protocolVersion()).serialize(translated),
type == MessageType.CHAT
? ClientboundChatPacket.CHAT_TYPE
: ClientboundChatPacket.SYSTEM_TYPE,
@@ -249,18 +279,21 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override
public void sendActionBar(net.kyori.adventure.text.@NonNull Component message) {
ProtocolVersion playerVersion = getProtocolVersion();
Component translated = translateMessage(message);
ProtocolVersion playerVersion = protocolVersion();
if (playerVersion.gte(ProtocolVersion.MINECRAFT_1_11)) {
// Use the title packet instead.
connection.write(new ClientboundTitlePacket(
ClientboundTitlePacket.SET_ACTION_BAR,
ProtocolUtils.getJsonChatSerializer(playerVersion).serialize(message)
ProtocolUtils.getJsonChatSerializer(playerVersion).serialize(translated)
));
} else {
// Due to issues with action bar packets, we'll need to convert the text message into a
// legacy message and then inject the legacy text into a component... yuck!
JsonObject object = new JsonObject();
object.addProperty("text", LegacyComponentSerializer.legacySection().serialize(message));
object.addProperty("text", LegacyComponentSerializer.legacySection()
.serialize(translated));
connection.write(new ClientboundChatPacket(
object.toString(),
ClientboundChatPacket.GAME_INFO_TYPE,
@@ -287,24 +320,24 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public void showTitle(net.kyori.adventure.title.@NonNull Title title) {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
public void showTitle(@NonNull Title title) {
if (this.protocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
GsonComponentSerializer serializer = ProtocolUtils.getJsonChatSerializer(this
.getProtocolVersion());
.protocolVersion());
connection.delayedWrite(new ClientboundTitlePacket(
ClientboundTitlePacket.SET_TITLE,
serializer.serialize(title.title())
serializer.serialize(translateMessage(title.title()))
));
connection.delayedWrite(new ClientboundTitlePacket(
ClientboundTitlePacket.SET_SUBTITLE,
serializer.serialize(title.subtitle())
serializer.serialize(translateMessage(title.subtitle()))
));
net.kyori.adventure.title.Title.Times times = title.times();
Times times = title.times();
if (times != null) {
connection.delayedWrite(ClientboundTitlePacket.times(this.getProtocolVersion(), times));
connection.delayedWrite(ClientboundTitlePacket.times(this.protocolVersion(), times));
}
connection.flush();
@@ -313,28 +346,28 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override
public void clearTitle() {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(ClientboundTitlePacket.hide(this.getProtocolVersion()));
if (this.protocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(ClientboundTitlePacket.hide(this.protocolVersion()));
}
}
@Override
public void resetTitle() {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(ClientboundTitlePacket.reset(this.getProtocolVersion()));
if (this.protocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(ClientboundTitlePacket.reset(this.protocolVersion()));
}
}
@Override
public void hideBossBar(@NonNull BossBar bar) {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
if (this.protocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
this.server.getBossBarManager().removeBossBar(this, bar);
}
}
@Override
public void showBossBar(@NonNull BossBar bar) {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
if (this.protocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
this.server.getBossBarManager().addBossBar(this, bar);
}
}
@@ -350,7 +383,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public VelocityTabList getTabList() {
public VelocityTabList tabList() {
return tabList;
}
@@ -369,9 +402,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
* @param duringLogin whether the disconnect happened during login
*/
public void disconnect0(Component reason, boolean duringLogin) {
Component translated = this.translateMessage(reason);
logger.info("{} has disconnected: {}", this,
LegacyComponentSerializer.legacySection().serialize(reason));
connection.closeWith(ClientboundDisconnectPacket.create(reason, this.getProtocolVersion()));
LegacyComponentSerializer.legacySection().serialize(translated));
connection.closeWith(ClientboundDisconnectPacket.create(translated, this.protocolVersion()));
}
public @Nullable VelocityServerConnection getConnectedServer() {
@@ -410,18 +445,18 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
wrapped = cause;
}
}
String userMessage;
if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) {
userMessage = "Your connection to " + server.getServerInfo().getName() + " encountered an "
+ "error.";
Component friendlyError;
if (connectedServer != null && connectedServer.serverInfo().equals(server.serverInfo())) {
friendlyError = Component.translatable("velocity.error.connected-server-error",
Component.text(server.serverInfo().name()));
} else {
logger.error("{}: unable to connect to server {}", this, server.getServerInfo().getName(),
logger.error("{}: unable to connect to server {}", this, server.serverInfo().name(),
wrapped);
userMessage = "Unable to connect to " + server.getServerInfo().getName() + ". Try again "
+ "later.";
friendlyError = Component.translatable("velocity.error.connecting-server-error",
Component.text(server.serverInfo().name()));
}
handleConnectionException(server, null, Component.text(userMessage,
NamedTextColor.RED), safe);
handleConnectionException(server, null, friendlyError.color(NamedTextColor.RED), safe);
}
/**
@@ -437,26 +472,22 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
return;
}
VelocityConfiguration.Messages messages = this.server.getConfiguration().getMessages();
Component disconnectReason = GsonComponentSerializer.gson().deserialize(disconnect.getReason());
String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason);
if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) {
logger.error("{}: kicked from server {}: {}", this, server.getServerInfo().getName(),
if (connectedServer != null && connectedServer.serverInfo().equals(server.serverInfo())) {
logger.error("{}: kicked from server {}: {}", this, server.serverInfo().name(),
plainTextReason);
handleConnectionException(server, disconnectReason, Component.text()
.append(messages.getKickPrefix(server.getServerInfo().getName())
.colorIfAbsent(NamedTextColor.RED))
.color(NamedTextColor.RED)
.append(disconnectReason)
.build(), safe);
handleConnectionException(server, disconnectReason,
Component.translatable("velocity.error.moved-to-new-server", NamedTextColor.RED,
Component.text(server.serverInfo().name()),
disconnectReason), safe);
} else {
logger.error("{}: disconnected while connecting to {}: {}", this,
server.getServerInfo().getName(), plainTextReason);
handleConnectionException(server, disconnectReason, Component.text()
.append(messages.getDisconnectPrefix(server.getServerInfo().getName())
.colorIfAbsent(NamedTextColor.RED))
.append(disconnectReason)
.build(), safe);
server.serverInfo().name(), plainTextReason);
handleConnectionException(server, disconnectReason,
Component.translatable("velocity.error.cant-connect", NamedTextColor.RED,
Component.text(server.serverInfo().name()),
disconnectReason), safe);
}
}
@@ -475,7 +506,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
return;
}
boolean kickedFromCurrent = connectedServer == null || connectedServer.getServer().equals(rs);
boolean kickedFromCurrent = connectedServer == null || connectedServer.target().equals(rs);
ServerKickResult result;
if (kickedFromCurrent) {
Optional<RegisteredServer> next = getNextServerToTry(rs);
@@ -483,19 +514,19 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
.orElseGet(() -> DisconnectPlayer.create(friendlyReason));
} else {
// If we were kicked by going to another server, the connection should not be in flight
if (connectionInFlight != null && connectionInFlight.getServer().equals(rs)) {
if (connectionInFlight != null && connectionInFlight.target().equals(rs)) {
resetInFlightConnection();
}
result = Notify.create(friendlyReason);
}
KickedFromServerEvent originalEvent = new KickedFromServerEvent(this, rs, kickReason,
KickedFromServerEvent originalEvent = new KickedFromServerEventImpl(this, rs, kickReason,
!kickedFromCurrent, result);
handleKickEvent(originalEvent, friendlyReason, kickedFromCurrent);
}
private void handleKickEvent(KickedFromServerEvent originalEvent, Component friendlyReason,
boolean kickedFromCurrent) {
server.getEventManager().fire(originalEvent)
server.eventManager().fire(originalEvent)
.thenAcceptAsync(event -> {
// There can't be any connection in flight now.
connectionInFlight = null;
@@ -511,49 +542,51 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
return;
}
if (event.getResult() instanceof DisconnectPlayer) {
DisconnectPlayer res = (DisconnectPlayer) event.getResult();
disconnect(res.getReason());
} else if (event.getResult() instanceof RedirectPlayer) {
RedirectPlayer res = (RedirectPlayer) event.getResult();
if (event.result() instanceof DisconnectPlayer) {
DisconnectPlayer res = (DisconnectPlayer) event.result();
disconnect(res.message());
} else if (event.result() instanceof RedirectPlayer) {
RedirectPlayer res = (RedirectPlayer) event.result();
createConnectionRequest(res.getServer())
.connect()
.whenCompleteAsync((status, throwable) -> {
if (throwable != null) {
handleConnectionException(status != null ? status.getAttemptedConnection()
handleConnectionException(status != null ? status.finalTarget()
: res.getServer(), throwable, true);
return;
}
switch (status.getStatus()) {
switch (status.status()) {
// Impossible/nonsensical cases
case ALREADY_CONNECTED:
case CONNECTION_IN_PROGRESS:
// Fatal case
case CONNECTION_CANCELLED:
disconnect(status.getReason().orElse(res.getMessage()));
disconnect(status.failureReason().orElse(res.message()));
break;
case SERVER_DISCONNECTED:
Component reason = status.getReason()
Component reason = status.failureReason()
.orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
handleConnectionException(res.getServer(), ClientboundDisconnectPacket.create(reason,
getProtocolVersion()), ((Impl) status).isSafe());
protocolVersion()), ((Impl) status).isSafe());
break;
case SUCCESS:
sendMessage(Identity.nil(), server.getConfiguration().getMessages()
.getMovedToNewServerPrefix().append(friendlyReason));
sendMessage(Component.translatable("velocity.error.moved-to-new-server",
NamedTextColor.RED,
Component.text(originalEvent.server().serverInfo().name()),
friendlyReason));
break;
default:
// The only remaining value is successful (no need to do anything!)
break;
}
}, connection.eventLoop());
} else if (event.getResult() instanceof Notify) {
Notify res = (Notify) event.getResult();
} else if (event.result() instanceof Notify) {
Notify res = (Notify) event.result();
if (event.kickedDuringServerConnect() && previouslyConnected) {
sendMessage(Identity.nil(), res.getMessage());
sendMessage(Identity.nil(), res.message());
} else {
disconnect(res.getMessage());
disconnect(res.message());
}
} else {
// In case someone gets creative, assume we want to disconnect the player.
@@ -582,31 +615,33 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
*/
private Optional<RegisteredServer> getNextServerToTry(@Nullable RegisteredServer current) {
if (serversToTry == null) {
String virtualHostStr = getVirtualHost().map(InetSocketAddress::getHostString).orElse("");
serversToTry = server.getConfiguration().getForcedHosts().getOrDefault(virtualHostStr,
String virtualHostStr = connectedHostname().map(InetSocketAddress::getHostString)
.orElse("")
.toLowerCase(Locale.ROOT);
serversToTry = server.configuration().getForcedHosts().getOrDefault(virtualHostStr,
Collections.emptyList());
}
if (serversToTry.isEmpty()) {
serversToTry = server.getConfiguration().getAttemptConnectionOrder();
serversToTry = server.configuration().getAttemptConnectionOrder();
}
for (int i = tryIndex; i < serversToTry.size(); i++) {
String toTryName = serversToTry.get(i);
if ((connectedServer != null && hasSameName(connectedServer.getServer(), toTryName))
|| (connectionInFlight != null && hasSameName(connectionInFlight.getServer(), toTryName))
if ((connectedServer != null && hasSameName(connectedServer.target(), toTryName))
|| (connectionInFlight != null && hasSameName(connectionInFlight.target(), toTryName))
|| (current != null && hasSameName(current, toTryName))) {
continue;
}
tryIndex = i;
return server.getServer(toTryName);
return server.server(toTryName);
}
return Optional.empty();
}
private static boolean hasSameName(RegisteredServer server, String name) {
return server.getServerInfo().getName().equalsIgnoreCase(name);
return server.serverInfo().name().equalsIgnoreCase(name);
}
/**
@@ -649,12 +684,12 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
connectedServer.disconnect();
}
Optional<Player> connectedPlayer = server.getPlayer(this.getUniqueId());
Optional<Player> connectedPlayer = server.getPlayer(this.id());
server.unregisterConnection(this);
DisconnectEvent.LoginStatus status;
DisconnectEventImpl.LoginStatus status;
if (connectedPlayer.isPresent()) {
if (!connectedPlayer.get().getCurrentServer().isPresent()) {
if (!connectedPlayer.get().connectedServer().isPresent()) {
status = LoginStatus.PRE_SERVER_JOIN;
} else {
status = connectedPlayer.get() == this ? LoginStatus.SUCCESSFUL_LOGIN
@@ -665,8 +700,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
LoginStatus.CANCELLED_BY_USER;
}
DisconnectEvent event = new DisconnectEvent(this, status);
server.getEventManager().fire(event).whenComplete((val, ex) -> {
DisconnectEvent event = new DisconnectEventImpl(this, status);
server.eventManager().fire(event).whenComplete((val, ex) -> {
if (ex == null) {
this.teardownFuture.complete(null);
} else {
@@ -681,19 +716,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override
public String toString() {
return "[connected player] " + profile.getName() + " (" + getRemoteAddress() + ")";
return "[connected player] " + profile.getName() + " (" + remoteAddress() + ")";
}
@Override
public Tristate getPermissionValue(String permission) {
return permissionFunction.getPermissionValue(permission);
public Tristate evaluatePermission(String permission) {
return permissionFunction.evaluatePermission(permission);
}
@Override
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
public boolean sendPluginMessage(PluginChannelId identifier, byte[] data) {
Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data");
ClientboundPluginMessagePacket message = new ClientboundPluginMessagePacket(identifier.getId(),
ClientboundPluginMessagePacket message = new ClientboundPluginMessagePacket(
channelIdForVersion(identifier, connection.getProtocolVersion()),
Unpooled.wrappedBuffer(data));
connection.write(message);
return true;
@@ -711,7 +747,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
public void sendResourcePack(String url) {
Preconditions.checkNotNull(url, "url");
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
if (this.protocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(new ClientboundResourcePackRequestPacket(url, ""));
}
}
@@ -722,7 +758,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
Preconditions.checkNotNull(hash, "hash");
Preconditions.checkArgument(hash.length == 20, "Hash length is not 20");
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
if (this.protocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(new ClientboundResourcePackRequestPacket(url, ByteBufUtil.hexDump(hash)));
}
}
@@ -794,7 +830,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
private class IdentityImpl implements Identity {
@Override
public @NonNull UUID uuid() {
return ConnectedPlayer.this.getUniqueId();
return ConnectedPlayer.this.id();
}
}
@@ -807,7 +843,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
}
@Override
public RegisteredServer getServer() {
public RegisteredServer target() {
return toConnect;
}
@@ -818,7 +854,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
&& !connectedServer.hasCompletedJoin())) {
return Optional.of(ConnectionRequestBuilder.Status.CONNECTION_IN_PROGRESS);
}
if (connectedServer != null && connectedServer.getServer().equals(server)) {
if (connectedServer != null && connectedServer.target().equals(server)) {
return Optional.of(ALREADY_CONNECTED);
}
return Optional.empty();
@@ -835,11 +871,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
return completedFuture(plainResult(initialCheck.get(), toConnect));
}
ServerPreConnectEvent event = new ServerPreConnectEvent(ConnectedPlayer.this,
ServerPreConnectEvent event = new ServerPreConnectEventImpl(ConnectedPlayer.this,
toConnect);
return server.getEventManager().fire(event)
return server.eventManager().fire(event)
.thenComposeAsync(newEvent -> {
Optional<RegisteredServer> newDest = newEvent.getResult().getServer();
Optional<RegisteredServer> newDest = newEvent.result().target();
if (!newDest.isPresent()) {
return completedFuture(
plainResult(ConnectionRequestBuilder.Status.CONNECTION_CANCELLED, toConnect)
@@ -856,8 +892,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
VelocityServerConnection con = new VelocityServerConnection(vrs,
ConnectedPlayer.this, server);
connectionInFlight = con;
return con.connect().whenCompleteAsync((result, throwable) ->
this.resetIfInFlightIs(con), connection.eventLoop());
return con.connect().thenApplyAsync((result) -> {
this.resetIfInFlightIs(con);
return result;
}, connection.eventLoop());
}, connection.eventLoop());
});
}
@@ -874,9 +912,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
.whenCompleteAsync((status, throwable) -> {
if (status != null && !status.isSuccessful()) {
if (!status.isSafe()) {
handleConnectionException(status.getAttemptedConnection(), throwable, false);
handleConnectionException(status.finalTarget(), throwable, false);
return;
}
}
if (throwable != null) {
logger.error("Exception during connect; status = {}", status, throwable);
}
}, connection.eventLoop())
.thenApply(x -> x);
}
@@ -887,12 +929,12 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
.whenCompleteAsync((status, throwable) -> {
if (throwable != null) {
// TODO: The exception handling from this is not very good. Find a better way.
handleConnectionException(status != null ? status.getAttemptedConnection()
handleConnectionException(status != null ? status.finalTarget()
: toConnect, throwable, true);
return;
}
switch (status.getStatus()) {
switch (status.status()) {
case ALREADY_CONNECTED:
sendMessage(Identity.nil(), ConnectionMessages.ALREADY_CONNECTED);
break;
@@ -903,10 +945,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
// Ignored; the plugin probably already handled this.
break;
case SERVER_DISCONNECTED:
Component reason = status.getReason()
Component reason = status.failureReason()
.orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
handleConnectionException(toConnect, ClientboundDisconnectPacket.create(reason,
getProtocolVersion()), status.isSafe());
protocolVersion()), status.isSafe());
break;
default:
// The only remaining value is successful (no need to do anything!)

View File

@@ -1,8 +1,25 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.connection.ConnectionHandshakeEvent;
import com.velocitypowered.api.event.connection.ConnectionHandshakeEventImpl;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.connection.InboundConnection;
import com.velocitypowered.proxy.VelocityServer;
@@ -53,7 +70,10 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(LegacyHandshakePacket packet) {
connection.closeWith(LegacyDisconnectPacket
.from(Component.text("Your client is old, please upgrade!", NamedTextColor.RED)));
.from(Component.text(
"Your client is extremely old. Please update to a newer version of Minecraft.",
NamedTextColor.RED)
));
return true;
}
@@ -105,7 +125,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
InetAddress address = ((InetSocketAddress) connection.getRemoteAddress()).getAddress();
if (!server.getIpAttemptLimiter().attempt(address)) {
ic.disconnectQuietly(Component.text("You are logging in too fast, try again later."));
ic.disconnectQuietly(Component.translatable("velocity.error.logging-in-too-fast"));
return;
}
@@ -113,13 +133,14 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
// If the proxy is configured for modern forwarding, we must deny connections from 1.12.2
// and lower, otherwise IP information will never get forwarded.
if (server.getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN
if (server.configuration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN
&& handshake.getProtocolVersion().lt(ProtocolVersion.MINECRAFT_1_13)) {
ic.disconnectQuietly(Component.text("This server is only compatible with 1.13 and above."));
ic.disconnectQuietly(Component.translatable(
"velocity.error.modern-forwarding-needs-new-client"));
return;
}
server.getEventManager().fireAndForget(new ConnectionHandshakeEvent(ic));
server.eventManager().fireAndForget(new ConnectionHandshakeEventImpl(ic));
connection.setSessionHandler(new LoginSessionHandler(server, connection, ic));
}
@@ -187,12 +208,12 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
}
@Override
public InetSocketAddress getRemoteAddress() {
public InetSocketAddress remoteAddress() {
return (InetSocketAddress) connection.getRemoteAddress();
}
@Override
public Optional<InetSocketAddress> getVirtualHost() {
public Optional<InetSocketAddress> connectedHostname() {
return Optional.ofNullable(ping.getVhost());
}
@@ -202,7 +223,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
}
@Override
public ProtocolVersion getProtocolVersion() {
public ProtocolVersion protocolVersion() {
return ProtocolVersion.LEGACY;
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.api.network.ProtocolVersion;
@@ -7,9 +24,11 @@ import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundDisconnectPacket;
import com.velocitypowered.proxy.network.packet.serverbound.ServerboundHandshakePacket;
import java.net.InetSocketAddress;
import java.util.Locale;
import java.util.Optional;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -30,12 +49,12 @@ public final class InitialInboundConnection implements InboundConnection,
}
@Override
public InetSocketAddress getRemoteAddress() {
public InetSocketAddress remoteAddress() {
return (InetSocketAddress) connection.getRemoteAddress();
}
@Override
public Optional<InetSocketAddress> getVirtualHost() {
public Optional<InetSocketAddress> connectedHostname() {
return Optional.of(InetSocketAddress.createUnresolved(cleanedAddress, handshake.getPort()));
}
@@ -45,7 +64,7 @@ public final class InitialInboundConnection implements InboundConnection,
}
@Override
public ProtocolVersion getProtocolVersion() {
public ProtocolVersion protocolVersion() {
return connection.getProtocolVersion();
}
@@ -59,9 +78,11 @@ public final class InitialInboundConnection implements InboundConnection,
* @param reason the reason for disconnecting
*/
public void disconnect(Component reason) {
Component translated = GlobalTranslator.render(reason, Locale.getDefault());
logger.info("{} has disconnected: {}", this,
LegacyComponentSerializer.legacySection().serialize(reason));
connection.closeWith(ClientboundDisconnectPacket.create(reason, getProtocolVersion()));
LegacyComponentSerializer.legacySection().serialize(translated));
connection.closeWith(ClientboundDisconnectPacket.create(translated, protocolVersion()));
}
/**
@@ -69,6 +90,7 @@ public final class InitialInboundConnection implements InboundConnection,
* @param reason the reason for disconnecting
*/
public void disconnectQuietly(Component reason) {
connection.closeWith(ClientboundDisconnectPacket.create(reason, getProtocolVersion()));
Component translated = GlobalTranslator.render(reason, Locale.getDefault());
connection.closeWith(ClientboundDisconnectPacket.create(translated, protocolVersion()));
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import static com.google.common.net.UrlEscapers.urlFormParameterEscaper;
@@ -8,15 +25,19 @@ import static com.velocitypowered.proxy.util.EncryptionUtils.decryptRsa;
import static com.velocitypowered.proxy.util.EncryptionUtils.generateServerId;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
import com.velocitypowered.api.event.player.DisconnectEvent;
import com.velocitypowered.api.event.permission.PermissionsSetupEventImpl;
import com.velocitypowered.api.event.player.DisconnectEvent.LoginStatus;
import com.velocitypowered.api.event.player.DisconnectEventImpl;
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
import com.velocitypowered.api.event.player.LoginEvent;
import com.velocitypowered.api.event.player.GameProfileRequestEventImpl;
import com.velocitypowered.api.event.player.LoginEventImpl;
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
import com.velocitypowered.api.event.player.PostLoginEvent;
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEventImpl;
import com.velocitypowered.api.event.player.PostLoginEventImpl;
import com.velocitypowered.api.event.player.PreLoginEvent;
import com.velocitypowered.api.event.player.PreLoginEvent.PreLoginComponentResult;
import com.velocitypowered.api.event.player.PreLoginEventImpl;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
@@ -26,7 +47,6 @@ import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.network.StateRegistry;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundDisconnectPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundEncryptionRequestPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundServerLoginSuccessPacket;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundSetCompressionPacket;
@@ -44,6 +64,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.asynchttpclient.ListenableFuture;
@@ -102,7 +123,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
String url = String.format(MOJANG_HASJOINED_URL,
urlFormParameterEscaper().escape(login.getUsername()), serverId);
if (server.getConfiguration().shouldPreventClientProxyConnections()) {
if (server.configuration().shouldPreventClientProxyConnections()) {
url += "&ip=" + urlFormParameterEscaper().escape(playerIp);
}
@@ -130,7 +151,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
GameProfile.class), true);
} else if (profileResponse.getStatusCode() == 204) {
// Apparently an offline-mode user logged onto this online-mode proxy.
inbound.disconnect(server.getConfiguration().getMessages().getOnlineModeOnly());
inbound.disconnect(Component.translatable("velocity.error.online-mode-only",
NamedTextColor.RED));
} else {
// Something else went wrong
logger.error(
@@ -158,24 +180,23 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
if (login == null) {
throw new IllegalStateException("No ServerLogin packet received yet.");
}
PreLoginEvent event = new PreLoginEvent(inbound, login.getUsername());
server.getEventManager().fire(event)
PreLoginEvent event = new PreLoginEventImpl(inbound, login.getUsername());
server.eventManager().fire(event)
.thenRunAsync(() -> {
if (mcConnection.isClosed()) {
// The player was disconnected
return;
}
PreLoginComponentResult result = event.getResult();
Optional<Component> disconnectReason = result.getReason();
PreLoginComponentResult result = event.result();
Optional<Component> disconnectReason = result.denialReason();
if (disconnectReason.isPresent()) {
// The component is guaranteed to be provided if the connection was denied.
mcConnection.closeWith(ClientboundDisconnectPacket.create(disconnectReason.get(),
inbound.getProtocolVersion()));
inbound.disconnect(disconnectReason.get());
return;
}
if (!result.isForceOfflineMode() && (server.getConfiguration().isOnlineMode() || result
if (!result.isForceOfflineMode() && (server.configuration().isOnlineMode() || result
.isOnlineModeAllowed())) {
// Request encryption.
ClientboundEncryptionRequestPacket request = generateEncryptionRequest();
@@ -204,34 +225,45 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
private void initializePlayer(GameProfile profile, boolean onlineMode) {
// Some connection types may need to alter the game profile.
profile = mcConnection.getType().addGameProfileTokensIfRequired(profile,
server.getConfiguration().getPlayerInfoForwardingMode());
GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(inbound, profile,
server.configuration().getPlayerInfoForwardingMode());
GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEventImpl(inbound, profile,
onlineMode);
final GameProfile finalProfile = profile;
server.getEventManager().fire(profileRequestEvent).thenComposeAsync(profileEvent -> {
server.eventManager().fire(profileRequestEvent).thenComposeAsync(profileEvent -> {
if (mcConnection.isClosed()) {
// The player disconnected after we authenticated them.
return CompletableFuture.completedFuture(null);
}
// Initiate a regular connection and move over to it.
ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
mcConnection, inbound.getVirtualHost().orElse(null), onlineMode);
ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.gameProfile(),
mcConnection, inbound.connectedHostname().orElse(null), onlineMode);
this.connectedPlayer = player;
if (!server.canRegisterConnection(player)) {
player.disconnect0(server.getConfiguration().getMessages().getAlreadyConnected(), true);
player.disconnect0(Component.translatable("velocity.error.already-connected-proxy",
NamedTextColor.RED), true);
return CompletableFuture.completedFuture(null);
}
logger.info("{} has connected", player);
return server.getEventManager()
.fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
return server.eventManager()
.fire(new PermissionsSetupEventImpl(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
.thenAcceptAsync(event -> {
if (!mcConnection.isClosed()) {
// wait for permissions to load, then set the players permission function
player.setPermissionFunction(event.createFunction(player));
final PermissionFunction function = event.createFunction(player);
if (function == null) {
logger.error(
"A plugin permission provider {} provided an invalid permission function"
+ " for player {}. This is a bug in the plugin, not in Velocity. Falling"
+ " back to the default permission function.",
event.provider().getClass().getName(),
player.username());
} else {
player.setPermissionFunction(function);
}
completeLoginProtocolPhaseAndInitialize(player);
}
}, mcConnection.eventLoop());
@@ -242,43 +274,47 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
}
private void completeLoginProtocolPhaseAndInitialize(ConnectedPlayer player) {
int threshold = server.getConfiguration().getCompressionThreshold();
int threshold = server.configuration().getCompressionThreshold();
if (threshold >= 0 && mcConnection.getProtocolVersion().gte(MINECRAFT_1_8)) {
mcConnection.write(new ClientboundSetCompressionPacket(threshold));
mcConnection.setCompressionThreshold(threshold);
}
VelocityConfiguration configuration = server.getConfiguration();
UUID playerUniqueId = player.getUniqueId();
VelocityConfiguration configuration = server.configuration();
UUID playerUniqueId = player.id();
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.username());
}
mcConnection.write(new ClientboundServerLoginSuccessPacket(playerUniqueId, player.getUsername()));
mcConnection.write(new ClientboundServerLoginSuccessPacket(playerUniqueId, player.username()));
mcConnection.setAssociation(player);
mcConnection.setState(StateRegistry.PLAY);
server.getEventManager().fire(new LoginEvent(player))
server.eventManager().fire(new LoginEventImpl(player))
.thenAcceptAsync(event -> {
if (mcConnection.isClosed()) {
// The player was disconnected
server.getEventManager().fireAndForget(new DisconnectEvent(player,
server.eventManager().fireAndForget(new DisconnectEventImpl(player,
LoginStatus.CANCELLED_BY_USER_BEFORE_COMPLETE));
return;
}
Optional<Component> reason = event.getResult().getReason();
Optional<Component> reason = event.result().reason();
if (reason.isPresent()) {
player.disconnect0(reason.get(), true);
} else {
if (!server.registerConnection(player)) {
player.disconnect0(server.getConfiguration().getMessages()
.getAlreadyConnected(), true);
player.disconnect0(Component.translatable("velocity.error.already-connected-proxy"),
true);
return;
}
mcConnection.setSessionHandler(new InitialConnectSessionHandler(player));
server.getEventManager().fire(new PostLoginEvent(player))
.thenRun(() -> connectToInitialServer(player));
server.eventManager().fire(new PostLoginEventImpl(player))
.thenCompose((ignored) -> connectToInitialServer(player))
.exceptionally((ex) -> {
logger.error("Exception while connecting {} to initial server", player, ex);
return null;
});
}
}, mcConnection.eventLoop())
.exceptionally((ex) -> {
@@ -287,25 +323,21 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
});
}
private void connectToInitialServer(ConnectedPlayer player) {
private CompletableFuture<Void> connectToInitialServer(ConnectedPlayer player) {
Optional<RegisteredServer> initialFromConfig = player.getNextServerToTry();
PlayerChooseInitialServerEvent event = new PlayerChooseInitialServerEvent(player,
PlayerChooseInitialServerEvent event = new PlayerChooseInitialServerEventImpl(player,
initialFromConfig.orElse(null));
server.getEventManager().fire(event)
return server.eventManager().fire(event)
.thenRunAsync(() -> {
Optional<RegisteredServer> toTry = event.getInitialServer();
Optional<RegisteredServer> toTry = event.initialServer();
if (!toTry.isPresent()) {
player.disconnect0(server.getConfiguration().getMessages()
.getNoAvailableServers(), true);
player.disconnect0(Component.translatable("velocity.error.no-available-servers",
NamedTextColor.RED), true);
return;
}
player.createConnectionRequest(toTry.get()).fireAndForget();
}, mcConnection.eventLoop())
.exceptionally((ex) -> {
logger.error("Exception while connecting {} to initial server", player, ex);
return null;
});
}, mcConnection.eventLoop());
}
@Override

View File

@@ -1,8 +1,25 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.client;
import com.google.common.collect.ImmutableList;
import com.spotify.futures.CompletableFutures;
import com.velocitypowered.api.event.connection.ProxyPingEvent;
import com.velocitypowered.api.event.connection.ProxyPingEventImpl;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.connection.InboundConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
@@ -25,6 +42,7 @@ import io.netty.buffer.ByteBuf;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.apache.logging.log4j.LogManager;
@@ -50,18 +68,18 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
@Override
public void activated() {
if (server.getConfiguration().isShowPingRequests()) {
if (server.configuration().isShowPingRequests()) {
logger.info("{} is pinging the server with version {}", this.inbound,
this.connection.getProtocolVersion());
}
}
private ServerPing constructLocalPing(ProtocolVersion version) {
VelocityConfiguration configuration = server.getConfiguration();
VelocityConfiguration configuration = server.configuration();
return new ServerPing(
new ServerPing.Version(version.getProtocol(),
new ServerPing.Version(version.protocol(),
"Velocity " + ProtocolVersion.SUPPORTED_VERSION_STRING),
new ServerPing.Players(server.getPlayerCount(), configuration.getShowMaxPlayers(),
new ServerPing.Players(server.countConnectedPlayers(), configuration.getShowMaxPlayers(),
ImmutableList.of()),
configuration.getMotd(),
configuration.getFavicon().orElse(null),
@@ -74,7 +92,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
ServerPing fallback = constructLocalPing(pingingVersion);
List<CompletableFuture<ServerPing>> pings = new ArrayList<>();
for (String s : servers) {
Optional<RegisteredServer> rs = server.getServer(s);
Optional<RegisteredServer> rs = server.server(s);
if (!rs.isPresent()) {
continue;
}
@@ -106,7 +124,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
if (response == fallback) {
continue;
}
Optional<ModInfo> modInfo = response.getModinfo();
Optional<ModInfo> modInfo = response.modInfo();
if (modInfo.isPresent()) {
return fallback.asBuilder().mods(modInfo.get()).build();
}
@@ -121,16 +139,16 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
continue;
}
if (response.getDescription() == null) {
if (response.description() == null) {
continue;
}
return new ServerPing(
fallback.getVersion(),
fallback.getPlayers().orElse(null),
response.getDescription(),
fallback.getFavicon().orElse(null),
response.getModinfo().orElse(null)
fallback.version(),
fallback.players().orElse(null),
response.description(),
fallback.favicon().orElse(null),
response.modInfo().orElse(null)
);
}
return fallback;
@@ -142,7 +160,7 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
}
private CompletableFuture<ServerPing> getInitialPing() {
VelocityConfiguration configuration = server.getConfiguration();
VelocityConfiguration configuration = server.configuration();
ProtocolVersion shownVersion = ProtocolVersion.isSupported(connection.getProtocolVersion())
? connection.getProtocolVersion() : ProtocolVersion.MAXIMUM_VERSION;
PingPassthroughMode passthrough = configuration.getPingPassthrough();
@@ -150,10 +168,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
if (passthrough == PingPassthroughMode.DISABLED) {
return CompletableFuture.completedFuture(constructLocalPing(shownVersion));
} else {
String virtualHostStr = inbound.getVirtualHost().map(InetSocketAddress::getHostString)
String virtualHostStr = inbound.connectedHostname().map(InetSocketAddress::getHostString)
.map(str -> str.toLowerCase(Locale.ROOT))
.orElse("");
List<String> serversToTry = server.getConfiguration().getForcedHosts().getOrDefault(
virtualHostStr, server.getConfiguration().getAttemptConnectionOrder());
List<String> serversToTry = server.configuration().getForcedHosts().getOrDefault(
virtualHostStr, server.configuration().getAttemptConnectionOrder());
return attemptPingPassthrough(configuration.getPingPassthrough(), serversToTry, shownVersion);
}
}
@@ -165,9 +184,9 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
}
this.pingReceived = true;
getInitialPing()
.thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inbound, ping)))
.thenCompose(ping -> server.eventManager().fire(new ProxyPingEventImpl(inbound, ping)))
.thenAcceptAsync(event -> connection.closeWith(
LegacyDisconnectPacket.fromServerPing(event.getPing(), packet.getVersion())),
LegacyDisconnectPacket.fromServerPing(event.ping(), packet.getVersion())),
connection.eventLoop())
.exceptionally((ex) -> {
logger.error("Exception while handling legacy ping {}", packet, ex);
@@ -190,12 +209,12 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
this.pingReceived = true;
getInitialPing()
.thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inbound, ping)))
.thenCompose(ping -> server.eventManager().fire(new ProxyPingEventImpl(inbound, ping)))
.thenAcceptAsync(
(event) -> {
StringBuilder json = new StringBuilder();
VelocityServer.getPingGsonInstance(connection.getProtocolVersion())
.toJson(event.getPing(), json);
.toJson(event.ping(), json);
connection.write(new ClientboundStatusResponsePacket(json));
},
connection.eventLoop())

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.forge.legacy;
import com.velocitypowered.api.util.GameProfile;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.forge.legacy;
/**

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.forge.legacy;
import com.velocitypowered.proxy.connection.ConnectionTypes;
@@ -43,7 +60,7 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase {
if (mc != null) {
mc.setType(ConnectionTypes.LEGACY_FORGE);
}
connection.getPlayer().sendLegacyForgeHandshakeResetPacket();
connection.player().sendLegacyForgeHandshakeResetPacket();
}
},

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.forge.legacy;
import com.velocitypowered.api.util.ModInfo;
@@ -76,7 +93,7 @@ public enum LegacyForgeHandshakeClientPhase implements ClientConnectionPhase {
AbstractPluginMessagePacket<?> message,
MinecraftConnection backendConn) {
// Read the mod list if we haven't already.
if (!player.getModInfo().isPresent()) {
if (!player.modInfo().isPresent()) {
List<ModInfo.Mod> mods = LegacyForgeUtil.readModList(message);
if (!mods.isEmpty()) {
player.setModInfo(new ModInfo("FML", mods));

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.forge.legacy;
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.FORGE_LEGACY_HANDSHAKE_CHANNEL;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.registry;
import com.google.common.base.Preconditions;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.registry;
import com.google.common.base.Preconditions;

View File

@@ -1,13 +1,28 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.registry;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.velocitypowered.api.network.ProtocolVersion;
import java.util.Map;
import java.util.Set;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;

View File

@@ -1,17 +1,35 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.util;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.format.NamedTextColor;
public class ConnectionMessages {
public static final TextComponent ALREADY_CONNECTED = Component
.text("You are already connected to this server!", NamedTextColor.RED);
public static final TextComponent IN_PROGRESS = Component
.text("You are already connecting to a server!", NamedTextColor.RED);
public static final TextComponent INTERNAL_SERVER_CONNECTION_ERROR = Component
.text("An internal server connection error occurred.", NamedTextColor.RED);
public static final TranslatableComponent ALREADY_CONNECTED = Component
.translatable("velocity.error.already-connected", NamedTextColor.RED);
public static final TranslatableComponent IN_PROGRESS = Component
.translatable("velocity.error.already-connecting", NamedTextColor.RED);
public static final TranslatableComponent INTERNAL_SERVER_CONNECTION_ERROR = Component
.translatable("velocity.error.internal-server-connection-error", NamedTextColor.RED);
private ConnectionMessages() {
throw new AssertionError();

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.util;
import com.velocitypowered.api.proxy.player.ConnectionRequestBuilder;
@@ -67,17 +84,17 @@ public class ConnectionRequestResults {
}
@Override
public Status getStatus() {
public Status status() {
return status;
}
@Override
public Optional<Component> getReason() {
public Optional<Component> failureReason() {
return Optional.ofNullable(component);
}
@Override
public RegisteredServer getAttemptedConnection() {
public RegisteredServer finalTarget() {
return attemptedConnection;
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.util;
import com.velocitypowered.api.util.GameProfile;

View File

@@ -1,16 +1,37 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.console;
import static com.velocitypowered.api.permission.PermissionFunction.ALWAYS_TRUE;
import com.velocitypowered.api.command.ConsoleCommandSource;
import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
import com.velocitypowered.api.event.permission.PermissionsSetupEventImpl;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.permission.Tristate;
import com.velocitypowered.proxy.VelocityServer;
import java.util.List;
import java.util.Locale;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.minecrell.terminalconsole.SimpleTerminalConsole;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
@@ -34,13 +55,13 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons
@Override
public void sendMessage(@NonNull Identity identity, @NonNull Component message) {
logger.info(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection()
.serialize(message));
Component translated = GlobalTranslator.render(message, Locale.getDefault());
logger.info(LegacyComponentSerializer.legacySection().serialize(translated));
}
@Override
public @NonNull Tristate getPermissionValue(@NonNull String permission) {
return this.permissionFunction.getPermissionValue(permission);
public @NonNull Tristate evaluatePermission(@NonNull String permission) {
return this.permissionFunction.evaluatePermission(permission);
}
/**
@@ -55,9 +76,17 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons
* Sets up permissions for the console.
*/
public void setupPermissions() {
PermissionsSetupEvent event = new PermissionsSetupEvent(this, s -> ALWAYS_TRUE);
PermissionsSetupEvent event = new PermissionsSetupEventImpl(this, s -> ALWAYS_TRUE);
// we can safely block here, this is before any listeners fire
this.permissionFunction = this.server.getEventManager().fire(event).join().createFunction(this);
this.permissionFunction = this.server.eventManager().fire(event).join().createFunction(this);
if (this.permissionFunction == null) {
logger.error(
"A plugin permission provider {} provided an invalid permission function"
+ " for the console. This is a bug in the plugin, not in Velocity. Falling"
+ " back to the default permission function.",
event.provider().getClass().getName());
this.permissionFunction = ALWAYS_TRUE;
}
}
@Override
@@ -66,7 +95,7 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons
.appName("Velocity")
.completer((reader, parsedLine, list) -> {
try {
List<String> offers = this.server.getCommandManager()
List<String> offers = this.server.commandManager()
.offerSuggestions(this, parsedLine.line())
.join(); // Console doesn't get harmed much by this...
for (String offer : offers) {
@@ -87,8 +116,9 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons
@Override
protected void runCommand(String command) {
try {
if (!this.server.getCommandManager().execute(this, command).join()) {
sendMessage(Component.text("Command not found.", NamedTextColor.RED));
if (!this.server.commandManager().execute(this, command).join()) {
sendMessage(Component.translatable("velocity.command.command-does-not-exist",
NamedTextColor.RED));
}
} catch (Exception e) {
logger.error("An error occurred while running this command.", e);

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.event;
import com.velocitypowered.api.event.EventTask;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.event;
import static java.util.Objects.requireNonNull;
@@ -302,7 +319,23 @@ public class VelocityEventManager implements EventManager {
if (plugin == listener) {
throw new IllegalArgumentException("The plugin main instance is automatically registered.");
}
registerInternally(pluginContainer, listener);
}
@Override
@SuppressWarnings("unchecked")
public <E> void register(final Object plugin, final Class<E> eventClass,
final short order, final EventHandler<E> handler) {
final PluginContainer pluginContainer = ensurePlugin(plugin);
requireNonNull(eventClass, "eventClass");
requireNonNull(handler, "handler");
final HandlerRegistration registration = new HandlerRegistration(pluginContainer, order,
eventClass, handler, (EventHandler<Object>) handler, AsyncType.SOMETIMES);
register(Collections.singletonList(registration));
}
public void registerInternally(final PluginContainer pluginContainer, final Object listener) {
final Class<?> targetClass = listener.getClass();
final Map<String, MethodHandlerInfo> collected = new HashMap<>();
collectMethods(targetClass, collected);
@@ -325,19 +358,6 @@ public class VelocityEventManager implements EventManager {
register(registrations);
}
@Override
@SuppressWarnings("unchecked")
public <E> void register(final Object plugin, final Class<E> eventClass,
final short order, final EventHandler<E> handler) {
final PluginContainer pluginContainer = ensurePlugin(plugin);
requireNonNull(eventClass, "eventClass");
requireNonNull(handler, "handler");
final HandlerRegistration registration = new HandlerRegistration(pluginContainer, order,
eventClass, handler, (EventHandler<Object>) handler, AsyncType.SOMETIMES);
register(Collections.singletonList(registration));
}
@Override
public void unregisterListeners(final Object plugin) {
final PluginContainer pluginContainer = ensurePlugin(plugin);
@@ -578,7 +598,7 @@ public class VelocityEventManager implements EventManager {
private static void logHandlerException(
final HandlerRegistration registration, final Throwable t) {
logger.error("Couldn't pass {} to {}", registration.eventType.getSimpleName(),
registration.plugin.getDescription().getId(), t);
registration.plugin.description().id(), t);
}
public boolean shutdown() throws InterruptedException {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import static com.velocitypowered.proxy.network.HandlerNames.FLOW_HANDLER;
@@ -31,10 +48,10 @@ public class BackendChannelInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline()
.addLast(READ_TIMEOUT,
new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(),
TimeUnit.MILLISECONDS))
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(READ_TIMEOUT,
new ReadTimeoutHandler(server.configuration().getReadTimeout(),
TimeUnit.MILLISECONDS))
.addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE)
.addLast(MINECRAFT_DECODER,
new MinecraftDecoder(PacketDirection.CLIENTBOUND))

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import io.netty.channel.Channel;

View File

@@ -1,9 +1,29 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import static org.asynchttpclient.Dsl.asyncHttpClient;
import static org.asynchttpclient.Dsl.config;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.lifecycle.network.ListenerBoundEventImpl;
import com.velocitypowered.api.event.lifecycle.network.ListenerClosedEventImpl;
import com.velocitypowered.api.network.ListenerType;
import com.velocitypowered.natives.util.Natives;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.network.pipeline.GS4QueryHandler;
@@ -35,7 +55,7 @@ public final class ConnectionManager {
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
1 << 21);
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
private final Map<SocketAddress, Channel> endpoints = new HashMap<>();
private final Map<SocketAddress, Endpoint> endpoints = new HashMap<>();
private final TransportType transportType;
private final EventLoopGroup bossGroup;
private final EventLoopGroup workerGroup;
@@ -67,7 +87,7 @@ public final class ConnectionManager {
this.resolver = new SeparatePoolInetNameResolver(GlobalEventExecutor.INSTANCE);
this.httpClient = asyncHttpClient(config()
.setEventLoopGroup(this.workerGroup)
.setUserAgent(server.getVersion().getName() + "/" + server.getVersion().getVersion())
.setUserAgent(server.version().getName() + "/" + server.version().getVersion())
.addRequestFilter(new RequestFilter() {
@Override
public <T> FilterContext<T> filter(FilterContext<T> ctx) {
@@ -102,7 +122,7 @@ public final class ConnectionManager {
if (address instanceof InetSocketAddress) {
bootstrap.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.IP_TOS, 0x18);
if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) {
if (transportType == TransportType.EPOLL && server.configuration().useTcpFastOpen()) {
bootstrap.option(EpollChannelOption.TCP_FASTOPEN, 3);
}
}
@@ -111,8 +131,12 @@ public final class ConnectionManager {
.addListener((ChannelFutureListener) future -> {
final Channel channel = future.channel();
if (future.isSuccess()) {
this.endpoints.put(address, channel);
this.endpoints.put(address, new Endpoint(channel, ListenerType.MINECRAFT));
LOGGER.info("Listening on {}", channel.localAddress());
// Fire the proxy bound event after the socket is bound
server.eventManager().fireAndForget(
new ListenerBoundEventImpl(address, ListenerType.MINECRAFT));
} else {
LOGGER.error("Can't bind to {}", address, future.cause());
}
@@ -136,8 +160,12 @@ public final class ConnectionManager {
.addListener((ChannelFutureListener) future -> {
final Channel channel = future.channel();
if (future.isSuccess()) {
this.endpoints.put(address, channel);
this.endpoints.put(address, new Endpoint(channel, ListenerType.QUERY));
LOGGER.info("Listening for GS4 query on {}", channel.localAddress());
// Fire the proxy bound event after the socket is bound
server.eventManager().fireAndForget(
new ListenerBoundEventImpl(address, ListenerType.QUERY));
} else {
LOGGER.error("Can't bind to {}", bootstrap.config().localAddress(), future.cause());
}
@@ -156,10 +184,10 @@ public final class ConnectionManager {
.channelFactory(this.transportType.getClientChannelFactory(target))
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
this.server.getConfiguration().getConnectTimeout())
this.server.configuration().getConnectTimeout())
.group(group == null ? this.workerGroup : group)
.resolver(this.resolver.asGroup());
if (transportType == TransportType.EPOLL && server.getConfiguration().useTcpFastOpen()) {
if (transportType == TransportType.EPOLL && server.configuration().useTcpFastOpen()) {
bootstrap.option(EpollChannelOption.TCP_FASTOPEN_CONNECT, true);
}
return bootstrap;
@@ -171,7 +199,14 @@ public final class ConnectionManager {
* @param oldBind the endpoint to close
*/
public void close(SocketAddress oldBind) {
Channel serverChannel = endpoints.remove(oldBind);
Endpoint endpoint = endpoints.remove(oldBind);
// Fire proxy close event to notify plugins of socket close. We block since plugins
// should have a chance to be notified before the server stops accepting connections.
server.eventManager().fire(new ListenerClosedEventImpl(oldBind, endpoint.getType())).join();
Channel serverChannel = endpoint.getChannel();
Preconditions.checkState(serverChannel != null, "Endpoint %s not registered", oldBind);
LOGGER.info("Closing endpoint {}", serverChannel.localAddress());
serverChannel.close().syncUninterruptibly();
@@ -181,10 +216,17 @@ public final class ConnectionManager {
* Closes all endpoints.
*/
public void shutdown() {
for (final Channel endpoint : this.endpoints.values()) {
for (final Map.Entry<SocketAddress, Endpoint> entry : this.endpoints.entrySet()) {
final SocketAddress address = entry.getKey();
final Endpoint endpoint = entry.getValue();
// Fire proxy close event to notify plugins of socket close. We block since plugins
// should have a chance to be notified before the server stops accepting connections.
server.eventManager().fire(new ListenerClosedEventImpl(address, endpoint.getType())).join();
try {
LOGGER.info("Closing endpoint {}", endpoint.localAddress());
endpoint.close().sync();
LOGGER.info("Closing endpoint {}", address);
endpoint.getChannel().close().sync();
} catch (final InterruptedException e) {
LOGGER.info("Interrupted whilst closing endpoint", e);
Thread.currentThread().interrupt();

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ListenerType;
import io.netty.channel.Channel;
/**
* Represents a listener endpoint.
*/
public final class Endpoint {
private final Channel channel;
private final ListenerType type;
public Endpoint(Channel channel, ListenerType type) {
this.channel = Preconditions.checkNotNull(channel, "channel");
this.type = Preconditions.checkNotNull(type, "type");
}
public Channel getChannel() {
return channel;
}
public ListenerType getType() {
return type;
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
public class HandlerNames {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import com.google.common.base.Strings;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import static com.google.common.base.Preconditions.checkArgument;
@@ -5,6 +22,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.KeyedPluginChannelId;
import com.velocitypowered.api.proxy.messages.PairedPluginChannelId;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import com.velocitypowered.api.util.ProxyVersion;
import com.velocitypowered.proxy.network.packet.AbstractPluginMessagePacket;
import io.netty.buffer.ByteBuf;
@@ -157,6 +177,19 @@ public final class PluginMessageUtil {
}
}
public static String channelIdForVersion(PluginChannelId id, ProtocolVersion version) {
if (id instanceof KeyedPluginChannelId) {
return ((KeyedPluginChannelId) id).key().asString();
} else if (id instanceof PairedPluginChannelId) {
if (version.gte(ProtocolVersion.MINECRAFT_1_13)) {
return ((PairedPluginChannelId) id).modernChannelKey().asString();
} else {
return ((PairedPluginChannelId) id).legacyChannel();
}
}
throw new IllegalArgumentException("Unknown channel ID " + id.getClass().getName());
}
private static final Pattern INVALID_IDENTIFIER_REGEX = Pattern.compile("[^a-z0-9\\-_]*");
/**

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import static com.google.common.base.Preconditions.checkArgument;
@@ -15,7 +32,6 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@@ -23,7 +39,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
@@ -44,7 +59,15 @@ public enum ProtocolUtils {
private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB
private static final QuietDecoderException BAD_VARINT_CACHED =
new QuietDecoderException("Bad varint decoded");
new QuietDecoderException("Bad VarInt decoded");
private static final int[] VARINT_EXACT_BYTE_LENGTHS = new int[33];
static {
for (int i = 0; i <= 32; ++i) {
VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d);
}
VARINT_EXACT_BYTE_LENGTHS[32] = 1; // Special case for the number 0.
}
/**
* Reads a Minecraft-style VarInt from the specified {@code buf}.
@@ -54,7 +77,7 @@ public enum ProtocolUtils {
public static int readVarInt(ByteBuf buf) {
int read = readVarIntSafely(buf);
if (read == Integer.MIN_VALUE) {
throw MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad varint decoded")
throw MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad VarInt decoded")
: BAD_VARINT_CACHED;
}
return read;
@@ -80,29 +103,73 @@ public enum ProtocolUtils {
return Integer.MIN_VALUE;
}
/**
* Returns the exact byte size of {@code value} if it were encoded as a VarInt.
* @param value the value to encode
* @return the byte size of {@code value} if encoded as a VarInt
*/
public static int varIntBytes(int value) {
return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(value)];
}
/**
* Writes a Minecraft-style VarInt to the specified {@code buf}.
* @param buf the buffer to read from
* @param value the integer to write
*/
public static void writeVarInt(ByteBuf buf, int value) {
while (true) {
if ((value & 0xFFFFFF80) == 0) {
buf.writeByte(value);
return;
}
buf.writeByte(value & 0x7F | 0x80);
value >>>= 7;
// Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
// that the proxy will write, to improve inlining.
if ((value & (0xFFFFFFFF << 7)) == 0) {
buf.writeByte(value);
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
buf.writeShort(w);
} else {
writeVarIntFull(buf, value);
}
}
private static void writeVarIntFull(ByteBuf buf, int value) {
// See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
if ((value & (0xFFFFFFFF << 7)) == 0) {
buf.writeByte(value);
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
buf.writeShort(w);
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
buf.writeMedium(w);
} else if ((value & (0xFFFFFFFF << 28)) == 0) {
int w = (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
| ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21);
buf.writeInt(w);
} else {
int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
| ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80);
buf.writeInt(w);
buf.writeByte(value >>> 28);
}
}
/**
* Writes the specified {@code value} as a 21-bit Minecraft VarInt to the specified {@code buf}.
* The upper 11 bits will be discarded.
* @param buf the buffer to read from
* @param value the integer to write
*/
public static void write21BitVarInt(ByteBuf buf, int value) {
// See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
buf.writeMedium(w);
}
public static String readString(ByteBuf buf) {
return readString(buf, DEFAULT_MAX_STRING_SIZE);
}
/**
* Reads a VarInt length-prefixed string from the {@code buf}, making sure to not go over
* Reads a VarInt length-prefixed UTF-8 string from the {@code buf}, making sure to not go over
* {@code cap} size.
* @param buf the buffer to read from
* @param cap the maximum size of the string, in UTF-8 character length

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import static com.velocitypowered.proxy.network.HandlerNames.FRAME_DECODER;
@@ -36,11 +53,11 @@ public class ServerChannelInitializer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(final Channel ch) {
ch.pipeline()
.addLast(READ_TIMEOUT,
new ReadTimeoutHandler(this.server.getConfiguration().getReadTimeout(),
TimeUnit.MILLISECONDS))
.addLast(LEGACY_PING_DECODER, new LegacyPingDecoder())
.addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(READ_TIMEOUT,
new ReadTimeoutHandler(this.server.configuration().getReadTimeout(),
TimeUnit.MILLISECONDS))
.addLast(LEGACY_PING_ENCODER, LegacyPingEncoder.INSTANCE)
.addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE)
.addLast(MINECRAFT_DECODER, new MinecraftDecoder(PacketDirection.SERVERBOUND))
@@ -50,7 +67,7 @@ public class ServerChannelInitializer extends ChannelInitializer<Channel> {
connection.setSessionHandler(new HandshakeSessionHandler(connection, this.server));
ch.pipeline().addLast(HandlerNames.HANDLER, connection);
if (this.server.getConfiguration().isProxyProtocol()) {
if (this.server.configuration().isProxyProtocol()) {
ch.pipeline().addFirst(new HAProxyMessageDecoder());
}
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import static com.google.common.collect.Iterables.getLast;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network;
import com.velocitypowered.proxy.util.concurrent.VelocityNettyThreadFactory;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.buffer;
import com.google.common.io.ByteArrayDataInput;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.buffer;
import com.google.common.io.ByteArrayDataOutput;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.buffer;
import io.netty.buffer.ByteBuf;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
import static com.velocitypowered.proxy.network.PluginMessageUtil.transformLegacyToModernChannel;
@@ -15,9 +32,6 @@ public abstract class AbstractPluginMessagePacket<S extends AbstractPluginMessag
protected static <P extends AbstractPluginMessagePacket<P>> PacketReader<P> decoder(final Factory<P> factory) {
return (buf, version) -> {
String channel = ProtocolUtils.readString(buf);
if (version.gte(ProtocolVersion.MINECRAFT_1_13)) {
channel = transformLegacyToModernChannel(channel);
}
final ByteBuf data;
if (version.gte(ProtocolVersion.MINECRAFT_1_8)) {
data = buf.readRetainedSlice(buf.readableBytes());

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
import com.google.common.base.MoreObjects;
@@ -12,6 +29,7 @@ public abstract class AbstractStatusPingPacket implements Packet {
return factory.apply(randomId);
};
}
protected static <P extends AbstractStatusPingPacket> PacketWriter<P> encoder() {
return (buf, packet, version) -> buf.writeLong(packet.getRandomId());
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
import com.velocitypowered.api.network.ProtocolVersion;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
public enum PacketDirection {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
import com.velocitypowered.proxy.network.packet.clientbound.ClientboundAvailableCommandsPacket;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
import com.velocitypowered.api.network.ProtocolVersion;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet;
import com.velocitypowered.api.network.ProtocolVersion;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;
@@ -14,13 +31,13 @@ import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public class ClientboundDisconnectPacket implements Packet {
public static final PacketReader<ClientboundDisconnectPacket> DECODER = PacketReader.method(ClientboundDisconnectPacket::new);
public static final PacketWriter<ClientboundDisconnectPacket> ENCODER = PacketWriter.deprecatedEncode();
public static final PacketReader<ClientboundDisconnectPacket> DECODER = (buf, version) ->
new ClientboundDisconnectPacket(ProtocolUtils.readString(buf));
public static final PacketWriter<ClientboundDisconnectPacket> ENCODER = (out, packet, version) -> {
ProtocolUtils.writeString(out, packet.reason);
};
private @Nullable String reason;
public ClientboundDisconnectPacket() {
}
private final String reason;
public ClientboundDisconnectPacket(String reason) {
this.reason = Preconditions.checkNotNull(reason, "reason");
@@ -33,23 +50,6 @@ public class ClientboundDisconnectPacket implements Packet {
return reason;
}
public void setReason(@Nullable String reason) {
this.reason = reason;
}
@Override
public void decode(ByteBuf buf, PacketDirection direction, ProtocolVersion version) {
reason = ProtocolUtils.readString(buf);
}
@Override
public void encode(ByteBuf buf, ProtocolVersion version) {
if (reason == null) {
throw new IllegalStateException("No reason specified.");
}
ProtocolUtils.writeString(buf, reason);
}
@Override
public boolean handle(PacketHandler handler) {
return handler.handle(this);

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import static com.velocitypowered.proxy.network.ProtocolUtils.writeString;
@@ -5,6 +22,7 @@ import static com.velocitypowered.proxy.network.ProtocolUtils.writeString;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.network.ProtocolUtils;
import com.velocitypowered.proxy.network.packet.Packet;
import com.velocitypowered.proxy.network.packet.PacketHandler;
import com.velocitypowered.proxy.network.packet.PacketReader;
@@ -12,8 +30,11 @@ import com.velocitypowered.proxy.network.packet.PacketWriter;
import io.netty.buffer.ByteBuf;
public class ClientboundHeaderAndFooterPacket implements Packet {
public static final PacketReader<ClientboundHeaderAndFooterPacket> DECODER = PacketReader.method(ClientboundHeaderAndFooterPacket::new);
public static final PacketWriter<ClientboundHeaderAndFooterPacket> ENCODER = PacketWriter.deprecatedEncode();
public static final PacketReader<ClientboundHeaderAndFooterPacket> DECODER = PacketReader.unsupported();
public static final PacketWriter<ClientboundHeaderAndFooterPacket> ENCODER = (out, packet, version) -> {
writeString(out, packet.header);
writeString(out, packet.footer);
};
private static final String EMPTY_COMPONENT = "{\"translate\":\"\"}";
private static final ClientboundHeaderAndFooterPacket RESET
@@ -31,12 +52,6 @@ public class ClientboundHeaderAndFooterPacket implements Packet {
this.footer = Preconditions.checkNotNull(footer, "footer");
}
@Override
public void encode(ByteBuf buf, ProtocolVersion version) {
writeString(buf, header);
writeString(buf, footer);
}
@Override
public boolean handle(PacketHandler handler) {
return handler.handle(this);

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.collect.ImmutableSet;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.velocitypowered.proxy.network.packet.AbstractKeepAlivePacket;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;
@@ -188,12 +205,12 @@ public class ClientboundPlayerListItemPacket implements Packet {
}
public static Item from(TabListEntry entry) {
return new Item(entry.getProfile().getId())
.setName(entry.getProfile().getName())
.setProperties(entry.getProfile().getProperties())
.setLatency(entry.getLatency())
.setGameMode(entry.getGameMode())
.setDisplayName(entry.getDisplayName().orElse(null));
return new Item(entry.gameProfile().getId())
.setName(entry.gameProfile().getName())
.setProperties(entry.gameProfile().getProperties())
.setLatency(entry.ping())
.setGameMode(entry.gameMode())
.setDisplayName(entry.displayName().orElse(null));
}
public @Nullable UUID getUuid() {

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.velocitypowered.proxy.network.packet.AbstractPluginMessagePacket;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;
@@ -16,7 +33,10 @@ public class ClientboundResourcePackRequestPacket implements Packet {
final String hash = ProtocolUtils.readString(buf);
return new ClientboundResourcePackRequestPacket(url, hash);
};
public static final PacketWriter<ClientboundResourcePackRequestPacket> ENCODER = PacketWriter.deprecatedEncode();
public static final PacketWriter<ClientboundResourcePackRequestPacket> ENCODER = (out, packet, version) -> {
ProtocolUtils.writeString(out, packet.url);
ProtocolUtils.writeString(out, packet.hash);
};
private final String url;
private final String hash;
@@ -26,12 +46,6 @@ public class ClientboundResourcePackRequestPacket implements Packet {
this.hash = Objects.requireNonNull(hash, "hash");
}
@Override
public void encode(ByteBuf buf, ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, url);
ProtocolUtils.writeString(buf, hash);
}
@Override
public boolean handle(PacketHandler handler) {
return handler.handle(this);

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;
@@ -25,7 +42,16 @@ public class ClientboundServerLoginSuccessPacket implements Packet {
final String username = ProtocolUtils.readString(buf, 16);
return new ClientboundServerLoginSuccessPacket(uuid, username);
};
public static final PacketWriter<ClientboundServerLoginSuccessPacket> ENCODER = PacketWriter.deprecatedEncode();
public static final PacketWriter<ClientboundServerLoginSuccessPacket> ENCODER = (out, packet, version) -> {
if (version.gte(ProtocolVersion.MINECRAFT_1_16)) {
ProtocolUtils.writeUuidIntArray(out, packet.uuid);
} else if (version.gte(ProtocolVersion.MINECRAFT_1_7_6)) {
ProtocolUtils.writeString(out, packet.uuid.toString());
} else {
ProtocolUtils.writeString(out, UuidUtils.toUndashed(packet.uuid));
}
ProtocolUtils.writeString(out, packet.username);
};
private final UUID uuid;
private final String username;
@@ -35,18 +61,6 @@ public class ClientboundServerLoginSuccessPacket implements Packet {
this.username = Objects.requireNonNull(username, "username");
}
@Override
public void encode(ByteBuf buf, ProtocolVersion version) {
if (version.gte(ProtocolVersion.MINECRAFT_1_16)) {
ProtocolUtils.writeUuidIntArray(buf, uuid);
} else if (version.gte(ProtocolVersion.MINECRAFT_1_7_6)) {
ProtocolUtils.writeString(buf, uuid.toString());
} else {
ProtocolUtils.writeString(buf, UuidUtils.toUndashed(uuid));
}
ProtocolUtils.writeString(buf, username);
}
@Override
public boolean handle(PacketHandler handler) {
return handler.handle(this);

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.velocitypowered.proxy.network.packet.AbstractStatusPingPacket;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;
@@ -16,7 +33,7 @@ public class ClientboundStatusResponsePacket implements Packet {
return new ClientboundStatusResponsePacket(status);
};
public static final PacketWriter<ClientboundStatusResponsePacket> ENCODER = (buf, packet, version) ->
ProtocolUtils.writeString(buf, packet.status);
ProtocolUtils.writeString(buf, packet.status);
private final @Nullable CharSequence status;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.clientbound;
import com.google.common.base.MoreObjects;

View File

@@ -1,3 +1,20 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.network.packet.legacy;
import com.google.common.collect.ImmutableList;
@@ -28,7 +45,7 @@ public class LegacyDisconnectPacket implements LegacyPacket {
*/
public static LegacyDisconnectPacket fromServerPing(ServerPing response,
LegacyMinecraftPingVersion version) {
Players players = response.getPlayers().orElse(FAKE_PLAYERS);
Players players = response.players().orElse(FAKE_PLAYERS);
switch (version) {
case MINECRAFT_1_3:
@@ -37,20 +54,20 @@ public class LegacyDisconnectPacket implements LegacyPacket {
// MOTD.
return new LegacyDisconnectPacket(String.join(LEGACY_COLOR_CODE,
cleanSectionSymbol(getFirstLine(PlainComponentSerializer.plain().serialize(
response.getDescription()))),
Integer.toString(players.getOnline()),
Integer.toString(players.getMax())));
response.description()))),
Integer.toString(players.online()),
Integer.toString(players.maximum())));
case MINECRAFT_1_4:
case MINECRAFT_1_6:
// Minecraft 1.4-1.6 provide support for more fields, and additionally support color codes.
return new LegacyDisconnectPacket(String.join("\0",
LEGACY_COLOR_CODE + "1",
Integer.toString(response.getVersion().getProtocol()),
response.getVersion().getName(),
Integer.toString(response.version().protocol()),
response.version().name(),
getFirstLine(LegacyComponentSerializer.legacySection().serialize(response
.getDescription())),
Integer.toString(players.getOnline()),
Integer.toString(players.getMax())
.description())),
Integer.toString(players.online()),
Integer.toString(players.maximum())
));
default:
throw new IllegalArgumentException("Unknown version " + version);

Some files were not shown because too many files have changed in this diff Show More