diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..e42123d
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,36 @@
+name: Build Pull Request
+
+on:
+ push:
+ paths-ignore:
+ - '.idea/copyright/*.xml'
+ - '.gitignore'
+ - 'LICENSE'
+ - 'README.md'
+ - 'bind9/**'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Set up JDK 17
+ uses: actions/setup-java@v1
+ with:
+ java-version: 17
+ distribution: temurin
+
+ - name: Checkout repository
+ uses: actions/checkout@v1
+
+ - name: Build Geyser
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: build
+
+ - name: Archive artifacts
+ uses: actions/upload-artifact@v1
+ if: success()
+ with:
+ name: Build
+ path: build/libs/GeyserConnect.jar
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index 6694a06..f17c350 100644
--- a/.github/workflows/pullrequest.yml
+++ b/.github/workflows/pullrequest.yml
@@ -1,29 +1,36 @@
name: Build Pull Request
-on: [pull_request]
+on:
+ pull_request:
+ paths-ignore:
+ - '.idea/copyright/*.xml'
+ - '.gitignore'
+ - 'LICENSE'
+ - 'README.md'
+ - 'bind9/**'
jobs:
build:
-
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
- - uses: actions/cache@v1
- with:
- path: ~/.m2/repository
- key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- restore-keys: |
- ${{ runner.os }}-maven-
- - name: Set up JDK 16
+ - name: Set up JDK 17
uses: actions/setup-java@v1
with:
- java-version: 16
- - name: Build with Maven
- run: mvn -B package
+ java-version: 17
+ distribution: temurin
+
+ - name: Checkout repository
+ uses: actions/checkout@v1
+
+ - name: Build Geyser
+ uses: gradle/gradle-build-action@v2
+ with:
+ arguments: build
+
- name: Archive artifacts
uses: actions/upload-artifact@v1
if: success()
with:
name: Build
- path: target/GeyserConnect.jar
+ path: build/libs/GeyserConnect.jar
diff --git a/.gitignore b/.gitignore
index 74191e5..f11ee9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
-# 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
+# Created by https://www.toptal.com/developers/gitignore/api/git,java,gradle,eclipse,netbeans,jetbrains+all
+# Edit at https://www.toptal.com/developers/gitignore?templates=git,java,gradle,eclipse,netbeans,jetbrains+all
### Eclipse ###
.metadata
@@ -53,22 +53,19 @@ local.properties
# Annotation Processing
.apt_generated/
+.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
+# Uncomment this line if you wish to ignore the project description file.
+# Typically, this file would be tracked if it contains build/dependency configurations:
+#.project
+
### Eclipse Patch ###
-# Eclipse Core
-.project
-
-# JDT-specific (Eclipse Java Development Tools)
-.classpath
-
-# Annotation Processing
-.apt_generated
-
+# Spring Boot Tooling
.sts4-cache/
### Git ###
@@ -110,9 +107,10 @@ local.properties
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+replay_pid*
### JetBrains+all ###
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
@@ -122,6 +120,9 @@ hs_err_pid*
.idea/**/dictionaries
.idea/**/shelf
+# AWS User-specific
+.idea/**/aws.xml
+
# Generated files
.idea/**/contentModel.xml
@@ -142,6 +143,9 @@ hs_err_pid*
# 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/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
@@ -169,6 +173,9 @@ atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
+# SonarLint plugin
+.idea/sonarlint/
+
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
@@ -182,32 +189,13 @@ fabric.properties
.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
+# Ignore everything but code style settings and run configurations
+# that are supposed to be shared within teams.
-#.idea/
+.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
+!.idea/codeStyles
+!.idea/runConfigurations
### NetBeans ###
**/nbproject/private/
@@ -219,15 +207,35 @@ dist/
nbdist/
.nb-gradle/
-# End of https://www.gitignore.io/api/git,java,maven,eclipse,netbeans,jetbrains+all
+### Gradle ###
+.gradle
+**/build/
+!src/**/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Avoid ignore Gradle wrappper properties
+!gradle-wrapper.properties
+
+# Cache of project
+.gradletasknamecache
+
+# Eclipse Gradle plugin generated files
+# Eclipse Core
+.project
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+### Gradle Patch ###
+# Java heap dump
+*.hprof
+
+# End of https://www.toptal.com/developers/gitignore/api/git,java,gradle,eclipse,netbeans,jetbrains+all
# Copyright header files
.idea/*
!.idea/copyright/
-
-/config.yml
-/welcome.txt
-/logs/
-/locales/
-/players/
-players.db
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
deleted file mode 100644
index cb2ba9d..0000000
--- a/Jenkinsfile
+++ /dev/null
@@ -1,77 +0,0 @@
-pipeline {
- agent any
-
- tools {
- maven 'Maven 3'
- jdk 'Java 16'
- }
-
- parameters{
- booleanParam(defaultValue: false, description: 'Skip Discord notification', name: 'SKIP_DISCORD')
- }
-
- options {
- buildDiscarder(logRotator(artifactNumToKeepStr: '20'))
- }
-
- stages {
- stage ('Build') {
- steps {
- sh 'git submodule update --init --recursive'
- rtMavenRun(
- pom: 'pom.xml',
- goals: 'clean package'
- )
- }
- post {
- success {
- archiveArtifacts artifacts: 'target/*.jar', excludes: 'target/geyser-connect-*.jar', fingerprint: true
- }
- }
- }
- }
-
- post {
- always {
- script {
- def changeLogSets = currentBuild.changeSets
- def message = "**Changes:**"
-
- if (changeLogSets.size() == 0) {
- message += "\n*No changes.*"
- } else {
- def repositoryUrl = scm.userRemoteConfigs[0].url.replace(".git", "")
- def count = 0;
- def extra = 0;
- for (int i = 0; i < changeLogSets.size(); i++) {
- def entries = changeLogSets[i].items
- for (int j = 0; j < entries.length; j++) {
- if (count <= 10) {
- def entry = entries[j]
- def commitId = entry.commitId.substring(0, 6)
- message += "\n - [`${commitId}`](${repositoryUrl}/commit/${entry.commitId}) ${entry.msg}"
- count++
- } else {
- extra++;
- }
- }
- }
-
- if (extra != 0) {
- message += "\n - ${extra} more commits"
- }
- }
-
- env.changes = message
- }
- deleteDir()
- script {
- if(!params.SKIP_DISCORD) {
- withCredentials([string(credentialsId: 'geyser-discord-webhook', variable: 'DISCORD_WEBHOOK')]) {
- discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.nukkitx.com/job/GeyserMC/job/GeyserConnect)", footer: 'Cloudburst Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK
- }
- }
- }
- }
- }
-}
diff --git a/README.md b/README.md
index 2437eb4..b83b520 100644
--- a/README.md
+++ b/README.md
@@ -3,17 +3,13 @@
[](https://java.com/)
[](LICENSE)
-[](https://ci.nukkitx.com/job/GeyserMC/job/GeyserConnect/job/master/)
+[](https://github.com/GeyserMC/GeyserConnect/actions/workflows/build.yml)
[](http://discord.geysermc.org/)
[](http://hits.dwyl.io/GeyserMC/GeyserConnect)
GeyserConnect is an easy way for Bedrock Edition clients to connect to any Java Edition servers without having to run anything.
## What is GeyserConnect?
-GeyserConnect is a server that Minecraft: Bedrock Edition clients can connect to that allows for a list of Minecraft: Java Edition servers to be displayed and accessed through 1 public Geyser instance. It is effectively a combination of [BedrockConnect](https://github.com/Pugmatt/BedrockConnect) and [Geyser](https://github.com/GeyserMC/Geyser).
+GeyserConnect is an extension for Geyser that allows for a list of Minecraft: Java Edition servers to be displayed and accessed through 1 public Geyser instance. It is effectively give the customisability of [BedrockConnect](https://github.com/Pugmatt/BedrockConnect) to [Geyser](https://github.com/GeyserMC/Geyser).
-**Please note, this project is still a work in progress and should not be used on production. Expect bugs!**
-
-If you wish to run this in docker and/or use DNS redirection please see the appropriate folders in this repo.
-#### Docker: [here](docker)
-#### DNS: [here](bind9)
+If you wish to use DNS redirection please see the [bind9](bind9) folder in this repository.
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..3a033bc
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,59 @@
+plugins {
+ id 'java-library'
+ id 'java'
+}
+
+group 'org.geysermc.connect.extension'
+version '1.0.0'
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ maven {
+ url 'https://repo.opencollab.dev/main'
+ }
+ maven {
+ url 'https://oss.sonatype.org/content/repositories/snapshots'
+ mavenContent {
+ snapshotsOnly()
+ }
+ }
+ maven {
+ url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
+ }
+ maven {
+ url 'https://jitpack.io'
+ }
+}
+
+dependencies {
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
+
+ compileOnly 'org.geysermc.geyser:api:2.1.0-SNAPSHOT'
+ compileOnly('org.geysermc.geyser:core:2.1.0-SNAPSHOT') {
+ exclude group: 'io.netty'
+ }
+
+ implementation 'org.xerial:sqlite-jdbc:3.41.2.1'
+ implementation 'com.mysql:mysql-connector-j:8.0.33'
+}
+
+jar {
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+
+ archiveFileName = "${project.name}.jar"
+
+ from {
+ configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
+ }
+}
+
+test {
+ useJUnitPlatform()
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
diff --git a/docker/Dockerfile b/docker/Dockerfile
deleted file mode 100644
index 2415c64..0000000
--- a/docker/Dockerfile
+++ /dev/null
@@ -1,5 +0,0 @@
-FROM openjdk:8-jre-slim
-RUN mkdir /gsc
-WORKDIR /gsc
-EXPOSE 19132/udp
-CMD ["java", "-Xms1G", "-jar", "GeyserConnect.jar"]
\ No newline at end of file
diff --git a/docker/README.md b/docker/README.md
deleted file mode 100644
index 7ef8aff..0000000
--- a/docker/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# GeyserConnect using Docker
-This contains the docker image and a basic way of running GeyserConnect
-
-## Setup
-1. Download GeyserConnect to a subfolder called `data`
-2. Build the docker file using `docker build -t geyser-connect .`
-3. Start geyser using this:
-```
-docker run --name "geyser-c" -d --restart always -p 19132:19132/udp -v $(pwd)/data:/gsc geyser-connect
-```
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..249e583
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ae04661
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..a69d9cb
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f127cfd
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,91 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 7549594..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
- 4.0.0
-
- org.geysermc
- geyser-connect
- 1.0-SNAPSHOT
-
- GeyserConnect
- Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers via a server list.
- https://geysermc.org
-
-
- GeyserConnect
- UTF-8
- UTF-8
- 16
- 16
-
-
-
- GeyserMC
- https://github.com/GeyserMC/GeyserConnect/blob/master/pom.xml
-
-
-
- scm:git:https://github.com/GeyserMC/GeyserConnect.git
- scm:git:git@github.com:GeyserMC/GeyserConnect.git
- https://github.com/GeyserMC/GeyserConnect
-
-
-
-
- jitpack.io
- https://jitpack.io
-
-
- opencollab-release-repo
- https://repo.opencollab.dev/maven-releases/
-
- true
-
-
- false
-
-
-
- opencollab-snapshot-repo
- https://repo.opencollab.dev/maven-snapshots/
-
- false
-
-
- true
-
-
-
- sonatype-s01
- https://s01.oss.sonatype.org/content/repositories/snapshots/
-
-
-
-
-
- org.projectlombok
- lombok
- 1.18.20
- provided
-
-
- org.geysermc.geyser
- core
- 2.1.0-SNAPSHOT
-
-
- net.minecrell
- terminalconsoleappender
- 1.1.1
-
-
- org.xerial
- sqlite-jdbc
- 3.31.1
-
-
- mysql
- mysql-connector-java
- 8.0.28
-
-
-
-
-
-
- src/main/resources
- true
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- org.geysermc.connect.GeyserConnect
-
-
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.2.1
-
-
- com.github.edwgiz
- maven-shade-plugin.log4j2-cachefile-transformer
- 2.8.1
-
-
-
-
- package
-
- shade
-
-
- ${outputName}
-
-
-
-
-
-
- *:*
-
- META-INF/versions/9/module-info.class
-
-
-
-
-
- org.geysermc.connect.GeyserConnect
-
- true
-
-
-
-
-
- ${project.build.directory}/dependency-reduced-pom.xml
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..4bedb10
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'GeyserConnect'
+
diff --git a/src/main/java/org/geysermc/connect/GeyserConnectConfig.java b/src/main/java/org/geysermc/connect/GeyserConnectConfig.java
deleted file mode 100644
index 43d4531..0000000
--- a/src/main/java/org/geysermc/connect/GeyserConnectConfig.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.Getter;
-import org.geysermc.connect.storage.AbstractStorageManager;
-import org.geysermc.connect.utils.Server;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-@Getter
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class GeyserConnectConfig {
-
- private String address;
-
- private int port;
-
- @JsonProperty("max-players")
- private int maxPlayers;
-
- private String motd;
-
- private String submotd = "GeyserConnect";
-
- @JsonProperty("welcome-file")
- private String welcomeFile = "welcome.txt";
-
- @JsonProperty("debug-mode")
- private boolean debugMode;
-
- private GeyserConfigSection geyser;
-
- private List servers = new ArrayList<>();
-
- @JsonProperty("custom-servers")
- private CustomServersSection customServers;
-
- private VirtualHostSection vhost;
-
- @Getter
- public static class GeyserConfigSection {
-
- @JsonProperty("allow-password-authentication")
- private boolean allowPasswordAuthentication = false;
-
- @JsonProperty("debug-mode")
- private boolean debugMode;
-
- @JsonProperty("saved-user-logins")
- private List savedUserLogins = Collections.emptyList();
- }
-
- @Getter
- public static class CustomServersSection {
-
- private boolean enabled;
- private int max;
-
- @JsonProperty("storage-type")
- private AbstractStorageManager.StorageType storageType;
-
- private MySQLConnectionSection mysql;
- }
-
- @Getter
- public static class MySQLConnectionSection {
-
- private String user;
- private String pass;
- private String database;
- private String host;
- private int port;
- }
-
- @Getter
- public static class VirtualHostSection {
-
- private boolean enabled;
- @JsonProperty("base-domain")
- private String baseDomain;
- }
-}
diff --git a/src/main/java/org/geysermc/connect/MasterServer.java b/src/main/java/org/geysermc/connect/MasterServer.java
deleted file mode 100644
index b40a013..0000000
--- a/src/main/java/org/geysermc/connect/MasterServer.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect;
-
-import com.nukkitx.protocol.bedrock.*;
-import io.netty.channel.DefaultEventLoopGroup;
-import io.netty.util.concurrent.DefaultThreadFactory;
-import it.unimi.dsi.fastutil.objects.ObjectArrayList;
-import lombok.Getter;
-import org.geysermc.connect.proxy.GeyserProxyBootstrap;
-import org.geysermc.connect.storage.AbstractStorageManager;
-import org.geysermc.connect.storage.DisabledStorageManager;
-import org.geysermc.connect.utils.*;
-import org.geysermc.geyser.GeyserImpl;
-import org.geysermc.geyser.network.GameProtocol;
-import org.geysermc.geyser.util.FileUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.*;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.stream.Collectors;
-
-public class MasterServer {
-
- private BedrockServer bdServer;
-
- @Getter
- private boolean shuttingDown = false;
-
- @Getter
- private static MasterServer instance;
-
- @Getter
- private final Logger logger;
-
- @Getter
- private final ScheduledExecutorService generalThreadPool;
-
- @Getter
- private final List players = new ObjectArrayList<>();
-
- @Getter
- private GeyserProxyBootstrap geyserProxy;
-
- @Getter
- private GeyserConnectConfig geyserConnectConfig;
-
- @Getter
- private AbstractStorageManager storageManager;
-
- @Getter
- private final DefaultEventLoopGroup eventLoopGroup = new DefaultEventLoopGroup(new DefaultThreadFactory("Geyser player thread"));
-
- public MasterServer() {
- instance = this;
-
- logger = new Logger();
-
- try {
- File configFile = GeyserConnectFileUtils.fileOrCopiedFromResource(new File("config.yml"), "config.yml", (x) -> x);
- this.geyserConnectConfig = FileUtils.loadConfig(configFile, GeyserConnectConfig.class);
- } catch (IOException ex) {
- logger.severe("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex);
- ex.printStackTrace();
- }
-
- logger.setDebug(geyserConnectConfig.isDebugMode());
-
- // As this is only used for fixing the form image bug, we don't need to handle many threads
- this.generalThreadPool = Executors.newSingleThreadScheduledExecutor();
-
- // Start a timer to keep the thread running
- Timer timer = new Timer();
- TimerTask task = new TimerTask() { public void run() { } };
- timer.scheduleAtFixedRate(task, 0L, 1000L);
-
- if (!geyserConnectConfig.getCustomServers().isEnabled()) {
- // Force the storage manager if we have it disabled
- storageManager = new DisabledStorageManager();
- logger.info("Disabled custom player servers");
- } else {
- try {
- storageManager = geyserConnectConfig.getCustomServers().getStorageType().getStorageManager().newInstance();
- } catch (Exception e) {
- logger.severe("Invalid storage manager class!", e);
- return;
- }
- }
-
- storageManager.setupStorage();
-
- // Create the base welcome.txt file
- try {
- GeyserConnectFileUtils.fileOrCopiedFromResource(new File(getGeyserConnectConfig().getWelcomeFile()), "welcome.txt", (x) -> x);
- } catch (IOException ignored) { }
-
- start(geyserConnectConfig.getPort());
-
- logger.start();
- }
-
- private void start(int port) {
- logger.info("Starting...");
-
- InetSocketAddress bindAddress = new InetSocketAddress(geyserConnectConfig.getAddress(), port);
- bdServer = new BedrockServer(bindAddress);
-
- bdServer.setHandler(new BedrockServerEventHandler() {
- @Override
- public boolean onConnectionRequest(InetSocketAddress address) {
- return true; // Connection will be accepted
- }
-
- @Override
- public BedrockPong onQuery(InetSocketAddress address) {
- int playerCount = players.size() + GeyserImpl.getInstance().getSessionManager().size();
-
- String subMotd = geyserConnectConfig.getSubmotd();
- if (subMotd == null || subMotd.isEmpty()) {
- subMotd = "GeyserConnect";
- }
-
- BedrockPong bdPong = new BedrockPong();
- bdPong.setEdition("MCPE");
- bdPong.setMotd(geyserConnectConfig.getMotd());
- bdPong.setSubMotd(subMotd);
- bdPong.setPlayerCount(playerCount);
- bdPong.setMaximumPlayerCount(geyserConnectConfig.getMaxPlayers());
- bdPong.setGameType("Survival");
- bdPong.setIpv4Port(port);
- bdPong.setProtocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion());
- bdPong.setVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion());
- return bdPong;
- }
-
- @Override
- public void onSessionCreation(BedrockServerSession session) {
- session.setPacketHandler(new PacketHandler(session, instance));
- }
- });
-
- // Start server up
- bdServer.bind().join();
-
- // Create the Geyser instance
- createGeyserProxy();
-
- logger.info("Server started on " + geyserConnectConfig.getAddress() + ":" + port);
- }
-
- public void shutdown() {
- shuttingDown = true;
- bdServer.close();
-
- shutdownGeyserProxy();
-
- generalThreadPool.shutdown();
- storageManager.closeStorage();
- System.exit(0);
- }
-
- public void createGeyserProxy() {
- if (geyserProxy == null) {
- // Make sure Geyser doesn't start the listener
- GeyserImpl.setShouldStartListener(false);
-
- this.geyserProxy = new GeyserProxyBootstrap();
- geyserProxy.onEnable();
- GeyserImpl.getInstance().getPendingMicrosoftAuthentication().setStoreServerInformation();
- }
- }
-
- public void shutdownGeyserProxy() {
- if (geyserProxy != null) {
- geyserProxy.onDisable();
- geyserProxy = null;
- }
- }
-
- public List getServers(ServerCategory serverCategory) {
- return getGeyserConnectConfig().getServers().stream().filter(server -> server.getCategory() == serverCategory).collect(Collectors.toList());
- }
-}
diff --git a/src/main/java/org/geysermc/connect/PacketHandler.java b/src/main/java/org/geysermc/connect/PacketHandler.java
deleted file mode 100644
index 9056c0f..0000000
--- a/src/main/java/org/geysermc/connect/PacketHandler.java
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectReader;
-import com.fasterxml.jackson.databind.node.JsonNodeType;
-import com.nimbusds.jose.JWSObject;
-import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory;
-import com.nimbusds.jose.shaded.json.JSONArray;
-import com.nukkitx.network.util.DisconnectReason;
-import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
-import com.nukkitx.protocol.bedrock.BedrockServerSession;
-import com.nukkitx.protocol.bedrock.data.AttributeData;
-import com.nukkitx.protocol.bedrock.data.ExperimentData;
-import com.nukkitx.protocol.bedrock.data.PacketCompressionAlgorithm;
-import com.nukkitx.protocol.bedrock.handler.BedrockPacketHandler;
-import com.nukkitx.protocol.bedrock.packet.*;
-import com.nukkitx.protocol.bedrock.util.EncryptionUtils;
-import com.nukkitx.protocol.bedrock.v471.Bedrock_v471;
-import org.checkerframework.checker.nullness.qual.NonNull;
-import org.geysermc.connect.proxy.GeyserProxySession;
-import org.geysermc.connect.ui.FormID;
-import org.geysermc.connect.ui.UIHandler;
-import org.geysermc.connect.utils.GeyserConnectFileUtils;
-import org.geysermc.connect.utils.Player;
-import org.geysermc.connect.utils.Server;
-import org.geysermc.cumulus.Form;
-import org.geysermc.cumulus.response.CustomFormResponse;
-import org.geysermc.cumulus.response.FormResponse;
-import org.geysermc.cumulus.response.SimpleFormResponse;
-import org.geysermc.geyser.GeyserImpl;
-import org.geysermc.geyser.api.network.AuthType;
-import org.geysermc.geyser.api.network.RemoteServer;
-import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
-import org.geysermc.geyser.network.GameProtocol;
-import org.geysermc.geyser.registry.Registries;
-import org.geysermc.geyser.session.PendingMicrosoftAuthentication.*;
-import org.geysermc.geyser.session.auth.AuthData;
-import org.geysermc.geyser.session.auth.BedrockClientData;
-import org.geysermc.geyser.util.FileUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.interfaces.ECPublicKey;
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-public class PacketHandler implements BedrockPacketHandler {
-
- private final BedrockServerSession session;
- private final MasterServer masterServer;
-
- private Player player;
-
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
-
- public PacketHandler(BedrockServerSession session, MasterServer masterServer) {
- this.session = session;
- this.masterServer = masterServer;
-
- session.addDisconnectHandler(this::disconnect);
- }
-
- public void disconnect(DisconnectReason reason) {
- if (player != null) {
- masterServer.getLogger().info(player.getAuthData().name() + " has disconnected from the master server (" + reason + ")");
- masterServer.getStorageManager().saveServers(player);
-
- masterServer.getPlayers().remove(player);
- }
- }
-
- private boolean checkedProtocol = false;
-
- @Override
- public boolean handle(RequestNetworkSettingsPacket packet) {
- if (checkProtocol(packet.getProtocolVersion())) {
- PacketCompressionAlgorithm algorithm = PacketCompressionAlgorithm.ZLIB;
-
- NetworkSettingsPacket responsePacket = new NetworkSettingsPacket();
- responsePacket.setCompressionAlgorithm(algorithm);
- responsePacket.setCompressionThreshold(512);
- session.sendPacketImmediately(responsePacket);
-
- session.setCompression(algorithm);
- }
- return true;
- }
-
- private boolean checkProtocol(int protocolVersion) {
- BedrockPacketCodec packetCodec = GameProtocol.getBedrockCodec(protocolVersion);
- if (packetCodec == null) {
- session.setPacketCodec(GameProtocol.DEFAULT_BEDROCK_CODEC);
-
- String message = "disconnectionScreen.internalError.cantConnect";
- PlayStatusPacket status = new PlayStatusPacket();
- if (protocolVersion > GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
- status.setStatus(PlayStatusPacket.Status.LOGIN_FAILED_SERVER_OLD);
- message = "disconnectionScreen.outdatedServer";
- } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) {
- status.setStatus(PlayStatusPacket.Status.LOGIN_FAILED_CLIENT_OLD);
- message = "disconnectionScreen.outdatedClient";
- }
- session.sendPacket(status);
- session.disconnect(message);
-
- return false;
- }
-
- // Set the session codec
- session.setPacketCodec(packetCodec);
- return true;
- }
-
- @Override
- public boolean handle(LoginPacket packet) {
- masterServer.getLogger().debug("Login: " + packet.toString());
-
- if (!checkedProtocol) {
- if (!checkProtocol(packet.getProtocolVersion())) {
- return false;
- }
- checkedProtocol = true;
- }
-
- // Read the raw chain data
- JsonNode rawChainData;
- try {
- rawChainData = OBJECT_MAPPER.readTree(packet.getChainData().toByteArray());
- } catch (IOException e) {
- throw new AssertionError("Unable to read chain data!");
- }
-
- // Get the parsed chain data
- JsonNode chainData = rawChainData.get("chain");
- if (chainData.getNodeType() != JsonNodeType.ARRAY) {
- throw new AssertionError("Invalid chain data!");
- }
-
- try {
- // Convert the chainData to a JSONArray
- ObjectReader reader = OBJECT_MAPPER.readerFor(new TypeReference>() { });
- JSONArray array = new JSONArray();
- array.addAll(reader.readValue(chainData));
-
- // Verify the chain data
- if (!EncryptionUtils.verifyChain(array)) {
- // Disconnect the client
- session.disconnect("disconnectionScreen.internalError.cantConnect");
- throw new AssertionError("Failed to login, due to invalid chain data!");
- }
-
- // Parse the signed jws object
- JWSObject jwsObject;
- jwsObject = JWSObject.parse(chainData.get(chainData.size() - 1).asText());
-
- // Read the JWS payload
- JsonNode payload = OBJECT_MAPPER.readTree(jwsObject.getPayload().toBytes());
-
- // Check the identityPublicKey is there
- if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) {
- throw new AssertionError("Missing identity public key!");
- }
-
- // Create an ECPublicKey from the identityPublicKey
- ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue());
-
- // Get the skin data to validate the JWS token
- JWSObject skinData = JWSObject.parse(packet.getSkinData().toString());
- if (skinData.verify(new DefaultJWSVerifierFactory().createJWSVerifier(skinData.getHeader(), identityPublicKey))) {
- // Make sure the client sent over the username, xuid and other info
- if (payload.get("extraData").getNodeType() != JsonNodeType.OBJECT) {
- throw new AssertionError("Missing client data");
- }
-
- // Fetch the client data
- JsonNode extraData = payload.get("extraData");
-
- AuthData authData = new AuthData(
- extraData.get("displayName").asText(),
- UUID.fromString(extraData.get("identity").asText()),
- extraData.get("XUID").asText()
- );
-
- // Create a new player and add it to the players list
- player = new Player(authData, session);
- masterServer.getPlayers().add(player);
-
- player.setChainData(chainData);
-
- // Store the full client data
- player.setClientData(OBJECT_MAPPER.convertValue(OBJECT_MAPPER.readTree(skinData.getPayload().toBytes()), BedrockClientData.class));
- player.getClientData().setOriginalString(packet.getSkinData().toString());
-
- // Tell the client we have logged in successfully
- PlayStatusPacket playStatusPacket = new PlayStatusPacket();
- playStatusPacket.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
- session.sendPacket(playStatusPacket);
-
- // Tell the client there are no resourcepacks
- ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
- session.sendPacket(resourcePacksInfo);
- } else {
- throw new AssertionError("Invalid identity public key!");
- }
- } catch (Exception e) {
- // Disconnect the client
- session.disconnect("disconnectionScreen.internalError.cantConnect");
- throw new AssertionError("Failed to login", e);
- }
-
- return false;
- }
-
- @Override
- public boolean handle(ResourcePackClientResponsePacket packet) {
- switch (packet.getStatus()) {
- case COMPLETED:
- masterServer.getLogger().info("Logged in " + player.getAuthData().name() + " (" + player.getAuthData().xuid() + ", " + player.getAuthData().uuid() + ")");
-
- ProxyAuthenticationTask task = (ProxyAuthenticationTask) GeyserImpl.getInstance()
- .getPendingMicrosoftAuthentication().getTask(player.getAuthData().xuid());
- if (task != null && task.getAuthentication().isDone()) {
- String address = task.getServer();
- int port = task.getPort();
- player.setCurrentServer(new Server(address, port, true, false));
- GeyserProxySession session = player.createGeyserSession(false);
- session.remoteServer(player.getCurrentServer());
-
- session.onMicrosoftLoginComplete(task);
- } else {
- player.sendStartGame();
- }
- break;
- case HAVE_ALL_PACKS:
- ResourcePackStackPacket stack = new ResourcePackStackPacket();
- stack.setExperimentsPreviouslyToggled(false);
- stack.setForcedToAccept(false);
- stack.setGameVersion("*");
-
- if (!Registries.ITEMS.forVersion(session.getPacketCodec().getProtocolVersion()).getComponentItemData().isEmpty()) {
- // Allow custom items to work
- stack.getExperiments().add(new ExperimentData("data_driven_items", true));
- }
-
- if (session.getPacketCodec().getProtocolVersion() <= Bedrock_v471.V471_CODEC.getProtocolVersion()) {
- // Allow extended world height in the overworld to work for pre-1.18 clients
- stack.getExperiments().add(new ExperimentData("caves_and_cliffs", true));
- }
-
- session.sendPacket(stack);
- break;
- default:
- session.disconnect("disconnectionScreen.resourcePack");
- break;
- }
-
- return true;
- }
-
- @Override
- public boolean handle(SetLocalPlayerAsInitializedPacket packet) {
- masterServer.getLogger().debug("Player initialized: " + player.getAuthData().name());
-
- if (player.getCurrentServer() != null) {
- // Player is already logged in via delayed Microsoft authentication
- return false;
- }
-
- // Handle the virtual host if specified
- GeyserConnectConfig.VirtualHostSection vhost = MasterServer.getInstance().getGeyserConnectConfig().getVhost();
- if (vhost.isEnabled()) {
- String domain = player.getClientData().getServerAddress().split(":")[0];
- if (!domain.equals(vhost.getBaseDomain()) && domain.endsWith("." + vhost.getBaseDomain())) {
- String address = "";
- int port = 25565;
- boolean online = true;
-
- // Parse the address used
- String[] domainParts = domain.replaceFirst("\\." + vhost.getBaseDomain() + "$", "").split("\\._");
- for (int i = 0; i < domainParts.length; i++) {
- String part = domainParts[i];
- if (i == 0) {
- address = part;
- } else if (part.startsWith("p")) {
- port = Integer.parseInt(part.substring(1));
- } else if (part.startsWith("o")) {
- online = false;
- }
- }
-
- // They didn't specify an address so disconnect them
- if (address.startsWith("_")) {
- session.disconnect("disconnectionScreen.invalidIP");
- return false;
- }
-
- // Log the virtual host usage
- masterServer.getLogger().info(player.getAuthData().name() + " is using virtualhost: " + address + ":" + port + (!online ? " (offline)" : ""));
-
- // Send the player to the wanted server
- player.sendToServer(new Server(address, port, online, false));
-
- return false;
- }
- }
-
- String message = "";
- try {
- File messageFile = GeyserConnectFileUtils.fileOrCopiedFromResource(new File(MasterServer.getInstance().getGeyserConnectConfig().getWelcomeFile()), "welcome.txt", (x) -> x);
- message = new String(FileUtils.readAllBytes(messageFile));
- } catch (IOException ignored) { }
-
- if (!message.trim().isEmpty()) {
- player.sendWindow(FormID.WELCOME, UIHandler.getMessageWindow(message));
- } else {
- player.sendWindow(FormID.MAIN, UIHandler.getMainMenu());
- }
-
- return false;
- }
-
- @Override
- public boolean handle(ModalFormResponsePacket packet) {
- // Make sure the form is valid
- FormID id = FormID.fromId(packet.getFormId());
- if (id != player.getCurrentWindowId())
- return false;
-
- // Fetch the form and parse the response
- Form window = player.getCurrentWindow();
- FormResponse response = window.parseResponse(packet.getFormData() == null ? null : packet.getFormData().trim());
-
- // Resend the form if they closed it
- if (!response.isCorrect() && !id.isHandlesNull()) {
- player.resendWindow();
- } else {
- // Send the response to the correct response function
- switch (id) {
- case WELCOME:
- player.sendWindow(FormID.MAIN, UIHandler.getMainMenu());
- break;
-
- case MAIN:
- UIHandler.handleMainMenuResponse(player, (SimpleFormResponse) response);
- break;
-
- case LIST_SERVERS:
- UIHandler.handleServerListResponse(player, (SimpleFormResponse) response);
- break;
-
- case DIRECT_CONNECT:
- UIHandler.handleDirectConnectResponse(player, (CustomFormResponse) response);
- break;
-
- case EDIT_SERVERS:
- UIHandler.handleEditServerListResponse(player, (SimpleFormResponse) response);
- break;
-
- case ADD_SERVER:
- UIHandler.handleAddServerResponse(player, (CustomFormResponse) response);
- break;
-
- case SERVER_OPTIONS:
- UIHandler.handleServerOptionsResponse(player, (SimpleFormResponse) response);
- break;
-
- case REMOVE_SERVER:
- UIHandler.handleServerRemoveResponse(player, (SimpleFormResponse) response);
- break;
-
- case EDIT_SERVER:
- UIHandler.handleEditServerResponse(player, (CustomFormResponse) response);
- break;
-
- default:
- player.resendWindow();
- break;
- }
- }
-
- return true;
- }
-
- @Override
- public boolean handle(NetworkStackLatencyPacket packet) {
- // This is to fix a bug in the client where it doesn't load form images
- UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
- updateAttributesPacket.setRuntimeEntityId(1);
- List attributes = Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0f));
- updateAttributesPacket.setAttributes(attributes);
-
- // Doesn't work 100% of the time but fixes it most of the time
- MasterServer.getInstance().getGeneralThreadPool().schedule(() -> session.sendPacket(updateAttributesPacket), 500, TimeUnit.MILLISECONDS);
-
- return false;
- }
-}
diff --git a/src/main/java/org/geysermc/connect/extension/GeyserConnect.java b/src/main/java/org/geysermc/connect/extension/GeyserConnect.java
new file mode 100644
index 0000000..585960f
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/GeyserConnect.java
@@ -0,0 +1,61 @@
+package org.geysermc.connect.extension;
+
+import org.cloudburstmc.protocol.bedrock.packet.BedrockPacketHandler;
+import org.geysermc.connect.extension.config.Config;
+import org.geysermc.connect.extension.config.ConfigLoader;
+import org.geysermc.connect.extension.storage.AbstractStorageManager;
+import org.geysermc.connect.extension.storage.DisabledStorageManager;
+import org.geysermc.event.subscribe.Subscribe;
+import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent;
+import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent;
+import org.geysermc.geyser.api.extension.Extension;
+import org.geysermc.geyser.session.GeyserSession;
+
+public class GeyserConnect implements Extension {
+ private static GeyserConnect instance;
+ private Config config;
+ private AbstractStorageManager storageManager;
+
+ public GeyserConnect() {
+ instance = this;
+ }
+
+ public static GeyserConnect instance() {
+ return instance;
+ }
+
+ public Config config() {
+ return config;
+ }
+
+ public AbstractStorageManager storageManager() {
+ return storageManager;
+ }
+
+ @Subscribe
+ public void onPostInitialize(GeyserPostInitializeEvent event) {
+ config = ConfigLoader.load(this, GeyserConnect.class, Config.class);
+
+ if (!config.customServers().enabled()) {
+ // Force the storage manager if we have it disabled
+ storageManager = new DisabledStorageManager();
+ this.logger().info("Disabled custom player servers");
+ } else {
+ try {
+ storageManager = config.customServers().storageType().storageManager().getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ this.logger().severe("Invalid storage manager class!", e);
+ return;
+ }
+ }
+
+ storageManager.setupStorage();
+ }
+
+ @Subscribe
+ public void onSessionInitialize(SessionInitializeEvent event) {
+ GeyserSession session = (GeyserSession) event.connection();
+ BedrockPacketHandler packetHandler = session.getUpstream().getSession().getPacketHandler();
+ session.getUpstream().getSession().setPacketHandler(new PacketHandler(this, session, packetHandler));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/geysermc/connect/extension/PacketHandler.java b/src/main/java/org/geysermc/connect/extension/PacketHandler.java
new file mode 100644
index 0000000..36ed72d
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/PacketHandler.java
@@ -0,0 +1,142 @@
+/*
+ * 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/GeyserConnect
+ */
+
+package org.geysermc.connect.extension;
+
+import org.cloudburstmc.protocol.bedrock.data.AttributeData;
+import org.cloudburstmc.protocol.bedrock.packet.BedrockPacketHandler;
+import org.cloudburstmc.protocol.bedrock.packet.NetworkStackLatencyPacket;
+import org.cloudburstmc.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
+import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
+import org.cloudburstmc.protocol.common.PacketSignal;
+import org.geysermc.connect.extension.ui.UIHandler;
+import org.geysermc.connect.extension.utils.ServerManager;
+import org.geysermc.connect.extension.utils.Utils;
+import org.geysermc.geyser.GeyserImpl;
+import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
+import org.geysermc.geyser.network.UpstreamPacketHandler;
+import org.geysermc.geyser.session.GeyserSession;
+import org.geysermc.geyser.util.DimensionUtils;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class PacketHandler extends UpstreamPacketHandler {
+
+ private final GeyserSession session;
+ private final GeyserConnect geyserConnect;
+ private final BedrockPacketHandler originalPacketHandler;
+
+ public PacketHandler(GeyserConnect geyserConnect, GeyserSession session, BedrockPacketHandler packetHandler) {
+ super(GeyserImpl.getInstance(), session);
+
+ this.session = session;
+ this.geyserConnect = geyserConnect;
+ this.originalPacketHandler = packetHandler;
+
+ // Spawn the player in the end (it just looks better)
+ session.setDimension(DimensionUtils.THE_END);
+ DimensionUtils.setBedrockDimension(session, DimensionUtils.THE_END);
+ }
+
+ @Override
+ public void onDisconnect(String reason) {
+ geyserConnect.logger().info(Utils.displayName(session) + " has disconnected (" + reason + ")");
+ ServerManager.unloadServers(session);
+ }
+
+ @Override
+ public PacketSignal handle(SetLocalPlayerAsInitializedPacket packet) {
+ if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) {
+ if (!session.getUpstream().isInitialized()) {
+ session.getUpstream().setInitialized(true);
+
+ // Load the players servers
+ ServerManager.loadServers(session);
+
+ geyserConnect.logger().debug("Player initialized: " + Utils.displayName(session));
+
+ UIHandler uiHandler = new UIHandler(session, packet, originalPacketHandler);
+ uiHandler.initialiseSession();
+ }
+ }
+
+ // Handle the virtual host if specified
+// GeyserConnectConfig.VirtualHostSection vhost = MasterServer.getInstance().getGeyserConnectConfig().getVhost();
+// if (vhost.isEnabled()) {
+// String domain = player.getClientData().getServerAddress().split(":")[0];
+// if (!domain.equals(vhost.getBaseDomain()) && domain.endsWith("." + vhost.getBaseDomain())) {
+// String address = "";
+// int port = 25565;
+// boolean online = true;
+//
+// // Parse the address used
+// String[] domainParts = domain.replaceFirst("\\." + vhost.getBaseDomain() + "$", "").split("\\._");
+// for (int i = 0; i < domainParts.length; i++) {
+// String part = domainParts[i];
+// if (i == 0) {
+// address = part;
+// } else if (part.startsWith("p")) {
+// port = Integer.parseInt(part.substring(1));
+// } else if (part.startsWith("o")) {
+// online = false;
+// }
+// }
+//
+// // They didn't specify an address so disconnect them
+// if (address.startsWith("_")) {
+// session.disconnect("disconnectionScreen.invalidIP");
+// return false;
+// }
+//
+// // Log the virtual host usage
+// masterServer.getLogger().info(player.getAuthData().name() + " is using virtualhost: " + address + ":" + port + (!online ? " (offline)" : ""));
+//
+// // Send the player to the wanted server
+// player.sendToServer(new Server(address, port, online, false));
+//
+// return false;
+// }
+// }
+
+ return PacketSignal.HANDLED;
+ }
+
+ @Override
+ public PacketSignal handle(NetworkStackLatencyPacket packet) {
+ // This is to fix a bug in the client where it doesn't load form images
+ UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
+ updateAttributesPacket.setRuntimeEntityId(1);
+ List attributes = Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0f));
+ updateAttributesPacket.setAttributes(attributes);
+
+ // Doesn't work 100% of the time but fixes it most of the time
+ GeyserImpl.getInstance().getScheduledThread().schedule(() -> session.sendUpstreamPacket(updateAttributesPacket), 500, TimeUnit.MILLISECONDS);
+
+ return super.handle(packet);
+ }
+}
+
diff --git a/src/main/java/org/geysermc/connect/GeyserConnect.java b/src/main/java/org/geysermc/connect/extension/config/Config.java
similarity index 76%
rename from src/main/java/org/geysermc/connect/GeyserConnect.java
rename to src/main/java/org/geysermc/connect/extension/config/Config.java
index 802d4ed..76463ff 100644
--- a/src/main/java/org/geysermc/connect/GeyserConnect.java
+++ b/src/main/java/org/geysermc/connect/extension/config/Config.java
@@ -23,11 +23,16 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect;
+package org.geysermc.connect.extension.config;
-public class GeyserConnect {
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.geysermc.connect.extension.utils.Server;
- public static void main(String[] args) {
- new MasterServer();
- }
-}
+import java.util.List;
+
+public record Config(
+ @JsonProperty("welcome-file") String welcomeFile,
+ List servers,
+ @JsonProperty("custom-servers") CustomServersSection customServers,
+ VirtualHostSection vhost) {
+}
\ No newline at end of file
diff --git a/src/main/java/org/geysermc/connect/extension/config/ConfigLoader.java b/src/main/java/org/geysermc/connect/extension/config/ConfigLoader.java
new file mode 100644
index 0000000..07127c4
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/config/ConfigLoader.java
@@ -0,0 +1,60 @@
+package org.geysermc.connect.extension.config;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import org.geysermc.geyser.api.extension.Extension;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.util.Collections;
+
+public class ConfigLoader {
+ public static T load(Extension extension, Class> extensionClass, Class configClass) {
+ File configFile = extension.dataFolder().resolve("config.yml").toFile();
+
+ // Ensure the data folder exists
+ if (!extension.dataFolder().toFile().exists()) {
+ if (!extension.dataFolder().toFile().mkdirs()) {
+ extension.logger().error("Failed to create data folder");
+ return null;
+ }
+ }
+
+ // Create the config file if it doesn't exist
+ if (!configFile.exists()) {
+ try (FileWriter writer = new FileWriter(configFile)) {
+ try (FileSystem fileSystem = FileSystems.newFileSystem(new File(extensionClass.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath(), Collections.emptyMap())) {
+ try (InputStream input = Files.newInputStream(fileSystem.getPath("config.yml"))) {
+ byte[] bytes = new byte[input.available()];
+
+ input.read(bytes);
+
+ writer.write(new String(bytes).toCharArray());
+
+ writer.flush();
+ }
+ }
+ } catch (IOException | URISyntaxException e) {
+ extension.logger().error("Failed to create config", e);
+ return null;
+ }
+ }
+
+ // Load the config file
+ try {
+ return new ObjectMapper(new YAMLFactory())
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ .readValue(configFile, configClass);
+ } catch (IOException e) {
+ extension.logger().error("Failed to load config", e);
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/geysermc/connect/extension/config/CustomServersSection.java b/src/main/java/org/geysermc/connect/extension/config/CustomServersSection.java
new file mode 100644
index 0000000..fd8cda6
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/config/CustomServersSection.java
@@ -0,0 +1,14 @@
+package org.geysermc.connect.extension.config;
+
+//import com.fasterxml.jackson.annotation.JsonProperty;
+//import org.geysermc.connect.extension.storage.AbstractStorageManager;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.geysermc.connect.extension.storage.AbstractStorageManager;
+
+public record CustomServersSection(
+ boolean enabled,
+ int max,
+ @JsonProperty("storage-type") AbstractStorageManager.StorageType storageType,
+ MySQLConnectionSection mysql) {
+}
diff --git a/src/main/java/org/geysermc/connect/extension/config/MySQLConnectionSection.java b/src/main/java/org/geysermc/connect/extension/config/MySQLConnectionSection.java
new file mode 100644
index 0000000..3e9aaed
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/config/MySQLConnectionSection.java
@@ -0,0 +1,9 @@
+package org.geysermc.connect.extension.config;
+
+public record MySQLConnectionSection(
+ String user,
+ String pass,
+ String database,
+ String host,
+ int port) {
+}
diff --git a/src/main/java/org/geysermc/connect/extension/config/VirtualHostSection.java b/src/main/java/org/geysermc/connect/extension/config/VirtualHostSection.java
new file mode 100644
index 0000000..3820144
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/config/VirtualHostSection.java
@@ -0,0 +1,8 @@
+package org.geysermc.connect.extension.config;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public record VirtualHostSection(
+ boolean enabled,
+ @JsonProperty("base-domain") String baseDomain) {
+}
diff --git a/src/main/java/org/geysermc/connect/storage/AbstractSQLStorageManager.java b/src/main/java/org/geysermc/connect/extension/storage/AbstractSQLStorageManager.java
similarity index 56%
rename from src/main/java/org/geysermc/connect/storage/AbstractSQLStorageManager.java
rename to src/main/java/org/geysermc/connect/extension/storage/AbstractSQLStorageManager.java
index 62996ac..9fced8b 100644
--- a/src/main/java/org/geysermc/connect/storage/AbstractSQLStorageManager.java
+++ b/src/main/java/org/geysermc/connect/extension/storage/AbstractSQLStorageManager.java
@@ -23,22 +23,26 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.storage;
+package org.geysermc.connect.extension.storage;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.geysermc.connect.MasterServer;
-import org.geysermc.connect.utils.Player;
-import org.geysermc.connect.utils.Server;
+import org.geysermc.connect.extension.GeyserConnect;
+import org.geysermc.connect.extension.utils.Server;
+import org.geysermc.connect.extension.utils.ServerManager;
+import org.geysermc.connect.extension.utils.Utils;
+import org.geysermc.geyser.session.GeyserSession;
import java.io.IOException;
-import java.sql.*;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractSQLStorageManager extends AbstractStorageManager {
- private final ObjectMapper mapper = new ObjectMapper();
-
protected Connection connection;
@Override
@@ -49,8 +53,19 @@ public abstract class AbstractSQLStorageManager extends AbstractStorageManager {
try (Statement createPlayersTable = connection.createStatement()) {
createPlayersTable.executeUpdate("CREATE TABLE IF NOT EXISTS players (xuid VARCHAR(32), servers TEXT, PRIMARY KEY(xuid));");
}
+
+ try (PreparedStatement getPlayersServers = connection.prepareStatement("SELECT xuid, servers FROM players")) {
+ ResultSet rs = getPlayersServers.executeQuery();
+
+ while (rs.next()) {
+ List loadedServers = Utils.OBJECT_MAPPER.readValue(rs.getString("servers"), new TypeReference<>() {});
+ GeyserConnect.instance().logger().info("Loaded " + loadedServers.size() + " servers for " + rs.getString("xuid"));
+ }
+ } catch (IOException | SQLException exception) {
+ GeyserConnect.instance().logger().error("Couldn't load servers", exception);
+ }
} catch (ClassNotFoundException | SQLException e) {
- MasterServer.getInstance().getLogger().severe("Unable to connect to MySQL database!", e);
+ GeyserConnect.instance().logger().severe("Unable to connect to MySQL database!", e);
}
}
@@ -61,37 +76,38 @@ public abstract class AbstractSQLStorageManager extends AbstractStorageManager {
try {
connection.close();
} catch (SQLException exception) {
- MasterServer.getInstance().getLogger().error("Failed to close SQL connection", exception);
+ GeyserConnect.instance().logger().error("Failed to close SQL connection", exception);
}
}
@Override
- public void saveServers(Player player) {
+ public void saveServers(GeyserSession session) {
// replace into works on MySQL and SQLite
try (PreparedStatement updatePlayersServers = connection.prepareStatement("REPLACE INTO players(xuid, servers) VALUES(?, ?)")) {
- updatePlayersServers.setString(1, player.getAuthData().xuid());
- updatePlayersServers.setString(2, mapper.writeValueAsString(player.getServers()));
+ updatePlayersServers.setString(1, session.getAuthData().xuid());
+ updatePlayersServers.setString(2, Utils.OBJECT_MAPPER.writeValueAsString(ServerManager.getServers(session)));
updatePlayersServers.executeUpdate();
} catch (IOException | SQLException exception) {
- MasterServer.getInstance().getLogger().error("Couldn't save servers for " + player.getAuthData().name(), exception);
+ GeyserConnect.instance().logger().error("Couldn't save servers for " + session.getAuthData().name(), exception);
}
}
@Override
- public List loadServers(Player player) {
+ public List loadServers(GeyserSession session) {
List servers = new ArrayList<>();
try (PreparedStatement getPlayersServers = connection.prepareStatement("SELECT servers FROM players WHERE xuid=?")) {
- getPlayersServers.setString(1, player.getAuthData().xuid());
+ getPlayersServers.setString(1, session.getAuthData().xuid());
ResultSet rs = getPlayersServers.executeQuery();
while (rs.next()) {
- List loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference<>() {
- });
- servers.addAll(loadedServers);
+ List loadedServers = Utils.OBJECT_MAPPER.readValue(rs.getString("servers"), new TypeReference<>() {});
+ if (loadedServers != null) {
+ servers.addAll(loadedServers);
+ }
}
} catch (IOException | SQLException exception) {
- MasterServer.getInstance().getLogger().error("Couldn't load servers for " + player.getAuthData().name(), exception);
+ GeyserConnect.instance().logger().error("Couldn't load servers for " + session.getAuthData().name(), exception);
}
return servers;
diff --git a/src/main/java/org/geysermc/connect/storage/AbstractStorageManager.java b/src/main/java/org/geysermc/connect/extension/storage/AbstractStorageManager.java
similarity index 72%
rename from src/main/java/org/geysermc/connect/storage/AbstractStorageManager.java
rename to src/main/java/org/geysermc/connect/extension/storage/AbstractStorageManager.java
index 28ee1a5..dea7c95 100644
--- a/src/main/java/org/geysermc/connect/storage/AbstractStorageManager.java
+++ b/src/main/java/org/geysermc/connect/extension/storage/AbstractStorageManager.java
@@ -23,12 +23,12 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.storage;
+package org.geysermc.connect.extension.storage;
import com.fasterxml.jackson.annotation.JsonValue;
-import lombok.Getter;
-import org.geysermc.connect.utils.Player;
-import org.geysermc.connect.utils.Server;
+import org.geysermc.connect.extension.utils.Server;
+import org.geysermc.geyser.session.GeyserSession;
+import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@@ -39,26 +39,33 @@ public class AbstractStorageManager {
public void closeStorage() { }
- public void saveServers(Player player) { }
+ public void saveServers(GeyserSession session) { }
- public List loadServers(Player player) {
+ public List loadServers(GeyserSession session) {
return new ArrayList<>();
}
- @Getter
public enum StorageType {
JSON("json", JsonStorageManager.class),
SQLITE("sqlite", SQLiteStorageManager.class),
MYSQL("mysql", MySQLStorageManager.class);
@JsonValue
- private final String name;
+ private final String configName;
private final Class extends AbstractStorageManager> storageManager;
- StorageType(String name, Class extends AbstractStorageManager> storageManager) {
- this.name = name;
+ StorageType(String configName, Class extends AbstractStorageManager> storageManager) {
+ this.configName = configName;
this.storageManager = storageManager;
}
+
+ public String configName() {
+ return configName;
+ }
+
+ public Class extends AbstractStorageManager> storageManager() {
+ return storageManager;
+ }
}
}
diff --git a/src/main/java/org/geysermc/connect/storage/DisabledStorageManager.java b/src/main/java/org/geysermc/connect/extension/storage/DisabledStorageManager.java
similarity index 84%
rename from src/main/java/org/geysermc/connect/storage/DisabledStorageManager.java
rename to src/main/java/org/geysermc/connect/extension/storage/DisabledStorageManager.java
index 8ca6eb8..23ebbdf 100644
--- a/src/main/java/org/geysermc/connect/storage/DisabledStorageManager.java
+++ b/src/main/java/org/geysermc/connect/extension/storage/DisabledStorageManager.java
@@ -23,10 +23,10 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.storage;
+package org.geysermc.connect.extension.storage;
-import org.geysermc.connect.utils.Player;
-import org.geysermc.connect.utils.Server;
+import org.geysermc.connect.extension.utils.Server;
+import org.geysermc.geyser.session.GeyserSession;
import java.util.ArrayList;
import java.util.List;
@@ -38,12 +38,12 @@ public class DisabledStorageManager extends AbstractStorageManager {
}
@Override
- public void saveServers(Player player) {
+ public void saveServers(GeyserSession session) {
}
@Override
- public List loadServers(Player player) {
+ public List loadServers(GeyserSession session) {
return new ArrayList<>();
}
}
diff --git a/src/main/java/org/geysermc/connect/storage/JsonStorageManager.java b/src/main/java/org/geysermc/connect/extension/storage/JsonStorageManager.java
similarity index 66%
rename from src/main/java/org/geysermc/connect/storage/JsonStorageManager.java
rename to src/main/java/org/geysermc/connect/extension/storage/JsonStorageManager.java
index cd07ed8..1a895a2 100644
--- a/src/main/java/org/geysermc/connect/storage/JsonStorageManager.java
+++ b/src/main/java/org/geysermc/connect/extension/storage/JsonStorageManager.java
@@ -23,12 +23,14 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.storage;
+package org.geysermc.connect.extension.storage;
import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.geysermc.connect.utils.Player;
-import org.geysermc.connect.utils.Server;
+import org.geysermc.connect.extension.GeyserConnect;
+import org.geysermc.connect.extension.utils.Server;
+import org.geysermc.connect.extension.utils.ServerManager;
+import org.geysermc.connect.extension.utils.Utils;
+import org.geysermc.geyser.session.GeyserSession;
import java.io.IOException;
import java.nio.file.Path;
@@ -37,31 +39,32 @@ import java.util.ArrayList;
import java.util.List;
public class JsonStorageManager extends AbstractStorageManager {
-
- private final ObjectMapper mapper = new ObjectMapper();
- private final Path dataFolder = Paths.get("players/");
+ private Path dataFolder;
@Override
public void setupStorage() {
+ dataFolder = GeyserConnect.instance().dataFolder().resolve("players/");
if (!dataFolder.toFile().exists()) {
dataFolder.toFile().mkdirs();
}
}
@Override
- public void saveServers(Player player) {
+ public void saveServers(GeyserSession session) {
try {
- mapper.writeValue(dataFolder.resolve(player.getAuthData().xuid() + ".json").toFile(), player.getServers());
+ Utils.OBJECT_MAPPER.writeValue(dataFolder.resolve(session.getAuthData().xuid() + ".json").toFile(), ServerManager.getServers(session));
} catch (IOException ignored) { }
}
@Override
- public List loadServers(Player player) {
+ public List loadServers(GeyserSession session) {
List servers = new ArrayList<>();
try {
- List loadedServers = mapper.readValue(dataFolder.resolve(player.getAuthData().xuid() + ".json").toFile(), new TypeReference<>(){});
- servers.addAll(loadedServers);
+ List loadedServers = Utils.OBJECT_MAPPER.readValue(dataFolder.resolve(session.getAuthData().xuid() + ".json").toFile(), new TypeReference<>(){});
+ if (loadedServers != null) {
+ servers.addAll(loadedServers);
+ }
} catch (IOException ignored) { }
return servers;
diff --git a/src/main/java/org/geysermc/connect/storage/MySQLStorageManager.java b/src/main/java/org/geysermc/connect/extension/storage/MySQLStorageManager.java
similarity index 74%
rename from src/main/java/org/geysermc/connect/storage/MySQLStorageManager.java
rename to src/main/java/org/geysermc/connect/extension/storage/MySQLStorageManager.java
index be12187..8852d63 100644
--- a/src/main/java/org/geysermc/connect/storage/MySQLStorageManager.java
+++ b/src/main/java/org/geysermc/connect/extension/storage/MySQLStorageManager.java
@@ -23,18 +23,19 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.storage;
+package org.geysermc.connect.extension.storage;
-import org.geysermc.connect.GeyserConnectConfig;
-import org.geysermc.connect.MasterServer;
+import org.geysermc.connect.extension.GeyserConnect;
+import org.geysermc.connect.extension.config.MySQLConnectionSection;
-import java.sql.*;
+import java.sql.DriverManager;
+import java.sql.SQLException;
public class MySQLStorageManager extends AbstractSQLStorageManager {
@Override
protected void connectToDatabase() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
- GeyserConnectConfig.MySQLConnectionSection connectionInformation = MasterServer.getInstance().getGeyserConnectConfig().getCustomServers().getMysql();
- connection = DriverManager.getConnection("jdbc:mysql://" + connectionInformation.getHost() + ":" + connectionInformation.getPort() + "/" + connectionInformation.getDatabase(), connectionInformation.getUser(), connectionInformation.getPass());
+ MySQLConnectionSection connectionInformation = GeyserConnect.instance().config().customServers().mysql();
+ connection = DriverManager.getConnection("jdbc:mysql://" + connectionInformation.host() + ":" + connectionInformation.port() + "/" + connectionInformation.database(), connectionInformation.user(), connectionInformation.pass());
}
}
diff --git a/src/main/java/org/geysermc/connect/storage/SQLiteStorageManager.java b/src/main/java/org/geysermc/connect/extension/storage/SQLiteStorageManager.java
similarity index 83%
rename from src/main/java/org/geysermc/connect/storage/SQLiteStorageManager.java
rename to src/main/java/org/geysermc/connect/extension/storage/SQLiteStorageManager.java
index be21f9c..957e331 100644
--- a/src/main/java/org/geysermc/connect/storage/SQLiteStorageManager.java
+++ b/src/main/java/org/geysermc/connect/extension/storage/SQLiteStorageManager.java
@@ -23,14 +23,17 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.storage;
+package org.geysermc.connect.extension.storage;
-import java.sql.*;
+import org.geysermc.connect.extension.GeyserConnect;
+
+import java.sql.DriverManager;
+import java.sql.SQLException;
public class SQLiteStorageManager extends AbstractSQLStorageManager {
@Override
protected void connectToDatabase() throws ClassNotFoundException, SQLException {
Class.forName("org.sqlite.JDBC");
- connection = DriverManager.getConnection("jdbc:sqlite:players.db");
+ connection = DriverManager.getConnection("jdbc:sqlite:" + GeyserConnect.instance().dataFolder().resolve("players.db"));
}
}
diff --git a/src/main/java/org/geysermc/connect/extension/ui/UIHandler.java b/src/main/java/org/geysermc/connect/extension/ui/UIHandler.java
new file mode 100644
index 0000000..e9f56f3
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/ui/UIHandler.java
@@ -0,0 +1,314 @@
+package org.geysermc.connect.extension.ui;
+
+import org.cloudburstmc.protocol.bedrock.packet.BedrockPacketHandler;
+import org.cloudburstmc.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
+import org.cloudburstmc.protocol.bedrock.packet.TransferPacket;
+import org.geysermc.connect.extension.GeyserConnect;
+import org.geysermc.connect.extension.utils.Server;
+import org.geysermc.connect.extension.utils.ServerCategory;
+import org.geysermc.connect.extension.utils.ServerManager;
+import org.geysermc.connect.extension.utils.Utils;
+import org.geysermc.cumulus.form.CustomForm;
+import org.geysermc.cumulus.form.ModalForm;
+import org.geysermc.cumulus.form.SimpleForm;
+import org.geysermc.geyser.session.GeyserSession;
+import org.geysermc.geyser.util.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+public class UIHandler {
+ private final GeyserSession session;
+ private final SetLocalPlayerAsInitializedPacket initializedPacket;
+ private final BedrockPacketHandler originalPacketHandler;
+
+ public UIHandler(GeyserSession session, SetLocalPlayerAsInitializedPacket packet, BedrockPacketHandler originalPacketHandler) {
+ this.session = session;
+ this.initializedPacket = new SetLocalPlayerAsInitializedPacket();
+ this.initializedPacket.setRuntimeEntityId(packet.getRuntimeEntityId());
+ this.originalPacketHandler = originalPacketHandler;
+ }
+
+ private void sendToServer(Server server) {
+ GeyserConnect.instance().logger().info("Sending " + Utils.displayName(session) + " to " + server.title());
+ GeyserConnect.instance().logger().debug(server.toString());
+
+ if (server.bedrock()) {
+ // Send them to the bedrock server
+ TransferPacket transferPacket = new TransferPacket();
+ transferPacket.setAddress(server.address());
+ transferPacket.setPort(server.port());
+ session.sendUpstreamPacket(transferPacket);
+ } else {
+ // Save the players servers since we are changing packet handlers
+ ServerManager.unloadServers(session);
+
+ // Restore the original packet handler
+ session.getUpstream().getSession().setPacketHandler(originalPacketHandler);
+
+ // Set the remote server and un-initialize the session
+ session.remoteServer(server);
+ session.getUpstream().setInitialized(false);
+
+ // Hand back to core geyser
+ originalPacketHandler.handle(initializedPacket);
+ }
+ }
+
+ public void initialiseSession() {
+ String message = "";
+ try {
+ File messageFile = Utils.fileOrCopiedFromResource(GeyserConnect.instance().config().welcomeFile(), "welcome.txt");
+ message = new String(FileUtils.readAllBytes(messageFile), StandardCharsets.UTF_8);
+ } catch (IOException ignored) { }
+
+ if (!message.trim().isEmpty()) {
+ session.sendForm(CustomForm.builder()
+ .title("Notice")
+ .label(message)
+ .resultHandler((customForm, customFormResponseFormResponseResult) -> {
+ sendMainMenu();
+// this.sendToServer(new Server("test.geysermc.org", 25565));
+ })
+ .build());
+ } else {
+ sendMainMenu();
+ }
+ }
+
+ public void sendMainMenu() {
+ SimpleForm.Builder mainMenu = SimpleForm.builder()
+ .title("Main Menu")
+ .button("Official Servers")
+ .button("Geyser Servers");
+
+ // Add a buttons for custom servers
+ if (GeyserConnect.instance().config().customServers().enabled()) {
+ mainMenu.button("Custom Servers");
+ mainMenu.button("Direct connect");
+ }
+
+ mainMenu
+ .button("Disconnect")
+ .closedResultHandler(response -> {
+ sendMainMenu();
+ })
+ .invalidResultHandler(response -> {
+ session.disconnect("disconnectionScreen.disconnected");
+ })
+ .validResultHandler(response -> {
+ switch (response.clickedButtonId()) {
+ case 0:
+ sendServersMenu(ServerCategory.OFFICIAL);
+ return;
+ case 1:
+ sendServersMenu(ServerCategory.GEYSER);
+ return;
+ default:
+ if (GeyserConnect.instance().config().customServers().enabled()) {
+ switch (response.clickedButtonId()) {
+ case 2:
+ sendServersMenu(ServerCategory.CUSTOM);
+ return;
+ case 3:
+ sendDirectConnectMenu();
+ return;
+ }
+ }
+ break;
+ }
+
+ session.disconnect("disconnectionScreen.disconnected");
+ });
+
+ session.sendForm(mainMenu);
+ }
+
+ public void sendServersMenu(ServerCategory category) {
+ SimpleForm.Builder serversMenu = SimpleForm.builder()
+ .title(category.title() + " Servers");
+
+ List servers;
+ if (category == ServerCategory.CUSTOM) {
+ servers = ServerManager.getServers(session);
+ } else {
+ servers = Utils.getServers(category);
+ }
+
+ for (Server server : servers) {
+ serversMenu.button(server.title(), server.formImage());
+ }
+
+ if (category == ServerCategory.CUSTOM) {
+ serversMenu.button("Edit servers");
+ }
+
+ serversMenu
+ .button("Back")
+ .closedOrInvalidResultHandler(response -> {
+ sendMainMenu();
+ })
+ .validResultHandler(response -> {
+ if (category == ServerCategory.CUSTOM) {
+ if (response.clickedButtonId() == servers.size()) {
+ sendEditServersMenu();
+ return;
+ } else if (response.clickedButtonId() == servers.size() + 1) {
+ sendMainMenu();
+ return;
+ }
+ } else if (response.clickedButtonId() == servers.size()) {
+ sendMainMenu();
+ return;
+ }
+
+ Server server = servers.get(response.clickedButtonId());
+ sendToServer(server);
+ });
+
+ session.sendForm(serversMenu);
+ }
+
+ public void sendEditServersMenu() {
+ SimpleForm.Builder editServersMenu = SimpleForm.builder()
+ .title("Edit Servers")
+ .content("Select a server to edit");
+
+ List servers = ServerManager.getServers(session);
+
+ for (Server server : servers) {
+ editServersMenu.button(server.title(), server.formImage());
+ }
+
+ editServersMenu
+ .button("Add server")
+ .button("Back")
+ .closedOrInvalidResultHandler(response -> {
+ sendServersMenu(ServerCategory.CUSTOM);
+ })
+ .validResultHandler(response -> {
+ if (response.clickedButtonId() == servers.size()) {
+ sendAddServerMenu();
+ return;
+ } else if (response.clickedButtonId() == servers.size() + 1) {
+ sendServersMenu(ServerCategory.CUSTOM);
+ return;
+ }
+
+ Server server = servers.get(response.clickedButtonId());
+ sendServerOptionsMenu(server);
+ });
+
+ session.sendForm(editServersMenu);
+ }
+
+ public void sendAddServerMenu() {
+ session.sendForm(CustomForm.builder()
+ .title("Add Server")
+ .input("IP", "play.cubecraft.net")
+ .input("Port", "25565", "25565")
+ .toggle("Online mode", true)
+ .toggle("Bedrock/Geyser server", false)
+ .closedOrInvalidResultHandler(response -> {
+ sendEditServersMenu();
+ })
+ .validResultHandler(response -> {
+ String ip = response.asInput(0);
+ int port = Integer.parseInt(response.asInput(1));
+ boolean onlineMode = response.asToggle(2);
+ boolean geyserServer = response.asToggle(3);
+
+ Server server = new Server(ip, port, onlineMode, geyserServer, null, null, ServerCategory.CUSTOM);
+ ServerManager.addServer(session, server);
+ sendEditServersMenu();
+ }));
+ }
+
+ public void sendServerOptionsMenu(Server server) {
+ session.sendForm(SimpleForm.builder()
+ .title("Server Options")
+ .content(server.title())
+ .button("Edit server")
+ .button("Delete server")
+ .button("Back")
+ .closedOrInvalidResultHandler(response -> {
+ sendEditServersMenu();
+ })
+ .validResultHandler(response -> {
+ switch (response.clickedButtonId()) {
+ case 0:
+ sendEditServerMenu(server);
+ return;
+ case 1:
+ sendDeleteServerMenu(server);
+ return;
+ case 2:
+ sendEditServersMenu();
+ return;
+ }
+ }));
+ }
+
+ public void sendEditServerMenu(Server server) {
+ int serverIndex = ServerManager.getServerIndex(session, server);
+ session.sendForm(CustomForm.builder()
+ .title("Edit Server")
+ .input("IP", server.address(), server.address())
+ .input("Port", String.valueOf(server.port()), String.valueOf(server.port()))
+ .toggle("Online mode", server.online())
+ .toggle("Bedrock/Geyser server", server.bedrock())
+ .closedOrInvalidResultHandler(response -> {
+ sendServerOptionsMenu(server);
+ })
+ .validResultHandler(response -> {
+ String ip = response.asInput(0);
+ int port = Integer.parseInt(response.asInput(1));
+ boolean onlineMode = response.asToggle(2);
+ boolean geyserServer = response.asToggle(3);
+
+ Server newServer = new Server(ip, port, onlineMode, geyserServer, null, null, ServerCategory.CUSTOM);
+ ServerManager.updateServer(session, serverIndex, newServer);
+ sendServerOptionsMenu(newServer);
+ }));
+ }
+
+ public void sendDeleteServerMenu(Server server) {
+ session.sendForm(ModalForm.builder()
+ .title("Delete Server")
+ .content("Are you sure you want to delete " + server.title() + "?")
+ .button1("Yes")
+ .button2("No")
+ .closedOrInvalidResultHandler(response -> {
+ sendServerOptionsMenu(server);
+ })
+ .validResultHandler(response -> {
+ if (response.clickedButtonId() == 0) {
+ ServerManager.removeServer(session, server);
+ }
+ sendEditServersMenu();
+ }));
+ }
+
+ public void sendDirectConnectMenu() {
+ session.sendForm(CustomForm.builder()
+ .title("Direct Connect")
+ .input("IP", "play.cubecraft.net")
+ .input("Port", "25565", "25565")
+ .toggle("Online mode", true)
+ .toggle("Bedrock/Geyser server", false)
+ .closedOrInvalidResultHandler(response -> {
+ sendMainMenu();
+ })
+ .validResultHandler(response -> {
+ String ip = response.asInput(0);
+ int port = Integer.parseInt(response.asInput(1));
+ boolean onlineMode = response.asToggle(2);
+ boolean geyserServer = response.asToggle(3);
+
+ Server server = new Server(ip, port, onlineMode, geyserServer, null, null, ServerCategory.CUSTOM);
+ sendToServer(server);
+ }));
+ }
+}
diff --git a/src/main/java/org/geysermc/connect/utils/Server.java b/src/main/java/org/geysermc/connect/extension/utils/Server.java
similarity index 59%
rename from src/main/java/org/geysermc/connect/utils/Server.java
rename to src/main/java/org/geysermc/connect/extension/utils/Server.java
index 2777f45..56aa9e3 100644
--- a/src/main/java/org/geysermc/connect/utils/Server.java
+++ b/src/main/java/org/geysermc/connect/extension/utils/Server.java
@@ -23,80 +23,32 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.utils;
+package org.geysermc.connect.extension.utils;
import com.fasterxml.jackson.annotation.JsonIgnore;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.cumulus.util.FormImage;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.api.network.RemoteServer;
-@Getter
-@AllArgsConstructor
-@NoArgsConstructor
-public class Server implements RemoteServer {
-
- private String address;
- private int port = -1;
- private boolean online = true;
- private boolean bedrock = false;
- private String name = null;
- private String imageUrl = null;
- private ServerCategory category = null;
-
- public Server(String address) {
- this(address, -1);
- }
-
+public record Server(
+ String address,
+ int port,
+ boolean online,
+ boolean bedrock,
+ String name,
+ String imageUrl,
+ ServerCategory category
+) implements RemoteServer {
public Server(String address, int port) {
- this(address, port, true);
- }
-
- public Server(String address, int port, boolean online) {
- this(address, port, online, false);
- }
-
- public Server(String address, int port, boolean online, boolean bedrock) {
- this(address, port, online, bedrock, null);
- }
-
- public Server(String address, int port, boolean online, boolean bedrock, String name) {
- this(address, port, online, bedrock, name, null);
- }
-
- public Server(String address, int port, boolean online, boolean bedrock, String name, String imageUrl) {
- this(address.replaceAll(" ", ""), port, online, bedrock, name, imageUrl, ServerCategory.CUSTOM);
+ this(address, port, true, false, null, null, ServerCategory.CUSTOM);
}
private int defaultPort() { return bedrock ? 19132 : 25565; }
- public int getPort() { return port < 0 ? defaultPort() : port; }
-
- @Override
- public String toString() {
- return name != null ? name : address + (getPort() != defaultPort() ? ":" + getPort() : "");
- }
-
- @JsonIgnore
- public FormImage getFormImage() {
- if (imageUrl != null && !imageUrl.isEmpty()) {
- return FormImage.of(FormImage.Type.URL, imageUrl);
- } else {
- return FormImage.of(FormImage.Type.URL, "https://eu.mc-api.net/v3/server/favicon/" + address + ":" + port + ".png?use-fallback-icon=true");
- }
- }
-
- @Override
- public String address() {
- return address;
- }
-
@Override
public int port() {
- return port;
+ return port < 0 ? defaultPort() : port;
}
@Override
@@ -104,6 +56,15 @@ public class Server implements RemoteServer {
return this.online ? AuthType.ONLINE : AuthType.OFFLINE;
}
+ @JsonIgnore
+ public FormImage formImage() {
+ if (imageUrl != null && !imageUrl.isEmpty()) {
+ return FormImage.of(FormImage.Type.URL, imageUrl);
+ } else {
+ return FormImage.of(FormImage.Type.URL, "https://eu.mc-api.net/v3/server/favicon/" + address + ":" + port + ".png?use-fallback-icon=true");
+ }
+ }
+
@Override
public String minecraftVersion() {
return null;
@@ -113,4 +74,8 @@ public class Server implements RemoteServer {
public int protocolVersion() {
return 0;
}
+
+ public String title() {
+ return name != null ? name : address + (port() != defaultPort() ? ":" + port() : "");
+ }
}
diff --git a/src/main/java/org/geysermc/connect/utils/ServerCategory.java b/src/main/java/org/geysermc/connect/extension/utils/ServerCategory.java
similarity index 93%
rename from src/main/java/org/geysermc/connect/utils/ServerCategory.java
rename to src/main/java/org/geysermc/connect/extension/utils/ServerCategory.java
index a7ade38..349f92e 100644
--- a/src/main/java/org/geysermc/connect/utils/ServerCategory.java
+++ b/src/main/java/org/geysermc/connect/extension/utils/ServerCategory.java
@@ -23,11 +23,8 @@
* @link https://github.com/GeyserMC/GeyserConnect
*/
-package org.geysermc.connect.utils;
+package org.geysermc.connect.extension.utils;
-import lombok.Getter;
-
-@Getter
public enum ServerCategory {
OFFICIAL("Official"),
GEYSER("Geyser"),
@@ -38,4 +35,8 @@ public enum ServerCategory {
ServerCategory(String title) {
this.title = title;
}
+
+ public String title() {
+ return title;
+ }
}
diff --git a/src/main/java/org/geysermc/connect/extension/utils/ServerManager.java b/src/main/java/org/geysermc/connect/extension/utils/ServerManager.java
new file mode 100644
index 0000000..5d19f21
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/utils/ServerManager.java
@@ -0,0 +1,43 @@
+package org.geysermc.connect.extension.utils;
+
+import org.geysermc.connect.extension.GeyserConnect;
+import org.geysermc.geyser.session.GeyserSession;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ServerManager {
+ private static final Map> servers = new HashMap<>();
+
+ public static void loadServers(GeyserSession session) {
+ GeyserConnect.instance().logger().debug("Loading servers for " + Utils.displayName(session));
+ servers.put(session.xuid(), GeyserConnect.instance().storageManager().loadServers(session));
+ }
+
+ public static void unloadServers(GeyserSession session) {
+ GeyserConnect.instance().logger().debug("Saving and unloading servers for " + Utils.displayName(session));
+ GeyserConnect.instance().storageManager().saveServers(session);
+ servers.remove(session.xuid());
+ }
+
+ public static List getServers(GeyserSession session) {
+ return servers.get(session.xuid());
+ }
+
+ public static void addServer(GeyserSession session, Server server) {
+ servers.get(session.xuid()).add(server);
+ }
+
+ public static void removeServer(GeyserSession session, Server server) {
+ getServers(session).remove(server);
+ }
+
+ public static int getServerIndex(GeyserSession session, Server server) {
+ return getServers(session).indexOf(server);
+ }
+
+ public static void updateServer(GeyserSession session, int serverIndex, Server server) {
+ getServers(session).set(serverIndex, server);
+ }
+}
diff --git a/src/main/java/org/geysermc/connect/extension/utils/Utils.java b/src/main/java/org/geysermc/connect/extension/utils/Utils.java
new file mode 100644
index 0000000..6a7e062
--- /dev/null
+++ b/src/main/java/org/geysermc/connect/extension/utils/Utils.java
@@ -0,0 +1,51 @@
+package org.geysermc.connect.extension.utils;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.geysermc.connect.extension.GeyserConnect;
+import org.geysermc.geyser.session.GeyserSession;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.List;
+
+public class Utils {
+ public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+
+ public static List getServers(ServerCategory category) {
+ return GeyserConnect.instance().config().servers().stream().filter(server -> server.category() == category).toList();
+ }
+
+ public static File fileOrCopiedFromResource(String fileName, String name) throws IOException {
+ File file = GeyserConnect.instance().dataFolder().resolve(fileName).toFile();
+
+ if (!file.exists()) {
+ try (FileWriter writer = new FileWriter(file)) {
+ try (FileSystem fileSystem = FileSystems.newFileSystem(new File(GeyserConnect.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath(), Collections.emptyMap())) {
+ try (InputStream input = Files.newInputStream(fileSystem.getPath(name))) {
+ byte[] bytes = new byte[input.available()];
+
+ input.read(bytes);
+
+ writer.write(new String(bytes).toCharArray());
+
+ writer.flush();
+ }
+ }
+ } catch (URISyntaxException ignored) { }
+ }
+
+ return file;
+ }
+
+ public static String displayName(GeyserSession session) {
+ return session.bedrockUsername() + " (" + session.xuid() + ")";
+ }
+}
diff --git a/src/main/java/org/geysermc/connect/proxy/GeyserProxyBootstrap.java b/src/main/java/org/geysermc/connect/proxy/GeyserProxyBootstrap.java
deleted file mode 100644
index a41afa7..0000000
--- a/src/main/java/org/geysermc/connect/proxy/GeyserProxyBootstrap.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.proxy;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
-import org.geysermc.common.PlatformType;
-import org.geysermc.connect.GeyserConnectConfig;
-import org.geysermc.connect.MasterServer;
-import org.geysermc.geyser.GeyserBootstrap;
-import org.geysermc.geyser.GeyserImpl;
-import org.geysermc.geyser.command.GeyserCommandManager;
-import org.geysermc.geyser.configuration.GeyserConfiguration;
-import org.geysermc.geyser.dump.BootstrapDumpInfo;
-import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
-import org.geysermc.geyser.ping.IGeyserPingPassthrough;
-import org.geysermc.geyser.text.GeyserLocale;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.stream.Collectors;
-
-public class GeyserProxyBootstrap implements GeyserBootstrap {
-
- private GeyserCommandManager geyserCommandManager;
- private GeyserProxyConfiguration geyserConfig;
- private GeyserProxyLogger geyserLogger;
- private IGeyserPingPassthrough geyserPingPassthrough;
-
- private GeyserImpl geyser;
-
- @Override
- public void onEnable() {
- GeyserLocale.init(this);
-
- // Setup a logger
- geyserLogger = new GeyserProxyLogger();
-
- // Read the static config from resources
- try {
- InputStream configFile = GeyserProxyBootstrap.class.getClassLoader().getResourceAsStream("proxy_config.yml");
-
- // Grab the config as text and replace static strings to the main config variables
- String text = new BufferedReader(new InputStreamReader(configFile, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n"));
- GeyserConnectConfig multiConfig = MasterServer.getInstance().getGeyserConnectConfig();
- text = text.replaceAll("%MOTD%", multiConfig.getMotd());
- text = text.replace("%PLAYERS%", String.valueOf(multiConfig.getMaxPlayers()));
- text = text.replace("%ALLOWPASSWORDAUTHENTICATION%", String.valueOf(multiConfig.getGeyser().isAllowPasswordAuthentication()));
-
- ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
- geyserConfig = objectMapper.readValue(text, GeyserProxyConfiguration.class);
-
- geyserConfig.getSavedUserLogins().clear();
- for (String savedUserLogin : MasterServer.getInstance().getGeyserConnectConfig().getGeyser().getSavedUserLogins()) {
- geyserConfig.getSavedUserLogins().add(savedUserLogin);
- }
-
- } catch (IOException ex) {
- geyserLogger.severe("Failed to read proxy_config.yml! Make sure it's up to date and/or readable+writable!", ex);
- return;
- }
-
- // Not sure there is a point in doing this as its a static config
- GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
-
- // Create the connector and command manager
- geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
- GeyserImpl.start();
- geyserCommandManager = new GeyserCommandManager(geyser);
-
- // Start the ping passthrough thread, again don't think there is a point
- geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
-
- // Swap the normal handler to our custom handler so we can change some
- geyser.getBedrockServer().setHandler(new ProxyConnectorServerEventHandler(geyser));
- }
-
- @Override
- public void onDisable() {
- geyser.shutdown();
- }
-
- @Override
- public GeyserConfiguration getGeyserConfig() {
- return geyserConfig;
- }
-
- @Override
- public GeyserProxyLogger getGeyserLogger() {
- return geyserLogger;
- }
-
- @Override
- public GeyserCommandManager getGeyserCommandManager() {
- return geyserCommandManager;
- }
-
- @Override
- public IGeyserPingPassthrough getGeyserPingPassthrough() {
- return geyserPingPassthrough;
- }
-
- @Override
- public Path getConfigFolder() {
- return Paths.get(System.getProperty("user.dir"));
- }
-
- @Override
- public BootstrapDumpInfo getDumpInfo() {
- return new BootstrapDumpInfo();
- }
-}
-
diff --git a/src/main/java/org/geysermc/connect/proxy/GeyserProxyConfiguration.java b/src/main/java/org/geysermc/connect/proxy/GeyserProxyConfiguration.java
deleted file mode 100644
index d7a93ae..0000000
--- a/src/main/java/org/geysermc/connect/proxy/GeyserProxyConfiguration.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.proxy;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import lombok.Getter;
-import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-@Getter
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class GeyserProxyConfiguration extends GeyserJacksonConfiguration {
- @Override
- public Path getFloodgateKeyPath() {
- return Paths.get(getFloodgateKeyFile());
- }
-}
diff --git a/src/main/java/org/geysermc/connect/proxy/GeyserProxyLogger.java b/src/main/java/org/geysermc/connect/proxy/GeyserProxyLogger.java
deleted file mode 100644
index 69a631d..0000000
--- a/src/main/java/org/geysermc/connect/proxy/GeyserProxyLogger.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.proxy;
-
-import lombok.extern.log4j.Log4j2;
-import org.geysermc.connect.MasterServer;
-import org.geysermc.connect.utils.Logger;
-
-@Log4j2
-public class GeyserProxyLogger extends Logger {
-
- /**
- * Disable debug messages depending on config
- */
- public void debug(String message) {
- if (MasterServer.getInstance().getGeyserConnectConfig().getGeyser().isDebugMode())
- super.debug(message);
- }
-}
diff --git a/src/main/java/org/geysermc/connect/proxy/GeyserProxySession.java b/src/main/java/org/geysermc/connect/proxy/GeyserProxySession.java
deleted file mode 100644
index 192128d..0000000
--- a/src/main/java/org/geysermc/connect/proxy/GeyserProxySession.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * @author GeyserMC
- * @link https://github.com/GeyserMC/GeyserConnect
- */
-
-package org.geysermc.connect.proxy;
-
-import com.nukkitx.protocol.bedrock.BedrockServerSession;
-import io.netty.channel.EventLoop;
-import org.geysermc.connect.utils.Player;
-import org.geysermc.connect.utils.Server;
-import org.geysermc.geyser.GeyserImpl;
-import org.geysermc.geyser.session.GeyserSession;
-import org.geysermc.geyser.session.PendingMicrosoftAuthentication.*;
-
-public class GeyserProxySession extends GeyserSession {
- private final Player player;
-
- public GeyserProxySession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop eventLoop, Player player, boolean initialized) {
- super(geyser, bedrockServerSession, eventLoop);
- sentSpawnPacket = initialized;
- this.player = player;
- }
-
- @Override
- protected void disableSrvResolving() {
- // Do nothing
- }
-
- @Override
- public void disconnect(String reason) {
- ProxyAuthenticationTask task = (ProxyAuthenticationTask) getGeyser().getPendingMicrosoftAuthentication().getTask(this.xuid());
- if (task != null) {
- Server server = player.getCurrentServer();
- task.setServer(server.getAddress());
- task.setPort(server.getPort());
- }
- super.disconnect(reason);
- }
-}
diff --git a/src/main/java/org/geysermc/connect/proxy/ProxyConnectorServerEventHandler.java b/src/main/java/org/geysermc/connect/proxy/ProxyConnectorServerEventHandler.java
deleted file mode 100644
index c067813..0000000
--- a/src/main/java/org/geysermc/connect/proxy/ProxyConnectorServerEventHandler.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.proxy;
-
-import com.nukkitx.protocol.bedrock.BedrockServerSession;
-import org.geysermc.geyser.GeyserImpl;
-import org.geysermc.geyser.network.ConnectorServerEventHandler;
-
-public class ProxyConnectorServerEventHandler extends ConnectorServerEventHandler {
-
- public ProxyConnectorServerEventHandler(GeyserImpl geyser) {
- super(geyser);
- }
-
- @Override
- public void onSessionCreation(BedrockServerSession bedrockServerSession) {
- super.onSessionCreation(bedrockServerSession);
- bedrockServerSession.disconnect("Not sure how you managed it, but you shouldn't be here!");
- }
-}
diff --git a/src/main/java/org/geysermc/connect/ui/FormID.java b/src/main/java/org/geysermc/connect/ui/FormID.java
deleted file mode 100644
index 2cef258..0000000
--- a/src/main/java/org/geysermc/connect/ui/FormID.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.ui;
-
-import lombok.Getter;
-
-@Getter
-public enum FormID {
-
- WELCOME,
- MAIN,
- DIRECT_CONNECT(true),
- LIST_SERVERS(true),
- EDIT_SERVERS(true),
- SERVER_OPTIONS(true),
- ADD_SERVER(true),
- REMOVE_SERVER,
- EDIT_SERVER(true),
- CONNECTING,
- ERROR;
-
- private final boolean handlesNull;
-
- private static final FormID[] VALUES = values();
-
- FormID() {
- this(false);
- }
-
- FormID(boolean handlesNull) {
- this.handlesNull = handlesNull;
- }
-
- public static FormID fromId(int id) {
- return id >= 0 && id < VALUES.length ? VALUES[id] : ERROR;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/org/geysermc/connect/ui/UIHandler.java b/src/main/java/org/geysermc/connect/ui/UIHandler.java
deleted file mode 100644
index 9739504..0000000
--- a/src/main/java/org/geysermc/connect/ui/UIHandler.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.ui;
-
-import org.geysermc.connect.MasterServer;
-import org.geysermc.connect.utils.Player;
-import org.geysermc.connect.utils.Server;
-import org.geysermc.connect.utils.ServerCategory;
-import org.geysermc.cumulus.CustomForm;
-import org.geysermc.cumulus.Form;
-import org.geysermc.cumulus.SimpleForm;
-import org.geysermc.cumulus.component.InputComponent;
-import org.geysermc.cumulus.component.LabelComponent;
-import org.geysermc.cumulus.component.ToggleComponent;
-import org.geysermc.cumulus.response.CustomFormResponse;
-import org.geysermc.cumulus.response.SimpleFormResponse;
-import org.geysermc.cumulus.util.FormImage;
-
-import java.util.List;
-
-public class UIHandler {
-
- /**
- * Create a list of servers for the client based on the passed servers list
- *
- * @return A {@link SimpleForm} object
- */
- public static Form getMainMenu() {
- SimpleForm.Builder window = SimpleForm.builder().title("Main Menu");
-
- window.button("Official Servers");
- window.button("Geyser Servers");
-
- // Add a buttons for custom servers
- if (MasterServer.getInstance().getGeyserConnectConfig().getCustomServers().isEnabled()) {
- window.button("Custom Servers");
- window.button("Direct connect");
- }
-
- window.button("Disconnect");
-
- return window.build();
- }
-
- /**
- * Create a list of servers for the client based on the passed servers list
- *
- * @param servers A list of {@link Server} objects
- * @param category The category of the current list
- * @return A {@link SimpleForm} object
- */
- public static Form getServerList(List servers, ServerCategory category) {
- SimpleForm.Builder window = SimpleForm.builder().title(category.getTitle() + " Servers");
-
- // Add a button for each global server
- for (Server server : servers) {
- // These images would be better if there was a default to fall back on
- // But that would require a web api as bedrock doesn't support doing that
- window.button(server.toString(), server.getFormImage());
- }
-
- // Add a button for editing
- if (category == ServerCategory.CUSTOM) {
- window.button("Edit servers");
- }
-
- window.button("Back");
-
- return window.build();
- }
-
- /**
- * Create a direct connect form
- *
- * @return A {@link CustomForm} object
- */
- public static Form getDirectConnect() {
- return CustomForm.builder().title("Direct Connect")
- .component(InputComponent.of("IP", "play.cubecraft.net"))
- .component(InputComponent.of("Port", "25565", "25565"))
- .component(ToggleComponent.of("Online mode", true))
- .component(ToggleComponent.of("Bedrock/Geyser server", false))
- .build();
- }
-
- /**
- * Create a list of servers for the client to edit
- *
- * @param servers A list of {@link Server} objects
- * @return A {@link SimpleForm} object
- */
- public static Form getEditServerList(List servers) {
- SimpleForm.Builder window = SimpleForm.builder().title("Edit Servers").content("Select a server to edit");
-
- // Add a button for each personal server
- for (Server server : servers) {
- window.button(server.toString(), FormImage.of(FormImage.Type.URL,
- "https://eu.mc-api.net/v3/server/favicon/" + server.getAddress() + ":" + server.getPort() + ".png"));
- }
-
- window.button("Add server");
- window.button("Back");
-
- return window.build();
- }
-
- /**
- * Create a add server form
- *
- * @return A {@link CustomForm} object
- */
- public static Form getAddServer() {
- return CustomForm.builder().title("Add Server")
- .component(InputComponent.of("IP", "play.cubecraft.net"))
- .component(InputComponent.of("Port", "25565", "25565"))
- .component(ToggleComponent.of("Online mode", true))
- .component(ToggleComponent.of("Bedrock/Geyser server", false))
- .build();
- }
-
- /**
- * Create a server options form
- *
- * @param server A {@link Server} object to show options for
- * @return A {@link SimpleForm} object
- */
- public static Form getServerOptions(Server server) {
- SimpleForm.Builder window = SimpleForm.builder().title("Server Options").content(server.toString());
-
- window.button("Edit");
- window.button("Remove");
- window.button("Back");
-
- return window.build();
- }
-
- /**
- * Create a remove server form
- *
- * @param server A {@link Server} object to remove
- * @return A {@link SimpleForm} object
- */
- public static Form getRemoveServer(Server server) {
- return SimpleForm.builder()
- .title("Remove Server")
- .content("Are you sure you want to remove server: " + server)
- .button("Remove")
- .button("Cancel")
- .build();
- }
-
- /**
- * Create a edit server form
- *
- * @param server A {@link Server} object to edit
- * @return A {@link CustomForm} object
- */
- public static Form getEditServer(int serverIndex, Server server) {
- String port = String.valueOf(server.getPort());
- return CustomForm.builder()
- .component(LabelComponent.of("Server at index: " + serverIndex))
- .component(InputComponent.of("IP", server.getAddress(), server.getAddress()))
- .component(InputComponent.of("Port", port, port))
- .component(ToggleComponent.of("Online mode", server.isOnline()))
- .component(ToggleComponent.of("Bedrock/Geyser server", server.isBedrock()))
- .build();
- }
-
- /**
- * Show a basic form window with a message
- *
- * @param message The message to display
- * @return A {@link CustomForm} object
- */
- public static Form getMessageWindow(String message) {
- return CustomForm.builder()
- .title("Notice")
- .component(LabelComponent.of(message))
- .build();
- }
-
- /**
- * Handle the main menu response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleMainMenuResponse(Player player, SimpleFormResponse data) {
- switch (data.getClickedButtonId()) {
- case 0:
- player.setServerCategory(ServerCategory.OFFICIAL);
- break;
-
- case 1:
- player.setServerCategory(ServerCategory.GEYSER);
- break;
-
- default:
- // If we have custom servers enabled there are a few extra buttons
- if (MasterServer.getInstance().getGeyserConnectConfig().getCustomServers().isEnabled()) {
- switch (data.getClickedButtonId()) {
- case 2:
- player.setServerCategory(ServerCategory.CUSTOM);
- break;
- case 3:
- player.sendWindow(FormID.DIRECT_CONNECT, getDirectConnect());
- return;
-
- default:
- player.getSession().disconnect("disconnectionScreen.disconnected");
- return;
- }
- } else {
- player.getSession().disconnect("disconnectionScreen.disconnected");
- return;
- }
- break;
- }
-
- // Send the server list
- player.sendWindow(FormID.LIST_SERVERS, getServerList(player.getCurrentServers(), player.getServerCategory()));
- }
-
- /**
- * Handle the server list response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleServerListResponse(Player player, SimpleFormResponse data) {
- List servers = player.getCurrentServers();
-
- if (player.getServerCategory() == ServerCategory.CUSTOM) {
- if (!data.isCorrect() || data.getClickedButtonId() == servers.size() + 1) {
- player.sendWindow(FormID.MAIN, UIHandler.getMainMenu());
- } else if (data.getClickedButtonId() == servers.size()) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getCurrentServers()));
- } else {
- // Get the server
- Server server = servers.get(data.getClickedButtonId());
-
- player.sendToServer(server);
- }
- } else {
- if (!data.isCorrect() || data.getClickedButtonId() == servers.size()) {
- player.sendWindow(FormID.MAIN, UIHandler.getMainMenu());
- } else {
- // Get the server
- Server server = servers.get(data.getClickedButtonId());
-
- player.sendToServer(server);
- }
- }
- }
-
- /**
- * Handle the direct connect response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleDirectConnectResponse(Player player, CustomFormResponse data) {
- // Take them back to the main menu if they close the direct connect window
- if (!data.isCorrect()) {
- player.sendWindow(FormID.MAIN, getMainMenu());
- return;
- }
-
- try {
- String address = data.getInput(0);
- int port = Integer.parseInt(data.getInput(1));
- boolean online = data.getToggle(2);
- boolean bedrock = data.getToggle(3);
-
- // Make sure we got an address
- if (address == null || "".equals(address)) {
- player.sendWindow(FormID.MAIN, getMainMenu());
- return;
- }
-
- // Make sure we got a valid port
- if (port <= 0 || port >= 65535) {
- player.resendWindow();
- return;
- }
-
- player.sendToServer(new Server(address, port, online, bedrock));
- } catch (NumberFormatException e) {
- player.resendWindow();
- }
- }
-
- /**
- * Handle the edit server list response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleEditServerListResponse(Player player, SimpleFormResponse data) {
- List servers = player.getCurrentServers();
-
- // Take them back to the main menu if they close the edit server list window
- if (!data.isCorrect()) {
- player.sendWindow(FormID.LIST_SERVERS, getServerList(servers, player.getServerCategory()));
- return;
- }
-
- if (data.getClickedButtonId() == servers.size()) {
- player.sendWindow(FormID.ADD_SERVER, getAddServer());
- } else if (data.getClickedButtonId() == servers.size() + 1) {
- player.sendWindow(FormID.LIST_SERVERS, getServerList(servers, player.getServerCategory()));
- } else {
- Server server = player.getServers().get(data.getClickedButtonId());
- player.sendWindow(FormID.SERVER_OPTIONS, getServerOptions(server));
- }
- }
-
- /**
- * Handle the add server response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleAddServerResponse(Player player, CustomFormResponse data) {
- // Take them back to the edit server list menu if they close the add server window
- if (!data.isCorrect()) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- return;
- }
-
- try {
- String address = data.getInput(0);
- int port = Integer.parseInt(data.getInput(1));
- boolean online = data.getToggle(2);
- boolean bedrock = data.getToggle(3);
-
- // Make sure we got an address
- if (address == null || "".equals(address)) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- return;
- }
-
- // Make sure we got a valid port
- if (port <= 0 || port >= 65535) {
- player.resendWindow();
- return;
- }
-
- player.getServers().add(new Server(address, port, online, bedrock));
-
- // Send them back to the edit screen
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- } catch (NumberFormatException e) {
- player.resendWindow();
- }
- }
-
- /**
- * Handle the server options response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleServerOptionsResponse(Player player, SimpleFormResponse data) {
- // Take them back to the main menu if they close the edit server list window
- if (!data.isCorrect()) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- return;
- }
-
- SimpleForm window = (SimpleForm) player.getCurrentWindow();
- Server selectedServer = null;
- for (Server server : player.getServers()) {
- if (server.toString().equals(window.getContent())) {
- selectedServer = server;
- break;
- }
- }
-
- if (selectedServer == null) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- return;
- }
-
- switch (data.getClickedButtonId()) {
- case 0:
- player.sendWindow(FormID.EDIT_SERVER, getEditServer(player.getServers().indexOf(selectedServer), selectedServer));
- break;
-
- case 1:
- player.sendWindow(FormID.REMOVE_SERVER, getRemoveServer(selectedServer));
- break;
-
- default:
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- break;
- }
- }
-
- /**
- * Handle the server remove response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleServerRemoveResponse(Player player, SimpleFormResponse data) {
- SimpleForm window = (SimpleForm) player.getCurrentWindow();
- String serverName = window.getContent().split(":")[1].trim();
- Server selectedServer = null;
- for (Server server : player.getServers()) {
- if (server.toString().equals(serverName)) {
- selectedServer = server;
- break;
- }
- }
-
- if (selectedServer == null) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- return;
- }
-
- if (data.getClickedButtonId() == 0) {
- player.getServers().remove(selectedServer);
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- } else {
- player.sendWindow(FormID.SERVER_OPTIONS, getServerOptions(selectedServer));
- }
- }
-
- /**
- * Handle the edit server response
- *
- * @param player The player that submitted the response
- * @param data The form response data
- */
- public static void handleEditServerResponse(Player player, CustomFormResponse data) {
- // Take them back to the edit server list menu if they close the add server window
- if (!data.isCorrect()) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- return;
- }
-
- try {
- int serverIndex = Integer.parseInt(((CustomForm)player.getCurrentWindow()).getContent().get(0).getText().split(":")[1].trim());
-
- String address = data.getInput(1);
- int port = Integer.parseInt(data.getInput(2));
- boolean online = data.getToggle(3);
- boolean bedrock = data.getToggle(4);
-
- // Make sure we got an address
- if (address == null || "".equals(address)) {
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- return;
- }
-
- // Make sure we got a valid port
- if (port <= 0 || port >= 65535) {
- player.resendWindow();
- return;
- }
-
- player.getServers().set(serverIndex, new Server(address, port, online, bedrock));
-
- // Send them back to the edit screen
- player.sendWindow(FormID.EDIT_SERVERS, getEditServerList(player.getServers()));
- } catch (NumberFormatException e) {
- player.resendWindow();
- }
- }
-}
diff --git a/src/main/java/org/geysermc/connect/utils/GeyserConnectFileUtils.java b/src/main/java/org/geysermc/connect/utils/GeyserConnectFileUtils.java
deleted file mode 100644
index 56b12a4..0000000
--- a/src/main/java/org/geysermc/connect/utils/GeyserConnectFileUtils.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * @author GeyserMC
- * @link https://github.com/GeyserMC/GeyserConnect
- */
-
-package org.geysermc.connect.utils;
-
-import org.geysermc.connect.MasterServer;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.function.Function;
-
-public final class GeyserConnectFileUtils {
-
- public static File fileOrCopiedFromResource(File file, String name, Function format) throws IOException {
- if (!file.exists()) {
- //noinspection ResultOfMethodCallIgnored
- file.createNewFile();
- try (FileOutputStream fos = new FileOutputStream(file)) {
- try (InputStream input = MasterServer.class.getClassLoader().getResourceAsStream(name)) {
- byte[] bytes = new byte[input.available()];
-
- //noinspection ResultOfMethodCallIgnored
- input.read(bytes);
-
- for(char c : format.apply(new String(bytes)).toCharArray()) {
- fos.write(c);
- }
-
- fos.flush();
- }
- }
- }
-
- return file;
- }
-
- private GeyserConnectFileUtils() {
- }
-}
diff --git a/src/main/java/org/geysermc/connect/utils/Logger.java b/src/main/java/org/geysermc/connect/utils/Logger.java
deleted file mode 100644
index 01975b5..0000000
--- a/src/main/java/org/geysermc/connect/utils/Logger.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.utils;
-
-import lombok.extern.log4j.Log4j2;
-import net.minecrell.terminalconsole.SimpleTerminalConsole;
-import org.apache.logging.log4j.core.config.Configurator;
-import org.geysermc.connect.MasterServer;
-import org.geysermc.geyser.GeyserLogger;
-import org.geysermc.geyser.text.ChatColor;
-
-@Log4j2
-public class Logger extends SimpleTerminalConsole implements GeyserLogger {
-
- @Override
- protected boolean isRunning() {
- return !MasterServer.getInstance().isShuttingDown();
- }
-
- @Override
- protected void runCommand(String line) {
- // Dont do anything rn
- }
-
- @Override
- protected void shutdown() {
- MasterServer.getInstance().shutdown();
- }
-
- public void severe(String message) {
- log.fatal(printConsole(ChatColor.DARK_RED + message));
- }
-
- public void severe(String message, Throwable error) {
- log.fatal(printConsole(ChatColor.DARK_RED + message), error);
- }
-
- public void error(String message) {
- log.error(printConsole(ChatColor.RED + message));
- }
-
- public void error(String message, Throwable error) {
- log.error(printConsole(ChatColor.RED + message), error);
- }
-
- public void warning(String message) {
- log.warn(printConsole(ChatColor.YELLOW + message));
- }
-
- public void info(String message) {
- log.info(printConsole(ChatColor.WHITE + message));
- }
-
- public void debug(String message) {
- log.debug(printConsole(ChatColor.GRAY + message));
- }
-
- public static String printConsole(String message) {
- return ChatColor.toANSI(message + ChatColor.RESET);
- }
-
- public void setDebug(boolean debug) {
- Configurator.setLevel(log.getName(), debug ? org.apache.logging.log4j.Level.DEBUG : log.getLevel());
- }
-
- public boolean isDebug() {
- return log.isDebugEnabled();
- }
-}
diff --git a/src/main/java/org/geysermc/connect/utils/Player.java b/src/main/java/org/geysermc/connect/utils/Player.java
deleted file mode 100644
index 6b0ad00..0000000
--- a/src/main/java/org/geysermc/connect/utils/Player.java
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * 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/GeyserConnect
- */
-
-package org.geysermc.connect.utils;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.nukkitx.math.vector.Vector2f;
-import com.nukkitx.math.vector.Vector3f;
-import com.nukkitx.math.vector.Vector3i;
-import com.nukkitx.nbt.NbtMap;
-import com.nukkitx.protocol.bedrock.BedrockServerSession;
-import com.nukkitx.protocol.bedrock.data.*;
-import com.nukkitx.protocol.bedrock.packet.*;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
-import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
-import lombok.Getter;
-import lombok.Setter;
-import org.geysermc.connect.MasterServer;
-import org.geysermc.connect.proxy.GeyserProxySession;
-import org.geysermc.connect.ui.FormID;
-import org.geysermc.cumulus.Form;
-import org.geysermc.geyser.GeyserImpl;
-import org.geysermc.geyser.api.network.AuthType;
-import org.geysermc.geyser.network.UpstreamPacketHandler;
-import org.geysermc.geyser.registry.BlockRegistries;
-import org.geysermc.geyser.registry.Registries;
-import org.geysermc.geyser.registry.type.ItemMappings;
-import org.geysermc.geyser.session.auth.AuthData;
-import org.geysermc.geyser.session.auth.BedrockClientData;
-import org.geysermc.geyser.util.ChunkUtils;
-import org.geysermc.geyser.util.DimensionUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-@Getter
-public class Player {
- private static final byte[] EMPTY_CHUNK_DATA;
-
- static {
- ByteBuf byteBuf = Unpooled.buffer();
- try {
- for (int i = 0; i < 32; i++) {
- byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
- }
-
- byteBuf.writeByte(0); // Border
-
- EMPTY_CHUNK_DATA = new byte[byteBuf.readableBytes()];
- byteBuf.readBytes(EMPTY_CHUNK_DATA);
- } finally {
- byteBuf.release();
- }
- }
-
- private final AuthData authData;
- @Setter
- private JsonNode chainData;
-
- private final BedrockServerSession session;
-
- private final List servers = new ArrayList<>();
- private final Long2ObjectMap forms = new Long2ObjectOpenHashMap<>();
-
- private Form currentWindow;
- private FormID currentWindowId;
-
- @Setter
- private Server currentServer;
-
- @Setter
- private BedrockClientData clientData;
-
- @Setter
- private ServerCategory serverCategory;
-
- public Player(AuthData authData, BedrockServerSession session) {
- this.authData = authData;
- this.session = session;
-
- // Should fetch the servers from some form of db
- if (MasterServer.getInstance().getGeyserConnectConfig().getCustomServers().isEnabled()) {
- servers.addAll(MasterServer.getInstance().getStorageManager().loadServers(this));
- }
- }
-
- /**
- * Send a few different packets to get the client to load in
- */
- public void sendStartGame() {
- ItemMappings itemMappings = Registries.ITEMS.forVersion(session.getPacketCodec().getProtocolVersion());
-
- // A lot of this likely doesn't need to be changed
- StartGamePacket startGamePacket = new StartGamePacket();
- startGamePacket.setUniqueEntityId(1);
- startGamePacket.setRuntimeEntityId(1);
- startGamePacket.setPlayerGameType(GameType.CREATIVE);
- startGamePacket.setPlayerPosition(Vector3f.from(0, 64 + 2, 0));
- startGamePacket.setRotation(Vector2f.ONE);
-
- startGamePacket.setSeed(-1L);
- startGamePacket.setDimensionId(2);
- startGamePacket.setGeneratorId(1);
- startGamePacket.setLevelGameType(GameType.CREATIVE);
- startGamePacket.setDifficulty(0);
- startGamePacket.setDefaultSpawn(Vector3i.ZERO);
- startGamePacket.setAchievementsDisabled(true);
- startGamePacket.setCurrentTick(-1);
- startGamePacket.setEduEditionOffers(0);
- startGamePacket.setEduFeaturesEnabled(false);
- startGamePacket.setRainLevel(0);
- startGamePacket.setLightningLevel(0);
- startGamePacket.setMultiplayerGame(true);
- startGamePacket.setBroadcastingToLan(true);
- startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", true));
- startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
- startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
- startGamePacket.setCommandsEnabled(true);
- startGamePacket.setTexturePacksRequired(false);
- startGamePacket.setBonusChestEnabled(false);
- startGamePacket.setStartingWithMap(false);
- startGamePacket.setTrustingPlayers(true);
- startGamePacket.setDefaultPlayerPermission(PlayerPermission.VISITOR);
- startGamePacket.setServerChunkTickRange(4);
- startGamePacket.setBehaviorPackLocked(false);
- startGamePacket.setResourcePackLocked(false);
- startGamePacket.setFromLockedWorldTemplate(false);
- startGamePacket.setUsingMsaGamertagsOnly(false);
- startGamePacket.setFromWorldTemplate(false);
- startGamePacket.setWorldTemplateOptionLocked(false);
-
- startGamePacket.setLevelId("");
- startGamePacket.setLevelName("GeyserConnect");
- startGamePacket.setPremiumWorldTemplateId("");
- startGamePacket.setCurrentTick(0);
- startGamePacket.setEnchantmentSeed(0);
- startGamePacket.setMultiplayerCorrelationId("");
- startGamePacket.setItemEntries(itemMappings.getItemEntries());
- startGamePacket.setInventoriesServerAuthoritative(true);
- startGamePacket.setServerEngine("");
- startGamePacket.setPlayerPropertyData(NbtMap.EMPTY);
- startGamePacket.setWorldTemplateId(UUID.randomUUID());
- startGamePacket.setChatRestrictionLevel(ChatRestrictionLevel.NONE);
-
- SyncedPlayerMovementSettings settings = new SyncedPlayerMovementSettings();
- settings.setMovementMode(AuthoritativeMovementMode.CLIENT);
- settings.setRewindHistorySize(0);
- settings.setServerAuthoritativeBlockBreaking(false);
- startGamePacket.setPlayerMovementSettings(settings);
-
- startGamePacket.setVanillaVersion("*");
- session.sendPacket(startGamePacket);
-
- if (!itemMappings.getComponentItemData().isEmpty()) {
- ItemComponentPacket itemComponentPacket = new ItemComponentPacket();
- itemComponentPacket.getItems().addAll(itemMappings.getComponentItemData());
- session.sendPacket(itemComponentPacket);
- }
-
- // Send an empty chunk
- LevelChunkPacket data = new LevelChunkPacket();
- data.setChunkX(0);
- data.setChunkZ(0);
- data.setSubChunksLength(0);
- data.setData(EMPTY_CHUNK_DATA);
- data.setCachingEnabled(false);
- session.sendPacket(data);
-
- // Send the biomes
- BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
- biomeDefinitionListPacket.setDefinitions(Registries.BIOMES_NBT.get());
- session.sendPacket(biomeDefinitionListPacket);
-
- AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket();
- entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get());
- session.sendPacket(entityPacket);
-
- // Send a CreativeContentPacket - required for 1.16.100
- CreativeContentPacket creativeContentPacket = new CreativeContentPacket();
- creativeContentPacket.setContents(itemMappings.getCreativeItems());
- session.sendPacket(creativeContentPacket);
-
- // Let the client know the player can spawn
- PlayStatusPacket playStatusPacket = new PlayStatusPacket();
- playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
- session.sendPacket(playStatusPacket);
-
- // Freeze the player
- SetEntityMotionPacket setEntityMotionPacket = new SetEntityMotionPacket();
- setEntityMotionPacket.setRuntimeEntityId(1);
- setEntityMotionPacket.setMotion(Vector3f.ZERO);
- session.sendPacket(setEntityMotionPacket);
- }
-
- /**
- * Send a window with the specified id and content
- * Also cache it against the player for later use
- *
- * @param id The {@link FormID} to use for the form
- * @param window The {@link Form} to turn into json and send
- */
- public void sendWindow(FormID id, Form window) {
- this.currentWindow = window;
- this.currentWindowId = id;
-
- ModalFormRequestPacket modalFormRequestPacket = new ModalFormRequestPacket();
- modalFormRequestPacket.setFormId(id.ordinal());
- modalFormRequestPacket.setFormData(window.getJsonData());
- session.sendPacket(modalFormRequestPacket);
-
- // This packet is used to fix the image loading bug
- NetworkStackLatencyPacket networkStackLatencyPacket = new NetworkStackLatencyPacket();
- networkStackLatencyPacket.setFromServer(true);
- networkStackLatencyPacket.setTimestamp(System.currentTimeMillis());
- session.sendPacket(networkStackLatencyPacket);
- }
-
- public void resendWindow() {
- sendWindow(currentWindowId, currentWindow);
- }
-
- /**
- * Send the player to the Geyser proxy server or straight to the bedrock server if it is
- */
- public void connectToProxy() {
- if (currentServer.isBedrock()) {
- TransferPacket transferPacket = new TransferPacket();
- transferPacket.setAddress(currentServer.getAddress());
- transferPacket.setPort(currentServer.getPort());
- session.sendPacket(transferPacket);
- } else {
- GeyserProxySession geyserSession = createGeyserSession(true);
- GeyserImpl geyser = geyserSession.getGeyser();
-
- geyserSession.setDimension(DimensionUtils.THE_END);
-
- geyserSession.remoteServer(currentServer);
-
- // Tell Geyser to handle the login
- SetLocalPlayerAsInitializedPacket initializedPacket = new SetLocalPlayerAsInitializedPacket();
- initializedPacket.setRuntimeEntityId(geyserSession.getPlayerEntity().getGeyserId());
- session.getPacketHandler().handle(initializedPacket);
-
- if (geyser.getConfig().getSavedUserLogins().contains(authData.name())) {
- String refreshToken = geyser.refreshTokenFor(authData.name());
- if (refreshToken != null) {
- geyserSession.authenticateWithRefreshToken(refreshToken);
- }
- }
-
- if (geyserSession.remoteServer().authType() != AuthType.ONLINE) {
- geyserSession.authenticate(geyserSession.getAuthData().name());
- }
- }
- }
-
- public GeyserProxySession createGeyserSession(boolean initialized) {
- GeyserProxySession geyserSession = new GeyserProxySession(GeyserImpl.getInstance(), session,
- MasterServer.getInstance().getEventLoopGroup().next(), this, initialized);
- session.setPacketHandler(new UpstreamPacketHandler(GeyserImpl.getInstance(), geyserSession));
- // The player will be tracked from Geyser from here
- MasterServer.getInstance().getPlayers().remove(this);
- GeyserImpl.getInstance().getSessionManager().addPendingSession(geyserSession);
-
- geyserSession.getUpstream().getSession().setPacketCodec(session.getPacketCodec());
-
- // Set the block translation based off of version
- geyserSession.setBlockMappings(BlockRegistries.BLOCKS.forVersion(session.getPacketCodec().getProtocolVersion()));
- geyserSession.setItemMappings(Registries.ITEMS.forVersion(session.getPacketCodec().getProtocolVersion()));
-
- geyserSession.setAuthData(authData);
- geyserSession.setCertChainData(chainData);
- geyserSession.setClientData(clientData);
-
- return geyserSession;
- }
-
- public void sendToServer(Server server) {
- // Geyser will show a "please wait" message in action bar
-
- // Send the user over to the server
- setCurrentServer(server);
- connectToProxy();
- }
-
- public List getCurrentServers() {
- if (serverCategory == ServerCategory.CUSTOM) {
- return servers;
- }
-
- return MasterServer.getInstance().getServers(serverCategory);
- }
-}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 44f8518..637bacc 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -2,39 +2,10 @@
# GeyserConnect Configuration File
# --------------------------------
-# The IP address that will listen for connections
-address: 0.0.0.0
-
-# The port that will listen for connections
-port: 19132
-
-# If debug messages should be sent through console
-debug-mode: false
-
-# Maximum amount of players that can connect
-max-players: 100
-
-# MOTD to display
-motd: "GeyserConnect Proxy"
-#Sub-MOTD to display. Will be "GeyserConnect" by default if left blank
-submotd: "GeyserConnect"
-
# Welcome message file, if file exists and is not empty this will show on join
# This is loaded live so will update without a server restart
welcome-file: welcome.txt
-# Config for the Geyser listener
-geyser:
- # If password authentication should be allowed in online mode.
- allow-password-authentication: false
-
- # If debug messages should be sent through console, has to be enabled in both places to work
- debug-mode: false
-
- saved-user-logins:
- - ThisExampleUsernameShouldBeLongEnoughToNeverBeAnXboxUsername
- - ThisOtherExampleUsernameShouldAlsoBeLongEnough
-
# A global list of servers sent to all clients
servers:
- name: The Hive
@@ -42,49 +13,49 @@ servers:
port: 19132
bedrock: true
category: OFFICIAL
- imageUrl: 'https://i.imgur.com/VQJW6XG.png'
+ imageUrl: 'https://i.imgur.com/myXmr7B.png'
- name: CubeCraft
address: 94.23.159.81
port: 19132
bedrock: true
category: OFFICIAL
- imageUrl: 'https://www.cubecraft.net/attachments/q3pmrsod_400x400-png.53219/'
+ imageUrl: 'https://i.imgur.com/f83ZeDS.jpg'
- name: Galaxite
address: 54.39.243.199
port: 19132
bedrock: true
category: OFFICIAL
- imageUrl: 'https://pbs.twimg.com/profile_images/1275867042583896066/UMPF5nTM_400x400.jpg'
+ imageUrl: 'https://i.imgur.com/PEkLROT.jpg'
- name: Lifeboat Network
address: 63.143.54.198
port: 19132
bedrock: true
category: OFFICIAL
- imageUrl: 'https://lbsg.net/wp-content/uploads/2017/06/lifeboat-square.png'
+ imageUrl: 'https://i.imgur.com/GjsxhCI.png'
- name: Mineplex
address: 108.178.12.38
port: 19132
bedrock: true
category: OFFICIAL
- imageUrl: 'https://www.mineplex.com/assets/www-mp/img/footer/footer_smalllogo.png'
+ imageUrl: 'https://i.imgur.com/xB6yncS.png'
- name: MineVille
address: 52.234.130.255
port: 19132
bedrock: true
category: OFFICIAL
- imageUrl: 'https://pbs.twimg.com/profile_images/1332400307050045441/MHQvGEUP_400x400.jpg'
+ imageUrl: 'https://i.imgur.com/yqrfMhP.jpg'
- name: Pixel Paradise
address: 40.87.84.59
port: 19132
bedrock: true
category: OFFICIAL
- imageUrl: 'https://xforgeassets001.xboxlive.com/pf-title-b63a0803d3653643-20ca2/fa7681c4-673d-40e4-9b6a-61d5d0f93d14/PixelParadise.jpg'
+ imageUrl: 'https://i.imgur.com/fXQdou8.jpg'
- name: Official Geyser Test Server
address: test.geysermc.org
diff --git a/src/main/resources/extension.yml b/src/main/resources/extension.yml
new file mode 100644
index 0000000..f9b1443
--- /dev/null
+++ b/src/main/resources/extension.yml
@@ -0,0 +1,6 @@
+id: geyserconnect
+name: GeyserConnect
+main: org.geysermc.connect.extension.GeyserConnect
+api: 1.0.0
+version: 1.0.0
+authors: [rtm516]
\ No newline at end of file
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
deleted file mode 100644
index 9574154..0000000
--- a/src/main/resources/log4j2.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/resources/proxy_config.yml b/src/main/resources/proxy_config.yml
deleted file mode 100644
index dbbac0d..0000000
--- a/src/main/resources/proxy_config.yml
+++ /dev/null
@@ -1,196 +0,0 @@
-# --------------------------------
-# Geyser Configuration File
-#
-# A bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition.
-#
-# GitHub: https://github.com/GeyserMC/Geyser
-# Discord: https://discord.geysermc.org/
-# --------------------------------
-
-bedrock:
- # The IP address that will listen for connections.
- # There is no reason to change this unless you want to limit what IPs can connect to your server.
- address: 0.0.0.0
- # The port that will listen for connections
- port: 19132
- # Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock.
- # This option makes the Bedrock port the same as the Java port every time you start the server.
- # This option is for the plugin version only.
- clone-remote-port: false
- # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. This is irrelevant if "passthrough-motd" is set to true
- # If either of these are empty, the respective string will default to "Geyser"
- motd1: "%MOTD%"
- motd2: "%MOTD%"
- # The Server Name that will be sent to Minecraft: Bedrock Edition clients. This is visible in both the pause menu and the settings menu.
- server-name: "Geyser"
- # How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but
- # the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable.
- compression-level: 6
- # Whether to enable PROXY protocol or not for clients. You DO NOT WANT this feature unless you run UDP reverse proxy
- # in front of your Geyser instance.
- enable-proxy-protocol: false
- # A list of allowed PROXY protocol speaking proxy IP addresses/subnets. Only effective when "enable-proxy-protocol" is enabled, and
- # should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.).
- # Keeping this list empty means there is no IP address whitelist.
- # Both IP addresses and subnets are supported.
- #proxy-protocol-whitelisted-ips: [ "127.0.0.1", "172.18.0.0/16" ]
-remote:
- # The IP address of the remote (Java Edition) server
- # If it is "auto", for standalone version the remote address will be set to 127.0.0.1,
- # for plugin versions, Geyser will attempt to find the best address to connect to.
- address: auto
- # The port of the remote (Java Edition) server
- # For plugin versions, if address has been set to "auto", the port will also follow the server's listening port.
- port: 25565
- # Authentication type. Can be offline, online, or floodgate (see https://github.com/GeyserMC/Geyser/wiki/Floodgate).
- auth-type: online
- # Allow for password-based authentication methods through Geyser. Only useful in online mode.
- # If this is false, users must authenticate to Microsoft using a code provided by Geyser on their desktop.
- allow-password-authentication: %ALLOWPASSWORDAUTHENTICATION%
- # Whether to enable PROXY protocol or not while connecting to the server.
- # This is useful only when:
- # 1) Your server supports PROXY protocol (it probably doesn't)
- # 2) You run Velocity or BungeeCord with the option enabled in the proxy's main config.
- # IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT!
- use-proxy-protocol: false
- # Forward the hostname that the Bedrock client used to connect over to the Java server
- # This is designed to be used for forced hosts on proxies
- forward-hostname: false
-
-# Floodgate uses encryption to ensure use from authorised sources.
-# This should point to the public key generated by Floodgate (Bungee or CraftBukkit)
-# You can ignore this when not using Floodgate.
-floodgate-key-file: public-key.pem
-
-saved-user-logins:
- - ThisExampleUsernameShouldBeLongEnoughToNeverBeAnXboxUsername
- - ThisOtherExampleUsernameShouldAlsoBeLongEnough
-
-# Bedrock clients can freeze when opening up the command prompt for the first time if given a lot of commands.
-# Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients.
-command-suggestions: false
-
-# The following three options enable "ping passthrough" - the MOTD, player count and/or protocol name gets retrieved from the Java server.
-# Relay the MOTD from the remote server to Bedrock players.
-passthrough-motd: false
-# Relay the protocol name (e.g. BungeeCord [X.X], Paper 1.X) - only really useful when using a custom protocol name!
-# This will also show up on sites like MCSrvStatus.
-passthrough-protocol-name: false
-# Relay the player count and max players from the remote server to Bedrock players.
-passthrough-player-counts: false
-# Enable LEGACY ping passthrough. There is no need to enable this unless your MOTD or player count does not appear properly.
-# This option does nothing on standalone.
-legacy-ping-passthrough: false
-# How often to ping the remote server, in seconds. Only relevant for standalone or legacy ping passthrough.
-# Increase if you are getting BrokenPipe errors.
-ping-passthrough-interval: 3
-
-# Whether to forward player ping to the server. While enabling this will allow Bedrock players to have more accurate
-# ping, it may also cause players to time out more easily.
-forward-player-ping: false
-
-# Maximum amount of players that can connect. This is only visual at this time and does not actually limit player count.
-max-players: %PLAYERS%
-
-# If debug messages should be sent through console
-debug-mode: false
-
-# Thread pool size
-general-thread-pool: 32
-
-# Allow third party capes to be visible. Currently allowing:
-# OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes
-allow-third-party-capes: true
-
-# Allow third party deadmau5 ears to be visible. Currently allowing:
-# MinecraftCapes
-allow-third-party-ears: false
-
-# Allow a fake cooldown indicator to be sent. Bedrock players do not see a cooldown as they still use 1.8 combat
-# Can be title, actionbar or false
-show-cooldown: title
-
-# Controls if coordinates are shown to players.
-show-coordinates: true
-
-# If set, when a Bedrock player performs any emote, it will swap the offhand and mainhand items, just like the Java Edition keybind
-# There are three options this can be set to:
-# disabled - the default/fallback, which doesn't apply this workaround
-# no-emotes - emotes will NOT be sent to other Bedrock clients and offhand will be swapped. This effectively disables all emotes from being seen.
-# emotes-and-offhand - emotes will be sent to Bedrock clients and offhand will be swapped
-emote-offhand-workaround: "disabled"
-
-# The default locale if we dont have the one the client requested. Uncomment to not use the default system language.
-# default-locale: en_us
-
-# Configures if chunk caching should be enabled or not. This keeps an individual
-# record of each block the client loads in. This feature does allow for a few things
-# such as more accurate movement that causes less problems with anticheat (meaning
-# you're less likely to be banned) and allows block break animations to show up in
-# creative mode (and other features). Although this increases RAM usage, it likely
-# won't have much of an effect for the vast majority of people. However, if you're
-# running out of RAM or are in a RAM-sensitive environment, you may want to disable
-# this. When using the Spigot version of Geyser, support for features or
-# implementations this allows is automatically enabled without the additional caching
-# as Geyser has direct access to the server itself.
-cache-chunks: true
-
-# Specify how many days images will be cached to disk to save downloading them from the internet.
-# A value of 0 is disabled. (Default: 0)
-cache-images: 0
-
-# Allows custom skulls to be displayed. Keeping them enabled may cause a performance decrease on older/weaker devices.
-allow-custom-skulls: true
-
-# Whether to add (at this time, only) the furnace minecart as a separate item in the game, which normally does not exist in Bedrock Edition.
-# This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching.
-# If this is disabled, furnace minecart items will be mapped to hopper minecart items.
-# This option requires a restart of Geyser in order to change its setting.
-add-non-bedrock-items: true
-
-# Bedrock prevents building and displaying blocks above Y127 in the Nether -
-# enabling this config option works around that by changing the Nether dimension ID
-# to the End ID. The main downside to this is that the sky will resemble that of
-# the end sky in the nether, but ultimately it's the only way for this feature to work.
-above-bedrock-nether-building: false
-
-# Force clients to load all resource packs if there are any.
-# If set to false, it allows the user to connect to the server even if they don't
-# want to download the resource packs.
-force-resource-packs: true
-
-# Allows Xbox achievements to be unlocked.
-# THIS DISABLES ALL COMMANDS FROM SUCCESSFULLY RUNNING FOR BEDROCK IN-GAME, as otherwise Bedrock thinks you are cheating.
-xbox-achievements-enabled: false
-
-# bStats is a stat tracker that is entirely anonymous and tracks only basic information
-# about Geyser, such as how many people are online, how many servers are using Geyser,
-# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
-# https://bstats.org/plugin/server-implementation/GeyserMC
-metrics:
- # If metrics should be enabled
- enabled: false
- # UUID of server, don't change!
- uuid: generateduuid
-
-# ADVANCED OPTIONS - DO NOT TOUCH UNLESS YOU KNOW WHAT YOU ARE DOING!
-
-# Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle
-# a lot of scoreboard packets per second can cause serious lag.
-# This option allows you to specify after how many Scoreboard packets per seconds
-# the Scoreboard updates will be limited to four updates per second.
-scoreboard-packet-threshold: 20
-
-# Allow connections from ProxyPass and Waterdog.
-# See https://www.spigotmc.org/wiki/firewall-guide/ for assistance - use UDP instead of TCP.
-enable-proxy-connections: false
-
-# The internet supports a maximum MTU of 1492 but could cause issues with packet fragmentation.
-# 1400 is the default.
-# mtu: 1400
-
-# Whether to use direct server methods to retrieve information such as block states.
-# Turning this off for Spigot will stop NMS from being used but will have a performance impact.
-use-adapters: true
-
-config-version: 4
\ No newline at end of file