1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Added PMD Plugin and changed isBedrockPlayer to isFloodgatePlayer

This commit is contained in:
Tim203
2021-01-16 16:52:04 +01:00
parent 4cf92c6551
commit b737184fb0
44 changed files with 392 additions and 225 deletions

View File

@@ -36,7 +36,7 @@ public interface FloodgateApi {
* Returns the Floodgate API instance. * Returns the Floodgate API instance.
*/ */
static FloodgateApi getInstance() { static FloodgateApi getInstance() {
return InstanceHolder.getInstance(); return InstanceHolder.getApi();
} }
/** /**
@@ -45,7 +45,7 @@ public interface FloodgateApi {
* @param uuid The uuid of the <b>online</b> player * @param uuid The uuid of the <b>online</b> player
* @return true if the given <b>online</b> player is a Bedrock player * @return true if the given <b>online</b> player is a Bedrock player
*/ */
boolean isBedrockPlayer(UUID uuid); boolean isFloodgatePlayer(UUID uuid);
/** /**
* Get info about the given Bedrock player * Get info about the given Bedrock player
@@ -66,7 +66,7 @@ public interface FloodgateApi {
/** /**
* Checks if the uuid of the player has the {@link #createJavaPlayerId(long)} format. This * Checks if the uuid of the player has the {@link #createJavaPlayerId(long)} format. This
* method can't validate a linked player uuid, since that doesn't equal the format. Use {@link * method can't validate a linked player uuid, since that doesn't equal the format. Use {@link
* #isBedrockPlayer(UUID)} if you want to include linked accounts. * #isFloodgatePlayer(UUID)} if you want to include linked accounts.
* *
* @param uuid the uuid to check * @param uuid the uuid to check
* @return true if the given uuid has the correct format. * @return true if the given uuid has the correct format.

View File

@@ -32,25 +32,28 @@ import org.geysermc.floodgate.api.inject.PlatformInjector;
import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.link.PlayerLink;
public final class InstanceHolder { public final class InstanceHolder {
@Getter private static FloodgateApi instance; @Getter private static FloodgateApi api;
@Getter private static PlayerLink playerLink; @Getter private static PlayerLink playerLink;
@Getter private static PlatformInjector injector; @Getter private static PlatformInjector injector;
@Getter private static HandshakeHandlers handshakeHandlers; @Getter private static HandshakeHandlers handshakeHandlers;
private static UUID key; private static UUID storedKey;
public static boolean setInstance( public static boolean set(
FloodgateApi floodgateApi, FloodgateApi floodgateApi,
PlayerLink link, PlayerLink link,
PlatformInjector platformInjector, PlatformInjector platformInjector,
HandshakeHandlers handshakeHandlers, HandshakeHandlers handshakeHandlers,
UUID key UUID key) {
) {
if (instance == null) { if (storedKey != null) {
InstanceHolder.key = key; if (!storedKey.equals(key)) {
} else if (!InstanceHolder.key.equals(key)) {
return false; return false;
} }
instance = floodgateApi; } else {
storedKey = key;
}
api = floodgateApi;
playerLink = link; playerLink = link;
injector = platformInjector; injector = platformInjector;
InstanceHolder.handshakeHandlers = handshakeHandlers; InstanceHolder.handshakeHandlers = handshakeHandlers;
@@ -59,6 +62,6 @@ public final class InstanceHolder {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends FloodgateApi> T castApi(Class<T> cast) { public static <T extends FloodgateApi> T castApi(Class<T> cast) {
return (T) instance; return (T) api;
} }
} }

View File

@@ -27,6 +27,7 @@ package org.geysermc.floodgate.api.link;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.util.LinkedPlayer; import org.geysermc.floodgate.util.LinkedPlayer;
/** /**
@@ -49,7 +50,8 @@ public interface PlayerLink {
* @return a completable future with the {@link LinkedPlayer}. The future will have a null value * @return a completable future with the {@link LinkedPlayer}. The future will have a null value
* if that Bedrock player isn't linked * if that Bedrock player isn't linked
*/ */
CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId); @NonNull
CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId);
/** /**
* Tells if the given player is a linked player * Tells if the given player is a linked player
@@ -57,7 +59,8 @@ public interface PlayerLink {
* @param playerId the uuid of the player to check, can be both a Java or a Bedrock uuid * @param playerId the uuid of the player to check, can be both a Java or a Bedrock uuid
* @return true if the player is a linked player * @return true if the player is a linked player
*/ */
CompletableFuture<Boolean> isLinkedPlayer(UUID playerId); @NonNull
CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId);
/** /**
* Links a Java account to a Bedrock account. * Links a Java account to a Bedrock account.
@@ -67,7 +70,11 @@ public interface PlayerLink {
* @param username the username of the Java player * @param username the username of the Java player
* @return a future holding void on success or completed exceptionally when failed * @return a future holding void on success or completed exceptionally when failed
*/ */
CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID javaId, String username); @NonNull
CompletableFuture<Void> linkPlayer(
@NonNull UUID bedrockId,
@NonNull UUID javaId,
@NonNull String username);
/** /**
* Unlinks a Java account from a Bedrock account. * Unlinks a Java account from a Bedrock account.
@@ -75,7 +82,8 @@ public interface PlayerLink {
* @param javaId the uuid of the Java player * @param javaId the uuid of the Java player
* @return a future holding void on success or completed exceptionally when failed * @return a future holding void on success or completed exceptionally when failed
*/ */
CompletableFuture<Void> unlinkPlayer(UUID javaId); @NonNull
CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId);
/** /**
* Creates a link request for the given Java player. * Creates a link request for the given Java player.
@@ -86,10 +94,11 @@ public interface PlayerLink {
* @return a future holding the result of the link request which will be a {@link * @return a future holding the result of the link request which will be a {@link
* LinkRequestResult} on failure and the link code (string) on success * LinkRequestResult} on failure and the link code (string) on success
*/ */
@NonNull
CompletableFuture<?> createLinkRequest( CompletableFuture<?> createLinkRequest(
UUID javaId, @NonNull UUID javaId,
String javaUsername, @NonNull String javaUsername,
String bedrockUsername @NonNull String bedrockUsername
); );
/** /**
@@ -101,11 +110,12 @@ public interface PlayerLink {
* @param code the code created in {@link #createLinkRequest(UUID, String, String)} * @param code the code created in {@link #createLinkRequest(UUID, String, String)}
* @return a future holding the result of the link verification * @return a future holding the result of the link verification
*/ */
@NonNull
CompletableFuture<LinkRequestResult> verifyLinkRequest( CompletableFuture<LinkRequestResult> verifyLinkRequest(
UUID bedrockId, @NonNull UUID bedrockId,
String javaUsername, @NonNull String javaUsername,
String bedrockUsername, @NonNull String bedrockUsername,
String code @NonNull String code
); );
/** /**

View File

@@ -51,18 +51,6 @@ public class PropertyKey {
this.removable = removable; this.removable = removable;
} }
@Override
public boolean equals(Object obj) {
if (obj instanceof PropertyKey) {
return key.equals(((PropertyKey) obj).key);
}
if (obj instanceof String) {
return key.equals(obj);
}
return false;
}
public Result isAddAllowed(Object obj) { //todo use for add and remove public Result isAddAllowed(Object obj) { //todo use for add and remove
if (obj instanceof PropertyKey) { if (obj instanceof PropertyKey) {
PropertyKey propertyKey = (PropertyKey) obj; PropertyKey propertyKey = (PropertyKey) obj;

View File

@@ -106,16 +106,19 @@ public final class BungeeDataHandler {
return; return;
} }
switch (result.getResultType()) { if (result.getResultType() == ResultType.EXCEPTION) {
case EXCEPTION:
event.setCancelReason(config.getDisconnect().getInvalidKey()); event.setCancelReason(config.getDisconnect().getInvalidKey());
break; event.completeIntent(plugin);
case INVALID_DATA_LENGTH: return;
}
if (result.getResultType() == ResultType.INVALID_DATA_LENGTH) {
event.setCancelReason(TextComponent.fromLegacyText(String.format( event.setCancelReason(TextComponent.fromLegacyText(String.format(
config.getDisconnect().getInvalidArgumentsLength(), config.getDisconnect().getInvalidArgumentsLength(),
BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength() BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength()
))); )));
break; event.completeIntent(plugin);
return;
} }
// only continue when SUCCESS // only continue when SUCCESS
@@ -155,7 +158,7 @@ public final class BungeeDataHandler {
public void handleServerConnect(ProxiedPlayer player) { public void handleServerConnect(ProxiedPlayer player) {
// Passes the information through to the connecting server if enabled // Passes the information through to the connecting server if enabled
if (config.isSendFloodgateData() && api.isBedrockPlayer(player.getUniqueId())) { if (config.isSendFloodgateData() && api.isFloodgatePlayer(player.getUniqueId())) {
Handshake handshake = ReflectionUtils.getCastedValue( Handshake handshake = ReflectionUtils.getCastedValue(
player.getPendingConnection(), CACHED_HANDSHAKE_PACKET player.getPendingConnection(), CACHED_HANDSHAKE_PACKET
); );

View File

@@ -25,7 +25,9 @@
package org.geysermc.floodgate.pluginmessage; package org.geysermc.floodgate.pluginmessage;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -165,7 +167,7 @@ public final class BungeePluginMessageHandler extends PluginMessageHandler imple
try { try {
Reader reader = new InputStreamReader(new ByteArrayInputStream(responseData)); Reader reader = new InputStreamReader(new ByteArrayInputStream(responseData));
response = GSON.fromJson(reader, JsonObject.class); response = GSON.fromJson(reader, JsonObject.class);
} catch (Throwable throwable) { } catch (JsonIOException | JsonSyntaxException throwable) {
logger.error("Failed to read Skin response", throwable); logger.error("Failed to read Skin response", throwable);
return; return;
} }

View File

@@ -116,14 +116,14 @@ public final class BungeeCommandUtil implements CommandUtil {
break; break;
case ONLY_JAVA: case ONLY_JAVA:
for (ProxiedPlayer player : players) { for (ProxiedPlayer player : players) {
if (!api.isBedrockPlayer(player.getUniqueId())) { if (!api.isFloodgatePlayer(player.getUniqueId())) {
usernames.add(player.getName()); usernames.add(player.getName());
} }
} }
break; break;
case ONLY_BEDROCK: case ONLY_BEDROCK:
for (ProxiedPlayer player : players) { for (ProxiedPlayer player : players) {
if (api.isBedrockPlayer(player.getUniqueId())) { if (api.isFloodgatePlayer(player.getUniqueId())) {
usernames.add(player.getName()); usernames.add(player.getName());
} }
} }

View File

@@ -29,6 +29,7 @@ import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.UUID; import java.util.UUID;
@@ -56,8 +57,12 @@ public class FloodgatePlatform {
private Injector guice; private Injector guice;
@Inject @Inject
public FloodgatePlatform(FloodgateApi api, PlatformInjector platformInjector, public FloodgatePlatform(
FloodgateLogger logger, Injector guice) { FloodgateApi api,
PlatformInjector platformInjector,
FloodgateLogger logger,
Injector guice) {
this.api = api; this.api = api;
this.injector = platformInjector; this.injector = platformInjector;
this.logger = logger; this.logger = logger;
@@ -69,15 +74,14 @@ public class FloodgatePlatform {
@Named("dataDirectory") Path dataDirectory, @Named("dataDirectory") Path dataDirectory,
ConfigLoader configLoader, ConfigLoader configLoader,
FloodgateConfigHolder configHolder, FloodgateConfigHolder configHolder,
HandshakeHandlers handshakeHandlers HandshakeHandlers handshakeHandlers) {
) {
if (!Files.isDirectory(dataDirectory)) { if (!Files.isDirectory(dataDirectory)) {
try { try {
Files.createDirectory(dataDirectory); Files.createDirectory(dataDirectory);
} catch (Exception exception) { } catch (IOException exception) {
logger.error("Failed to create the data folder", exception); logger.error("Failed to create the data folder", exception);
throw new RuntimeException("Failed to create the data folder"); throw new RuntimeException("Failed to create the data folder", exception);
} }
} }
@@ -90,7 +94,7 @@ public class FloodgatePlatform {
guice = guice.createChildInjector(new ConfigLoadedModule(config)); guice = guice.createChildInjector(new ConfigLoadedModule(config));
PlayerLink link = guice.getInstance(PlayerLinkLoader.class).load(); PlayerLink link = guice.getInstance(PlayerLinkLoader.class).load();
InstanceHolder.setInstance(api, link, this.injector, handshakeHandlers, KEY); InstanceHolder.set(api, link, this.injector, handshakeHandlers, KEY);
} }
public boolean enable(Module... postInitializeModules) { public boolean enable(Module... postInitializeModules) {

View File

@@ -31,6 +31,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.inject.CommonPlatformInjector;
import org.geysermc.floodgate.util.Constants;
@RequiredArgsConstructor @RequiredArgsConstructor
public final class AddonManagerHandler extends MessageToByteEncoder<ByteBuf> { public final class AddonManagerHandler extends MessageToByteEncoder<ByteBuf> {
@@ -57,8 +58,7 @@ public final class AddonManagerHandler extends MessageToByteEncoder<ByteBuf> {
} }
int index = msg.readerIndex(); int index = msg.readerIndex();
// LoginSuccess packet = 2 if (readVarInt(msg) == Constants.LOGIN_SUCCESS_PACKET_ID) {
if (readVarInt(msg) == 2) {
loggedIn = true; loggedIn = true;
injector.loginSuccessCall(channel); injector.loginSuccessCall(channel);
} }

View File

@@ -43,7 +43,7 @@ public class SimpleFloodgateApi implements FloodgateApi {
private final PluginMessageHandler pluginMessageHandler; private final PluginMessageHandler pluginMessageHandler;
@Override @Override
public boolean isBedrockPlayer(UUID uuid) { public boolean isFloodgatePlayer(UUID uuid) {
return getPlayer(uuid) != null; return getPlayer(uuid) != null;
} }

View File

@@ -35,6 +35,7 @@ import cloud.commandframework.context.CommandContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.kyori.adventure.text.Component;
import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.link.LinkRequestResult; import org.geysermc.floodgate.api.link.LinkRequestResult;
import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.link.PlayerLink;
@@ -74,7 +75,7 @@ public final class LinkAccountCommand implements FloodgateCommand {
} }
// when the player is a Bedrock player // when the player is a Bedrock player
if (api.isBedrockPlayer(sender.uuid())) { if (api.isFloodgatePlayer(sender.uuid())) {
if (!context.contains("code")) { if (!context.contains("code")) {
sender.sendMessage(Message.BEDROCK_USAGE); sender.sendMessage(Message.BEDROCK_USAGE);
return; return;
@@ -107,6 +108,9 @@ public final class LinkAccountCommand implements FloodgateCommand {
case LINK_COMPLETED: case LINK_COMPLETED:
sender.disconnect(Message.LINK_REQUEST_COMPLETED, targetName); sender.disconnect(Message.LINK_REQUEST_COMPLETED, targetName);
break; break;
default:
sender.disconnect(Component.text("Invalid account linking result"));
break;
} }
}); });
return; return;

View File

@@ -47,7 +47,7 @@ public class FloodgateConfig {
private boolean debug; private boolean debug;
private int configVersion; private int configVersion;
private Key key = null; private Key key;
public void setKey(Key key) { public void setKey(Key key) {
if (this.key == null) { if (this.key == null) {

View File

@@ -43,8 +43,8 @@ public class ConfigInitializer {
private static final Yaml YAML; private static final Yaml YAML;
static { static {
Constructor constructor = new CustomClassLoaderConstructor( Constructor constructor =
ConfigInitializer.class.getClassLoader()); new CustomClassLoaderConstructor(ConfigInitializer.class.getClassLoader());
constructor.setPropertyUtils(new PropertyUtils() { constructor.setPropertyUtils(new PropertyUtils() {
@Override @Override
@@ -54,8 +54,11 @@ public class ConfigInitializer {
return properties; return properties;
} }
private void getPropertiesFromClass(Class<?> type, Class<?> stopAfter, private void getPropertiesFromClass(
Class<?> type,
Class<?> stopAfter,
Map<String, Property> propertyMap) { Map<String, Property> propertyMap) {
Class<?> current = type; Class<?> current = type;
while (!Object.class.equals(current)) { while (!Object.class.equals(current)) {
for (Field field : current.getDeclaredFields()) { for (Field field : current.getDeclaredFields()) {
@@ -98,7 +101,8 @@ public class ConfigInitializer {
YAML = new Yaml(constructor); YAML = new Yaml(constructor);
} }
public static <T extends FloodgateConfig> T initializeFrom(InputStream dataStream, public static <T extends FloodgateConfig> T initializeFrom(
InputStream dataStream,
Class<T> configClass) { Class<T> configClass) {
return YAML.loadAs(dataStream, configClass); return YAML.loadAs(dataStream, configClass);
} }

View File

@@ -86,7 +86,8 @@ public final class ConfigLoader {
} }
} catch (Exception exception) { } catch (Exception exception) {
logger.error("Error while loading config", exception); logger.error("Error while loading config", exception);
throw new RuntimeException("Failed to load the config! Try to delete the config file"); throw new RuntimeException(
"Failed to load the config! Try to delete the config file", exception);
} }
Path keyPath = dataFolder.resolve(configInstance.getKeyFileName()); Path keyPath = dataFolder.resolve(configInstance.getKeyFileName());
@@ -125,7 +126,7 @@ public final class ConfigLoader {
configInstance.setKey(key); configInstance.setKey(key);
} catch (IOException exception) { } catch (IOException exception) {
logger.error("Error while reading the key", exception); logger.error("Error while reading the key", exception);
throw new RuntimeException("Failed to read the key!"); throw new RuntimeException("Failed to read the key!", exception);
} }
return configInstance; return configInstance;

View File

@@ -51,9 +51,13 @@ public final class ConfigFileUpdater {
* @param defaultConfigLocation the location of the default Floodgate config * @param defaultConfigLocation the location of the default Floodgate config
* @throws IOException if an I/O error occurs * @throws IOException if an I/O error occurs
*/ */
public void update(Path configLocation, Map<String, Object> currentVersion, public void update(
Map<String, String> renames, String defaultConfigLocation) Path configLocation,
Map<String, Object> currentVersion,
Map<String, String> renames,
String defaultConfigLocation)
throws IOException { throws IOException {
List<String> notFound = new ArrayList<>(); List<String> notFound = new ArrayList<>();
List<String> newConfig = defaultConfigHandler.loadDefaultConfig(defaultConfigLocation); List<String> newConfig = defaultConfigHandler.loadDefaultConfig(defaultConfigLocation);
@@ -64,7 +68,7 @@ public final class ConfigFileUpdater {
for (int i = 0; i < newConfig.size(); i++) { for (int i = 0; i < newConfig.size(); i++) {
line = newConfig.get(i); line = newConfig.get(i);
// we don't have to check comments or empty lines // we don't have to check comments or empty lines
if (line.length() == 0 || line.startsWith("#")) { if (line.isEmpty() || line.charAt(0) == '#') {
continue; continue;
} }
@@ -74,7 +78,7 @@ public final class ConfigFileUpdater {
} }
// end of subcategory // end of subcategory
if (spaces.length() > 0 && currentSpaces.length() < spaces.length()) { if (!spaces.isEmpty() && currentSpaces.length() < spaces.length()) {
// we can assume this since we don't allow subcategories of subcategories // we can assume this since we don't allow subcategories of subcategories
spaces = ""; spaces = "";
map = null; map = null;
@@ -134,7 +138,7 @@ public final class ConfigFileUpdater {
logger.info("Successfully updated the config file! " + logger.info("Successfully updated the config file! " +
"Your old config has been moved to config-old.yml"); "Your old config has been moved to config-old.yml");
if (notFound.size() > 0) { if (!notFound.isEmpty()) {
StringBuilder messageBuilder = new StringBuilder( StringBuilder messageBuilder = new StringBuilder(
"Please note that the following keys we not found in the old config and " + "Please note that the following keys we not found in the old config and " +
"are now using the default Floodgate config value. Missing/new keys: "); "are now using the default Floodgate config value. Missing/new keys: ");

View File

@@ -48,16 +48,15 @@ public final class ConfigUpdater {
public void update(String defaultConfigLocation) { public void update(String defaultConfigLocation) {
Path configLocation = dataFolder.resolve("config.yml"); Path configLocation = dataFolder.resolve("config.yml");
BufferedReader configReader; Map<String, Object> config;
try {
configReader = Files.newBufferedReader(configLocation); try (BufferedReader configReader = Files.newBufferedReader(configLocation)) {
config = new Yaml().load(configReader);
} catch (IOException exception) { } catch (IOException exception) {
logger.error("Error while opening the config file", exception); logger.error("Error while opening the config file", exception);
throw new RuntimeException("Failed to update config"); throw new RuntimeException("Failed to update config", exception);
} }
Map<String, Object> config = new Yaml().load(configReader);
// new name -> old name // new name -> old name
Map<String, String> renames = new HashMap<>(); Map<String, String> renames = new HashMap<>();
@@ -93,7 +92,7 @@ public final class ConfigUpdater {
fileUpdater.update(configLocation, config, renames, defaultConfigLocation); fileUpdater.update(configLocation, config, renames, defaultConfigLocation);
} catch (IOException exception) { } catch (IOException exception) {
logger.error("Error while updating the config file", exception); logger.error("Error while updating the config file", exception);
throw new RuntimeException("Failed to update config"); throw new RuntimeException("Failed to update config", exception);
} }
} }
} }

View File

@@ -42,6 +42,8 @@ final class DisabledPlayerLink implements PlayerLink {
public void load() { public void load() {
} }
//todo don't return null
@Override @Override
public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) { public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) {
return null; return null;

View File

@@ -99,7 +99,7 @@ public class GlobalPlayerLinking extends CommonPlayerLink {
// player linking and unlinking now goes through the global player linking server. // player linking and unlinking now goes through the global player linking server.
// so individual servers can't register nor unlink players. // so individual servers can't register nor unlink players.
//todo probably return a failed future instead of returning null? //todo don't return null
@Override @Override
public CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID javaId, String username) { public CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID javaId, String username) {

View File

@@ -47,6 +47,7 @@ public final class LinkRequestImpl implements LinkRequest {
requestTime = Instant.now().getEpochSecond(); requestTime = Instant.now().getEpochSecond();
} }
@Override
public boolean isExpired(long linkTimeout) { public boolean isExpired(long linkTimeout) {
long timePassed = Instant.now().getEpochSecond() - requestTime; long timePassed = Instant.now().getEpochSecond() - requestTime;
return timePassed > linkTimeout; return timePassed > linkTimeout;

View File

@@ -40,6 +40,7 @@ import java.net.URLClassLoader;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Named; import javax.inject.Named;
import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.link.PlayerLink;
@@ -61,13 +62,13 @@ public final class PlayerLinkLoader {
throw new IllegalStateException("Config cannot be null!"); throw new IllegalStateException("Config cannot be null!");
} }
FloodgateConfig.PlayerLinkConfig linkConfig = config.getPlayerLink(); FloodgateConfig.PlayerLinkConfig linkingConfig = config.getPlayerLink();
if (!linkConfig.isEnabled()) { if (!linkingConfig.isEnabled()) {
return new DisabledPlayerLink(); return new DisabledPlayerLink();
} }
// we use our own internal PlayerLinking when global linking is enabled // we use our own internal PlayerLinking when global linking is enabled
if (linkConfig.isUseGlobalLinking()) { if (linkingConfig.isUseGlobalLinking()) {
return injector.getInstance(GlobalPlayerLinking.class); return injector.getInstance(GlobalPlayerLinking.class);
} }
@@ -81,7 +82,7 @@ public final class PlayerLinkLoader {
return null; return null;
} }
if (files.size() == 0) { if (files.isEmpty()) {
logger.error("Failed to find a database implementation"); logger.error("Failed to find a database implementation");
return null; return null;
} }
@@ -89,17 +90,20 @@ public final class PlayerLinkLoader {
Path implementationPath = files.get(0); Path implementationPath = files.get(0);
// We only want to load one database implementation // We only want to load one database implementation
String type = linkConfig.getType().toLowerCase();
if (files.size() > 1) { if (files.size() > 1) {
implementationPath = null; boolean found = false;
String type = linkingConfig.getType().toLowerCase(Locale.ROOT);
for (Path path : files) { for (Path path : files) {
if (path.getFileName().toString().toLowerCase().contains(type)) { if (path.getFileName().toString().toLowerCase(Locale.ROOT).contains(type)) {
implementationPath = path; implementationPath = path;
found = true;
} }
} }
if (implementationPath == null) {
if (!found) {
logger.error("Failed to find an implementation for type: {}", logger.error("Failed to find an implementation for type: {}",
linkConfig.getType()); linkingConfig.getType());
return null; return null;
} }
} }
@@ -107,23 +111,29 @@ public final class PlayerLinkLoader {
Class<? extends PlayerLink> mainClass; Class<? extends PlayerLink> mainClass;
try { try {
URL pluginUrl = implementationPath.toUri().toURL(); URL pluginUrl = implementationPath.toUri().toURL();
URLClassLoader classLoader = new URLClassLoader(
new URL[]{pluginUrl}, try (URLClassLoader classLoader = new URLClassLoader(
PlayerLinkLoader.class.getClassLoader() new URL[]{pluginUrl}, PlayerLinkLoader.class.getClassLoader())) {
String mainClassName;
try (InputStream linkConfigStream =
classLoader.getResourceAsStream("config.json")) {
requireNonNull(linkConfigStream, "Implementation should have a config");
JsonObject linkConfig = new Gson().fromJson(
new InputStreamReader(linkConfigStream), JsonObject.class
); );
InputStream linkImplConfigStream = classLoader.getResourceAsStream("config.json"); mainClassName = linkConfig.get("mainClass").getAsString();
requireNonNull(linkImplConfigStream, "Database implementation should contain a config"); }
JsonObject linkImplConfig = new Gson().fromJson(
new InputStreamReader(linkImplConfigStream), JsonObject.class
);
String mainClassName = linkImplConfig.get("mainClass").getAsString();
mainClass = (Class<? extends PlayerLink>) classLoader.loadClass(mainClassName); mainClass = (Class<? extends PlayerLink>) classLoader.loadClass(mainClassName);
}
} catch (ClassCastException exception) { } catch (ClassCastException exception) {
logger.error("The database implementation ({}) doesn't extend the PlayerLink class!", logger.error("The database implementation ({}) doesn't extend the PlayerLink class!",
implementationPath.getFileName().toString()); implementationPath.getFileName().toString(), exception);
return null; return null;
} catch (Exception exception) { } catch (Exception exception) {
logger.error("Error while loading database jar", exception); logger.error("Error while loading database jar", exception);

View File

@@ -37,7 +37,7 @@ import org.geysermc.floodgate.util.LanguageManager;
public final class JavaUtilFloodgateLogger implements FloodgateLogger { public final class JavaUtilFloodgateLogger implements FloodgateLogger {
private final Logger logger; private final Logger logger;
private final LanguageManager languageManager; private final LanguageManager languageManager;
private Level originLevel = null; private Level originLevel;
@Override @Override
public void error(String message, Object... args) { public void error(String message, Object... args) {

View File

@@ -51,7 +51,7 @@ public interface CommandMessage {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (int i = 0; i < translateParts.length; i++) { for (int i = 0; i < translateParts.length; i++) {
builder.append(manager.getString(translateParts[i], locale, args)); builder.append(manager.getString(translateParts[i], locale, args));
if (++i != translateParts.length) { if (translateParts.length != i + 1) {
builder.append(" "); builder.append(" ");
} }
} }

View File

@@ -53,7 +53,7 @@ public abstract class PluginMessageHandler {
} }
public void sendSkinResponse(UUID player, boolean failed, String response) { public void sendSkinResponse(UUID player, boolean failed, String response) {
return; // Proxy implementations don't send responses // Proxy implementations don't send responses
} }
protected byte[] createFormData(Form form) { protected byte[] createFormData(Form form) {

View File

@@ -154,10 +154,12 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
CompletableFuture.completedFuture(null); CompletableFuture.completedFuture(null);
} }
@Override
public UUID getCorrectUniqueId() { public UUID getCorrectUniqueId() {
return linkedPlayer != null ? linkedPlayer.getJavaUniqueId() : javaUniqueId; return linkedPlayer != null ? linkedPlayer.getJavaUniqueId() : javaUniqueId;
} }
@Override
public String getCorrectUsername() { public String getCorrectUsername() {
return linkedPlayer != null ? linkedPlayer.getJavaUsername() : javaUsername; return linkedPlayer != null ? linkedPlayer.getJavaUsername() : javaUsername;
} }

View File

@@ -64,9 +64,8 @@ public final class HandshakeHandler {
private final AttributeKey<FloodgatePlayer> playerAttribute; private final AttributeKey<FloodgatePlayer> playerAttribute;
private final FloodgateLogger logger; private final FloodgateLogger logger;
public HandshakeResult handle(Channel channel, @NonNull String hostname) { public HandshakeResult handle(Channel channel, @NonNull String originalHostname) {
try { String[] split = originalHostname.split("\0");
String[] split = hostname.split("\0");
String data = null; String data = null;
StringBuilder hostnameBuilder = new StringBuilder(); StringBuilder hostnameBuilder = new StringBuilder();
@@ -78,7 +77,7 @@ public final class HandshakeHandler {
hostnameBuilder.append(value); hostnameBuilder.append(value);
} }
// hostname now doesn't have Floodgate data anymore if it had // hostname now doesn't have Floodgate data anymore if it had
hostname = hostnameBuilder.toString(); String hostname = hostnameBuilder.toString();
if (data == null) { if (data == null) {
return callHandlerAndReturnResult( return callHandlerAndReturnResult(
@@ -91,6 +90,7 @@ public final class HandshakeHandler {
Base64Utils.getEncodedLength(AesCipher.IV_LENGTH); Base64Utils.getEncodedLength(AesCipher.IV_LENGTH);
int lastSplitIndex = data.lastIndexOf(0x21); int lastSplitIndex = data.lastIndexOf(0x21);
try {
byte[] floodgateData; byte[] floodgateData;
byte[] rawSkinData = null; byte[] rawSkinData = null;
@@ -181,8 +181,8 @@ public final class HandshakeHandler {
ResultType resultType, ResultType resultType,
Channel channel, Channel channel,
BedrockData bedrockData, BedrockData bedrockData,
String hostname String hostname) {
) {
HandshakeData handshakeData = new HandshakeDataImpl(channel, bedrockData != null, HandshakeData handshakeData = new HandshakeDataImpl(channel, bedrockData != null,
bedrockData, null, null, hostname, null); bedrockData, null, null, hostname, null);
handshakeHandlers.callHandshakeHandlers(handshakeData); handshakeHandlers.callHandshakeHandlers(handshakeData);

View File

@@ -46,6 +46,7 @@ public interface UserAudience extends Identified, Identity, Audience {
boolean hasPermission(@NonNull final String permission); boolean hasPermission(@NonNull final String permission);
@Override
void sendMessage(final @NonNull Identity source, void sendMessage(final @NonNull Identity source,
final @NonNull Component message, final @NonNull Component message,
final @NonNull MessageType type); final @NonNull MessageType type);

View File

@@ -31,6 +31,7 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Queue; import java.util.Queue;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -154,13 +155,18 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
final @NonNull CommandContext<UserAudience> commandContext, final @NonNull CommandContext<UserAudience> commandContext,
final @NonNull String input final @NonNull String input
) { ) {
CommandUtil commandUtil = commandContext.get("CommandUtil"); final CommandUtil commandUtil = commandContext.get("CommandUtil");
final String trimmedInput = input.trim();
final String lowercaseInput = input.toLowerCase().trim(); if (trimmedInput.isEmpty()) {
return ImmutableList.copyOf(commandUtil.getOnlineUsernames(limitTo));
}
final String lowercaseInput = input.toLowerCase(Locale.ROOT);
final ImmutableList.Builder<String> builder = ImmutableList.builder(); final ImmutableList.Builder<String> builder = ImmutableList.builder();
for (final String player : commandUtil.getOnlineUsernames(limitTo)) { for (final String player : commandUtil.getOnlineUsernames(limitTo)) {
if (lowercaseInput.isEmpty() || player.toLowerCase().startsWith(lowercaseInput)) { if (player.toLowerCase(Locale.ROOT).startsWith(lowercaseInput)) {
builder.add(player); builder.add(player);
} }
} }
@@ -182,7 +188,7 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
} }
@Override @Override
public synchronized @NonNull Throwable fillInStackTrace() { public @NonNull Throwable fillInStackTrace() {
return this; return this;
} }
} }

View File

@@ -27,6 +27,7 @@ package org.geysermc.floodgate.skin;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.Locale;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -43,7 +44,7 @@ public final class SkinUploader {
private static final int MAX_TRIES = 3; private static final int MAX_TRIES = 3;
private final Executor requestExecutor = Executors.newSingleThreadExecutor(); private final Executor requestExecutor = Executors.newSingleThreadExecutor();
private long nextResult = 0; private long nextResult;
public CompletableFuture<UploadResult> uploadSkin(@Nonnull RawSkin rawSkin) { public CompletableFuture<UploadResult> uploadSkin(@Nonnull RawSkin rawSkin) {
return CompletableFuture.supplyAsync(() -> uploadSkinInner(rawSkin, 0), requestExecutor); return CompletableFuture.supplyAsync(() -> uploadSkinInner(rawSkin, 0), requestExecutor);
@@ -65,12 +66,11 @@ public final class SkinUploader {
try { try {
UploadResult result = parseAndHandleResponse(HttpUtils.post(url, image)); UploadResult result = parseAndHandleResponse(HttpUtils.post(url, image));
if (result.httpCode == 429) { if (result.getHttpCode() == 429) {
times += 1; if (times + 1 >= MAX_TRIES) {
if (times >= MAX_TRIES) {
return result; return result;
} }
uploadSkinInner(rawSkin, times); uploadSkinInner(rawSkin, times + 1);
} }
return result; return result;
} catch (RuntimeException exception) { } catch (RuntimeException exception) {
@@ -79,7 +79,7 @@ public final class SkinUploader {
} }
private String getUploadUrlParameters(SkinModel model) { private String getUploadUrlParameters(SkinModel model) {
return "?visibility=1&model=" + model.name; return "?visibility=1&model=" + model.getName();
} }
private UploadResult parseAndHandleResponse(HttpResponse response) { private UploadResult parseAndHandleResponse(HttpResponse response) {
@@ -103,14 +103,15 @@ public final class SkinUploader {
STEVE, ALEX; STEVE, ALEX;
public static final SkinModel[] VALUES = values(); public static final SkinModel[] VALUES = values();
private final String name = name().toLowerCase();
@Getter private final String name = name().toLowerCase(Locale.ROOT);
public static SkinModel getByOrdinal(int ordinal) { public static SkinModel getByOrdinal(int ordinal) {
return VALUES.length > ordinal ? VALUES[ordinal] : STEVE; return VALUES.length > ordinal ? VALUES[ordinal] : STEVE;
} }
public static SkinModel getByName(String name) { public static SkinModel getByName(String name) {
return name == null || !name.equalsIgnoreCase("alex") ? STEVE : ALEX; return "alex".equalsIgnoreCase(name) ? ALEX : STEVE;
} }
} }

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.util;
public final class Constants {
public static final int LOGIN_SUCCESS_PACKET_ID = 2;
}

View File

@@ -28,6 +28,7 @@ package org.geysermc.floodgate.util;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.MessageFormat; import java.text.MessageFormat;
@@ -63,8 +64,8 @@ public final class LanguageManager {
*/ */
private static String formatLocale(String locale) { private static String formatLocale(String locale) {
try { try {
String[] parts = locale.toLowerCase().split("_"); String[] parts = locale.toLowerCase(Locale.ROOT).split("_");
return parts[0] + "_" + parts[1].toUpperCase(); return parts[0] + "_" + parts[1].toUpperCase(Locale.ROOT);
} catch (Exception e) { } catch (Exception e) {
return locale; return locale;
} }
@@ -117,31 +118,32 @@ public final class LanguageManager {
* @return true if the locale has been found * @return true if the locale has been found
*/ */
public boolean loadLocale(String locale) { public boolean loadLocale(String locale) {
locale = formatLocale(locale); String formatLocale = formatLocale(locale);
// just return if the locale has been loaded already // just return if the locale has been loaded already
if (localeMappings.containsKey(locale)) { if (localeMappings.containsKey(formatLocale)) {
return true; return true;
} }
InputStream localeStream = LanguageManager.class.getClassLoader().getResourceAsStream( InputStream localeStream = LanguageManager.class.getClassLoader().getResourceAsStream(
"languages/texts/" + locale + ".properties"); "languages/texts/" + formatLocale + ".properties");
// load the locale // load the locale
if (localeStream != null) { if (localeStream != null) {
Properties localeProp = new Properties(); Properties localeProp = new Properties();
try {
localeProp.load(new InputStreamReader(localeStream, StandardCharsets.UTF_8)); try (Reader reader = new InputStreamReader(localeStream, StandardCharsets.UTF_8)) {
localeProp.load(reader);
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError("Failed to load Floodgate locale", e); throw new AssertionError("Failed to load Floodgate locale", e);
} }
// insert the locale into the mappings // insert the locale into the mappings
localeMappings.put(locale, localeProp); localeMappings.put(formatLocale, localeProp);
return true; return true;
} }
logger.warn("Missing locale file: " + locale); logger.warn("Missing locale file: " + formatLocale);
return false; return false;
} }

View File

@@ -42,9 +42,8 @@ public final class MessageFormatter {
int previousIndex = -1; int previousIndex = -1;
int currentIndex; int currentIndex;
StringBuilder stringBuilder = new StringBuilder( StringBuilder stringBuilder =
message.length() + getArgsContentLength(args) new StringBuilder(message.length() + getArgsContentLength(args));
);
for (String argument : args) { for (String argument : args) {
currentIndex = message.indexOf(DELIM_STR, previousIndex); currentIndex = message.indexOf(DELIM_STR, previousIndex);

View File

@@ -48,7 +48,7 @@ public final class ReflectionUtils {
*/ */
@Getter @Getter
@Setter @Setter
private static String prefix = null; private static String prefix;
static { static {
Field modifiersField = null; Field modifiersField = null;
@@ -59,12 +59,12 @@ public final class ReflectionUtils {
try { try {
Method declaredFields = getMethod(Class.class, "getDeclaredFields0", boolean.class); Method declaredFields = getMethod(Class.class, "getDeclaredFields0", boolean.class);
if (declaredFields == null) { if (declaredFields == null) {
throw new NoSuchMethodException(); throw new NoSuchMethodException("Cannot find method getDeclaredFields0");
} }
Field[] fields = castedInvoke(Field.class, declaredFields, false); Field[] fields = castedInvoke(Field.class, declaredFields, false);
if (fields == null) { if (fields == null) {
throw new Exception(); throw new RuntimeException("The Field class cannot have null fields");
} }
for (Field field : fields) { for (Field field : fields) {
@@ -79,7 +79,7 @@ public final class ReflectionUtils {
System.getProperty("java.version"), System.getProperty("java.version"),
System.getProperty("java.vendor"), System.getProperty("java.vendor"),
System.getProperty("java.vendor.url") System.getProperty("java.vendor.url")
)); ), exception);
} }
} }
@@ -382,6 +382,9 @@ public final class ReflectionUtils {
*/ */
@Nullable @Nullable
public static Object invoke(Object instance, Method method, Object... arguments) { public static Object invoke(Object instance, Method method, Object... arguments) {
if (method == null) {
return null;
}
makeAccessible(method); makeAccessible(method);
try { try {
return method.invoke(instance, arguments); return method.invoke(instance, arguments);
@@ -410,11 +413,7 @@ public final class ReflectionUtils {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @Nullable
public static <T> T castedInvoke(Object instance, Method method, Object... arguments) { public static <T> T castedInvoke(Object instance, Method method, Object... arguments) {
try {
return (T) invoke(instance, method, arguments); return (T) invoke(instance, method, arguments);
} catch (NullPointerException exception) {
return null;
}
} }
/** /**
@@ -438,12 +437,7 @@ public final class ReflectionUtils {
*/ */
@Nullable @Nullable
public static Object invokeStatic(Class<?> clazz, String method) { public static Object invokeStatic(Class<?> clazz, String method) {
try {
return invoke(null, getMethod(clazz, method)); return invoke(null, getMethod(clazz, method));
} catch (NullPointerException exception) {
exception.printStackTrace();
return null;
}
} }
/** /**

View File

@@ -39,6 +39,7 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.api.link.LinkRequest; import org.geysermc.floodgate.api.link.LinkRequest;
import org.geysermc.floodgate.api.link.LinkRequestResult; import org.geysermc.floodgate.api.link.LinkRequestResult;
import org.geysermc.floodgate.link.CommonPlayerLink; import org.geysermc.floodgate.link.CommonPlayerLink;
@@ -59,11 +60,12 @@ public class SqliteDatabase extends CommonPlayerLink {
try { try {
Class.forName("org.sqlite.JDBC"); Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath.toString()); connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath.toString());
Statement statement = connection.createStatement(); try (Statement statement = connection.createStatement()) {
statement.setQueryTimeout(30); // set timeout to 30 sec. statement.setQueryTimeout(30); // set timeout to 30 sec.
statement.executeUpdate( statement.executeUpdate(
"create table if not exists LinkedPlayers (bedrockId string, javaUniqueId string, javaUsername string)" "create table if not exists LinkedPlayers (bedrockId string, javaUniqueId string, javaUsername string)"
); );
}
} catch (ClassNotFoundException exception) { } catch (ClassNotFoundException exception) {
getLogger().error("The required class to load the SQLite database wasn't found"); getLogger().error("The required class to load the SQLite database wasn't found");
} catch (SQLException exception) { } catch (SQLException exception) {
@@ -82,12 +84,12 @@ public class SqliteDatabase extends CommonPlayerLink {
} }
@Override @Override
public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) { @NonNull
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try { try (PreparedStatement query = connection.prepareStatement(
PreparedStatement query = connection.prepareStatement( "select * from LinkedPlayers where bedrockId = ?")) {
"select * from LinkedPlayers where bedrockId = ?"
);
query.setString(1, bedrockId.toString()); query.setString(1, bedrockId.toString());
ResultSet result = query.executeQuery(); ResultSet result = query.executeQuery();
if (!result.next()) { if (!result.next()) {
@@ -97,7 +99,7 @@ public class SqliteDatabase extends CommonPlayerLink {
String javaUsername = result.getString("javaUsername"); String javaUsername = result.getString("javaUsername");
UUID javaUniqueId = UUID.fromString(result.getString("javaUniqueId")); UUID javaUniqueId = UUID.fromString(result.getString("javaUniqueId"));
return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId); return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId);
} catch (SQLException | NullPointerException exception) { } catch (SQLException exception) {
getLogger().error("Error while getting LinkedPlayer", exception); getLogger().error("Error while getting LinkedPlayer", exception);
throw new CompletionException("Error while getting LinkedPlayer", exception); throw new CompletionException("Error while getting LinkedPlayer", exception);
} }
@@ -105,17 +107,17 @@ public class SqliteDatabase extends CommonPlayerLink {
} }
@Override @Override
public CompletableFuture<Boolean> isLinkedPlayer(UUID playerId) { @NonNull
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try { try (PreparedStatement query = connection.prepareStatement(
PreparedStatement query = connection.prepareStatement( "select javaUniqueId from LinkedPlayers where bedrockId = ? or javaUniqueId = ?")) {
"select javaUniqueId from LinkedPlayers where bedrockId = ? or javaUniqueId = ?"
);
query.setString(1, playerId.toString()); query.setString(1, playerId.toString());
query.setString(2, playerId.toString()); query.setString(2, playerId.toString());
ResultSet result = query.executeQuery(); ResultSet result = query.executeQuery();
return result.next(); return result.next();
} catch (SQLException | NullPointerException exception) { } catch (SQLException exception) {
getLogger().error("Error while checking if player is a LinkedPlayer", exception); getLogger().error("Error while checking if player is a LinkedPlayer", exception);
throw new CompletionException( throw new CompletionException(
"Error while checking if player is a LinkedPlayer", exception "Error while checking if player is a LinkedPlayer", exception
@@ -125,38 +127,41 @@ public class SqliteDatabase extends CommonPlayerLink {
} }
@Override @Override
public CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID javaId, String username) { @NonNull
public CompletableFuture<Void> linkPlayer(
@NonNull UUID bedrockId,
@NonNull UUID javaId,
@NonNull String username) {
return CompletableFuture.runAsync( return CompletableFuture.runAsync(
() -> linkPlayer0(bedrockId, javaId, username), () -> linkPlayer0(bedrockId, javaId, username),
getExecutorService()); getExecutorService());
} }
private void linkPlayer0(UUID bedrockId, UUID javaId, String username) { private void linkPlayer0(UUID bedrockId, UUID javaId, String username) {
try { try (PreparedStatement query =
PreparedStatement query = connection.prepareStatement( connection.prepareStatement("insert into LinkedPlayers values(?, ?, ?)")) {
"insert into LinkedPlayers values(?, ?, ?)"
);
query.setString(1, bedrockId.toString()); query.setString(1, bedrockId.toString());
query.setString(2, javaId.toString()); query.setString(2, javaId.toString());
query.setString(3, username); query.setString(3, username);
query.executeUpdate(); query.executeUpdate();
} catch (SQLException | NullPointerException exception) { } catch (SQLException exception) {
getLogger().error("Error while linking player", exception); getLogger().error("Error while linking player", exception);
throw new CompletionException("Error while linking player", exception); throw new CompletionException("Error while linking player", exception);
} }
} }
@Override @Override
public CompletableFuture<Void> unlinkPlayer(UUID javaId) { @NonNull
public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) {
return CompletableFuture.runAsync(() -> { return CompletableFuture.runAsync(() -> {
try { try (PreparedStatement query = connection.prepareStatement(
PreparedStatement query = connection.prepareStatement( "delete from LinkedPlayers where javaUniqueId = ? or bedrockId = ?")) {
"delete from LinkedPlayers where javaUniqueId = ? or bedrockId = ?"
);
query.setString(1, javaId.toString()); query.setString(1, javaId.toString());
query.setString(2, javaId.toString()); query.setString(2, javaId.toString());
query.executeUpdate(); query.executeUpdate();
} catch (SQLException | NullPointerException exception) { } catch (SQLException exception) {
getLogger().error("Error while unlinking player", exception); getLogger().error("Error while unlinking player", exception);
throw new CompletionException("Error while unlinking player", exception); throw new CompletionException("Error while unlinking player", exception);
} }
@@ -164,10 +169,11 @@ public class SqliteDatabase extends CommonPlayerLink {
} }
@Override @Override
@NonNull
public CompletableFuture<String> createLinkRequest( public CompletableFuture<String> createLinkRequest(
UUID javaId, @NonNull UUID javaId,
String javaUsername, @NonNull String javaUsername,
String bedrockUsername @NonNull String bedrockUsername
) { ) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
LinkRequest request = LinkRequest request =
@@ -180,11 +186,12 @@ public class SqliteDatabase extends CommonPlayerLink {
} }
@Override @Override
@NonNull
public CompletableFuture<LinkRequestResult> verifyLinkRequest( public CompletableFuture<LinkRequestResult> verifyLinkRequest(
UUID bedrockId, @NonNull UUID bedrockId,
String javaUsername, @NonNull String javaUsername,
String bedrockUsername, @NonNull String bedrockUsername,
String code @NonNull String code
) { ) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
LinkRequest request = activeLinkRequests.get(javaUsername); LinkRequest request = activeLinkRequests.get(javaUsername);

43
pom.xml
View File

@@ -84,4 +84,47 @@
<repositories> <repositories>
</repositories> </repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<rulesets>
<ruleset>${session.executionRootDirectory}/ruleset.xml</ruleset>
</rulesets>
<printFailingErrors>true</printFailingErrors>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<rulesets>
<ruleset>${session.executionRootDirectory}/ruleset.xml</ruleset>
</rulesets>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.3</version>
</plugin>
</plugins>
</reporting>
</project> </project>

44
ruleset.xml Normal file
View File

@@ -0,0 +1,44 @@
<?xml version="1.0"?>
<ruleset name="Floodgate Rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Floodgate Code ruleset
</description>
<exclude-pattern>.*/ReflectionUtils.*</exclude-pattern>
<exclude-pattern>.*/CommonPlayerLink.*</exclude-pattern><!-- UnusedPrivateMethod -->
<exclude-pattern>.*/DefaultConfigHandler.*</exclude-pattern><!-- NullAssignment -->
<exclude-pattern>.*/ConfigFileUpdater.*</exclude-pattern><!-- NullAssignment -->
<exclude-pattern>.*/FloodgateConfig.*</exclude-pattern><!-- RedundantFieldInitializer -->
<rule ref="category/java/bestpractices.xml/MissingOverride" />
<rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty" />
<rule ref="category/java/bestpractices.xml/UseTryWithResources" />
<rule ref="category/java/errorprone.xml/CloseResource" />
<rule ref="category/java/errorprone.xml/UseLocaleWithCaseConversions" />
<!-- got a bit lazy, just added whole sections and excluded some unnecessary ones -->
<rule ref="category/java/bestpractices.xml">
<!-- maybe add these ones back later on -->
<exclude name="AvoidPrintStackTrace" />
<exclude name="GuardLogStatement" />
</rule>
<rule ref="category/java/errorprone.xml">
<exclude name="AvoidDuplicateLiterals" />
<exclude name="AvoidLiteralsInIfCondition" /><!-- Some things just don't change :shrug: -->
<exclude name="AvoidFieldNameMatchingMethodName" />
<exclude name="BeanMembersShouldSerialize" />
<exclude name="UseProperClassLoader" />
</rule>
<rule ref="category/java/performance.xml">
<exclude name="AvoidUsingShortType" />
<exclude name="AvoidInstantiatingObjectsInLoops" /><!-- this is ok to some extend -->
<exclude name="InefficientStringBuffering" />
</rule>
<rule ref="category/java/security.xml" />
</ruleset>

View File

@@ -126,7 +126,7 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
Enum<?>[] protocolStates = (Enum<?>[]) PROTOCOL_STATE.getType().getEnumConstants(); Enum<?>[] protocolStates = (Enum<?>[]) PROTOCOL_STATE.getType().getEnumConstants();
Object readyToAcceptState = null; Object readyToAcceptState = null;
for (Enum<?> protocolState : protocolStates) { for (Enum<?> protocolState : protocolStates) {
if (protocolState.name().equals("READY_TO_ACCEPT")) { if ("READY_TO_ACCEPT".equals(protocolState.name())) {
readyToAcceptState = protocolState; readyToAcceptState = protocolState;
} }
} }

View File

@@ -50,7 +50,7 @@ public final class SpigotInjector extends CommonPlatformInjector {
private Object serverConnection; private Object serverConnection;
private String injectedFieldName; private String injectedFieldName;
@Getter private boolean injected = false; @Getter private boolean injected;
@Override @Override
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
@@ -148,7 +148,6 @@ public final class SpigotInjector extends CommonPlatformInjector {
} }
} }
injectedFieldName = null;
injected = false; injected = false;
return true; return true;
} }
@@ -162,7 +161,7 @@ public final class SpigotInjector extends CommonPlatformInjector {
Object minecraftServerInstance = ReflectionUtils.invokeStatic(minecraftServer, "getServer"); Object minecraftServerInstance = ReflectionUtils.invokeStatic(minecraftServer, "getServer");
for (Method method : minecraftServer.getDeclaredMethods()) { for (Method method : minecraftServer.getDeclaredMethods()) {
if (method.getReturnType().getSimpleName().equals("ServerConnection")) { if ("ServerConnection".equals(method.getReturnType().getSimpleName())) {
if (method.getParameterTypes().length == 0) { if (method.getParameterTypes().length == 0) {
serverConnection = method.invoke(minecraftServerInstance); serverConnection = method.invoke(minecraftServerInstance);
} }

View File

@@ -57,8 +57,7 @@ public final class SpigotSkinApplier implements SkinApplier {
public SpigotSkinApplier( public SpigotSkinApplier(
SpigotVersionSpecificMethods versionSpecificMethods, SpigotVersionSpecificMethods versionSpecificMethods,
SpigotPlugin plugin, SpigotPlugin plugin,
FloodgateConfigHolder configHolder FloodgateConfigHolder configHolder) {
) {
this.versionSpecificMethods = versionSpecificMethods; this.versionSpecificMethods = versionSpecificMethods;
this.plugin = plugin; this.plugin = plugin;
this.configHolder = configHolder; this.configHolder = configHolder;
@@ -89,7 +88,7 @@ public final class SpigotSkinApplier implements SkinApplier {
// By running as a task, we don't run into async issues // By running as a task, we don't run into async issues
plugin.getServer().getScheduler().runTask(plugin, () -> { plugin.getServer().getScheduler().runTask(plugin, () -> {
for (Player p : Bukkit.getOnlinePlayers()) { for (Player p : Bukkit.getOnlinePlayers()) {
if (p != player && p.canSee(player)) { if (!p.equals(player) && p.canSee(player)) {
versionSpecificMethods.hidePlayer(p, player); versionSpecificMethods.hidePlayer(p, player);
versionSpecificMethods.showPlayer(p, player); versionSpecificMethods.showPlayer(p, player);
} }

View File

@@ -60,6 +60,7 @@ public final class SpigotCommandUtil implements CommandUtil {
private final FloodgateLogger logger; private final FloodgateLogger logger;
private final LanguageManager manager; private final LanguageManager manager;
@Override
public @NonNull UserAudience getAudience(final @NonNull Object sourceObj) { public @NonNull UserAudience getAudience(final @NonNull Object sourceObj) {
if (!(sourceObj instanceof CommandSender)) { if (!(sourceObj instanceof CommandSender)) {
throw new IllegalArgumentException("Source has to be a CommandSender!"); throw new IllegalArgumentException("Source has to be a CommandSender!");
@@ -116,14 +117,14 @@ public final class SpigotCommandUtil implements CommandUtil {
break; break;
case ONLY_JAVA: case ONLY_JAVA:
for (Player player : players) { for (Player player : players) {
if (!api.isBedrockPlayer(player.getUniqueId())) { if (!api.isFloodgatePlayer(player.getUniqueId())) {
usernames.add(player.getName()); usernames.add(player.getName());
} }
} }
break; break;
case ONLY_BEDROCK: case ONLY_BEDROCK:
for (Player player : players) { for (Player player : players) {
if (api.isBedrockPlayer(player.getUniqueId())) { if (api.isFloodgatePlayer(player.getUniqueId())) {
usernames.add(player.getName()); usernames.add(player.getName());
} }
} }

View File

@@ -106,6 +106,7 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<
break; break;
case EXCEPTION: case EXCEPTION:
ctx.channel().attr(kickMessageAttribute).set(config.getDisconnect().getInvalidKey()); ctx.channel().attr(kickMessageAttribute).set(config.getDisconnect().getInvalidKey());
return;
case INVALID_DATA_LENGTH: case INVALID_DATA_LENGTH:
ctx.channel().attr(kickMessageAttribute) ctx.channel().attr(kickMessageAttribute)
.set(config.getDisconnect().getInvalidArgumentsLength()); .set(config.getDisconnect().getInvalidArgumentsLength());

View File

@@ -110,7 +110,7 @@ public final class VelocityServerDataHandler extends MessageToMessageEncoder<Obj
checkArgument(encryptedData != null, "Encrypted data cannot be null"); checkArgument(encryptedData != null, "Encrypted data cannot be null");
// use the same system that we use on bungee, our data goes before all the other data // use the same system that we use on bungee, our data goes before all the other data
int addressFinished = address.indexOf("\0"); int addressFinished = address.indexOf('\0');
String originalAddress = address.substring(0, addressFinished); String originalAddress = address.substring(0, addressFinished);
String remaining = address.substring(addressFinished); String remaining = address.substring(addressFinished);

View File

@@ -44,8 +44,9 @@ public final class VelocityInjector extends CommonPlatformInjector {
private final ProxyServer server; private final ProxyServer server;
private final FloodgateLogger logger; private final FloodgateLogger logger;
@Getter private boolean injected = false; @Getter private boolean injected;
@Override
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public boolean inject() { public boolean inject() {
if (isInjected()) { if (isInjected()) {

View File

@@ -25,7 +25,9 @@
package org.geysermc.floodgate.listener; package org.geysermc.floodgate.listener;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.Subscribe;
@@ -165,7 +167,7 @@ public class VelocityPluginMessageHandler extends PluginMessageHandler {
try { try {
Reader reader = new InputStreamReader(new ByteArrayInputStream(responseData)); Reader reader = new InputStreamReader(new ByteArrayInputStream(responseData));
response = GSON.fromJson(reader, JsonObject.class); response = GSON.fromJson(reader, JsonObject.class);
} catch (Throwable throwable) { } catch (JsonIOException | JsonSyntaxException throwable) {
logger.error("Failed to read Skin response", throwable); logger.error("Failed to read Skin response", throwable);
return; return;
} }

View File

@@ -115,14 +115,14 @@ public final class VelocityCommandUtil implements CommandUtil {
break; break;
case ONLY_JAVA: case ONLY_JAVA:
for (Player player : players) { for (Player player : players) {
if (!api.isBedrockPlayer(player.getUniqueId())) { if (!api.isFloodgatePlayer(player.getUniqueId())) {
usernames.add(player.getUsername()); usernames.add(player.getUsername());
} }
} }
break; break;
case ONLY_BEDROCK: case ONLY_BEDROCK:
for (Player player : players) { for (Player player : players) {
if (api.isBedrockPlayer(player.getUniqueId())) { if (api.isFloodgatePlayer(player.getUniqueId())) {
usernames.add(player.getUsername()); usernames.add(player.getUsername());
} }
} }