mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
First commit
This commit is contained in:
221
.gitignore
vendored
Normal file
221
.gitignore
vendored
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
# Created by https://www.gitignore.io/api/git,java,maven,eclipse,netbeans,jetbrains+all
|
||||||
|
# Edit at https://www.gitignore.io/?templates=git,java,maven,eclipse,netbeans,jetbrains+all
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### Eclipse Patch ###
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated
|
||||||
|
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
### Git ###
|
||||||
|
# Created by git for backups. To disable backups in Git:
|
||||||
|
# $ git config --global mergetool.keepBackup false
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Created by git when using merge tools for conflicts
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
*.REMOTE.*
|
||||||
|
*_BACKUP_*.txt
|
||||||
|
*_BASE_*.txt
|
||||||
|
*_LOCAL_*.txt
|
||||||
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### JetBrains+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### JetBrains+all Patch ###
|
||||||
|
# Ignores the whole .idea folder and all .iml files
|
||||||
|
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
modules.xml
|
||||||
|
.idea/misc.xml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# Sonarlint plugin
|
||||||
|
.idea/sonarlint
|
||||||
|
|
||||||
|
### Maven ###
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
**/nbproject/private/
|
||||||
|
**/nbproject/Makefile-*.mk
|
||||||
|
**/nbproject/Package-*.bash
|
||||||
|
build/
|
||||||
|
nbbuild/
|
||||||
|
dist/
|
||||||
|
nbdist/
|
||||||
|
.nb-gradle/
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/git,java,maven,eclipse,netbeans,jetbrains+all
|
||||||
63
bungee/pom.xml
Normal file
63
bungee/pom.xml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?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>floodgate-parent</artifactId>
|
||||||
|
<groupId>org.geysermc</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>floodgate-bungee</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.md-5</groupId>
|
||||||
|
<artifactId>bungeecord-api</artifactId>
|
||||||
|
<version>1.14-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.md-5</groupId>
|
||||||
|
<artifactId>bungeecord-api</artifactId>
|
||||||
|
<version>1.14-SNAPSHOT</version>
|
||||||
|
<type>javadoc</type>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.md-5</groupId>
|
||||||
|
<artifactId>bungeecord-proxy</artifactId>
|
||||||
|
<version>1.12-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.geysermc</groupId>
|
||||||
|
<artifactId>floodgate-common</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<finalName>${outputName}</finalName>
|
||||||
|
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||||
|
<minimizeJar>true</minimizeJar>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.geysermc.floodgate;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
|
|
||||||
|
public class BungeeFloodgateAPI extends FloodgateAPI {
|
||||||
|
public static FloodgatePlayer getPlayerByConnection(PendingConnection connection) {
|
||||||
|
return getPlayer(connection.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package org.geysermc.floodgate;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
|
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||||
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.md_5.bungee.event.EventPriority;
|
||||||
|
import org.geysermc.floodgate.util.BedrockData;
|
||||||
|
import org.geysermc.floodgate.util.EncryptionUtil;
|
||||||
|
import org.geysermc.floodgate.util.ReflectionUtil;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public class BungeePlugin extends Plugin implements Listener {
|
||||||
|
@Getter private static BungeePlugin instance;
|
||||||
|
@Getter private FloodgateConfig config = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoad() {
|
||||||
|
instance = this;
|
||||||
|
if (!getDataFolder().exists()) {
|
||||||
|
getDataFolder().mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
config = FloodgateConfig.load(getLogger(), getDataFolder().toPath().resolve("config.yml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
getProxy().getPluginManager().registerListener(this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOW)
|
||||||
|
public void onPreLogin(PreLoginEvent event) {
|
||||||
|
// only need to check when server is in online mode :D
|
||||||
|
if (ProxyServer.getInstance().getConfig().isOnlineMode()) {
|
||||||
|
event.registerIntent(this);
|
||||||
|
|
||||||
|
getProxy().getScheduler().runAsync(this, () -> {
|
||||||
|
String[] data = ((InitialHandler) event.getConnection()).getExtraDataInHandshake().split("\0");
|
||||||
|
System.out.println(data.length);
|
||||||
|
|
||||||
|
if (data.length == 4 && data[1].equals("Geyser-Floodgate")) {
|
||||||
|
try {
|
||||||
|
BedrockData bedrockData = EncryptionUtil.decryptToInstance(
|
||||||
|
config.getPrivateKey(),
|
||||||
|
data[2] + '\0' + data[3]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bedrockData.getDataLength() != BedrockData.EXPECTED_LENGTH) {
|
||||||
|
event.setCancelReason(String.format(
|
||||||
|
config.getMessages().getInvalidArgumentsLength(),
|
||||||
|
BedrockData.EXPECTED_LENGTH, bedrockData.getDataLength()
|
||||||
|
));
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FloodgatePlayer player = new FloodgatePlayer(bedrockData);
|
||||||
|
FloodgateAPI.players.put(player.getJavaUniqueId(), player);
|
||||||
|
|
||||||
|
event.getConnection().setOnlineMode(false);
|
||||||
|
event.getConnection().setUniqueId(player.getJavaUniqueId());
|
||||||
|
ReflectionUtil.setValue(event.getConnection(), "name", player.getJavaUsername());
|
||||||
|
|
||||||
|
System.out.println("Added " + player.getUsername() + " " + player.getJavaUniqueId());
|
||||||
|
} catch (NullPointerException | NoSuchPaddingException | NoSuchAlgorithmException |
|
||||||
|
InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
|
||||||
|
event.setCancelReason(config.getMessages().getInvalidKey());
|
||||||
|
event.setCancelled(true);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event.completeIntent(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onDisconnect(PlayerDisconnectEvent event) {
|
||||||
|
FloodgatePlayer player = BungeeFloodgateAPI.getPlayerByConnection(event.getPlayer().getPendingConnection());
|
||||||
|
if (player != null) {
|
||||||
|
FloodgateAPI.players.remove(player.getJavaUniqueId());
|
||||||
|
System.out.println("Removed " + player.getUsername() + " " + event.getPlayer().getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
bungee/src/main/resources/plugin.yml
Normal file
4
bungee/src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
name: ${project.name}
|
||||||
|
version: ${project.version}
|
||||||
|
author: ${project.organization.name}
|
||||||
|
main: org.geysermc.flootgate.BungeePlugin
|
||||||
28
common/pom.xml
Normal file
28
common/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?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>floodgate-parent</artifactId>
|
||||||
|
<groupId>org.geysermc</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>floodgate-common</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.9.9.3</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||||
|
<version>2.9.9</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package org.geysermc.floodgate;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
class FloodgateAPI {
|
||||||
|
static final Map<UUID, FloodgatePlayer> players = new HashMap<>();
|
||||||
|
|
||||||
|
public static FloodgatePlayer getPlayer(UUID uuid) {
|
||||||
|
return players.get(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID createJavaPlayerId(long xuid) {
|
||||||
|
return new UUID(0, xuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DeviceOS {
|
||||||
|
@JsonEnumDefaultValue
|
||||||
|
UNKOWN,
|
||||||
|
ANDROID,
|
||||||
|
IOS,
|
||||||
|
OSX,
|
||||||
|
FIREOS,
|
||||||
|
GEARVR,
|
||||||
|
HOLOLENS,
|
||||||
|
WIN10,
|
||||||
|
WIN32,
|
||||||
|
DEDICATED,
|
||||||
|
ORBIS,
|
||||||
|
NX;
|
||||||
|
|
||||||
|
private static final DeviceOS[] VALUES = values();
|
||||||
|
|
||||||
|
public static DeviceOS getById(int id) {
|
||||||
|
return id < VALUES.length ? VALUES[id] : VALUES[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private PrivateKey privateKey = null;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class DisconnectMessages {
|
||||||
|
@JsonProperty("invalid-key")
|
||||||
|
private String invalidKey;
|
||||||
|
@JsonProperty("invalid-arguments-length")
|
||||||
|
private String invalidArgumentsLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FloodgateConfig load(Logger logger, Path path) {
|
||||||
|
FloodgateConfig config = null;
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
if (!path.toFile().exists()) {
|
||||||
|
Files.copy(FloodgateConfig.class.getClassLoader().getResourceAsStream("config.yml"), path);
|
||||||
|
|
||||||
|
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(path.getParent().resolve("encrypted.txt"), encrypted.getBytes());
|
||||||
|
|
||||||
|
Files.write(path.getParent().resolve("public-key.pem"), keyPair.getPublic().getEncoded());
|
||||||
|
Files.write(path.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(path), FloodgateConfig.class
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.SEVERE, "Error while loading config", e);
|
||||||
|
}
|
||||||
|
if (config == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
config.privateKey = EncryptionUtil.getKeyFromFile(
|
||||||
|
path.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,48 @@
|
|||||||
|
package org.geysermc.floodgate;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.geysermc.floodgate.util.BedrockData;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class FloodgatePlayer {
|
||||||
|
/**
|
||||||
|
* Bedrock version of the client
|
||||||
|
*/
|
||||||
|
private String version;
|
||||||
|
/**
|
||||||
|
* Bedrock username (full version)
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
/**
|
||||||
|
* Bedrock username with > identifier
|
||||||
|
*/
|
||||||
|
private String javaUsername;
|
||||||
|
/**
|
||||||
|
* The Xbox Unique Identifier
|
||||||
|
*/
|
||||||
|
private String xuid;
|
||||||
|
/**
|
||||||
|
* The operation system of the bedrock client
|
||||||
|
*/
|
||||||
|
private FloodgateAPI.DeviceOS deviceOS;
|
||||||
|
/**
|
||||||
|
* The language code of the bedrock client
|
||||||
|
*/
|
||||||
|
private String languageCode;
|
||||||
|
/**
|
||||||
|
* The Java UUID used to identify the bedrock client
|
||||||
|
*/
|
||||||
|
private UUID javaUniqueId;
|
||||||
|
|
||||||
|
FloodgatePlayer(BedrockData data) {
|
||||||
|
version = data.getVersion();
|
||||||
|
username = data.getUsername();
|
||||||
|
javaUsername = "*" + data.getUsername().substring(0, Math.min(data.getUsername().length(), 15));
|
||||||
|
xuid = data.getXuid();
|
||||||
|
deviceOS = FloodgateAPI.DeviceOS.getById(data.getDeviceId());
|
||||||
|
languageCode = data.getLanguageCode();
|
||||||
|
javaUniqueId = FloodgateAPI.createJavaPlayerId(Long.parseLong(data.getXuid()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class BedrockData {
|
||||||
|
public static final int EXPECTED_LENGTH = 6;
|
||||||
|
private String version;
|
||||||
|
private String username;
|
||||||
|
private String xuid;
|
||||||
|
private int deviceId;
|
||||||
|
private String languageCode;
|
||||||
|
private int inputMode;
|
||||||
|
private int dataLength;
|
||||||
|
|
||||||
|
public BedrockData(String version, String username, String xuid, int deviceId, String languageCode, int inputMode) {
|
||||||
|
this(version, username, xuid, deviceId, languageCode, inputMode, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BedrockData fromString(String data) {
|
||||||
|
String[] split = data.split("\0");
|
||||||
|
if (split.length != 5) return null;
|
||||||
|
return new BedrockData(split[0], split[1], split[2], Integer.parseInt(split[3]), split[4], split.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BedrockData fromRawData(byte[] data) {
|
||||||
|
return fromString(new String(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return version +"\0"+ username +"\0"+ xuid +"\0"+ deviceId +"\0"+ languageCode +"\0"+ inputMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
import javax.crypto.*;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.EncodedKeySpec;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class EncryptionUtil {
|
||||||
|
public static String encrypt(Key key, String data) throws IllegalBlockSizeException,
|
||||||
|
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
|
||||||
|
KeyGenerator generator = KeyGenerator.getInstance("AES");
|
||||||
|
generator.init(128);
|
||||||
|
SecretKey secretKey = generator.generateKey();
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance("AES");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||||
|
byte[] encryptedText = cipher.doFinal(data.getBytes());
|
||||||
|
|
||||||
|
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||||
|
cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key);
|
||||||
|
return Base64.getEncoder().encodeToString(cipher.doFinal(secretKey.getEncoded())) + '\0' +
|
||||||
|
Base64.getEncoder().encodeToString(encryptedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encryptFromInstance(Key key, BedrockData data) throws IllegalBlockSizeException,
|
||||||
|
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
|
||||||
|
return encrypt(key, data.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decrypt(Key key, String encryptedData) throws IllegalBlockSizeException,
|
||||||
|
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
|
||||||
|
String[] split = encryptedData.split("\0");
|
||||||
|
if (split.length != 2) {
|
||||||
|
throw new IllegalArgumentException("Expected two arguments, got " + split.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||||
|
cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key);
|
||||||
|
byte[] decryptedKey = cipher.doFinal(Base64.getDecoder().decode(split[0]));
|
||||||
|
|
||||||
|
SecretKey secretKey = new SecretKeySpec(decryptedKey, 0, decryptedKey.length, "AES");
|
||||||
|
cipher = Cipher.getInstance("AES");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||||
|
return cipher.doFinal(Base64.getDecoder().decode(split[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BedrockData decryptToInstance(Key key, String encryptedData) throws IllegalBlockSizeException,
|
||||||
|
InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
|
||||||
|
return BedrockData.fromRawData(decrypt(key, encryptedData));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends Key> T getKeyFromFile(Path fileLocation, Class<T> keyType) throws
|
||||||
|
IOException, InvalidKeySpecException, NoSuchAlgorithmException {
|
||||||
|
boolean isPublicKey = keyType == PublicKey.class;
|
||||||
|
if (!isPublicKey && keyType != PrivateKey.class) {
|
||||||
|
throw new RuntimeException("I can only read public and private keys!");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] key = Files.readAllBytes(fileLocation);
|
||||||
|
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
EncodedKeySpec keySpec = isPublicKey ? new X509EncodedKeySpec(key) : new PKCS8EncodedKeySpec(key);
|
||||||
|
return (T) (isPublicKey ?
|
||||||
|
keyFactory.generatePublic(keySpec) :
|
||||||
|
keyFactory.generatePrivate(keySpec)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Field getField(Class<?> clazz, String fieldName) {
|
||||||
|
Field field = getField(clazz, fieldName, false);
|
||||||
|
return field != null ? field : getField(clazz, fieldName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setValue(Object instance, String fieldName, Object value) {
|
||||||
|
Field field = getField(instance.getClass(), fieldName);
|
||||||
|
if (field != null) {
|
||||||
|
setValue(instance, field, value);
|
||||||
|
}
|
||||||
|
return field != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setValue(Object instance, Field field, Object value) {
|
||||||
|
if (!field.isAccessible()) field.setAccessible(true);
|
||||||
|
try {
|
||||||
|
field.set(instance, value);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
common/src/main/resources/config.yml
Normal file
4
common/src/main/resources/config.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
key-file-name: key.pem
|
||||||
|
disconnect:
|
||||||
|
invalid-key: Please connect through the official Geyser
|
||||||
|
invalid-arguments-length: Expected {0} arguments, got {1}. Is Geyser up-to-date?
|
||||||
96
pom.xml
Normal file
96
pom.xml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.geysermc</groupId>
|
||||||
|
<artifactId>floodgate-parent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<modules>
|
||||||
|
<module>bungee</module>
|
||||||
|
<module>common</module>
|
||||||
|
</modules>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>floodgate</name>
|
||||||
|
<description>Allows Bedrock players to join Java edition servers while keeping online mode</description>
|
||||||
|
<url>https://geysermc.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<outputName>${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>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<organization>
|
||||||
|
<name>GeyserMC</name>
|
||||||
|
<url>https://github.com/GeyserMC/Floodgate/blob/master/pom.xml</url>
|
||||||
|
</organization>
|
||||||
|
|
||||||
|
<scm>
|
||||||
|
<connection>scm:git:https://github.com/GeyserMC/Floodgate.git</connection>
|
||||||
|
<developerConnection>scm:git:git@github.com:GeyserMC/Floodgate.git</developerConnection>
|
||||||
|
<url>https://github.com/GeyserMC/Floodgate</url>
|
||||||
|
</scm>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>BungeeCord</id>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>releases</id>
|
||||||
|
<name>nukkitx-releases</name>
|
||||||
|
<url>https://repo.nukkitx.com/maven-releases</url>
|
||||||
|
</repository>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>snapshots</id>
|
||||||
|
<name>nukkitx-snapshots</name>
|
||||||
|
<url>https://repo.nukkitx.com/maven-snapshots</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<defaultGoal>clean install</defaultGoal>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources/</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.7.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<configuration>
|
||||||
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
|
<minimizeJar>true</minimizeJar>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
Reference in New Issue
Block a user