forked from git-mirrors/GeyserConnect
Rewrite to move to a Geyser extension
This commit is contained in:
parent
6b6973c5b9
commit
91bc9657a4
49 changed files with 1350 additions and 2796 deletions
36
.github/workflows/build.yml
vendored
Normal file
36
.github/workflows/build.yml
vendored
Normal file
|
@ -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
|
35
.github/workflows/pullrequest.yml
vendored
35
.github/workflows/pullrequest.yml
vendored
|
@ -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
|
||||
|
|
96
.gitignore
vendored
96
.gitignore
vendored
|
@ -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
|
77
Jenkinsfile
vendored
77
Jenkinsfile
vendored
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
README.md
10
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.
|
||||
|
|
59
build.gradle
Normal file
59
build.gradle
Normal file
|
@ -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
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
FROM openjdk:8-jre-slim
|
||||
RUN mkdir /gsc
|
||||
WORKDIR /gsc
|
||||
EXPOSE 19132/udp
|
||||
CMD ["java", "-Xms1G", "-jar", "GeyserConnect.jar"]
|
|
@ -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
|
||||
```
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -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
|
240
gradlew
vendored
Normal file
240
gradlew
vendored
Normal file
|
@ -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" "$@"
|
91
gradlew.bat
vendored
Normal file
91
gradlew.bat
vendored
Normal file
|
@ -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
|
160
pom.xml
160
pom.xml
|
@ -1,160 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>geyser-connect</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>GeyserConnect</name>
|
||||
<description>Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers via a server list.</description>
|
||||
<url>https://geysermc.org</url>
|
||||
|
||||
<properties>
|
||||
<outputName>GeyserConnect</outputName>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<organization>
|
||||
<name>GeyserMC</name>
|
||||
<url>https://github.com/GeyserMC/GeyserConnect/blob/master/pom.xml</url>
|
||||
</organization>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:https://github.com/GeyserMC/GeyserConnect.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:GeyserMC/GeyserConnect.git</developerConnection>
|
||||
<url>https://github.com/GeyserMC/GeyserConnect</url>
|
||||
</scm>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>opencollab-release-repo</id>
|
||||
<url>https://repo.opencollab.dev/maven-releases/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>opencollab-snapshot-repo</id>
|
||||
<url>https://repo.opencollab.dev/maven-snapshots/</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype-s01</id>
|
||||
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.20</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.minecrell</groupId>
|
||||
<artifactId>terminalconsoleappender</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.31.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.28</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.geysermc.connect.GeyserConnect</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.edwgiz</groupId>
|
||||
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<finalName>${outputName}</finalName>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/versions/9/module-info.class</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.geysermc.connect.GeyserConnect</mainClass>
|
||||
<manifestEntries>
|
||||
<Multi-Release>true</Multi-Release>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer">
|
||||
</transformer>
|
||||
</transformers>
|
||||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
2
settings.gradle
Normal file
2
settings.gradle
Normal file
|
@ -0,0 +1,2 @@
|
|||
rootProject.name = 'GeyserConnect'
|
||||
|
|
@ -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<Server> 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<String> 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;
|
||||
}
|
||||
}
|
|
@ -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<Player> 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<Server> getServers(ServerCategory serverCategory) {
|
||||
return getGeyserConnectConfig().getServers().stream().filter(server -> server.getCategory() == serverCategory).collect(Collectors.toList());
|
||||
}
|
||||
}
|
|
@ -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<List<String>>() { });
|
||||
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<AttributeData> 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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
142
src/main/java/org/geysermc/connect/extension/PacketHandler.java
Normal file
142
src/main/java/org/geysermc/connect/extension/PacketHandler.java
Normal file
|
@ -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<AttributeData> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Server> servers,
|
||||
@JsonProperty("custom-servers") CustomServersSection customServers,
|
||||
VirtualHostSection vhost) {
|
||||
}
|
|
@ -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> T load(Extension extension, Class<?> extensionClass, Class<T> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.geysermc.connect.extension.config;
|
||||
|
||||
public record MySQLConnectionSection(
|
||||
String user,
|
||||
String pass,
|
||||
String database,
|
||||
String host,
|
||||
int port) {
|
||||
}
|
|
@ -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) {
|
||||
}
|
|
@ -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<Server> 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<Server> loadServers(Player player) {
|
||||
public List<Server> loadServers(GeyserSession session) {
|
||||
List<Server> 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<Server> loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference<>() {
|
||||
});
|
||||
servers.addAll(loadedServers);
|
||||
List<Server> 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;
|
|
@ -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<Server> loadServers(Player player) {
|
||||
public List<Server> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Server> loadServers(Player player) {
|
||||
public List<Server> loadServers(GeyserSession session) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
|
@ -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<Server> loadServers(Player player) {
|
||||
public List<Server> loadServers(GeyserSession session) {
|
||||
List<Server> servers = new ArrayList<>();
|
||||
|
||||
try {
|
||||
List<Server> loadedServers = mapper.readValue(dataFolder.resolve(player.getAuthData().xuid() + ".json").toFile(), new TypeReference<>(){});
|
||||
servers.addAll(loadedServers);
|
||||
List<Server> 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;
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
314
src/main/java/org/geysermc/connect/extension/ui/UIHandler.java
Normal file
314
src/main/java/org/geysermc/connect/extension/ui/UIHandler.java
Normal file
|
@ -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<Server> 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<Server> 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);
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -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() : "");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<String, List<Server>> 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<Server> 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);
|
||||
}
|
||||
}
|
|
@ -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<Server> 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() + ")";
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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!");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<Server> 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<Server> 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<Server> 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<Server> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String, String> 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() {
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<Server> servers = new ArrayList<>();
|
||||
private final Long2ObjectMap<ModalFormRequestPacket> 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<Server> getCurrentServers() {
|
||||
if (serverCategory == ServerCategory.CUSTOM) {
|
||||
return servers;
|
||||
}
|
||||
|
||||
return MasterServer.getInstance().getServers(serverCategory);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
6
src/main/resources/extension.yml
Normal file
6
src/main/resources/extension.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
id: geyserconnect
|
||||
name: GeyserConnect
|
||||
main: org.geysermc.connect.extension.GeyserConnect
|
||||
api: 1.0.0
|
||||
version: 1.0.0
|
||||
authors: [rtm516]
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<TerminalConsole name="Console">
|
||||
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red dark, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
||||
</TerminalConsole>
|
||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||
<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %level{length=1} - %msg%n"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy/>
|
||||
<OnStartupTriggeringPolicy/>
|
||||
</Policies>
|
||||
</RollingRandomAccessFile>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="INFO">
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="File"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
|
@ -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. <mcsrvstat.us>
|
||||
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
|
Loading…
Add table
Reference in a new issue