mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Initial Floodgate 2.0 commit
This commit is contained in:
9
.idea/codeStyles/Project.xml
generated
Normal file
9
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="RIGHT_MARGIN" value="100" />
|
||||
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
|
||||
<ScalaCodeStyleSettings>
|
||||
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
|
||||
</ScalaCodeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
6
.idea/copyright/GeyserMC.xml
generated
Normal file
6
.idea/copyright/GeyserMC.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright (c) 2019-&#36;today.year 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 " />
|
||||
<option name="myName" value="GeyserMC" />
|
||||
</copyright>
|
||||
</component>
|
||||
7
.idea/copyright/profiles_settings.xml
generated
Normal file
7
.idea/copyright/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="GeyserMC">
|
||||
<module2copyright>
|
||||
<element module="all" copyright="Geyser" />
|
||||
</module2copyright>
|
||||
</settings>
|
||||
</component>
|
||||
77
api/pom.xml
Normal file
77
api/pom.xml
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>api</artifactId>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>nukkitx-release-repo</id>
|
||||
<url>https://repo.nukkitx.com/maven-releases/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>nukkitx-snapshot-repo</id>
|
||||
<url>https://repo.nukkitx.com/maven-snapshots/</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${geyser-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>4.1.49.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<finalName>${outputName}</finalName>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api;
|
||||
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FloodgateApi {
|
||||
/**
|
||||
* Method to determine if the given <b>online</b> player is a bedrock player
|
||||
*
|
||||
* @param uuid The uuid of the <b>online</b> player
|
||||
* @return true if the given <b>online</b> player is a Bedrock player
|
||||
*/
|
||||
boolean isBedrockPlayer(UUID uuid);
|
||||
|
||||
/**
|
||||
* Get info about the given Bedrock player
|
||||
*
|
||||
* @param uuid the uuid of the <b>online</b> Bedrock player
|
||||
* @return FloodgatePlayer if the given uuid is a Bedrock player
|
||||
*/
|
||||
FloodgatePlayer getPlayer(UUID uuid);
|
||||
|
||||
/**
|
||||
* Create a valid Java player uuid of a xuid
|
||||
*
|
||||
* @param xuid the xuid that should be converted
|
||||
* @return the created uuid based of the given xuid
|
||||
*/
|
||||
UUID createJavaPlayerId(long xuid);
|
||||
|
||||
/**
|
||||
* 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 #isBedrockPlayer(UUID)} if you want to include linked accounts.
|
||||
*
|
||||
* @param uuid the uuid to check
|
||||
* @return true if the given uuid has the correct format.
|
||||
*/
|
||||
boolean isFloodgateId(UUID uuid);
|
||||
|
||||
/**
|
||||
* Returns the instance that manages all the linking.
|
||||
*/
|
||||
default PlayerLink getPlayerLink() {
|
||||
return InstanceHolder.getPlayerLink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Floodgate API instance.
|
||||
* This method is equal to running {@link InstanceHolder#getInstance()}
|
||||
*/
|
||||
static FloodgateApi getInstance() {
|
||||
return InstanceHolder.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class InstanceHolder {
|
||||
@Getter private static FloodgateApi instance;
|
||||
@Getter private static PlayerLink playerLink;
|
||||
private static UUID key;
|
||||
|
||||
public static boolean setInstance(FloodgateApi floodgateApi, PlayerLink link, UUID key) {
|
||||
if (instance == null) {
|
||||
InstanceHolder.key = key;
|
||||
} else if (!InstanceHolder.key.equals(key)) {
|
||||
return false;
|
||||
}
|
||||
instance = floodgateApi;
|
||||
playerLink = link;
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends FloodgateApi> T castApi(Class<T> cast) {
|
||||
return (T) instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api.inject;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public interface InjectorAddon {
|
||||
/**
|
||||
* Called when injecting a specific channel
|
||||
* (every client that is connected to the server has his own channel).
|
||||
* Internally used for the Floodgate debugger and data handler but can also be used for
|
||||
* third party things.
|
||||
*
|
||||
* @param channel the channel that the injector is injecting
|
||||
* @param proxyToServer if the the connection is between the proxy and a server
|
||||
*/
|
||||
void onInject(Channel channel, boolean proxyToServer);
|
||||
|
||||
/**
|
||||
* Called when the player successfully logged in.
|
||||
* That is the moment that most of the addons can deregister.
|
||||
* Note that it is entirely optional to remove the addon from the channel,
|
||||
* the injector won't force the addon to remove.
|
||||
*
|
||||
* @param channel the channel that the injector injected
|
||||
*/
|
||||
void onLoginDone(Channel channel);
|
||||
|
||||
/**
|
||||
* Called when Floodgate is removing the injection from the server.
|
||||
* The addon should remove his traces otherwise it is likely that an error will popup after
|
||||
* the server is injected again.
|
||||
*
|
||||
* @param channel the channel that the injector injected
|
||||
*/
|
||||
void onRemoveInject(Channel channel);
|
||||
|
||||
/**
|
||||
* If the Injector should call {@link #onInject(Channel, boolean)}
|
||||
*
|
||||
* @return true if it should, false otherwise
|
||||
*/
|
||||
boolean shouldInject();
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api.inject;
|
||||
|
||||
/**
|
||||
* The global interface of all the Platform Injectors.
|
||||
* The injector can be used for various things. It is used internally for getting Floodgate
|
||||
* data out of the handshake packet and for debug mode, but there is also an option to add your
|
||||
* own addons.
|
||||
* Note that every Floodgate platform that supports netty should implement this,
|
||||
* but the platform implementation isn't required to implement this.
|
||||
*/
|
||||
public interface PlatformInjector {
|
||||
/**
|
||||
* Injects the server connection.
|
||||
* This will allow various addons (like getting the Floodgate data and debug mode) to work.
|
||||
*
|
||||
* @return true if the connection has successfully been injected
|
||||
* @throws Exception if something went wrong while injecting the server connection
|
||||
*/
|
||||
boolean inject() throws Exception;
|
||||
|
||||
/**
|
||||
* Removes the injection from the server.
|
||||
* Please note that this function should only be used internally (on plugin shutdown).
|
||||
* This method will also remove every added addon.
|
||||
*
|
||||
* @return true if the injection has successfully been removed
|
||||
* @throws Exception if something went wrong while removing the injection
|
||||
*/
|
||||
boolean removeInjection() throws Exception;
|
||||
|
||||
/**
|
||||
* If the server connection is currently injected.
|
||||
*
|
||||
* @return true if the server connection is currently injected, returns false otherwise
|
||||
*/
|
||||
boolean isInjected();
|
||||
|
||||
/**
|
||||
* Adds an addon to the addon list of the Floodgate Injector
|
||||
* (the addon is called when Floodgate injects a channel).
|
||||
* See {@link InjectorAddon} for more info.
|
||||
*
|
||||
* @param addon the addon to add to the addon list
|
||||
* @return true if the addon has been added, false if the addon is already present
|
||||
*/
|
||||
boolean addAddon(InjectorAddon addon);
|
||||
|
||||
/**
|
||||
* Removes an addon from the addon list of the Floodgate Injector
|
||||
* (the addon is called when Floodgate injects a channel).
|
||||
* See {@link InjectorAddon} for more info.
|
||||
*
|
||||
* @param addon the class of the addon to remove from the addon list
|
||||
* @param <T> the addon type
|
||||
* @return the instance that was present when removing
|
||||
*/
|
||||
<T extends InjectorAddon> T removeAddon(Class<T> addon);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.geysermc.floodgate.api.link;
|
||||
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface LinkRequest {
|
||||
/**
|
||||
* Returns the Java username of the linked player.
|
||||
*/
|
||||
String getJavaUsername();
|
||||
|
||||
/**
|
||||
* Returns the Java unique id of the linked player.
|
||||
*/
|
||||
UUID getJavaUniqueId();
|
||||
|
||||
/**
|
||||
* Returns the code that the Bedrock player has to enter in order to link the account.
|
||||
*/
|
||||
String getLinkCode();
|
||||
|
||||
/**
|
||||
* Returns the username of player being linked.
|
||||
*/
|
||||
String getBedrockUsername();
|
||||
|
||||
/**
|
||||
* Returns the unix time when the player link was requested.
|
||||
*/
|
||||
long getRequestTime();
|
||||
|
||||
/**
|
||||
* If this player link request is expired.
|
||||
*
|
||||
* @param linkTimeout the link timeout in millis
|
||||
* @return true if the difference between now and requestTime is greater then the link timout
|
||||
*/
|
||||
boolean isExpired(long linkTimeout);
|
||||
|
||||
/**
|
||||
* Checks if the given FloodgatePlayer is the player requested in this LinkRequest.
|
||||
* This method will check both the real bedrock username
|
||||
* {@link FloodgatePlayer#getUsername()} and the edited username
|
||||
* {@link FloodgatePlayer#getJavaUsername()} and returns true if one of the two matches.
|
||||
*
|
||||
* @param player the player to check
|
||||
* @return true if the given player is the player requested
|
||||
*/
|
||||
default boolean isRequestedPlayer(FloodgatePlayer player) {
|
||||
return getBedrockUsername().equals(player.getUsername()) ||
|
||||
getBedrockUsername().equals(player.getJavaUsername());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api.link;
|
||||
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The base class of the PlayerLink database implementation.
|
||||
* The implementation is responsible for making a connection with the database
|
||||
* and keeping that connection alive so that Floodgate (or a third party plugin)
|
||||
* can check for example if a given player is linked.
|
||||
*/
|
||||
public interface PlayerLink {
|
||||
/**
|
||||
* Called by Floodgate after the initialization of the class.
|
||||
* In this method the implementation should start the connection with the database and
|
||||
* create the collections if they don't exist already.
|
||||
*/
|
||||
void load();
|
||||
|
||||
/**
|
||||
* Get a linked player by the bedrock uuid
|
||||
*
|
||||
* @param bedrockId the uuid of the bedrock player
|
||||
* @return a completable future with the {@link LinkedPlayer}.
|
||||
* The future will have a null value if that Bedrock player isn't linked
|
||||
*/
|
||||
CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId);
|
||||
|
||||
/**
|
||||
* Tells if the given player is a linked player
|
||||
*
|
||||
* @param bedrockId the bedrock uuid of the linked player
|
||||
* @return true if the player is a linked player
|
||||
*/
|
||||
CompletableFuture<Boolean> isLinkedPlayer(UUID bedrockId);
|
||||
|
||||
/**
|
||||
* Links a Java account to a Bedrock account.
|
||||
*
|
||||
* @param bedrockId the uuid of the Bedrock player
|
||||
* @param javaId the uuid of the Java player
|
||||
* @param username the username of the Java player
|
||||
* @return a future holding void on success or completed exceptionally when failed
|
||||
*/
|
||||
CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID javaId, String username);
|
||||
|
||||
/**
|
||||
* Unlinks a Java account from a Bedrock account.
|
||||
*
|
||||
* @param javaId the uuid of the Java player
|
||||
* @return a future holding void on success or completed exceptionally when failed
|
||||
*/
|
||||
CompletableFuture<Void> unlinkPlayer(UUID javaId);
|
||||
|
||||
/**
|
||||
* Return if account linking is enabled.
|
||||
* The difference between enabled and allowed is that 'enabled' still allows already linked
|
||||
* people to join with their linked account while 'allow linking' allows people to link
|
||||
* accounts using the commands.
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Returns the duration (in seconds) before a {@link LinkRequest} timeouts
|
||||
*/
|
||||
long getVerifyLinkTimeout();
|
||||
|
||||
/**
|
||||
* Return if account linking is allowed.
|
||||
* The difference between enabled and allowed is that 'enabled' still allows already linked
|
||||
* people to join with their linked account while 'allow linking' allows people to link
|
||||
* accounts using the commands.
|
||||
*/
|
||||
boolean isAllowLinking();
|
||||
|
||||
default boolean isEnabledAndAllowed() {
|
||||
return isEnabled() && isAllowLinking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the Floodgate plugin is going to shutdown
|
||||
*/
|
||||
void stop();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api.logger;
|
||||
|
||||
public interface FloodgateLogger {
|
||||
String LOGGER_NAME = "Floodgate";
|
||||
|
||||
/**
|
||||
* Logs an error message to the console, with 0 or more arguments.
|
||||
*
|
||||
* @param message the message to log to the console
|
||||
* @param args the arguments to fill the missing spots in the message
|
||||
*/
|
||||
void error(String message, Object... args);
|
||||
|
||||
/**
|
||||
* Logs an error message to the console, with 0 or more arguments.
|
||||
*
|
||||
* @param message the message to log to the console
|
||||
* @param throwable the throwable to log
|
||||
* @param args the arguments to fill the missing spots in the message
|
||||
*/
|
||||
void error(String message, Throwable throwable, Object... args);
|
||||
|
||||
/**
|
||||
* Logs a warning message to the console, with 0 or more arguments.
|
||||
*
|
||||
* @param message the message to log to the console
|
||||
* @param args the arguments to fill the missing spots in the message
|
||||
*/
|
||||
void warn(String message, Object... args);
|
||||
|
||||
/**
|
||||
* Logs an info message to the console, with 0 or more arguments.
|
||||
*
|
||||
* @param message the message to log to the console
|
||||
* @param args the arguments to fill the missing spots in the message
|
||||
*/
|
||||
void info(String message, Object... args);
|
||||
|
||||
/**
|
||||
* Logs a debug message to the console, with 0 or more arguments.
|
||||
*
|
||||
* @param message the message to log to the console
|
||||
* @param args the arguments to fill the missing spots in the message
|
||||
*/
|
||||
void debug(String message, Object... args);
|
||||
|
||||
/**
|
||||
* Logs a trace message to the console, with 0 or more arguments.
|
||||
*
|
||||
* @param message the message to log to the console
|
||||
* @param args the arguments to fill the missing spots in the message
|
||||
*/
|
||||
void trace(String message, Object... args);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.geysermc.floodgate.api.player;
|
||||
|
||||
import org.geysermc.floodgate.util.DeviceOs;
|
||||
import org.geysermc.floodgate.util.InputMode;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
import org.geysermc.floodgate.util.UiProfile;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FloodgatePlayer {
|
||||
/**
|
||||
* Returns the Bedrock username that will be used as username on the server.
|
||||
* This includes replace spaces (if enabled), username shortened and prefix appended.<br>
|
||||
* Note that this field is not used when the player is a {@link LinkedPlayer LinkedPlayer}
|
||||
*/
|
||||
String getJavaUsername();
|
||||
|
||||
/**
|
||||
* Returns the uuid that will be used as UUID on the server.<br>
|
||||
* Note that this field is not used when the player is a {@link LinkedPlayer LinkedPlayer}
|
||||
*/
|
||||
UUID getJavaUniqueId();
|
||||
|
||||
/**
|
||||
* Returns the uuid that the server will use as uuid of that player.
|
||||
* Will return {@link #getJavaUniqueId()} when not linked or
|
||||
* {@link LinkedPlayer#getJavaUniqueId()} when linked.
|
||||
*/
|
||||
UUID getCorrectUniqueId();
|
||||
|
||||
/**
|
||||
* Returns the username the server will as username for that player.
|
||||
* Will return {@link #getJavaUsername()} when not linked or
|
||||
* {@link LinkedPlayer#getJavaUsername()} when linked.
|
||||
*/
|
||||
String getCorrectUsername();
|
||||
|
||||
/**
|
||||
* Returns the version of the Bedrock client
|
||||
*/
|
||||
String getVersion();
|
||||
|
||||
/**
|
||||
* Returns the real username of the Bedrock client.
|
||||
* No prefix nor shortened nor replaced spaces.
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* Returns the Xbox Unique Identifier of the Bedrock client
|
||||
*/
|
||||
String getXuid();
|
||||
|
||||
/**
|
||||
* Returns the Operation System of the Bedrock client
|
||||
*/
|
||||
DeviceOs getDeviceOs();
|
||||
|
||||
/**
|
||||
* Returns the language code of the Bedrock client
|
||||
*/
|
||||
String getLanguageCode();
|
||||
|
||||
/**
|
||||
* Returns the User Interface Profile of the Bedrock client
|
||||
*/
|
||||
UiProfile getUiProfile();
|
||||
|
||||
/**
|
||||
* Returns the Input Mode of the Bedrock client
|
||||
*/
|
||||
InputMode getInputMode();
|
||||
|
||||
/**
|
||||
* Returns the LinkedPlayer object if the player is linked to a Java account.
|
||||
*/
|
||||
LinkedPlayer getLinkedPlayer();
|
||||
|
||||
/**
|
||||
* Casts the FloodgatePlayer instance to a class that extends FloodgatePlayer.
|
||||
*
|
||||
* @param <T> The instance to cast to.
|
||||
* @return The FloodgatePlayer casted to the given class
|
||||
* @throws ClassCastException when it can't cast the instance to the given class
|
||||
*/
|
||||
default <T extends FloodgatePlayer> T as(Class<T> clazz) {
|
||||
return clazz.cast(this);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.floodgate.command.LinkAccountCommand;
|
||||
import org.geysermc.floodgate.command.UnlinkAccountCommand;
|
||||
import org.geysermc.floodgate.injector.BukkitInjector;
|
||||
import org.geysermc.floodgate.util.CommandUtil;
|
||||
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BukkitPlugin extends JavaPlugin implements Listener {
|
||||
@Getter private static BukkitPlugin instance;
|
||||
@Getter private FloodgateConfig configuration;
|
||||
@Getter private PlayerLink playerLink;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
instance = this;
|
||||
if (!getDataFolder().exists()) {
|
||||
getDataFolder().mkdir();
|
||||
}
|
||||
ReflectionUtil.setPrefix("net.minecraft.server." + getServer().getClass().getPackage().getName().split("\\.")[3]);
|
||||
|
||||
configuration = FloodgateConfig.load(getLogger(), getDataFolder().toPath().resolve("config.yml"));
|
||||
playerLink = PlayerLink.initialize(getLogger(), getDataFolder().toPath(), configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
try {
|
||||
if (!BukkitInjector.inject()) getLogger().severe("Failed to inject the packet listener!");
|
||||
} catch (Exception e) {
|
||||
getLogger().log(Level.SEVERE, "Failed to inject the packet listener!", e);
|
||||
} finally {
|
||||
if (!BukkitInjector.isInjected()) {
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
}
|
||||
CommandUtil commandUtil = new CommandUtil(this);
|
||||
getCommand(CommandUtil.LINK_ACCOUNT_COMMAND).setExecutor(new LinkAccountCommand(playerLink, commandUtil));
|
||||
getCommand(CommandUtil.UNLINK_ACCOUNT_COMMAND).setExecutor(new UnlinkAccountCommand(playerLink, commandUtil));
|
||||
|
||||
// Register the plugin as an event listener to we get join and leave events
|
||||
Bukkit.getServer().getPluginManager().registerEvents(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
try {
|
||||
if (!BukkitInjector.removeInjection()) getLogger().severe("Failed to remove the injection!");
|
||||
} catch (Exception e) {
|
||||
getLogger().log(Level.SEVERE, "Failed to remove the injection!", e);
|
||||
}
|
||||
playerLink.stop();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onAsyncPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
|
||||
FloodgateAPI.removePlayer(event.getUniqueId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerLogin(PlayerLoginEvent event) {
|
||||
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||
FloodgateAPI.removePlayer(event.getPlayer().getUniqueId());
|
||||
return;
|
||||
}
|
||||
// if there was another player with the same uuid online,
|
||||
// he would've been disconnected by now
|
||||
FloodgatePlayer player = FloodgateAPI.getPlayer(event.getPlayer());
|
||||
if (player != null) player.setLogin(false);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (FloodgateAPI.removePlayer(player.getUniqueId())) {
|
||||
System.out.println("Removed Bedrock player who was logged in as " + player.getName() + " " + event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class FloodgateAPI extends AbstractFloodgateAPI {
|
||||
/**
|
||||
* See {@link AbstractFloodgateAPI#getPlayer(UUID)}
|
||||
*/
|
||||
public static FloodgatePlayer getPlayer(Player player) {
|
||||
return getPlayer(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link AbstractFloodgateAPI#isBedrockPlayer(UUID)}
|
||||
*/
|
||||
public static boolean isBedrockPlayer(Player player) {
|
||||
return isBedrockPlayer(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.HandshakeHandler.HandshakeResult;
|
||||
import org.geysermc.floodgate.injector.BukkitInjector;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.geysermc.floodgate.util.ReflectionUtil.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PacketHandler extends SimpleChannelInboundHandler<Object> {
|
||||
private static BukkitPlugin plugin = BukkitPlugin.getInstance();
|
||||
private static HandshakeHandler handshakeHandler;
|
||||
|
||||
private static Class<?> networkManagerClass;
|
||||
private static Class<?> handshakePacketClass;
|
||||
private static Field hostField;
|
||||
|
||||
private static Class<?> gameProfileClass;
|
||||
private static Constructor<?> gameProfileConstructor;
|
||||
private static Field gameProfileField;
|
||||
|
||||
private static Class<?> loginStartPacketClass;
|
||||
private static Class<?> loginListenerClass;
|
||||
private static Method initUUIDMethod;
|
||||
|
||||
private static Class<?> loginHandlerClass;
|
||||
private static Constructor<?> loginHandlerConstructor;
|
||||
private static Method fireEventsMethod;
|
||||
|
||||
private static Field packetListenerField;
|
||||
private static Field protocolStateField;
|
||||
private static Object readyToAcceptState;
|
||||
|
||||
/* per player stuff */
|
||||
|
||||
private final ChannelFuture future;
|
||||
private Object networkManager;
|
||||
private FloodgatePlayer fPlayer;
|
||||
private boolean bungee;
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Object packet) throws Exception {
|
||||
boolean isHandhake = handshakePacketClass.isInstance(packet);
|
||||
boolean isLogin = loginStartPacketClass.isInstance(packet);
|
||||
|
||||
try {
|
||||
if (isHandhake) {
|
||||
networkManager = ctx.channel().pipeline().get("packet_handler");
|
||||
|
||||
HandshakeResult result = handshakeHandler.handle(getCastedValue(packet, hostField, String.class));
|
||||
switch (result.getResultType()) {
|
||||
case SUCCESS:
|
||||
break;
|
||||
case INVALID_DATA_LENGTH:
|
||||
plugin.getLogger().info(String.format(
|
||||
plugin.getConfiguration().getMessages().getInvalidArgumentsLength(),
|
||||
BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength()
|
||||
));
|
||||
ctx.close();
|
||||
default: // only continue when SUCCESS
|
||||
return;
|
||||
}
|
||||
|
||||
fPlayer = result.getFloodgatePlayer();
|
||||
BedrockData bedrockData = result.getBedrockData();
|
||||
String[] data = result.getHandshakeData();
|
||||
|
||||
if (bungee = (data.length == 6 || data.length == 7)) {
|
||||
setValue(packet, hostField, data[0] + '\0' +
|
||||
bedrockData.getIp() + '\0' + fPlayer.getCorrectUniqueId() +
|
||||
(data.length == 7 ? '\0' + data[6] : "")
|
||||
);
|
||||
} else {
|
||||
// Use a spoofedUUID for initUUID (just like Bungeecord)
|
||||
setValue(networkManager, "spoofedUUID", fPlayer.getCorrectUniqueId());
|
||||
// Use the player his IP for stuff instead of Geyser his IP
|
||||
SocketAddress newAddress = new InetSocketAddress(
|
||||
bedrockData.getIp(),
|
||||
((InetSocketAddress) ctx.channel().remoteAddress()).getPort()
|
||||
);
|
||||
setValue(networkManager, getFieldOfType(networkManagerClass, SocketAddress.class, false), newAddress);
|
||||
}
|
||||
plugin.getLogger().info("Added " + fPlayer.getCorrectUsername() + " " + fPlayer.getCorrectUniqueId());
|
||||
} else if (isLogin) {
|
||||
if (!bungee) {
|
||||
// we have to fake the offline player cycle
|
||||
Object loginListener = packetListenerField.get(networkManager);
|
||||
|
||||
// Set the player his GameProfile
|
||||
Object gameProfile = gameProfileConstructor.newInstance(fPlayer.getCorrectUniqueId(), fPlayer.getCorrectUsername());
|
||||
setValue(loginListener, gameProfileField, gameProfile);
|
||||
|
||||
initUUIDMethod.invoke(loginListener); // LoginListener#initUUID
|
||||
fireEventsMethod.invoke(loginHandlerConstructor.newInstance(loginListener)); // new LoginHandler().fireEvents();
|
||||
setValue(loginListener, protocolStateField, readyToAcceptState); // LoginLister#protocolState = READY_TO_ACCEPT
|
||||
// The tick of LoginListener will do the rest
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// don't let the packet through if the packet is the login packet
|
||||
// because we want to skip the login cycle
|
||||
if (!isLogin) ctx.fireChannelRead(packet);
|
||||
|
||||
if (isHandhake && bungee || isLogin && !bungee || fPlayer == null) {
|
||||
// remove the injection of the client because we're finished
|
||||
BukkitInjector.removeInjectedClient(future, ctx.channel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
super.exceptionCaught(ctx, cause);
|
||||
cause.printStackTrace();
|
||||
}
|
||||
|
||||
static {
|
||||
handshakeHandler = new HandshakeHandler(plugin.getConfiguration().getPrivateKey(), false, plugin.getConfiguration().getUsernamePrefix(), plugin.getConfiguration().isReplaceSpaces());
|
||||
|
||||
networkManagerClass = getPrefixedClass("NetworkManager");
|
||||
loginStartPacketClass = getPrefixedClass("PacketLoginInStart");
|
||||
|
||||
gameProfileClass = ReflectionUtil.getClass("com.mojang.authlib.GameProfile");
|
||||
assert gameProfileClass != null;
|
||||
try {
|
||||
gameProfileConstructor = gameProfileClass.getConstructor(UUID.class, String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
handshakePacketClass = getPrefixedClass("PacketHandshakingInSetProtocol");
|
||||
assert handshakePacketClass != null;
|
||||
hostField = getFieldOfType(handshakePacketClass, String.class, true);
|
||||
|
||||
loginListenerClass = getPrefixedClass("LoginListener");
|
||||
assert loginListenerClass != null;
|
||||
gameProfileField = getFieldOfType(loginListenerClass, gameProfileClass, true);
|
||||
initUUIDMethod = getMethod(loginListenerClass, "initUUID");
|
||||
|
||||
for (Field field : loginListenerClass.getDeclaredFields()) {
|
||||
if (field.getType().isEnum()) {
|
||||
protocolStateField = field;
|
||||
}
|
||||
}
|
||||
assert protocolStateField != null;
|
||||
|
||||
Enum<?>[] protocolStates = (Enum<?>[]) protocolStateField.getType().getEnumConstants();
|
||||
for (Enum<?> protocolState : protocolStates) {
|
||||
if (protocolState.name().equals("READY_TO_ACCEPT")) {
|
||||
readyToAcceptState = protocolState;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> packetListenerClass = getPrefixedClass("PacketListener");
|
||||
packetListenerField = getFieldOfType(networkManagerClass, packetListenerClass, true);
|
||||
|
||||
loginHandlerClass = getPrefixedClass("LoginListener$LoginHandler");
|
||||
assert loginHandlerClass != null;
|
||||
try {
|
||||
loginHandlerConstructor = makeAccessible(loginHandlerClass.getDeclaredConstructor(loginListenerClass));
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
fireEventsMethod = getMethod(loginHandlerClass, "fireEvents");
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.geysermc.floodgate.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.floodgate.AbstractLinkAccountCommand;
|
||||
import org.geysermc.floodgate.PlayerLink;
|
||||
import org.geysermc.floodgate.util.CommandUtil;
|
||||
import org.geysermc.floodgate.util.CommonMessage;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class LinkAccountCommand extends AbstractLinkAccountCommand<Player, CommandUtil> implements CommandExecutor {
|
||||
public LinkAccountCommand(PlayerLink link, CommandUtil commandUtil) {
|
||||
super(link, commandUtil);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(CommonMessage.NOT_A_PLAYER.getMessage());
|
||||
return true;
|
||||
}
|
||||
UUID uuid = ((Player) sender).getUniqueId();
|
||||
String username = sender.getName();
|
||||
execute((Player) sender, uuid, username, args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.geysermc.floodgate.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.floodgate.AbstractUnlinkAccountCommand;
|
||||
import org.geysermc.floodgate.PlayerLink;
|
||||
import org.geysermc.floodgate.util.CommandUtil;
|
||||
import org.geysermc.floodgate.util.CommonMessage;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class UnlinkAccountCommand extends AbstractUnlinkAccountCommand<Player, CommandUtil> implements CommandExecutor {
|
||||
public UnlinkAccountCommand(PlayerLink link, CommandUtil commandUtil) {
|
||||
super(link, commandUtil);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage(CommonMessage.NOT_A_PLAYER.getMessage());
|
||||
return true;
|
||||
}
|
||||
UUID uuid = ((Player) sender).getUniqueId();
|
||||
execute((Player) sender, uuid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package org.geysermc.floodgate.injector;
|
||||
|
||||
import io.netty.channel.*;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.PacketHandler;
|
||||
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BukkitInjector {
|
||||
private static Object serverConnection;
|
||||
private static List<ChannelFuture> injectedClients = new ArrayList<>();
|
||||
|
||||
@Getter private static boolean injected = false;
|
||||
private static String injectedFieldName;
|
||||
|
||||
public static boolean inject() throws Exception {
|
||||
if (isInjected()) return true;
|
||||
if (getServerConnection() != null) {
|
||||
for (Field f : serverConnection.getClass().getDeclaredFields()) {
|
||||
if (f.getType() == List.class) {
|
||||
f.setAccessible(true);
|
||||
boolean rightList = ((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0] == ChannelFuture.class;
|
||||
if (!rightList) continue;
|
||||
|
||||
injectedFieldName = f.getName();
|
||||
List<?> newList = new CustomList((List<?>) f.get(serverConnection)) {
|
||||
@Override
|
||||
public void onAdd(Object o) {
|
||||
try {
|
||||
injectClient((ChannelFuture) o);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
synchronized (newList) {
|
||||
for (Object o : newList) {
|
||||
try {
|
||||
injectClient((ChannelFuture) o);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.set(serverConnection, newList);
|
||||
injected = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void injectClient(ChannelFuture future) {
|
||||
future.channel().pipeline().addFirst("floodgate-init", new ChannelInboundHandlerAdapter() {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
super.channelRead(ctx, msg);
|
||||
|
||||
Channel channel = (Channel) msg;
|
||||
channel.pipeline().addLast(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
channel.pipeline().addBefore("packet_handler", "floodgate_handler", new PacketHandler(future));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
injectedClients.add(future);
|
||||
}
|
||||
|
||||
public static void removeInjectedClient(ChannelFuture future, Channel channel) {
|
||||
if (channel != null) channel.pipeline().remove("floodgate_handler");
|
||||
injectedClients.remove(future);
|
||||
}
|
||||
|
||||
public static boolean removeInjection() throws Exception {
|
||||
if (!isInjected()) return true;
|
||||
for (ChannelFuture future : new ArrayList<>(injectedClients)) {
|
||||
removeInjectedClient(future, null);
|
||||
}
|
||||
|
||||
Object serverConnection = getServerConnection();
|
||||
if (serverConnection != null) {
|
||||
Field field = ReflectionUtil.getField(serverConnection.getClass(), injectedFieldName);
|
||||
List<?> list = (List<?>) ReflectionUtil.getValue(serverConnection, field);
|
||||
if (list instanceof CustomList) {
|
||||
ReflectionUtil.setValue(serverConnection, field, ((CustomList) list).getOriginalList());
|
||||
}
|
||||
}
|
||||
injectedFieldName = null;
|
||||
injected = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Object getServerConnection() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
if (serverConnection != null) return serverConnection;
|
||||
Class<?> minecraftServer = ReflectionUtil.getPrefixedClass("MinecraftServer");
|
||||
assert minecraftServer != null;
|
||||
|
||||
Object minecraftServerInstance = ReflectionUtil.invokeStatic(minecraftServer, "getServer");
|
||||
for (Method m : minecraftServer.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != null) {
|
||||
if (m.getReturnType().getSimpleName().equals("ServerConnection")) {
|
||||
if (m.getParameterTypes().length == 0) {
|
||||
serverConnection = m.invoke(minecraftServerInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serverConnection;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.geysermc.floodgate.command.CommandMessage;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class CommandUtil extends AbstractCommandResponseCache<String> implements ICommandUtil<Player> {
|
||||
private final Plugin plugin;
|
||||
|
||||
@Override
|
||||
public void sendMessage(Player player, CommandMessage message, Object... args) {
|
||||
player.sendMessage(format(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(Player player, CommandMessage message, Object... args) {
|
||||
// Have to run this in a non async thread so we don't get a `Asynchronous player kick!` error
|
||||
Bukkit.getScheduler().runTask(plugin, () -> player.kickPlayer(format(message, args)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String transformMessage(String message) {
|
||||
// unlike others, Bukkit doesn't have to transform a message into another class.
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
name: ${project.name}
|
||||
description: ${project.description}
|
||||
version: ${project.version}
|
||||
author: ${project.organization.name}
|
||||
website: ${project.url}
|
||||
main: org.geysermc.floodgate.BukkitPlugin
|
||||
api-version: 1.13
|
||||
commands:
|
||||
linkaccount:
|
||||
description: Link your Java and Bedrock accounts
|
||||
usage: /linkaccount [code]
|
||||
permission: floodgate.linkaccount
|
||||
permission-message: You don't have the floodgate.linkaccount permission.
|
||||
unlinkaccount:
|
||||
description: Unlink your Java account from your Bedrock account
|
||||
usage: /unlinkaccount
|
||||
permission: floodgate.unlinkaccount
|
||||
permission-message: You don't have the floodgate.unlinkaccount permission.
|
||||
permissions:
|
||||
floodgate.linkaccount:
|
||||
description: Allows players to link their Bedrock and Java accounts
|
||||
default: true
|
||||
floodgate.unlinkaccount:
|
||||
description: Allows players to unlink their Bedrock and Java accounts
|
||||
default: true
|
||||
@@ -3,13 +3,13 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>floodgate-parent</artifactId>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>floodgate-bungee</artifactId>
|
||||
<artifactId>bungee</artifactId>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
@@ -28,12 +28,12 @@
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-protocol</artifactId>
|
||||
<version>1.15-SNAPSHOT</version>
|
||||
<version>${bungee-version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>floodgate-common</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.protocol.MinecraftEncoder;
|
||||
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
|
||||
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
public class BungeeDebugger {
|
||||
public BungeeDebugger() {
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
Class<?> pipelineUtils = ReflectionUtil.getPrefixedClass("netty.PipelineUtils");
|
||||
Field framePrepender = ReflectionUtil.getField(pipelineUtils, "framePrepender");
|
||||
ReflectionUtil.setFinalValue(null, framePrepender, new CustomVarint21LengthFieldPrepender());
|
||||
}
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
private static class CustomVarint21LengthFieldPrepender extends Varint21LengthFieldPrepender {
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||
// we're getting called before the encoder and decoder are added,
|
||||
// so we have to wait with a nice while loop :D
|
||||
ctx.executor().execute(() -> {
|
||||
System.out.println("Channel: " + ctx.channel().isActive() + " " + ctx.channel().isOpen() + " " + ctx.channel().isRegistered());
|
||||
while (ctx.channel().isOpen()) {
|
||||
System.out.println("Trying to find decoder for " + getHostString(ctx, true) + " " + getParentName(ctx, true));
|
||||
if (ctx.channel().pipeline().get(MinecraftEncoder.class) != null) {
|
||||
System.out.println("Found decoder for " + getHostString(ctx, true));
|
||||
ctx.channel().pipeline().addLast(
|
||||
"floodgate-debug-init",
|
||||
new BungeeChannelInitializer(
|
||||
ctx.channel().parent() instanceof ServerSocketChannel
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHostString(ChannelHandlerContext ctx, boolean alwaysString) {
|
||||
SocketAddress address = ctx.channel().remoteAddress();
|
||||
return address != null ? ((InetSocketAddress) address).getHostString() : (alwaysString ? "null" : null);
|
||||
}
|
||||
|
||||
public static String getParentName(ChannelHandlerContext ctx, boolean alwaysString) {
|
||||
Channel parent = ctx.channel().parent();
|
||||
return parent != null ? parent.getClass().getSimpleName() : (alwaysString ? "null" : null);
|
||||
}
|
||||
|
||||
public static int executorsCount(ChannelHandlerContext ctx) {
|
||||
return ((MultithreadEventLoopGroup) ctx.channel().eventLoop().parent()).executorCount();
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class BungeeChannelInitializer extends ChannelInitializer<Channel> {
|
||||
private final boolean player;
|
||||
|
||||
@Override
|
||||
protected void initChannel(Channel channel) throws Exception {
|
||||
System.out.println("Init " + (player ? "Bungee" : "Server"));
|
||||
// can't add our debugger when the inbound-boss is missing
|
||||
if (channel.pipeline().get("packet-encoder") == null) return;
|
||||
if (channel.pipeline().get("packet-decoder") == null) return;
|
||||
channel.pipeline()
|
||||
.addBefore("packet-decoder", "floodgate-debug-in", new BungeeInPacketHandler(player))
|
||||
.addBefore("packet-encoder", "floodgate-debug-out", new BungeeOutPacketHandler(player));
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class BungeeInPacketHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
private final boolean player;
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
|
||||
int index = msg.readerIndex();
|
||||
System.out.println((player ? "Player -> Bungee" : "Server -> Bungee") + ":\n" + ByteBufUtil.prettyHexDump(msg));
|
||||
msg.readerIndex(index);
|
||||
ctx.fireChannelRead(msg.retain());
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class BungeeOutPacketHandler extends MessageToByteEncoder<ByteBuf> {
|
||||
private final boolean player;
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
|
||||
int index = msg.readerIndex();
|
||||
System.out.println((player ? "Bungee -> Player" : "Bungee -> Server") + ":\n" + ByteBufUtil.prettyHexDump(msg));
|
||||
msg.readerIndex(index);
|
||||
out.writeBytes(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
|
||||
public class BungeeFloodgateConfig extends FloodgateConfig {
|
||||
@JsonProperty(value = "send-floodgate-data")
|
||||
@Getter private boolean sendFloodgateData;
|
||||
}
|
||||
@@ -1,168 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
import net.md_5.bungee.protocol.packet.Handshake;
|
||||
import org.geysermc.floodgate.HandshakeHandler.HandshakeResult;
|
||||
import org.geysermc.floodgate.HandshakeHandler.ResultType;
|
||||
import org.geysermc.floodgate.command.LinkAccountCommand;
|
||||
import org.geysermc.floodgate.command.UnlinkAccountCommand;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.CommandUtil;
|
||||
import org.geysermc.floodgate.module.*;
|
||||
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static org.geysermc.floodgate.util.BedrockData.FLOODGATE_IDENTIFIER;
|
||||
|
||||
public class BungeePlugin extends Plugin implements Listener {
|
||||
@Getter private static BungeePlugin instance;
|
||||
private static Field extraHandshakeData;
|
||||
|
||||
@Getter private BungeeFloodgateConfig config;
|
||||
@Getter private PlayerLink playerLink;
|
||||
private BungeeDebugger debugger;
|
||||
private HandshakeHandler handshakeHandler;
|
||||
public final class BungeePlugin extends Plugin {
|
||||
private FloodgatePlatform platform;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
instance = this;
|
||||
if (!getDataFolder().exists()) {
|
||||
getDataFolder().mkdir();
|
||||
}
|
||||
config = FloodgateConfig.load(getLogger(), getDataFolder().toPath().resolve("config.yml"), BungeeFloodgateConfig.class);
|
||||
playerLink = PlayerLink.initialize(getLogger(), getDataFolder().toPath(), config);
|
||||
handshakeHandler = new HandshakeHandler(config.getPrivateKey(), true, config.getUsernamePrefix(), config.isReplaceSpaces());
|
||||
ReflectionUtil.setPrefix("net.md_5.bungee");
|
||||
|
||||
long ctm = System.currentTimeMillis();
|
||||
Injector injector = Guice.createInjector(
|
||||
new CommonModule(getDataFolder().toPath()),
|
||||
new BungeePlatformModule(this)
|
||||
);
|
||||
|
||||
long endCtm = System.currentTimeMillis();
|
||||
getLogger().info("Took " + (endCtm - ctm) + "ms to boot Floodgate");
|
||||
|
||||
// Bungeecord doesn't have a build-in function to disable plugins,
|
||||
// so there is no need to have a custom Platform class like Spigot
|
||||
platform = injector.getInstance(FloodgatePlatform.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getProxy().getPluginManager().registerListener(this, this);
|
||||
if (config.isDebug()) {
|
||||
debugger = new BungeeDebugger();
|
||||
}
|
||||
|
||||
CommandUtil commandUtil = new CommandUtil();
|
||||
getProxy().getPluginManager().registerCommand(this, new LinkAccountCommand(playerLink, commandUtil));
|
||||
getProxy().getPluginManager().registerCommand(this, new UnlinkAccountCommand(playerLink, commandUtil));
|
||||
platform.enable(new CommandModule(), new BungeeListenerModule(), new BungeeAddonModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (config.isDebug()) {
|
||||
getLogger().warning("Please note that it is not possible to reload this plugin when debug mode is enabled. At least for now");
|
||||
}
|
||||
playerLink.stop();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onServerConnect(ServerConnectEvent e) {
|
||||
// Passes the information through to the connecting server if enabled
|
||||
if (config.isSendFloodgateData() && FloodgateAPI.isBedrockPlayer(e.getPlayer())) {
|
||||
Handshake handshake = ReflectionUtil.getCastedValue(e.getPlayer().getPendingConnection(), "handshake", Handshake.class);
|
||||
handshake.setHost(
|
||||
handshake.getHost().split("\0")[0] + '\0' + // Ensures that only the hostname remains!
|
||||
FLOODGATE_IDENTIFIER + '\0' + FloodgateAPI.getEncryptedData(e.getPlayer().getUniqueId())
|
||||
);
|
||||
// Bungeecord will add his data after our data
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPreLogin(PreLoginEvent event) {
|
||||
event.registerIntent(this);
|
||||
getProxy().getScheduler().runAsync(this, () -> {
|
||||
String extraData = ReflectionUtil.getCastedValue(event.getConnection(), extraHandshakeData, String.class);
|
||||
|
||||
HandshakeResult result = handshakeHandler.handle(extraData);
|
||||
switch (result.getResultType()) {
|
||||
case SUCCESS:
|
||||
break;
|
||||
case EXCEPTION:
|
||||
event.setCancelReason(config.getMessages().getInvalidKey());
|
||||
break;
|
||||
case INVALID_DATA_LENGTH:
|
||||
event.setCancelReason(String.format(
|
||||
config.getMessages().getInvalidArgumentsLength(),
|
||||
BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength()
|
||||
));
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.getResultType() != ResultType.SUCCESS) {
|
||||
// only continue when SUCCESS
|
||||
event.completeIntent(this);
|
||||
return;
|
||||
}
|
||||
|
||||
FloodgatePlayer player = result.getFloodgatePlayer();
|
||||
FloodgateAPI.addEncryptedData(player.getCorrectUniqueId(), result.getHandshakeData()[2] + '\0' + result.getHandshakeData()[3]);
|
||||
|
||||
event.getConnection().setOnlineMode(false);
|
||||
event.getConnection().setUniqueId(player.getCorrectUniqueId());
|
||||
|
||||
ReflectionUtil.setValue(event.getConnection(), "name", player.getCorrectUsername());
|
||||
Object channelWrapper = ReflectionUtil.getValue(event.getConnection(), "ch");
|
||||
SocketAddress remoteAddress = ReflectionUtil.getCastedValue(channelWrapper, "remoteAddress", SocketAddress.class);
|
||||
if (!(remoteAddress instanceof InetSocketAddress)) {
|
||||
getLogger().info(
|
||||
"Player " + player.getUsername() + " doesn't use an InetSocketAddress. " +
|
||||
"It uses " + remoteAddress.getClass().getSimpleName() + ". Ignoring the player, I guess."
|
||||
);
|
||||
} else {
|
||||
ReflectionUtil.setValue(
|
||||
channelWrapper, "remoteAddress",
|
||||
new InetSocketAddress(result.getBedrockData().getIp(), ((InetSocketAddress) remoteAddress).getPort())
|
||||
);
|
||||
}
|
||||
event.completeIntent(this);
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPreLoginMonitor(PreLoginEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
FloodgateAPI.removePlayer(event.getConnection().getUniqueId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLogin(LoginEvent event) {
|
||||
// if there was another player with the same uuid / name online,
|
||||
// he has been disconnected by now
|
||||
FloodgatePlayer player = FloodgateAPI.getPlayerByConnection(event.getConnection());
|
||||
if (player != null) player.setLogin(false);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onLoginMonitor(LoginEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
FloodgateAPI.removePlayer(event.getConnection().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
if (FloodgateAPI.removePlayer(player.getUniqueId())) {
|
||||
FloodgateAPI.removeEncryptedData(player.getUniqueId());
|
||||
System.out.println("Removed Bedrock player who was logged in as " + player.getName() + " " + player.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
ReflectionUtil.setPrefix("net.md_5.bungee");
|
||||
Class<?> initial_handler = ReflectionUtil.getPrefixedClass("connection.InitialHandler");
|
||||
extraHandshakeData = ReflectionUtil.getField(initial_handler, "extraDataInHandshake");
|
||||
platform.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FloodgateAPI extends AbstractFloodgateAPI {
|
||||
private static Map<UUID, String> encryptedData = new HashMap<>();
|
||||
|
||||
static void addEncryptedData(UUID uuid, String encryptedData) {
|
||||
FloodgateAPI.encryptedData.put(uuid, encryptedData); // just override it I guess
|
||||
}
|
||||
|
||||
static void removeEncryptedData(UUID uuid) {
|
||||
encryptedData.remove(uuid);
|
||||
}
|
||||
|
||||
static String getEncryptedData(UUID uuid) {
|
||||
return encryptedData.get(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link AbstractFloodgateAPI#getPlayer(UUID)}
|
||||
*/
|
||||
public static FloodgatePlayer getPlayerByConnection(PendingConnection connection) {
|
||||
return getPlayer(connection.getUniqueId());
|
||||
}
|
||||
|
||||
public static boolean isBedrockPlayer(ProxiedPlayer player) {
|
||||
return isBedrockPlayer(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.command;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.geysermc.floodgate.BungeePlugin;
|
||||
import org.geysermc.floodgate.platform.command.Command;
|
||||
import org.geysermc.floodgate.platform.command.CommandRegistration;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class BungeeCommandRegistration implements CommandRegistration {
|
||||
private final BungeePlugin plugin;
|
||||
|
||||
@Override
|
||||
public void register(Command command) {
|
||||
ProxyServer.getInstance().getPluginManager().registerCommand(
|
||||
plugin, new BungeeCommandWrapper(command)
|
||||
);
|
||||
}
|
||||
|
||||
protected static class BungeeCommandWrapper extends net.md_5.bungee.api.plugin.Command {
|
||||
private final Command command;
|
||||
|
||||
public BungeeCommandWrapper(Command command) {
|
||||
super(command.getName());
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof ProxiedPlayer)) {
|
||||
if (command.isRequirePlayer()) {
|
||||
//todo let it use the response cache
|
||||
sender.sendMessage(CommonCommandMessage.NOT_A_PLAYER.getMessage());
|
||||
return;
|
||||
}
|
||||
command.execute(sender, args);
|
||||
return;
|
||||
}
|
||||
|
||||
UUID uuid = ((ProxiedPlayer) sender).getUniqueId();
|
||||
String username = sender.getName();
|
||||
command.execute(sender, uuid, username, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.geysermc.floodgate.command;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import org.geysermc.floodgate.AbstractLinkAccountCommand;
|
||||
import org.geysermc.floodgate.PlayerLink;
|
||||
import org.geysermc.floodgate.util.CommandUtil;
|
||||
import org.geysermc.floodgate.util.CommonMessage;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class LinkAccountCommand extends Command {
|
||||
private final LinkAccountCommandHandler handler;
|
||||
|
||||
public LinkAccountCommand(PlayerLink playerLink, CommandUtil responseCache) {
|
||||
super(CommandUtil.LINK_ACCOUNT_COMMAND);
|
||||
handler = new LinkAccountCommandHandler(playerLink, responseCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof ProxiedPlayer)) {
|
||||
sender.sendMessage(CommonMessage.NOT_A_PLAYER.getMessage());
|
||||
return;
|
||||
}
|
||||
UUID uuid = ((ProxiedPlayer) sender).getUniqueId();
|
||||
String username = sender.getName();
|
||||
handler.execute((ProxiedPlayer) sender, uuid, username, args);
|
||||
}
|
||||
|
||||
private static class LinkAccountCommandHandler extends AbstractLinkAccountCommand<ProxiedPlayer, CommandUtil> {
|
||||
public LinkAccountCommandHandler(PlayerLink link, CommandUtil commandUtil) {
|
||||
super(link, commandUtil);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.geysermc.floodgate.command;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import org.geysermc.floodgate.AbstractUnlinkAccountCommand;
|
||||
import org.geysermc.floodgate.PlayerLink;
|
||||
import org.geysermc.floodgate.util.CommandUtil;
|
||||
import org.geysermc.floodgate.util.CommonMessage;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class UnlinkAccountCommand extends Command {
|
||||
private final UnlinkAccountCommandHandler handler;
|
||||
|
||||
public UnlinkAccountCommand(PlayerLink playerLink, CommandUtil commandUtil) {
|
||||
super(CommandUtil.UNLINK_ACCOUNT_COMMAND);
|
||||
handler = new UnlinkAccountCommandHandler(playerLink, commandUtil);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof ProxiedPlayer)) {
|
||||
sender.sendMessage(CommonMessage.NOT_A_PLAYER.getMessage());
|
||||
return;
|
||||
}
|
||||
UUID uuid = ((ProxiedPlayer) sender).getUniqueId();
|
||||
handler.execute((ProxiedPlayer) sender, uuid);
|
||||
}
|
||||
|
||||
private static class UnlinkAccountCommandHandler extends AbstractUnlinkAccountCommand<ProxiedPlayer, CommandUtil> {
|
||||
public UnlinkAccountCommandHandler(PlayerLink link, CommandUtil commandUtil) {
|
||||
super(link, commandUtil);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.handler;
|
||||
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.protocol.packet.Handshake;
|
||||
import org.geysermc.floodgate.HandshakeHandler;
|
||||
import org.geysermc.floodgate.HandshakeHandler.HandshakeResult;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.geysermc.floodgate.HandshakeHandler.*;
|
||||
import static org.geysermc.floodgate.util.BedrockData.FLOODGATE_IDENTIFIER;
|
||||
|
||||
public final class BungeeDataHandler {
|
||||
private static final Field EXTRA_HANDSHAKE_DATA;
|
||||
private static final Field PLAYER_NAME;
|
||||
private static final Field PLAYER_CHANNEL_WRAPPER;
|
||||
private static final Field PLAYER_REMOTE_ADDRESS;
|
||||
private static final Field CACHED_HANDSHAKE_PACKET;
|
||||
|
||||
private final Plugin plugin;
|
||||
private final ProxyFloodgateConfig config;
|
||||
private final ProxyFloodgateApi api;
|
||||
private final HandshakeHandler handler;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
public BungeeDataHandler(Plugin plugin, ProxyFloodgateConfig config, ProxyFloodgateApi api,
|
||||
HandshakeHandler handshakeHandler, FloodgateLogger logger) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.handler = handshakeHandler;
|
||||
this.api = api;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void handle(PreLoginEvent event) {
|
||||
event.registerIntent(plugin);
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, () -> {
|
||||
String extraData = ReflectionUtil.getCastedValue(
|
||||
event.getConnection(), EXTRA_HANDSHAKE_DATA
|
||||
);
|
||||
|
||||
HandshakeResult result = handler.handle(extraData);
|
||||
switch (result.getResultType()) {
|
||||
case SUCCESS:
|
||||
break;
|
||||
case EXCEPTION:
|
||||
event.setCancelReason(config.getMessages().getInvalidKey());
|
||||
break;
|
||||
case INVALID_DATA_LENGTH:
|
||||
event.setCancelReason(String.format(
|
||||
config.getMessages().getInvalidArgumentsLength(),
|
||||
BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength()
|
||||
));
|
||||
break;
|
||||
}
|
||||
|
||||
// only continue when SUCCESS
|
||||
if (result.getResultType() != ResultType.SUCCESS) {
|
||||
event.completeIntent(plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
FloodgatePlayer player = result.getFloodgatePlayer();
|
||||
api.addEncryptedData(
|
||||
player.getCorrectUniqueId(),
|
||||
result.getHandshakeData()[2] + '\0' + result.getHandshakeData()[3]
|
||||
);
|
||||
|
||||
event.getConnection().setOnlineMode(false);
|
||||
event.getConnection().setUniqueId(player.getCorrectUniqueId());
|
||||
|
||||
ReflectionUtil.setValue(
|
||||
event.getConnection(), PLAYER_NAME, player.getCorrectUsername()
|
||||
);
|
||||
|
||||
Object channelWrapper =
|
||||
ReflectionUtil.getValue(event.getConnection(), PLAYER_CHANNEL_WRAPPER);
|
||||
|
||||
SocketAddress remoteAddress =
|
||||
ReflectionUtil.getCastedValue(channelWrapper, PLAYER_REMOTE_ADDRESS);
|
||||
|
||||
if (!(remoteAddress instanceof InetSocketAddress)) {
|
||||
logger.info("Player {} doesn't use an InetSocketAddress. " +
|
||||
"It uses {}. Ignoring the player, I guess.",
|
||||
player.getUsername(), remoteAddress.getClass().getSimpleName()
|
||||
);
|
||||
} else {
|
||||
int port = ((InetSocketAddress) remoteAddress).getPort();
|
||||
ReflectionUtil.setValue(
|
||||
channelWrapper, PLAYER_REMOTE_ADDRESS,
|
||||
new InetSocketAddress(result.getBedrockData().getIp(), port)
|
||||
);
|
||||
}
|
||||
event.completeIntent(plugin);
|
||||
});
|
||||
}
|
||||
|
||||
public void handle(ServerConnectEvent event) {
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
// Passes the information through to the connecting server if enabled
|
||||
if (config.isSendFloodgateData() && api.isBedrockPlayer(player.getUniqueId())) {
|
||||
Handshake handshake = ReflectionUtil.getCastedValue(
|
||||
player.getPendingConnection(), CACHED_HANDSHAKE_PACKET
|
||||
);
|
||||
|
||||
// Ensures that only the hostname remains,
|
||||
// this way it can't mess up the Floodgate data format
|
||||
String initialHostname = handshake.getHost().split("\0")[0];
|
||||
handshake.setHost(initialHostname + '\0' + FLOODGATE_IDENTIFIER + '\0' +
|
||||
api.getEncryptedData(player.getUniqueId())
|
||||
);
|
||||
|
||||
// Bungeecord will add his data after our data
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
Class<?> initialHandler = ReflectionUtil.getPrefixedClass("connection.InitialHandler");
|
||||
EXTRA_HANDSHAKE_DATA = ReflectionUtil.getField(initialHandler, "extraDataInHandshake");
|
||||
checkNotNull(EXTRA_HANDSHAKE_DATA, "extraDataInHandshake field cannot be null");
|
||||
|
||||
PLAYER_NAME = ReflectionUtil.getField(initialHandler, "name");
|
||||
checkNotNull(PLAYER_NAME, "Initial name field cannot be null");
|
||||
|
||||
Class<?> channelWrapper = ReflectionUtil.getPrefixedClass("netty.ChannelWrapper");
|
||||
PLAYER_CHANNEL_WRAPPER = ReflectionUtil.getFieldOfType(initialHandler, channelWrapper);
|
||||
checkNotNull(PLAYER_CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
|
||||
PLAYER_REMOTE_ADDRESS = ReflectionUtil.getFieldOfType(channelWrapper, SocketAddress.class);
|
||||
checkNotNull(PLAYER_REMOTE_ADDRESS, "Remote address field cannot be null");
|
||||
|
||||
Class<?> handshakePacket = ReflectionUtil.getPrefixedClass("protocol.packet.Handshake");
|
||||
CACHED_HANDSHAKE_PACKET = ReflectionUtil.getFieldOfType(initialHandler, handshakePacket);
|
||||
checkNotNull(CACHED_HANDSHAKE_PACKET, "Cached handshake packet field cannot be null");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.inject.bungee;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.protocol.MinecraftEncoder;
|
||||
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||
|
||||
import javax.naming.OperationNotSupportedException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class BungeeInjector extends CommonPlatformInjector {
|
||||
private final FloodgateLogger logger;
|
||||
@Getter
|
||||
private boolean injected;
|
||||
|
||||
@Override
|
||||
public boolean inject() {
|
||||
Class<?> pipelineUtils = ReflectionUtil.getPrefixedClass("netty.PipelineUtils");
|
||||
Field framePrepender = ReflectionUtil.getField(pipelineUtils, "framePrepender");
|
||||
Object customPrepender = new CustomVarint21LengthFieldPrepender(this, logger);
|
||||
|
||||
ReflectionUtil.setFinalValue(null, framePrepender, customPrepender);
|
||||
|
||||
injected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeInjection() throws Exception {
|
||||
//todo implement injection removal support
|
||||
throw new OperationNotSupportedException(
|
||||
"Floodgate cannot remove the Bungee injection at the moment");
|
||||
}
|
||||
|
||||
public void injectClient(Channel channel) {
|
||||
boolean clientToProxy = channel.parent() instanceof ServerSocketChannel;
|
||||
logger.info("Client to proxy? " + clientToProxy);
|
||||
|
||||
channel.pipeline().addLast(new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) {
|
||||
injectAddonsCall(channel, !clientToProxy);
|
||||
addInjectedClient(channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@ChannelHandler.Sharable
|
||||
private static class CustomVarint21LengthFieldPrepender extends Varint21LengthFieldPrepender {
|
||||
private final BungeeInjector injector;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
// we're getting called before the encoder and decoder are added,
|
||||
// so we have to wait a little, so we have a nice while loop here :D
|
||||
//todo look if we can make this nicer
|
||||
ctx.executor().execute(() -> {
|
||||
logger.debug("Channel: {} {} {}",
|
||||
ctx.channel().isActive(),
|
||||
ctx.channel().isOpen(),
|
||||
ctx.channel().isRegistered()
|
||||
);
|
||||
|
||||
long ctm = System.currentTimeMillis();
|
||||
while (ctx.channel().isOpen()) {
|
||||
logger.debug("Trying to find decoder for {} {}",
|
||||
getHostString(ctx, true),
|
||||
getParentName(ctx, true)
|
||||
);
|
||||
|
||||
if (ctx.channel().pipeline().get(MinecraftEncoder.class) != null) {
|
||||
logger.debug("Found decoder for {}",
|
||||
getHostString(ctx, true)
|
||||
);
|
||||
|
||||
injector.injectClient(ctx.channel());
|
||||
break;
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - ctm > 3000) {
|
||||
logger.error("Failed to find decoder for client after 3 seconds!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHostString(ChannelHandlerContext ctx, boolean alwaysString) {
|
||||
SocketAddress address = ctx.channel().remoteAddress();
|
||||
if (address != null) {
|
||||
return ((InetSocketAddress) address).getHostString();
|
||||
}
|
||||
return alwaysString ? "null" : null;
|
||||
}
|
||||
|
||||
public static String getParentName(ChannelHandlerContext ctx, boolean alwaysString) {
|
||||
Channel parent = ctx.channel().parent();
|
||||
if (parent != null) {
|
||||
return parent.getClass().getSimpleName();
|
||||
}
|
||||
return alwaysString ? "null" : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.geysermc.floodgate.listener;
|
||||
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
import org.geysermc.floodgate.FloodgatePlayerImpl;
|
||||
import org.geysermc.floodgate.HandshakeHandler;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.handler.BungeeDataHandler;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class BungeeListener implements Listener {
|
||||
private final BungeeDataHandler dataHandler;
|
||||
private final ProxyFloodgateApi api;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
public BungeeListener(Plugin plugin, ProxyFloodgateConfig config, ProxyFloodgateApi api,
|
||||
HandshakeHandler handshakeHandler, FloodgateLogger logger) {
|
||||
this.dataHandler = new BungeeDataHandler(plugin, config, api, handshakeHandler, logger);
|
||||
this.api = api;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onServerConnect(ServerConnectEvent event) {
|
||||
dataHandler.handle(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPreLogin(PreLoginEvent event) {
|
||||
dataHandler.handle(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPreLoginMonitor(PreLoginEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
api.removePlayer(event.getConnection().getUniqueId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLogin(LoginEvent event) {
|
||||
// if there was another player with the same uuid / name online,
|
||||
// he has been disconnected by now
|
||||
UUID uniqueId = event.getConnection().getUniqueId();
|
||||
FloodgatePlayer player = api.getPlayer(uniqueId);
|
||||
logger.info("Login" + (player != null) + " " + uniqueId);
|
||||
if (player != null) {
|
||||
player.as(FloodgatePlayerImpl.class).setLogin(false);
|
||||
logger.info("Floodgate player who is logged in as {} {} joined",
|
||||
player.getCorrectUsername(), player.getCorrectUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onLoginMonitor(LoginEvent event) {
|
||||
logger.info("LoginMonitor" + event.isCancelled());
|
||||
if (event.isCancelled()) {
|
||||
api.removePlayer(event.getConnection().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
FloodgatePlayer fPlayer;
|
||||
if ((fPlayer = api.removePlayer(player.getUniqueId())) != null) {
|
||||
api.removeEncryptedData(player.getUniqueId());
|
||||
logger.info(
|
||||
"Floodgate player who was logged in as {} {} disconnected",
|
||||
player.getName(), player.getUniqueId()
|
||||
);
|
||||
}
|
||||
logger.info("Disconnected " + fPlayer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.listener;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import org.geysermc.floodgate.BungeePlugin;
|
||||
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
||||
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Inject))
|
||||
public final class BungeeListenerRegistration implements ListenerRegistration<Listener> {
|
||||
private final BungeePlugin plugin;
|
||||
|
||||
@Override
|
||||
public void register(Listener listener) {
|
||||
ProxyServer.getInstance().getPluginManager().registerListener(plugin, listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.module;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.multibindings.ProvidesIntoSet;
|
||||
import org.geysermc.floodgate.addon.AddonManagerAddon;
|
||||
import org.geysermc.floodgate.addon.DebugAddon;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.register.AddonRegister;
|
||||
|
||||
public final class BungeeAddonModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(AddonRegister.class).asEagerSingleton();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public InjectorAddon managerAddon() {
|
||||
return new AddonManagerAddon();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public InjectorAddon debugAddon() {
|
||||
return new DebugAddon();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.module;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.multibindings.ProvidesIntoSet;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.geysermc.floodgate.HandshakeHandler;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.listener.BungeeListener;
|
||||
import org.geysermc.floodgate.register.ListenerRegister;
|
||||
|
||||
public final class BungeeListenerModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<ListenerRegister<Listener>>() {}).asEagerSingleton();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public Listener bungeeListener(Plugin plugin, ProxyFloodgateConfig config,
|
||||
ProxyFloodgateApi api, HandshakeHandler handshakeHandler,
|
||||
FloodgateLogger logger) {
|
||||
return new BungeeListener(plugin, config, api, handshakeHandler, logger);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.module;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.geysermc.floodgate.BungeePlugin;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.command.BungeeCommandRegistration;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
import org.geysermc.floodgate.inject.bungee.BungeeInjector;
|
||||
import org.geysermc.floodgate.listener.BungeeListenerRegistration;
|
||||
import org.geysermc.floodgate.logger.JavaDefaultFloodgateLogger;
|
||||
import org.geysermc.floodgate.platform.command.CommandRegistration;
|
||||
import org.geysermc.floodgate.platform.command.util.CommandUtil;
|
||||
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
||||
import org.geysermc.floodgate.util.BungeeCommandUtil;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class BungeePlatformModule extends AbstractModule {
|
||||
private final BungeePlugin plugin;
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SimpleFloodgateApi.class).to(ProxyFloodgateApi.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public Plugin bungeePlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("configClass")
|
||||
public Class<? extends FloodgateConfig> floodgateConfigClass() {
|
||||
return ProxyFloodgateConfig.class;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ProxyFloodgateApi proxyFloodgateApi() {
|
||||
return new ProxyFloodgateApi();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public FloodgateLogger floodgateLogger() {
|
||||
return new JavaDefaultFloodgateLogger(plugin.getLogger());
|
||||
}
|
||||
|
||||
/*
|
||||
Commands / Listeners
|
||||
*/
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommandRegistration commandRegistration() {
|
||||
return new BungeeCommandRegistration(plugin);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommandUtil commandUtil(FloodgateLogger logger) {
|
||||
return new BungeeCommandUtil(logger);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ListenerRegistration<Listener> listenerRegistration() {
|
||||
return new BungeeListenerRegistration(plugin);
|
||||
}
|
||||
|
||||
/*
|
||||
DebugAddon / PlatformInjector
|
||||
*/
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommonPlatformInjector platformInjector(FloodgateLogger logger) {
|
||||
return new BungeeInjector(logger);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("packetEncoder")
|
||||
public String packetEncoder() {
|
||||
return "packet-encoder";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("packetDecoder")
|
||||
public String packetDecoder() {
|
||||
return "packet-decoder";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("packetHandler")
|
||||
public String packetHandler() {
|
||||
return "inbound-boss";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("implementationName")
|
||||
public String implementationName() {
|
||||
return "Bungeecord";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.platform.command.CommandMessage;
|
||||
import org.geysermc.floodgate.platform.command.util.CommandResponseCache;
|
||||
import org.geysermc.floodgate.platform.command.util.CommandUtil;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class BungeeCommandUtil extends CommandResponseCache<BaseComponent[]> implements CommandUtil {
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public void sendMessage(Object player, CommandMessage message, Object... args) {
|
||||
cast(player).sendMessage(getOrAddCachedMessage(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(Object player, CommandMessage message, Object... args) {
|
||||
cast(player).disconnect(getOrAddCachedMessage(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseComponent[] transformMessage(String message) {
|
||||
return TextComponent.fromLegacyText(message);
|
||||
}
|
||||
|
||||
protected ProxiedPlayer cast(Object player) {
|
||||
try {
|
||||
return (ProxiedPlayer) player;
|
||||
} catch (ClassCastException exception) {
|
||||
logger.error("Failed to cast {} to ProxiedPlayer", player.getClass().getName());
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.geysermc.floodgate.command.CommandMessage;
|
||||
|
||||
public class CommandUtil extends AbstractCommandResponseCache<BaseComponent[]> implements ICommandUtil<ProxiedPlayer> {
|
||||
@Override
|
||||
public void sendMessage(ProxiedPlayer player, CommandMessage message, Object... args) {
|
||||
player.sendMessage(getOrAddCachedMessage(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(ProxiedPlayer player, CommandMessage message, Object... args) {
|
||||
player.disconnect(getOrAddCachedMessage(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseComponent[] transformMessage(String message) {
|
||||
return TextComponent.fromLegacyText(message);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,5 @@
|
||||
name: ${project.name}
|
||||
name: ${outputName}
|
||||
description: ${project.description}
|
||||
version: ${project.version}
|
||||
author: ${project.organization.name}
|
||||
main: org.geysermc.floodgate.BungeePlugin
|
||||
|
||||
# Configuration for player linking
|
||||
player-link:
|
||||
# Whether to enable the linking system. Turning this off will prevent
|
||||
# players from using the linking feature even if they are already linked.
|
||||
enable: false
|
||||
# The type of storage system you want to use
|
||||
# Currently implemented: SQLite
|
||||
type: sqlite
|
||||
# Whether to allow the use of /linkaccount and /unlinkaccount
|
||||
# You can also use allow specific people to use the commands using the
|
||||
# permissions floodgate.linkaccount and floodgate.unlinkaccount.
|
||||
# This is only for linking, already connected people will stay connected
|
||||
allow-linking: true
|
||||
# The amount of time until a link code expires in seconds
|
||||
link-code-timeout: 300
|
||||
main: org.geysermc.floodgate.BungeePlugin
|
||||
@@ -3,13 +3,13 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>floodgate-parent</artifactId>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>floodgate-common</artifactId>
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
@@ -36,28 +36,31 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${geyser-version}</version>
|
||||
<scope>compile</scope>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.10.4</version>
|
||||
<scope>compile</scope>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>4.2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>4.1.49.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>4.1.49.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
<version>2.9.9</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.30.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
abstract class AbstractFloodgateAPI {
|
||||
static final Map<UUID, FloodgatePlayer> players = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Get info about the given Bedrock player
|
||||
* @param uuid the uuid of the <b>online</b> Bedrock player
|
||||
* @return FloodgatePlayer if the given uuid is a Bedrock player
|
||||
*/
|
||||
public static FloodgatePlayer getPlayer(UUID uuid) {
|
||||
FloodgatePlayer player = players.get(uuid);
|
||||
if (player != null || isFloodgateId(uuid)) return player;
|
||||
// make it possible to find player by Java id (for example for a linked player)
|
||||
for (FloodgatePlayer player1 : players.values()) {
|
||||
if (player1.getCorrectUniqueId().equals(uuid)) {
|
||||
return player1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a player (should only be used internally)
|
||||
* @param onlineId The UUID of the online player
|
||||
* @param removeLogin true if it should remove a sessions who is still logging in
|
||||
* @return true if player was a LinkedPlayer
|
||||
*/
|
||||
static boolean removePlayer(UUID onlineId, boolean removeLogin) {
|
||||
FloodgatePlayer player = players.get(onlineId);
|
||||
// the player is a non-linked player or a linked player but somehow someone tried to
|
||||
// remove the player by his xuid, we have to find out
|
||||
if (player != null) {
|
||||
// we don't allow them to remove a player by his xuid
|
||||
// because a linked player is never registered by his linked java uuid
|
||||
if (player.getLinkedPlayer() != null) return false;
|
||||
|
||||
// removeLogin logics
|
||||
if (player.isLogin() && !removeLogin || !player.isLogin() && removeLogin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// passed the test
|
||||
players.remove(onlineId);
|
||||
// was the account linked?
|
||||
return player.getLinkedPlayer() != null;
|
||||
}
|
||||
|
||||
// we still want to be able to remove a linked-player by his linked java uuid
|
||||
for (FloodgatePlayer player1 : players.values()) {
|
||||
if (player1.isLogin() && !removeLogin || !player1.isLogin() && removeLogin) continue;
|
||||
if (!player1.getCorrectUniqueId().equals(onlineId)) continue;
|
||||
players.remove(createJavaPlayerId(Long.parseLong(player1.getXuid())));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #removePlayer(UUID, boolean)} but with removeLogin on false
|
||||
*/
|
||||
static boolean removePlayer(UUID onlineId) {
|
||||
return removePlayer(onlineId, false);
|
||||
}
|
||||
|
||||
static boolean removePlayer(FloodgatePlayer player) {
|
||||
boolean removed = players.remove(createJavaPlayerId(Long.parseLong(player.getXuid())), player);
|
||||
return removed && player.getLinkedPlayer() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if the given <b>online</b> player is a bedrock player
|
||||
* @param uuid The uuid of the <b>online</b> player
|
||||
* @return true if the given <b>online</b> player is a Bedrock player
|
||||
*/
|
||||
public static boolean isBedrockPlayer(UUID uuid) {
|
||||
return getPlayer(uuid) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a valid Java player uuid of a xuid
|
||||
*/
|
||||
public static UUID createJavaPlayerId(long xuid) {
|
||||
return new UUID(0, xuid);
|
||||
}
|
||||
|
||||
public static boolean isFloodgateId(UUID uuid) {
|
||||
return uuid.getMostSignificantBits() == 0;
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.command.CommandMessage;
|
||||
import org.geysermc.floodgate.util.CommonMessage;
|
||||
import org.geysermc.floodgate.util.ICommandUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class AbstractLinkAccountCommand<P, C extends ICommandUtil<P>> {
|
||||
private final Map<String, LinkRequest> activeLinkRequests = new HashMap<>();
|
||||
private final PlayerLink link;
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final C commandUtil;
|
||||
|
||||
public void execute(P player, UUID uuid, String username, String[] args) {
|
||||
if (!PlayerLink.isEnabledAndAllowed()) {
|
||||
sendMessage(player, Message.LINK_REQUEST_DISABLED);
|
||||
return;
|
||||
}
|
||||
link.isLinkedPlayer(uuid).whenComplete((linked, throwable) -> {
|
||||
if (throwable != null) {
|
||||
sendMessage(player, CommonMessage.IS_LINKED_ERROR);
|
||||
return;
|
||||
}
|
||||
if (linked) {
|
||||
sendMessage(player, Message.ALREADY_LINKED);
|
||||
return;
|
||||
}
|
||||
// when the player is a Java player
|
||||
if (!AbstractFloodgateAPI.isBedrockPlayer(uuid)) {
|
||||
if (args.length != 1) {
|
||||
sendMessage(player, Message.JAVA_USAGE);
|
||||
return;
|
||||
}
|
||||
String code = String.format("%04d", new Random().nextInt(10000));
|
||||
String bedrockUsername = args[0];
|
||||
activeLinkRequests.put(username, new LinkRequest(username, uuid, code, bedrockUsername));
|
||||
sendMessage(player, Message.LINK_REQUEST_CREATED, bedrockUsername, username, code);
|
||||
return;
|
||||
}
|
||||
// when the player is a Bedrock player
|
||||
if (args.length != 2) {
|
||||
sendMessage(player, Message.BEDROCK_USAGE);
|
||||
return;
|
||||
}
|
||||
String javaUsername = args[0];
|
||||
String code = args[1];
|
||||
LinkRequest request = activeLinkRequests.getOrDefault(javaUsername, null);
|
||||
if (request != null && request.checkGamerTag(AbstractFloodgateAPI.getPlayer(uuid))) {
|
||||
if (request.getLinkCode().equals(code)) {
|
||||
activeLinkRequests.remove(javaUsername); // Delete the request, whether it has expired or is successful
|
||||
if (request.isExpired()) {
|
||||
sendMessage(player, Message.LINK_REQUEST_EXPIRED);
|
||||
return;
|
||||
}
|
||||
link.linkPlayer(uuid, request.getJavaUniqueId(), request.getJavaUsername()).whenComplete((aVoid, throwable1) -> {
|
||||
if (throwable1 != null) {
|
||||
sendMessage(player, Message.LINK_REQUEST_ERROR);
|
||||
return;
|
||||
}
|
||||
commandUtil.kickPlayer(player, Message.LINK_REQUEST_COMPLETED, request.getJavaUsername());
|
||||
});
|
||||
return;
|
||||
}
|
||||
sendMessage(player, Message.INVALID_CODE);
|
||||
return;
|
||||
}
|
||||
sendMessage(player, Message.NO_LINK_REQUESTED);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMessage(P player, CommandMessage message, Object... args) {
|
||||
commandUtil.sendMessage(player, message, args);
|
||||
}
|
||||
|
||||
public enum Message implements CommandMessage {
|
||||
ALREADY_LINKED(
|
||||
"&cYour account is already linked!\n" +
|
||||
"&cIf you want to link to a different account, run &6/unlinkaccount&c and try it again."
|
||||
),
|
||||
JAVA_USAGE("&cUsage: /linkaccount <gamertag>"),
|
||||
LINK_REQUEST_CREATED(
|
||||
"&aLog in as %s on Bedrock and run &6/linkaccount %s %s\n" +
|
||||
"&cWarning: Any progress on your Bedrock account will not be carried over! Save any items in your inventory first.\n" +
|
||||
"&cIf you change your mind you can run &6/unlinkaccount&c to get your progess back."
|
||||
),
|
||||
BEDROCK_USAGE("&cStart the process from Java! Usage: /linkaccount <gamertag>"),
|
||||
LINK_REQUEST_EXPIRED("&cThe code you entered is expired! Run &6/linkaccount&c again on your Java account"),
|
||||
LINK_REQUEST_COMPLETED("You are successfully linked to %s!\nIf you want to undo this run /unlinkaccount"),
|
||||
LINK_REQUEST_ERROR("&cAn error occurred while linking. " + CommonMessage.CHECK_CONSOLE),
|
||||
INVALID_CODE("&cInvalid code! Please check your code or run the &6/linkaccount&c command again on your Java account."),
|
||||
NO_LINK_REQUESTED("&cThis player has not requested an account link! Please log in on Java and request one with &6/linkaccount"),
|
||||
LINK_REQUEST_DISABLED("&cLinking is not enabled on this server.");
|
||||
|
||||
@Getter private final String message;
|
||||
|
||||
Message(String message) {
|
||||
this.message = message.replace('&', COLOR_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.command.CommandMessage;
|
||||
import org.geysermc.floodgate.util.CommonMessage;
|
||||
import org.geysermc.floodgate.util.ICommandUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class AbstractUnlinkAccountCommand<P, C extends ICommandUtil<P>> {
|
||||
private final PlayerLink link;
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final C commandUtil;
|
||||
|
||||
public void execute(P player, UUID uuid) {
|
||||
if (!PlayerLink.isEnabledAndAllowed()) {
|
||||
sendMessage(player, Message.LINKING_NOT_ENABLED);
|
||||
return;
|
||||
}
|
||||
link.isLinkedPlayer(uuid).whenComplete((linked, throwable) -> {
|
||||
if (throwable != null) {
|
||||
sendMessage(player, CommonMessage.IS_LINKED_ERROR);
|
||||
return;
|
||||
}
|
||||
if (!linked) {
|
||||
sendMessage(player, Message.NOT_LINKED);
|
||||
return;
|
||||
}
|
||||
link.unlinkPlayer(uuid).whenComplete((aVoid, throwable1) ->
|
||||
sendMessage(player, throwable1 == null ? Message.UNLINK_SUCCESS : Message.UNLINK_ERROR)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMessage(P player, CommandMessage message, Object... args) {
|
||||
commandUtil.sendMessage(player, message, args);
|
||||
}
|
||||
|
||||
public enum Message implements CommandMessage {
|
||||
NOT_LINKED("&cYour account isn't linked"),
|
||||
UNLINK_SUCCESS("&cUnlink successful! Rejoin to return to your Bedrock account"),
|
||||
UNLINK_ERROR("&cAn error occurred while unlinking player! " + CommonMessage.CHECK_CONSOLE),
|
||||
LINKING_NOT_ENABLED("&cLinking is not enabled on this server");
|
||||
|
||||
@Getter
|
||||
private final String message;
|
||||
|
||||
Message(String message) {
|
||||
this.message = message.replace('&', COLOR_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.util.EncryptionUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Base64;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@Getter
|
||||
public class FloodgateConfig {
|
||||
@JsonProperty(value = "key-file-name")
|
||||
private String keyFileName;
|
||||
@JsonProperty(value = "disconnect")
|
||||
private DisconnectMessages messages;
|
||||
@JsonProperty(value = "username-prefix")
|
||||
private String usernamePrefix;
|
||||
@JsonProperty(value = "replace-spaces")
|
||||
private boolean replaceSpaces;
|
||||
|
||||
@JsonProperty(value = "player-link")
|
||||
private PlayerLinkConfig playerLink;
|
||||
|
||||
@JsonProperty
|
||||
private boolean debug;
|
||||
|
||||
@JsonIgnore
|
||||
PrivateKey privateKey = null;
|
||||
|
||||
@Getter
|
||||
public static class DisconnectMessages {
|
||||
@JsonProperty("invalid-key")
|
||||
private String invalidKey;
|
||||
@JsonProperty("invalid-arguments-length")
|
||||
private String invalidArgumentsLength;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class PlayerLinkConfig {
|
||||
@JsonProperty("enable")
|
||||
private boolean enabled;
|
||||
@JsonProperty("type")
|
||||
private String type;
|
||||
@JsonProperty("allow-linking")
|
||||
private boolean allowLinking;
|
||||
@JsonProperty("link-code-timeout")
|
||||
private long linkCodeTimeout;
|
||||
}
|
||||
|
||||
public static FloodgateConfig load(Logger logger, Path configPath) {
|
||||
return load(logger, configPath, FloodgateConfig.class);
|
||||
}
|
||||
|
||||
public static <T extends FloodgateConfig> T load(Logger logger, Path configPath, Class<T> configClass) {
|
||||
T config = null;
|
||||
try {
|
||||
try {
|
||||
if (!configPath.toFile().exists()) {
|
||||
Files.copy(FloodgateConfig.class.getClassLoader().getResourceAsStream("config.yml"), configPath);
|
||||
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
|
||||
generator.initialize(2048);
|
||||
KeyPair keyPair = generator.generateKeyPair();
|
||||
|
||||
String test = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||
|
||||
String encrypted = EncryptionUtil.encrypt(keyPair.getPublic(), test);
|
||||
String decrypted = new String(EncryptionUtil.decrypt(keyPair.getPrivate(), encrypted));
|
||||
|
||||
if (!test.equals(decrypted)) {
|
||||
System.out.println(test +" "+ decrypted +" "+ encrypted +" " + new String(Base64.getDecoder().decode(encrypted.split("\0")[1])));
|
||||
throw new RuntimeException(
|
||||
"Testing the private and public key failed," +
|
||||
"the original message isn't the same as the decrypted!"
|
||||
);
|
||||
}
|
||||
|
||||
Files.write(configPath.getParent().resolve("encrypted.txt"), encrypted.getBytes());
|
||||
|
||||
Files.write(configPath.getParent().resolve("public-key.pem"), keyPair.getPublic().getEncoded());
|
||||
Files.write(configPath.getParent().resolve("key.pem"), keyPair.getPrivate().getEncoded());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error while creating config", e);
|
||||
}
|
||||
|
||||
config = new ObjectMapper(new YAMLFactory()).readValue(
|
||||
Files.readAllBytes(configPath), configClass
|
||||
);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error while loading config", e);
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
throw new RuntimeException("Failed to load config file! Try to delete the data folder of Floodgate");
|
||||
}
|
||||
|
||||
try {
|
||||
config.privateKey = EncryptionUtil.getKeyFromFile(
|
||||
configPath.getParent().resolve(config.getKeyFileName()),
|
||||
PrivateKey.class
|
||||
);
|
||||
} catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
logger.log(Level.SEVERE, "Error while reading private key", e);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.name.Named;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.InstanceHolder;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.loader.ConfigLoader;
|
||||
import org.geysermc.floodgate.link.PlayerLinkLoader;
|
||||
import org.geysermc.floodgate.module.ConfigLoadedModule;
|
||||
import org.geysermc.floodgate.module.PostInitializeModule;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FloodgatePlatform {
|
||||
private static final UUID KEY = UUID.randomUUID();
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final FloodgateConfig config;
|
||||
private final FloodgateApi api;
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
private final Injector guice;
|
||||
|
||||
@Inject private PlatformInjector injector;
|
||||
|
||||
@Inject
|
||||
public FloodgatePlatform(@Named("dataDirectory") Path dataDirectory, FloodgateApi api,
|
||||
ConfigLoader configLoader, PlayerLinkLoader playerLinkLoader,
|
||||
HandshakeHandler handshakeHandler, FloodgateLogger logger,
|
||||
Injector injector) {
|
||||
this.api = api;
|
||||
this.logger = logger;
|
||||
|
||||
if (!Files.isDirectory(dataDirectory)) {
|
||||
try {
|
||||
Files.createDirectory(dataDirectory);
|
||||
} catch (Exception exception) {
|
||||
logger.error("Failed to create the data folder", exception);
|
||||
throw new RuntimeException("Failed to create the data folder");
|
||||
}
|
||||
}
|
||||
|
||||
config = configLoader.load();
|
||||
|
||||
// make the config available for other classes
|
||||
guice = injector.createChildInjector(new ConfigLoadedModule(config, api));
|
||||
|
||||
guice.injectMembers(playerLinkLoader);
|
||||
guice.injectMembers(handshakeHandler);
|
||||
|
||||
PlayerLink link = playerLinkLoader.load();
|
||||
|
||||
InstanceHolder.setInstance(api, link, KEY);
|
||||
}
|
||||
|
||||
public boolean enable(Module... postCreateModules) {
|
||||
if (injector == null) {
|
||||
getLogger().error("Failed to find the platform injector!");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!injector.inject()) {
|
||||
getLogger().error("Failed to inject the packet listener!");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Failed to inject the packet listener!", exception);
|
||||
return false;
|
||||
}
|
||||
|
||||
guice.createChildInjector(new PostInitializeModule(postCreateModules));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean disable() {
|
||||
if (injector != null) {
|
||||
try {
|
||||
if (!injector.removeInjection()) {
|
||||
getLogger().error("Failed to remove the injection!");
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
getLogger().error("Failed to remove the injection!", exception);
|
||||
}
|
||||
}
|
||||
|
||||
api.getPlayerLink().stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isProxy() {
|
||||
return config.isProxy();
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.DeviceOS;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@Getter
|
||||
public class FloodgatePlayer {
|
||||
/**
|
||||
* Bedrock version of the client
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* Bedrock username (full version)
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* Bedrock username with the given identifier<br>
|
||||
* This won't be null if it is an {@link LinkedPlayer LinkedPlayer}, but it isn't used
|
||||
*/
|
||||
private String javaUsername;
|
||||
/**
|
||||
* The Unique Identifier used at the server to identify the bedrock client.<br>
|
||||
* Note that this field is only used when the player is not an {@link LinkedPlayer LinkedPlayer}
|
||||
*/
|
||||
@Setter
|
||||
private UUID javaUniqueId;
|
||||
/**
|
||||
* The Xbox Unique Identifier
|
||||
*/
|
||||
private String xuid;
|
||||
/**
|
||||
* The operation system of the bedrock client
|
||||
*/
|
||||
private DeviceOS deviceOS;
|
||||
/**
|
||||
* The language code of the bedrock client
|
||||
*/
|
||||
private String languageCode;
|
||||
/**
|
||||
* The InputMode of the bedrock client
|
||||
*/
|
||||
private int inputMode;
|
||||
/**
|
||||
* The LinkedPlayer object if the player is linked to Java account.
|
||||
*/
|
||||
private LinkedPlayer linkedPlayer;
|
||||
|
||||
/**
|
||||
* Returns true if the player is still logging in
|
||||
*/
|
||||
@Setter
|
||||
private boolean login = true;
|
||||
|
||||
FloodgatePlayer(BedrockData data, String prefix, boolean replaceSpaces) {
|
||||
xuid = data.getXuid();
|
||||
version = data.getVersion();
|
||||
username = data.getUsername();
|
||||
javaUsername = prefix + data.getUsername().substring(0, Math.min(data.getUsername().length(), 16 - prefix.length()));
|
||||
if (replaceSpaces) {
|
||||
javaUsername = javaUsername.replaceAll(" ", "_");
|
||||
}
|
||||
deviceOS = DeviceOS.getById(data.getDeviceId());
|
||||
languageCode = data.getLanguageCode();
|
||||
inputMode = data.getInputMode();
|
||||
javaUniqueId = AbstractFloodgateAPI.createJavaPlayerId(Long.parseLong(data.getXuid()));
|
||||
// every implementation (Bukkit, Bungee and Velocity) all run this async,
|
||||
// so we can block this thread
|
||||
if (PlayerLink.isEnabledAndAllowed()) {
|
||||
linkedPlayer = fetchLinkedPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getCorrectUniqueId() {
|
||||
return linkedPlayer != null ? linkedPlayer.javaUniqueId : javaUniqueId;
|
||||
}
|
||||
|
||||
public String getCorrectUsername() {
|
||||
return linkedPlayer != null ? linkedPlayer.javaUsername : javaUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return the LinkedPlayer object if the player is linked.<br>
|
||||
* Please note that the LinkedPlayer will be loaded (sync) when used for the first time.<br>
|
||||
* This method also checks if linking is enabled
|
||||
* @return LinkedPlayer or null if the player isn't linked or linking isn't enabled
|
||||
* @see #fetchLinkedPlayerAsync() for the async alternative
|
||||
*/
|
||||
public LinkedPlayer fetchLinkedPlayer() {
|
||||
if (!PlayerLink.isEnabledAndAllowed()) return null;
|
||||
try {
|
||||
return PlayerLink.getInstance().getLinkedPlayer(javaUniqueId).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async alternative to {@link #fetchLinkedPlayer()}.
|
||||
*
|
||||
* @see #fetchLinkedPlayer() for the sync version
|
||||
* @return a completable future of the fetched link player
|
||||
*/
|
||||
public CompletableFuture<LinkedPlayer> fetchLinkedPlayerAsync() {
|
||||
return PlayerLink.isEnabledAndAllowed() ?
|
||||
PlayerLink.getInstance().getLinkedPlayer(javaUniqueId) :
|
||||
CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.InstanceHolder;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.util.*;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@Getter
|
||||
public final class FloodgatePlayerImpl implements FloodgatePlayer {
|
||||
private final String version;
|
||||
private final String username;
|
||||
private final String javaUsername;
|
||||
//todo maybe add a map for platform specific things
|
||||
private final UUID javaUniqueId;
|
||||
private final String xuid;
|
||||
private final DeviceOs deviceOs;
|
||||
private final String languageCode;
|
||||
private final UiProfile uiProfile;
|
||||
private final InputMode inputMode;
|
||||
private final String ip;
|
||||
private final LinkedPlayer linkedPlayer;
|
||||
|
||||
/**
|
||||
* Returns true if the player is still logging in
|
||||
*/
|
||||
@Setter
|
||||
private boolean login = true;
|
||||
|
||||
FloodgatePlayerImpl(BedrockData data, String prefix, boolean replaceSpaces) {
|
||||
FloodgateApi api = FloodgateApi.getInstance();
|
||||
version = data.getVersion();
|
||||
username = data.getUsername();
|
||||
|
||||
int usernameLength = Math.min(data.getUsername().length(), 16 - prefix.length());
|
||||
String editedUsername = prefix + data.getUsername().substring(0, usernameLength);
|
||||
|
||||
if (replaceSpaces) {
|
||||
editedUsername = editedUsername.replaceAll(" ", "_");
|
||||
}
|
||||
javaUsername = editedUsername;
|
||||
javaUniqueId = api.createJavaPlayerId(Long.parseLong(data.getXuid()));
|
||||
|
||||
xuid = data.getXuid();
|
||||
deviceOs = DeviceOs.getById(data.getDeviceOs());
|
||||
languageCode = data.getLanguageCode();
|
||||
uiProfile = UiProfile.getById(data.getUiProfile());
|
||||
inputMode = InputMode.getById(data.getInputMode());
|
||||
ip = data.getIp();
|
||||
|
||||
// we'll use the LinkedPlayer provided by Bungee or Velocity (if they included one)
|
||||
if (data.hasPlayerLink()) {
|
||||
linkedPlayer = data.getLinkedPlayer();
|
||||
return;
|
||||
}
|
||||
|
||||
// every implementation (Bukkit, Bungee and Velocity) run this constructor async, so we
|
||||
// should be fine doing this synchronised.
|
||||
linkedPlayer = fetchLinkedPlayer(api.getPlayerLink());
|
||||
|
||||
if (linkedPlayer != null && api instanceof ProxyFloodgateApi) {
|
||||
// oh oh, now our encrypted data is incorrect. Updating...
|
||||
InstanceHolder.castApi(ProxyFloodgateApi.class)
|
||||
.updateEncryptedData(getCorrectUniqueId(), toBedrockData());
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getCorrectUniqueId() {
|
||||
return linkedPlayer != null ? linkedPlayer.getJavaUniqueId() : javaUniqueId;
|
||||
}
|
||||
|
||||
public String getCorrectUsername() {
|
||||
return linkedPlayer != null ? linkedPlayer.getJavaUsername() : javaUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and return the LinkedPlayer object associated to the player if the player is linked.
|
||||
* Please note that this method loads the LinkedPlayer synchronously.
|
||||
*
|
||||
* @return LinkedPlayer or null if the player isn't linked or linking isn't enabled
|
||||
* @see #fetchLinkedPlayerAsync(PlayerLink) for the asynchronously alternative
|
||||
*/
|
||||
public LinkedPlayer fetchLinkedPlayer(PlayerLink link) {
|
||||
if (!link.isEnabledAndAllowed()) return null;
|
||||
try {
|
||||
return link.getLinkedPlayer(javaUniqueId).get();
|
||||
} catch (InterruptedException | ExecutionException exception) {
|
||||
exception.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and return the LinkedPlayer object associated to the player if the player is linked.
|
||||
*
|
||||
* @return a future holding the LinkedPlayer or null if the player isn't linked or when
|
||||
* linking isn't enabled
|
||||
* @see #fetchLinkedPlayer(PlayerLink) for the sync version
|
||||
*/
|
||||
public CompletableFuture<LinkedPlayer> fetchLinkedPlayerAsync(PlayerLink link) {
|
||||
return link.isEnabledAndAllowed() ?
|
||||
link.getLinkedPlayer(javaUniqueId) :
|
||||
CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
public BedrockData toBedrockData() {
|
||||
return new BedrockData(version, username, xuid, deviceOs.ordinal(), languageCode,
|
||||
uiProfile.ordinal(), inputMode.ordinal(), ip, linkedPlayer);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.*;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.EncryptionUtil;
|
||||
|
||||
@@ -10,22 +40,27 @@ import javax.crypto.NoSuchPaddingException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.geysermc.floodgate.util.BedrockData.EXPECTED_LENGTH;
|
||||
import static org.geysermc.floodgate.util.BedrockData.FLOODGATE_IDENTIFIER;
|
||||
|
||||
public class HandshakeHandler {
|
||||
@RequiredArgsConstructor
|
||||
public final class HandshakeHandler {
|
||||
private final SimpleFloodgateApi api;
|
||||
private PrivateKey privateKey;
|
||||
private boolean bungee;
|
||||
private boolean proxy;
|
||||
|
||||
private String usernamePrefix;
|
||||
private boolean replaceSpaces;
|
||||
|
||||
public HandshakeHandler(@NonNull PrivateKey privateKey, boolean bungee, String usernamePrefix, boolean replaceSpaces) {
|
||||
this.privateKey = privateKey;
|
||||
this.bungee = bungee;
|
||||
this.usernamePrefix = usernamePrefix;
|
||||
this.replaceSpaces = replaceSpaces;
|
||||
@Inject
|
||||
public void init(FloodgateConfig config) {
|
||||
this.privateKey = config.getPrivateKey();
|
||||
checkNotNull(privateKey, "Floodgate key cannot be null");
|
||||
this.proxy = config.isProxy();
|
||||
this.usernamePrefix = config.getUsernamePrefix();
|
||||
this.replaceSpaces = config.isReplaceSpaces();
|
||||
}
|
||||
|
||||
public HandshakeResult handle(@NonNull String handshakeData) {
|
||||
@@ -33,7 +68,8 @@ public class HandshakeHandler {
|
||||
String[] data = handshakeData.split("\0");
|
||||
boolean isBungeeData = data.length == 6 || data.length == 7;
|
||||
|
||||
if (bungee && isBungeeData || !isBungeeData && data.length != 4 || !data[1].equals(FLOODGATE_IDENTIFIER)) {
|
||||
if (proxy && isBungeeData || !isBungeeData && data.length != 4
|
||||
|| !data[1].equals(FLOODGATE_IDENTIFIER)) {
|
||||
return ResultType.NOT_FLOODGATE_DATA.getCachedResult();
|
||||
}
|
||||
|
||||
@@ -45,30 +81,25 @@ public class HandshakeHandler {
|
||||
return ResultType.INVALID_DATA_LENGTH.getCachedResult();
|
||||
}
|
||||
|
||||
FloodgatePlayer player = new FloodgatePlayer(bedrockData, usernamePrefix, replaceSpaces);
|
||||
// javaUniqueId will always be (at this point) the xuid but converted into an uuid form
|
||||
AbstractFloodgateAPI.players.put(player.getJavaUniqueId(), player);
|
||||
|
||||
// Get the UUID from the bungee instance to fix linked account UUIDs being wrong
|
||||
if (isBungeeData) {
|
||||
String uuid = data[5].replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5");
|
||||
player.setJavaUniqueId(UUID.fromString(uuid));
|
||||
}
|
||||
FloodgatePlayer player =
|
||||
new FloodgatePlayerImpl(bedrockData, usernamePrefix, replaceSpaces);
|
||||
api.addPlayer(player.getJavaUniqueId(), player);
|
||||
|
||||
return new HandshakeResult(ResultType.SUCCESS, data, bedrockData, player);
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException |
|
||||
IllegalBlockSizeException | BadPaddingException exception) {
|
||||
exception.printStackTrace();
|
||||
return ResultType.EXCEPTION.getCachedResult();
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter @ToString
|
||||
@Getter
|
||||
public static class HandshakeResult {
|
||||
private ResultType resultType;
|
||||
private String[] handshakeData;
|
||||
private BedrockData bedrockData;
|
||||
private FloodgatePlayer floodgatePlayer;
|
||||
private final ResultType resultType;
|
||||
private final String[] handshakeData;
|
||||
private final BedrockData bedrockData;
|
||||
private final FloodgatePlayer floodgatePlayer;
|
||||
}
|
||||
|
||||
public enum ResultType {
|
||||
@@ -77,7 +108,8 @@ public class HandshakeHandler {
|
||||
INVALID_DATA_LENGTH,
|
||||
SUCCESS;
|
||||
|
||||
@Getter private HandshakeResult cachedResult;
|
||||
@Getter
|
||||
private final HandshakeResult cachedResult;
|
||||
|
||||
ResultType() {
|
||||
cachedResult = new HandshakeResult(this, null, null, null);
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public class LinkRequest {
|
||||
/**
|
||||
* The Java username of the linked player
|
||||
*/
|
||||
private String javaUsername;
|
||||
/**
|
||||
* The Java UUID of the linked player
|
||||
*/
|
||||
private UUID javaUniqueId;
|
||||
/**
|
||||
* The link code
|
||||
*/
|
||||
private String linkCode;
|
||||
/**
|
||||
* The username of player being linked
|
||||
*/
|
||||
private String bedrockUsername;
|
||||
/**
|
||||
* The time when the link was requested
|
||||
*/
|
||||
private long unixTime;
|
||||
|
||||
LinkRequest(String username, UUID uuid, String code, String beUsername) {
|
||||
javaUniqueId = uuid;
|
||||
javaUsername = username;
|
||||
linkCode = code;
|
||||
bedrockUsername = beUsername;
|
||||
unixTime = Instant.now().getEpochSecond();
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
long timePassed = Instant.now().getEpochSecond() - unixTime;
|
||||
return timePassed > PlayerLink.getVerifyLinkTimeout();
|
||||
}
|
||||
|
||||
public boolean checkGamerTag(FloodgatePlayer player) {
|
||||
// Accept the request whether the prefix was used or not
|
||||
return bedrockUsername.equals(player.getUsername()) || bedrockUsername.equals(player.getJavaUsername());
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
@Getter @Setter
|
||||
public class LinkedPlayer {
|
||||
/**
|
||||
* The Java username of the linked player
|
||||
*/
|
||||
public String javaUsername;
|
||||
/**
|
||||
* The Java UUID of the linked player
|
||||
*/
|
||||
public UUID javaUniqueId;
|
||||
/**
|
||||
* The UUID of the Bedrock player
|
||||
*/
|
||||
public UUID bedrockId;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.geysermc.floodgate;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.link.SQLitePlayerLink;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class PlayerLink {
|
||||
@Getter private static PlayerLink instance;
|
||||
@Getter private static boolean enabled;
|
||||
@Getter private static long verifyLinkTimeout;
|
||||
@Getter private static boolean allowLinking;
|
||||
|
||||
@Getter private final ExecutorService executorService = Executors.newFixedThreadPool(11);
|
||||
@Getter private Logger logger;
|
||||
|
||||
protected abstract void load(Path dataFolder);
|
||||
|
||||
public abstract CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId);
|
||||
|
||||
public abstract CompletableFuture<Boolean> isLinkedPlayer(UUID bedrockId);
|
||||
|
||||
public abstract CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID uuid, String username);
|
||||
|
||||
public abstract CompletableFuture<Void> unlinkPlayer(UUID uuid);
|
||||
|
||||
public static PlayerLink initialize(Logger logger, Path dataFolder, FloodgateConfig config) {
|
||||
if (PlayerLink.instance == null) {
|
||||
FloodgateConfig.PlayerLinkConfig linkConfig = config.getPlayerLink();
|
||||
ImplementationType type = ImplementationType.getByName(linkConfig.getType());
|
||||
if (type == null) {
|
||||
logger.severe("Failed to find an implementation for type: " + linkConfig.getType());
|
||||
return null;
|
||||
}
|
||||
PlayerLink.instance = type.instanceSupplier.get();
|
||||
PlayerLink.enabled = linkConfig.isEnabled();
|
||||
PlayerLink.verifyLinkTimeout = linkConfig.getLinkCodeTimeout();
|
||||
PlayerLink.allowLinking = linkConfig.isAllowLinking();
|
||||
instance.logger = logger;
|
||||
instance.load(dataFolder);
|
||||
return instance;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the thread pool and invalidates the PlayerLink instance
|
||||
*/
|
||||
public void stop() {
|
||||
instance = null;
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
protected LinkedPlayer createLinkedPlayer(String javaUsername, UUID javaUniqueId, UUID bedrockId) {
|
||||
return new LinkedPlayer(javaUsername, javaUniqueId, bedrockId);
|
||||
}
|
||||
|
||||
public static boolean isEnabledAndAllowed() {
|
||||
return enabled && allowLinking;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum ImplementationType {
|
||||
SQLITE(SQLitePlayerLink::new);
|
||||
|
||||
private Supplier<? extends PlayerLink> instanceSupplier;
|
||||
|
||||
public static final ImplementationType[] VALUES = values();
|
||||
|
||||
public static ImplementationType getByName(String implementationName) {
|
||||
String uppercase = implementationName.toUpperCase();
|
||||
for (ImplementationType type : VALUES) {
|
||||
if (type.name().equals(uppercase)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <U> CompletableFuture<U> failedFuture(Throwable exception) {
|
||||
CompletableFuture<U> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(exception);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.addon;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.netty.channel.Channel;
|
||||
import org.geysermc.floodgate.addon.addonmanager.AddonManagerHandler;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
|
||||
public final class AddonManagerAddon implements InjectorAddon {
|
||||
@Inject private CommonPlatformInjector injector;
|
||||
|
||||
@Override
|
||||
public void onInject(Channel channel, boolean proxyToServer) {
|
||||
channel.pipeline().addLast(
|
||||
"floodgate_addon", new AddonManagerHandler(injector, channel)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginDone(Channel channel) {
|
||||
onRemoveInject(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
channel.pipeline().remove("floodgate_addon");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldInject() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.addon;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import io.netty.channel.Channel;
|
||||
import org.geysermc.floodgate.addon.debug.ChannelInDebugHandler;
|
||||
import org.geysermc.floodgate.addon.debug.ChannelOutDebugHandler;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
|
||||
public final class DebugAddon implements InjectorAddon {
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Inject
|
||||
@Named("implementationName")
|
||||
private String implementationName;
|
||||
|
||||
@Inject
|
||||
@Named("packetEncoder")
|
||||
private String packetEncoder;
|
||||
|
||||
@Inject
|
||||
@Named("packetDecoder")
|
||||
private String packetDecoder;
|
||||
|
||||
@Override
|
||||
public void onInject(Channel channel, boolean proxyToServer) {
|
||||
//todo enable debug mode in our logger
|
||||
channel.pipeline().addBefore(
|
||||
packetEncoder, "floodgate_debug_out",
|
||||
new ChannelOutDebugHandler(implementationName, !proxyToServer, logger)
|
||||
).addBefore(
|
||||
packetDecoder, "floodgate_debug_in",
|
||||
new ChannelInDebugHandler(logger, implementationName, !proxyToServer)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginDone(Channel channel) {
|
||||
onRemoveInject(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
channel.pipeline().remove("floodgate_debug_out");
|
||||
channel.pipeline().remove("floodgate_debug_in");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldInject() {
|
||||
logger.info("Is debug? " + config.isDebug());
|
||||
return config.isDebug();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.addon.addonmanager;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class AddonManagerHandler extends MessageToByteEncoder<ByteBuf> {
|
||||
private final CommonPlatformInjector injector;
|
||||
private final Channel channel;
|
||||
|
||||
@Override
|
||||
public void handlerRemoved(ChannelHandlerContext ctx) {
|
||||
injector.removeAddonsCall(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
|
||||
int index = msg.readerIndex();
|
||||
// LoginSuccess packet = 2
|
||||
if (readVarInt(msg) == 2) {
|
||||
injector.loginSuccessCall(channel);
|
||||
}
|
||||
msg.readerIndex(index);
|
||||
out.writeBytes(msg);
|
||||
}
|
||||
|
||||
private int readVarInt(ByteBuf buffer) {
|
||||
int out = 0;
|
||||
int count = 0;
|
||||
byte current;
|
||||
do {
|
||||
current = buffer.readByte();
|
||||
out |= (current & 0x7F) << (count++ * 7);
|
||||
|
||||
if (count > 5) {
|
||||
throw new RuntimeException("VarInt is bigger then allowed");
|
||||
}
|
||||
} while ((current & 0x80) != 0);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.addon.debug;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
|
||||
public final class ChannelInDebugHandler extends SimpleChannelInboundHandler<ByteBuf> {
|
||||
private final String message;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
public ChannelInDebugHandler(FloodgateLogger logger, String implementationType,
|
||||
boolean player) {
|
||||
this.logger = logger;
|
||||
this.message = (player ? "Player ->" : "Server ->") + ' ' + implementationType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
|
||||
int index = msg.readerIndex();
|
||||
logger.info("{}:\n{}", message, ByteBufUtil.prettyHexDump(msg));
|
||||
msg.readerIndex(index);
|
||||
ctx.fireChannelRead(msg.retain());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.addon.debug;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
|
||||
public final class ChannelOutDebugHandler extends MessageToByteEncoder<ByteBuf> {
|
||||
private final String direction;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
public ChannelOutDebugHandler(String implementationType, boolean player,
|
||||
FloodgateLogger logger) {
|
||||
this.direction = implementationType + (player ? " -> Player" : " -> Server");
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
|
||||
int index = msg.readerIndex();
|
||||
logger.info("{}:\n{}", direction, ByteBufUtil.prettyHexDump(msg));
|
||||
|
||||
msg.readerIndex(index);
|
||||
out.writeBytes(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.EncryptionUtil;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ProxyFloodgateApi extends SimpleFloodgateApi {
|
||||
private final Map<UUID, String> encryptedData = new HashMap<>();
|
||||
private PrivateKey key;
|
||||
|
||||
@Inject
|
||||
public void init(@Named("floodgateKey") PrivateKey key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getEncryptedData(UUID uuid) {
|
||||
return encryptedData.get(uuid);
|
||||
}
|
||||
|
||||
public void addEncryptedData(UUID uuid, String encryptedData) {
|
||||
this.encryptedData.put(uuid, encryptedData); // just override already existing data I guess
|
||||
}
|
||||
|
||||
public void removeEncryptedData(UUID uuid) {
|
||||
encryptedData.remove(uuid);
|
||||
}
|
||||
|
||||
public void updateEncryptedData(UUID uuid, BedrockData bedrockData) {
|
||||
//todo move away from public/private key system
|
||||
try {
|
||||
String data = EncryptionUtil.encryptBedrockData(key, bedrockData);
|
||||
addEncryptedData(uuid, data);
|
||||
} catch (Exception exception) {
|
||||
// throw new IllegalStateException("We failed to update the BedrockData, " +
|
||||
// "but we can't continue without the updated version!", exception);
|
||||
System.out.println("We failed to update the BedrockData, " +
|
||||
"but we can't continue without the updated version!");
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.api;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.FloodgatePlayerImpl;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleFloodgateApi implements FloodgateApi {
|
||||
private final Map<UUID, FloodgatePlayer> players = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean isBedrockPlayer(UUID uuid) {
|
||||
return getPlayer(uuid) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloodgatePlayer getPlayer(UUID uuid) {
|
||||
FloodgatePlayer player = players.get(uuid);
|
||||
// bedrock players are always stored by their xuid,
|
||||
// so we return the instance if we know that the given uuid is a Floodgate uuid
|
||||
if (player != null || isFloodgateId(uuid)) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// make it possible to find player by Java id (linked players)
|
||||
for (FloodgatePlayer player1 : players.values()) {
|
||||
if (player1.getCorrectUniqueId().equals(uuid)) {
|
||||
return player1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID createJavaPlayerId(long xuid) {
|
||||
return new UUID(0, xuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFloodgateId(UUID uuid) {
|
||||
return uuid.getMostSignificantBits() == 0;
|
||||
}
|
||||
|
||||
public FloodgatePlayer addPlayer(UUID uuid, FloodgatePlayer player) {
|
||||
return players.put(uuid, player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a player (should only be used internally)
|
||||
*
|
||||
* @param onlineId The UUID of the online player
|
||||
* @param removeLogin true if it should remove a sessions who is still logging in
|
||||
* @return the FloodgatePlayer the player was logged in with
|
||||
*/
|
||||
@Nullable
|
||||
public FloodgatePlayer removePlayer(UUID onlineId, boolean removeLogin) {
|
||||
FloodgatePlayer player = players.get(onlineId);
|
||||
// the player is a non-linked player or a linked player but somehow someone tried to
|
||||
// remove the player by his xuid, we have to find out
|
||||
if (player != null) {
|
||||
// we don't allow them to remove a player by his xuid
|
||||
// because a linked player is never registered by his linked java uuid
|
||||
if (player.getLinkedPlayer() != null) return null;
|
||||
|
||||
// removeLogin logic
|
||||
if (!shouldRemove(player, removeLogin)) return null;
|
||||
|
||||
// passed the test
|
||||
players.remove(onlineId);
|
||||
// was the account linked?
|
||||
return player;
|
||||
}
|
||||
|
||||
// we still want to be able to remove a linked-player by his linked java uuid
|
||||
for (FloodgatePlayer player1 : players.values()) {
|
||||
if (!shouldRemove(player1, removeLogin)) continue;
|
||||
if (!player1.getCorrectUniqueId().equals(onlineId)) continue;
|
||||
players.remove(player1.getJavaUniqueId());
|
||||
return player1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean shouldRemove(FloodgatePlayer player, boolean removeLogin) {
|
||||
FloodgatePlayerImpl impl = player.as(FloodgatePlayerImpl.class);
|
||||
return impl.isLogin() && removeLogin || !impl.isLogin() && !removeLogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalant of {@link #removePlayer(UUID, boolean)} but with removeLogin = false.
|
||||
*/
|
||||
public FloodgatePlayer removePlayer(UUID onlineId) {
|
||||
return removePlayer(onlineId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of {@link #removePlayer(UUID, boolean)} except that it removes a
|
||||
* FloodgatePlayer instance directly.
|
||||
*/
|
||||
public boolean removePlayer(FloodgatePlayer player) {
|
||||
boolean removed = players.remove(player.getJavaUniqueId(), player);
|
||||
return removed && player.getLinkedPlayer() != null;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package org.geysermc.floodgate.command;
|
||||
|
||||
public interface CommandMessage {
|
||||
char COLOR_CHAR = '\u00A7';
|
||||
String getMessage();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.command;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.platform.command.CommandMessage;
|
||||
|
||||
/**
|
||||
* Messages (or part of messages) that are used in two or more commands and thus are 'commonly used'
|
||||
*/
|
||||
public enum CommonCommandMessage implements CommandMessage {
|
||||
NOT_A_PLAYER("Please head over to your Minecraft Account and link from there."),
|
||||
CHECK_CONSOLE("Please check the console for more info!"),
|
||||
IS_LINKED_ERROR("&cError while checking if the given player is linked. " + CHECK_CONSOLE);
|
||||
|
||||
@Getter private final String message;
|
||||
|
||||
CommonCommandMessage(String message) {
|
||||
this.message = message.replace('&', COLOR_CHAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getMessage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.command;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.link.LinkRequest;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.link.LinkRequestImpl;
|
||||
import org.geysermc.floodgate.platform.command.Command;
|
||||
import org.geysermc.floodgate.platform.command.CommandMessage;
|
||||
import org.geysermc.floodgate.platform.command.util.CommandUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
@NoArgsConstructor
|
||||
public final class LinkAccountCommand implements Command {
|
||||
private final Map<String, LinkRequest> activeLinkRequests = new HashMap<>();
|
||||
|
||||
@Inject private FloodgateApi api;
|
||||
@Inject private CommandUtil commandUtil;
|
||||
|
||||
@Override
|
||||
public void execute(Object player, UUID uuid, String username, String[] args) {
|
||||
PlayerLink link = api.getPlayerLink();
|
||||
if (!link.isEnabledAndAllowed()) {
|
||||
sendMessage(player, Message.LINK_REQUEST_DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
link.isLinkedPlayer(uuid).whenComplete((linked, throwable) -> {
|
||||
if (throwable != null) {
|
||||
sendMessage(player, CommonCommandMessage.IS_LINKED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (linked) {
|
||||
sendMessage(player, Message.ALREADY_LINKED);
|
||||
return;
|
||||
}
|
||||
|
||||
// when the player is a Java player
|
||||
if (!api.isBedrockPlayer(uuid)) {
|
||||
if (args.length != 1) {
|
||||
sendMessage(player, Message.JAVA_USAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
String code = String.format("%04d", new Random().nextInt(10000));
|
||||
String bedrockUsername = args[0];
|
||||
|
||||
LinkRequest linkRequest =
|
||||
new LinkRequestImpl(username, uuid, code, bedrockUsername);
|
||||
|
||||
activeLinkRequests.put(username, linkRequest);
|
||||
sendMessage(player, Message.LINK_REQUEST_CREATED, bedrockUsername, username, code);
|
||||
return;
|
||||
}
|
||||
|
||||
// when the player is a Bedrock player
|
||||
|
||||
if (args.length != 2) {
|
||||
sendMessage(player, Message.BEDROCK_USAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
String javaUsername = args[0];
|
||||
String code = args[1];
|
||||
LinkRequest request = activeLinkRequests.getOrDefault(javaUsername, null);
|
||||
if (request == null || !request.isRequestedPlayer(api.getPlayer(uuid))) {
|
||||
sendMessage(player, Message.NO_LINK_REQUESTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request.getLinkCode().equals(code)) {
|
||||
sendMessage(player, Message.INVALID_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the request, whether it has expired or is successful
|
||||
activeLinkRequests.remove(javaUsername);
|
||||
if (request.isExpired(link.getVerifyLinkTimeout())) {
|
||||
sendMessage(player, Message.LINK_REQUEST_EXPIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
link.linkPlayer(uuid, request.getJavaUniqueId(), request.getJavaUsername())
|
||||
.whenComplete((aVoid, error) -> {
|
||||
if (error != null) {
|
||||
sendMessage(player, Message.LINK_REQUEST_ERROR);
|
||||
return;
|
||||
}
|
||||
commandUtil.kickPlayer(
|
||||
player, Message.LINK_REQUEST_COMPLETED, request.getJavaUsername()
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "linkaccount";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "floodgate.linkaccount";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequirePlayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendMessage(Object player, CommandMessage message, Object... args) {
|
||||
commandUtil.sendMessage(player, message, args);
|
||||
}
|
||||
|
||||
public enum Message implements CommandMessage {
|
||||
ALREADY_LINKED("&cYour account is already linked!\n" +
|
||||
"&cIf you want to link to a different account, run &6/unlinkaccount&c and try it again."
|
||||
),
|
||||
JAVA_USAGE("&cUsage: /linkaccount <gamertag>"),
|
||||
LINK_REQUEST_CREATED("&aLog in as {} on Bedrock and run &6/linkaccount {} {}\n" +
|
||||
"&cWarning: Any progress on your Bedrock account will not be carried over! Save any items in your inventory first.\n" +
|
||||
"&cIf you change your mind you can run &6/unlinkaccount&c to get your progess back."
|
||||
),
|
||||
BEDROCK_USAGE("&cStart the process from Java! Usage: /linkaccount <gamertag>"),
|
||||
LINK_REQUEST_EXPIRED("&cThe code you entered is expired! Run &6/linkaccount&c again on your Java account"),
|
||||
LINK_REQUEST_COMPLETED("You are successfully linked to {}!\nIf you want to undo this run /unlinkaccount"),
|
||||
LINK_REQUEST_ERROR("&cAn error occurred while linking. " + CommonCommandMessage.CHECK_CONSOLE),
|
||||
INVALID_CODE("&cInvalid code! Please check your code or run the &6/linkaccount&c command again on your Java account."),
|
||||
NO_LINK_REQUESTED("&cThis player has not requested an account link! Please log in on Java and request one with &6/linkaccount"),
|
||||
LINK_REQUEST_DISABLED("&cLinking is not enabled on this server.");
|
||||
|
||||
@Getter private final String message;
|
||||
|
||||
Message(String message) {
|
||||
this.message = message.replace('&', COLOR_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.command;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.platform.command.Command;
|
||||
import org.geysermc.floodgate.platform.command.CommandMessage;
|
||||
import org.geysermc.floodgate.platform.command.util.CommandUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@NoArgsConstructor
|
||||
public final class UnlinkAccountCommand implements Command {
|
||||
@Inject private FloodgateApi api;
|
||||
@Inject private CommandUtil commandUtil;
|
||||
|
||||
@Override
|
||||
public void execute(Object player, UUID uuid, String username, String... args) {
|
||||
PlayerLink link = api.getPlayerLink();
|
||||
if (!link.isEnabledAndAllowed()) {
|
||||
sendMessage(player, Message.LINKING_NOT_ENABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
link.isLinkedPlayer(uuid).whenComplete((linked, throwable) -> {
|
||||
if (throwable != null) {
|
||||
sendMessage(player, CommonCommandMessage.IS_LINKED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!linked) {
|
||||
sendMessage(player, Message.NOT_LINKED);
|
||||
return;
|
||||
}
|
||||
|
||||
link.unlinkPlayer(uuid).whenComplete((aVoid, throwable1) ->
|
||||
sendMessage(player, throwable1 == null ?
|
||||
Message.UNLINK_SUCCESS :
|
||||
Message.UNLINK_ERROR
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "unlinkaccount";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "floodgate.unlinkaccount";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequirePlayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendMessage(Object player, CommandMessage message, Object... args) {
|
||||
commandUtil.sendMessage(player, message, args);
|
||||
}
|
||||
|
||||
public enum Message implements CommandMessage {
|
||||
NOT_LINKED("&cYour account isn't linked"),
|
||||
UNLINK_SUCCESS("&cUnlink successful! Rejoin to return to your Bedrock account"),
|
||||
UNLINK_ERROR("&cAn error occurred while unlinking player! " + CommonCommandMessage.CHECK_CONSOLE),
|
||||
LINKING_NOT_ENABLED("&cLinking is not enabled on this server");
|
||||
|
||||
@Getter private final String message;
|
||||
|
||||
Message(String message) {
|
||||
this.message = message.replace('&', COLOR_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
|
||||
/**
|
||||
* The global Floodgate configuration file used in every platform.
|
||||
* Some platforms have their own addition to the global configuration like
|
||||
* {@link ProxyFloodgateConfig} for the proxies.
|
||||
*/
|
||||
@Getter
|
||||
public class FloodgateConfig {
|
||||
@JsonProperty(value = "key-file-name")
|
||||
private String keyFileName;
|
||||
@JsonProperty(value = "username-prefix")
|
||||
private String usernamePrefix;
|
||||
@JsonProperty(value = "replace-spaces")
|
||||
private boolean replaceSpaces;
|
||||
|
||||
@JsonProperty(value = "disconnect")
|
||||
private DisconnectMessages messages;
|
||||
|
||||
@JsonProperty(value = "player-link")
|
||||
private PlayerLinkConfig playerLink;
|
||||
|
||||
@JsonProperty
|
||||
private boolean debug;
|
||||
|
||||
@JsonProperty("config-version")
|
||||
private boolean configVersion;
|
||||
|
||||
@JsonIgnore
|
||||
private PrivateKey privateKey = null;
|
||||
|
||||
@Getter
|
||||
public static class DisconnectMessages {
|
||||
@JsonProperty("invalid-key")
|
||||
private String invalidKey;
|
||||
@JsonProperty("invalid-arguments-length")
|
||||
private String invalidArgumentsLength;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class PlayerLinkConfig {
|
||||
@JsonProperty("enable")
|
||||
private boolean enabled;
|
||||
@JsonProperty("allow-linking")
|
||||
private boolean allowLinking;
|
||||
@JsonProperty("link-code-timeout")
|
||||
private long linkCodeTimeout;
|
||||
@JsonProperty("type")
|
||||
private String type;
|
||||
@JsonProperty("auto-download")
|
||||
private boolean autoDownload;
|
||||
}
|
||||
|
||||
public void setPrivateKey(PrivateKey privateKey) {
|
||||
if (this.privateKey == null) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isProxy() {
|
||||
return this instanceof ProxyFloodgateConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* The Floodgate configuration used by proxy platforms, currently Velocity and Bungeecord.
|
||||
*/
|
||||
public final class ProxyFloodgateConfig extends FloodgateConfig {
|
||||
@JsonProperty(value = "send-floodgate-data")
|
||||
@Getter private boolean sendFloodgateData;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.config.loader;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.config.updater.ConfigUpdater;
|
||||
import org.geysermc.floodgate.util.EncryptionUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ConfigLoader {
|
||||
private final Path dataFolder;
|
||||
private final Class<? extends FloodgateConfig> configClass;
|
||||
private final ConfigUpdater updater;
|
||||
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends FloodgateConfig> T load() {
|
||||
Path configPath = dataFolder.resolve("config.yml");
|
||||
|
||||
String defaultConfigName = "config.yml";
|
||||
boolean proxy = ProxyFloodgateConfig.class.isAssignableFrom(configClass);
|
||||
if (proxy) {
|
||||
defaultConfigName = "proxy-" + defaultConfigName;
|
||||
}
|
||||
|
||||
Path defaultConfigPath;
|
||||
try {
|
||||
defaultConfigPath = Paths.get("./" + defaultConfigName);
|
||||
} catch (RuntimeException exception) {
|
||||
logger.error("Failed to get the default config location", exception);
|
||||
throw new RuntimeException("Failed to get the default config location");
|
||||
}
|
||||
|
||||
boolean newConfig = !Files.exists(configPath);
|
||||
try {
|
||||
if (newConfig) {
|
||||
Files.copy(defaultConfigPath, configPath);
|
||||
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
|
||||
generator.initialize(2048);
|
||||
KeyPair pair = generator.generateKeyPair();
|
||||
|
||||
String test = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
String encrypted = EncryptionUtil.encrypt(pair.getPublic(), test);
|
||||
String decrypted = new String(EncryptionUtil.decrypt(pair.getPrivate(), encrypted));
|
||||
|
||||
if (!test.equals(decrypted)) {
|
||||
logger.error("Whoops, we tested the generated Floodgate keys but " +
|
||||
"the decrypted test message doesn't match the original.\n" +
|
||||
"Original message: " + test + "." +
|
||||
"Decrypted message: " + decrypted + ".\n" +
|
||||
"The encrypted message itself: " + encrypted
|
||||
);
|
||||
throw new RuntimeException(
|
||||
"Tested the generated public and private key but, " +
|
||||
"the decrypted message doesn't match the original!"
|
||||
);
|
||||
}
|
||||
|
||||
Files.write(dataFolder.resolve("public-key.pem"), pair.getPublic().getEncoded());
|
||||
Files.write(dataFolder.resolve("key.pem"), pair.getPrivate().getEncoded());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
logger.error("Error while creating config", exception);
|
||||
}
|
||||
|
||||
T configInstance;
|
||||
try {
|
||||
// check and update if the config is outdated
|
||||
if (!newConfig) {
|
||||
updater.update(defaultConfigPath);
|
||||
}
|
||||
|
||||
configInstance = (T) new ObjectMapper(new YAMLFactory())
|
||||
.readValue(Files.readAllBytes(configPath), configClass);
|
||||
} catch (ClassCastException exception) {
|
||||
logger.error("Provided class {} cannot be cast to the required return type",
|
||||
configClass.getName());
|
||||
|
||||
throw new RuntimeException("Failed to load cast the config! " +
|
||||
"Try to contact the platform developer");
|
||||
} catch (Exception exception) {
|
||||
logger.error("Error while loading config", exception);
|
||||
throw new RuntimeException("Failed to load the config! Try to delete the config file");
|
||||
}
|
||||
|
||||
try {
|
||||
PrivateKey key = EncryptionUtil.getKeyFromFile(
|
||||
dataFolder.resolve(configInstance.getKeyFileName()), PrivateKey.class);
|
||||
configInstance.setPrivateKey(key);
|
||||
} catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException exception) {
|
||||
logger.error("Error while reading private key", exception);
|
||||
throw new RuntimeException("Failed to read the private key!");
|
||||
}
|
||||
|
||||
return configInstance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.config.updater;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigFileUpdater {
|
||||
@Inject
|
||||
private FloodgateLogger logger;
|
||||
|
||||
/**
|
||||
* Simple config file updater.
|
||||
* Please note that all the keys should be unique and that this system wasn't made for complex
|
||||
* configurations.
|
||||
*
|
||||
* @param configLocation the location of the Floodgate config
|
||||
* @param currentVersion the key value map of the current config
|
||||
* @param renames name changes introduced in this version. new (key) to old (value)
|
||||
* @param defaultConfigLocation the location of the default Floodgate config
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void update(Path configLocation, Map<String, Object> currentVersion,
|
||||
Map<String, String> renames, Path defaultConfigLocation) throws IOException {
|
||||
List<String> notFound = new ArrayList<>();
|
||||
List<String> newConfig = Files.readAllLines(defaultConfigLocation);
|
||||
|
||||
String line;
|
||||
for (int i = 0; i < newConfig.size(); i++) {
|
||||
line = newConfig.get(i);
|
||||
// we don't have to check comments
|
||||
if (line.startsWith("#")) continue;
|
||||
|
||||
int splitIndex = line.indexOf(':');
|
||||
// if the line has a 'key: value' structure
|
||||
if (splitIndex != -1) {
|
||||
String nameUntrimmed = line.substring(0, splitIndex);
|
||||
String name = nameUntrimmed.trim();
|
||||
Object value;
|
||||
|
||||
logger.info(name);
|
||||
if (renames.containsKey(name)) {
|
||||
value = currentVersion.get(renames.get(name));
|
||||
} else {
|
||||
value = currentVersion.get(name);
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
notFound.add(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
String v = (String) value;
|
||||
if (!v.startsWith("\"") || !v.endsWith("\"")) {
|
||||
value = "\"" + value + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(nameUntrimmed + " has been changed to " + value);
|
||||
newConfig.set(i, nameUntrimmed + ": " + value);
|
||||
}
|
||||
}
|
||||
|
||||
Files.deleteIfExists(configLocation.getParent().resolve("config-old.yml"));
|
||||
Files.copy(configLocation, configLocation.getParent().resolve("config-old.yml"));
|
||||
Files.write(configLocation, newConfig);
|
||||
|
||||
logger.info("Successfully updated the config file! " +
|
||||
"Your old config has been moved to config-old.yml");
|
||||
|
||||
if (notFound.size() > 0) {
|
||||
StringBuilder messageBuilder = new StringBuilder(
|
||||
"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: "
|
||||
);
|
||||
|
||||
boolean first = true;
|
||||
for (String value : notFound) {
|
||||
if (!first) {
|
||||
messageBuilder.append(", ");
|
||||
}
|
||||
|
||||
messageBuilder.append(value);
|
||||
|
||||
String renamed = renames.get(value);
|
||||
if (renamed != null) {
|
||||
messageBuilder.append(" to ").append(renamed);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
logger.info(messageBuilder.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.config.updater;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ConfigUpdater {
|
||||
private final Path dataFolder;
|
||||
private final ConfigFileUpdater fileUpdater;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
public void update(Path defaultConfigLocation) {
|
||||
Path configLocation = dataFolder.resolve("config.yml");
|
||||
|
||||
BufferedReader configReader;
|
||||
try {
|
||||
configReader = Files.newBufferedReader(configLocation);
|
||||
} catch (IOException exception) {
|
||||
logger.error("Error while opening the config file", exception);
|
||||
throw new RuntimeException("Failed to update config");
|
||||
}
|
||||
|
||||
Map<String, Object> config = new Yaml().load(configReader);
|
||||
// currently unused, but can be used when a config name has been changed
|
||||
Map<String, String> renames = new HashMap<>(0);
|
||||
|
||||
Object versionElement = config.get("config-version");
|
||||
// not a pre-rewrite config
|
||||
if (versionElement != null) {
|
||||
checkArgument(
|
||||
versionElement instanceof Integer,
|
||||
"Config version should be an integer. Did someone mess with the config?"
|
||||
);
|
||||
|
||||
int version = (int) versionElement;
|
||||
checkArgument(
|
||||
version == 1,
|
||||
"Config is newer then possible on this version! Expected 1, got " + version
|
||||
);
|
||||
|
||||
// config is already up-to-date
|
||||
if (version == 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
fileUpdater.update(configLocation, config, renames, defaultConfigLocation);
|
||||
} catch (IOException exception) {
|
||||
logger.error("Error while updating the config file", exception);
|
||||
throw new RuntimeException("Failed to update config");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.inject;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class CommonPlatformInjector implements PlatformInjector {
|
||||
private final Set<Channel> injectedClients = new HashSet<>();
|
||||
private final Map<Class<?>, InjectorAddon> addons = new HashMap<>();
|
||||
|
||||
protected boolean addInjectedClient(Channel channel) {
|
||||
return injectedClients.add(channel);
|
||||
}
|
||||
|
||||
protected boolean removeInjectedClient(Channel channel) {
|
||||
return injectedClients.remove(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAddon(InjectorAddon addon) {
|
||||
return addons.putIfAbsent(addon.getClass(), addon) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends InjectorAddon> T removeAddon(Class<T> addon) {
|
||||
return (T) addons.remove(addon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to loop through all the addons and call
|
||||
* {@link InjectorAddon#onInject(Channel, boolean)} if
|
||||
* {@link InjectorAddon#shouldInject()}.
|
||||
*
|
||||
* @param channel the channel to inject
|
||||
* @param proxyToServer true if the proxy is connecting to a server or false when the player
|
||||
* is connecting to the proxy or false when the platform isn't a proxy
|
||||
*/
|
||||
public void injectAddonsCall(Channel channel, boolean proxyToServer) {
|
||||
for (InjectorAddon addon : addons.values()) {
|
||||
if (addon.shouldInject()) {
|
||||
addon.onInject(channel, proxyToServer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to loop through all the addons and call
|
||||
* {@link InjectorAddon#onLoginDone(Channel)} if
|
||||
* {@link InjectorAddon#shouldInject()}.
|
||||
*
|
||||
* @param channel the channel that was injected
|
||||
*/
|
||||
public void loginSuccessCall(Channel channel) {
|
||||
for (InjectorAddon addon : addons.values()) {
|
||||
if (addon.shouldInject()) {
|
||||
addon.onLoginDone(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to loop through all the addons and call
|
||||
* {@link InjectorAddon#onRemoveInject(Channel)} if
|
||||
* {@link InjectorAddon#shouldInject()}.
|
||||
*
|
||||
* @param channel the channel that was injected
|
||||
*/
|
||||
public void removeAddonsCall(Channel channel) {
|
||||
for (InjectorAddon addon : addons.values()) {
|
||||
if (addon.shouldInject()) {
|
||||
addon.onRemoveInject(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.link;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public abstract class CommonPlayerLink implements PlayerLink {
|
||||
@Getter private boolean enabled;
|
||||
@Getter private boolean allowLinking;
|
||||
@Getter private long verifyLinkTimeout;
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final ExecutorService executorService = Executors.newFixedThreadPool(11);
|
||||
|
||||
@Inject @Getter(AccessLevel.PROTECTED)
|
||||
private FloodgateLogger logger;
|
||||
|
||||
@Inject
|
||||
private void init(FloodgateConfig config) {
|
||||
FloodgateConfig.PlayerLinkConfig linkConfig = config.getPlayerLink();
|
||||
enabled = linkConfig.isEnabled();
|
||||
allowLinking = linkConfig.isAllowLinking();
|
||||
verifyLinkTimeout = linkConfig.getLinkCodeTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.link;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Simple class used when PlayerLinking is disabled.
|
||||
* This class has been made because Floodgate doesn't have a default PlayerLink implementation
|
||||
* anymore and {@link FloodgateApi#getPlayerLink()} returning null} is also not an option.
|
||||
*/
|
||||
final class DisabledPlayerLink implements PlayerLink {
|
||||
@Override
|
||||
public void load() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> isLinkedPlayer(UUID bedrockId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID javaId, String username) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> unlinkPlayer(UUID javaId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVerifyLinkTimeout() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowLinking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.link;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.link.LinkRequest;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public final class LinkRequestImpl implements LinkRequest {
|
||||
private final String javaUsername;
|
||||
private final UUID javaUniqueId;
|
||||
private final String linkCode;
|
||||
private final String bedrockUsername;
|
||||
private final long requestTime;
|
||||
|
||||
public LinkRequestImpl(String javaUsername, UUID javaUniqueId,
|
||||
String linkCode, String bedrockUsername) {
|
||||
this.javaUniqueId = javaUniqueId;
|
||||
this.javaUsername = javaUsername;
|
||||
this.linkCode = linkCode;
|
||||
this.bedrockUsername = bedrockUsername;
|
||||
requestTime = Instant.now().getEpochSecond();
|
||||
}
|
||||
|
||||
public boolean isExpired(long linkTimeout) {
|
||||
long timePassed = Instant.now().getEpochSecond() - requestTime;
|
||||
return timePassed > linkTimeout;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.link;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.name.Named;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class PlayerLinkLoader {
|
||||
@Named("dataDirectory")
|
||||
@Inject private Path dataDirectory;
|
||||
@Inject private Injector injector;
|
||||
@Inject private FloodgateConfig config;
|
||||
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
public PlayerLink load() {
|
||||
FloodgateConfig.PlayerLinkConfig linkConfig = config.getPlayerLink();
|
||||
if (!linkConfig.isEnabled()) {
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
|
||||
List<Path> files;
|
||||
try {
|
||||
files = Files.list(dataDirectory)
|
||||
.filter(path -> Files.isRegularFile(path) && path.toString().endsWith("jar"))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException exception) {
|
||||
logger.error("Failed to list possible database implementations", exception);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (files.size() == 0) {
|
||||
logger.error("Failed to find a database implementation");
|
||||
return null;
|
||||
}
|
||||
|
||||
Path implementationPath = files.get(0);
|
||||
|
||||
// We only want to load one database implementation
|
||||
String type = linkConfig.getType().toLowerCase();
|
||||
if (files.size() > 1) {
|
||||
implementationPath = null;
|
||||
for (Path path : files) {
|
||||
if (path.getFileName().toString().toLowerCase().contains(type)) {
|
||||
implementationPath = path;
|
||||
}
|
||||
}
|
||||
if (implementationPath == null) {
|
||||
logger.error("Failed to find an implementation for type: {}",
|
||||
linkConfig.getType());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Class<? extends PlayerLink> mainClass;
|
||||
try {
|
||||
URL pluginUrl = implementationPath.toUri().toURL();
|
||||
URLClassLoader classLoader = new URLClassLoader(
|
||||
new URL[]{pluginUrl},
|
||||
PlayerLinkLoader.class.getClassLoader()
|
||||
);
|
||||
|
||||
InputStream linkImplConfigStream = classLoader.getResourceAsStream("config.json");
|
||||
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);
|
||||
} catch (ClassCastException exception) {
|
||||
logger.error("The database implementation ({}) doesn't extend the PlayerLink class!",
|
||||
implementationPath.getFileName().toString());
|
||||
return null;
|
||||
} catch (Exception exception) {
|
||||
logger.error("Error while loading database jar", exception);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
PlayerLink instance = injector.getInstance(mainClass);
|
||||
instance.load();
|
||||
return instance;
|
||||
} catch (Exception exception) {
|
||||
logger.error("Error while initializing database jar", exception);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package org.geysermc.floodgate.link;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.LinkedPlayer;
|
||||
import org.geysermc.floodgate.PlayerLink;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SQLitePlayerLink extends PlayerLink {
|
||||
@Getter private Connection connection;
|
||||
|
||||
@Override
|
||||
public void load(Path dataFolder) {
|
||||
Path databasePath = dataFolder.resolve("linked-players.db");
|
||||
getLogger().info("Loading Floodgate linked player database...");
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
|
||||
// create a database connection
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath.toString());
|
||||
Statement statement = connection.createStatement();
|
||||
statement.setQueryTimeout(30); // set timeout to 30 sec.
|
||||
statement.executeUpdate("create table if not exists LinkedPlayers (bedrockId string, javaUniqueId string, javaUsername string)");
|
||||
} catch (ClassNotFoundException e) {
|
||||
getLogger().severe("The required class to load the SQLite database wasn't found");
|
||||
} catch (SQLException e) {
|
||||
getLogger().log(Level.SEVERE, "Error while loading database", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) {
|
||||
// TODO: make it work with Java player UUIDs
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("select * from LinkedPlayers where bedrockId = ?");
|
||||
query.setString(1, bedrockId.toString());
|
||||
ResultSet result = query.executeQuery();
|
||||
if (!result.next()) return null;
|
||||
|
||||
String javaUsername = result.getString("javaUsername");
|
||||
UUID javaUniqueId = UUID.fromString(result.getString("javaUniqueId"));
|
||||
return createLinkedPlayer(javaUsername, javaUniqueId, bedrockId);
|
||||
} catch (SQLException | NullPointerException e) {
|
||||
getLogger().log(Level.SEVERE, "Error while getting LinkedPlayer", e);
|
||||
throw new CompletionException("Error while getting LinkedPlayer", e);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> isLinkedPlayer(UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("select javaUniqueId from LinkedPlayers where bedrockId = ? or javaUniqueId = ?");
|
||||
query.setString(1, bedrockId.toString());
|
||||
query.setString(2, bedrockId.toString());
|
||||
ResultSet result = query.executeQuery();
|
||||
return result.next();
|
||||
} catch (SQLException | NullPointerException e) {
|
||||
getLogger().log(Level.SEVERE, "Error while checking if player is a LinkedPlayer", e);
|
||||
throw new CompletionException("Error while checking if player is a LinkedPlayer", e);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID uuid, String username) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("insert into LinkedPlayers values(?, ?, ?)");
|
||||
query.setString(1, bedrockId.toString());
|
||||
query.setString(2, uuid.toString());
|
||||
query.setString(3, username);
|
||||
query.executeUpdate();
|
||||
} catch (SQLException | NullPointerException e) {
|
||||
getLogger().log(Level.SEVERE, "Error while linking player", e);
|
||||
throw new CompletionException("Error while linking player", e);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> unlinkPlayer(UUID uuid) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("delete from LinkedPlayers where javaUniqueId = ? or bedrockId = ?");
|
||||
query.setString(1, uuid.toString());
|
||||
query.setString(2, uuid.toString());
|
||||
query.executeUpdate();
|
||||
} catch (SQLException | NullPointerException e) {
|
||||
getLogger().log(Level.SEVERE, "Error while unlinking player", e);
|
||||
throw new CompletionException("Error while unlinking player", e);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.logger;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.geysermc.floodgate.util.MessageFormatter.format;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public final class JavaDefaultFloodgateLogger implements FloodgateLogger {
|
||||
private Logger logger = Logger.getLogger(LOGGER_NAME);
|
||||
|
||||
@Override
|
||||
public void error(String message, Object... args) {
|
||||
logger.severe(format(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message, Throwable throwable, Object... args) {
|
||||
logger.log(Level.SEVERE, format(message, args), throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String message, Object... args) {
|
||||
logger.warning(format(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String message, Object... args) {
|
||||
logger.info(format(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message, Object... args) {
|
||||
logger.fine(format(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String message, Object... args) {
|
||||
logger.finer(format(message, args));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.module;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.multibindings.ProvidesIntoSet;
|
||||
import org.geysermc.floodgate.command.LinkAccountCommand;
|
||||
import org.geysermc.floodgate.command.UnlinkAccountCommand;
|
||||
import org.geysermc.floodgate.platform.command.Command;
|
||||
import org.geysermc.floodgate.register.CommandRegister;
|
||||
|
||||
public final class CommandModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(CommandRegister.class).asEagerSingleton();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public Command linkAccountCommand() {
|
||||
return new LinkAccountCommand();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public Command unlinkAccountCommand() {
|
||||
return new UnlinkAccountCommand();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.module;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import io.netty.util.AttributeKey;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.HandshakeHandler;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.loader.ConfigLoader;
|
||||
import org.geysermc.floodgate.config.updater.ConfigFileUpdater;
|
||||
import org.geysermc.floodgate.config.updater.ConfigUpdater;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
import org.geysermc.floodgate.link.PlayerLinkLoader;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class CommonModule extends AbstractModule {
|
||||
private final Path dataDirectory;
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(FloodgateApi.class).to(SimpleFloodgateApi.class);
|
||||
bind(PlatformInjector.class).to(CommonPlatformInjector.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("playerAttribute")
|
||||
public AttributeKey<FloodgatePlayer> playerAttribute() {
|
||||
return AttributeKey.newInstance("floodgate-player");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("dataDirectory")
|
||||
public Path dataDirectory() {
|
||||
return dataDirectory;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ConfigLoader configLoader(@Named("configClass") Class<? extends FloodgateConfig> configClass,
|
||||
ConfigUpdater configUpdater, FloodgateLogger logger) {
|
||||
return new ConfigLoader(dataDirectory, configClass, configUpdater, logger);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ConfigUpdater configUpdater(ConfigFileUpdater configFileUpdater,
|
||||
FloodgateLogger logger) {
|
||||
return new ConfigUpdater(dataDirectory, configFileUpdater, logger);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public PlayerLinkLoader playerLinkLoader() {
|
||||
return new PlayerLinkLoader();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public HandshakeHandler handshakeHandler(SimpleFloodgateApi api) {
|
||||
return new HandshakeHandler(api);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.module;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class ConfigLoadedModule extends AbstractModule {
|
||||
private final FloodgateConfig config;
|
||||
private final FloodgateApi api;
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
if (config instanceof ProxyFloodgateConfig) {
|
||||
bind(ProxyFloodgateConfig.class).toInstance((ProxyFloodgateConfig) config);
|
||||
// The ProxyFloodgateApi needs the floodgateKey
|
||||
requestInjection(api);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public FloodgateConfig floodgateConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("floodgateKey")
|
||||
public PrivateKey floodgateKey() {
|
||||
return config.getPrivateKey();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.module;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Module;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class PostInitializeModule extends AbstractModule {
|
||||
private final Module[] postCreateModules;
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
//todo move this to FloodgatePlatform itself
|
||||
for (Module module : postCreateModules) {
|
||||
install(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.platform.command;
|
||||
|
||||
import org.geysermc.floodgate.platform.command.util.CommandUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The base class for every Floodgate command.
|
||||
*/
|
||||
public interface Command {
|
||||
/**
|
||||
* Should be implemented when {@link #isRequirePlayer()} is true
|
||||
* or when the source is a player.
|
||||
*
|
||||
* @param player the player instance (used for example in combination with
|
||||
* {@link CommandUtil#kickPlayer(Object, CommandMessage, Object...)}
|
||||
* @param uuid the uuid of the player
|
||||
* @param username the username of the player
|
||||
* @param args the arguments of the command
|
||||
*/
|
||||
default void execute(Object player, UUID uuid, String username, String... args) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be implemented when {@link #isRequirePlayer()} is false.
|
||||
*
|
||||
* @param source the CommandSource (Velocity) or CommandExecutor (Bungee and Bukkit) that
|
||||
* executed this command
|
||||
* @param args the arguments of the command
|
||||
*/
|
||||
default void execute(Object source, String... args) {
|
||||
if (isRequirePlayer()) {
|
||||
throw new RuntimeException(
|
||||
"Cannot execute this command since it requires a player"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The command name that should be registered and used by the CommandSource.
|
||||
*
|
||||
* @return the name of the command that should be registered
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The permission that is required to execute the specific command.
|
||||
* Should return null when there is no permission required.
|
||||
*
|
||||
* @return the permission required to execute the command
|
||||
*/
|
||||
String getPermission();
|
||||
|
||||
/**
|
||||
* If the Command requires a Player to execute this command
|
||||
* or if it doesn't matter if (for example) the console executes the command.
|
||||
*
|
||||
* @return true if this command can only be executed by a player
|
||||
*/
|
||||
boolean isRequirePlayer();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.platform.command;
|
||||
|
||||
import org.geysermc.floodgate.platform.command.util.CommandResponseCache;
|
||||
import org.geysermc.floodgate.util.MessageFormatter;
|
||||
|
||||
/**
|
||||
* CommandMessage is the interface of a message that can be send to a command source after
|
||||
* executing a command. Those messages are generally implemented using enums, so that they are
|
||||
* only created once and can be used over and over again. This in combination with
|
||||
* {@link CommandResponseCache message caching} should make this system quite fast.
|
||||
*/
|
||||
public interface CommandMessage {
|
||||
char COLOR_CHAR = '\u00A7';
|
||||
|
||||
/**
|
||||
* Returns the message attached to the enum identifier
|
||||
*/
|
||||
String getMessage();
|
||||
|
||||
/**
|
||||
* This method will format a message by putting the arguments on the missing spots.
|
||||
*
|
||||
* @param args the arguments to fill in at the missing spots
|
||||
* @return the formatted message
|
||||
*/
|
||||
default String format(Object... args) {
|
||||
return MessageFormatter.format(getMessage(), args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.platform.command;
|
||||
|
||||
/**
|
||||
* This class is responsible for registering commands to the command register of the platform
|
||||
* that is currently in use. So that the commands only have to be written once
|
||||
* (in the common module) and can be used across all platforms without the need of adding platform
|
||||
* specific commands.
|
||||
*/
|
||||
public interface CommandRegistration {
|
||||
/**
|
||||
* This method will register the specified command.
|
||||
*
|
||||
* @param command the command to register
|
||||
*/
|
||||
void register(Command command);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.platform.command.util;
|
||||
|
||||
import org.geysermc.floodgate.platform.command.CommandMessage;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class used in all platforms to cache simple messages (messages without arguments) to improve
|
||||
* execution speed.
|
||||
*
|
||||
* @param <T> the message type (of the platform) that is send to the player.
|
||||
* For example BaseComponent[] on Bungeecord. Please note that not every platform has
|
||||
* something like that (for example Bukkit) and then a String should be used instead.
|
||||
*/
|
||||
public abstract class CommandResponseCache<T> {
|
||||
private final Map<CommandMessage, T> cachedResponses = new HashMap<>(0);
|
||||
|
||||
/**
|
||||
* Transforms a string (raw input) into a format that can be send to the player.
|
||||
*
|
||||
* @param message the message to transform
|
||||
* @return the transformed message
|
||||
*/
|
||||
protected abstract T transformMessage(String message);
|
||||
|
||||
/**
|
||||
* Get the cached message or (if it isn't cached) {@link #transformMessage(String) transform
|
||||
* the message} and add it to the cached messages.
|
||||
* Please note that the transformed message will only be added to the cached messages when the
|
||||
* transformed message has zero arguments, and thus can be cached.
|
||||
*
|
||||
* @param message the command message
|
||||
* @param args the arguments
|
||||
* @return the transformed (and maybe cached) message.
|
||||
*/
|
||||
public T getOrAddCachedMessage(CommandMessage message, Object... args) {
|
||||
if (args != null && args.length > 0) {
|
||||
return transformMessage(format(message, args));
|
||||
}
|
||||
if (!cachedResponses.containsKey(message)) {
|
||||
T components = transformMessage(message.getMessage());
|
||||
cachedResponses.put(message, components);
|
||||
return components;
|
||||
}
|
||||
return cachedResponses.get(message);
|
||||
}
|
||||
|
||||
protected String format(CommandMessage message, Object... args) {
|
||||
return args != null && args.length > 0 ?
|
||||
message.format(args) :
|
||||
message.getMessage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.platform.command.util;
|
||||
|
||||
import org.geysermc.floodgate.platform.command.CommandMessage;
|
||||
|
||||
/**
|
||||
* An interface used across all Floodgate platforms to simple stuff in commands like kicking
|
||||
* players and sending player messages independent of the Floodgate platform implementation.
|
||||
*/
|
||||
public interface CommandUtil {
|
||||
/**
|
||||
* Send a message to the specified player, no matter what platform Floodgate is running on.
|
||||
*
|
||||
* @param player the player to send the message to
|
||||
* @param message the command message
|
||||
* @param args the arguments
|
||||
*/
|
||||
void sendMessage(Object player, CommandMessage message, Object... args);
|
||||
|
||||
/**
|
||||
* Same as {@link CommandUtil#sendMessage(Object, CommandMessage, Object...)} except it kicks the player.
|
||||
*
|
||||
* @param player the player to send the message to
|
||||
* @param message the command message
|
||||
* @param args the arguments
|
||||
*/
|
||||
void kickPlayer(Object player, CommandMessage message, Object... args);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.platform.listener;
|
||||
|
||||
/**
|
||||
* This class is responsible for registering listeners to the listener manager of the platform
|
||||
* that is currently in use. Unfortunately due to the major differences between the platforms
|
||||
* (when it comes to listeners) every Floodgate platform has to implement their own listeners.
|
||||
*
|
||||
* @param <T> the platform-specific listener class
|
||||
*/
|
||||
public interface ListenerRegistration<T> {
|
||||
/**
|
||||
* This method will register the specified listener.
|
||||
*
|
||||
* @param listener the listener to register
|
||||
*/
|
||||
void register(T listener);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.register;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class AddonRegister {
|
||||
@Inject private Injector guice;
|
||||
@Inject private PlatformInjector injector;
|
||||
|
||||
@Inject
|
||||
public void registerAddons(Set<InjectorAddon> addons) {
|
||||
for (InjectorAddon addon : addons) {
|
||||
guice.injectMembers(addon);
|
||||
injector.addAddon(addon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.register;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.platform.command.Command;
|
||||
import org.geysermc.floodgate.platform.command.CommandRegistration;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Inject))
|
||||
public final class CommandRegister {
|
||||
private final CommandRegistration registration;
|
||||
private final Injector guice;
|
||||
|
||||
@Inject
|
||||
public void registerCommands(Set<Command> foundCommands) {
|
||||
for (Command command : foundCommands) {
|
||||
guice.injectMembers(command);
|
||||
registration.register(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.register;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Inject))
|
||||
public final class ListenerRegister<T> {
|
||||
private final ListenerRegistration<T> registration;
|
||||
|
||||
@Inject
|
||||
public void registerListeners(Set<T> foundListeners) {
|
||||
for (T listener : foundListeners) {
|
||||
registration.register(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import org.geysermc.floodgate.command.CommandMessage;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @param <T> Message Type stored. For example BaseComponent[] for Bungeecord
|
||||
*/
|
||||
public abstract class AbstractCommandResponseCache<T> {
|
||||
private final Map<CommandMessage, T> cachedResponses = new HashMap<>(0);
|
||||
|
||||
/**
|
||||
* Transforms a string (raw input) into a format that can be sent to the player
|
||||
* @param message he message to transform
|
||||
* @return the transformed message
|
||||
*/
|
||||
protected abstract T transformMessage(String message);
|
||||
|
||||
/**
|
||||
* If the message has no arguments:<br>
|
||||
* If cached: return cached message.<br>
|
||||
* If not cached: transform it, add the message to cache and return the message.<br>
|
||||
* It will only transform the message if the message has one or more arguments
|
||||
* @param message the command message
|
||||
* @param args the arguments
|
||||
* @return the message; caches if not already
|
||||
*/
|
||||
public T getOrAddCachedMessage(CommandMessage message, Object... args) {
|
||||
if (args != null && args.length > 0) {
|
||||
return transformMessage(format(message, args));
|
||||
}
|
||||
if (!cachedResponses.containsKey(message)) {
|
||||
T components = transformMessage(message.getMessage());
|
||||
cachedResponses.put(message, components);
|
||||
return components;
|
||||
}
|
||||
return cachedResponses.get(message);
|
||||
}
|
||||
|
||||
protected String format(CommandMessage message, Object... args) {
|
||||
String msg = message.getMessage();
|
||||
return args != null && args.length > 0 ? String.format(msg, args) : msg;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.command.CommandMessage;
|
||||
|
||||
public enum CommonMessage implements CommandMessage {
|
||||
NOT_A_PLAYER("Please head over to your Minecraft Account and link from there."),
|
||||
CHECK_CONSOLE("Please check the console for more info!"),
|
||||
IS_LINKED_ERROR("&cError while checking if the given player is linked. " + CHECK_CONSOLE);
|
||||
|
||||
@Getter private final String message;
|
||||
|
||||
CommonMessage(String message) {
|
||||
this.message = message.replace('&', COLOR_CHAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getMessage();
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import org.geysermc.floodgate.command.CommandMessage;
|
||||
|
||||
public interface ICommandUtil<P> {
|
||||
String LINK_ACCOUNT_COMMAND = "linkaccount";
|
||||
String UNLINK_ACCOUNT_COMMAND = "unlinkaccount";
|
||||
|
||||
/**
|
||||
* Send the specified player a message
|
||||
*
|
||||
* @param player the player to send the message to
|
||||
* @param message the command message
|
||||
* @param args the arguments
|
||||
*/
|
||||
void sendMessage(P player, CommandMessage message, Object... args);
|
||||
|
||||
/**
|
||||
* Same as {@link ICommandUtil#sendMessage(Object, CommandMessage, Object...)} except it kicks the player.
|
||||
*
|
||||
* @param player the player to send the message to
|
||||
* @param message the command message
|
||||
* @param args the arguments
|
||||
*/
|
||||
void kickPlayer(P player, CommandMessage message, Object... args);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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 MessageFormatter {
|
||||
private static final String DELIM_STR = "{}";
|
||||
private static final int DELIM_LENGTH = DELIM_STR.length();
|
||||
|
||||
public static String format(String message, Object... arguments) {
|
||||
// simple variant of slf4j's parameters.
|
||||
if (arguments == null || arguments.length == 0) return message;
|
||||
|
||||
String[] args = new String[arguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
args[i] = arguments[i].toString();
|
||||
}
|
||||
|
||||
int previousIndex = -1;
|
||||
int currentIndex;
|
||||
StringBuilder stringBuilder = new StringBuilder(
|
||||
message.length() + getArgsContentLength(args)
|
||||
);
|
||||
|
||||
for (String argument : args) {
|
||||
currentIndex = message.indexOf(DELIM_STR, previousIndex);
|
||||
if (currentIndex == -1) {
|
||||
// no parameter places left in message,
|
||||
// we'll ignore the remaining parameters and return the message
|
||||
if (previousIndex == -1) return message;
|
||||
else {
|
||||
stringBuilder.append(message.substring(previousIndex));
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (previousIndex == -1) stringBuilder.append(message, 0, currentIndex);
|
||||
else stringBuilder.append(message, previousIndex, currentIndex);
|
||||
stringBuilder.append(argument);
|
||||
|
||||
// we finished this argument, so we're past the current delimiter
|
||||
previousIndex = currentIndex + DELIM_LENGTH;
|
||||
}
|
||||
|
||||
if (previousIndex != message.length())
|
||||
stringBuilder.append(message, previousIndex, message.length());
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static int getArgsContentLength(String... args) {
|
||||
int length = 0;
|
||||
for (String arg : args)
|
||||
length += arg.length();
|
||||
return length;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
|
||||
public class ReflectionUtil {
|
||||
public final class ReflectionUtil {
|
||||
/**
|
||||
* Prefix without dot<br>
|
||||
* Example net.minecraft.server.v1_8R3.PacketHandhakingInSetProtocol will become:<br>
|
||||
* net.minecraft.server.v1_8R3
|
||||
*/
|
||||
@Getter @Setter
|
||||
private static String prefix = null;
|
||||
@Setter private static String prefix = null;
|
||||
|
||||
private static Field modifiersField = null;
|
||||
|
||||
public static Class<?> getPrefixedClass(String className) {
|
||||
return getClass(prefix +"."+ className);
|
||||
return getClass(prefix + "." + className);
|
||||
}
|
||||
|
||||
public static Class<?> getClass(String className) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException exception) {
|
||||
exception.printStackTrace();
|
||||
// class is not found, so we return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -30,15 +57,26 @@ public class ReflectionUtil {
|
||||
|
||||
public static Field getField(Class<?> clazz, String fieldName, boolean isPublic) {
|
||||
try {
|
||||
return isPublic ? clazz.getField(fieldName) : clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
if (isPublic) {
|
||||
return clazz.getField(fieldName);
|
||||
}
|
||||
return clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException exception) {
|
||||
// field is not found, so we return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Field getField(Class<?> clazz, String fieldName) {
|
||||
Field field = getField(clazz, fieldName, false);
|
||||
return field != null ? field : getField(clazz, fieldName, true);
|
||||
if (field != null) {
|
||||
return field;
|
||||
}
|
||||
return getField(clazz, fieldName, true);
|
||||
}
|
||||
|
||||
public static Field getFieldOfType(Class<?> clazz, Class<?> fieldType) {
|
||||
return getFieldOfType(clazz, fieldType, true);
|
||||
}
|
||||
|
||||
public static Field getFieldOfType(Class<?> clazz, Class<?> fieldType, boolean declared) {
|
||||
@@ -54,24 +92,33 @@ public class ReflectionUtil {
|
||||
makeAccessible(field);
|
||||
try {
|
||||
return field.get(instance);
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException | IllegalAccessException exception) {
|
||||
exception.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is equal to running:<br>
|
||||
* <code>{@link #getValue(Object, Field) getValue}(instance,
|
||||
* {@link #getField(Class, String) getField}(instance.getClass(), fieldName))</code>
|
||||
*/
|
||||
public static Object getValue(Object instance, String fieldName) {
|
||||
return getValue(instance, getField(instance.getClass(), fieldName));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getCastedValue(Object instance, Field field, Class<T> returnType) {
|
||||
public static <T> T getCastedValue(Object instance, Field field) {
|
||||
return (T) getValue(instance, field);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getCastedValue(Object instance, String fieldName, Class<T> returnType) {
|
||||
return (T) getValue(instance, getField(instance.getClass(), fieldName));
|
||||
/**
|
||||
* This method is equal to running:<br>
|
||||
* <code>{@link #getCastedValue(Object, Field) getCastedValue}(instance,
|
||||
* {@link #getField(Class, String) getField}(instance.getClass(), fieldName))</code>
|
||||
*/
|
||||
public static <T> T getCastedValue(Object instance, String fieldName) {
|
||||
return getCastedValue(instance, getField(instance.getClass(), fieldName));
|
||||
}
|
||||
|
||||
public static boolean setValue(Object instance, String fieldName, Object value) {
|
||||
@@ -86,8 +133,8 @@ public class ReflectionUtil {
|
||||
makeAccessible(field);
|
||||
try {
|
||||
field.set(instance, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,69 +142,110 @@ public class ReflectionUtil {
|
||||
try {
|
||||
makeAccessible(field);
|
||||
|
||||
Field modifiersField = null;
|
||||
int modifiers = field.getModifiers();
|
||||
if (Modifier.isFinal(modifiers)) {
|
||||
try {
|
||||
modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
} catch (NoSuchFieldException e) {
|
||||
// Java 12 compatibility, thanks to https://github.com/powermock/powermock/pull/1010
|
||||
Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
|
||||
makeAccessible(getDeclaredFields0);
|
||||
Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
|
||||
for (Field classField : fields) {
|
||||
if ("modifiers".equals(classField.getName())) {
|
||||
modifiersField = classField;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert modifiersField != null;
|
||||
Field modifiersField = getModifiersField();
|
||||
makeAccessible(modifiersField);
|
||||
modifiersField.setInt(field, modifiers & ~Modifier.FINAL);
|
||||
}
|
||||
setValue(instance, field, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Method getMethod(Class<?> clazz, String method, Class<?>... args) {
|
||||
public static Field getModifiersField(){
|
||||
if (modifiersField != null) return modifiersField;
|
||||
|
||||
try {
|
||||
return clazz.getMethod(method, args);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
} catch (NoSuchFieldException exception) {
|
||||
// Java 12 compatibility, thanks to https://github.com/powermock/powermock/pull/1010
|
||||
try {
|
||||
Method getDeclaredFields0 = Class.class.getDeclaredMethod(
|
||||
"getDeclaredFields0", boolean.class
|
||||
);
|
||||
makeAccessible(getDeclaredFields0);
|
||||
|
||||
Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
|
||||
for (Field field : fields) {
|
||||
if ("modifiers".equals(field.getName())) {
|
||||
modifiersField = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception exception1) {
|
||||
exception1.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return modifiersField;
|
||||
}
|
||||
|
||||
public static Method getMethod(Class<?> clazz, String method,
|
||||
boolean isPublic, Class<?>... args) {
|
||||
try {
|
||||
return isPublic ? clazz.getMethod(method, args) : clazz.getDeclaredMethod(method, args);
|
||||
} catch (NoSuchMethodException exception) {
|
||||
// method is not found, so we return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object invoke(Object instance, Method method) {
|
||||
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... args) {
|
||||
Method method = getMethod(clazz, methodName, false, args);
|
||||
if (method != null) {
|
||||
return method;
|
||||
}
|
||||
return getMethod(clazz, methodName, true, args);
|
||||
}
|
||||
|
||||
public static Method getMethod(Object instance, String method, Class<?>... args) {
|
||||
return getMethod(instance.getClass(), method, args);
|
||||
}
|
||||
|
||||
public static Object invoke(Object instance, Method method, Object... args) {
|
||||
makeAccessible(method);
|
||||
try {
|
||||
return method.invoke(instance);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return method.invoke(instance, args);
|
||||
} catch (IllegalAccessException | InvocationTargetException exception) {
|
||||
exception.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object invoke(Object instance, String method) {
|
||||
return invoke(instance, getMethod(instance.getClass(), method));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T invokeCasted(Object instance, Method method, Class<T> cast) {
|
||||
return (T) invoke(instance, method);
|
||||
public static <T> T castedInvoke(Object instance, Method method, Object... args) {
|
||||
try {
|
||||
return (T) invoke(instance, method, args);
|
||||
} catch (NullPointerException exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T castedInvoke(Object instance, String method) {
|
||||
return castedInvoke(instance, getMethod(instance.getClass(), method));
|
||||
}
|
||||
|
||||
public static Object invokeStatic(Class<?> clazz, String method) {
|
||||
try {
|
||||
return getMethod(clazz, method).invoke(null);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return invoke(null, getMethod(clazz, method));
|
||||
} catch (NullPointerException exception) {
|
||||
exception.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T extends AccessibleObject> T makeAccessible(T accessibleObject) {
|
||||
if (!accessibleObject.isAccessible()) accessibleObject.setAccessible(true);
|
||||
if (!accessibleObject.isAccessible()) {
|
||||
accessibleObject.setAccessible(true);
|
||||
}
|
||||
return accessibleObject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,16 +17,13 @@ disconnect:
|
||||
invalid-key: Please connect through the official Geyser
|
||||
# The disconnect message Geyser users should get when connecting
|
||||
# to the server with the correct key but not with the correct data format
|
||||
invalid-arguments-length: Expected {0} arguments, got {1}. Is Geyser up-to-date?
|
||||
invalid-arguments-length: Expected {} arguments, got {}. Is Geyser up-to-date?
|
||||
|
||||
# Configuration for player linking
|
||||
player-link:
|
||||
# Whether to enable the linking system. Turning this off will prevent
|
||||
# players from using the linking feature even if they are already linked.
|
||||
enable: false
|
||||
# The type of storage system you want to use
|
||||
# Currently implemented: SQLite
|
||||
type: sqlite
|
||||
# Whether to allow the use of /linkaccount and /unlinkaccount
|
||||
# You can also use allow specific people to use the commands using the
|
||||
# permissions floodgate.linkaccount and floodgate.unlinkaccount.
|
||||
@@ -34,3 +31,13 @@ player-link:
|
||||
allow-linking: true
|
||||
# The amount of time until a link code expires in seconds
|
||||
link-code-timeout: 300
|
||||
# The database type you want to use.
|
||||
# The option is only used when auto-download is enabled or when
|
||||
# there are more then one database implementations found in the config directory
|
||||
type: sqlite
|
||||
# Automatically download the database type you want to use
|
||||
# This doesn't include updating automatically (I don't expect the database implementations to be updated frequently)
|
||||
auto-download: true
|
||||
|
||||
# Do not change this
|
||||
config-version: 1
|
||||
|
||||
@@ -11,7 +11,8 @@ username-prefix: "*"
|
||||
# Should spaces be replaced with '_' in bedrock usernames?
|
||||
replace-spaces: true
|
||||
|
||||
# Should Bungeecord send the bedrock player data to the servers it is connecting to?
|
||||
# todo impl args like {proxy}
|
||||
# Should {proxy} send the bedrock player data to the servers it is connecting to?
|
||||
# This requires Floodgate to be installed on the servers.
|
||||
# You'll get kicked if you don't use the plugin. The default value is false because of it
|
||||
send-floodgate-data: false
|
||||
@@ -22,16 +23,13 @@ disconnect:
|
||||
invalid-key: Please connect through the official Geyser
|
||||
# The disconnect message Geyser users should get when connecting
|
||||
# to the server with the correct key but not with the correct data format
|
||||
invalid-arguments-length: Expected {0} arguments, got {1}. Is Geyser up-to-date?
|
||||
invalid-arguments-length: Expected {} arguments, got {}. Is Geyser up-to-date?
|
||||
|
||||
# Configuration for player linking
|
||||
player-link:
|
||||
# Whether to enable the linking system. Turning this off will prevent
|
||||
# players from using the linking feature even if they are already linked.
|
||||
enable: false
|
||||
# The type of storage system you want to use
|
||||
# Currently implemented: SQLite
|
||||
type: sqlite
|
||||
# Whether to allow the use of /linkaccount and /unlinkaccount
|
||||
# You can also use allow specific people to use the commands using the
|
||||
# permissions floodgate.linkaccount and floodgate.unlinkaccount.
|
||||
@@ -39,4 +37,14 @@ player-link:
|
||||
allow-linking: true
|
||||
# The amount of time until a link code expires in seconds
|
||||
link-code-timeout: 300
|
||||
|
||||
# The database type you want to use.
|
||||
# The option is only used when auto-download is enabled or when
|
||||
# there are more then one database implementations found in the config directory
|
||||
type: sqlite
|
||||
# Automatically download the database type you want to use
|
||||
# This doesn't include updating automatically
|
||||
# (because the database implementations won't be updated frequently)
|
||||
auto-download: true
|
||||
|
||||
# Do not change this
|
||||
config-version: 1
|
||||
52
database/pom.xml
Normal file
52
database/pom.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019-2020 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
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.geysermc.floodgate.database</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>database</name>
|
||||
|
||||
<modules>
|
||||
<module>sqlite</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<outputName>floodgate-${project.name}-database</outputName>
|
||||
</properties>
|
||||
</project>
|
||||
88
database/sqlite/pom.xml
Normal file
88
database/sqlite/pom.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2019-2020 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
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>org.geysermc.floodgate.database</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sqlite</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.30.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.12</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<finalName>${outputName}</finalName>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 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.database;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import org.geysermc.floodgate.link.CommonPlayerLink;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class SqliteDatabase extends CommonPlayerLink {
|
||||
private Connection connection;
|
||||
@Inject @Named("dataDirectory")
|
||||
private Path dataDirectory;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
Path databasePath = dataDirectory.resolve("linked-players.db");
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath.toString());
|
||||
Statement statement = connection.createStatement();
|
||||
statement.setQueryTimeout(30); // set timeout to 30 sec.
|
||||
statement.executeUpdate("create table if not exists LinkedPlayers (bedrockId string, javaUniqueId string, javaUsername string)");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
getLogger().error("The required class to load the SQLite database wasn't found");
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while loading database", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while closing database connection", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("select * from LinkedPlayers where bedrockId = ?");
|
||||
query.setString(1, bedrockId.toString());
|
||||
ResultSet result = query.executeQuery();
|
||||
if (!result.next()) return null;
|
||||
|
||||
String javaUsername = result.getString("javaUsername");
|
||||
UUID javaUniqueId = UUID.fromString(result.getString("javaUniqueId"));
|
||||
return new LinkedPlayer(javaUsername, javaUniqueId, bedrockId);
|
||||
} catch (SQLException | NullPointerException exception) {
|
||||
getLogger().error("Error while getting LinkedPlayer", exception);
|
||||
throw new CompletionException("Error while getting LinkedPlayer", exception);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Boolean> isLinkedPlayer(UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("select javaUniqueId from LinkedPlayers where bedrockId = ? or javaUniqueId = ?");
|
||||
query.setString(1, bedrockId.toString());
|
||||
query.setString(2, bedrockId.toString());
|
||||
ResultSet result = query.executeQuery();
|
||||
return result.next();
|
||||
} catch (SQLException | NullPointerException exception) {
|
||||
getLogger().error("Error while checking if player is a LinkedPlayer", exception);
|
||||
throw new CompletionException(
|
||||
"Error while checking if player is a LinkedPlayer", exception
|
||||
);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> linkPlayer(UUID bedrockId, UUID javaId, String username) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("insert into LinkedPlayers values(?, ?, ?)");
|
||||
query.setString(1, bedrockId.toString());
|
||||
query.setString(2, javaId.toString());
|
||||
query.setString(3, username);
|
||||
query.executeUpdate();
|
||||
} catch (SQLException | NullPointerException exception) {
|
||||
getLogger().error("Error while linking player", exception);
|
||||
throw new CompletionException("Error while linking player", exception);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> unlinkPlayer(UUID javaId) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
PreparedStatement query = connection.prepareStatement("delete from LinkedPlayers where javaUniqueId = ? or bedrockId = ?");
|
||||
query.setString(1, javaId.toString());
|
||||
query.setString(2, javaId.toString());
|
||||
query.executeUpdate();
|
||||
} catch (SQLException | NullPointerException exception) {
|
||||
getLogger().error("Error while unlinking player", exception);
|
||||
throw new CompletionException("Error while unlinking player", exception);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
}
|
||||
3
database/sqlite/src/main/resources/config.json
Normal file
3
database/sqlite/src/main/resources/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"mainClass": "org.geysermc.floodgate.database.SqliteDatabase"
|
||||
}
|
||||
14
pom.xml
14
pom.xml
@@ -4,19 +4,21 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>floodgate-parent</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<modules>
|
||||
<module>bungee</module>
|
||||
<module>api</module>
|
||||
<module>common</module>
|
||||
<module>bukkit</module>
|
||||
<module>spigot</module>
|
||||
<module>bungee</module>
|
||||
<module>velocity</module>
|
||||
<module>database</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
<name>floodgate</name>
|
||||
<description>Allows Bedrock players to join Java edition servers while keeping online mode</description>
|
||||
<url>https://github.com/GeyserMC/Floodgate/</url>
|
||||
<url>https://github.com/GeyserMC/Floodgate</url>
|
||||
|
||||
<properties>
|
||||
<geyser-version>1.0-SNAPSHOT</geyser-version>
|
||||
@@ -24,7 +26,7 @@
|
||||
<bungee-version>1.15-SNAPSHOT</bungee-version>
|
||||
<velocity-version>1.1.0-SNAPSHOT</velocity-version>
|
||||
|
||||
<outputName>${project.name}</outputName>
|
||||
<outputName>floodgate-${project.name}</outputName>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>floodgate-parent</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>floodgate-bukkit</artifactId>
|
||||
<artifactId>spigot</artifactId>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
@@ -38,10 +38,9 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>floodgate-common</artifactId>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user