mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Merge pull request #397 from GeyserMC/development
* Started using plugin messages internally. Started subcommand refactor * Removed config holder and changed a few things * Small changes to platforms and injectors * Changed how post-enable messages work internally + minor other changes * Deprecate handshake handlers and player properties * Added auto-binding * Switched event library * Fixed a circular dependency issue when a locale couldn't be found * Simplified plugin message channel logic * Switched to Hikari for MySQL * SkinApplier now only applies a skin if a player doesn't already have one (#330) * SkinApplier now only applies a skin if a player doesn't already have one * add `hasSkin` method to SkinApplier and check for exising skins before overwriting * remove the use of Streams and Optionals * correct delay in SpigotSkinApplier to use ticks instead of milliseconds * Minor changes Co-authored-by: Tim203 <mctim203@gmail.com> * Updated to the latest events version. Share a thread pool * News needs a scheduled executor * Bumped version to 2.1.1 and publish entire java component * Fix checking for existing skins on Spigot (#362) * Close all skin sockets on shutdown (#363) * Only apply skin when someone doesn't have a skin applied already (#365) Instead of aplying only when the player has a skin apply only when it doesn't have a skin * Use UTF-8 for language files (#366) Languages like ru_RU don't work because they have specific characters, and your files are encoded in UTF-8, but it reads them as ISO 8859-1 * Fixed building Floodgate and added a version subcommand * Remove Blossom and use templates * Use weak references for injected Netty channels * Use newSetFromMap * Added a SkinApplyEvent that can cancel/edit the to be applied skin * Use a common ScheduledThreadPool and make the player map concurrent * Don't try to remove all injector references * Add branch name when not master, simplify publish, use GitHub Actions And updated Gradle * Remove Jenkinsfile * Retrieve version from gradle.properties * Let's see if the setup-java action's cache is helpful & fixed building * Add publishing to downloads site (#385) * Add publishing to downloads site * Only publish to Downloads API when branch is master and status success --------- Co-authored-by: Tim203 <mctim203@gmail.com> * Notify Discord after building & fixed building * Temporarily restore Jenkinsfile to give people time to switch * Localize floodgate.core.not_linked (#383) * Localize floodgate.core.not_linked * Update languages submodule * not_linked string has changed slightly --------- Co-authored-by: Tim203 <mctim203@gmail.com> * Update bstats dependency * Only publish and notify Discord on push if running inside this repository (#387) * Only attempt publishing if running in GeyserMC/Floodgate * Also restrict the discord notification * Fix injection of LanguageManager/Slf4jFloodgate on Velocity (circular proxy) (#388) * Made various changes to the build action * Update maven deploy location * Relocate MySQL database extension dependencies * Shutdown metrics on platform shutdown (#386) * Shutdown metrics on platorm shutdown * Listen to event instead of hardcoding it * Annotate Metrics as a Listener * Use temporary bStats fork to properly shutdown bStats * Use bstats-base dependency (instead of the whole project I guess?) * Formatting change --------- Co-authored-by: Tim203 <mctim203@gmail.com> --------- Co-authored-by: Alex <me@teamplayer.io> Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> Co-authored-by: mastermc05 <63639746+mastermc05@users.noreply.github.com> Co-authored-by: rtm516 <rtm516@users.noreply.github.com>
This commit is contained in:
63
.github/workflows/build.yml
vendored
Normal file
63
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Build
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository and submodules
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: temurin
|
||||
|
||||
- name: Build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: build
|
||||
cache-read-only: ${{ github.ref_name != 'master' && github.ref_name != 'development' }}
|
||||
|
||||
- name: Publish to Maven Repository
|
||||
if: ${{ github.repository == 'GeyserMC/Floodgate' }}
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
||||
with:
|
||||
arguments: publish
|
||||
cache-read-only: ${{ github.ref_name != 'master' && github.ref_name != 'development' }}
|
||||
|
||||
- name: Publish to Downloads API
|
||||
if: ${{ github.ref_name == 'master' && github.repository == 'GeyserMC/Floodgate' }}
|
||||
shell: bash
|
||||
env:
|
||||
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
|
||||
DOWNLOADS_PRIVATE_KEY: ${{ secrets.DOWNLOADS_PRIVATE_KEY }}
|
||||
DOWNLOADS_SERVER_IP: ${{ secrets.DOWNLOADS_SERVER_IP }}
|
||||
run: |
|
||||
# Save the private key to a file
|
||||
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
|
||||
chmod 600 id_ecdsa
|
||||
|
||||
# Get the version from gradle.properties
|
||||
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
|
||||
|
||||
# Copy over artifacts
|
||||
scp -B -o StrictHostKeyChecking=no -i id_ecdsa **/build/libs/floodgate-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/files/
|
||||
|
||||
# Remove un-needed artifacts
|
||||
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP rm ~/files/floodgate-parent-*.jar ~/files/floodgate-api.jar ~/files/floodgate-core.jar
|
||||
|
||||
# Run the build script
|
||||
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh floodgate $version $GITHUB_RUN_NUMBER $GITHUB_SHA
|
||||
|
||||
- name: Notify Discord
|
||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Floodgate' }}
|
||||
uses: Tim203/actions-git-discord-webhook@main
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
97
.gitignore
vendored
97
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
# Created by https://www.gitignore.io/api/git,java,maven,eclipse,netbeans,jetbrains+all
|
||||
# Edit at https://www.gitignore.io/?templates=git,java,maven,eclipse,netbeans,jetbrains+all
|
||||
# Created by https://www.gitignore.io/api/git,java,gradle,eclipse,netbeans,jetbrains+all
|
||||
# Edit at https://www.gitignore.io/?templates=git,gradle,maven,eclipse,netbeans,jetbrains+all
|
||||
|
||||
### Eclipse ###
|
||||
.metadata
|
||||
@@ -52,22 +52,19 @@ local.properties
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
.apt_generated_test/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
# Uncomment this line if you wish to ignore the project description file.
|
||||
# Typically, this file would be tracked if it contains build/dependency configurations:
|
||||
#.project
|
||||
|
||||
### Eclipse Patch ###
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated
|
||||
|
||||
# Spring Boot Tooling
|
||||
.sts4-cache/
|
||||
|
||||
### Git ###
|
||||
@@ -109,9 +106,10 @@ local.properties
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
### JetBrains+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
@@ -121,6 +119,9 @@ hs_err_pid*
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
@@ -141,11 +142,14 @@ hs_err_pid*
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
.idea/artifacts
|
||||
.idea/compiler.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/modules.xml
|
||||
.idea/*.iml
|
||||
.idea/modules
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
@@ -168,6 +172,9 @@ atlassian-ide-plugin.xml
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
@@ -181,32 +188,13 @@ fabric.properties
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### JetBrains+all Patch ###
|
||||
# Ignores the whole .idea folder and all .iml files
|
||||
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||
# Ignore everything but code style settings and run configurations
|
||||
# that are supposed to be shared within teams.
|
||||
|
||||
.idea/
|
||||
.idea/*
|
||||
|
||||
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||
|
||||
*.iml
|
||||
modules.xml
|
||||
.idea/misc.xml
|
||||
*.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
!.idea/codeStyles
|
||||
!.idea/runConfigurations
|
||||
|
||||
### NetBeans ###
|
||||
**/nbproject/private/
|
||||
@@ -218,8 +206,29 @@ dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
# End of https://www.gitignore.io/api/git,java,maven,eclipse,netbeans,jetbrains+all
|
||||
gradle/
|
||||
**/.gradle/
|
||||
### 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
|
||||
|
||||
# End of https://www.gitignore.io/api/git,java,gradle,eclipse,netbeans,jetbrains+all
|
||||
|
||||
/core/src/main/resources/languages/
|
||||
|
||||
82
Jenkinsfile
vendored
82
Jenkinsfile
vendored
@@ -26,87 +26,5 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Deploy') {
|
||||
when {
|
||||
anyOf {
|
||||
branch "master"
|
||||
branch "development"
|
||||
}
|
||||
}
|
||||
|
||||
steps {
|
||||
rtGradleDeployer(
|
||||
id: "GRADLE_DEPLOYER",
|
||||
serverId: "opencollab-artifactory",
|
||||
releaseRepo: "maven-releases",
|
||||
snapshotRepo: "maven-snapshots"
|
||||
)
|
||||
rtGradleResolver(
|
||||
id: "GRADLE_RESOLVER",
|
||||
serverId: "opencollab-artifactory"
|
||||
)
|
||||
rtGradleRun(
|
||||
usesPlugin: true,
|
||||
tool: 'Gradle 7',
|
||||
rootDir: "",
|
||||
useWrapper: true,
|
||||
buildFile: 'build.gradle.kts',
|
||||
tasks: 'artifactoryPublish',
|
||||
deployerId: "GRADLE_DEPLOYER",
|
||||
resolverId: "GRADLE_RESOLVER"
|
||||
)
|
||||
rtPublishBuildInfo(
|
||||
serverId: "opencollab-artifactory"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
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.opencollab.dev/job/GeyserMC/job/Floodgate)", footer: 'Open Collaboration Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK
|
||||
}
|
||||
}
|
||||
success {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'master') {
|
||||
build propagate: false, wait: false, job: 'GeyserMC/Floodgate-Fabric/master', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
ap/build.gradle.kts
Normal file
0
ap/build.gradle.kts
Normal file
@@ -23,37 +23,16 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.config;
|
||||
package org.geysermc.floodgate.ap;
|
||||
|
||||
import org.geysermc.floodgate.util.FloodgateInfoHolder;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
|
||||
public class FloodgateConfigHolder {
|
||||
private FloodgateConfig config;
|
||||
|
||||
public boolean has() {
|
||||
return config != null;
|
||||
}
|
||||
|
||||
public boolean isProxy() {
|
||||
return config instanceof ProxyFloodgateConfig;
|
||||
}
|
||||
|
||||
public FloodgateConfig get() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public ProxyFloodgateConfig getAsProxy() {
|
||||
return getAs();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends FloodgateConfig> T getAs() {
|
||||
return (T) config;
|
||||
}
|
||||
|
||||
public void set(FloodgateConfig config) {
|
||||
this.config = config;
|
||||
// for Geyser dump
|
||||
FloodgateInfoHolder.setConfig(config);
|
||||
@SupportedAnnotationTypes("*")
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
public class AutoBindProcessor extends ClassProcessor {
|
||||
public AutoBindProcessor() {
|
||||
super("org.geysermc.floodgate.util.AutoBind");
|
||||
}
|
||||
}
|
||||
221
ap/src/main/java/org/geysermc/floodgate/ap/ClassProcessor.java
Normal file
221
ap/src/main/java/org/geysermc/floodgate/ap/ClassProcessor.java
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.ap;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
/*
|
||||
* Copied from Geyser
|
||||
*/
|
||||
public class ClassProcessor extends AbstractProcessor {
|
||||
private final String annotationClassName;
|
||||
|
||||
private Path outputPath;
|
||||
|
||||
private final Set<String> locations = new HashSet<>();
|
||||
|
||||
public ClassProcessor(String annotationClassName) {
|
||||
this.annotationClassName = annotationClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||
super.init(processingEnv);
|
||||
|
||||
processingEnv.getMessager().printMessage(
|
||||
Kind.NOTE, "Initializing processor " + annotationClassName
|
||||
);
|
||||
|
||||
String outputFile = processingEnv.getOptions().get("metadataOutputFile");
|
||||
if (outputFile != null && !outputFile.isEmpty()) {
|
||||
outputPath = Paths.get(outputFile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
if (roundEnv.processingOver()) {
|
||||
if (!roundEnv.errorRaised()) {
|
||||
complete();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!contains(annotations, annotationClassName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Element element : roundEnv.getRootElements()) {
|
||||
if (element.getKind() != ElementKind.CLASS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!contains(element.getAnnotationMirrors(), annotationClassName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TypeElement typeElement = (TypeElement) element;
|
||||
locations.add(typeElement.getQualifiedName().toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean contains(Collection<? extends TypeElement> elements, String className) {
|
||||
if (elements.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (TypeElement element : elements) {
|
||||
if (element.getQualifiedName().contentEquals(className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean contains(List<? extends AnnotationMirror> elements, String className) {
|
||||
if (elements.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AnnotationMirror element : elements) {
|
||||
if (element.getAnnotationType().toString().equals(className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
// Read existing annotation list and verify each class still has this annotation
|
||||
try (BufferedReader reader = createReader()) {
|
||||
if (reader != null) {
|
||||
reader.lines().forEach(canonicalName -> {
|
||||
if (!locations.contains(canonicalName)) {
|
||||
|
||||
TypeElement element =
|
||||
processingEnv.getElementUtils().getTypeElement(canonicalName);
|
||||
|
||||
if (element != null && element.getKind() == ElementKind.CLASS &&
|
||||
contains(element.getAnnotationMirrors(), annotationClassName)) {
|
||||
locations.add(canonicalName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (!locations.isEmpty()) {
|
||||
try (BufferedWriter writer = createWriter()) {
|
||||
for (String location : locations) {
|
||||
writer.write(location);
|
||||
writer.newLine();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
processingEnv.getMessager().printMessage(Kind.NOTE,
|
||||
"Did not find any classes annotated with " + annotationClassName
|
||||
);
|
||||
}
|
||||
|
||||
processingEnv.getMessager().printMessage(
|
||||
Kind.NOTE, "Completed processing for " + annotationClassName
|
||||
);
|
||||
}
|
||||
|
||||
private BufferedReader createReader() throws IOException {
|
||||
if (outputPath != null) {
|
||||
processingEnv.getMessager().printMessage(Kind.NOTE,
|
||||
"Reading existing " + annotationClassName + " list from " + outputPath
|
||||
);
|
||||
|
||||
return Files.newBufferedReader(outputPath);
|
||||
}
|
||||
|
||||
FileObject obj = processingEnv.getFiler().getResource(
|
||||
StandardLocation.CLASS_OUTPUT, "", annotationClassName
|
||||
);
|
||||
|
||||
if (obj != null) {
|
||||
processingEnv.getMessager().printMessage(
|
||||
Kind.NOTE,
|
||||
"Reading existing " + annotationClassName + " list from " + obj.toUri()
|
||||
);
|
||||
|
||||
try {
|
||||
return new BufferedReader(obj.openReader(false));
|
||||
} catch (NoSuchFileException ignored) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private BufferedWriter createWriter() throws IOException {
|
||||
if (outputPath != null) {
|
||||
processingEnv.getMessager().printMessage(
|
||||
Kind.NOTE, "Writing " + annotationClassName + " to " + outputPath
|
||||
);
|
||||
|
||||
return Files.newBufferedWriter(outputPath);
|
||||
}
|
||||
|
||||
FileObject obj = processingEnv.getFiler().createResource(
|
||||
StandardLocation.CLASS_OUTPUT, "", annotationClassName
|
||||
);
|
||||
|
||||
processingEnv.getMessager().printMessage(
|
||||
Kind.NOTE, "Writing " + annotationClassName + " to " + obj.toUri()
|
||||
);
|
||||
|
||||
return new BufferedWriter(obj.openWriter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.geysermc.floodgate.ap.AutoBindProcessor
|
||||
@@ -1,6 +1,7 @@
|
||||
dependencies {
|
||||
api("org.geysermc", "common", Versions.geyserVersion)
|
||||
api("org.geysermc.cumulus", "cumulus", Versions.cumulusVersion)
|
||||
api("org.geysermc.event", "events", Versions.eventsVersion)
|
||||
|
||||
compileOnly("io.netty", "netty-transport", Versions.nettyVersion)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.unsafe.Unsafe;
|
||||
@@ -148,6 +149,10 @@ public interface FloodgateApi {
|
||||
*/
|
||||
CompletableFuture<String> getGamertagFor(long xuid);
|
||||
|
||||
default FloodgateEventBus getEventBus() {
|
||||
return InstanceHolder.getEventBus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance that manages all the linking.
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,7 @@ package org.geysermc.floodgate.api;
|
||||
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
@@ -35,6 +36,7 @@ import org.geysermc.floodgate.api.packet.PacketHandlers;
|
||||
public final class InstanceHolder {
|
||||
@Getter private static FloodgateApi api;
|
||||
@Getter private static PlayerLink playerLink;
|
||||
@Getter private static FloodgateEventBus eventBus;
|
||||
|
||||
@Getter private static PlatformInjector injector;
|
||||
@Getter private static PacketHandlers packetHandlers;
|
||||
@@ -44,11 +46,12 @@ public final class InstanceHolder {
|
||||
public static boolean set(
|
||||
FloodgateApi floodgateApi,
|
||||
PlayerLink link,
|
||||
FloodgateEventBus floodgateEventBus,
|
||||
PlatformInjector platformInjector,
|
||||
PacketHandlers packetHandlers,
|
||||
HandshakeHandlers handshakeHandlers,
|
||||
UUID key) {
|
||||
|
||||
UUID key
|
||||
) {
|
||||
if (storedKey != null) {
|
||||
if (!storedKey.equals(key)) {
|
||||
return false;
|
||||
@@ -59,14 +62,10 @@ public final class InstanceHolder {
|
||||
|
||||
api = floodgateApi;
|
||||
playerLink = link;
|
||||
eventBus = floodgateEventBus;
|
||||
injector = platformInjector;
|
||||
InstanceHolder.packetHandlers = packetHandlers;
|
||||
InstanceHolder.handshakeHandlers = handshakeHandlers;
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends FloodgateApi> T castApi(Class<T> cast) {
|
||||
return (T) api;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.api.event;
|
||||
|
||||
import org.geysermc.event.bus.EventBus;
|
||||
|
||||
public interface FloodgateEventBus extends EventBus<Object, FloodgateSubscriber<?>> {
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.api.event;
|
||||
|
||||
import org.geysermc.event.subscribe.Subscriber;
|
||||
|
||||
public interface FloodgateSubscriber<T> extends Subscriber<T> {
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.api.event.skin;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.common.returnsreceiver.qual.This;
|
||||
import org.geysermc.event.Cancellable;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
/**
|
||||
* An event that's fired when Floodgate receives a player skin. The event will be cancelled by
|
||||
* default when hasSkin is true, as Floodgate by default only applies skins when the player has no
|
||||
* skin applied yet.
|
||||
*/
|
||||
public interface SkinApplyEvent extends Cancellable {
|
||||
/**
|
||||
* Returns the player that will receive the skin.
|
||||
*/
|
||||
@NonNull FloodgatePlayer player();
|
||||
|
||||
/**
|
||||
* Returns the skin texture currently applied to the player.
|
||||
*/
|
||||
@Nullable SkinData currentSkin();
|
||||
|
||||
/**
|
||||
* Returns the skin texture to be applied to the player.
|
||||
*/
|
||||
@NonNull SkinData newSkin();
|
||||
|
||||
/**
|
||||
* Sets the skin texture to be applied to the player
|
||||
*
|
||||
* @param skinData the skin to apply
|
||||
* @return this
|
||||
*/
|
||||
@This SkinApplyEvent newSkin(@NonNull SkinData skinData);
|
||||
|
||||
interface SkinData {
|
||||
/**
|
||||
* Returns the value of the skin texture.
|
||||
*/
|
||||
@NonNull String value();
|
||||
|
||||
/**
|
||||
* Returns the signature of the skin texture.
|
||||
*/
|
||||
@NonNull String signature();
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
* server. Note that at the time I'm writing this that the HandshakeData is created after requesting
|
||||
* the player link. So the link is present here, if applicable.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface HandshakeData {
|
||||
/**
|
||||
* Returns the Channel holding the connection between the client and the server.
|
||||
|
||||
@@ -34,6 +34,7 @@ package org.geysermc.floodgate.api.handshake;
|
||||
* HandshakeData#isFloodgatePlayer()} will be false and Floodgate related methods will return null
|
||||
* for Java players
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface HandshakeHandler {
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
|
||||
package org.geysermc.floodgate.api.handshake;
|
||||
|
||||
/**
|
||||
* @deprecated This system has been deprecated and will not be available in the new API that will be
|
||||
* introduced when Geyser will include Floodgate (and thus will have some common base API).
|
||||
* <br>
|
||||
* It might be replaced with an event (probably internal), but that isn't certain yet.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface HandshakeHandlers {
|
||||
/**
|
||||
* Register a custom handshake handler. This can be used to check and edit the player during the
|
||||
|
||||
@@ -37,10 +37,9 @@ public interface PlatformInjector {
|
||||
* Injects the server connection. This will allow various addons (like getting the Floodgate
|
||||
* data and debug mode) to work.
|
||||
*
|
||||
* @return true if the connection has successfully been injected
|
||||
* @throws Exception if something went wrong while injecting the server connection
|
||||
* @throws Exception if the platform couldn't be injected
|
||||
*/
|
||||
boolean inject() throws Exception;
|
||||
void inject() throws Exception;
|
||||
|
||||
/**
|
||||
* Some platforms may not be able to remove their injection process. If so, this method will
|
||||
@@ -56,10 +55,9 @@ public interface PlatformInjector {
|
||||
* Removes the injection from the server. Please note that this function should only be used
|
||||
* internally (on plugin shutdown). This method will also remove every added addon.
|
||||
*
|
||||
* @return true if the injection has successfully been removed
|
||||
* @throws Exception if something went wrong while removing the injection
|
||||
* @throws Exception if the platform injection could not be removed
|
||||
*/
|
||||
boolean removeInjection() throws Exception;
|
||||
void removeInjection() throws Exception;
|
||||
|
||||
/**
|
||||
* If the server connection is currently injected.
|
||||
|
||||
@@ -34,10 +34,6 @@ public enum LinkRequestResult {
|
||||
* An unknown error encountered while creating / verifying the link request.
|
||||
*/
|
||||
UNKNOWN_ERROR,
|
||||
/**
|
||||
* @deprecated this result isn't used. Instead the link code is returned
|
||||
*/
|
||||
REQUEST_CREATED,
|
||||
/**
|
||||
* The specified bedrock username is already linked to a Java account.
|
||||
*/
|
||||
|
||||
@@ -80,18 +80,7 @@ public interface FloodgateLogger {
|
||||
void trace(String message, Object... args);
|
||||
|
||||
/**
|
||||
* Enables debug mode for the Floodgate logger.
|
||||
*/
|
||||
void enableDebug();
|
||||
|
||||
/**
|
||||
* Disables debug mode for the Floodgate logger. Debug messages can still be sent after running
|
||||
* this method, but they will be hidden from the console.
|
||||
*/
|
||||
void disableDebug();
|
||||
|
||||
/**
|
||||
* Returns if debugging is enabled
|
||||
* Returns true if debugging is enabled
|
||||
*/
|
||||
boolean isDebug();
|
||||
}
|
||||
|
||||
@@ -143,20 +143,28 @@ public interface FloodgatePlayer {
|
||||
return FloodgateApi.getInstance().transferPlayer(getCorrectUniqueId(), address, port);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
boolean hasProperty(PropertyKey key);
|
||||
|
||||
@Deprecated
|
||||
boolean hasProperty(String key);
|
||||
|
||||
@Deprecated
|
||||
<T> T getProperty(PropertyKey key);
|
||||
|
||||
@Deprecated
|
||||
<T> T getProperty(String key);
|
||||
|
||||
@Deprecated
|
||||
<T> T removeProperty(PropertyKey key);
|
||||
|
||||
@Deprecated
|
||||
<T> T removeProperty(String key);
|
||||
|
||||
@Deprecated
|
||||
<T> T addProperty(PropertyKey key, Object value);
|
||||
|
||||
@Deprecated
|
||||
<T> T addProperty(String key, Object value);
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ package org.geysermc.floodgate.api.player;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Deprecated
|
||||
public class PropertyKey {
|
||||
/**
|
||||
* Socket Address returns the InetSocketAddress of the Bedrock player
|
||||
|
||||
@@ -9,9 +9,10 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.kyori", "indra-common", "2.0.6")
|
||||
implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1")
|
||||
implementation("net.kyori", "indra-common", "3.0.1")
|
||||
implementation("net.kyori", "indra-git", "3.0.1")
|
||||
implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.1")
|
||||
implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext", "gradle-idea-ext", "1.1.7")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
|
||||
@@ -26,14 +26,15 @@
|
||||
object Versions {
|
||||
const val geyserVersion = "2.0.7-SNAPSHOT"
|
||||
const val cumulusVersion = "1.1.1"
|
||||
const val eventsVersion = "1.0-SNAPSHOT"
|
||||
const val configUtilsVersion = "1.0-SNAPSHOT"
|
||||
const val spigotVersion = "1.13-R0.1-SNAPSHOT"
|
||||
const val fastutilVersion = "8.5.3"
|
||||
const val guiceVersion = "5.0.1"
|
||||
const val guiceVersion = "5.1.0"
|
||||
const val nettyVersion = "4.1.49.Final"
|
||||
const val snakeyamlVersion = "1.28"
|
||||
const val cloudVersion = "1.5.0"
|
||||
const val bstatsVersion = "3.0.0"
|
||||
const val bstatsVersion = "d2fbbd6823"
|
||||
|
||||
const val javaWebsocketVersion = "1.5.2"
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.kotlin.dsl.the
|
||||
|
||||
fun Project.isSnapshot(): Boolean =
|
||||
version.toString().endsWith("-SNAPSHOT")
|
||||
|
||||
fun Project.fullVersion(): String {
|
||||
var version = version.toString()
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
@@ -42,14 +39,19 @@ fun Project.fullVersion(): String {
|
||||
fun Project.lastCommitHash(): String? =
|
||||
the<IndraGitExtension>().commit()?.name?.substring(0, 7)
|
||||
|
||||
// retrieved from https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
|
||||
// some properties might be specific to Jenkins
|
||||
fun Project.branchName(): String =
|
||||
System.getenv("GIT_BRANCH") ?: "local/dev"
|
||||
fun Project.buildNumber(): Int =
|
||||
Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1")
|
||||
the<IndraGitExtension>().branchName() ?: System.getenv("BRANCH_NAME") ?: "local/dev"
|
||||
|
||||
fun Project.buildNumberAsString(): String =
|
||||
fun Project.shouldAddBranchName(): Boolean =
|
||||
System.getenv("IGNORE_BRANCH")?.toBoolean() ?: (branchName() !in arrayOf("master", "local/dev"))
|
||||
|
||||
fun Project.versionWithBranchName(): String =
|
||||
branchName().replace(Regex("[^0-9A-Za-z-_]"), "-") + '-' + version
|
||||
|
||||
fun buildNumber(): Int =
|
||||
System.getenv("BUILD_NUMBER")?.let { Integer.parseInt(it) } ?: -1
|
||||
|
||||
fun buildNumberAsString(): String =
|
||||
buildNumber().takeIf { it != -1 }?.toString() ?: "??"
|
||||
|
||||
val providedDependencies = mutableMapOf<String, MutableSet<Pair<String, Any>>>()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
// id("net.ltgt.errorprone")
|
||||
id("net.kyori.indra")
|
||||
id("net.kyori.indra.git")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,21 @@ dependencies {
|
||||
compileOnly("org.checkerframework", "checker-qual", Versions.checkerQual)
|
||||
}
|
||||
|
||||
indra {
|
||||
github("GeyserMC", "Floodgate") {
|
||||
ci(true)
|
||||
issues(true)
|
||||
scm(true)
|
||||
}
|
||||
mitLicense()
|
||||
|
||||
javaVersions {
|
||||
// without toolchain & strictVersion sun.misc.Unsafe won't be found
|
||||
minimumToolchain(8)
|
||||
strictVersions(true)
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json")) {
|
||||
@@ -22,14 +37,4 @@ tasks {
|
||||
)
|
||||
}
|
||||
}
|
||||
compileJava {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
withSourcesJar()
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseModel
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
import org.jetbrains.gradle.ext.ProjectSettings
|
||||
import org.jetbrains.gradle.ext.TaskTriggersConfig
|
||||
|
||||
plugins {
|
||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||
}
|
||||
|
||||
registerGenerateTemplateTasks()
|
||||
|
||||
fun Project.registerGenerateTemplateTasks() {
|
||||
// main and test
|
||||
extensions.getByType<SourceSetContainer>().all {
|
||||
val javaDestination = layout.buildDirectory.dir("generated/sources/templates/$name")
|
||||
val javaSrcDir = layout.projectDirectory.dir("src/$name/templates")
|
||||
val javaGenerateTask = tasks.register<GenerateSourceTemplates>(
|
||||
getTaskName("template", "sources")
|
||||
) {
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
from(javaSrcDir)
|
||||
into(javaDestination)
|
||||
filter<ReplaceTokens>("tokens" to replacements())
|
||||
}
|
||||
java.srcDir(javaGenerateTask.map { it.outputs })
|
||||
|
||||
val resourcesDestination = layout.buildDirectory.dir("generated/resources/templates/$name")
|
||||
val resourcesSrcDir = layout.projectDirectory.dir("src/$name/resourceTemplates")
|
||||
val resourcesGenerateTask = tasks.register<GenerateResourceTemplates>(
|
||||
getTaskName("template", "resources")
|
||||
) {
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
from(resourcesSrcDir)
|
||||
into(resourcesDestination)
|
||||
filter<ReplaceTokens>("tokens" to replacements())
|
||||
}
|
||||
resources.srcDir(resourcesGenerateTask.map { it.outputs })
|
||||
}
|
||||
|
||||
return configureIdeSync(
|
||||
tasks.register("allTemplateSources") {
|
||||
dependsOn(tasks.withType<GenerateSourceTemplates>())
|
||||
},
|
||||
tasks.register("allTemplateResources") {
|
||||
dependsOn(tasks.withType<GenerateResourceTemplates>())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun Project.configureIdeSync(vararg generateAllTasks: TaskProvider<Task>) {
|
||||
extensions.findByType<EclipseModel> {
|
||||
synchronizationTasks(generateAllTasks)
|
||||
}
|
||||
|
||||
extensions.findByType<IdeaModel> {
|
||||
if (project != null) {
|
||||
(project as ExtensionAware).extensions.configure<ProjectSettings> {
|
||||
(this as ExtensionAware).extensions.configure<TaskTriggersConfig> {
|
||||
afterSync(generateAllTasks)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//todo wasn't able to find something for VS(Code)
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> ExtensionContainer.findByType(noinline action: T.() -> Unit) {
|
||||
val extension = findByType(T::class)
|
||||
if (extension != null) {
|
||||
action.invoke(extension)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenerateAnyTemplates : Copy() {
|
||||
private val replacements = mutableMapOf<String, String>()
|
||||
|
||||
fun replaceToken(key: String, value: () -> Any) {
|
||||
replaceToken(key, value.invoke())
|
||||
}
|
||||
|
||||
fun replaceToken(key: String, value: Any) {
|
||||
replacements[key] = value.toString()
|
||||
}
|
||||
|
||||
fun replacements(): Map<String, String> {
|
||||
return replacements
|
||||
}
|
||||
}
|
||||
|
||||
open class GenerateResourceTemplates : GenerateAnyTemplates()
|
||||
open class GenerateSourceTemplates : GenerateAnyTemplates()
|
||||
@@ -1,34 +1,15 @@
|
||||
plugins {
|
||||
id("floodgate.shadow-conventions")
|
||||
id("com.jfrog.artifactory")
|
||||
id("maven-publish")
|
||||
id("net.kyori.indra.publishing")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
groupId = project.group as String
|
||||
artifactId = project.name
|
||||
version = project.version as String
|
||||
|
||||
artifact(tasks["shadowJar"])
|
||||
artifact(tasks["sourcesJar"])
|
||||
indra {
|
||||
configurePublications {
|
||||
if (shouldAddBranchName()) {
|
||||
version = versionWithBranchName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
artifactory {
|
||||
setContextUrl("https://repo.opencollab.dev/artifactory")
|
||||
publish {
|
||||
repository {
|
||||
setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases")
|
||||
setMavenCompatible(true)
|
||||
}
|
||||
defaults {
|
||||
publications("mavenJava")
|
||||
setPublishArtifacts(true)
|
||||
setPublishPom(true)
|
||||
setPublishIvy(false)
|
||||
}
|
||||
}
|
||||
publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/maven-snapshots")
|
||||
publishReleasesTo("geysermc", "https://repo.opencollab.dev/maven-releases")
|
||||
}
|
||||
@@ -31,6 +31,11 @@ tasks {
|
||||
// for example Velocity, the relocation will be gone for Velocity)
|
||||
addRelocations(project, sJar)
|
||||
}
|
||||
|
||||
val destinationDir = System.getenv("DESTINATION_DIRECTORY");
|
||||
if (destinationDir != null) {
|
||||
destinationDirectory.set(file(destinationDir))
|
||||
}
|
||||
}
|
||||
named("build") {
|
||||
dependsOn(shadowJar)
|
||||
|
||||
@@ -6,7 +6,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "org.geysermc.floodgate"
|
||||
version = "2.2.0-SNAPSHOT"
|
||||
version = property("version")!!
|
||||
description = "Allows Bedrock players to join Java edition servers while keeping the server in online mode"
|
||||
}
|
||||
|
||||
|
||||
@@ -49,27 +49,21 @@ public final class BungeeInjector extends CommonPlatformInjector {
|
||||
@Getter private boolean injected;
|
||||
|
||||
@Override
|
||||
public boolean inject() {
|
||||
try {
|
||||
// Can everyone just switch to Velocity please :)
|
||||
public void inject() {
|
||||
// Can everyone just switch to Velocity please :)
|
||||
|
||||
Field framePrepender = ReflectionUtils.getField(PipelineUtils.class, "framePrepender");
|
||||
Field framePrepender = ReflectionUtils.getField(PipelineUtils.class, "framePrepender");
|
||||
|
||||
// Required in order to inject into both Geyser <-> proxy AND proxy <-> server
|
||||
// (Instead of just replacing the ChannelInitializer which is only called for
|
||||
// player <-> proxy)
|
||||
BungeeCustomPrepender customPrepender = new BungeeCustomPrepender(
|
||||
this, ReflectionUtils.castedStaticValue(framePrepender)
|
||||
);
|
||||
// Required in order to inject into both Geyser <-> proxy AND proxy <-> server
|
||||
// (Instead of just replacing the ChannelInitializer which is only called for
|
||||
// player <-> proxy)
|
||||
BungeeCustomPrepender customPrepender = new BungeeCustomPrepender(
|
||||
this, ReflectionUtils.castedStaticValue(framePrepender)
|
||||
);
|
||||
|
||||
BungeeReflectionUtils.setFieldValue(null, framePrepender, customPrepender);
|
||||
BungeeReflectionUtils.setFieldValue(null, framePrepender, customPrepender);
|
||||
|
||||
injected = true;
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
injected = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,9 +72,9 @@ public final class BungeeInjector extends CommonPlatformInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeInjection() {
|
||||
logger.error("Floodgate cannot remove itself from Bungee without a reboot");
|
||||
return false;
|
||||
public void removeInjection() {
|
||||
throw new IllegalStateException(
|
||||
"Floodgate cannot remove itself from Bungee without a reboot");
|
||||
}
|
||||
|
||||
void injectClient(Channel channel, boolean clientToProxy) {
|
||||
|
||||
@@ -48,7 +48,7 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@@ -131,7 +131,7 @@ public final class BungeeListener implements Listener {
|
||||
if (!config.isSendFloodgateData()) {
|
||||
FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId());
|
||||
if (player != null && !player.isLinked()) {
|
||||
skinApplier.applySkin(player, new SkinData("", ""));
|
||||
skinApplier.applySkin(player, new SkinDataImpl("", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
import java.util.logging.Logger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
@@ -66,6 +68,9 @@ public final class BungeePlatformModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(PlatformUtils.class).to(BungeePlatformUtils.class);
|
||||
bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(plugin.getLogger());
|
||||
bind(FloodgateLogger.class).to(JavaUtilFloodgateLogger.class);
|
||||
bind(SkinApplier.class).to(BungeeSkinApplier.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -74,12 +79,6 @@ public final class BungeePlatformModule extends AbstractModule {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public FloodgateLogger floodgateLogger(LanguageManager languageManager) {
|
||||
return new JavaUtilFloodgateLogger(plugin.getLogger(), languageManager);
|
||||
}
|
||||
|
||||
/*
|
||||
Commands / Listeners
|
||||
*/
|
||||
@@ -123,12 +122,6 @@ public final class BungeePlatformModule extends AbstractModule {
|
||||
return new BungeePluginMessageRegistration();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SkinApplier skinApplier(FloodgateLogger logger) {
|
||||
return new BungeeSkinApplier(logger);
|
||||
}
|
||||
|
||||
/*
|
||||
DebugAddon / PlatformInjector
|
||||
*/
|
||||
|
||||
@@ -53,21 +53,6 @@ public final class BungeePluginMessageUtils extends PluginMessageUtils implement
|
||||
return;
|
||||
}
|
||||
|
||||
UUID targetUuid = null;
|
||||
String targetUsername = null;
|
||||
Identity targetIdentity = Identity.UNKNOWN;
|
||||
|
||||
Connection target = event.getReceiver();
|
||||
if (target instanceof ProxiedPlayer) {
|
||||
ProxiedPlayer player = (ProxiedPlayer) target;
|
||||
targetUuid = player.getUniqueId();
|
||||
targetUsername = player.getName();
|
||||
targetIdentity = Identity.PLAYER;
|
||||
|
||||
} else if (target instanceof ServerConnection) {
|
||||
targetIdentity = Identity.SERVER;
|
||||
}
|
||||
|
||||
UUID sourceUuid = null;
|
||||
String sourceUsername = null;
|
||||
Identity sourceIdentity = Identity.UNKNOWN;
|
||||
@@ -83,8 +68,9 @@ public final class BungeePluginMessageUtils extends PluginMessageUtils implement
|
||||
sourceIdentity = Identity.SERVER;
|
||||
}
|
||||
|
||||
Result result = channel.handleProxyCall(event.getData(), targetUuid, targetUsername,
|
||||
targetIdentity, sourceUuid, sourceUsername, sourceIdentity);
|
||||
Result result = channel.handleProxyCall(
|
||||
event.getData(), sourceUuid, sourceUsername, sourceIdentity
|
||||
);
|
||||
|
||||
event.setCancelled(!result.isAllowed());
|
||||
|
||||
|
||||
@@ -26,61 +26,46 @@
|
||||
package org.geysermc.floodgate.pluginmessage;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.geysermc.floodgate.util.ReflectionUtils.getConstructor;
|
||||
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
|
||||
import static org.geysermc.floodgate.util.ReflectionUtils.getMethodByName;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.skin.SkinApplyEventImpl;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public final class BungeeSkinApplier implements SkinApplier {
|
||||
private static final Constructor<?> LOGIN_RESULT_CONSTRUCTOR;
|
||||
private static final Field LOGIN_RESULT_FIELD;
|
||||
private static final Method SET_PROPERTIES_METHOD;
|
||||
|
||||
private static final Class<?> PROPERTY_CLASS;
|
||||
private static final Constructor<?> PROPERTY_CONSTRUCTOR;
|
||||
|
||||
static {
|
||||
PROPERTY_CLASS = ReflectionUtils.getClassOrFallbackPrefixed(
|
||||
"protocol.Property", "connection.LoginResult$Property"
|
||||
);
|
||||
|
||||
LOGIN_RESULT_CONSTRUCTOR = getConstructor(
|
||||
LoginResult.class, true,
|
||||
String.class, String.class, Array.newInstance(PROPERTY_CLASS, 0).getClass()
|
||||
);
|
||||
|
||||
LOGIN_RESULT_FIELD = getFieldOfType(InitialHandler.class, LoginResult.class);
|
||||
checkNotNull(LOGIN_RESULT_FIELD, "LoginResult field cannot be null");
|
||||
|
||||
SET_PROPERTIES_METHOD = getMethodByName(LoginResult.class, "setProperties", true);
|
||||
|
||||
PROPERTY_CONSTRUCTOR = ReflectionUtils.getConstructor(
|
||||
PROPERTY_CLASS, true,
|
||||
String.class, String.class, String.class
|
||||
);
|
||||
checkNotNull(PROPERTY_CONSTRUCTOR, "Property constructor cannot be null");
|
||||
}
|
||||
|
||||
private final FloodgateLogger logger;
|
||||
private final ProxyServer server = ProxyServer.getInstance();
|
||||
|
||||
@Inject private EventBus eventBus;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public void applySkin(FloodgatePlayer uuid, SkinData skinData) {
|
||||
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid.getCorrectUniqueId());
|
||||
public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData) {
|
||||
ProxiedPlayer player = server.getPlayer(floodgatePlayer.getCorrectUniqueId());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
@@ -97,21 +82,46 @@ public final class BungeeSkinApplier implements SkinApplier {
|
||||
// expected to be null since LoginResult is the data from hasJoined,
|
||||
// which Floodgate players don't have
|
||||
if (loginResult == null) {
|
||||
// id and name are unused and properties will be overridden
|
||||
loginResult = (LoginResult) ReflectionUtils.newInstance(
|
||||
LOGIN_RESULT_CONSTRUCTOR, null, null, null
|
||||
);
|
||||
// id and name are unused
|
||||
loginResult = new LoginResult(null, null, new Property[0]);
|
||||
ReflectionUtils.setValue(handler, LOGIN_RESULT_FIELD, loginResult);
|
||||
}
|
||||
|
||||
Object property = ReflectionUtils.newInstance(
|
||||
PROPERTY_CONSTRUCTOR,
|
||||
"textures", skinData.getValue(), skinData.getSignature()
|
||||
);
|
||||
Property[] properties = loginResult.getProperties();
|
||||
|
||||
Object propertyArray = Array.newInstance(PROPERTY_CLASS, 1);
|
||||
Array.set(propertyArray, 0, property);
|
||||
SkinData currentSkin = currentSkin(properties);
|
||||
|
||||
ReflectionUtils.invoke(loginResult, SET_PROPERTIES_METHOD, propertyArray);
|
||||
SkinApplyEvent event = new SkinApplyEventImpl(floodgatePlayer, currentSkin, skinData);
|
||||
event.setCancelled(floodgatePlayer.isLinked());
|
||||
|
||||
eventBus.fire(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
loginResult.setProperties(replaceSkin(properties, event.newSkin()));
|
||||
}
|
||||
|
||||
private SkinData currentSkin(Property[] properties) {
|
||||
for (Property property : properties) {
|
||||
if (property.getName().equals("textures")) {
|
||||
if (!property.getValue().isEmpty()) {
|
||||
return new SkinDataImpl(property.getValue(), property.getSignature());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Property[] replaceSkin(Property[] properties, SkinData skinData) {
|
||||
List<Property> list = new ArrayList<>();
|
||||
for (Property property : properties) {
|
||||
if (!property.getName().equals("textures")) {
|
||||
list.add(property);
|
||||
}
|
||||
}
|
||||
list.add(new Property("textures", skinData.value(), skinData.signature()));
|
||||
return list.toArray(new Property[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import net.kyori.blossom.BlossomExtension
|
||||
|
||||
plugins {
|
||||
id("net.kyori.blossom")
|
||||
id("floodgate.generate-templates")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.api)
|
||||
api("org.geysermc.configutils", "configutils", Versions.configUtilsVersion)
|
||||
|
||||
compileOnly(projects.ap)
|
||||
annotationProcessor(projects.ap)
|
||||
|
||||
api("com.google.inject", "guice", Versions.guiceVersion)
|
||||
api("com.nukkitx.fastutil", "fastutil-short-object-maps", Versions.fastutilVersion)
|
||||
api("com.nukkitx.fastutil", "fastutil-int-object-maps", Versions.fastutilVersion)
|
||||
api("org.java-websocket", "Java-WebSocket", Versions.javaWebsocketVersion)
|
||||
api("cloud.commandframework", "cloud-core", Versions.cloudVersion)
|
||||
api("org.yaml", "snakeyaml", Versions.snakeyamlVersion)
|
||||
api("org.bstats", "bstats-base", Versions.bstatsVersion)
|
||||
|
||||
//todo use official dependency once https://github.com/Bastian/bstats-metrics/pull/118 is merged
|
||||
api("com.github.Konicai.bstats-metrics", "bstats-base", Versions.bstatsVersion)
|
||||
}
|
||||
|
||||
// present on all platforms
|
||||
@@ -23,9 +26,10 @@ provided("io.netty", "netty-codec", Versions.nettyVersion)
|
||||
|
||||
relocate("org.bstats")
|
||||
|
||||
configure<BlossomExtension> {
|
||||
val constantsFile = "src/main/java/org/geysermc/floodgate/util/Constants.java"
|
||||
replaceToken("\${floodgateVersion}", fullVersion(), constantsFile)
|
||||
replaceToken("\${branch}", branchName(), constantsFile)
|
||||
replaceToken("\${buildNumber}", buildNumber(), constantsFile)
|
||||
tasks {
|
||||
templateSources {
|
||||
replaceToken("floodgateVersion", fullVersion())
|
||||
replaceToken("branch", branchName())
|
||||
replaceToken("buildNumber", buildNumber())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,121 +28,66 @@ package org.geysermc.floodgate;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.name.Named;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.InstanceHolder;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.packet.PacketHandlers;
|
||||
import org.geysermc.floodgate.config.ConfigLoader;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
import org.geysermc.floodgate.link.PlayerLinkLoader;
|
||||
import org.geysermc.floodgate.module.ConfigLoadedModule;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.lifecycle.PostEnableEvent;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.module.PostInitializeModule;
|
||||
import org.geysermc.floodgate.news.NewsChecker;
|
||||
import org.geysermc.floodgate.util.Metrics;
|
||||
import org.geysermc.floodgate.util.PrefixCheckTask;
|
||||
|
||||
public class FloodgatePlatform {
|
||||
private static final UUID KEY = UUID.randomUUID();
|
||||
private final FloodgateApi api;
|
||||
private final PlatformInjector injector;
|
||||
@Inject private PlatformInjector injector;
|
||||
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
private FloodgateConfig config;
|
||||
private Injector guice;
|
||||
|
||||
@Inject
|
||||
public FloodgatePlatform(
|
||||
FloodgateApi api,
|
||||
PlatformInjector platformInjector,
|
||||
FloodgateLogger logger,
|
||||
Injector guice) {
|
||||
|
||||
this.api = api;
|
||||
this.injector = platformInjector;
|
||||
this.logger = logger;
|
||||
this.guice = guice;
|
||||
}
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private Injector guice;
|
||||
|
||||
@Inject
|
||||
public void init(
|
||||
@Named("dataDirectory") Path dataDirectory,
|
||||
ConfigLoader configLoader,
|
||||
FloodgateConfigHolder configHolder,
|
||||
FloodgateApi api,
|
||||
PlayerLink link,
|
||||
FloodgateEventBus eventBus,
|
||||
PacketHandlers packetHandlers,
|
||||
HandshakeHandlers handshakeHandlers) {
|
||||
|
||||
if (!Files.isDirectory(dataDirectory)) {
|
||||
try {
|
||||
Files.createDirectory(dataDirectory);
|
||||
} catch (IOException exception) {
|
||||
logger.error("Failed to create the data folder", exception);
|
||||
throw new RuntimeException("Failed to create the data folder", exception);
|
||||
}
|
||||
}
|
||||
|
||||
config = configLoader.load();
|
||||
if (config.isDebug()) {
|
||||
logger.enableDebug();
|
||||
}
|
||||
|
||||
configHolder.set(config);
|
||||
guice = guice.createChildInjector(new ConfigLoadedModule(config));
|
||||
PlayerLink link = guice.getInstance(PlayerLinkLoader.class).load();
|
||||
|
||||
InstanceHolder.set(api, link, this.injector, packetHandlers, handshakeHandlers, KEY);
|
||||
|
||||
guice.getInstance(NewsChecker.class).start();
|
||||
HandshakeHandlers handshakeHandlers
|
||||
) {
|
||||
InstanceHolder.set(
|
||||
api, link, eventBus, this.injector, packetHandlers, handshakeHandlers, KEY
|
||||
);
|
||||
}
|
||||
|
||||
public boolean enable(Module... postInitializeModules) {
|
||||
public void enable(Module... postInitializeModules) throws RuntimeException {
|
||||
if (injector == null) {
|
||||
logger.error("Failed to find the platform injector!");
|
||||
return false;
|
||||
throw new RuntimeException("Failed to find the platform injector!");
|
||||
}
|
||||
|
||||
try {
|
||||
if (!injector.inject()) {
|
||||
logger.error("Failed to inject the packet listener!");
|
||||
return false;
|
||||
}
|
||||
injector.inject();
|
||||
} catch (Exception exception) {
|
||||
logger.error("Failed to inject the packet listener!", exception);
|
||||
return false;
|
||||
throw new RuntimeException("Failed to inject the packet listener!", exception);
|
||||
}
|
||||
|
||||
this.guice = guice.createChildInjector(new PostInitializeModule(postInitializeModules));
|
||||
|
||||
PrefixCheckTask.checkAndExecuteDelayed(config, logger);
|
||||
|
||||
guice.getInstance(Metrics.class);
|
||||
|
||||
return true;
|
||||
guice.getInstance(EventBus.class).fire(new PostEnableEvent());
|
||||
}
|
||||
|
||||
public boolean disable() {
|
||||
public void disable() {
|
||||
guice.getInstance(EventBus.class).fire(new ShutdownEvent());
|
||||
|
||||
if (injector != null && injector.canRemoveInjection()) {
|
||||
try {
|
||||
if (!injector.removeInjection()) {
|
||||
logger.error("Failed to remove the injection!");
|
||||
}
|
||||
injector.removeInjection();
|
||||
} catch (Exception exception) {
|
||||
logger.error("Failed to remove the injection!", exception);
|
||||
throw new RuntimeException("Failed to remove the injection!", exception);
|
||||
}
|
||||
}
|
||||
|
||||
guice.getInstance(NewsChecker.class).shutdown();
|
||||
api.getPlayerLink().stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isProxy() {
|
||||
|
||||
@@ -25,24 +25,14 @@
|
||||
|
||||
package org.geysermc.floodgate.api;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
|
||||
public final class ProxyFloodgateApi extends SimpleFloodgateApi {
|
||||
private final FloodgateCipher cipher;
|
||||
|
||||
public ProxyFloodgateApi(
|
||||
PluginMessageManager pluginMessageManager,
|
||||
FloodgateConfigHolder configHolder,
|
||||
FloodgateLogger logger,
|
||||
FloodgateCipher cipher) {
|
||||
super(pluginMessageManager, configHolder, logger);
|
||||
this.cipher = cipher;
|
||||
}
|
||||
@Inject
|
||||
private FloodgateCipher cipher;
|
||||
|
||||
public byte[] createEncryptedData(BedrockData bedrockData) {
|
||||
try {
|
||||
|
||||
@@ -30,41 +30,41 @@ import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.unsafe.Unsafe;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||
import org.geysermc.floodgate.pluginmessage.channel.FormChannel;
|
||||
import org.geysermc.floodgate.pluginmessage.channel.TransferChannel;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpUtils;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleFloodgateApi implements FloodgateApi {
|
||||
private final Map<UUID, FloodgatePlayer> players = new HashMap<>();
|
||||
private final Map<UUID, FloodgatePlayer> players = new ConcurrentHashMap<>();
|
||||
private final Cache<UUID, FloodgatePlayer> pendingRemove =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
private final PluginMessageManager pluginMessageManager;
|
||||
private final FloodgateConfigHolder configHolder;
|
||||
private final FloodgateLogger logger;
|
||||
@Inject private PluginMessageManager pluginMessageManager;
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private HttpClient httpClient;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public String getPlayerPrefix() {
|
||||
return configHolder.get().getUsernamePrefix();
|
||||
return config.getUsernamePrefix();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -148,7 +148,7 @@ public class SimpleFloodgateApi implements FloodgateApi {
|
||||
return Utils.failedFuture(new IllegalStateException("Received an invalid gamertag"));
|
||||
}
|
||||
|
||||
return HttpUtils.asyncGet(Constants.GET_XUID_URL + gamertag)
|
||||
return httpClient.asyncGet(Constants.GET_XUID_URL + gamertag)
|
||||
.thenApply(result -> {
|
||||
JsonObject response = result.getResponse();
|
||||
|
||||
@@ -163,7 +163,7 @@ public class SimpleFloodgateApi implements FloodgateApi {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> getGamertagFor(long xuid) {
|
||||
return HttpUtils.asyncGet(Constants.GET_GAMERTAG_URL + xuid)
|
||||
return httpClient.asyncGet(Constants.GET_GAMERTAG_URL + xuid)
|
||||
.thenApply(result -> {
|
||||
JsonObject response = result.getResponse();
|
||||
|
||||
|
||||
@@ -49,10 +49,11 @@ import org.geysermc.floodgate.player.UserAudience;
|
||||
import org.geysermc.floodgate.player.audience.ProfileAudience;
|
||||
import org.geysermc.floodgate.player.audience.ProfileAudienceArgument;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpUtils;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
|
||||
public class WhitelistCommand implements FloodgateCommand {
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private HttpClient httpClient;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
@@ -128,7 +129,7 @@ public class WhitelistCommand implements FloodgateCommand {
|
||||
final String strippedName = name;
|
||||
|
||||
// We need to get the UUID of the player if it's not manually specified
|
||||
HttpUtils.asyncGet(Constants.GET_XUID_URL + name)
|
||||
httpClient.asyncGet(Constants.GET_XUID_URL + name)
|
||||
.whenComplete((result, error) -> {
|
||||
if (error != null) {
|
||||
sender.sendMessage(Message.API_UNAVAILABLE);
|
||||
|
||||
@@ -29,20 +29,40 @@ import static org.geysermc.floodgate.util.Constants.COLOR_CHAR;
|
||||
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.inject.Inject;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateSubCommand;
|
||||
import org.geysermc.floodgate.player.UserAudience;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpUtils;
|
||||
import org.geysermc.floodgate.util.HttpUtils.HttpResponse;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.HttpClient.HttpResponse;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
final class FirewallCheckSubcommand {
|
||||
private FirewallCheckSubcommand() {}
|
||||
final class FirewallCheckSubcommand extends FloodgateSubCommand {
|
||||
@Inject
|
||||
private HttpClient httpClient;
|
||||
|
||||
static void executeFirewall(CommandContext<UserAudience> context) {
|
||||
@Override
|
||||
public String name() {
|
||||
return "firewall";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Check if your outgoing firewall allows Floodgate to work properly";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permission permission() {
|
||||
return Permission.COMMAND_MAIN_FIREWALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext<UserAudience> context) {
|
||||
UserAudience sender = context.getSender();
|
||||
executeChecks(
|
||||
globalApiCheck(sender)
|
||||
@@ -54,12 +74,12 @@ final class FirewallCheckSubcommand {
|
||||
);
|
||||
}
|
||||
|
||||
private static BooleanSupplier globalApiCheck(UserAudience sender) {
|
||||
private BooleanSupplier globalApiCheck(UserAudience sender) {
|
||||
return executeFirewallText(
|
||||
sender, "global api",
|
||||
() -> {
|
||||
HttpResponse<JsonElement> response =
|
||||
HttpUtils.get(Constants.HEALTH_URL, JsonElement.class);
|
||||
httpClient.get(Constants.HEALTH_URL, JsonElement.class);
|
||||
|
||||
if (!response.isCodeOk()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
@@ -70,7 +90,7 @@ final class FirewallCheckSubcommand {
|
||||
});
|
||||
}
|
||||
|
||||
private static BooleanSupplier executeFirewallText(
|
||||
private BooleanSupplier executeFirewallText(
|
||||
UserAudience sender, String name, Runnable runnable) {
|
||||
return () -> {
|
||||
sender.sendMessage(COLOR_CHAR + "eTesting " + name + "...");
|
||||
@@ -86,9 +106,7 @@ final class FirewallCheckSubcommand {
|
||||
};
|
||||
}
|
||||
|
||||
private static CompletableFuture<Pair<Integer, Integer>> executeChecks(
|
||||
BooleanSupplier... checks) {
|
||||
|
||||
private CompletableFuture<Pair<Integer, Integer>> executeChecks(BooleanSupplier... checks) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
AtomicInteger okCount = new AtomicInteger();
|
||||
AtomicInteger failCount = new AtomicInteger();
|
||||
|
||||
@@ -33,13 +33,18 @@ import cloud.commandframework.Command.Builder;
|
||||
import cloud.commandframework.CommandManager;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateSubCommand;
|
||||
import org.geysermc.floodgate.platform.command.SubCommands;
|
||||
import org.geysermc.floodgate.player.UserAudience;
|
||||
|
||||
public final class MainCommand implements FloodgateCommand {
|
||||
public final class MainCommand extends SubCommands implements FloodgateCommand {
|
||||
public MainCommand() {
|
||||
defineSubCommand(FirewallCheckSubcommand.class);
|
||||
defineSubCommand(VersionSubcommand.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
|
||||
Builder<UserAudience> builder = commandManager.commandBuilder(
|
||||
@@ -49,11 +54,11 @@ public final class MainCommand implements FloodgateCommand {
|
||||
.permission(Permission.COMMAND_MAIN.get())
|
||||
.handler(this::execute);
|
||||
|
||||
for (SubCommand subCommand : SubCommand.VALUES) {
|
||||
for (FloodgateSubCommand subCommand : subCommands()) {
|
||||
commandManager.command(builder
|
||||
.literal(subCommand.name().toLowerCase(Locale.ROOT), subCommand.description)
|
||||
.permission(subCommand.permission.get())
|
||||
.handler(subCommand.executor::accept)
|
||||
.literal(subCommand.name().toLowerCase(Locale.ROOT), subCommand.description())
|
||||
.permission(subCommand.permission().get())
|
||||
.handler(subCommand::execute)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,27 +70,15 @@ public final class MainCommand implements FloodgateCommand {
|
||||
public void execute(CommandContext<UserAudience> context) {
|
||||
StringBuilder helpMessage = new StringBuilder("Available subcommands are:\n");
|
||||
|
||||
for (SubCommand subCommand : SubCommand.VALUES) {
|
||||
if (context.getSender().hasPermission(subCommand.permission.get())) {
|
||||
for (FloodgateSubCommand subCommand : subCommands()) {
|
||||
if (context.getSender().hasPermission(subCommand.permission().get())) {
|
||||
helpMessage.append('\n').append(COLOR_CHAR).append('b')
|
||||
.append(subCommand.name().toLowerCase(Locale.ROOT))
|
||||
.append(COLOR_CHAR).append("f - ").append(COLOR_CHAR).append('7')
|
||||
.append(subCommand.description);
|
||||
.append(subCommand.description());
|
||||
}
|
||||
}
|
||||
|
||||
context.getSender().sendMessage(helpMessage.toString());
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
enum SubCommand {
|
||||
FIREWALL("Check if your outgoing firewall allows Floodgate to work properly",
|
||||
Permission.COMMAND_MAIN_FIREWALL, FirewallCheckSubcommand::executeFirewall);
|
||||
|
||||
static final SubCommand[] VALUES = values();
|
||||
|
||||
final String description;
|
||||
final Permission permission;
|
||||
final Consumer<CommandContext<UserAudience>> executor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.command.main;
|
||||
|
||||
import static org.geysermc.floodgate.util.Constants.COLOR_CHAR;
|
||||
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.inject.Inject;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.command.WhitelistCommand.Message;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateSubCommand;
|
||||
import org.geysermc.floodgate.player.UserAudience;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
|
||||
public class VersionSubcommand extends FloodgateSubCommand {
|
||||
@Inject
|
||||
private HttpClient httpClient;
|
||||
|
||||
@Inject
|
||||
private FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "version";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Displays version information about Floodgate";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permission permission() {
|
||||
return Permission.COMMAND_MAIN_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext<UserAudience> context) {
|
||||
UserAudience sender = context.getSender();
|
||||
sender.sendMessage(String.format(
|
||||
COLOR_CHAR + "7You're currently on " + COLOR_CHAR + "b%s" +
|
||||
COLOR_CHAR + "7 (branch: " + COLOR_CHAR + "b%s" + COLOR_CHAR + "7)\n" +
|
||||
COLOR_CHAR + "eFetching latest build info...",
|
||||
Constants.VERSION, Constants.GIT_BRANCH
|
||||
));
|
||||
|
||||
String baseUrl = String.format(
|
||||
"https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/%s/lastSuccessfulBuild/",
|
||||
Constants.GIT_BRANCH
|
||||
);
|
||||
|
||||
httpClient.asyncGet(
|
||||
baseUrl + "buildNumber",
|
||||
JsonElement.class
|
||||
).whenComplete((result, error) -> {
|
||||
if (error != null) {
|
||||
sender.sendMessage(COLOR_CHAR + "cCould not retrieve latest version info!");
|
||||
error.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
JsonElement response = result.getResponse();
|
||||
|
||||
logger.info(String.valueOf(response));
|
||||
logger.info("{}", result.getHttpCode());
|
||||
|
||||
if (result.getHttpCode() == 404) {
|
||||
sender.sendMessage(
|
||||
COLOR_CHAR + "cGot a 404 (not found) while requesting the latest version." +
|
||||
" Are you using a custom Floodgate version?"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.isCodeOk()) {
|
||||
//todo make it more generic instead of using a Whitelist command strings
|
||||
logger.error(
|
||||
"Got an error from requesting the latest Floodgate version: {}",
|
||||
response.toString()
|
||||
);
|
||||
sender.sendMessage(Message.UNEXPECTED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
int buildNumber = response.getAsInt();
|
||||
|
||||
if (buildNumber > Constants.BUILD_NUMBER) {
|
||||
sender.sendMessage(String.format(
|
||||
COLOR_CHAR + "7There is a newer version of Floodgate available!\n" +
|
||||
COLOR_CHAR + "7You are " + COLOR_CHAR + "e%s " + COLOR_CHAR + "7builds behind.\n" +
|
||||
COLOR_CHAR + "7Download the latest Floodgate version here: " + COLOR_CHAR + "b%s",
|
||||
buildNumber - Constants.BUILD_NUMBER, baseUrl
|
||||
));
|
||||
return;
|
||||
}
|
||||
if (buildNumber == Constants.BUILD_NUMBER) {
|
||||
sender.sendMessage(COLOR_CHAR + "aYou're running the latest version of Floodgate!");
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(COLOR_CHAR + "cCannot check version for custom Floodgate versions!");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import static org.geysermc.floodgate.command.util.PermissionDefault.TRUE;
|
||||
public enum Permission {
|
||||
COMMAND_MAIN("floodgate.command.floodgate", TRUE),
|
||||
COMMAND_MAIN_FIREWALL(COMMAND_MAIN, "firewall", OP),
|
||||
COMMAND_MAIN_VERSION(COMMAND_MAIN, "version", OP),
|
||||
COMMAND_LINK("floodgate.command.linkaccount", TRUE),
|
||||
COMMAND_UNLINK("floodgate.command.unlinkaccount", TRUE),
|
||||
COMMAND_WHITELIST("floodgate.command.fwhitelist", OP),
|
||||
|
||||
@@ -35,21 +35,18 @@ import org.geysermc.configutils.ConfigUtilities;
|
||||
import org.geysermc.configutils.file.codec.PathFileCodec;
|
||||
import org.geysermc.configutils.file.template.ResourceTemplateReader;
|
||||
import org.geysermc.configutils.updater.change.Changes;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.crypto.KeyProducer;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class ConfigLoader {
|
||||
private final Path dataFolder;
|
||||
private final Path dataDirectory;
|
||||
private final Class<? extends FloodgateConfig> configClass;
|
||||
|
||||
private final KeyProducer keyProducer;
|
||||
private final FloodgateCipher cipher;
|
||||
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends FloodgateConfig> T load() {
|
||||
String templateFile = "config.yml";
|
||||
@@ -65,7 +62,7 @@ public final class ConfigLoader {
|
||||
|
||||
ConfigUtilities utilities =
|
||||
ConfigUtilities.builder()
|
||||
.fileCodec(PathFileCodec.of(dataFolder))
|
||||
.fileCodec(PathFileCodec.of(dataDirectory))
|
||||
.configFile("config.yml")
|
||||
.templateReader(ResourceTemplateReader.of(getClass()))
|
||||
.template(templateFile)
|
||||
@@ -100,21 +97,16 @@ public final class ConfigLoader {
|
||||
String decrypted = cipher.decryptToString(encrypted);
|
||||
|
||||
if (!test.equals(decrypted)) {
|
||||
logger.error("Whoops, we tested the generated Floodgate keys but " +
|
||||
"the decrypted test message doesn't match the original.\n" +
|
||||
throw new RuntimeException("Failed to decrypt test message.\n" +
|
||||
"Original message: " + test + "." +
|
||||
"Decrypted message: " + decrypted + ".\n" +
|
||||
"The encrypted message itself: " + new String(encrypted)
|
||||
);
|
||||
throw new RuntimeException(
|
||||
"Tested the generated public and private key but, " +
|
||||
"the decrypted message doesn't match the original!"
|
||||
);
|
||||
}
|
||||
|
||||
Files.write(keyPath, key.getEncoded());
|
||||
} catch (Exception exception) {
|
||||
logger.error("Error while creating key", exception);
|
||||
throw new RuntimeException("Error while creating key", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,9 @@ public class FloodgateConfig implements GenericPostInitializeCallback<ConfigLoad
|
||||
private boolean debug;
|
||||
private int configVersion;
|
||||
|
||||
|
||||
private Key key;
|
||||
private String rawUsernamePrefix;
|
||||
|
||||
public boolean isProxy() {
|
||||
return this instanceof ProxyFloodgateConfig;
|
||||
@@ -60,7 +62,7 @@ public class FloodgateConfig implements GenericPostInitializeCallback<ConfigLoad
|
||||
|
||||
@Override
|
||||
public CallbackResult postInitialize(ConfigLoader loader) {
|
||||
Path keyPath = loader.getDataFolder().resolve(getKeyFileName());
|
||||
Path keyPath = loader.getDataDirectory().resolve(getKeyFileName());
|
||||
|
||||
// don't assume that the key always exists with the existence of a config
|
||||
if (!Files.exists(keyPath)) {
|
||||
@@ -74,6 +76,14 @@ public class FloodgateConfig implements GenericPostInitializeCallback<ConfigLoad
|
||||
} catch (IOException exception) {
|
||||
return CallbackResult.failed(exception.getMessage());
|
||||
}
|
||||
|
||||
rawUsernamePrefix = usernamePrefix;
|
||||
|
||||
// Java usernames can't be longer than 16 chars
|
||||
if (usernamePrefix.length() >= 16) {
|
||||
usernamePrefix = ".";
|
||||
}
|
||||
|
||||
return CallbackResult.ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.event.bus.impl.EventBusImpl;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.event.subscribe.Subscriber;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.event.FloodgateSubscriber;
|
||||
|
||||
@Singleton
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class EventBus extends EventBusImpl<Object, FloodgateSubscriber<?>>
|
||||
implements FloodgateEventBus {
|
||||
@Override
|
||||
protected <H, T, B extends Subscriber<T>> B makeSubscription(
|
||||
@NonNull Class<T> eventClass,
|
||||
@NonNull Subscribe subscribe,
|
||||
@NonNull H listener,
|
||||
@NonNull BiConsumer<H, T> handler
|
||||
) {
|
||||
return (B) new EventSubscriber<>(
|
||||
eventClass, subscribe.postOrder(), subscribe.ignoreCancelled(), listener, handler
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T, B extends Subscriber<T>> B makeSubscription(
|
||||
@NonNull Class<T> eventClass,
|
||||
@NonNull Consumer<T> handler,
|
||||
@NonNull PostOrder postOrder
|
||||
) {
|
||||
return (B) new EventSubscriber<>(eventClass, handler, postOrder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.event.subscribe.impl.SubscriberImpl;
|
||||
import org.geysermc.floodgate.api.event.FloodgateSubscriber;
|
||||
|
||||
public final class EventSubscriber<E> extends SubscriberImpl<E> implements FloodgateSubscriber<E> {
|
||||
EventSubscriber(
|
||||
@NonNull Class<E> eventClass,
|
||||
@NonNull Consumer<E> handler,
|
||||
@NonNull PostOrder postOrder
|
||||
) {
|
||||
super(eventClass, handler, postOrder);
|
||||
}
|
||||
|
||||
<H> EventSubscriber(
|
||||
Class<E> eventClass,
|
||||
PostOrder postOrder,
|
||||
boolean ignoreCancelled,
|
||||
H handlerInstance,
|
||||
BiConsumer<H, E> handler
|
||||
) {
|
||||
super(eventClass, postOrder, ignoreCancelled, handlerInstance, handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event.lifecycle;
|
||||
|
||||
public class PostEnableEvent {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event.lifecycle;
|
||||
|
||||
public class ShutdownEvent {
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event.skin;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.event.util.AbstractCancellable;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public class SkinApplyEventImpl extends AbstractCancellable implements SkinApplyEvent {
|
||||
private final FloodgatePlayer player;
|
||||
private final SkinData currentSkin;
|
||||
private SkinData newSkin;
|
||||
|
||||
public SkinApplyEventImpl(
|
||||
@NonNull FloodgatePlayer player,
|
||||
@Nullable SkinData currentSkin,
|
||||
@NonNull SkinData newSkin
|
||||
) {
|
||||
this.player = Objects.requireNonNull(player);
|
||||
this.currentSkin = currentSkin;
|
||||
this.newSkin = Objects.requireNonNull(newSkin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull FloodgatePlayer player() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public @Nullable SkinData currentSkin() {
|
||||
return currentSkin;
|
||||
}
|
||||
|
||||
public @NonNull SkinData newSkin() {
|
||||
return newSkin;
|
||||
}
|
||||
|
||||
public SkinApplyEventImpl newSkin(@NonNull SkinData skinData) {
|
||||
this.newSkin = Objects.requireNonNull(skinData);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event.util;
|
||||
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.matcher.AbstractMatcher;
|
||||
import org.geysermc.event.Listener;
|
||||
|
||||
public class ListenerAnnotationMatcher extends AbstractMatcher<TypeLiteral<?>> {
|
||||
@Override
|
||||
public boolean matches(TypeLiteral<?> typeLiteral) {
|
||||
Class<?> rawType = typeLiteral.getRawType();
|
||||
return rawType.isAnnotationPresent(Listener.class);
|
||||
}
|
||||
}
|
||||
@@ -26,18 +26,17 @@
|
||||
package org.geysermc.floodgate.inject;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import java.util.WeakHashMap;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
|
||||
public abstract class CommonPlatformInjector implements PlatformInjector {
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final Set<Channel> injectedClients = new HashSet<>();
|
||||
private final Set<Channel> injectedClients =
|
||||
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
|
||||
|
||||
private final Map<Class<?>, InjectorAddon> addons = new HashMap<>();
|
||||
|
||||
@@ -49,6 +48,10 @@ public abstract class CommonPlatformInjector implements PlatformInjector {
|
||||
return injectedClients.remove(channel);
|
||||
}
|
||||
|
||||
public Set<Channel> injectedClients() {
|
||||
return injectedClients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAddon(InjectorAddon addon) {
|
||||
return addons.putIfAbsent(addon.getClass(), addon) == null;
|
||||
|
||||
@@ -34,6 +34,8 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.link.LinkRequest;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
@@ -41,11 +43,13 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.database.config.DatabaseConfig;
|
||||
import org.geysermc.floodgate.database.config.DatabaseConfigLoader;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.util.InjectorHolder;
|
||||
|
||||
@Listener
|
||||
public abstract class CommonPlayerLink implements PlayerLink {
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final ExecutorService executorService = Executors.newFixedThreadPool(11);
|
||||
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
@Getter private boolean enabled;
|
||||
@Getter private boolean allowLinking;
|
||||
@@ -102,4 +106,9 @@ public abstract class CommonPlayerLink implements PlayerLink {
|
||||
public void stop() {
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onShutdown(ShutdownEvent ignored) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,19 +29,23 @@ import static org.geysermc.floodgate.util.Constants.GET_BEDROCK_LINK;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.link.LinkRequestResult;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.util.HttpUtils;
|
||||
import org.geysermc.floodgate.util.HttpUtils.DefaultHttpResponse;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.HttpClient.DefaultHttpResponse;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@Getter
|
||||
public class GlobalPlayerLinking extends CommonPlayerLink {
|
||||
@Inject
|
||||
private HttpClient httpClient;
|
||||
|
||||
private PlayerLink databaseImpl;
|
||||
|
||||
public void setDatabaseImpl(PlayerLink databaseImpl) {
|
||||
@@ -94,7 +98,7 @@ public class GlobalPlayerLinking extends CommonPlayerLink {
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
DefaultHttpResponse response =
|
||||
HttpUtils.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits());
|
||||
httpClient.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits());
|
||||
|
||||
// either the global api is down or it failed to return link
|
||||
if (!response.isCodeOk()) {
|
||||
@@ -144,7 +148,7 @@ public class GlobalPlayerLinking extends CommonPlayerLink {
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
DefaultHttpResponse response =
|
||||
HttpUtils.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits());
|
||||
httpClient.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits());
|
||||
|
||||
if (!response.isCodeOk()) {
|
||||
getLogger().error(
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -42,18 +43,23 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Named;
|
||||
import java.util.stream.Stream;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig.PlayerLinkConfig;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.InjectorHolder;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@Listener
|
||||
@Singleton
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class PlayerLinkLoader {
|
||||
public final class PlayerLinkHolder {
|
||||
@Inject private Injector injector;
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private FloodgateLogger logger;
|
||||
@@ -62,30 +68,38 @@ public final class PlayerLinkLoader {
|
||||
@Named("dataDirectory")
|
||||
private Path dataDirectory;
|
||||
|
||||
@Nonnull
|
||||
private URLClassLoader classLoader;
|
||||
private PlayerLink instance;
|
||||
|
||||
@NonNull
|
||||
public PlayerLink load() {
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
throw new IllegalStateException("Config cannot be null!");
|
||||
}
|
||||
|
||||
FloodgateConfig.PlayerLinkConfig lConfig = config.getPlayerLink();
|
||||
if (!lConfig.isEnabled()) {
|
||||
PlayerLinkConfig linkConfig = config.getPlayerLink();
|
||||
if (!linkConfig.isEnabled()) {
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
|
||||
List<Path> files;
|
||||
try {
|
||||
files = Files.list(dataDirectory)
|
||||
.filter(path -> Files.isRegularFile(path) && path.toString().endsWith("jar"))
|
||||
try (Stream<Path> list = Files.list(dataDirectory)) {
|
||||
files = list
|
||||
.filter(path -> Files.isRegularFile(path) && path.toString().endsWith(".jar"))
|
||||
.collect(Collectors.toList());
|
||||
} catch (IOException exception) {
|
||||
logger.error("Failed to list possible database implementations", exception);
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
|
||||
// we can skip the rest if global linking is enabled and no database implementations has been
|
||||
// found, or when global linking is enabled and own player linking is disabled.
|
||||
if (lConfig.isEnableGlobalLinking() && (files.isEmpty() || !lConfig.isEnableOwnLinking())) {
|
||||
// we can skip the rest if global linking is enabled and no database implementations has
|
||||
// been found, or when global linking is enabled and own player linking is disabled.
|
||||
if (linkConfig.isEnableGlobalLinking() &&
|
||||
(files.isEmpty() || !linkConfig.isEnableOwnLinking())) {
|
||||
return injector.getInstance(GlobalPlayerLinking.class);
|
||||
}
|
||||
|
||||
@@ -100,7 +114,7 @@ public final class PlayerLinkLoader {
|
||||
// We only want to load one database implementation
|
||||
if (files.size() > 1) {
|
||||
boolean found = false;
|
||||
databaseName = lConfig.getType();
|
||||
databaseName = linkConfig.getType();
|
||||
|
||||
String expectedName = "floodgate-" + databaseName + "-database.jar";
|
||||
for (Path path : files) {
|
||||
@@ -111,14 +125,18 @@ public final class PlayerLinkLoader {
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
logger.error("Failed to find an implementation for type: {}", lConfig.getType());
|
||||
logger.error(
|
||||
"Failed to find an implementation for type: {}", linkConfig.getType()
|
||||
);
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
} else {
|
||||
String name = implementationPath.getFileName().toString();
|
||||
if (!Utils.isValidDatabaseName(name)) {
|
||||
logger.error("Found database {} but the name doesn't match {}",
|
||||
name, Constants.DATABASE_NAME_FORMAT);
|
||||
logger.error(
|
||||
"Found database {} but the name doesn't match {}",
|
||||
name, Constants.DATABASE_NAME_FORMAT
|
||||
);
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
int firstSplit = name.indexOf('-') + 1;
|
||||
@@ -133,24 +151,22 @@ public final class PlayerLinkLoader {
|
||||
// we don't have a way to close this properly since we have no stop method and we have
|
||||
// to be able to load classes on the fly, but that doesn't matter anyway since Floodgate
|
||||
// doesn't support reloading
|
||||
URLClassLoader classLoader = new URLClassLoader(
|
||||
classLoader = new URLClassLoader(
|
||||
new URL[]{pluginUrl},
|
||||
PlayerLinkLoader.class.getClassLoader()
|
||||
PlayerLinkHolder.class.getClassLoader()
|
||||
);
|
||||
|
||||
String mainClassName;
|
||||
JsonObject linkConfig;
|
||||
|
||||
try (InputStream linkConfigStream =
|
||||
classLoader.getResourceAsStream("init.json")) {
|
||||
JsonObject dbInitConfig;
|
||||
|
||||
try (InputStream linkConfigStream = classLoader.getResourceAsStream("init.json")) {
|
||||
requireNonNull(linkConfigStream, "Implementation should have an init file");
|
||||
|
||||
linkConfig = new Gson().fromJson(
|
||||
dbInitConfig = new Gson().fromJson(
|
||||
new InputStreamReader(linkConfigStream), JsonObject.class
|
||||
);
|
||||
|
||||
mainClassName = linkConfig.get("mainClass").getAsString();
|
||||
mainClassName = dbInitConfig.get("mainClass").getAsString();
|
||||
}
|
||||
|
||||
Class<? extends PlayerLink> mainClass =
|
||||
@@ -167,16 +183,16 @@ public final class PlayerLinkLoader {
|
||||
Names.named("databaseClassLoader")).toInstance(classLoader);
|
||||
binder.bind(JsonObject.class)
|
||||
.annotatedWith(Names.named("databaseInitData"))
|
||||
.toInstance(linkConfig);
|
||||
.toInstance(dbInitConfig);
|
||||
binder.bind(InjectorHolder.class)
|
||||
.toInstance(injectorHolder);
|
||||
});
|
||||
injectorHolder.set(linkInjector);
|
||||
|
||||
PlayerLink instance = linkInjector.getInstance(mainClass);
|
||||
instance = linkInjector.getInstance(mainClass);
|
||||
|
||||
// we use our own internal PlayerLinking when global linking is enabled
|
||||
if (lConfig.isEnableGlobalLinking()) {
|
||||
if (linkConfig.isEnableGlobalLinking()) {
|
||||
GlobalPlayerLinking linking = linkInjector.getInstance(GlobalPlayerLinking.class);
|
||||
linking.setDatabaseImpl(instance);
|
||||
linking.load();
|
||||
@@ -186,8 +202,10 @@ public final class PlayerLinkLoader {
|
||||
return instance;
|
||||
}
|
||||
} catch (ClassCastException exception) {
|
||||
logger.error("The database implementation ({}) doesn't extend the PlayerLink class!",
|
||||
implementationPath.getFileName().toString(), exception);
|
||||
logger.error(
|
||||
"The database implementation ({}) doesn't extend the PlayerLink class!",
|
||||
implementationPath.getFileName().toString(), exception
|
||||
);
|
||||
return new DisabledPlayerLink();
|
||||
} catch (Exception exception) {
|
||||
if (init) {
|
||||
@@ -198,4 +216,10 @@ public final class PlayerLinkLoader {
|
||||
return new DisabledPlayerLink();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onShutdown(ShutdownEvent ignored) throws Exception {
|
||||
instance.stop();
|
||||
classLoader.close();
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,29 @@ package org.geysermc.floodgate.logger;
|
||||
|
||||
import static org.geysermc.floodgate.util.MessageFormatter.format;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public final class JavaUtilFloodgateLogger implements FloodgateLogger {
|
||||
private final Logger logger;
|
||||
private final LanguageManager languageManager;
|
||||
private Level originLevel;
|
||||
@Inject
|
||||
@Named("logger")
|
||||
private Logger logger;
|
||||
private LanguageManager languageManager;
|
||||
|
||||
@Inject
|
||||
private void init(LanguageManager languageManager, FloodgateConfig config) {
|
||||
this.languageManager = languageManager;
|
||||
if (config.isDebug()) {
|
||||
logger.setLevel(Level.ALL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message, Object... args) {
|
||||
@@ -74,19 +86,6 @@ public final class JavaUtilFloodgateLogger implements FloodgateLogger {
|
||||
logger.finer(format(message, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableDebug() {
|
||||
originLevel = logger.getLevel();
|
||||
logger.setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableDebug() {
|
||||
if (originLevel != null) {
|
||||
logger.setLevel(originLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return logger.getLevel() == Level.ALL;
|
||||
|
||||
@@ -23,25 +23,17 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.skin;
|
||||
package org.geysermc.floodgate.module;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import com.google.inject.AbstractModule;
|
||||
import org.geysermc.floodgate.util.AutoBind;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class SkinData {
|
||||
private final String value;
|
||||
private final String signature;
|
||||
|
||||
public static SkinData from(JsonObject data) {
|
||||
if (data.has("signature") && !data.get("signature").isJsonNull()) {
|
||||
return new SkinData(
|
||||
data.get("value").getAsString(),
|
||||
data.get("signature").getAsString()
|
||||
);
|
||||
public class AutoBindModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
for (Class<?> clazz : Utils.getGeneratedClassesForAnnotation(AutoBind.class)) {
|
||||
bind(clazz).asEagerSingleton();
|
||||
}
|
||||
return new SkinData(data.get("value").getAsString(), null);
|
||||
}
|
||||
}
|
||||
@@ -28,49 +28,105 @@ package org.geysermc.floodgate.module;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
import com.google.inject.spi.InjectionListener;
|
||||
import com.google.inject.spi.TypeEncounter;
|
||||
import com.google.inject.spi.TypeListener;
|
||||
import io.netty.util.AttributeKey;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.floodgate.addon.data.HandshakeHandlersImpl;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.packet.PacketHandlers;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ConfigLoader;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
import org.geysermc.floodgate.crypto.AesCipher;
|
||||
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
||||
import org.geysermc.floodgate.crypto.Base64Topping;
|
||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.crypto.KeyProducer;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.event.util.ListenerAnnotationMatcher;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
import org.geysermc.floodgate.news.NewsChecker;
|
||||
import org.geysermc.floodgate.link.PlayerLinkHolder;
|
||||
import org.geysermc.floodgate.packet.PacketHandlersImpl;
|
||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinUploadManager;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommonModule extends AbstractModule {
|
||||
private final EventBus eventBus = new EventBus();
|
||||
private final Path dataDirectory;
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(EventBus.class).toInstance(eventBus);
|
||||
bind(FloodgateEventBus.class).to(EventBus.class);
|
||||
// register every class that has the Listener annotation
|
||||
bindListener(new ListenerAnnotationMatcher(), new TypeListener() {
|
||||
@Override
|
||||
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
|
||||
encounter.register((InjectionListener<I>) eventBus::register);
|
||||
}
|
||||
});
|
||||
|
||||
ExecutorService commonPool = Executors.newCachedThreadPool();
|
||||
ScheduledExecutorService commonScheduledPool = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
eventBus.subscribe(ShutdownEvent.class, ignored -> {
|
||||
commonPool.shutdown();
|
||||
commonScheduledPool.shutdown();
|
||||
}, PostOrder.LAST);
|
||||
|
||||
bind(ExecutorService.class)
|
||||
.annotatedWith(Names.named("commonPool"))
|
||||
.toInstance(commonPool);
|
||||
bind(ScheduledExecutorService.class)
|
||||
.annotatedWith(Names.named("commonScheduledPool"))
|
||||
.toInstance(commonScheduledPool);
|
||||
|
||||
bind(HttpClient.class).in(Singleton.class);
|
||||
|
||||
bind(FloodgateApi.class).to(SimpleFloodgateApi.class);
|
||||
bind(PlatformInjector.class).to(CommonPlatformInjector.class);
|
||||
|
||||
bind(HandshakeHandlers.class).to(HandshakeHandlersImpl.class);
|
||||
bind(HandshakeHandlersImpl.class).in(Singleton.class);
|
||||
|
||||
bind(PacketHandlers.class).to(PacketHandlersImpl.class);
|
||||
bind(PacketHandlersImpl.class).asEagerSingleton();
|
||||
|
||||
install(new AutoBindModule());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public FloodgateConfig floodgateConfig(ConfigLoader configLoader) {
|
||||
return configLoader.load();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public PlayerLink playerLink(PlayerLinkHolder linkLoader) {
|
||||
return linkLoader.load();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -92,34 +148,13 @@ public class CommonModule extends AbstractModule {
|
||||
return dataDirectory;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public FloodgateConfigHolder configHolder() {
|
||||
return new FloodgateConfigHolder();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ConfigLoader configLoader(
|
||||
@Named("configClass") Class<? extends FloodgateConfig> configClass,
|
||||
KeyProducer producer,
|
||||
FloodgateCipher cipher,
|
||||
FloodgateLogger logger) {
|
||||
return new ConfigLoader(dataDirectory, configClass, producer, cipher, logger);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public LanguageManager languageLoader(
|
||||
FloodgateConfigHolder configHolder,
|
||||
FloodgateLogger logger) {
|
||||
return new LanguageManager(configHolder, logger);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public HandshakeHandlersImpl handshakeHandlers() {
|
||||
return new HandshakeHandlersImpl();
|
||||
FloodgateCipher cipher) {
|
||||
return new ConfigLoader(dataDirectory, configClass, producer, cipher);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -128,13 +163,14 @@ public class CommonModule extends AbstractModule {
|
||||
HandshakeHandlersImpl handshakeHandlers,
|
||||
SimpleFloodgateApi api,
|
||||
FloodgateCipher cipher,
|
||||
FloodgateConfigHolder configHolder,
|
||||
FloodgateConfig config,
|
||||
SkinUploadManager skinUploadManager,
|
||||
@Named("playerAttribute") AttributeKey<FloodgatePlayer> playerAttribute,
|
||||
FloodgateLogger logger) {
|
||||
FloodgateLogger logger,
|
||||
LanguageManager languageManager) {
|
||||
|
||||
return new FloodgateHandshakeHandler(handshakeHandlers, api, cipher, configHolder,
|
||||
skinUploadManager, playerAttribute, logger);
|
||||
return new FloodgateHandshakeHandler(handshakeHandlers, api, cipher, config,
|
||||
skinUploadManager, playerAttribute, logger, languageManager);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -145,17 +181,16 @@ public class CommonModule extends AbstractModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SkinUploadManager skinUploadManager(
|
||||
FloodgateApi api,
|
||||
SkinApplier skinApplier,
|
||||
FloodgateLogger logger) {
|
||||
return new SkinUploadManager(api, skinApplier, logger);
|
||||
@Named("gitBranch")
|
||||
public String gitBranch() {
|
||||
return Constants.GIT_BRANCH;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public NewsChecker newsChecker(CommandUtil commandUtil, FloodgateLogger logger) {
|
||||
return new NewsChecker(commandUtil, logger, Constants.GIT_BRANCH, Constants.BUILD_NUMBER);
|
||||
@Named("buildNumber")
|
||||
public int buildNumber() {
|
||||
return Constants.BUILD_NUMBER;
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -31,12 +31,8 @@ import com.google.inject.name.Named;
|
||||
import java.nio.file.Path;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||
|
||||
public final class ProxyCommonModule extends CommonModule {
|
||||
public ProxyCommonModule(Path dataDirectory) {
|
||||
@@ -46,7 +42,15 @@ public final class ProxyCommonModule extends CommonModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
|
||||
bind(SimpleFloodgateApi.class).to(ProxyFloodgateApi.class);
|
||||
bind(ProxyFloodgateApi.class).in(Singleton.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ProxyFloodgateConfig proxyFloodgateConfig(FloodgateConfig config) {
|
||||
return (ProxyFloodgateConfig) config;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -55,14 +59,4 @@ public final class ProxyCommonModule extends CommonModule {
|
||||
public Class<? extends FloodgateConfig> floodgateConfigClass() {
|
||||
return ProxyFloodgateConfig.class;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ProxyFloodgateApi proxyFloodgateApi(
|
||||
PluginMessageManager pluginMessageManager,
|
||||
FloodgateConfigHolder configHolder,
|
||||
FloodgateLogger logger,
|
||||
FloodgateCipher cipher) {
|
||||
return new ProxyFloodgateApi(pluginMessageManager, configHolder, logger, cipher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,29 +30,23 @@ import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import java.nio.file.Path;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||
|
||||
public final class ServerCommonModule extends CommonModule {
|
||||
public ServerCommonModule(Path dataDirectory) {
|
||||
super(dataDirectory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
super.configure();
|
||||
bind(SimpleFloodgateApi.class).in(Singleton.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("configClass")
|
||||
public Class<? extends FloodgateConfig> floodgateConfigClass() {
|
||||
return FloodgateConfig.class;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SimpleFloodgateApi floodgateApi(
|
||||
PluginMessageManager pluginMessageManager,
|
||||
FloodgateConfigHolder configHolder,
|
||||
FloodgateLogger logger) {
|
||||
return new SimpleFloodgateApi(pluginMessageManager, configHolder, logger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,42 +27,50 @@ package org.geysermc.floodgate.news;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.news.data.AnnouncementData;
|
||||
import org.geysermc.floodgate.news.data.BuildSpecificData;
|
||||
import org.geysermc.floodgate.news.data.CheckAfterData;
|
||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||
import org.geysermc.floodgate.util.AutoBind;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpUtils;
|
||||
import org.geysermc.floodgate.util.HttpUtils.HttpResponse;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.HttpClient.HttpResponse;
|
||||
|
||||
@AutoBind
|
||||
public class NewsChecker {
|
||||
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
|
||||
private final CommandUtil commandUtil;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
private final Map<Integer, NewsItem> activeNewsItems = new HashMap<>();
|
||||
private final String branch;
|
||||
private final int build;
|
||||
@Inject
|
||||
@Named("commonScheduledPool")
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
@Inject
|
||||
private CommandUtil commandUtil;
|
||||
@Inject
|
||||
private HttpClient httpClient;
|
||||
@Inject
|
||||
private FloodgateLogger logger;
|
||||
|
||||
@Inject
|
||||
@Named("gitBranch")
|
||||
private String branch;
|
||||
@Inject
|
||||
@Named("buildNumber")
|
||||
private int build;
|
||||
|
||||
private boolean firstCheck;
|
||||
|
||||
public NewsChecker(CommandUtil commandUtil, FloodgateLogger logger, String branch, int build) {
|
||||
this.commandUtil = commandUtil;
|
||||
this.logger = logger;
|
||||
this.branch = branch;
|
||||
this.build = build;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void start() {
|
||||
executorService.scheduleWithFixedDelay(this::checkNews, 0, 30, TimeUnit.MINUTES);
|
||||
}
|
||||
@@ -72,10 +80,10 @@ public class NewsChecker {
|
||||
}
|
||||
|
||||
private void checkNews() {
|
||||
HttpResponse<JsonArray> response =
|
||||
HttpUtils.getSilent(
|
||||
Constants.NEWS_OVERVIEW_URL + Constants.NEWS_PROJECT_NAME,
|
||||
JsonArray.class);
|
||||
HttpResponse<JsonArray> response = httpClient.getSilent(
|
||||
Constants.NEWS_OVERVIEW_URL + Constants.NEWS_PROJECT_NAME,
|
||||
JsonArray.class
|
||||
);
|
||||
|
||||
JsonArray array = response.getResponse();
|
||||
|
||||
@@ -193,8 +201,4 @@ public class NewsChecker {
|
||||
handleNewsItem(null, item, action);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.platform.command;
|
||||
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.player.UserAudience;
|
||||
|
||||
public abstract class FloodgateSubCommand {
|
||||
public abstract String name();
|
||||
|
||||
public abstract String description();
|
||||
|
||||
public abstract Permission permission();
|
||||
|
||||
public abstract void execute(CommandContext<UserAudience> context);
|
||||
}
|
||||
@@ -23,33 +23,32 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate;
|
||||
package org.geysermc.floodgate.platform.command;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class SpigotPlatform extends FloodgatePlatform {
|
||||
@Inject private JavaPlugin plugin;
|
||||
public abstract class SubCommands {
|
||||
private final Set<Class<? extends FloodgateSubCommand>> toRegister = new HashSet<>();
|
||||
private Set<FloodgateSubCommand> subCommands;
|
||||
|
||||
public void defineSubCommand(Class<? extends FloodgateSubCommand> subCommandClass) {
|
||||
toRegister.add(subCommandClass);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public SpigotPlatform(FloodgateApi api, PlatformInjector platformInjector,
|
||||
FloodgateLogger logger, Injector injector) {
|
||||
super(api, platformInjector, logger, injector);
|
||||
public void registerSubCommands(Injector injector) {
|
||||
Set<FloodgateSubCommand> subCommandSet = new HashSet<>();
|
||||
for (Class<? extends FloodgateSubCommand> subCommand : toRegister) {
|
||||
subCommandSet.add(injector.getInstance(subCommand));
|
||||
}
|
||||
subCommands = Collections.unmodifiableSet(subCommandSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enable(Module... postInitializeModules) {
|
||||
boolean success = super.enable(postInitializeModules);
|
||||
if (!success) {
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
protected Set<FloodgateSubCommand> subCommands() {
|
||||
return subCommands;
|
||||
}
|
||||
}
|
||||
@@ -48,11 +48,13 @@ import org.geysermc.floodgate.api.handshake.HandshakeData;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.player.PropertyKey;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.skin.SkinUploadManager;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.InvalidFormatException;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@@ -60,27 +62,30 @@ public final class FloodgateHandshakeHandler {
|
||||
private final HandshakeHandlersImpl handshakeHandlers;
|
||||
private final SimpleFloodgateApi api;
|
||||
private final FloodgateCipher cipher;
|
||||
private final FloodgateConfigHolder configHolder;
|
||||
private final FloodgateConfig config;
|
||||
private final SkinUploadManager skinUploadManager;
|
||||
private final AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
private final FloodgateLogger logger;
|
||||
private final LanguageManager languageManager;
|
||||
|
||||
public FloodgateHandshakeHandler(
|
||||
HandshakeHandlersImpl handshakeHandlers,
|
||||
SimpleFloodgateApi api,
|
||||
FloodgateCipher cipher,
|
||||
FloodgateConfigHolder configHolder,
|
||||
FloodgateConfig config,
|
||||
SkinUploadManager skinUploadManager,
|
||||
AttributeKey<FloodgatePlayer> playerAttribute,
|
||||
FloodgateLogger logger) {
|
||||
FloodgateLogger logger,
|
||||
LanguageManager languageManager) {
|
||||
|
||||
this.handshakeHandlers = handshakeHandlers;
|
||||
this.api = api;
|
||||
this.cipher = cipher;
|
||||
this.configHolder = configHolder;
|
||||
this.config = config;
|
||||
this.skinUploadManager = skinUploadManager;
|
||||
this.playerAttribute = playerAttribute;
|
||||
this.logger = logger;
|
||||
this.languageManager = languageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +139,7 @@ public final class FloodgateHandshakeHandler {
|
||||
);
|
||||
} catch (Exception e) {
|
||||
// all the other exceptions are caused by invalid/tempered Floodgate data
|
||||
if (configHolder.get().isDebug()) {
|
||||
if (config.isDebug()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -207,11 +212,16 @@ public final class FloodgateHandshakeHandler {
|
||||
|
||||
try {
|
||||
HandshakeData handshakeData = new HandshakeDataImpl(
|
||||
channel, true, bedrockData.clone(), configHolder.get(),
|
||||
channel, true, bedrockData.clone(), config,
|
||||
linkedPlayer != null ? linkedPlayer.clone() : null, hostname);
|
||||
|
||||
if (configHolder.get().getPlayerLink().isRequireLink() && linkedPlayer == null) {
|
||||
handshakeData.setDisconnectReason("floodgate.core.not_linked");
|
||||
if (config.getPlayerLink().isRequireLink() && linkedPlayer == null) {
|
||||
String reason = languageManager.getString(
|
||||
"floodgate.core.not_linked",
|
||||
bedrockData.getLanguageCode(),
|
||||
Constants.LINK_INFO_URL
|
||||
);
|
||||
handshakeData.setDisconnectReason(reason);
|
||||
}
|
||||
|
||||
handshakeHandlers.callHandshakeHandlers(handshakeData);
|
||||
@@ -245,7 +255,7 @@ public final class FloodgateHandshakeHandler {
|
||||
String hostname) {
|
||||
|
||||
HandshakeData handshakeData = new HandshakeDataImpl(channel, bedrockData != null,
|
||||
bedrockData, configHolder.get(), null, hostname);
|
||||
bedrockData, config, null, hostname);
|
||||
handshakeHandlers.callHandshakeHandlers(handshakeData);
|
||||
|
||||
return new HandshakeResult(resultType, handshakeData, bedrockData, null);
|
||||
|
||||
@@ -32,7 +32,6 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.InstanceHolder;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
@@ -72,7 +71,7 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
|
||||
private Map<String, PropertyKey> stringToPropertyKey;
|
||||
|
||||
static FloodgatePlayerImpl from(BedrockData data, HandshakeData handshakeData) {
|
||||
FloodgateApi api = InstanceHolder.getApi();
|
||||
FloodgateApi api = FloodgateApi.getInstance();
|
||||
|
||||
UUID javaUniqueId = Utils.getJavaUuid(data.getXuid());
|
||||
|
||||
|
||||
@@ -36,14 +36,12 @@ public interface PluginMessageChannel {
|
||||
|
||||
Result handleProxyCall(
|
||||
byte[] data,
|
||||
UUID targetUuid,
|
||||
String targetUsername,
|
||||
Identity targetIdentity,
|
||||
UUID sourceUuid,
|
||||
String sourceUsername,
|
||||
Identity sourceIdentity);
|
||||
Identity sourceIdentity
|
||||
);
|
||||
|
||||
Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername);
|
||||
Result handleServerCall(byte[] data, UUID playerUuid, String playerUsername);
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
|
||||
@@ -28,6 +28,7 @@ package org.geysermc.floodgate.pluginmessage.channel;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.inject.Inject;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -41,7 +42,8 @@ import org.geysermc.floodgate.pluginmessage.PluginMessageChannel;
|
||||
|
||||
public class FormChannel implements PluginMessageChannel {
|
||||
private final FormDefinitions formDefinitions = FormDefinitions.instance();
|
||||
private final Short2ObjectMap<Form> storedForms = new Short2ObjectOpenHashMap<>();
|
||||
private final Short2ObjectMap<Form> storedForms =
|
||||
Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>());
|
||||
private final AtomicInteger nextFormId = new AtomicInteger(0);
|
||||
|
||||
@Inject private PluginMessageUtils pluginMessageUtils;
|
||||
@@ -56,13 +58,10 @@ public class FormChannel implements PluginMessageChannel {
|
||||
@Override
|
||||
public Result handleProxyCall(
|
||||
byte[] data,
|
||||
UUID targetUuid,
|
||||
String targetUsername,
|
||||
Identity targetIdentity,
|
||||
UUID sourceUuid,
|
||||
String sourceUsername,
|
||||
Identity sourceIdentity) {
|
||||
|
||||
Identity sourceIdentity
|
||||
) {
|
||||
if (sourceIdentity == Identity.SERVER) {
|
||||
// send it to the client
|
||||
return Result.forward();
|
||||
@@ -89,7 +88,7 @@ public class FormChannel implements PluginMessageChannel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername) {
|
||||
public Result handleServerCall(byte[] data, UUID playerUuid, String playerUsername) {
|
||||
callResponseConsumer(data);
|
||||
return Result.handled();
|
||||
}
|
||||
|
||||
@@ -40,23 +40,26 @@ public final class PacketChannel implements PluginMessageChannel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result handleProxyCall(byte[] data, UUID targetUuid, String targetUsername,
|
||||
Identity targetIdentity, UUID sourceUuid, String sourceUsername,
|
||||
Identity sourceIdentity) {
|
||||
public Result handleProxyCall(
|
||||
byte[] data,
|
||||
UUID sourceUuid,
|
||||
String sourceUsername,
|
||||
Identity sourceIdentity
|
||||
) {
|
||||
if (sourceIdentity == Identity.SERVER) {
|
||||
// send it to the client
|
||||
return Result.forward();
|
||||
}
|
||||
|
||||
if (sourceIdentity == Identity.PLAYER) {
|
||||
return handleServerCall(data, targetUuid, targetUsername);
|
||||
return handleServerCall(data, sourceUuid, sourceUsername);
|
||||
}
|
||||
|
||||
return Result.handled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername) {
|
||||
public Result handleServerCall(byte[] data, UUID playerUuid, String playerUsername) {
|
||||
return Result.kick("Cannot send packets from Geyser/Floodgate to Floodgate");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.floodgate.pluginmessage.channel;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
@@ -36,7 +35,7 @@ import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageChannel;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
|
||||
public class SkinChannel implements PluginMessageChannel {
|
||||
@Inject private FloodgateApi api;
|
||||
@@ -51,16 +50,13 @@ public class SkinChannel implements PluginMessageChannel {
|
||||
@Override
|
||||
public Result handleProxyCall(
|
||||
byte[] data,
|
||||
UUID targetUuid,
|
||||
String targetUsername,
|
||||
Identity targetIdentity,
|
||||
UUID sourceUuid,
|
||||
String sourceUsername,
|
||||
Identity sourceIdentity) {
|
||||
|
||||
Identity sourceIdentity
|
||||
) {
|
||||
// we can only get skins from Geyser (client)
|
||||
if (sourceIdentity == Identity.PLAYER) {
|
||||
Result result = handleServerCall(data, targetUuid, targetUsername);
|
||||
Result result = handleServerCall(data, sourceUuid, sourceUsername);
|
||||
// aka translate 'handled' into 'forward' when send-floodgate-data is enabled
|
||||
if (!result.isAllowed() && result.getReason() == null) {
|
||||
if (config.isProxy() && ((ProxyFloodgateConfig) config).isSendFloodgateData()) {
|
||||
@@ -78,8 +74,8 @@ public class SkinChannel implements PluginMessageChannel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername) {
|
||||
FloodgatePlayer floodgatePlayer = api.getPlayer(targetUuid);
|
||||
public Result handleServerCall(byte[] data, UUID playerUuid, String playerUsername) {
|
||||
FloodgatePlayer floodgatePlayer = api.getPlayer(playerUuid);
|
||||
if (floodgatePlayer == null) {
|
||||
return Result.kick("Player sent skins data for a non-Floodgate player");
|
||||
}
|
||||
@@ -92,18 +88,10 @@ public class SkinChannel implements PluginMessageChannel {
|
||||
return Result.kick("Got invalid skin data");
|
||||
}
|
||||
|
||||
if (floodgatePlayer.isLinked()) {
|
||||
return Result.handled();
|
||||
}
|
||||
|
||||
String value = split[0];
|
||||
String signature = split[1];
|
||||
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("value", value);
|
||||
result.addProperty("signature", signature);
|
||||
|
||||
SkinData skinData = new SkinData(value, signature);
|
||||
SkinDataImpl skinData = new SkinDataImpl(value, signature);
|
||||
|
||||
floodgatePlayer.addProperty(PropertyKey.SKIN_UPLOADED, skinData);
|
||||
skinApplier.applySkin(floodgatePlayer, skinData);
|
||||
|
||||
@@ -42,27 +42,24 @@ public class TransferChannel implements PluginMessageChannel {
|
||||
@Override
|
||||
public Result handleProxyCall(
|
||||
byte[] data,
|
||||
UUID targetUuid,
|
||||
String targetUsername,
|
||||
Identity targetIdentity,
|
||||
UUID sourceUuid,
|
||||
String sourceUsername,
|
||||
Identity sourceIdentity) {
|
||||
|
||||
Identity sourceIdentity
|
||||
) {
|
||||
if (sourceIdentity == Identity.SERVER) {
|
||||
// send it to the client
|
||||
return Result.forward();
|
||||
}
|
||||
|
||||
if (sourceIdentity == Identity.PLAYER) {
|
||||
handleServerCall(data, targetUuid, targetUsername);
|
||||
handleServerCall(data, sourceUuid, sourceUsername);
|
||||
}
|
||||
|
||||
return Result.handled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername) {
|
||||
public Result handleServerCall(byte[] data, UUID playerUuid, String playerUsername) {
|
||||
return Result.kick("I'm sorry, I'm unable to transfer a server :(");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,16 @@
|
||||
|
||||
package org.geysermc.floodgate.skin;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public interface SkinApplier {
|
||||
void applySkin(FloodgatePlayer floodgatePlayer, SkinData skinData);
|
||||
/**
|
||||
* Apply a skin to a {@link FloodgatePlayer player}
|
||||
*
|
||||
* @param floodgatePlayer player to apply skin to
|
||||
* @param skinData data for skin to apply to player
|
||||
*/
|
||||
void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.skin;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import java.util.Objects;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
|
||||
public class SkinDataImpl implements SkinData {
|
||||
private final String value;
|
||||
private final String signature;
|
||||
|
||||
public SkinDataImpl(@NonNull String value, @NonNull String signature) {
|
||||
this.value = Objects.requireNonNull(value);
|
||||
this.signature = Objects.requireNonNull(signature);
|
||||
}
|
||||
|
||||
public static SkinData from(JsonObject data) {
|
||||
return new SkinDataImpl(
|
||||
data.get("value").getAsString(),
|
||||
data.get("signature").getAsString()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String signature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
@@ -25,21 +25,26 @@
|
||||
|
||||
package org.geysermc.floodgate.skin;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Listener
|
||||
@Singleton
|
||||
public final class SkinUploadManager {
|
||||
private final Int2ObjectMap<SkinUploadSocket> connections =
|
||||
Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
|
||||
|
||||
private final FloodgateApi api;
|
||||
private final SkinApplier applier;
|
||||
private final FloodgateLogger logger;
|
||||
@Inject private FloodgateApi api;
|
||||
@Inject private SkinApplier applier;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
public void addConnectionIfNeeded(int id, String verifyCode) {
|
||||
connections.computeIfAbsent(id, (ignored) -> {
|
||||
@@ -53,4 +58,16 @@ public final class SkinUploadManager {
|
||||
public void removeConnection(int id, SkinUploadSocket socket) {
|
||||
connections.remove(id, socket);
|
||||
}
|
||||
|
||||
public void closeAllSockets() {
|
||||
for (SkinUploadSocket socket : connections.values()) {
|
||||
socket.close();
|
||||
}
|
||||
connections.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onShutdown(ShutdownEvent ignored) {
|
||||
closeAllSockets();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.net.URI;
|
||||
import javax.net.ssl.SSLException;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.player.PropertyKey;
|
||||
@@ -61,8 +62,8 @@ final class SkinUploadSocket extends WebSocketClient {
|
||||
SkinUploadManager uploadManager,
|
||||
FloodgateApi api,
|
||||
SkinApplier applier,
|
||||
FloodgateLogger logger) {
|
||||
|
||||
FloodgateLogger logger
|
||||
) {
|
||||
super(getWebsocketUri(id, verifyCode));
|
||||
this.id = id;
|
||||
this.verifyCode = verifyCode;
|
||||
@@ -83,7 +84,7 @@ final class SkinUploadSocket extends WebSocketClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ServerHandshake handshakedata) {
|
||||
public void onOpen(ServerHandshake ignored) {
|
||||
setConnectionLostTimeout(11);
|
||||
}
|
||||
|
||||
@@ -114,10 +115,14 @@ final class SkinUploadSocket extends WebSocketClient {
|
||||
player.getCorrectUsername());
|
||||
return;
|
||||
}
|
||||
|
||||
SkinData skinData = SkinDataImpl.from(message.getAsJsonObject("data"));
|
||||
applier.applySkin(player, skinData);
|
||||
|
||||
// legacy stuff,
|
||||
// will be removed shortly after or during the Floodgate-Geyser integration
|
||||
if (!player.isLinked()) {
|
||||
SkinData skinData = SkinData.from(message.getAsJsonObject("data"));
|
||||
player.addProperty(PropertyKey.SKIN_UPLOADED, skinData);
|
||||
applier.applySkin(player, skinData);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
38
core/src/main/java/org/geysermc/floodgate/util/AutoBind.java
Normal file
38
core/src/main/java/org/geysermc/floodgate/util/AutoBind.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Automatically binds an instance of itself as an eager singleton during the post-initialise stage
|
||||
* of the FloodgatePlatform. Add the annotation to a class, and it should automatically create an
|
||||
* instance and inject it for you.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AutoBind {
|
||||
}
|
||||
@@ -27,13 +27,15 @@ package org.geysermc.floodgate.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -42,29 +44,36 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
// resources are properly closed and ignoring the original stack trace is intended
|
||||
@SuppressWarnings({"PMD.CloseResource", "PMD.PreserveStackTrace"})
|
||||
public class HttpUtils {
|
||||
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
@Singleton
|
||||
public class HttpClient {
|
||||
private static final String USER_AGENT = "GeyserMC/Floodgate";
|
||||
|
||||
public static CompletableFuture<DefaultHttpResponse> asyncGet(String urlString) {
|
||||
return CompletableFuture.supplyAsync(() -> get(urlString), EXECUTOR_SERVICE);
|
||||
private final Gson gson = new Gson();
|
||||
@Inject
|
||||
@Named("commonPool")
|
||||
private ExecutorService executorService;
|
||||
|
||||
public CompletableFuture<DefaultHttpResponse> asyncGet(String urlString) {
|
||||
return CompletableFuture.supplyAsync(() -> get(urlString), executorService);
|
||||
}
|
||||
|
||||
public static DefaultHttpResponse get(String urlString) {
|
||||
public <T> CompletableFuture<HttpResponse<T>> asyncGet(String urlString, Class<T> response) {
|
||||
return CompletableFuture.supplyAsync(() -> get(urlString, response), executorService);
|
||||
}
|
||||
|
||||
public DefaultHttpResponse get(String urlString) {
|
||||
return readDefaultResponse(request(urlString));
|
||||
}
|
||||
|
||||
public static <T> HttpResponse<T> get(String urlString, Class<T> clazz) {
|
||||
public <T> HttpResponse<T> get(String urlString, Class<T> clazz) {
|
||||
return readResponse(request(urlString), clazz);
|
||||
}
|
||||
|
||||
public static <T> HttpResponse<T> getSilent(String urlString, Class<T> clazz) {
|
||||
public <T> HttpResponse<T> getSilent(String urlString, Class<T> clazz) {
|
||||
return readResponseSilent(request(urlString), clazz);
|
||||
}
|
||||
|
||||
private static HttpURLConnection request(String urlString) {
|
||||
private HttpURLConnection request(String urlString) {
|
||||
HttpURLConnection connection;
|
||||
|
||||
try {
|
||||
@@ -88,18 +97,20 @@ public class HttpUtils {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static <T> HttpResponse<T> readResponse(HttpURLConnection connection, Class<T> clazz) {
|
||||
private <T> HttpResponse<T> readResponse(HttpURLConnection connection, Class<T> clazz) {
|
||||
InputStreamReader streamReader = createReader(connection);
|
||||
if (streamReader == null) {
|
||||
return new HttpResponse<>(-1, null);
|
||||
}
|
||||
|
||||
int responseCode = -1;
|
||||
try {
|
||||
int responseCode = connection.getResponseCode();
|
||||
T response = GSON.fromJson(streamReader, clazz);
|
||||
responseCode = connection.getResponseCode();
|
||||
T response = gson.fromJson(streamReader, clazz);
|
||||
return new HttpResponse<>(responseCode, response);
|
||||
} catch (Exception ignored) {
|
||||
return new HttpResponse<>(-1, null);
|
||||
// e.g. when the response isn't JSON
|
||||
return new HttpResponse<>(responseCode, null);
|
||||
} finally {
|
||||
try {
|
||||
streamReader.close();
|
||||
@@ -109,9 +120,7 @@ public class HttpUtils {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static <T> HttpResponse<T> readResponseSilent(
|
||||
HttpURLConnection connection,
|
||||
Class<T> clazz) {
|
||||
private <T> HttpResponse<T> readResponseSilent(HttpURLConnection connection, Class<T> clazz) {
|
||||
try {
|
||||
return readResponse(connection, clazz);
|
||||
} catch (Exception ignored) {
|
||||
@@ -120,7 +129,7 @@ public class HttpUtils {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static DefaultHttpResponse readDefaultResponse(HttpURLConnection connection) {
|
||||
private DefaultHttpResponse readDefaultResponse(HttpURLConnection connection) {
|
||||
InputStreamReader streamReader = createReader(connection);
|
||||
if (streamReader == null) {
|
||||
return new DefaultHttpResponse(-1, null);
|
||||
@@ -128,7 +137,7 @@ public class HttpUtils {
|
||||
|
||||
try {
|
||||
int responseCode = connection.getResponseCode();
|
||||
JsonObject response = GSON.fromJson(streamReader, JsonObject.class);
|
||||
JsonObject response = gson.fromJson(streamReader, JsonObject.class);
|
||||
return new DefaultHttpResponse(responseCode, response);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException("Failed to read response", exception);
|
||||
@@ -141,16 +150,12 @@ public class HttpUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static InputStreamReader createReader(HttpURLConnection connection) {
|
||||
private InputStreamReader createReader(HttpURLConnection connection) {
|
||||
InputStream stream;
|
||||
try {
|
||||
stream = connection.getInputStream();
|
||||
} catch (Exception exception) {
|
||||
try {
|
||||
stream = connection.getErrorStream();
|
||||
} catch (Exception exception1) {
|
||||
throw new RuntimeException("Both the input and the error stream failed?!");
|
||||
}
|
||||
stream = connection.getErrorStream();
|
||||
}
|
||||
|
||||
// it's null for example when it couldn't connect to the server
|
||||
@@ -26,30 +26,27 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||
|
||||
/**
|
||||
* Manages translations for strings in Floodgate
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public final class LanguageManager {
|
||||
private final Map<String, Properties> localeMappings = new HashMap<>();
|
||||
private final FloodgateConfigHolder configHolder;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
/**
|
||||
* The locale used in console and as a fallback
|
||||
@@ -71,24 +68,15 @@ public final class LanguageManager {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return logger != null && defaultLocale != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load the log's locale file once a string has been requested
|
||||
*/
|
||||
@Inject
|
||||
private void init() {
|
||||
if (!loadLocale("en_US")) {// Fallback
|
||||
logger.error("Failed to load the fallback language. This will likely cause errors!");
|
||||
}
|
||||
|
||||
FloodgateConfig config = configHolder.get();
|
||||
if (config == null) {
|
||||
// :thonk:
|
||||
return;
|
||||
}
|
||||
|
||||
defaultLocale = formatLocale(config.getDefaultLocale());
|
||||
|
||||
if (isValidLanguage(defaultLocale)) {
|
||||
@@ -125,21 +113,11 @@ public final class LanguageManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
InputStream localeStream = LanguageManager.class.getClassLoader().getResourceAsStream(
|
||||
"languages/texts/" + formatLocale + ".properties");
|
||||
Properties properties =
|
||||
Utils.readProperties("languages/texts/" + formatLocale + ".properties");
|
||||
|
||||
// load the locale
|
||||
if (localeStream != null) {
|
||||
Properties localeProp = new Properties();
|
||||
|
||||
try (Reader reader = new InputStreamReader(localeStream, StandardCharsets.UTF_8)) {
|
||||
localeProp.load(reader);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Failed to load Floodgate locale", e);
|
||||
}
|
||||
|
||||
// insert the locale into the mappings
|
||||
localeMappings.put(formatLocale, localeProp);
|
||||
if (properties != null) {
|
||||
localeMappings.put(formatLocale, properties);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -167,14 +145,6 @@ public final class LanguageManager {
|
||||
* @return translated string or "key arg1, arg2 (etc.)" if it was not found in the given locale
|
||||
*/
|
||||
public String getString(String key, String locale, Object... values) {
|
||||
if (!isLoaded()) {
|
||||
init();
|
||||
// we can skip everything if the LanguageManager can't be loaded yet
|
||||
if (!isLoaded()) {
|
||||
return formatNotFound(key, values);
|
||||
}
|
||||
}
|
||||
|
||||
Properties properties = localeMappings.get(locale);
|
||||
String formatString = null;
|
||||
|
||||
|
||||
@@ -26,24 +26,29 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Named;
|
||||
import org.bstats.MetricsBase;
|
||||
import org.bstats.charts.DrilldownPie;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bstats.charts.SingleLineChart;
|
||||
import org.bstats.json.JsonObjectBuilder;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig.MetricsConfig;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.platform.util.PlatformUtils;
|
||||
|
||||
@Listener
|
||||
@AutoBind
|
||||
public final class Metrics {
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
@@ -144,4 +149,9 @@ public final class Metrics {
|
||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onShutdown(ShutdownEvent ignored) {
|
||||
metricsBase.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.event.lifecycle.PostEnableEvent;
|
||||
|
||||
@AutoBind
|
||||
@Listener
|
||||
public final class PostEnableMessages {
|
||||
private final List<String> messages = new ArrayList<>();
|
||||
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private FloodgateLogger logger;
|
||||
@Inject
|
||||
@Named("commonScheduledPool")
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
public void add(String[] message, Object... args) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("\n**********************************\n");
|
||||
for (String part : message) {
|
||||
builder.append("* ").append(part).append('\n');
|
||||
}
|
||||
builder.append("**********************************");
|
||||
|
||||
messages.add(MessageFormatter.format(builder.toString(), args));
|
||||
}
|
||||
|
||||
@Inject
|
||||
private void init() {
|
||||
registerPrefixMessages();
|
||||
}
|
||||
|
||||
private void registerPrefixMessages() {
|
||||
String prefix = config.getRawUsernamePrefix();
|
||||
|
||||
if (prefix.isEmpty()) {
|
||||
add(new String[]{
|
||||
"You specified an empty prefix in your Floodgate config for Bedrock players!",
|
||||
"Should a Java player join and a Bedrock player join with the same username, unwanted results and conflicts will happen!",
|
||||
"We strongly recommend using . as the prefix, but other alternatives that will not conflict include: +, - and *"
|
||||
});
|
||||
} else if (!Utils.isUniquePrefix(prefix)) {
|
||||
add(new String[]{
|
||||
"The prefix you entered in your Floodgate config ({}) could lead to username conflicts!",
|
||||
"Should a Java player join with the username {}Notch, and a Bedrock player join as Notch (who will be given the name {}Notch), unwanted results will happen!",
|
||||
"We strongly recommend using . as the prefix, but other alternatives that will not conflict include: +, - and *"
|
||||
}, prefix, prefix, prefix, prefix);
|
||||
}
|
||||
|
||||
if (prefix.length() >= 16) {
|
||||
add(new String[]{
|
||||
"The prefix you entered in your Floodgate config ({}) is longer than a Java username can be!",
|
||||
"Because of this, we reset the prefix to the default Floodgate prefix (.)"
|
||||
}, prefix);
|
||||
} else if (prefix.length() > 2) {
|
||||
// we only have to warn them if we haven't replaced the prefix
|
||||
add(new String[]{
|
||||
"The prefix you entered in your Floodgate config ({}) is long! ({} characters)",
|
||||
"A prefix is there to prevent username conflicts. However, a long prefix makes the chance of username conflicts higher.",
|
||||
"We strongly recommend using . as the prefix, but other alternatives that will not conflict include: +, - and *"
|
||||
}, prefix, prefix.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPostEnable(PostEnableEvent ignored) {
|
||||
// normally proxies don't have a lot of plugins, so proxies don't need to sleep as long
|
||||
executorService.schedule(
|
||||
() -> messages.forEach(logger::warn),
|
||||
config.isProxy() ? 2 : 5,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
|
||||
public final class PrefixCheckTask {
|
||||
public static void checkAndExecuteDelayed(FloodgateConfig config, FloodgateLogger logger) {
|
||||
if (Utils.isUniquePrefix(config.getUsernamePrefix())) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
// normally proxies don't have a lot of plugins, so proxies don't need to sleep as long
|
||||
try {
|
||||
Thread.sleep(config.isProxy() ? 1000 : 2000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
|
||||
if (config.getUsernamePrefix().isEmpty()) {
|
||||
logger.warn("\n" +
|
||||
"**********************************\n" +
|
||||
"* You specified an empty prefix in your Floodgate config for Bedrock players!\n" +
|
||||
"* Should a Java player join and a Bedrock player join with the same username, unwanted results and conflicts will happen!\n" +
|
||||
"* We strongly recommend using . as the prefix, but other alternatives that will not conflict include: +, - and *\n" +
|
||||
"**********************************");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
"\n" +
|
||||
"**********************************\n" +
|
||||
"* The prefix you entered in your Floodgate config ({}) could lead to username conflicts!\n" +
|
||||
"* Should a Java player join with the username {}Notch, and a Bedrock player join as Notch (who will be given the name {}Notch), unwanted results will happen!\n" +
|
||||
"* We strongly recommend using . as the prefix, but other alternatives that will not conflict include: +, - and *\n" +
|
||||
"**********************************",
|
||||
config.getUsernamePrefix(), config.getUsernamePrefix(),
|
||||
config.getUsernamePrefix(), config.getUsernamePrefix());
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@@ -287,7 +287,7 @@ public final class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a field and cast it to <T>.
|
||||
* Get the value of a field and cast it to T.
|
||||
*
|
||||
* @param instance the instance to get the value from
|
||||
* @param field the field to get the value from
|
||||
@@ -301,7 +301,7 @@ public final class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a field and cast it to <T>.
|
||||
* Get the value of a field and cast it to T.
|
||||
*
|
||||
* @param instance the instance to get the value from
|
||||
* @param fieldName the field to get the value from
|
||||
@@ -424,7 +424,7 @@ public final class ReflectionUtils {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Method getMethod(
|
||||
public static Method getMethodThatReturns(
|
||||
Class<?> clazz,
|
||||
Class<?> returnType,
|
||||
boolean declared,
|
||||
|
||||
@@ -33,21 +33,19 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Utils {
|
||||
private static final Pattern NON_UNIQUE_PREFIX = Pattern.compile("^[a-zA-Z0-9_]{0,16}$");
|
||||
private static final Pattern NON_UNIQUE_PREFIX = Pattern.compile("^\\w{0,16}$");
|
||||
private static final Pattern DATABASE_NAME = Pattern.compile(Constants.DATABASE_NAME_FORMAT);
|
||||
|
||||
/**
|
||||
@@ -66,33 +64,21 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> readAllLines(String resourcePath) throws IOException {
|
||||
InputStream stream = Utils.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||
try (BufferedReader reader = newBufferedReader(stream, StandardCharsets.UTF_8)) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (; ; ) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
result.add(line);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static BufferedReader newBufferedReader(InputStream inputStream, Charset charset) {
|
||||
CharsetDecoder decoder = charset.newDecoder();
|
||||
Reader reader = new InputStreamReader(inputStream, decoder);
|
||||
return new BufferedReader(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a properties resource file
|
||||
* @param resourceFile the resource file to read
|
||||
* @return the properties file if the resource exists, otherwise null
|
||||
* @throws AssertionError if something went wrong while readin the resource file
|
||||
*/
|
||||
public static Properties readProperties(String resourceFile) {
|
||||
Properties properties = new Properties();
|
||||
try (InputStream is = Utils.class.getClassLoader().getResourceAsStream(resourceFile)) {
|
||||
properties.load(is);
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
properties.load(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new AssertionError("Failed to read properties file", e);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
@@ -151,4 +137,42 @@ public class Utils {
|
||||
future.completeExceptionally(ex);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all the classes that are annotated by a given annotation.
|
||||
* Keep in mind that these are from a set of generated annotations generated
|
||||
* at compile time by the annotation processor, meaning that arbitrary annotations
|
||||
* cannot be passed into this method and expected to get a set of classes back.
|
||||
*
|
||||
* @param annotationClass the annotation class
|
||||
* @return a set of all the classes annotated by the given annotation
|
||||
*/
|
||||
public static Set<Class<?>> getGeneratedClassesForAnnotation(Class<? extends Annotation> annotationClass) {
|
||||
return getGeneratedClassesForAnnotation(annotationClass.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of all the classes that are annotated by a given annotation.
|
||||
* Keep in mind that these are from a set of generated annotations generated
|
||||
* at compile time by the annotation processor, meaning that arbitrary annotations
|
||||
* cannot be passed into this method and expected to have a set of classes
|
||||
* returned back.
|
||||
*
|
||||
* @param input the fully qualified name of the annotation
|
||||
* @return a set of all the classes annotated by the given annotation
|
||||
*/
|
||||
public static Set<Class<?>> getGeneratedClassesForAnnotation(String input) {
|
||||
try (InputStream annotatedClass = Utils.class.getClassLoader().getResourceAsStream(input);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(annotatedClass))) {
|
||||
return reader.lines().map(className -> {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new RuntimeException("Failed to find class for annotation " + input, ex);
|
||||
}
|
||||
}).collect(Collectors.toSet());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Submodule core/src/main/resources/languages updated: 38cb4a52df...204f4fe492
@@ -26,12 +26,12 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
public final class Constants {
|
||||
public static final String VERSION = "${floodgateVersion}";
|
||||
public static final int BUILD_NUMBER = Integer.parseInt("${buildNumber}");
|
||||
public static final String GIT_BRANCH = "${branch}";
|
||||
public static final String VERSION = "@floodgateVersion@";
|
||||
public static final int BUILD_NUMBER = Integer.parseInt("@buildNumber@");
|
||||
public static final String GIT_BRANCH = "@branch@";
|
||||
public static final int METRICS_ID = 14649;
|
||||
|
||||
public static final char COLOR_CHAR = '§';
|
||||
public static final char COLOR_CHAR = '\u00A7';
|
||||
|
||||
public static final boolean DEBUG_MODE = false;
|
||||
public static final boolean PRINT_ALL_PACKETS = false;
|
||||
@@ -55,7 +55,7 @@ public final class Constants {
|
||||
|
||||
public static final String NTP_SERVER = "time.cloudflare.com";
|
||||
public static final String INTERNAL_ERROR_MESSAGE =
|
||||
"An internal error happened while handling Floodgate data." +
|
||||
"An internal error happened while handling Floodgate data." +
|
||||
" Try logging in again or contact a server administrator if the issue persists.";
|
||||
public static final String UNSUPPORTED_DATA_VERSION =
|
||||
"Received an unsupported Floodgate data version." +
|
||||
4
database/mysql/.editorconfig
Normal file
4
database/mysql/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
[*]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_continuation_indent_size = 4
|
||||
@@ -1,10 +1,17 @@
|
||||
val mariadbClientVersion = "2.7.4"
|
||||
|
||||
dependencies {
|
||||
provided(projects.core)
|
||||
implementation("org.mariadb.jdbc", "mariadb-java-client" , mariadbClientVersion)
|
||||
provided(projects.core)
|
||||
|
||||
// update HikariCP when we move to Java 11+
|
||||
implementation("com.zaxxer", "HikariCP", "4.0.3")
|
||||
|
||||
implementation("com.mysql", "mysql-connector-j", "8.0.32") {
|
||||
exclude("com.google.protobuf", "protobuf-java")
|
||||
}
|
||||
}
|
||||
|
||||
description = "The Floodgate database extension for MySQL"
|
||||
|
||||
relocate("org.mariadb")
|
||||
// relocate everything from mysql-connector-j and HikariCP
|
||||
relocate("com.mysql")
|
||||
relocate("com.zaxxer.hikari")
|
||||
relocate("org.slf4j")
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.floodgate.database;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.sql.Connection;
|
||||
@@ -43,309 +45,296 @@ import org.geysermc.floodgate.database.config.MysqlConfig;
|
||||
import org.geysermc.floodgate.link.CommonPlayerLink;
|
||||
import org.geysermc.floodgate.link.LinkRequestImpl;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
import org.mariadb.jdbc.MariaDbPoolDataSource;
|
||||
|
||||
public class MysqlDatabase extends CommonPlayerLink {
|
||||
private MariaDbPoolDataSource pool;
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
getLogger().info("Connecting to a MySQL-like database...");
|
||||
try {
|
||||
Class.forName("org.mariadb.jdbc.Driver");
|
||||
MysqlConfig databaseconfig = getConfig(MysqlConfig.class);
|
||||
@Override
|
||||
public void load() {
|
||||
getLogger().info("Connecting to a MySQL-like database...");
|
||||
try {
|
||||
MysqlConfig config = getConfig(MysqlConfig.class);
|
||||
|
||||
pool = new MariaDbPoolDataSource();
|
||||
HikariConfig hikariConfig = new HikariConfig();
|
||||
hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
|
||||
hikariConfig.setJdbcUrl("jdbc:mysql://" + config.getHostname() + "/" + config.getDatabase());
|
||||
hikariConfig.setUsername(config.getUsername());
|
||||
hikariConfig.setPassword(config.getPassword());
|
||||
hikariConfig.setPoolName("floodgate-linking-mysql");
|
||||
hikariConfig.setMinimumIdle(5);
|
||||
hikariConfig.setMaximumPoolSize(10);
|
||||
|
||||
String hostname = databaseconfig.getHostname();
|
||||
if (hostname.contains(":")) {
|
||||
String[] split = hostname.split(":");
|
||||
dataSource = new HikariDataSource(hikariConfig);
|
||||
|
||||
pool.setServerName(split[0]);
|
||||
try {
|
||||
pool.setPortNumber(Integer.parseInt(split[1]));
|
||||
} catch (NumberFormatException exception) {
|
||||
getLogger().info("{} is not a valid port! Will use the default port", split[1]);
|
||||
}
|
||||
} else {
|
||||
pool.setServerName(hostname);
|
||||
}
|
||||
|
||||
pool.setUser(databaseconfig.getUsername());
|
||||
pool.setPassword(databaseconfig.getPassword());
|
||||
pool.setDatabaseName(databaseconfig.getDatabase());
|
||||
pool.setMinPoolSize(2);
|
||||
pool.setMaxPoolSize(10);
|
||||
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS `LinkedPlayers` ( " +
|
||||
"`bedrockId` BINARY(16) NOT NULL , " +
|
||||
"`javaUniqueId` BINARY(16) NOT NULL , " +
|
||||
"`javaUsername` VARCHAR(16) NOT NULL , " +
|
||||
" PRIMARY KEY (`bedrockId`) , " +
|
||||
" INDEX (`bedrockId`, `javaUniqueId`)" +
|
||||
") ENGINE = InnoDB;"
|
||||
);
|
||||
statement.executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS `LinkedPlayersRequest` ( " +
|
||||
"`javaUsername` VARCHAR(16) NOT NULL , `javaUniqueId` BINARY(16) NOT NULL , " +
|
||||
"`linkCode` VARCHAR(16) NOT NULL , " +
|
||||
"`bedrockUsername` VARCHAR(16) NOT NULL ," +
|
||||
"`requestTime` BIGINT NOT NULL , " +
|
||||
" PRIMARY KEY (`javaUsername`), INDEX(`requestTime`)" +
|
||||
" ) ENGINE = InnoDB;"
|
||||
);
|
||||
}
|
||||
}
|
||||
getLogger().info("Connected to MySQL-like database.");
|
||||
} catch (ClassNotFoundException exception) {
|
||||
getLogger().error("The required class to load the MySQL database wasn't found");
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while loading database", exception);
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS `LinkedPlayers` ( " +
|
||||
"`bedrockId` BINARY(16) NOT NULL , " +
|
||||
"`javaUniqueId` BINARY(16) NOT NULL , " +
|
||||
"`javaUsername` VARCHAR(16) NOT NULL , " +
|
||||
" PRIMARY KEY (`bedrockId`) , " +
|
||||
" INDEX (`bedrockId`, `javaUniqueId`)" +
|
||||
") ENGINE = InnoDB;"
|
||||
);
|
||||
statement.executeUpdate(
|
||||
"CREATE TABLE IF NOT EXISTS `LinkedPlayersRequest` ( " +
|
||||
"`javaUsername` VARCHAR(16) NOT NULL , `javaUniqueId` BINARY(16) NOT NULL , " +
|
||||
"`linkCode` VARCHAR(16) NOT NULL , " +
|
||||
"`bedrockUsername` VARCHAR(16) NOT NULL ," +
|
||||
"`requestTime` BIGINT NOT NULL , " +
|
||||
" PRIMARY KEY (`javaUsername`), INDEX(`requestTime`)" +
|
||||
" ) ENGINE = InnoDB;"
|
||||
);
|
||||
}
|
||||
}
|
||||
getLogger().info("Connected to MySQL-like database.");
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while loading database", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
pool.close();
|
||||
}
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
dataSource.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ?"
|
||||
)) {
|
||||
query.setBytes(1, uuidToBytes(bedrockId));
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
if (!result.next()) {
|
||||
return null;
|
||||
}
|
||||
String javaUsername = result.getString("javaUsername");
|
||||
UUID javaUniqueId = bytesToUUID(result.getBytes("javaUniqueId"));
|
||||
return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId);
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while getting LinkedPlayer", exception);
|
||||
throw new CompletionException("Error while getting LinkedPlayer", exception);
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ?"
|
||||
)) {
|
||||
query.setBytes(1, uuidToBytes(bedrockId));
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
if (!result.next()) {
|
||||
return null;
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ? OR `javaUniqueId` = ?"
|
||||
)) {
|
||||
byte[] uuidBytes = uuidToBytes(playerId);
|
||||
query.setBytes(1, uuidBytes);
|
||||
query.setBytes(2, uuidBytes);
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
return result.next();
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while checking if player is a LinkedPlayer", exception);
|
||||
throw new CompletionException(
|
||||
"Error while checking if player is a LinkedPlayer", exception
|
||||
);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Void> linkPlayer(
|
||||
@NonNull UUID bedrockId,
|
||||
@NonNull UUID javaId,
|
||||
@NonNull String javaUsername) {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> linkPlayer0(bedrockId, javaId, javaUsername),
|
||||
getExecutorService());
|
||||
}
|
||||
|
||||
private void linkPlayer0(UUID bedrockId, UUID javaId, String javaUsername) {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"INSERT INTO `LinkedPlayers` VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE " +
|
||||
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
||||
"`javaUsername`=VALUES(`javaUsername`);"
|
||||
)) {
|
||||
query.setBytes(1, uuidToBytes(bedrockId));
|
||||
query.setBytes(2, uuidToBytes(javaId));
|
||||
query.setString(3, javaUsername);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while linking player", exception);
|
||||
throw new CompletionException("Error while linking player", exception);
|
||||
String javaUsername = result.getString("javaUsername");
|
||||
UUID javaUniqueId = bytesToUUID(result.getBytes("javaUniqueId"));
|
||||
return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while getting LinkedPlayer", exception);
|
||||
throw new CompletionException("Error while getting LinkedPlayer", exception);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayers` WHERE `javaUniqueId` = ? OR `bedrockId` = ?"
|
||||
)) {
|
||||
byte[] uuidBytes = uuidToBytes(javaId);
|
||||
query.setBytes(1, uuidBytes);
|
||||
query.setBytes(2, uuidBytes);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while unlinking player", exception);
|
||||
throw new CompletionException("Error while unlinking player", exception);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<String> createLinkRequest(
|
||||
@NonNull UUID javaId,
|
||||
@NonNull String javaUsername,
|
||||
@NonNull String bedrockUsername) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
String linkCode = createCode();
|
||||
|
||||
createLinkRequest0(javaUsername, javaId, linkCode, bedrockUsername);
|
||||
|
||||
return linkCode;
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
private void createLinkRequest0(
|
||||
String javaUsername,
|
||||
UUID javaId,
|
||||
String linkCode,
|
||||
String bedrockUsername) {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"INSERT INTO `LinkedPlayersRequest` VALUES (?, ?, ?, ?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE " +
|
||||
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
||||
"`linkCode`=VALUES(`linkCode`), " +
|
||||
"`bedrockUsername`=VALUES(`bedrockUsername`), " +
|
||||
"`requestTime`=VALUES(`requestTime`);"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
query.setBytes(2, uuidToBytes(javaId));
|
||||
query.setString(3, linkCode);
|
||||
query.setString(4, bedrockUsername);
|
||||
query.setLong(5, Instant.now().getEpochSecond());
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while linking player", exception);
|
||||
throw new CompletionException("Error while linking player", exception);
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayers` WHERE `bedrockId` = ? OR `javaUniqueId` = ?"
|
||||
)) {
|
||||
byte[] uuidBytes = uuidToBytes(playerId);
|
||||
query.setBytes(1, uuidBytes);
|
||||
query.setBytes(2, uuidBytes);
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
return result.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while checking if player is a LinkedPlayer", exception);
|
||||
throw new CompletionException(
|
||||
"Error while checking if player is a LinkedPlayer", exception
|
||||
);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
private void removeLinkRequest(String javaUsername) {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up LinkRequest", exception);
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Void> linkPlayer(
|
||||
@NonNull UUID bedrockId,
|
||||
@NonNull UUID javaId,
|
||||
@NonNull String javaUsername) {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> linkPlayer0(bedrockId, javaId, javaUsername),
|
||||
getExecutorService());
|
||||
}
|
||||
|
||||
private void linkPlayer0(UUID bedrockId, UUID javaId, String javaUsername) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"INSERT INTO `LinkedPlayers` VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE " +
|
||||
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
||||
"`javaUsername`=VALUES(`javaUsername`);"
|
||||
)) {
|
||||
query.setBytes(1, uuidToBytes(bedrockId));
|
||||
query.setBytes(2, uuidToBytes(javaId));
|
||||
query.setString(3, javaUsername);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while linking player", exception);
|
||||
throw new CompletionException("Error while linking player", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<Void> unlinkPlayer(@NonNull UUID javaId) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayers` WHERE `javaUniqueId` = ? OR `bedrockId` = ?"
|
||||
)) {
|
||||
byte[] uuidBytes = uuidToBytes(javaId);
|
||||
query.setBytes(1, uuidBytes);
|
||||
query.setBytes(2, uuidBytes);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while unlinking player", exception);
|
||||
throw new CompletionException("Error while unlinking player", exception);
|
||||
}
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<String> createLinkRequest(
|
||||
@NonNull UUID javaId,
|
||||
@NonNull String javaUsername,
|
||||
@NonNull String bedrockUsername
|
||||
) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
String linkCode = createCode();
|
||||
|
||||
createLinkRequest0(javaUsername, javaId, linkCode, bedrockUsername);
|
||||
|
||||
return linkCode;
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
private void createLinkRequest0(
|
||||
String javaUsername,
|
||||
UUID javaId,
|
||||
String linkCode,
|
||||
String bedrockUsername
|
||||
) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"INSERT INTO `LinkedPlayersRequest` VALUES (?, ?, ?, ?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE " +
|
||||
"`javaUniqueId`=VALUES(`javaUniqueId`), " +
|
||||
"`linkCode`=VALUES(`linkCode`), " +
|
||||
"`bedrockUsername`=VALUES(`bedrockUsername`), " +
|
||||
"`requestTime`=VALUES(`requestTime`);"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
query.setBytes(2, uuidToBytes(javaId));
|
||||
query.setString(3, linkCode);
|
||||
query.setString(4, bedrockUsername);
|
||||
query.setLong(5, Instant.now().getEpochSecond());
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while linking player", exception);
|
||||
throw new CompletionException("Error while linking player", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkRequestResult> verifyLinkRequest(
|
||||
@NonNull UUID bedrockId,
|
||||
@NonNull String javaUsername,
|
||||
@NonNull String bedrockUsername,
|
||||
@NonNull String code) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
LinkRequest request = getLinkRequest0(javaUsername);
|
||||
|
||||
if (request == null || !isRequestedPlayer(request, bedrockId)) {
|
||||
return LinkRequestResult.NO_LINK_REQUESTED;
|
||||
}
|
||||
|
||||
if (!request.getLinkCode().equals(code)) {
|
||||
return LinkRequestResult.INVALID_CODE;
|
||||
}
|
||||
|
||||
// link request can be removed. Doesn't matter if the request is expired or not
|
||||
removeLinkRequest(javaUsername);
|
||||
|
||||
if (request.isExpired(getVerifyLinkTimeout())) {
|
||||
return LinkRequestResult.REQUEST_EXPIRED;
|
||||
}
|
||||
|
||||
linkPlayer0(bedrockId, request.getJavaUniqueId(), javaUsername);
|
||||
return LinkRequestResult.LINK_COMPLETED;
|
||||
}, getExecutorService());
|
||||
private void removeLinkRequest(String javaUsername) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up LinkRequest", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private LinkRequest getLinkRequest0(String javaUsername) {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
@Override
|
||||
@NonNull
|
||||
public CompletableFuture<LinkRequestResult> verifyLinkRequest(
|
||||
@NonNull UUID bedrockId,
|
||||
@NonNull String javaUsername,
|
||||
@NonNull String bedrockUsername,
|
||||
@NonNull String code
|
||||
) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
LinkRequest request = getLinkRequest0(javaUsername);
|
||||
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
if (result.next()) {
|
||||
UUID javaId = bytesToUUID(result.getBytes(2));
|
||||
String linkCode = result.getString(3);
|
||||
String bedrockUsername = result.getString(4);
|
||||
long requestTime = result.getLong(5);
|
||||
return new LinkRequestImpl(javaUsername, javaId, linkCode, bedrockUsername,
|
||||
requestTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while getLinkRequest", exception);
|
||||
throw new CompletionException("Error while getLinkRequest", exception);
|
||||
if (request == null || !isRequestedPlayer(request, bedrockId)) {
|
||||
return LinkRequestResult.NO_LINK_REQUESTED;
|
||||
}
|
||||
|
||||
if (!request.getLinkCode().equals(code)) {
|
||||
return LinkRequestResult.INVALID_CODE;
|
||||
}
|
||||
|
||||
// link request can be removed. Doesn't matter if the request is expired or not
|
||||
removeLinkRequest(javaUsername);
|
||||
|
||||
if (request.isExpired(getVerifyLinkTimeout())) {
|
||||
return LinkRequestResult.REQUEST_EXPIRED;
|
||||
}
|
||||
|
||||
linkPlayer0(bedrockId, request.getJavaUniqueId(), javaUsername);
|
||||
return LinkRequestResult.LINK_COMPLETED;
|
||||
}, getExecutorService());
|
||||
}
|
||||
|
||||
private LinkRequest getLinkRequest0(String javaUsername) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"SELECT * FROM `LinkedPlayersRequest` WHERE `javaUsername` = ?"
|
||||
)) {
|
||||
query.setString(1, javaUsername);
|
||||
|
||||
try (ResultSet result = query.executeQuery()) {
|
||||
if (result.next()) {
|
||||
UUID javaId = bytesToUUID(result.getBytes(2));
|
||||
String linkCode = result.getString(3);
|
||||
String bedrockUsername = result.getString(4);
|
||||
long requestTime = result.getLong(5);
|
||||
return new LinkRequestImpl(javaUsername, javaId, linkCode, bedrockUsername,
|
||||
requestTime);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while getLinkRequest", exception);
|
||||
throw new CompletionException("Error while getLinkRequest", exception);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void cleanLinkRequests() {
|
||||
try (Connection connection = pool.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `requestTime` < ?"
|
||||
)) {
|
||||
query.setLong(1, Instant.now().getEpochSecond() - getVerifyLinkTimeout());
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up link requests", exception);
|
||||
}
|
||||
public void cleanLinkRequests() {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (PreparedStatement query = connection.prepareStatement(
|
||||
"DELETE FROM `LinkedPlayersRequest` WHERE `requestTime` < ?"
|
||||
)) {
|
||||
query.setLong(1, Instant.now().getEpochSecond() - getVerifyLinkTimeout());
|
||||
query.executeUpdate();
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
getLogger().error("Error while cleaning up link requests", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] uuidToBytes(UUID uuid) {
|
||||
byte[] uuidBytes = new byte[16];
|
||||
ByteBuffer.wrap(uuidBytes)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid.getMostSignificantBits())
|
||||
.putLong(uuid.getLeastSignificantBits());
|
||||
return uuidBytes;
|
||||
}
|
||||
|
||||
private UUID bytesToUUID(byte[] uuidBytes) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(uuidBytes);
|
||||
return new UUID(buf.getLong(), buf.getLong());
|
||||
}
|
||||
private byte[] uuidToBytes(UUID uuid) {
|
||||
byte[] uuidBytes = new byte[16];
|
||||
ByteBuffer.wrap(uuidBytes)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(uuid.getMostSignificantBits())
|
||||
.putLong(uuid.getLeastSignificantBits());
|
||||
return uuidBytes;
|
||||
}
|
||||
|
||||
private UUID bytesToUUID(byte[] uuidBytes) {
|
||||
ByteBuffer buf = ByteBuffer.wrap(uuidBytes);
|
||||
return new UUID(buf.getLong(), buf.getLong());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class MysqlConfig implements DatabaseConfig {
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
private String hostname = "localhost";
|
||||
private String database = "floodgate";
|
||||
private String username = "floodgate";
|
||||
private String password;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
version=2.2.2-SNAPSHOT
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
10
gradlew
vendored
10
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright <EFBFBD> 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.
|
||||
@@ -32,10 +32,10 @@
|
||||
# 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».
|
||||
# * expansions <EFBFBD>$var<EFBFBD>, <EFBFBD>${var}<EFBFBD>, <EFBFBD>${var:-default}<EFBFBD>, <EFBFBD>${var+SET}<EFBFBD>,
|
||||
# <EFBFBD>${var#prefix}<EFBFBD>, <EFBFBD>${var%suffix}<EFBFBD>, and <EFBFBD>$( cmd )<EFBFBD>;
|
||||
# * compound commands having a testable exit status, especially <EFBFBD>case<EFBFBD>;
|
||||
# * various built-in commands including <EFBFBD>command<EFBFBD>, <EFBFBD>set<EFBFBD>, and <EFBFBD>ulimit<EFBFBD>.
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
@@ -14,7 +15,19 @@ dependencyResolutionManagement {
|
||||
}
|
||||
|
||||
// Paper, Velocity
|
||||
maven("https://papermc.io/repo/repository/maven-public")
|
||||
// maven("https://repo.papermc.io/repository/maven-releases") {
|
||||
// mavenContent { releasesOnly() }
|
||||
// }
|
||||
// maven("https://repo.papermc.io/repository/maven-snapshots") {
|
||||
// mavenContent { snapshotsOnly() }
|
||||
// }
|
||||
maven("https://repo.papermc.io/repository/maven-public") {
|
||||
content {
|
||||
includeGroupByRegex(
|
||||
"(io\\.papermc\\..*|com\\.destroystokyo\\..*|com\\.velocitypowered)"
|
||||
)
|
||||
}
|
||||
}
|
||||
// Spigot
|
||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") {
|
||||
mavenContent { snapshotsOnly() }
|
||||
@@ -43,7 +56,8 @@ pluginManagement {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
plugins {
|
||||
id("net.kyori.blossom") version "1.2.0"
|
||||
id("net.kyori.indra")
|
||||
id("net.kyori.indra.git")
|
||||
}
|
||||
includeBuild("build-logic")
|
||||
}
|
||||
@@ -51,6 +65,7 @@ pluginManagement {
|
||||
rootProject.name = "floodgate-parent"
|
||||
|
||||
include(":api")
|
||||
include(":ap")
|
||||
include(":core")
|
||||
include(":bungee")
|
||||
include(":spigot")
|
||||
|
||||
@@ -27,6 +27,7 @@ package org.geysermc.floodgate;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
@@ -43,7 +44,7 @@ import org.geysermc.floodgate.util.SpigotProtocolSupportHandler;
|
||||
import org.geysermc.floodgate.util.SpigotProtocolSupportListener;
|
||||
|
||||
public final class SpigotPlugin extends JavaPlugin {
|
||||
private SpigotPlatform platform;
|
||||
private FloodgatePlatform platform;
|
||||
private Injector injector;
|
||||
|
||||
@Override
|
||||
@@ -54,7 +55,7 @@ public final class SpigotPlugin extends JavaPlugin {
|
||||
new SpigotPlatformModule(this)
|
||||
);
|
||||
|
||||
platform = injector.getInstance(SpigotPlatform.class);
|
||||
platform = injector.getInstance(FloodgatePlatform.class);
|
||||
|
||||
long endCtm = System.currentTimeMillis();
|
||||
injector.getInstance(FloodgateLogger.class)
|
||||
@@ -66,14 +67,18 @@ public final class SpigotPlugin extends JavaPlugin {
|
||||
boolean usePaperListener = ReflectionUtils.getClassSilently(
|
||||
"com.destroystokyo.paper.event.profile.PreFillProfileEvent") != null;
|
||||
|
||||
platform.enable(
|
||||
new SpigotCommandModule(this),
|
||||
new SpigotAddonModule(),
|
||||
new PluginMessageModule(),
|
||||
(usePaperListener ? new PaperListenerModule() : new SpigotListenerModule())
|
||||
);
|
||||
try {
|
||||
platform.enable(
|
||||
new SpigotCommandModule(this),
|
||||
new SpigotAddonModule(),
|
||||
new PluginMessageModule(),
|
||||
(usePaperListener ? new PaperListenerModule() : new SpigotListenerModule())
|
||||
);
|
||||
} catch (Exception exception) {
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
//todo add proper support for disabling things on shutdown and enabling this on enable
|
||||
injector.getInstance(HandshakeHandlers.class)
|
||||
.addHandshakeHandler(injector.getInstance(SpigotHandshakeHandler.class));
|
||||
|
||||
|
||||
@@ -25,25 +25,28 @@
|
||||
|
||||
package org.geysermc.floodgate.inject.spigot;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
import org.geysermc.floodgate.util.ClassNames;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public final class SpigotInjector extends CommonPlatformInjector {
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
private Object serverConnection;
|
||||
private String injectedFieldName;
|
||||
|
||||
@@ -51,54 +54,56 @@ public final class SpigotInjector extends CommonPlatformInjector {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
|
||||
public boolean inject() throws Exception {
|
||||
public void inject() throws Exception {
|
||||
if (isInjected()) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (getServerConnection() != null) {
|
||||
for (Field field : serverConnection.getClass().getDeclaredFields()) {
|
||||
if (field.getType() == List.class) {
|
||||
field.setAccessible(true);
|
||||
Object serverConnection = getServerConnection();
|
||||
if (serverConnection == null) {
|
||||
throw new RuntimeException("Unable to find server connection");
|
||||
}
|
||||
|
||||
ParameterizedType parameterType = ((ParameterizedType) field.getGenericType());
|
||||
Type listType = parameterType.getActualTypeArguments()[0];
|
||||
for (Field field : serverConnection.getClass().getDeclaredFields()) {
|
||||
if (field.getType() == List.class) {
|
||||
field.setAccessible(true);
|
||||
|
||||
// the list we search has ChannelFuture as type
|
||||
if (listType != ChannelFuture.class) {
|
||||
continue;
|
||||
}
|
||||
ParameterizedType parameterType = ((ParameterizedType) field.getGenericType());
|
||||
Type listType = parameterType.getActualTypeArguments()[0];
|
||||
|
||||
injectedFieldName = field.getName();
|
||||
List<?> newList = new CustomList((List<?>) field.get(serverConnection)) {
|
||||
@Override
|
||||
public void onAdd(Object object) {
|
||||
try {
|
||||
injectClient((ChannelFuture) object);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// inject existing
|
||||
synchronized (newList) {
|
||||
for (Object object : newList) {
|
||||
try {
|
||||
injectClient((ChannelFuture) object);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field.set(serverConnection, newList);
|
||||
injected = true;
|
||||
return true;
|
||||
// the list we search has ChannelFuture as type
|
||||
if (listType != ChannelFuture.class) {
|
||||
continue;
|
||||
}
|
||||
|
||||
injectedFieldName = field.getName();
|
||||
List<?> newList = new CustomList((List<?>) field.get(serverConnection)) {
|
||||
@Override
|
||||
public void onAdd(Object object) {
|
||||
try {
|
||||
injectClient((ChannelFuture) object);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// inject existing
|
||||
synchronized (newList) {
|
||||
for (Object object : newList) {
|
||||
try {
|
||||
injectClient((ChannelFuture) object);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
field.set(serverConnection, newList);
|
||||
injected = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void injectClient(ChannelFuture future) {
|
||||
@@ -120,36 +125,48 @@ public final class SpigotInjector extends CommonPlatformInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeInjection() throws Exception {
|
||||
public void removeInjection() {
|
||||
if (!isInjected()) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// remove injection from clients
|
||||
for (Channel channel : getInjectedClients()) {
|
||||
removeAddonsCall(channel);
|
||||
}
|
||||
getInjectedClients().clear();
|
||||
|
||||
// and change the list back to the original
|
||||
// let's change the list back to the original first
|
||||
// so that new connections are not handled through our custom list
|
||||
Object serverConnection = getServerConnection();
|
||||
if (serverConnection != null) {
|
||||
Field field = ReflectionUtils.getField(serverConnection.getClass(), injectedFieldName);
|
||||
List<?> list = (List<?>) ReflectionUtils.getValue(serverConnection, field);
|
||||
Object value = ReflectionUtils.getValue(serverConnection, field);
|
||||
|
||||
if (list instanceof CustomList) {
|
||||
CustomList customList = (CustomList) list;
|
||||
if (value instanceof CustomList) {
|
||||
// all we have to do is replace the list with the original list.
|
||||
// the original list is up-to-date, so we don't have to clear/add/whatever anything
|
||||
CustomList customList = (CustomList) value;
|
||||
ReflectionUtils.setValue(serverConnection, field, customList.getOriginalList());
|
||||
customList.clear();
|
||||
customList.addAll(list);
|
||||
return;
|
||||
}
|
||||
|
||||
// we could replace all references of CustomList that are directly in 'value', but that
|
||||
// only brings you so far. ProtocolLib for example stores the original value
|
||||
// (which would be our CustomList e.g.) in a separate object
|
||||
logger.debug(
|
||||
"Unable to remove all references of Floodgate due to {}! ",
|
||||
value.getClass().getName()
|
||||
);
|
||||
}
|
||||
|
||||
// remove injection from clients
|
||||
for (Channel channel : injectedClients()) {
|
||||
removeAddonsCall(channel);
|
||||
}
|
||||
|
||||
//todo make sure that all references are removed from the channels,
|
||||
// except from one AttributeKey with Floodgate player data which could be used
|
||||
// after reloading
|
||||
|
||||
injected = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object getServerConnection() throws IllegalAccessException, InvocationTargetException {
|
||||
private Object getServerConnection() {
|
||||
if (serverConnection != null) {
|
||||
return serverConnection;
|
||||
}
|
||||
@@ -158,14 +175,11 @@ public final class SpigotInjector extends CommonPlatformInjector {
|
||||
// method by CraftBukkit to get the instance of the MinecraftServer
|
||||
Object minecraftServerInstance = ReflectionUtils.invokeStatic(minecraftServer, "getServer");
|
||||
|
||||
for (Method method : minecraftServer.getDeclaredMethods()) {
|
||||
if (ClassNames.SERVER_CONNECTION.equals(method.getReturnType())) {
|
||||
// making sure that it's a getter
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
serverConnection = method.invoke(minecraftServerInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
Method method = ReflectionUtils.getMethodThatReturns(
|
||||
minecraftServer, ClassNames.SERVER_CONNECTION, true
|
||||
);
|
||||
|
||||
serverConnection = ReflectionUtils.invoke(minecraftServerInstance, method);
|
||||
|
||||
return serverConnection;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.name.Named;
|
||||
import com.google.inject.name.Names;
|
||||
import java.util.logging.Logger;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
@@ -59,7 +61,12 @@ public final class SpigotPlatformModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SpigotPlugin.class).toInstance(plugin);
|
||||
bind(PlatformUtils.class).to(SpigotPlatformUtils.class);
|
||||
bind(CommonPlatformInjector.class).to(SpigotInjector.class);
|
||||
bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(plugin.getLogger());
|
||||
bind(FloodgateLogger.class).to(JavaUtilFloodgateLogger.class);
|
||||
bind(SkinApplier.class).to(SpigotSkinApplier.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -68,12 +75,6 @@ public final class SpigotPlatformModule extends AbstractModule {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public FloodgateLogger floodgateLogger(LanguageManager languageManager) {
|
||||
return new JavaUtilFloodgateLogger(plugin.getLogger(), languageManager);
|
||||
}
|
||||
|
||||
/*
|
||||
Commands / Listeners
|
||||
*/
|
||||
@@ -98,12 +99,6 @@ public final class SpigotPlatformModule extends AbstractModule {
|
||||
DebugAddon / PlatformInjector
|
||||
*/
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommonPlatformInjector platformInjector() {
|
||||
return new SpigotInjector();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("packetEncoder")
|
||||
public String packetEncoder() {
|
||||
@@ -144,12 +139,6 @@ public final class SpigotPlatformModule extends AbstractModule {
|
||||
return new SpigotPluginMessageRegistration(plugin);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SkinApplier skinApplier(SpigotVersionSpecificMethods versionSpecificMethods) {
|
||||
return new SpigotSkinApplier(versionSpecificMethods, plugin);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SpigotVersionSpecificMethods versionSpecificMethods() {
|
||||
|
||||
@@ -25,32 +25,34 @@
|
||||
|
||||
package org.geysermc.floodgate.pluginmessage;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.SpigotPlugin;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.skin.SkinApplyEventImpl;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
import org.geysermc.floodgate.util.ClassNames;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
import org.geysermc.floodgate.util.SpigotVersionSpecificMethods;
|
||||
|
||||
@Singleton
|
||||
public final class SpigotSkinApplier implements SkinApplier {
|
||||
private final SpigotVersionSpecificMethods versionSpecificMethods;
|
||||
private final SpigotPlugin plugin;
|
||||
|
||||
public SpigotSkinApplier(
|
||||
SpigotVersionSpecificMethods versionSpecificMethods,
|
||||
SpigotPlugin plugin) {
|
||||
this.versionSpecificMethods = versionSpecificMethods;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@Inject private SpigotVersionSpecificMethods versionSpecificMethods;
|
||||
@Inject private SpigotPlugin plugin;
|
||||
@Inject private EventBus eventBus;
|
||||
|
||||
@Override
|
||||
public void applySkin(FloodgatePlayer floodgatePlayer, SkinData skinData) {
|
||||
public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData) {
|
||||
applySkin0(floodgatePlayer, skinData, true);
|
||||
}
|
||||
|
||||
@@ -60,9 +62,11 @@ public final class SpigotSkinApplier implements SkinApplier {
|
||||
// player is probably not logged in yet
|
||||
if (player == null) {
|
||||
if (firstTry) {
|
||||
Bukkit.getScheduler().runTaskLater(plugin,
|
||||
Bukkit.getScheduler().runTaskLater(
|
||||
plugin,
|
||||
() -> applySkin0(floodgatePlayer, skinData, false),
|
||||
10 * 1000);
|
||||
10 * 20
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -73,11 +77,22 @@ public final class SpigotSkinApplier implements SkinApplier {
|
||||
throw new IllegalStateException("The GameProfile cannot be null! " + player.getName());
|
||||
}
|
||||
|
||||
// Need to be careful here - getProperties() returns an authlib PropertyMap, which extends
|
||||
// MultiMap from Guava. Floodgate relocates Guava.
|
||||
PropertyMap properties = profile.getProperties();
|
||||
|
||||
properties.removeAll("textures");
|
||||
Property property = new Property("textures", skinData.getValue(), skinData.getSignature());
|
||||
properties.put("textures", property);
|
||||
SkinData currentSkin = currentSkin(properties);
|
||||
|
||||
SkinApplyEvent event = new SkinApplyEventImpl(floodgatePlayer, currentSkin, skinData);
|
||||
event.setCancelled(floodgatePlayer.isLinked());
|
||||
|
||||
eventBus.fire(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
replaceSkin(properties, event.newSkin());
|
||||
|
||||
// By running as a task, we don't run into async issues
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
@@ -89,4 +104,19 @@ public final class SpigotSkinApplier implements SkinApplier {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private SkinData currentSkin(PropertyMap properties) {
|
||||
for (Property texture : properties.get("textures")) {
|
||||
if (!texture.getValue().isEmpty()) {
|
||||
return new SkinDataImpl(texture.getValue(), texture.getSignature());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void replaceSkin(PropertyMap properties, SkinData skinData) {
|
||||
properties.removeAll("textures");
|
||||
Property property = new Property("textures", skinData.value(), skinData.signature());
|
||||
properties.put("textures", property);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var velocityVersion = "3.0.1"
|
||||
var velocityVersion = "3.1.1"
|
||||
var log4jVersion = "2.11.2"
|
||||
var gsonVersion = "2.8.8"
|
||||
var guavaVersion = "25.1-jre"
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import java.nio.file.Path;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
@@ -69,4 +70,9 @@ public final class VelocityPlugin {
|
||||
new PluginMessageModule()
|
||||
);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyShutdown(ProxyShutdownEvent event) {
|
||||
platform.disable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,21 +36,19 @@ import io.netty.channel.ChannelInitializer;
|
||||
import java.lang.reflect.Method;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class VelocityInjector extends CommonPlatformInjector {
|
||||
private final ProxyServer server;
|
||||
private final FloodgateLogger logger;
|
||||
|
||||
@Getter private boolean injected;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean inject() {
|
||||
public void inject() {
|
||||
if (isInjected()) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
Object connectionManager = getValue(server, "cm");
|
||||
@@ -72,7 +70,8 @@ public final class VelocityInjector extends CommonPlatformInjector {
|
||||
Method backendSetter = getMethod(backendInitializerHolder, "set", ChannelInitializer.class);
|
||||
invoke(backendInitializerHolder, backendSetter,
|
||||
new VelocityChannelInitializer(this, backendInitializer, true));
|
||||
return injected = true;
|
||||
|
||||
injected = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,9 +80,9 @@ public final class VelocityInjector extends CommonPlatformInjector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeInjection() {
|
||||
logger.error("Floodgate cannot remove itself from Velocity without a reboot");
|
||||
return false;
|
||||
public void removeInjection() {
|
||||
throw new IllegalStateException(
|
||||
"Floodgate cannot remove itself from Velocity without a reboot");
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
|
||||
@@ -27,17 +27,27 @@ package org.geysermc.floodgate.logger;
|
||||
|
||||
import static org.geysermc.floodgate.util.MessageFormatter.format;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public final class Slf4jFloodgateLogger implements FloodgateLogger {
|
||||
private final Logger logger;
|
||||
private final LanguageManager languageManager;
|
||||
@Inject private Logger logger;
|
||||
private LanguageManager languageManager;
|
||||
|
||||
@Inject
|
||||
private void init(LanguageManager languageManager, FloodgateConfig config) {
|
||||
this.languageManager = languageManager;
|
||||
if (config.isDebug() && !logger.isDebugEnabled()) {
|
||||
Configurator.setLevel(logger.getName(), Level.DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message, Object... args) {
|
||||
@@ -74,20 +84,6 @@ public final class Slf4jFloodgateLogger implements FloodgateLogger {
|
||||
logger.trace(message, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableDebug() {
|
||||
if (!logger.isDebugEnabled()) {
|
||||
Configurator.setLevel(logger.getName(), Level.DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableDebug() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
Configurator.setLevel(logger.getName(), Level.INFO);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return logger.isDebugEnabled();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user