mirror of
https://github.com/GeyserMC/GeyserConnect.git
synced 2025-06-26 14:15:22 +02:00
Merge pull request #74 from rewrite/extension
Rewrite to being an extension
This commit is contained in:
commit
1fac3bc615
46 changed files with 1542 additions and 2731 deletions
37
.github/workflows/build.yml
vendored
Normal file
37
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
name: Build Pull Request
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
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 GeyserConnect
|
||||||
|
uses: gradle/gradle-build-action@v2
|
||||||
|
with:
|
||||||
|
arguments: build
|
||||||
|
|
||||||
|
- name: Archive artifacts
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
if: success()
|
||||||
|
with:
|
||||||
|
name: GeyserConnect
|
||||||
|
path: build/libs/GeyserConnect.jar
|
38
.github/workflows/pullrequest.yml
vendored
38
.github/workflows/pullrequest.yml
vendored
|
@ -1,29 +1,37 @@
|
||||||
name: Build Pull Request
|
name: Build Pull Request
|
||||||
|
|
||||||
on: [pull_request]
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '.idea/copyright/*.xml'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'README.md'
|
||||||
|
- 'bind9/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- name: Set up JDK 17
|
||||||
- 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
|
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 16
|
java-version: 17
|
||||||
- name: Build with Maven
|
distribution: temurin
|
||||||
run: mvn -B package
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Build GeyserConnect
|
||||||
|
uses: gradle/gradle-build-action@v2
|
||||||
|
with:
|
||||||
|
arguments: build
|
||||||
|
|
||||||
- name: Archive artifacts
|
- name: Archive artifacts
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Build
|
name: GeyserConnect
|
||||||
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
|
# Created by https://www.toptal.com/developers/gitignore/api/git,java,gradle,eclipse,netbeans,jetbrains+all
|
||||||
# Edit at https://www.gitignore.io/?templates=git,java,maven,eclipse,netbeans,jetbrains+all
|
# Edit at https://www.toptal.com/developers/gitignore?templates=git,java,gradle,eclipse,netbeans,jetbrains+all
|
||||||
|
|
||||||
### Eclipse ###
|
### Eclipse ###
|
||||||
.metadata
|
.metadata
|
||||||
|
@ -53,22 +53,19 @@ local.properties
|
||||||
|
|
||||||
# Annotation Processing
|
# Annotation Processing
|
||||||
.apt_generated/
|
.apt_generated/
|
||||||
|
.apt_generated_test/
|
||||||
|
|
||||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
.cache-main
|
.cache-main
|
||||||
.scala_dependencies
|
.scala_dependencies
|
||||||
.worksheet
|
.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 Patch ###
|
||||||
# Eclipse Core
|
# Spring Boot Tooling
|
||||||
.project
|
|
||||||
|
|
||||||
# JDT-specific (Eclipse Java Development Tools)
|
|
||||||
.classpath
|
|
||||||
|
|
||||||
# Annotation Processing
|
|
||||||
.apt_generated
|
|
||||||
|
|
||||||
.sts4-cache/
|
.sts4-cache/
|
||||||
|
|
||||||
### Git ###
|
### Git ###
|
||||||
|
@ -110,9 +107,10 @@ local.properties
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
|
replay_pid*
|
||||||
|
|
||||||
### JetBrains+all ###
|
### 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
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
|
@ -122,6 +120,9 @@ hs_err_pid*
|
||||||
.idea/**/dictionaries
|
.idea/**/dictionaries
|
||||||
.idea/**/shelf
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
.idea/**/contentModel.xml
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
@ -142,6 +143,9 @@ hs_err_pid*
|
||||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
# 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
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
# auto-import.
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
# .idea/modules.xml
|
# .idea/modules.xml
|
||||||
# .idea/*.iml
|
# .idea/*.iml
|
||||||
# .idea/modules
|
# .idea/modules
|
||||||
|
@ -169,6 +173,9 @@ atlassian-ide-plugin.xml
|
||||||
# Cursive Clojure plugin
|
# Cursive Clojure plugin
|
||||||
.idea/replstate.xml
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
com_crashlytics_export_strings.xml
|
com_crashlytics_export_strings.xml
|
||||||
crashlytics.properties
|
crashlytics.properties
|
||||||
|
@ -182,32 +189,13 @@ fabric.properties
|
||||||
.idea/caches/build_file_checksums.ser
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
### JetBrains+all Patch ###
|
### JetBrains+all Patch ###
|
||||||
# Ignores the whole .idea folder and all .iml files
|
# Ignore everything but code style settings and run configurations
|
||||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
# that are supposed to be shared within teams.
|
||||||
|
|
||||||
#.idea/
|
.idea/*
|
||||||
|
|
||||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
!.idea/codeStyles
|
||||||
|
!.idea/runConfigurations
|
||||||
*.iml
|
|
||||||
modules.xml
|
|
||||||
.idea/misc.xml
|
|
||||||
*.ipr
|
|
||||||
|
|
||||||
# Sonarlint plugin
|
|
||||||
.idea/sonarlint
|
|
||||||
|
|
||||||
### Maven ###
|
|
||||||
target/
|
|
||||||
pom.xml.tag
|
|
||||||
pom.xml.releaseBackup
|
|
||||||
pom.xml.versionsBackup
|
|
||||||
pom.xml.next
|
|
||||||
release.properties
|
|
||||||
dependency-reduced-pom.xml
|
|
||||||
buildNumber.properties
|
|
||||||
.mvn/timing.properties
|
|
||||||
.mvn/wrapper/maven-wrapper.jar
|
|
||||||
|
|
||||||
### NetBeans ###
|
### NetBeans ###
|
||||||
**/nbproject/private/
|
**/nbproject/private/
|
||||||
|
@ -219,15 +207,35 @@ dist/
|
||||||
nbdist/
|
nbdist/
|
||||||
.nb-gradle/
|
.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
|
# Copyright header files
|
||||||
.idea/*
|
.idea/*
|
||||||
!.idea/copyright/
|
!.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/)
|
[](https://java.com/)
|
||||||
|
|
||||||
[](LICENSE)
|
[](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://discord.geysermc.org/)
|
||||||
[](http://hits.dwyl.io/GeyserMC/GeyserConnect)
|
[](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.
|
GeyserConnect is an easy way for Bedrock Edition clients to connect to any Java Edition servers without having to run anything.
|
||||||
|
|
||||||
## What is GeyserConnect?
|
## 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 use DNS redirection please see the [bind9](bind9) folder in this repository.
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
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
Executable file
240
gradlew
vendored
Executable 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;
|
|
||||||
}
|
|
||||||
}
|
|
120
src/main/java/org/geysermc/connect/extension/GeyserConnect.java
Normal file
120
src/main/java/org/geysermc/connect/extension/GeyserConnect.java
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 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.packet.BedrockPacketHandler;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.TransferPacket;
|
||||||
|
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.command.Command;
|
||||||
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
|
import org.geysermc.geyser.api.event.bedrock.SessionInitializeEvent;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent;
|
||||||
|
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();
|
||||||
|
|
||||||
|
// Remove all saved logins to prevent issues connecting
|
||||||
|
// Maybe worth adding support for this later
|
||||||
|
session.getGeyser().getConfig().getSavedUserLogins().clear();
|
||||||
|
|
||||||
|
// Change the packet handler to our own
|
||||||
|
BedrockPacketHandler packetHandler = session.getUpstream().getSession().getPacketHandler();
|
||||||
|
session.getUpstream().getSession().setPacketHandler(new PacketHandler(this, session, packetHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onCommandDefine(GeyserDefineCommandsEvent event) {
|
||||||
|
event.register(Command.builder(this)
|
||||||
|
.source(GeyserConnection.class)
|
||||||
|
.name("menu")
|
||||||
|
.description("Take you back to the GeyserConnect menu.")
|
||||||
|
.executor((source, command, args) -> {
|
||||||
|
GeyserSession session = (GeyserSession) source;
|
||||||
|
String serverAddress = session.getClientData().getServerAddress();
|
||||||
|
String ip = serverAddress.split(":")[0];
|
||||||
|
int port = 19132;
|
||||||
|
try {
|
||||||
|
port = Integer.parseInt(serverAddress.split(":")[1]);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferPacket transferPacket = new TransferPacket();
|
||||||
|
transferPacket.setAddress(ip);
|
||||||
|
transferPacket.setPort(port);
|
||||||
|
session.sendUpstreamPacket(transferPacket);
|
||||||
|
})
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
160
src/main/java/org/geysermc/connect/extension/PacketHandler.java
Normal file
160
src/main/java/org/geysermc/connect/extension/PacketHandler.java
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 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.LoginPacket;
|
||||||
|
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.config.VirtualHostSection;
|
||||||
|
import org.geysermc.connect.extension.ui.UIHandler;
|
||||||
|
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.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(session.getGeyser(), 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) {
|
||||||
|
if (session.getAuthData() != null) {
|
||||||
|
geyserConnect.logger().info(Utils.displayName(session) + " has disconnected (" + reason + ")");
|
||||||
|
ServerManager.unloadServers(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketSignal handle(LoginPacket loginPacket) {
|
||||||
|
// Check to see if the server is full and we have a hard player cap
|
||||||
|
if (geyserConnect.config().hardPlayerLimit()) {
|
||||||
|
if (session.getGeyser().getSessionManager().size() >= session.getGeyser().getConfig().getMaxPlayers()) {
|
||||||
|
session.disconnect("disconnectionScreen.serverFull");
|
||||||
|
return PacketSignal.HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.handle(loginPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketSignal handle(SetLocalPlayerAsInitializedPacket packet) {
|
||||||
|
geyserConnect.logger().debug("Player initialized: " + Utils.displayName(session));
|
||||||
|
|
||||||
|
// Handle the virtual host if specified
|
||||||
|
VirtualHostSection vhost = geyserConnect.config().vhost();
|
||||||
|
if (vhost.enabled()) {
|
||||||
|
String domain = session.getClientData().getServerAddress().split(":")[0];
|
||||||
|
if (!domain.equals(vhost.baseDomain()) && domain.endsWith("." + vhost.baseDomain())) {
|
||||||
|
String address = "";
|
||||||
|
int port = 25565;
|
||||||
|
boolean online = true;
|
||||||
|
|
||||||
|
// Parse the address used
|
||||||
|
String[] domainParts = domain.replaceFirst("\\." + vhost.baseDomain() + "$", "").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 PacketSignal.HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the virtual host usage
|
||||||
|
geyserConnect.logger().info(Utils.displayName(session) + " is using virtualhost: " + address + ":" + port + (!online ? " (offline)" : ""));
|
||||||
|
|
||||||
|
// Send the player to the wanted server
|
||||||
|
Utils.sendToServer(session, originalPacketHandler, new Server(address, port, online, false, null, null, null));
|
||||||
|
|
||||||
|
return PacketSignal.HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle normal connections
|
||||||
|
if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) {
|
||||||
|
if (!session.getUpstream().isInitialized()) {
|
||||||
|
session.getUpstream().setInitialized(true);
|
||||||
|
|
||||||
|
// Load the players servers
|
||||||
|
ServerManager.loadServers(session);
|
||||||
|
|
||||||
|
UIHandler uiHandler = new UIHandler(session, originalPacketHandler);
|
||||||
|
uiHandler.initialiseSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
session.getGeyser().getScheduledThread().schedule(() -> session.sendUpstreamPacket(updateAttributesPacket), 500, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
return super.handle(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,38 +23,17 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @link https://github.com/GeyserMC/GeyserConnect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connect.ui;
|
package org.geysermc.connect.extension.config;
|
||||||
|
|
||||||
import lombok.Getter;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.geysermc.connect.extension.utils.Server;
|
||||||
|
|
||||||
@Getter
|
import java.util.List;
|
||||||
public enum FormID {
|
|
||||||
|
|
||||||
WELCOME,
|
public record Config(
|
||||||
MAIN,
|
@JsonProperty("welcome-file") String welcomeFile,
|
||||||
DIRECT_CONNECT(true),
|
@JsonProperty("hard-player-limit") boolean hardPlayerLimit,
|
||||||
LIST_SERVERS(true),
|
List<Server> servers,
|
||||||
EDIT_SERVERS(true),
|
@JsonProperty("custom-servers") CustomServersSection customServers,
|
||||||
SERVER_OPTIONS(true),
|
VirtualHostSection vhost) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 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.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,20 +23,14 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @link https://github.com/GeyserMC/GeyserConnect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connect.proxy;
|
package org.geysermc.connect.extension.config;
|
||||||
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.geysermc.connect.MasterServer;
|
import org.geysermc.connect.extension.storage.AbstractStorageManager;
|
||||||
import org.geysermc.connect.utils.Logger;
|
|
||||||
|
|
||||||
@Log4j2
|
public record CustomServersSection(
|
||||||
public class GeyserProxyLogger extends Logger {
|
boolean enabled,
|
||||||
|
int max,
|
||||||
/**
|
@JsonProperty("storage-type") AbstractStorageManager.StorageType storageType,
|
||||||
* Disable debug messages depending on config
|
MySQLConnectionSection mysql) {
|
||||||
*/
|
|
||||||
public void debug(String message) {
|
|
||||||
if (MasterServer.getInstance().getGeyserConnectConfig().getGeyser().isDebugMode())
|
|
||||||
super.debug(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,11 +23,12 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @link https://github.com/GeyserMC/GeyserConnect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connect;
|
package org.geysermc.connect.extension.config;
|
||||||
|
|
||||||
public class GeyserConnect {
|
public record MySQLConnectionSection(
|
||||||
|
String user,
|
||||||
public static void main(String[] args) {
|
String pass,
|
||||||
new MasterServer();
|
String database,
|
||||||
}
|
String host,
|
||||||
|
int port) {
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,20 +23,11 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @link https://github.com/GeyserMC/GeyserConnect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connect.proxy;
|
package org.geysermc.connect.extension.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Getter;
|
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
public record VirtualHostSection(
|
||||||
import java.nio.file.Paths;
|
boolean enabled,
|
||||||
|
@JsonProperty("base-domain") String baseDomain) {
|
||||||
@Getter
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public class GeyserProxyConfiguration extends GeyserJacksonConfiguration {
|
|
||||||
@Override
|
|
||||||
public Path getFloodgateKeyPath() {
|
|
||||||
return Paths.get(getFloodgateKeyFile());
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,22 +23,25 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @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.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import org.geysermc.connect.extension.GeyserConnect;
|
||||||
import org.geysermc.connect.MasterServer;
|
import org.geysermc.connect.extension.utils.Server;
|
||||||
import org.geysermc.connect.utils.Player;
|
import org.geysermc.connect.extension.utils.ServerManager;
|
||||||
import org.geysermc.connect.utils.Server;
|
import org.geysermc.connect.extension.utils.Utils;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class AbstractSQLStorageManager extends AbstractStorageManager {
|
public abstract class AbstractSQLStorageManager extends AbstractStorageManager {
|
||||||
private final ObjectMapper mapper = new ObjectMapper();
|
|
||||||
|
|
||||||
protected Connection connection;
|
protected Connection connection;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -50,7 +53,7 @@ public abstract class AbstractSQLStorageManager extends AbstractStorageManager {
|
||||||
createPlayersTable.executeUpdate("CREATE TABLE IF NOT EXISTS players (xuid VARCHAR(32), servers TEXT, PRIMARY KEY(xuid));");
|
createPlayersTable.executeUpdate("CREATE TABLE IF NOT EXISTS players (xuid VARCHAR(32), servers TEXT, PRIMARY KEY(xuid));");
|
||||||
}
|
}
|
||||||
} catch (ClassNotFoundException | SQLException e) {
|
} 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 +64,39 @@ public abstract class AbstractSQLStorageManager extends AbstractStorageManager {
|
||||||
try {
|
try {
|
||||||
connection.close();
|
connection.close();
|
||||||
} catch (SQLException exception) {
|
} catch (SQLException exception) {
|
||||||
MasterServer.getInstance().getLogger().error("Failed to close SQL connection", exception);
|
GeyserConnect.instance().logger().error("Failed to close SQL connection", exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveServers(Player player) {
|
public void saveServers(GeyserSession session) {
|
||||||
// replace into works on MySQL and SQLite
|
// replace into works on MySQL and SQLite
|
||||||
try (PreparedStatement updatePlayersServers = connection.prepareStatement("REPLACE INTO players(xuid, servers) VALUES(?, ?)")) {
|
try (PreparedStatement updatePlayersServers = connection.prepareStatement("REPLACE INTO players(xuid, servers) VALUES(?, ?)")) {
|
||||||
updatePlayersServers.setString(1, player.getAuthData().xuid());
|
updatePlayersServers.setString(1, session.getAuthData().xuid());
|
||||||
updatePlayersServers.setString(2, mapper.writeValueAsString(player.getServers()));
|
updatePlayersServers.setString(2, Utils.OBJECT_MAPPER.writeValueAsString(ServerManager.getServers(session)));
|
||||||
updatePlayersServers.executeUpdate();
|
updatePlayersServers.executeUpdate();
|
||||||
} catch (IOException | SQLException exception) {
|
} 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
|
@Override
|
||||||
public List<Server> loadServers(Player player) {
|
public List<Server> loadServers(GeyserSession session) {
|
||||||
List<Server> servers = new ArrayList<>();
|
List<Server> servers = new ArrayList<>();
|
||||||
|
|
||||||
try (PreparedStatement getPlayersServers = connection.prepareStatement("SELECT servers FROM players WHERE xuid=?")) {
|
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();
|
ResultSet rs = getPlayersServers.executeQuery();
|
||||||
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
List<Server> loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference<>() {
|
List<Server> loadedServers = Utils.OBJECT_MAPPER.readValue(rs.getString("servers"), new TypeReference<>() {
|
||||||
});
|
});
|
||||||
|
if (loadedServers != null) {
|
||||||
servers.addAll(loadedServers);
|
servers.addAll(loadedServers);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (IOException | SQLException exception) {
|
} 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;
|
return servers;
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,42 +23,51 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @link https://github.com/GeyserMC/GeyserConnect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connect.storage;
|
package org.geysermc.connect.extension.storage;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonValue;
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
import lombok.Getter;
|
import org.geysermc.connect.extension.utils.Server;
|
||||||
import org.geysermc.connect.utils.Player;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.connect.utils.Server;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AbstractStorageManager {
|
public class AbstractStorageManager {
|
||||||
|
|
||||||
public void setupStorage() { }
|
public void setupStorage() {
|
||||||
|
}
|
||||||
|
|
||||||
public void closeStorage() { }
|
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<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
|
||||||
public enum StorageType {
|
public enum StorageType {
|
||||||
JSON("json", JsonStorageManager.class),
|
JSON("json", JsonStorageManager.class),
|
||||||
SQLITE("sqlite", SQLiteStorageManager.class),
|
SQLITE("sqlite", SQLiteStorageManager.class),
|
||||||
MYSQL("mysql", MySQLStorageManager.class);
|
MYSQL("mysql", MySQLStorageManager.class);
|
||||||
|
|
||||||
@JsonValue
|
@JsonValue
|
||||||
private final String name;
|
private final String configName;
|
||||||
|
|
||||||
private final Class<? extends AbstractStorageManager> storageManager;
|
private final Class<? extends AbstractStorageManager> storageManager;
|
||||||
|
|
||||||
StorageType(String name, Class<? extends AbstractStorageManager> storageManager) {
|
StorageType(String configName, Class<? extends AbstractStorageManager> storageManager) {
|
||||||
this.name = name;
|
this.configName = configName;
|
||||||
this.storageManager = storageManager;
|
this.storageManager = storageManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String configName() {
|
||||||
|
return configName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends AbstractStorageManager> storageManager() {
|
||||||
|
return storageManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,10 +23,10 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @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.extension.utils.Server;
|
||||||
import org.geysermc.connect.utils.Server;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -38,12 +38,12 @@ public class DisabledStorageManager extends AbstractStorageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveServers(Player player) {
|
public void saveServers(GeyserSession session) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Server> loadServers(Player player) {
|
public List<Server> loadServers(GeyserSession session) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,46 +23,51 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @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.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import org.geysermc.connect.extension.GeyserConnect;
|
||||||
import org.geysermc.connect.utils.Player;
|
import org.geysermc.connect.extension.utils.Server;
|
||||||
import org.geysermc.connect.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.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class JsonStorageManager extends AbstractStorageManager {
|
public class JsonStorageManager extends AbstractStorageManager {
|
||||||
|
private Path dataFolder;
|
||||||
private final ObjectMapper mapper = new ObjectMapper();
|
|
||||||
private final Path dataFolder = Paths.get("players/");
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setupStorage() {
|
public void setupStorage() {
|
||||||
|
dataFolder = GeyserConnect.instance().dataFolder().resolve("players/");
|
||||||
if (!dataFolder.toFile().exists()) {
|
if (!dataFolder.toFile().exists()) {
|
||||||
dataFolder.toFile().mkdirs();
|
dataFolder.toFile().mkdirs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveServers(Player player) {
|
public void saveServers(GeyserSession session) {
|
||||||
try {
|
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) { }
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Server> loadServers(Player player) {
|
public List<Server> loadServers(GeyserSession session) {
|
||||||
List<Server> servers = new ArrayList<>();
|
List<Server> servers = new ArrayList<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<Server> loadedServers = mapper.readValue(dataFolder.resolve(player.getAuthData().xuid() + ".json").toFile(), new TypeReference<>(){});
|
List<Server> loadedServers = Utils.OBJECT_MAPPER.readValue(dataFolder.resolve(session.getAuthData().xuid() + ".json").toFile(), new TypeReference<>() {
|
||||||
|
});
|
||||||
|
if (loadedServers != null) {
|
||||||
servers.addAll(loadedServers);
|
servers.addAll(loadedServers);
|
||||||
} catch (IOException ignored) { }
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
return servers;
|
return servers;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,18 +23,19 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @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.extension.GeyserConnect;
|
||||||
import org.geysermc.connect.MasterServer;
|
import org.geysermc.connect.extension.config.MySQLConnectionSection;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public class MySQLStorageManager extends AbstractSQLStorageManager {
|
public class MySQLStorageManager extends AbstractSQLStorageManager {
|
||||||
@Override
|
@Override
|
||||||
protected void connectToDatabase() throws ClassNotFoundException, SQLException {
|
protected void connectToDatabase() throws ClassNotFoundException, SQLException {
|
||||||
Class.forName("com.mysql.cj.jdbc.Driver");
|
Class.forName("com.mysql.cj.jdbc.Driver");
|
||||||
GeyserConnectConfig.MySQLConnectionSection connectionInformation = MasterServer.getInstance().getGeyserConnectConfig().getCustomServers().getMysql();
|
MySQLConnectionSection connectionInformation = GeyserConnect.instance().config().customServers().mysql();
|
||||||
connection = DriverManager.getConnection("jdbc:mysql://" + connectionInformation.getHost() + ":" + connectionInformation.getPort() + "/" + connectionInformation.getDatabase(), connectionInformation.getUser(), connectionInformation.getPass());
|
connection = DriverManager.getConnection("jdbc:mysql://" + connectionInformation.host() + ":" + connectionInformation.port() + "/" + connectionInformation.database(), connectionInformation.user(), connectionInformation.pass());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,14 +23,17 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @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 {
|
public class SQLiteStorageManager extends AbstractSQLStorageManager {
|
||||||
@Override
|
@Override
|
||||||
protected void connectToDatabase() throws ClassNotFoundException, SQLException {
|
protected void connectToDatabase() throws ClassNotFoundException, SQLException {
|
||||||
Class.forName("org.sqlite.JDBC");
|
Class.forName("org.sqlite.JDBC");
|
||||||
connection = DriverManager.getConnection("jdbc:sqlite:players.db");
|
connection = DriverManager.getConnection("jdbc:sqlite:" + GeyserConnect.instance().dataFolder().resolve("players.db"));
|
||||||
}
|
}
|
||||||
}
|
}
|
310
src/main/java/org/geysermc/connect/extension/ui/UIHandler.java
Normal file
310
src/main/java/org/geysermc/connect/extension/ui/UIHandler.java
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 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.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 BedrockPacketHandler originalPacketHandler;
|
||||||
|
|
||||||
|
public UIHandler(GeyserSession session, BedrockPacketHandler originalPacketHandler) {
|
||||||
|
this.session = session;
|
||||||
|
this.originalPacketHandler = originalPacketHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
})
|
||||||
|
.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());
|
||||||
|
Utils.sendToServer(session, originalPacketHandler, 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);
|
||||||
|
Utils.sendToServer(session, originalPacketHandler, server);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,80 +23,30 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @link https://github.com/GeyserMC/GeyserConnect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connect.utils;
|
package org.geysermc.connect.extension.utils;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.cumulus.util.FormImage;
|
import org.geysermc.cumulus.util.FormImage;
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
import org.geysermc.geyser.api.network.AuthType;
|
||||||
import org.geysermc.geyser.api.network.RemoteServer;
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
|
|
||||||
@Getter
|
public record Server(
|
||||||
@AllArgsConstructor
|
String address,
|
||||||
@NoArgsConstructor
|
int port,
|
||||||
public class Server implements RemoteServer {
|
boolean online,
|
||||||
|
boolean bedrock,
|
||||||
private String address;
|
String name,
|
||||||
private int port = -1;
|
String imageUrl,
|
||||||
private boolean online = true;
|
ServerCategory category
|
||||||
private boolean bedrock = false;
|
) implements RemoteServer {
|
||||||
private String name = null;
|
private int defaultPort() {
|
||||||
private String imageUrl = null;
|
return bedrock ? 19132 : 25565;
|
||||||
private ServerCategory category = null;
|
|
||||||
|
|
||||||
public Server(String address) {
|
|
||||||
this(address, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public int port() {
|
public int port() {
|
||||||
return port;
|
return port < 0 ? defaultPort() : port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -104,6 +54,15 @@ public class Server implements RemoteServer {
|
||||||
return this.online ? AuthType.ONLINE : AuthType.OFFLINE;
|
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
|
@Override
|
||||||
public String minecraftVersion() {
|
public String minecraftVersion() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -113,4 +72,8 @@ public class Server implements RemoteServer {
|
||||||
public int protocolVersion() {
|
public int protocolVersion() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String title() {
|
||||||
|
return name != null ? name : address + (port() != defaultPort() ? ":" + port() : "");
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,11 +23,8 @@
|
||||||
* @link https://github.com/GeyserMC/GeyserConnect
|
* @link https://github.com/GeyserMC/GeyserConnect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.connect.utils;
|
package org.geysermc.connect.extension.utils;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
public enum ServerCategory {
|
public enum ServerCategory {
|
||||||
OFFICIAL("Official"),
|
OFFICIAL("Official"),
|
||||||
GEYSER("Geyser"),
|
GEYSER("Geyser"),
|
||||||
|
@ -38,4 +35,8 @@ public enum ServerCategory {
|
||||||
ServerCategory(String title) {
|
ServerCategory(String title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String title() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 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.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) {
|
||||||
|
if (getServers(session) == null) return;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
114
src/main/java/org/geysermc/connect/extension/utils/Utils.java
Normal file
114
src/main/java/org/geysermc/connect/extension/utils/Utils.java
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2023 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.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
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.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() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToServer(GeyserSession session, BedrockPacketHandler originalPacketHandler, 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);
|
||||||
|
|
||||||
|
// If we are offline then authenticate
|
||||||
|
// Online mode is handled by geyser code
|
||||||
|
if (!server.online()) {
|
||||||
|
session.authenticate(session.getAuthData().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hand back to core geyser
|
||||||
|
SetLocalPlayerAsInitializedPacket initializedPacket = new SetLocalPlayerAsInitializedPacket();
|
||||||
|
initializedPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||||
|
originalPacketHandler.handle(initializedPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,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,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,38 +2,13 @@
|
||||||
# GeyserConnect Configuration File
|
# 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
|
# 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
|
# This is loaded live so will update without a server restart
|
||||||
welcome-file: welcome.txt
|
welcome-file: welcome.txt
|
||||||
|
|
||||||
# Config for the Geyser listener
|
# Enabled hard max player limit
|
||||||
geyser:
|
# This will kick players if the server is full
|
||||||
# If password authentication should be allowed in online mode.
|
hard-player-limit: false
|
||||||
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
|
# A global list of servers sent to all clients
|
||||||
servers:
|
servers:
|
||||||
|
@ -42,49 +17,49 @@ servers:
|
||||||
port: 19132
|
port: 19132
|
||||||
bedrock: true
|
bedrock: true
|
||||||
category: OFFICIAL
|
category: OFFICIAL
|
||||||
imageUrl: 'https://i.imgur.com/VQJW6XG.png'
|
imageUrl: 'https://i.imgur.com/myXmr7B.png'
|
||||||
|
|
||||||
- name: CubeCraft
|
- name: CubeCraft
|
||||||
address: 94.23.159.81
|
address: 94.23.159.81
|
||||||
port: 19132
|
port: 19132
|
||||||
bedrock: true
|
bedrock: true
|
||||||
category: OFFICIAL
|
category: OFFICIAL
|
||||||
imageUrl: 'https://www.cubecraft.net/attachments/q3pmrsod_400x400-png.53219/'
|
imageUrl: 'https://i.imgur.com/f83ZeDS.jpg'
|
||||||
|
|
||||||
- name: Galaxite
|
- name: Galaxite
|
||||||
address: 54.39.243.199
|
address: 54.39.243.199
|
||||||
port: 19132
|
port: 19132
|
||||||
bedrock: true
|
bedrock: true
|
||||||
category: OFFICIAL
|
category: OFFICIAL
|
||||||
imageUrl: 'https://pbs.twimg.com/profile_images/1275867042583896066/UMPF5nTM_400x400.jpg'
|
imageUrl: 'https://i.imgur.com/PEkLROT.jpg'
|
||||||
|
|
||||||
- name: Lifeboat Network
|
- name: Lifeboat Network
|
||||||
address: 63.143.54.198
|
address: 63.143.54.198
|
||||||
port: 19132
|
port: 19132
|
||||||
bedrock: true
|
bedrock: true
|
||||||
category: OFFICIAL
|
category: OFFICIAL
|
||||||
imageUrl: 'https://lbsg.net/wp-content/uploads/2017/06/lifeboat-square.png'
|
imageUrl: 'https://i.imgur.com/GjsxhCI.png'
|
||||||
|
|
||||||
- name: Mineplex
|
- name: Mineplex
|
||||||
address: 108.178.12.38
|
address: 108.178.12.38
|
||||||
port: 19132
|
port: 19132
|
||||||
bedrock: true
|
bedrock: true
|
||||||
category: OFFICIAL
|
category: OFFICIAL
|
||||||
imageUrl: 'https://www.mineplex.com/assets/www-mp/img/footer/footer_smalllogo.png'
|
imageUrl: 'https://i.imgur.com/xB6yncS.png'
|
||||||
|
|
||||||
- name: MineVille
|
- name: MineVille
|
||||||
address: 52.234.130.255
|
address: 52.234.130.255
|
||||||
port: 19132
|
port: 19132
|
||||||
bedrock: true
|
bedrock: true
|
||||||
category: OFFICIAL
|
category: OFFICIAL
|
||||||
imageUrl: 'https://pbs.twimg.com/profile_images/1332400307050045441/MHQvGEUP_400x400.jpg'
|
imageUrl: 'https://i.imgur.com/yqrfMhP.jpg'
|
||||||
|
|
||||||
- name: Pixel Paradise
|
- name: Pixel Paradise
|
||||||
address: 40.87.84.59
|
address: 40.87.84.59
|
||||||
port: 19132
|
port: 19132
|
||||||
bedrock: true
|
bedrock: true
|
||||||
category: OFFICIAL
|
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
|
- name: Official Geyser Test Server
|
||||||
address: test.geysermc.org
|
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