mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Merge remote-tracking branch 'origin/dev/2.1.1' into feature/cumulus-1.1
# Conflicts: # build-logic/src/main/kotlin/Versions.kt
This commit is contained in:
33
.github/workflows/pullrequest.yml
vendored
33
.github/workflows/pullrequest.yml
vendored
@@ -8,34 +8,35 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.m2/repository
|
|
||||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-maven-
|
|
||||||
- name: Set up JDK 1.8
|
- name: Set up JDK 1.8
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
distribution: 'temurin'
|
||||||
|
java-version: '8'
|
||||||
|
cache: 'gradle'
|
||||||
|
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: mvn -B package
|
run: ./gradlew build
|
||||||
|
|
||||||
- name: Archive artifacts (Floodgate Bungee)
|
- name: Archive artifacts (Floodgate Bungee)
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Floodgate Bungee
|
name: Floodgate Bungee
|
||||||
path: bungee/target/floodgate-bungee.jar
|
path: bungee/build/libs/floodgate-bungee.jar
|
||||||
|
|
||||||
- name: Archive artifacts (Floodgate Spigot)
|
- name: Archive artifacts (Floodgate Spigot)
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Floodgate Spigot
|
name: Floodgate Spigot
|
||||||
path: spigot/target/floodgate-spigot.jar
|
path: spigot/build/libs/floodgate-spigot.jar
|
||||||
|
|
||||||
- name: Archive artifacts (Floodgate Velocity)
|
- name: Archive artifacts (Floodgate Velocity)
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
name: Floodgate Velocity
|
name: Floodgate Velocity
|
||||||
path: velocity/target/floodgate-velocity.jar
|
path: velocity/build/libs/floodgate-velocity.jar
|
||||||
13
Jenkinsfile
vendored
13
Jenkinsfile
vendored
@@ -11,11 +11,18 @@ pipeline {
|
|||||||
stage ('Build') {
|
stage ('Build') {
|
||||||
steps {
|
steps {
|
||||||
sh 'git submodule update --init --recursive'
|
sh 'git submodule update --init --recursive'
|
||||||
sh './gradlew clean build'
|
rtGradleRun(
|
||||||
|
usesPlugin: true,
|
||||||
|
tool: 'Gradle 7',
|
||||||
|
buildFile: 'build.gradle.kts',
|
||||||
|
tasks: 'clean build',
|
||||||
|
)
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
success {
|
success {
|
||||||
archiveArtifacts artifacts: '**/build/libs/floodgate-*.jar', excludes: "**/build/libs/floodgate-api.jar,**/build/libs/floodgate-core.jar", fingerprint: true
|
archiveArtifacts artifacts: '**/build/libs/floodgate-*.jar',
|
||||||
|
excludes: '**/floodgate-parent-*.jar,**/floodgate-api.jar,**/floodgate-core.jar',
|
||||||
|
fingerprint: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +52,7 @@ pipeline {
|
|||||||
rootDir: "",
|
rootDir: "",
|
||||||
useWrapper: true,
|
useWrapper: true,
|
||||||
buildFile: 'build.gradle.kts',
|
buildFile: 'build.gradle.kts',
|
||||||
tasks: 'build artifactoryPublish',
|
tasks: 'artifactoryPublish',
|
||||||
deployerId: "GRADLE_DEPLOYER",
|
deployerId: "GRADLE_DEPLOYER",
|
||||||
resolverId: "GRADLE_RESOLVER"
|
resolverId: "GRADLE_RESOLVER"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
[](http://discord.geysermc.org/)
|
[](http://discord.geysermc.org/)
|
||||||
[](http://hits.dwyl.com/GeyserMC/Floodgate)
|
[](http://hits.dwyl.com/GeyserMC/Floodgate)
|
||||||
|
|
||||||
|
[Download](https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/)
|
||||||
|
|
||||||
Hybrid mode plugin to allow for connections from [Geyser](https://github.com/GeyserMC/Geyser) to join online mode servers.
|
Hybrid mode plugin to allow for connections from [Geyser](https://github.com/GeyserMC/Geyser) to join online mode servers.
|
||||||
|
|
||||||
Geyser is an open collaboration project by [CubeCraft Games](https://cubecraft.net).
|
Geyser is an open collaboration project by [CubeCraft Games](https://cubecraft.net).
|
||||||
|
|
||||||
See the [Floodgate](https://github.com/GeyserMC/Geyser/wiki/Floodgate) page in the Geyser Wiki for more info about the what Floodgate is, how you setup Floodgate and known issues/caveats.
|
See the [Floodgate](https://wiki.geysermc.org/floodgate/) section in the GeyserMC Wiki for more info about what Floodgate is, how you setup Floodgate and known issues/caveats. Additionally, it includes a more in-depth look into how Floodgate works and the Floodgate API.
|
||||||
|
|
||||||
See the [Floodgate wiki](https://github.com/GeyserMC/Floodgate/wiki) (currently work in progress) for a more in-depth look into Floodgate, how it works and the Floodgate API.
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public interface InjectorAddon {
|
|||||||
* used for third party things.
|
* used for third party things.
|
||||||
*
|
*
|
||||||
* @param channel the channel that the injector is injecting
|
* @param channel the channel that the injector is injecting
|
||||||
* @param toServer if the the connection is between a proxy and a server
|
* @param toServer if the connection is between a proxy and a server
|
||||||
*/
|
*/
|
||||||
void onInject(Channel channel, boolean toServer);
|
void onInject(Channel channel, boolean toServer);
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public interface InjectorAddon {
|
|||||||
* closed connection (if it is injected), so it'll also run this method for closed connections
|
* closed connection (if it is injected), so it'll also run this method for closed connections
|
||||||
* between a server and the proxy (when Floodgate is running on a proxy).
|
* between a server and the proxy (when Floodgate is running on a proxy).
|
||||||
*
|
*
|
||||||
* @param channel the channel that the injecor injected
|
* @param channel the channel that the injector injected
|
||||||
*/
|
*/
|
||||||
default void onChannelClosed(Channel channel) {
|
default void onChannelClosed(Channel channel) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class PropertyKey {
|
|||||||
this.removable = removable;
|
this.removable = removable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result isAddAllowed(Object obj) { //todo use for add and remove
|
public Result isAddAllowed(Object obj) {
|
||||||
if (obj instanceof PropertyKey) {
|
if (obj instanceof PropertyKey) {
|
||||||
PropertyKey propertyKey = (PropertyKey) obj;
|
PropertyKey propertyKey = (PropertyKey) obj;
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
object Versions {
|
object Versions {
|
||||||
const val geyserVersion = "2.0.1-cumulus-SNAPSHOT"
|
const val geyserVersion = "2.0.1-cumulus-SNAPSHOT"
|
||||||
const val cumulusVersion = "1.1-SNAPSHOT"
|
const val cumulusVersion = "1.1-SNAPSHOT"
|
||||||
|
const val configUtilsVersion = "1.0-SNAPSHOT"
|
||||||
const valspigotVersion = "1.13-R0.1-SNAPSHOT"
|
const valspigotVersion = "1.13-R0.1-SNAPSHOT"
|
||||||
const val fastutilVersion = "8.5.3"
|
const val fastutilVersion = "8.5.3"
|
||||||
const val lombokVersion = "1.18.20"
|
const val lombokVersion = "1.18.20"
|
||||||
@@ -33,8 +34,7 @@ object Versions {
|
|||||||
const val nettyVersion = "4.1.49.Final"
|
const val nettyVersion = "4.1.49.Final"
|
||||||
const val snakeyamlVersion = "1.28"
|
const val snakeyamlVersion = "1.28"
|
||||||
const val cloudVersion = "1.5.0"
|
const val cloudVersion = "1.5.0"
|
||||||
const val adventureApiVersion = "4.9.1"
|
const val bstatsVersion = "3.0.0"
|
||||||
const val adventurePlatformVersion = "4.0.0"
|
|
||||||
|
|
||||||
const val javaWebsocketVersion = "1.5.2"
|
const val javaWebsocketVersion = "1.5.2"
|
||||||
|
|
||||||
|
|||||||
@@ -23,30 +23,37 @@
|
|||||||
* @link https://github.com/GeyserMC/Floodgate
|
* @link https://github.com/GeyserMC/Floodgate
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
|
||||||
import net.kyori.indra.git.IndraGitExtension
|
import net.kyori.indra.git.IndraGitExtension
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.ProjectDependency
|
import org.gradle.api.artifacts.ProjectDependency
|
||||||
import org.gradle.kotlin.dsl.named
|
|
||||||
import org.gradle.kotlin.dsl.the
|
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")) {
|
||||||
|
version += " (b${buildNumberAsString()}-${lastCommitHash()})"
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
fun Project.lastCommitHash(): String? =
|
fun Project.lastCommitHash(): String? =
|
||||||
the<IndraGitExtension>().commit()?.name?.substring(0, 7)
|
the<IndraGitExtension>().commit()?.name?.substring(0, 7)
|
||||||
|
|
||||||
// retrieved from https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
|
// retrieved from https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
|
||||||
// some properties might be specific to Jenkins
|
// some properties might be specific to Jenkins
|
||||||
fun Project.branchName(): String =
|
fun Project.branchName(): String =
|
||||||
System.getProperty("GIT_BRANCH", "local/dev")
|
System.getenv("GIT_BRANCH") ?: "local/dev"
|
||||||
fun Project.buildNumber(): Int =
|
fun Project.buildNumber(): Int =
|
||||||
Integer.parseInt(System.getProperty("BUILD_NUMBER", "-1"))
|
Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1")
|
||||||
|
|
||||||
fun Project.relocate(pattern: String) {
|
fun Project.buildNumberAsString(): String =
|
||||||
tasks.named<ShadowJar>("shadowJar") {
|
buildNumber().takeIf { it != -1 }?.toString() ?: "??"
|
||||||
relocate(pattern, "org.geysermc.floodgate.shaded.$pattern")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val providedDependencies = mutableMapOf<String, MutableSet<String>>()
|
val providedDependencies = mutableMapOf<String, MutableSet<String>>()
|
||||||
|
val relocatedPackages = mutableMapOf<String, MutableSet<String>>()
|
||||||
|
|
||||||
fun Project.provided(pattern: String, name: String, version: String, excludedOn: Int = 0b110) {
|
fun Project.provided(pattern: String, name: String, version: String, excludedOn: Int = 0b110) {
|
||||||
providedDependencies.getOrPut(project.name) { mutableSetOf() }
|
providedDependencies.getOrPut(project.name) { mutableSetOf() }
|
||||||
@@ -59,5 +66,10 @@ fun Project.provided(pattern: String, name: String, version: String, excludedOn:
|
|||||||
fun Project.provided(dependency: ProjectDependency) =
|
fun Project.provided(dependency: ProjectDependency) =
|
||||||
provided(dependency.group!!, dependency.name, dependency.version!!)
|
provided(dependency.group!!, dependency.name, dependency.version!!)
|
||||||
|
|
||||||
|
|
||||||
|
fun Project.relocate(pattern: String) =
|
||||||
|
relocatedPackages.getOrPut(project.name) { mutableSetOf() }
|
||||||
|
.add(pattern)
|
||||||
|
|
||||||
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
|
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
|
||||||
if (excludedOn and bit > 0) section else ""
|
if (excludedOn and bit > 0) section else ""
|
||||||
@@ -2,6 +2,7 @@ plugins {
|
|||||||
`java-library`
|
`java-library`
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
// id("net.ltgt.errorprone")
|
// id("net.ltgt.errorprone")
|
||||||
|
id("net.kyori.indra.git")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -14,7 +15,7 @@ tasks {
|
|||||||
expand(
|
expand(
|
||||||
"id" to "floodgate",
|
"id" to "floodgate",
|
||||||
"name" to "floodgate",
|
"name" to "floodgate",
|
||||||
"version" to project.version,
|
"version" to fullVersion(),
|
||||||
"description" to project.description,
|
"description" to project.description,
|
||||||
"url" to "https://geysermc.org",
|
"url" to "https://geysermc.org",
|
||||||
"author" to "GeyserMC"
|
"author" to "GeyserMC"
|
||||||
@@ -32,11 +33,3 @@ java {
|
|||||||
|
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications.create<MavenPublication>("mavenJava") {
|
|
||||||
groupId = project.group as String
|
|
||||||
artifactId = "floodgate-" + project.name
|
|
||||||
version = project.version as String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
plugins {
|
||||||
|
id("floodgate.shadow-conventions")
|
||||||
|
id("com.jfrog.artifactory")
|
||||||
|
id("maven-publish")
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("mavenJava") {
|
||||||
|
groupId = project.group as String
|
||||||
|
artifactId = project.name
|
||||||
|
version = project.version as String
|
||||||
|
|
||||||
|
artifact(tasks["shadowJar"])
|
||||||
|
artifact(tasks["sourcesJar"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
|||||||
plugins {
|
plugins {
|
||||||
id("floodgate.base-conventions")
|
id("floodgate.base-conventions")
|
||||||
id("com.github.johnrengelman.shadow")
|
id("com.github.johnrengelman.shadow")
|
||||||
id("com.jfrog.artifactory")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
@@ -25,6 +24,12 @@ tasks {
|
|||||||
exclude(dependency(string))
|
exclude(dependency(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// relocations made in included project dependencies are for whatever reason not
|
||||||
|
// forwarded to the project implementing the dependency.
|
||||||
|
// (e.g. a relocation in `core` will relocate for core. But when you include `core` in
|
||||||
|
// for example Velocity, the relocation will be gone for Velocity)
|
||||||
|
addRelocations(project, sJar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
named("build") {
|
named("build") {
|
||||||
@@ -32,24 +37,18 @@ tasks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
fun addRelocations(project: Project, shadowJar: ShadowJar) {
|
||||||
publications.named<MavenPublication>("mavenJava") {
|
callAddRelocations(project.configurations.api.get(), shadowJar)
|
||||||
artifact(tasks["shadowJar"])
|
callAddRelocations(project.configurations.implementation.get(), shadowJar)
|
||||||
artifact(tasks["sourcesJar"])
|
|
||||||
|
relocatedPackages[project.name]?.forEach { pattern ->
|
||||||
|
println("Relocating $pattern for ${shadowJar.project.name}")
|
||||||
|
shadowJar.relocate(pattern, "org.geysermc.floodgate.shadow.$pattern")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
artifactory {
|
fun callAddRelocations(configuration: Configuration, shadowJar: ShadowJar) =
|
||||||
publish {
|
configuration.dependencies.forEach {
|
||||||
repository {
|
if (it is ProjectDependency)
|
||||||
setRepoKey("maven-snapshots")
|
addRelocations(it.dependencyProject, shadowJar)
|
||||||
setMavenCompatible(true)
|
|
||||||
}
|
|
||||||
defaults {
|
|
||||||
publishConfigs("archives")
|
|
||||||
setPublishArtifacts(true)
|
|
||||||
setPublishPom(true)
|
|
||||||
setPublishIvy(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ plugins {
|
|||||||
id("floodgate.build-logic")
|
id("floodgate.build-logic")
|
||||||
// id("com.github.spotbugs") version "4.8.0" apply false
|
// id("com.github.spotbugs") version "4.8.0" apply false
|
||||||
id("io.freefair.lombok") version "6.3.0" apply false
|
id("io.freefair.lombok") version "6.3.0" apply false
|
||||||
|
// checkstyle
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@@ -11,7 +12,10 @@ allprojects {
|
|||||||
description = "Allows Bedrock players to join Java edition servers while keeping the server in online mode"
|
description = "Allows Bedrock players to join Java edition servers while keeping the server in online mode"
|
||||||
}
|
}
|
||||||
|
|
||||||
val platforms = setOf(
|
val deployProjects = setOf(
|
||||||
|
projects.api,
|
||||||
|
// for future Floodgate integration + Fabric
|
||||||
|
projects.core,
|
||||||
projects.bungee,
|
projects.bungee,
|
||||||
projects.spigot,
|
projects.spigot,
|
||||||
projects.velocity
|
projects.velocity
|
||||||
@@ -19,28 +23,32 @@ val platforms = setOf(
|
|||||||
|
|
||||||
//todo re-add pmd and organisation/license/sdcm/issuemanagement stuff
|
//todo re-add pmd and organisation/license/sdcm/issuemanagement stuff
|
||||||
|
|
||||||
val api: Project = projects.api.dependencyProject
|
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
// apply(plugin = "pmd")
|
|
||||||
// apply(plugin = "com.github.spotbugs")
|
// apply(plugin = "com.github.spotbugs")
|
||||||
|
|
||||||
apply {
|
apply {
|
||||||
plugin("java-library")
|
plugin("java-library")
|
||||||
|
// plugin("checkstyle")
|
||||||
plugin("io.freefair.lombok")
|
plugin("io.freefair.lombok")
|
||||||
plugin("floodgate.build-logic")
|
plugin("floodgate.build-logic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkstyle {
|
||||||
|
// toolVersion = "9.3"
|
||||||
|
// configFile = rootProject.projectDir.resolve("checkstyle.xml")
|
||||||
|
// maxErrors = 0
|
||||||
|
// maxWarnings = 0
|
||||||
|
// }
|
||||||
|
|
||||||
val relativePath = projectDir.relativeTo(rootProject.projectDir).path
|
val relativePath = projectDir.relativeTo(rootProject.projectDir).path
|
||||||
|
|
||||||
if (relativePath.startsWith("database" + File.separator)) {
|
if (relativePath.startsWith("database" + File.separator)) {
|
||||||
group = rootProject.group as String + ".database"
|
group = rootProject.group as String + ".database"
|
||||||
plugins.apply("floodgate.database-conventions")
|
plugins.apply("floodgate.database-conventions")
|
||||||
} else {
|
}
|
||||||
|
|
||||||
when (this) {
|
when (this) {
|
||||||
in platforms -> plugins.apply("floodgate.shadow-conventions")
|
in deployProjects -> plugins.apply("floodgate.publish-conventions")
|
||||||
api -> plugins.apply("floodgate.shadow-conventions")
|
|
||||||
else -> plugins.apply("floodgate.base-conventions")
|
else -> plugins.apply("floodgate.base-conventions")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -4,10 +4,7 @@ var guavaVersion = "21.0"
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
|
||||||
implementation("cloud.commandframework", "cloud-bungee", Versions.cloudVersion)
|
implementation("cloud.commandframework", "cloud-bungee", Versions.cloudVersion)
|
||||||
implementation("net.kyori", "adventure-text-serializer-gson", Versions.adventureApiVersion)
|
|
||||||
implementation("net.kyori", "adventure-text-serializer-bungeecord", Versions.adventurePlatformVersion)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
relocate("com.google.inject")
|
relocate("com.google.inject")
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public final class BungeeInjector extends CommonPlatformInjector {
|
|||||||
// (Instead of just replacing the ChannelInitializer which is only called for
|
// (Instead of just replacing the ChannelInitializer which is only called for
|
||||||
// player <-> proxy)
|
// player <-> proxy)
|
||||||
BungeeCustomPrepender customPrepender = new BungeeCustomPrepender(
|
BungeeCustomPrepender customPrepender = new BungeeCustomPrepender(
|
||||||
this, ReflectionUtils.getCastedValue(null, framePrepender)
|
this, ReflectionUtils.castedStaticValue(framePrepender)
|
||||||
);
|
);
|
||||||
|
|
||||||
BungeeReflectionUtils.setFieldValue(null, framePrepender, customPrepender);
|
BungeeReflectionUtils.setFieldValue(null, framePrepender, customPrepender);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import java.util.UUID;
|
|||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.event.LoginEvent;
|
import net.md_5.bungee.api.event.LoginEvent;
|
||||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
|
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||||
import net.md_5.bungee.api.plugin.Listener;
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
import net.md_5.bungee.connection.InitialHandler;
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
@@ -45,8 +46,9 @@ import net.md_5.bungee.netty.ChannelWrapper;
|
|||||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||||
import org.geysermc.floodgate.player.FloodgatePlayerImpl;
|
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||||
import org.geysermc.floodgate.util.BungeeCommandUtil;
|
import org.geysermc.floodgate.skin.SkinApplier;
|
||||||
|
import org.geysermc.floodgate.skin.SkinData;
|
||||||
import org.geysermc.floodgate.util.LanguageManager;
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||||
|
|
||||||
@@ -64,9 +66,11 @@ public final class BungeeListener implements Listener {
|
|||||||
checkNotNull(PLAYER_NAME, "Initial name field cannot be null");
|
checkNotNull(PLAYER_NAME, "Initial name field cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject private ProxyFloodgateConfig config;
|
||||||
@Inject private ProxyFloodgateApi api;
|
@Inject private ProxyFloodgateApi api;
|
||||||
@Inject private LanguageManager languageManager;
|
@Inject private LanguageManager languageManager;
|
||||||
@Inject private FloodgateLogger logger;
|
@Inject private FloodgateLogger logger;
|
||||||
|
@Inject private SkinApplier skinApplier;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Named("playerAttribute")
|
@Named("playerAttribute")
|
||||||
@@ -121,10 +125,19 @@ public final class BungeeListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPostLogin(PostLoginEvent event) {
|
||||||
|
// To fix the February 2 2022 Mojang authentication changes
|
||||||
|
if (!config.isSendFloodgateData()) {
|
||||||
|
FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId());
|
||||||
|
if (player != null && !player.isLinked()) {
|
||||||
|
skinApplier.applySkin(player, new SkinData("", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||||
api.playerRemoved(event.getPlayer().getUniqueId());
|
api.playerRemoved(event.getPlayer().getUniqueId());
|
||||||
|
|
||||||
BungeeCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import org.geysermc.floodgate.logger.JavaUtilFloodgateLogger;
|
|||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
||||||
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
|
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlatformUtils;
|
||||||
import org.geysermc.floodgate.player.FloodgateCommandPreprocessor;
|
import org.geysermc.floodgate.player.FloodgateCommandPreprocessor;
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.pluginmessage.BungeePluginMessageRegistration;
|
import org.geysermc.floodgate.pluginmessage.BungeePluginMessageRegistration;
|
||||||
@@ -55,12 +56,18 @@ import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
|||||||
import org.geysermc.floodgate.pluginmessage.PluginMessageRegistration;
|
import org.geysermc.floodgate.pluginmessage.PluginMessageRegistration;
|
||||||
import org.geysermc.floodgate.skin.SkinApplier;
|
import org.geysermc.floodgate.skin.SkinApplier;
|
||||||
import org.geysermc.floodgate.util.BungeeCommandUtil;
|
import org.geysermc.floodgate.util.BungeeCommandUtil;
|
||||||
|
import org.geysermc.floodgate.util.BungeePlatformUtils;
|
||||||
import org.geysermc.floodgate.util.LanguageManager;
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public final class BungeePlatformModule extends AbstractModule {
|
public final class BungeePlatformModule extends AbstractModule {
|
||||||
private final BungeePlugin plugin;
|
private final BungeePlugin plugin;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(PlatformUtils.class).to(BungeePlatformUtils.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
public Plugin bungeePlugin() {
|
public Plugin bungeePlugin() {
|
||||||
@@ -83,7 +90,7 @@ public final class BungeePlatformModule extends AbstractModule {
|
|||||||
CommandManager<UserAudience> commandManager = new BungeeCommandManager<>(
|
CommandManager<UserAudience> commandManager = new BungeeCommandManager<>(
|
||||||
plugin,
|
plugin,
|
||||||
CommandExecutionCoordinator.simpleCoordinator(),
|
CommandExecutionCoordinator.simpleCoordinator(),
|
||||||
commandUtil::getAudience,
|
commandUtil::getUserAudience,
|
||||||
audience -> (CommandSender) audience.source()
|
audience -> (CommandSender) audience.source()
|
||||||
);
|
);
|
||||||
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
|
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
|
||||||
@@ -92,11 +99,8 @@ public final class BungeePlatformModule extends AbstractModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
public CommandUtil commandUtil(
|
public CommandUtil commandUtil(FloodgateApi api, LanguageManager languageManager) {
|
||||||
FloodgateApi api,
|
return new BungeeCommandUtil(languageManager, plugin.getProxy(), api);
|
||||||
FloodgateLogger logger,
|
|
||||||
LanguageManager languageManager) {
|
|
||||||
return new BungeeCommandUtil(plugin.getProxy(), api, logger, languageManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.pluginmessage;
|
package org.geysermc.floodgate.pluginmessage;
|
||||||
|
|
||||||
|
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
|
||||||
|
import static org.geysermc.floodgate.util.ReflectionUtils.setValue;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
@@ -38,6 +42,12 @@ import org.geysermc.floodgate.skin.SkinData;
|
|||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public final class BungeeSkinApplier implements SkinApplier {
|
public final class BungeeSkinApplier implements SkinApplier {
|
||||||
|
private static final Field LOGIN_RESULT;
|
||||||
|
|
||||||
|
static {
|
||||||
|
LOGIN_RESULT = getFieldOfType(InitialHandler.class, LoginResult.class);
|
||||||
|
}
|
||||||
|
|
||||||
private final FloodgateLogger logger;
|
private final FloodgateLogger logger;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,6 +71,7 @@ public final class BungeeSkinApplier implements SkinApplier {
|
|||||||
if (loginResult == null) {
|
if (loginResult == null) {
|
||||||
// id and name are unused and properties will be overridden
|
// id and name are unused and properties will be overridden
|
||||||
loginResult = new LoginResult(null, null, null);
|
loginResult = new LoginResult(null, null, null);
|
||||||
|
setValue(handler, LOGIN_RESULT, loginResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
Property property = new Property("textures", skinData.getValue(), skinData.getSignature());
|
Property property = new Property("textures", skinData.getValue(), skinData.getSignature());
|
||||||
|
|||||||
@@ -25,42 +25,29 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType;
|
import org.geysermc.floodgate.player.UserAudience.ConsoleAudience;
|
||||||
import org.geysermc.floodgate.util.BungeeUserAudience.BungeeConsoleAudience;
|
import org.geysermc.floodgate.player.UserAudience.PlayerAudience;
|
||||||
import org.geysermc.floodgate.util.BungeeUserAudience.BungeePlayerAudience;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public final class BungeeCommandUtil implements CommandUtil {
|
|
||||||
public static final @NonNull Map<UUID, UserAudience> AUDIENCE_CACHE = new HashMap<>();
|
|
||||||
private static UserAudience console;
|
|
||||||
|
|
||||||
|
public final class BungeeCommandUtil extends CommandUtil {
|
||||||
private final ProxyServer server;
|
private final ProxyServer server;
|
||||||
private final FloodgateApi api;
|
private UserAudience console;
|
||||||
|
|
||||||
private final FloodgateLogger logger;
|
public BungeeCommandUtil(LanguageManager manager, ProxyServer server, FloodgateApi api) {
|
||||||
private final LanguageManager manager;
|
super(manager, api);
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getAudience(@NonNull Object sourceObj) {
|
public @NonNull UserAudience getUserAudience(@NonNull Object sourceObj) {
|
||||||
if (!(sourceObj instanceof CommandSender)) {
|
if (!(sourceObj instanceof CommandSender)) {
|
||||||
throw new IllegalArgumentException("Can only work with CommandSource!");
|
throw new IllegalArgumentException("Can only work with CommandSource!");
|
||||||
}
|
}
|
||||||
@@ -70,7 +57,7 @@ public final class BungeeCommandUtil implements CommandUtil {
|
|||||||
if (console != null) {
|
if (console != null) {
|
||||||
return console;
|
return console;
|
||||||
}
|
}
|
||||||
return console = new BungeeConsoleAudience(source, this);
|
return console = new ConsoleAudience(source, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProxiedPlayer player = (ProxiedPlayer) source;
|
ProxiedPlayer player = (ProxiedPlayer) source;
|
||||||
@@ -78,82 +65,39 @@ public final class BungeeCommandUtil implements CommandUtil {
|
|||||||
String username = player.getName();
|
String username = player.getName();
|
||||||
String locale = Utils.getLocale(player.getLocale());
|
String locale = Utils.getLocale(player.getLocale());
|
||||||
|
|
||||||
return AUDIENCE_CACHE.computeIfAbsent(uuid,
|
return new PlayerAudience(uuid, username, locale, source, this, true);
|
||||||
$ -> new BungeePlayerAudience(uuid, username, locale, source, true, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UserAudience getAudienceByUsername(@NonNull String username) {
|
protected String getUsernameFromSource(@NonNull Object source) {
|
||||||
ProxiedPlayer player = server.getPlayer(username);
|
return ((ProxiedPlayer) source).getName();
|
||||||
return player != null ? getAudience(player) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getOfflineAudienceByUsername(@NonNull String username) {
|
protected UUID getUuidFromSource(@NonNull Object source) {
|
||||||
return new BungeePlayerAudience(null, username, null, null, false, this);
|
return ((ProxiedPlayer) source).getUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UserAudience getAudienceByUuid(@NonNull UUID uuid) {
|
protected Collection<?> getOnlinePlayers() {
|
||||||
|
return server.getPlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPlayerByUuid(@NonNull UUID uuid) {
|
||||||
ProxiedPlayer player = server.getPlayer(uuid);
|
ProxiedPlayer player = server.getPlayer(uuid);
|
||||||
return player != null ? getAudience(player) : null;
|
return player != null ? player : uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) {
|
public Object getPlayerByUsername(@NonNull String username) {
|
||||||
return new BungeePlayerAudience(uuid, null, null, null, false, this);
|
ProxiedPlayer player = server.getPlayer(username);
|
||||||
}
|
return player != null ? player : username;
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
|
|
||||||
Collection<ProxiedPlayer> players = server.getPlayers();
|
|
||||||
|
|
||||||
Collection<String> usernames = new ArrayList<>();
|
|
||||||
switch (limitTo) {
|
|
||||||
case ALL_PLAYERS:
|
|
||||||
for (ProxiedPlayer player : players) {
|
|
||||||
usernames.add(player.getName());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLY_JAVA:
|
|
||||||
for (ProxiedPlayer player : players) {
|
|
||||||
if (!api.isFloodgatePlayer(player.getUniqueId())) {
|
|
||||||
usernames.add(player.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLY_BEDROCK:
|
|
||||||
for (ProxiedPlayer player : players) {
|
|
||||||
if (api.isFloodgatePlayer(player.getUniqueId())) {
|
|
||||||
usernames.add(player.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown PlayerType");
|
|
||||||
}
|
|
||||||
return usernames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(Object player, String permission) {
|
public boolean hasPermission(Object player, String permission) {
|
||||||
return cast(player).hasPermission(permission);
|
return ((CommandSender) player).hasPermission(permission);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Object> getOnlinePlayersWithPermission(String permission) {
|
|
||||||
List<Object> players = new ArrayList<>();
|
|
||||||
for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) {
|
|
||||||
if (hasPermission(player, permission)) {
|
|
||||||
players.add(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return players;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(Object target, String locale, TranslatableMessage message, Object... args) {
|
|
||||||
((CommandSender) target).sendMessage(translateAndTransform(locale, message, args));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -162,23 +106,10 @@ public final class BungeeCommandUtil implements CommandUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args) {
|
public void kickPlayer(Object player, String message) {
|
||||||
cast(player).disconnect(translateAndTransform(locale, message, args));
|
// can also be a console
|
||||||
}
|
if (player instanceof ProxiedPlayer) {
|
||||||
|
((ProxiedPlayer) player).disconnect(message);
|
||||||
public BaseComponent[] translateAndTransform(
|
|
||||||
String locale,
|
|
||||||
TranslatableMessage message,
|
|
||||||
Object... args) {
|
|
||||||
return TextComponent.fromLegacyText(message.translateMessage(manager, locale, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ProxiedPlayer cast(Object player) {
|
|
||||||
try {
|
|
||||||
return (ProxiedPlayer) player;
|
|
||||||
} catch (ClassCastException exception) {
|
|
||||||
logger.error("Failed to cast {} to ProxiedPlayer", player.getClass().getName());
|
|
||||||
throw exception;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlatformUtils;
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
public final class BungeePlatformUtils extends PlatformUtils {
|
||||||
|
private static final String LATEST_SUPPORTED_VERSION;
|
||||||
|
private final ProxyServer proxyServer = ProxyServer.getInstance();
|
||||||
|
|
||||||
|
static {
|
||||||
|
int protocolNumber = -1;
|
||||||
|
String versionName = "";
|
||||||
|
|
||||||
|
for (Field field : ProtocolConstants.class.getFields()) {
|
||||||
|
if (!field.getName().startsWith("MINECRAFT_")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fieldValue = ReflectionUtils.castedStaticValue(field);
|
||||||
|
if (fieldValue > protocolNumber) {
|
||||||
|
protocolNumber = fieldValue;
|
||||||
|
versionName = field.getName().substring(10).replace('_', '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protocolNumber == -1) {
|
||||||
|
List<String> versions = ProtocolConstants.SUPPORTED_VERSIONS;
|
||||||
|
versionName = versions.get(versions.size() - 1);
|
||||||
|
}
|
||||||
|
LATEST_SUPPORTED_VERSION = versionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthType authType() {
|
||||||
|
return proxyServer.getConfig().isOnlineMode() ? AuthType.ONLINE : AuthType.OFFLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String minecraftVersion() {
|
||||||
|
return LATEST_SUPPORTED_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serverImplementationName() {
|
||||||
|
return proxyServer.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,159 +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 java.util.UUID;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import net.kyori.adventure.audience.Audience;
|
|
||||||
import net.kyori.adventure.audience.ForwardingAudience;
|
|
||||||
import net.kyori.adventure.audience.MessageType;
|
|
||||||
import net.kyori.adventure.identity.Identity;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
|
||||||
import net.md_5.bungee.api.CommandSender;
|
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class BungeeUserAudience implements UserAudience, ForwardingAudience.Single {
|
|
||||||
private final UUID uuid;
|
|
||||||
private final String locale;
|
|
||||||
private final CommandSender source;
|
|
||||||
private final CommandUtil commandUtil;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull UUID uuid() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String username() {
|
|
||||||
return source.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String locale() {
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull CommandSender source() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(@NonNull String permission) {
|
|
||||||
return source.hasPermission(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(
|
|
||||||
@NonNull Identity source,
|
|
||||||
@NonNull Component message,
|
|
||||||
@NonNull MessageType type) {
|
|
||||||
this.source.sendMessage(GsonComponentSerializer.gson().serialize(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(TranslatableMessage message, Object... args) {
|
|
||||||
commandUtil.sendMessage(source(), locale(), message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnect(@NonNull Component reason) {
|
|
||||||
if (source instanceof ProxiedPlayer) {
|
|
||||||
((ProxiedPlayer) source).disconnect(GsonComponentSerializer.gson().serialize(reason));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnect(TranslatableMessage message, Object... args) {
|
|
||||||
commandUtil.kickPlayer(source(), locale(), message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Audience audience() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class BungeeConsoleAudience extends BungeeUserAudience
|
|
||||||
implements ConsoleAudience {
|
|
||||||
|
|
||||||
public BungeeConsoleAudience(CommandSender source, CommandUtil commandUtil) {
|
|
||||||
super(new UUID(0, 0), "en_us", source, commandUtil);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(
|
|
||||||
@NonNull Identity source,
|
|
||||||
@NonNull Component message,
|
|
||||||
@NonNull MessageType type) {
|
|
||||||
source().sendMessage(BungeeComponentSerializer.get().serialize(message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class BungeePlayerAudience extends BungeeUserAudience
|
|
||||||
implements PlayerAudience {
|
|
||||||
|
|
||||||
private final String username;
|
|
||||||
private final boolean online;
|
|
||||||
|
|
||||||
public BungeePlayerAudience(
|
|
||||||
UUID uuid,
|
|
||||||
String username,
|
|
||||||
String locale,
|
|
||||||
CommandSender source,
|
|
||||||
boolean online,
|
|
||||||
CommandUtil commandUtil) {
|
|
||||||
super(uuid, locale, source, commandUtil);
|
|
||||||
this.username = username;
|
|
||||||
this.online = online;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BungeePlayerAudience(
|
|
||||||
UUID uuid,
|
|
||||||
String locale,
|
|
||||||
CommandSender source,
|
|
||||||
boolean online,
|
|
||||||
CommandUtil commandUtil) {
|
|
||||||
this(uuid, source.getName(), locale, source, online, commandUtil);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String username() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean online() {
|
|
||||||
return online;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
364
checkstyle.xml
Normal file
364
checkstyle.xml
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE module PUBLIC
|
||||||
|
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||||
|
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Checkstyle configuration that checks the Google coding conventions from Google Java Style
|
||||||
|
that can be found at https://google.github.io/styleguide/javaguide.html
|
||||||
|
|
||||||
|
Checkstyle is very configurable. Be sure to read the documentation at
|
||||||
|
http://checkstyle.org (or in your downloaded distribution).
|
||||||
|
|
||||||
|
To completely disable a check, just comment it out or delete it from the file.
|
||||||
|
To suppress certain violations please review suppression filters.
|
||||||
|
|
||||||
|
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module name = "Checker">
|
||||||
|
<property name="charset" value="UTF-8"/>
|
||||||
|
|
||||||
|
<property name="severity" value="warning"/>
|
||||||
|
|
||||||
|
<property name="fileExtensions" value="java, properties, xml"/>
|
||||||
|
<!-- Excludes all 'module-info.java' files -->
|
||||||
|
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||||
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
|
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||||
|
</module>
|
||||||
|
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
||||||
|
<module name="SuppressionFilter">
|
||||||
|
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
|
||||||
|
default="checkstyle-suppressions.xml" />
|
||||||
|
<property name="optional" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- Checks for whitespace -->
|
||||||
|
<!-- See http://checkstyle.org/config_whitespace.html -->
|
||||||
|
<module name="FileTabCharacter">
|
||||||
|
<property name="eachLine" value="true"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="LineLength">
|
||||||
|
<property name="fileExtensions" value="java"/>
|
||||||
|
<property name="max" value="100"/>
|
||||||
|
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<module name="TreeWalker">
|
||||||
|
<module name="OuterTypeFilename"/>
|
||||||
|
<module name="IllegalTokenText">
|
||||||
|
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||||
|
<property name="format"
|
||||||
|
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||||
|
<property name="message"
|
||||||
|
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidEscapedUnicodeCharacters">
|
||||||
|
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||||
|
<property name="allowByTailComment" value="true"/>
|
||||||
|
<property name="allowNonPrintableEscapes" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="AvoidStarImport"/>
|
||||||
|
<module name="OneTopLevelClass"/>
|
||||||
|
<module name="NoLineWrap">
|
||||||
|
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
|
||||||
|
</module>
|
||||||
|
<module name="EmptyBlock">
|
||||||
|
<property name="option" value="TEXT"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||||
|
</module>
|
||||||
|
<module name="NeedBraces">
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
|
||||||
|
</module>
|
||||||
|
<module name="LeftCurly">
|
||||||
|
<property name="tokens"
|
||||||
|
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
|
||||||
|
INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
|
||||||
|
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
|
||||||
|
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
|
||||||
|
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="id" value="RightCurlySame"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
|
||||||
|
LITERAL_DO"/>
|
||||||
|
</module>
|
||||||
|
<module name="RightCurly">
|
||||||
|
<property name="id" value="RightCurlyAlone"/>
|
||||||
|
<property name="option" value="alone"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||||
|
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
|
||||||
|
COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="SuppressionXpathSingleFilter">
|
||||||
|
<!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
|
||||||
|
<property name="id" value="RightCurlyAlone"/>
|
||||||
|
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1]
|
||||||
|
or preceding-sibling::*[last()][self::LCURLY]]"/>
|
||||||
|
</module>
|
||||||
|
<module name="WhitespaceAfter">
|
||||||
|
<property name="tokens"
|
||||||
|
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE,
|
||||||
|
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, DO_WHILE"/>
|
||||||
|
</module>
|
||||||
|
<module name="WhitespaceAround">
|
||||||
|
<property name="allowEmptyConstructors" value="true"/>
|
||||||
|
<property name="allowEmptyLambdas" value="true"/>
|
||||||
|
<property name="allowEmptyMethods" value="true"/>
|
||||||
|
<property name="allowEmptyTypes" value="true"/>
|
||||||
|
<property name="allowEmptyLoops" value="true"/>
|
||||||
|
<property name="ignoreEnhancedForColon" value="false"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
|
||||||
|
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
|
||||||
|
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
|
||||||
|
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
|
||||||
|
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
|
||||||
|
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
|
||||||
|
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
|
||||||
|
<message key="ws.notFollowed"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="OneStatementPerLine"/>
|
||||||
|
<module name="MultipleVariableDeclarations"/>
|
||||||
|
<module name="ArrayTypeStyle"/>
|
||||||
|
<module name="MissingSwitchDefault"/>
|
||||||
|
<module name="FallThrough"/>
|
||||||
|
<module name="UpperEll"/>
|
||||||
|
<module name="ModifierOrder"/>
|
||||||
|
<module name="EmptyLineSeparator">
|
||||||
|
<property name="tokens"
|
||||||
|
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||||
|
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
|
||||||
|
COMPACT_CTOR_DEF"/>
|
||||||
|
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapDot"/>
|
||||||
|
<property name="tokens" value="DOT"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapComma"/>
|
||||||
|
<property name="tokens" value="COMMA"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||||
|
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||||
|
<property name="tokens" value="ELLIPSIS"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||||
|
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||||
|
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||||
|
<property name="option" value="EOL"/>
|
||||||
|
</module>
|
||||||
|
<module name="SeparatorWrap">
|
||||||
|
<property name="id" value="SeparatorWrapMethodRef"/>
|
||||||
|
<property name="tokens" value="METHOD_REF"/>
|
||||||
|
<property name="option" value="nl"/>
|
||||||
|
</module>
|
||||||
|
<module name="PackageName">
|
||||||
|
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="TypeName">
|
||||||
|
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||||
|
ANNOTATION_DEF, RECORD_DEF"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MemberName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="LambdaParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="CatchParameterName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="LocalVariableName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="PatternVariableName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="ClassTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="RecordComponentName">
|
||||||
|
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Record component name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="RecordTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Record type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="InterfaceTypeParameterName">
|
||||||
|
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="NoFinalizer"/>
|
||||||
|
<module name="GenericWhitespace">
|
||||||
|
<message key="ws.followed"
|
||||||
|
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||||
|
<message key="ws.preceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||||
|
<message key="ws.illegalFollow"
|
||||||
|
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||||
|
<message key="ws.notPreceded"
|
||||||
|
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||||
|
</module>
|
||||||
|
<module name="Indentation">
|
||||||
|
<property name="basicOffset" value="2"/>
|
||||||
|
<property name="braceAdjustment" value="2"/>
|
||||||
|
<property name="caseIndent" value="2"/>
|
||||||
|
<property name="throwsIndent" value="4"/>
|
||||||
|
<property name="lineWrappingIndentation" value="4"/>
|
||||||
|
<property name="arrayInitIndent" value="2"/>
|
||||||
|
</module>
|
||||||
|
<module name="AbbreviationAsWordInName">
|
||||||
|
<property name="ignoreFinal" value="false"/>
|
||||||
|
<property name="allowedAbbreviationLength" value="0"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
|
||||||
|
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
|
||||||
|
RECORD_COMPONENT_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
|
||||||
|
<module name="OverloadMethodsDeclarationOrder"/>
|
||||||
|
<module name="VariableDeclarationUsageDistance"/>
|
||||||
|
<module name="CustomImportOrder">
|
||||||
|
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||||
|
<property name="separateLineBetweenGroups" value="true"/>
|
||||||
|
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
|
||||||
|
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodParamPad">
|
||||||
|
<property name="tokens"
|
||||||
|
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
|
||||||
|
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="NoWhitespaceBefore">
|
||||||
|
<property name="tokens"
|
||||||
|
value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
|
||||||
|
LABELED_STAT, METHOD_REF"/>
|
||||||
|
<property name="allowLineBreaks" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="ParenPad">
|
||||||
|
<property name="tokens"
|
||||||
|
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
|
||||||
|
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
|
||||||
|
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
|
||||||
|
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
|
||||||
|
RECORD_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="OperatorWrap">
|
||||||
|
<property name="option" value="NL"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||||
|
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
|
||||||
|
TYPE_EXTENSION_AND "/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="id" value="AnnotationLocationMostCases"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
|
||||||
|
RECORD_DEF, COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="AnnotationLocation">
|
||||||
|
<property name="id" value="AnnotationLocationVariables"/>
|
||||||
|
<property name="tokens" value="VARIABLE_DEF"/>
|
||||||
|
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||||
|
</module>
|
||||||
|
<module name="NonEmptyAtclauseDescription"/>
|
||||||
|
<module name="InvalidJavadocPosition"/>
|
||||||
|
<module name="JavadocTagContinuationIndentation"/>
|
||||||
|
<module name="SummaryJavadoc">
|
||||||
|
<property name="forbiddenSummaryFragments"
|
||||||
|
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocParagraph"/>
|
||||||
|
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
|
||||||
|
<module name="AtclauseOrder">
|
||||||
|
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||||
|
<property name="target"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="JavadocMethod">
|
||||||
|
<property name="accessModifiers" value="public"/>
|
||||||
|
<property name="allowMissingParamTags" value="true"/>
|
||||||
|
<property name="allowMissingReturnTag" value="true"/>
|
||||||
|
<property name="allowedAnnotations" value="Override, Test"/>
|
||||||
|
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="MissingJavadocMethod">
|
||||||
|
<property name="scope" value="public"/>
|
||||||
|
<property name="minLineCount" value="2"/>
|
||||||
|
<property name="allowedAnnotations" value="Override, Test"/>
|
||||||
|
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
|
||||||
|
COMPACT_CTOR_DEF"/>
|
||||||
|
</module>
|
||||||
|
<module name="MissingJavadocType">
|
||||||
|
<property name="scope" value="protected"/>
|
||||||
|
<property name="tokens"
|
||||||
|
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||||
|
RECORD_DEF, ANNOTATION_DEF"/>
|
||||||
|
<property name="excludeScope" value="nothing"/>
|
||||||
|
</module>
|
||||||
|
<module name="MethodName">
|
||||||
|
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||||
|
<message key="name.invalidPattern"
|
||||||
|
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||||
|
</module>
|
||||||
|
<module name="SingleLineJavadoc"/>
|
||||||
|
<module name="EmptyCatchBlock">
|
||||||
|
<property name="exceptionVariableName" value="expected"/>
|
||||||
|
</module>
|
||||||
|
<module name="CommentsIndentation">
|
||||||
|
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
|
||||||
|
</module>
|
||||||
|
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
|
||||||
|
<module name="SuppressionXpathFilter">
|
||||||
|
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
|
||||||
|
default="checkstyle-xpath-suppressions.xml" />
|
||||||
|
<property name="optional" value="true"/>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
|
</module>
|
||||||
@@ -6,22 +6,26 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.api)
|
api(projects.api)
|
||||||
|
api("org.geysermc.configutils", "configutils", Versions.configUtilsVersion)
|
||||||
|
|
||||||
api("com.google.inject", "guice", Versions.guiceVersion)
|
api("com.google.inject", "guice", Versions.guiceVersion)
|
||||||
api("com.nukkitx.fastutil", "fastutil-short-object-maps", Versions.fastutilVersion)
|
api("com.nukkitx.fastutil", "fastutil-short-object-maps", Versions.fastutilVersion)
|
||||||
api("com.nukkitx.fastutil", "fastutil-int-object-maps", Versions.fastutilVersion)
|
api("com.nukkitx.fastutil", "fastutil-int-object-maps", Versions.fastutilVersion)
|
||||||
api("org.java-websocket", "Java-WebSocket", Versions.javaWebsocketVersion)
|
api("org.java-websocket", "Java-WebSocket", Versions.javaWebsocketVersion)
|
||||||
api("net.kyori", "adventure-api", Versions.adventureApiVersion)
|
|
||||||
api("cloud.commandframework", "cloud-core", Versions.cloudVersion)
|
api("cloud.commandframework", "cloud-core", Versions.cloudVersion)
|
||||||
api("org.yaml", "snakeyaml", Versions.snakeyamlVersion)
|
api("org.yaml", "snakeyaml", Versions.snakeyamlVersion)
|
||||||
|
api("org.bstats", "bstats-base", Versions.bstatsVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// present on all platforms
|
// present on all platforms
|
||||||
provided("io.netty", "netty-transport", Versions.nettyVersion)
|
provided("io.netty", "netty-transport", Versions.nettyVersion)
|
||||||
provided("io.netty", "netty-codec", Versions.nettyVersion)
|
provided("io.netty", "netty-codec", Versions.nettyVersion)
|
||||||
|
|
||||||
|
relocate("org.bstats")
|
||||||
|
|
||||||
configure<BlossomExtension> {
|
configure<BlossomExtension> {
|
||||||
val constantsFile = "src/main/java/org/geysermc/floodgate/util/Constants.java"
|
val constantsFile = "src/main/java/org/geysermc/floodgate/util/Constants.java"
|
||||||
|
replaceToken("\${floodgateVersion}", fullVersion(), constantsFile)
|
||||||
replaceToken("\${branch}", branchName(), constantsFile)
|
replaceToken("\${branch}", branchName(), constantsFile)
|
||||||
replaceToken("\${buildNumber}", buildNumber(), constantsFile)
|
replaceToken("\${buildNumber}", buildNumber(), constantsFile)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,13 +40,14 @@ import org.geysermc.floodgate.api.inject.PlatformInjector;
|
|||||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
import org.geysermc.floodgate.api.packet.PacketHandlers;
|
import org.geysermc.floodgate.api.packet.PacketHandlers;
|
||||||
|
import org.geysermc.floodgate.config.ConfigLoader;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||||
import org.geysermc.floodgate.config.loader.ConfigLoader;
|
|
||||||
import org.geysermc.floodgate.link.PlayerLinkLoader;
|
import org.geysermc.floodgate.link.PlayerLinkLoader;
|
||||||
import org.geysermc.floodgate.module.ConfigLoadedModule;
|
import org.geysermc.floodgate.module.ConfigLoadedModule;
|
||||||
import org.geysermc.floodgate.module.PostInitializeModule;
|
import org.geysermc.floodgate.module.PostInitializeModule;
|
||||||
import org.geysermc.floodgate.news.NewsChecker;
|
import org.geysermc.floodgate.news.NewsChecker;
|
||||||
|
import org.geysermc.floodgate.util.Metrics;
|
||||||
import org.geysermc.floodgate.util.PrefixCheckTask;
|
import org.geysermc.floodgate.util.PrefixCheckTask;
|
||||||
|
|
||||||
public class FloodgatePlatform {
|
public class FloodgatePlatform {
|
||||||
@@ -100,8 +101,6 @@ public class FloodgatePlatform {
|
|||||||
|
|
||||||
InstanceHolder.set(api, link, this.injector, packetHandlers, handshakeHandlers, KEY);
|
InstanceHolder.set(api, link, this.injector, packetHandlers, handshakeHandlers, KEY);
|
||||||
|
|
||||||
// todo provide build number and branch for Geyser dump
|
|
||||||
|
|
||||||
guice.getInstance(NewsChecker.class).start();
|
guice.getInstance(NewsChecker.class).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +124,8 @@ public class FloodgatePlatform {
|
|||||||
|
|
||||||
PrefixCheckTask.checkAndExecuteDelayed(config, logger);
|
PrefixCheckTask.checkAndExecuteDelayed(config, logger);
|
||||||
|
|
||||||
|
guice.getInstance(Metrics.class);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +140,7 @@ public class FloodgatePlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guice.getInstance(NewsChecker.class).shutdown();
|
||||||
api.getPlayerLink().stop();
|
api.getPlayerLink().stop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class HandshakeDataImpl implements HandshakeData {
|
|||||||
int usernameLength = Math.min(bedrockData.getUsername().length(), 16 - prefix.length());
|
int usernameLength = Math.min(bedrockData.getUsername().length(), 16 - prefix.length());
|
||||||
javaUsername = prefix + bedrockData.getUsername().substring(0, usernameLength);
|
javaUsername = prefix + bedrockData.getUsername().substring(0, usernameLength);
|
||||||
if (config.isReplaceSpaces()) {
|
if (config.isReplaceSpaces()) {
|
||||||
javaUsername = javaUsername.replaceAll(" ", "_");
|
javaUsername = javaUsername.replace(" ", "_");
|
||||||
}
|
}
|
||||||
|
|
||||||
javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid());
|
javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid());
|
||||||
|
|||||||
@@ -35,20 +35,20 @@ import cloud.commandframework.context.CommandContext;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
import org.geysermc.floodgate.api.link.LinkRequestResult;
|
import org.geysermc.floodgate.api.link.LinkRequestResult;
|
||||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
|
import org.geysermc.floodgate.command.util.Permission;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||||
import org.geysermc.floodgate.link.GlobalPlayerLinking;
|
import org.geysermc.floodgate.link.GlobalPlayerLinking;
|
||||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudience.PlayerAudience;
|
import org.geysermc.floodgate.player.UserAudience.PlayerAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudienceArgument;
|
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.Constants;
|
||||||
import org.geysermc.floodgate.util.Permissions;
|
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public final class LinkAccountCommand implements FloodgateCommand {
|
public final class LinkAccountCommand implements FloodgateCommand {
|
||||||
@@ -60,8 +60,8 @@ public final class LinkAccountCommand implements FloodgateCommand {
|
|||||||
return commandManager.commandBuilder("linkaccount",
|
return commandManager.commandBuilder("linkaccount",
|
||||||
ArgumentDescription.of("Link your Java account with your Bedrock account"))
|
ArgumentDescription.of("Link your Java account with your Bedrock account"))
|
||||||
.senderType(PlayerAudience.class)
|
.senderType(PlayerAudience.class)
|
||||||
.permission(Permissions.COMMAND_LINK.get())
|
.permission(Permission.COMMAND_LINK.get())
|
||||||
.argument(UserAudienceArgument.of("player", true))
|
.argument(ProfileAudienceArgument.of("player", true))
|
||||||
.argument(StringArgument.optional("code"))
|
.argument(StringArgument.optional("code"))
|
||||||
.handler(this::execute)
|
.handler(this::execute)
|
||||||
.build();
|
.build();
|
||||||
@@ -90,6 +90,10 @@ public final class LinkAccountCommand implements FloodgateCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProfileAudience targetUser = context.get("player");
|
||||||
|
// allowUuid is false so username cannot be null
|
||||||
|
String targetName = targetUser.username();
|
||||||
|
|
||||||
// when the player is a Bedrock player
|
// when the player is a Bedrock player
|
||||||
if (api.isFloodgatePlayer(sender.uuid())) {
|
if (api.isFloodgatePlayer(sender.uuid())) {
|
||||||
if (!context.contains("code")) {
|
if (!context.contains("code")) {
|
||||||
@@ -97,8 +101,6 @@ public final class LinkAccountCommand implements FloodgateCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAudience targetUser = context.get("player");
|
|
||||||
String targetName = targetUser.username();
|
|
||||||
String code = context.get("code");
|
String code = context.get("code");
|
||||||
|
|
||||||
link.verifyLinkRequest(sender.uuid(), targetName, sender.username(), code)
|
link.verifyLinkRequest(sender.uuid(), targetName, sender.username(), code)
|
||||||
@@ -125,7 +127,7 @@ public final class LinkAccountCommand implements FloodgateCommand {
|
|||||||
sender.disconnect(Message.LINK_REQUEST_COMPLETED, targetName);
|
sender.disconnect(Message.LINK_REQUEST_COMPLETED, targetName);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
sender.disconnect(Component.text("Invalid account linking result"));
|
sender.disconnect("Invalid account linking result");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -137,9 +139,6 @@ public final class LinkAccountCommand implements FloodgateCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAudience targetUser = context.get("player");
|
|
||||||
String targetName = targetUser.username();
|
|
||||||
|
|
||||||
link.createLinkRequest(sender.uuid(), sender.username(), targetName)
|
link.createLinkRequest(sender.uuid(), sender.username(), targetName)
|
||||||
.whenComplete((result, throwable) -> {
|
.whenComplete((result, throwable) -> {
|
||||||
if (throwable != null || result == LinkRequestResult.UNKNOWN_ERROR) {
|
if (throwable != null || result == LinkRequestResult.UNKNOWN_ERROR) {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ package org.geysermc.floodgate.command;
|
|||||||
import cloud.commandframework.Command;
|
import cloud.commandframework.Command;
|
||||||
import cloud.commandframework.CommandManager;
|
import cloud.commandframework.CommandManager;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||||
@@ -47,7 +46,7 @@ public class TestCommand implements FloodgateCommand {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(CommandContext<UserAudience> context) {
|
public void execute(CommandContext<UserAudience> context) {
|
||||||
int players = FloodgateApi.getInstance().getPlayers().size();
|
int players = FloodgateApi.getInstance().getPlayers().size();
|
||||||
context.getSender().sendMessage(Component.text(players));
|
context.getSender().sendMessage(String.valueOf(players));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudience.PlayerAudience;
|
import org.geysermc.floodgate.player.UserAudience.PlayerAudience;
|
||||||
import org.geysermc.floodgate.util.Constants;
|
import org.geysermc.floodgate.util.Constants;
|
||||||
import org.geysermc.floodgate.util.Permissions;
|
import org.geysermc.floodgate.command.util.Permission;
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public final class UnlinkAccountCommand implements FloodgateCommand {
|
public final class UnlinkAccountCommand implements FloodgateCommand {
|
||||||
@@ -54,7 +54,7 @@ public final class UnlinkAccountCommand implements FloodgateCommand {
|
|||||||
return commandManager.commandBuilder("unlinkaccount",
|
return commandManager.commandBuilder("unlinkaccount",
|
||||||
ArgumentDescription.of("Unlink your Java account from your Bedrock account"))
|
ArgumentDescription.of("Unlink your Java account from your Bedrock account"))
|
||||||
.senderType(PlayerAudience.class)
|
.senderType(PlayerAudience.class)
|
||||||
.permission(Permissions.COMMAND_UNLINK.get())
|
.permission(Permission.COMMAND_UNLINK.get())
|
||||||
.handler(this::execute)
|
.handler(this::execute)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,17 +38,18 @@ import java.util.UUID;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
|
import org.geysermc.floodgate.command.util.Permission;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlayerType;
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudienceArgument;
|
import org.geysermc.floodgate.player.audience.ProfileAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType;
|
import org.geysermc.floodgate.player.audience.ProfileAudienceArgument;
|
||||||
import org.geysermc.floodgate.util.Constants;
|
import org.geysermc.floodgate.util.Constants;
|
||||||
import org.geysermc.floodgate.util.HttpUtils;
|
import org.geysermc.floodgate.util.HttpUtils;
|
||||||
import org.geysermc.floodgate.util.Permissions;
|
|
||||||
|
|
||||||
public class WhitelistCommand implements FloodgateCommand {
|
public class WhitelistCommand implements FloodgateCommand {
|
||||||
@Inject private FloodgateConfig config;
|
@Inject private FloodgateConfig config;
|
||||||
@@ -58,25 +59,25 @@ public class WhitelistCommand implements FloodgateCommand {
|
|||||||
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
|
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
|
||||||
Command.Builder<UserAudience> builder = commandManager.commandBuilder("fwhitelist",
|
Command.Builder<UserAudience> builder = commandManager.commandBuilder("fwhitelist",
|
||||||
ArgumentDescription.of("Easy way to whitelist Bedrock players"))
|
ArgumentDescription.of("Easy way to whitelist Bedrock players"))
|
||||||
.permission(Permissions.COMMAND_WHITELIST.get());
|
.permission(Permission.COMMAND_WHITELIST.get());
|
||||||
|
|
||||||
commandManager.command(builder
|
commandManager.command(builder
|
||||||
.literal("add", "a")
|
.literal("add", "a")
|
||||||
.argument(UserAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK))
|
.argument(ProfileAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK))
|
||||||
.handler(context -> performCommand(context, true)));
|
.handler(context -> performCommand(context, true)));
|
||||||
|
|
||||||
return builder
|
return builder
|
||||||
.literal("remove", "r")
|
.literal("remove", "r")
|
||||||
.argument(UserAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK))
|
.argument(ProfileAudienceArgument.of("player", true, true, PlayerType.ONLY_BEDROCK))
|
||||||
.handler(context -> performCommand(context, false))
|
.handler(context -> performCommand(context, false))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void performCommand(CommandContext<UserAudience> context, boolean add) {
|
public void performCommand(CommandContext<UserAudience> context, boolean add) {
|
||||||
UserAudience sender = context.getSender();
|
UserAudience sender = context.getSender();
|
||||||
UserAudience player = context.get("player");
|
ProfileAudience profile = context.get("player");
|
||||||
UUID uuid = player.uuid();
|
UUID uuid = profile.uuid();
|
||||||
String name = player.username();
|
String name = profile.username();
|
||||||
|
|
||||||
if (name == null && uuid == null) {
|
if (name == null && uuid == null) {
|
||||||
sender.sendMessage(Message.UNEXPECTED_ERROR);
|
sender.sendMessage(Message.UNEXPECTED_ERROR);
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ import cloud.commandframework.context.CommandContext;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.geysermc.floodgate.command.util.Permission;
|
||||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.util.Permissions;
|
|
||||||
|
|
||||||
public final class MainCommand implements FloodgateCommand {
|
public final class MainCommand implements FloodgateCommand {
|
||||||
@Override
|
@Override
|
||||||
@@ -46,12 +46,13 @@ public final class MainCommand implements FloodgateCommand {
|
|||||||
"floodgate",
|
"floodgate",
|
||||||
ArgumentDescription.of("A set of Floodgate related actions in one command"))
|
ArgumentDescription.of("A set of Floodgate related actions in one command"))
|
||||||
.senderType(UserAudience.class)
|
.senderType(UserAudience.class)
|
||||||
.permission(Permissions.COMMAND_MAIN.get())
|
.permission(Permission.COMMAND_MAIN.get())
|
||||||
.handler(this::execute);
|
.handler(this::execute);
|
||||||
|
|
||||||
for (SubCommand subCommand : SubCommand.VALUES) {
|
for (SubCommand subCommand : SubCommand.VALUES) {
|
||||||
commandManager.command(builder
|
commandManager.command(builder
|
||||||
.literal(subCommand.name().toLowerCase(Locale.ROOT), subCommand.description)
|
.literal(subCommand.name().toLowerCase(Locale.ROOT), subCommand.description)
|
||||||
|
.permission(subCommand.permission.get())
|
||||||
.handler(subCommand.executor::accept)
|
.handler(subCommand.executor::accept)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -65,11 +66,13 @@ public final class MainCommand implements FloodgateCommand {
|
|||||||
StringBuilder helpMessage = new StringBuilder("Available subcommands are:\n");
|
StringBuilder helpMessage = new StringBuilder("Available subcommands are:\n");
|
||||||
|
|
||||||
for (SubCommand subCommand : SubCommand.VALUES) {
|
for (SubCommand subCommand : SubCommand.VALUES) {
|
||||||
|
if (context.getSender().hasPermission(subCommand.permission.get())) {
|
||||||
helpMessage.append('\n').append(COLOR_CHAR).append('b')
|
helpMessage.append('\n').append(COLOR_CHAR).append('b')
|
||||||
.append(subCommand.name().toLowerCase(Locale.ROOT))
|
.append(subCommand.name().toLowerCase(Locale.ROOT))
|
||||||
.append(COLOR_CHAR).append("f - ").append(COLOR_CHAR).append('7')
|
.append(COLOR_CHAR).append("f - ").append(COLOR_CHAR).append('7')
|
||||||
.append(subCommand.description);
|
.append(subCommand.description);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.getSender().sendMessage(helpMessage.toString());
|
context.getSender().sendMessage(helpMessage.toString());
|
||||||
}
|
}
|
||||||
@@ -77,11 +80,12 @@ public final class MainCommand implements FloodgateCommand {
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
enum SubCommand {
|
enum SubCommand {
|
||||||
FIREWALL("Check if your outgoing firewall allows Floodgate to work properly",
|
FIREWALL("Check if your outgoing firewall allows Floodgate to work properly",
|
||||||
FirewallCheckSubcommand::executeFirewall);
|
Permission.COMMAND_MAIN_FIREWALL, FirewallCheckSubcommand::executeFirewall);
|
||||||
|
|
||||||
static final SubCommand[] VALUES = values();
|
static final SubCommand[] VALUES = values();
|
||||||
|
|
||||||
final String description;
|
final String description;
|
||||||
|
final Permission permission;
|
||||||
final Consumer<CommandContext<UserAudience>> executor;
|
final Consumer<CommandContext<UserAudience>> executor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import static org.geysermc.floodgate.command.util.PermissionDefault.OP;
|
||||||
|
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_LINK("floodgate.command.linkaccount", TRUE),
|
||||||
|
COMMAND_UNLINK("floodgate.command.unlinkaccount", TRUE),
|
||||||
|
COMMAND_WHITELIST("floodgate.command.fwhitelist", OP),
|
||||||
|
|
||||||
|
NEWS_RECEIVE("floodgate.news.receive", OP);
|
||||||
|
|
||||||
|
private final String permission;
|
||||||
|
private final PermissionDefault defaultValue;
|
||||||
|
|
||||||
|
Permission(String permission, PermissionDefault defaultValue) {
|
||||||
|
this.permission = permission;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Permission(Permission parent, String child, PermissionDefault defaultValue) {
|
||||||
|
this(parent.get() + "." + child, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String get() {
|
||||||
|
return permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionDefault defaultValue() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
public enum PermissionDefault {
|
||||||
|
TRUE, FALSE, OP, NOT_OP
|
||||||
|
}
|
||||||
@@ -23,26 +23,27 @@
|
|||||||
* @link https://github.com/GeyserMC/Floodgate
|
* @link https://github.com/GeyserMC/Floodgate
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.floodgate.config.loader;
|
package org.geysermc.floodgate.config;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.api.logger.FloodgateLogger;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
|
||||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
|
||||||
import org.geysermc.floodgate.config.updater.ConfigUpdater;
|
|
||||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||||
import org.geysermc.floodgate.crypto.KeyProducer;
|
import org.geysermc.floodgate.crypto.KeyProducer;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public final class ConfigLoader {
|
public final class ConfigLoader {
|
||||||
private final Path dataFolder;
|
private final Path dataFolder;
|
||||||
private final Class<? extends FloodgateConfig> configClass;
|
private final Class<? extends FloodgateConfig> configClass;
|
||||||
private final DefaultConfigHandler configCreator;
|
|
||||||
private final ConfigUpdater updater;
|
|
||||||
|
|
||||||
private final KeyProducer keyProducer;
|
private final KeyProducer keyProducer;
|
||||||
private final FloodgateCipher cipher;
|
private final FloodgateCipher cipher;
|
||||||
@@ -51,61 +52,42 @@ public final class ConfigLoader {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T extends FloodgateConfig> T load() {
|
public <T extends FloodgateConfig> T load() {
|
||||||
Path configPath = dataFolder.resolve("config.yml");
|
String templateFile = "config.yml";
|
||||||
|
if (ProxyFloodgateConfig.class.isAssignableFrom(configClass)) {
|
||||||
String defaultConfigName = "config.yml";
|
templateFile = "proxy-" + templateFile;
|
||||||
boolean proxy = ProxyFloodgateConfig.class.isAssignableFrom(configClass);
|
|
||||||
if (proxy) {
|
|
||||||
defaultConfigName = "proxy-" + defaultConfigName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean newConfig = !Files.exists(configPath);
|
//todo old Floodgate logged a message when version = 0 and it generated a new key.
|
||||||
if (newConfig) {
|
// Might be nice to allow you to run a function for a specific version.
|
||||||
try {
|
|
||||||
configCreator.createDefaultConfig(defaultConfigName, configPath);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
logger.error("Error while creating config", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T configInstance;
|
// it would also be nice to have sections in versionBuilder so that you don't have to
|
||||||
try {
|
// provide the path all the time
|
||||||
// check and update if the config is outdated
|
|
||||||
if (!newConfig) {
|
|
||||||
updater.update(this, defaultConfigName);
|
|
||||||
}
|
|
||||||
|
|
||||||
FloodgateConfig config = ConfigInitializer.initializeFrom(
|
ConfigUtilities utilities =
|
||||||
Files.newInputStream(configPath), configClass);
|
ConfigUtilities.builder()
|
||||||
|
.fileCodec(PathFileCodec.of(dataFolder))
|
||||||
|
.configFile("config.yml")
|
||||||
|
.templateReader(ResourceTemplateReader.of(getClass()))
|
||||||
|
.template(templateFile)
|
||||||
|
.changes(Changes.builder()
|
||||||
|
.version(1, Changes.versionBuilder()
|
||||||
|
.keyRenamed("player-link.enable", "player-link.enabled")
|
||||||
|
.keyRenamed("player-link.allow-linking", "player-link.allowed"))
|
||||||
|
.version(2, Changes.versionBuilder()
|
||||||
|
.keyRenamed("player-link.use-global-linking", "player-link.enable-global-linking"))
|
||||||
|
.build())
|
||||||
|
.definePlaceholder("metrics.uuid", UUID::randomUUID)
|
||||||
|
.postInitializeCallbackArgument(this)
|
||||||
|
.build();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configInstance = (T) config;
|
return (T) utilities.executeOn(configClass);
|
||||||
} catch (ClassCastException exception) {
|
} catch (Throwable throwable) {
|
||||||
logger.error("Failed to cast config file to required class.", exception);
|
|
||||||
throw new RuntimeException(exception);
|
|
||||||
}
|
|
||||||
} catch (Exception exception) {
|
|
||||||
logger.error("Error while loading config", exception);
|
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Failed to load the config! Try to delete the config file", exception);
|
"Failed to load the config! Try to delete the config file if this error persists",
|
||||||
|
throwable
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Path keyPath = dataFolder.resolve(configInstance.getKeyFileName());
|
|
||||||
// don't assume that the key always exists with the existence of a config
|
|
||||||
if (!Files.exists(keyPath)) {
|
|
||||||
generateKey(keyPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Key key = keyProducer.produceFrom(keyPath);
|
|
||||||
cipher.init(key);
|
|
||||||
configInstance.setKey(key);
|
|
||||||
} catch (IOException exception) {
|
|
||||||
logger.error("Error while reading the key", exception);
|
|
||||||
throw new RuntimeException("Failed to read the key!", exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
return configInstance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateKey(Path keyPath) {
|
public void generateKey(Path keyPath) {
|
||||||
@@ -25,15 +25,20 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.config;
|
package org.geysermc.floodgate.config;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.geysermc.configutils.loader.callback.CallbackResult;
|
||||||
|
import org.geysermc.configutils.loader.callback.GenericPostInitializeCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The global Floodgate configuration file used in every platform. Some platforms have their own
|
* The global Floodgate configuration file used in every platform. Some platforms have their own
|
||||||
* addition to the global configuration like {@link ProxyFloodgateConfig} for the proxies.
|
* addition to the global configuration like {@link ProxyFloodgateConfig} for the proxies.
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class FloodgateConfig {
|
public class FloodgateConfig implements GenericPostInitializeCallback<ConfigLoader> {
|
||||||
private String keyFileName;
|
private String keyFileName;
|
||||||
private String usernamePrefix;
|
private String usernamePrefix;
|
||||||
private boolean replaceSpaces;
|
private boolean replaceSpaces;
|
||||||
@@ -42,22 +47,36 @@ public class FloodgateConfig {
|
|||||||
|
|
||||||
private DisconnectMessages disconnect;
|
private DisconnectMessages disconnect;
|
||||||
private PlayerLinkConfig playerLink;
|
private PlayerLinkConfig playerLink;
|
||||||
|
private MetricsConfig metrics;
|
||||||
|
|
||||||
private boolean debug;
|
private boolean debug;
|
||||||
private int configVersion;
|
private int configVersion;
|
||||||
|
|
||||||
private Key key;
|
private Key key;
|
||||||
|
|
||||||
public void setKey(Key key) {
|
|
||||||
if (this.key == null) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isProxy() {
|
public boolean isProxy() {
|
||||||
return this instanceof ProxyFloodgateConfig;
|
return this instanceof ProxyFloodgateConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallbackResult postInitialize(ConfigLoader loader) {
|
||||||
|
Path keyPath = loader.getDataFolder().resolve(getKeyFileName());
|
||||||
|
|
||||||
|
// don't assume that the key always exists with the existence of a config
|
||||||
|
if (!Files.exists(keyPath)) {
|
||||||
|
loader.generateKey(keyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Key floodgateKey = loader.getKeyProducer().produceFrom(keyPath);
|
||||||
|
loader.getCipher().init(floodgateKey);
|
||||||
|
key = floodgateKey;
|
||||||
|
} catch (IOException exception) {
|
||||||
|
return CallbackResult.failed(exception.getMessage());
|
||||||
|
}
|
||||||
|
return CallbackResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public static class DisconnectMessages {
|
public static class DisconnectMessages {
|
||||||
private String invalidKey;
|
private String invalidKey;
|
||||||
@@ -68,10 +87,16 @@ public class FloodgateConfig {
|
|||||||
public static class PlayerLinkConfig {
|
public static class PlayerLinkConfig {
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private boolean requireLink;
|
private boolean requireLink;
|
||||||
private boolean enableOwnLinking = false;
|
private boolean enableOwnLinking;
|
||||||
private boolean allowed;
|
private boolean allowed;
|
||||||
private long linkCodeTimeout;
|
private long linkCodeTimeout;
|
||||||
private String type;
|
private String type;
|
||||||
private boolean enableGlobalLinking;
|
private boolean enableGlobalLinking;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class MetricsConfig {
|
||||||
|
private boolean enabled;
|
||||||
|
private String uuid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,109 +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.config.loader;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
import org.yaml.snakeyaml.constructor.Constructor;
|
|
||||||
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
|
|
||||||
import org.yaml.snakeyaml.introspector.BeanAccess;
|
|
||||||
import org.yaml.snakeyaml.introspector.FieldProperty;
|
|
||||||
import org.yaml.snakeyaml.introspector.Property;
|
|
||||||
import org.yaml.snakeyaml.introspector.PropertyUtils;
|
|
||||||
|
|
||||||
public class ConfigInitializer {
|
|
||||||
private static final Yaml YAML;
|
|
||||||
|
|
||||||
static {
|
|
||||||
Constructor constructor =
|
|
||||||
new CustomClassLoaderConstructor(ConfigInitializer.class.getClassLoader());
|
|
||||||
|
|
||||||
constructor.setPropertyUtils(new PropertyUtils() {
|
|
||||||
@Override
|
|
||||||
protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess) {
|
|
||||||
Map<String, Property> properties = new LinkedHashMap<>();
|
|
||||||
getPropertiesFromClass(type, FloodgateConfig.class, properties);
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getPropertiesFromClass(
|
|
||||||
Class<?> type,
|
|
||||||
Class<?> stopAfter,
|
|
||||||
Map<String, Property> propertyMap) {
|
|
||||||
|
|
||||||
Class<?> current = type;
|
|
||||||
while (!Object.class.equals(current)) {
|
|
||||||
for (Field field : current.getDeclaredFields()) {
|
|
||||||
int modifiers = field.getModifiers();
|
|
||||||
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
|
|
||||||
String correctName = getCorrectName(field.getName());
|
|
||||||
// children should override parents
|
|
||||||
propertyMap.putIfAbsent(correctName, new FieldProperty(field));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.getClass().getSuperclass().equals(current)) {
|
|
||||||
getPropertiesFromClass(field.getClass(), field.getClass(), propertyMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current.equals(stopAfter)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = type.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCorrectName(String name) {
|
|
||||||
// convert sendFloodgateData to send-floodgate-data,
|
|
||||||
// which is the style of writing config fields
|
|
||||||
StringBuilder propertyBuilder = new StringBuilder();
|
|
||||||
for (int i = 0; i < name.length(); i++) {
|
|
||||||
char current = name.charAt(i);
|
|
||||||
if (Character.isUpperCase(current)) {
|
|
||||||
propertyBuilder.append('-').append(Character.toLowerCase(current));
|
|
||||||
} else {
|
|
||||||
propertyBuilder.append(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return propertyBuilder.toString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
constructor.getPropertyUtils().setSkipMissingProperties(true);
|
|
||||||
YAML = new Yaml(constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends FloodgateConfig> T initializeFrom(
|
|
||||||
InputStream dataStream,
|
|
||||||
Class<T> configClass) {
|
|
||||||
return YAML.loadAs(dataStream, configClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,159 +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.config.loader;
|
|
||||||
|
|
||||||
import static org.geysermc.floodgate.util.MessageFormatter.format;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import org.geysermc.floodgate.util.Utils;
|
|
||||||
|
|
||||||
public class DefaultConfigHandler {
|
|
||||||
public void createDefaultConfig(String defaultConfigLocation, Path configPath) throws IOException {
|
|
||||||
List<String> configLines = loadDefaultConfig(defaultConfigLocation);
|
|
||||||
|
|
||||||
// writing the new config file
|
|
||||||
Files.write(configPath, configLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> loadDefaultConfig(String defaultConfigLocation)
|
|
||||||
throws IOException {
|
|
||||||
List<String> lines = Utils.readAllLines(defaultConfigLocation);
|
|
||||||
|
|
||||||
List<String> configLines = new ArrayList<>();
|
|
||||||
String parentConfig = null;
|
|
||||||
List<String> parentLines = null;
|
|
||||||
|
|
||||||
int lastInsertLine = -1;
|
|
||||||
int tempAddAfter = -1;
|
|
||||||
|
|
||||||
for (String line : lines) {
|
|
||||||
// >>(space) or >>|
|
|
||||||
if (line.startsWith(">>")) {
|
|
||||||
if (line.length() >= 3) {
|
|
||||||
|
|
||||||
// define parent file
|
|
||||||
if (line.charAt(2) == ' ') {
|
|
||||||
if (tempAddAfter != -1) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Cannot define new parent without closing the current section");
|
|
||||||
}
|
|
||||||
parentConfig = line.substring(3);
|
|
||||||
parentLines = null;
|
|
||||||
lastInsertLine = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// define start / end of insert section
|
|
||||||
if (line.charAt(2) == '|') {
|
|
||||||
// end section
|
|
||||||
if (line.length() == 3) {
|
|
||||||
if (tempAddAfter == -1) {
|
|
||||||
throw new IllegalStateException("Cannot close an unclosed section");
|
|
||||||
}
|
|
||||||
lastInsertLine = tempAddAfter;
|
|
||||||
tempAddAfter = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// start insert section
|
|
||||||
if (parentConfig == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Cannot start insert section without providing a parent");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempAddAfter != -1) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Cannot start section with an unclosed section");
|
|
||||||
}
|
|
||||||
|
|
||||||
// note that addAfter starts counting from 1
|
|
||||||
int addAfter = Integer.parseInt(line.substring(4)) - 1;
|
|
||||||
if (lastInsertLine > -1 && addAfter < lastInsertLine) {
|
|
||||||
throw new IllegalStateException(format(
|
|
||||||
"Cannot add the same lines twice {} {}",
|
|
||||||
addAfter, lastInsertLine
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// as you can see by this implementation
|
|
||||||
// we don't support parent files in parent files
|
|
||||||
|
|
||||||
if (lastInsertLine == -1) {
|
|
||||||
parentLines = Utils.readAllLines(parentConfig);
|
|
||||||
|
|
||||||
for (int i = 0; i <= addAfter; i++) {
|
|
||||||
configLines.add(parentLines.get(i));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = lastInsertLine; i <= addAfter; i++) {
|
|
||||||
configLines.add(parentLines.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tempAddAfter = addAfter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.charAt(2) == '*') {
|
|
||||||
if (parentConfig == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Cannot write rest of the parent without providing a parent");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempAddAfter != -1) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Cannot write rest of the parent config while an insert section is still open");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastInsertLine == -1) {
|
|
||||||
parentLines = Utils.readAllLines(parentConfig);
|
|
||||||
configLines.addAll(parentLines);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the lastInsertLine has already been printed, so we won't print it twice
|
|
||||||
for (int i = lastInsertLine + 1; i < parentLines.size(); i++) {
|
|
||||||
configLines.add(parentLines.get(i));
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"The use of >>" + line.charAt(2) + " is unknown");
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("Unable do something with just >>");
|
|
||||||
}
|
|
||||||
// everything else: comments and key/value lines will be added
|
|
||||||
configLines.add(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
return configLines;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,182 +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.config.updater;
|
|
||||||
|
|
||||||
import com.google.common.base.Ascii;
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
|
||||||
import org.geysermc.floodgate.config.loader.DefaultConfigHandler;
|
|
||||||
|
|
||||||
public final class ConfigFileUpdater {
|
|
||||||
@Inject private FloodgateLogger logger;
|
|
||||||
@Inject private DefaultConfigHandler defaultConfigHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple config file updater. Please note that all the keys should be unique and that this
|
|
||||||
* system wasn't made for complex configurations.
|
|
||||||
*
|
|
||||||
* @param configLocation the location of the Floodgate config
|
|
||||||
* @param currentVersion the key value map of the current config
|
|
||||||
* @param renames name changes introduced in this version. new (key) to old
|
|
||||||
* (value)
|
|
||||||
* @param defaultConfigLocation the location of the default Floodgate config
|
|
||||||
* @throws IOException if an I/O error occurs
|
|
||||||
*/
|
|
||||||
public void update(
|
|
||||||
Path configLocation,
|
|
||||||
Map<String, Object> currentVersion,
|
|
||||||
Map<String, String> renames,
|
|
||||||
String defaultConfigLocation)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
List<String> notFound = new ArrayList<>();
|
|
||||||
List<String> newConfig = defaultConfigHandler.loadDefaultConfig(defaultConfigLocation);
|
|
||||||
|
|
||||||
String spaces = "";
|
|
||||||
Map<String, Object> map = null;
|
|
||||||
|
|
||||||
String line;
|
|
||||||
for (int i = 0; i < newConfig.size(); i++) {
|
|
||||||
line = newConfig.get(i);
|
|
||||||
// we don't have to check comments or empty lines
|
|
||||||
if (line.isEmpty() || line.charAt(0) == '#') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder currentSpaces = new StringBuilder();
|
|
||||||
while (line.charAt(currentSpaces.length()) == Ascii.SPACE) {
|
|
||||||
currentSpaces.append(Ascii.SPACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// end of subcategory
|
|
||||||
if (!spaces.isEmpty() && currentSpaces.length() < spaces.length()) {
|
|
||||||
// we can assume this since we don't allow subcategories of subcategories
|
|
||||||
spaces = "";
|
|
||||||
map = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore comments
|
|
||||||
if (line.charAt(currentSpaces.length()) == '#') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int splitIndex = line.indexOf(':');
|
|
||||||
// if the line has a 'key: value' structure
|
|
||||||
if (splitIndex != -1) {
|
|
||||||
|
|
||||||
// start of a subcategory
|
|
||||||
if (line.length() == splitIndex + 1) {
|
|
||||||
if (currentSpaces.length() > 0) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Config too complex! I can't understand subcategories of a subcategory");
|
|
||||||
}
|
|
||||||
|
|
||||||
spaces = " ";
|
|
||||||
//todo allow rename of subcategory?
|
|
||||||
//noinspection unchecked
|
|
||||||
map = (Map<String, Object>) currentVersion.get(line.substring(0, splitIndex));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = line.substring(spaces.length(), splitIndex);
|
|
||||||
|
|
||||||
// don't change the config-version to the old value!
|
|
||||||
if (name.equals("config-version")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow multiple renames
|
|
||||||
String tempName;
|
|
||||||
String oldName = name;
|
|
||||||
do {
|
|
||||||
tempName = oldName;
|
|
||||||
oldName = renames.getOrDefault(oldName, oldName);
|
|
||||||
} while (!oldName.equals(tempName));
|
|
||||||
|
|
||||||
Object value;
|
|
||||||
if (map != null) {
|
|
||||||
value = map.get(oldName);
|
|
||||||
} else {
|
|
||||||
value = currentVersion.get(spaces + oldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// use default value if the key doesn't exist in the current version
|
|
||||||
if (value == null) {
|
|
||||||
notFound.add(name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value instanceof String) {
|
|
||||||
String v = (String) value;
|
|
||||||
if (!v.startsWith("\"") || !v.endsWith("\"")) {
|
|
||||||
value = "\"" + value + "\"";
|
|
||||||
}
|
|
||||||
//todo this doesn't update {0} {1} to {} {} e.g.
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(name + " has been changed to " + value);
|
|
||||||
newConfig.set(i, spaces + name + ": " + value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Files.deleteIfExists(configLocation.getParent().resolve("config-old.yml"));
|
|
||||||
Files.copy(configLocation, configLocation.getParent().resolve("config-old.yml"));
|
|
||||||
Files.write(configLocation, newConfig);
|
|
||||||
|
|
||||||
logger.info("Successfully updated the config file! " +
|
|
||||||
"Your old config has been moved to config-old.yml");
|
|
||||||
|
|
||||||
if (!notFound.isEmpty()) {
|
|
||||||
StringBuilder messageBuilder = new StringBuilder(
|
|
||||||
"Please note that the following keys we not found in the old config and " +
|
|
||||||
"are now using the default Floodgate config value. Missing/new keys: ");
|
|
||||||
|
|
||||||
boolean first = true;
|
|
||||||
for (String value : notFound) {
|
|
||||||
if (!first) {
|
|
||||||
messageBuilder.append(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
String renamed = renames.get(value);
|
|
||||||
if (renamed != null) {
|
|
||||||
messageBuilder.append(renamed).append(" to ");
|
|
||||||
}
|
|
||||||
|
|
||||||
messageBuilder.append(value);
|
|
||||||
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(messageBuilder.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +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.config.updater;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static org.geysermc.floodgate.util.MessageFormatter.format;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
|
||||||
import org.geysermc.floodgate.config.loader.ConfigLoader;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public final class ConfigUpdater {
|
|
||||||
private static final int CONFIG_VERSION = 2;
|
|
||||||
private final Path dataFolder;
|
|
||||||
private final ConfigFileUpdater fileUpdater;
|
|
||||||
private final FloodgateLogger logger;
|
|
||||||
|
|
||||||
public void update(ConfigLoader loader, String defaultConfigLocation) {
|
|
||||||
Path configLocation = dataFolder.resolve("config.yml");
|
|
||||||
|
|
||||||
Map<String, Object> config;
|
|
||||||
|
|
||||||
try (BufferedReader configReader = Files.newBufferedReader(configLocation)) {
|
|
||||||
config = new Yaml().load(configReader);
|
|
||||||
} catch (IOException exception) {
|
|
||||||
logger.error("Error while opening the config file", exception);
|
|
||||||
throw new RuntimeException("Failed to update config", exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
// new name -> old name
|
|
||||||
Map<String, String> renames = new HashMap<>();
|
|
||||||
|
|
||||||
int version = 0; // pre-rewrite is the default config version
|
|
||||||
|
|
||||||
Object versionElement = config.get("config-version");
|
|
||||||
// only rewrite configs have a config-version
|
|
||||||
if (versionElement == null) {
|
|
||||||
logger.warn("We've detected a pre-rewrite config file, please note that Floodgate " +
|
|
||||||
"doesn't not work properly if you don't update your Floodgate key used on " +
|
|
||||||
"all your servers (including Geyser). We'll try to update your Floodgate " +
|
|
||||||
"config now and we'll also generate a new Floodgate key for you, but if " +
|
|
||||||
"you're running a network or if you're running a Spigot server with " +
|
|
||||||
"Geyser Standalone please update as you'll no longer be able to connect.");
|
|
||||||
renames.put("enabled", "enable"); //todo make dump system and add a boolean 'found-legacy-key' or something like that
|
|
||||||
renames.put("allowed", "allow-linking");
|
|
||||||
|
|
||||||
// relocate the old key so that they can restore it if it was a new key
|
|
||||||
Path keyFilePath = dataFolder.resolve((String) config.get("key-file-name"));
|
|
||||||
if (Files.exists(keyFilePath)) {
|
|
||||||
try {
|
|
||||||
Files.copy(keyFilePath, dataFolder.resolve("old-key.pem"));
|
|
||||||
} catch (IOException exception) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Failed to relocate the old key to make place for a new key",
|
|
||||||
exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loader.generateKey(keyFilePath);
|
|
||||||
} else {
|
|
||||||
// get (and verify) the config version
|
|
||||||
checkArgument(
|
|
||||||
versionElement instanceof Integer,
|
|
||||||
"Config version should be an integer. Did someone mess with the config?"
|
|
||||||
);
|
|
||||||
|
|
||||||
version = (int) versionElement;
|
|
||||||
checkArgument(
|
|
||||||
version > 0 && version <= CONFIG_VERSION,
|
|
||||||
format("Config is newer then possible on this version! Expected {}, got {}",
|
|
||||||
CONFIG_VERSION, version));
|
|
||||||
}
|
|
||||||
|
|
||||||
// config is already up-to-date
|
|
||||||
if (version == CONFIG_VERSION) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version < 2) {
|
|
||||||
// renamed 'use-global-linking' to 'enable-global-linking'
|
|
||||||
// and added 'enable-own-linking'
|
|
||||||
renames.put("enable-global-linking", "use-global-linking");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
fileUpdater.update(configLocation, config, renames, defaultConfigLocation);
|
|
||||||
} catch (IOException exception) {
|
|
||||||
logger.error("Error while updating the config file", exception);
|
|
||||||
throw new RuntimeException("Failed to update config", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -77,7 +77,7 @@ public abstract class CommonPlatformInjector implements PlatformInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to loop throguh all the addons and call {@link InjectorAddon#onChannelClosed(Channel)}
|
* Method to loop through all the addons and call {@link InjectorAddon#onChannelClosed(Channel)}
|
||||||
* if {@link InjectorAddon#shouldInject()}
|
* if {@link InjectorAddon#shouldInject()}
|
||||||
*
|
*
|
||||||
* @param channel the channel that was injected
|
* @param channel the channel that was injected
|
||||||
|
|||||||
@@ -40,12 +40,9 @@ import org.geysermc.floodgate.api.inject.PlatformInjector;
|
|||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
import org.geysermc.floodgate.api.packet.PacketHandlers;
|
import org.geysermc.floodgate.api.packet.PacketHandlers;
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||||
|
import org.geysermc.floodgate.config.ConfigLoader;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||||
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
import org.geysermc.floodgate.config.FloodgateConfigHolder;
|
||||||
import org.geysermc.floodgate.config.loader.ConfigLoader;
|
|
||||||
import org.geysermc.floodgate.config.loader.DefaultConfigHandler;
|
|
||||||
import org.geysermc.floodgate.config.updater.ConfigFileUpdater;
|
|
||||||
import org.geysermc.floodgate.config.updater.ConfigUpdater;
|
|
||||||
import org.geysermc.floodgate.crypto.AesCipher;
|
import org.geysermc.floodgate.crypto.AesCipher;
|
||||||
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
||||||
import org.geysermc.floodgate.crypto.Base64Topping;
|
import org.geysermc.floodgate.crypto.Base64Topping;
|
||||||
@@ -105,27 +102,10 @@ public class CommonModule extends AbstractModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
public ConfigLoader configLoader(
|
public ConfigLoader configLoader(
|
||||||
@Named("configClass") Class<? extends FloodgateConfig> configClass,
|
@Named("configClass") Class<? extends FloodgateConfig> configClass,
|
||||||
DefaultConfigHandler defaultConfigHandler,
|
|
||||||
ConfigUpdater configUpdater,
|
|
||||||
KeyProducer producer,
|
KeyProducer producer,
|
||||||
FloodgateCipher cipher,
|
FloodgateCipher cipher,
|
||||||
FloodgateLogger logger) {
|
FloodgateLogger logger) {
|
||||||
return new ConfigLoader(dataDirectory, configClass, defaultConfigHandler, configUpdater,
|
return new ConfigLoader(dataDirectory, configClass, producer, cipher, logger);
|
||||||
producer, cipher, logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public DefaultConfigHandler defaultConfigCreator() {
|
|
||||||
return new DefaultConfigHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public ConfigUpdater configUpdater(
|
|
||||||
ConfigFileUpdater configFileUpdater,
|
|
||||||
FloodgateLogger logger) {
|
|
||||||
return new ConfigUpdater(dataDirectory, configFileUpdater, logger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import org.geysermc.floodgate.platform.command.CommandUtil;
|
|||||||
import org.geysermc.floodgate.util.Constants;
|
import org.geysermc.floodgate.util.Constants;
|
||||||
import org.geysermc.floodgate.util.HttpUtils;
|
import org.geysermc.floodgate.util.HttpUtils;
|
||||||
import org.geysermc.floodgate.util.HttpUtils.HttpResponse;
|
import org.geysermc.floodgate.util.HttpUtils.HttpResponse;
|
||||||
import org.geysermc.floodgate.util.Permissions;
|
import org.geysermc.floodgate.command.util.Permission;
|
||||||
|
|
||||||
public class NewsChecker {
|
public class NewsChecker {
|
||||||
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
|
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
|
||||||
@@ -72,8 +72,6 @@ public class NewsChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkNews() {
|
private void checkNews() {
|
||||||
// todo also check news for the downloaded database types
|
|
||||||
|
|
||||||
HttpResponse<JsonArray> response =
|
HttpResponse<JsonArray> response =
|
||||||
HttpUtils.getSilent(
|
HttpUtils.getSilent(
|
||||||
Constants.NEWS_OVERVIEW_URL + Constants.NEWS_PROJECT_NAME,
|
Constants.NEWS_OVERVIEW_URL + Constants.NEWS_PROJECT_NAME,
|
||||||
@@ -121,14 +119,14 @@ public class NewsChecker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandUtil.hasPermission(player, Permissions.NEWS_RECEIVE.get())) {
|
if (commandUtil.hasPermission(player, Permission.NEWS_RECEIVE.get())) {
|
||||||
String message = Constants.COLOR_CHAR + "a " + news.getMessage();
|
String message = Constants.COLOR_CHAR + "a " + news.getMessage();
|
||||||
commandUtil.sendMessage(player, message);
|
commandUtil.sendMessage(player, message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BROADCAST_TO_OPERATORS:
|
case BROADCAST_TO_OPERATORS:
|
||||||
Collection<Object> onlinePlayers = commandUtil.getOnlinePlayersWithPermission(
|
Collection<Object> onlinePlayers = commandUtil.getOnlinePlayersWithPermission(
|
||||||
Permissions.NEWS_RECEIVE.get()
|
Permission.NEWS_RECEIVE.get()
|
||||||
);
|
);
|
||||||
|
|
||||||
for (Object onlinePlayer : onlinePlayers) {
|
for (Object onlinePlayer : onlinePlayers) {
|
||||||
@@ -182,7 +180,8 @@ public class NewsChecker {
|
|||||||
schedule(delayMs > 0 ? delayMs : 0);
|
schedule(delayMs > 0 ? delayMs : 0);
|
||||||
break;
|
break;
|
||||||
case CONFIG_SPECIFIC:
|
case CONFIG_SPECIFIC:
|
||||||
//todo
|
//todo this can replace the downloaded database types update check.
|
||||||
|
// check if ConfigUtils has a way to check this easily
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
activeNewsItems.put(item.getId(), item);
|
activeNewsItems.put(item.getId(), item);
|
||||||
|
|||||||
@@ -25,30 +25,118 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.platform.command;
|
package org.geysermc.floodgate.platform.command;
|
||||||
|
|
||||||
|
import static org.geysermc.floodgate.platform.util.PlayerType.ALL_PLAYERS;
|
||||||
|
import static org.geysermc.floodgate.platform.util.PlayerType.ONLY_BEDROCK;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlayerType;
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType;
|
import org.geysermc.floodgate.player.audience.ProfileAudience;
|
||||||
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
import org.geysermc.floodgate.util.Utils;
|
import org.geysermc.floodgate.util.Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface used across all Floodgate platforms to simple stuff in commands like kicking players
|
* An interface used across all Floodgate platforms to simple stuff in commands like kicking players
|
||||||
* and sending player messages independent of the Floodgate platform implementation.
|
* and sending player messages independent of the Floodgate platform implementation.
|
||||||
*/
|
*/
|
||||||
public interface CommandUtil {
|
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
@NonNull UserAudience getAudience(final @NonNull Object source);
|
public abstract class CommandUtil {
|
||||||
|
protected final LanguageManager manager;
|
||||||
|
protected final FloodgateApi api;
|
||||||
|
|
||||||
@Nullable UserAudience getAudienceByUuid(final @NonNull UUID uuid);
|
public abstract @NonNull UserAudience getUserAudience(@NonNull Object source);
|
||||||
|
|
||||||
@NonNull UserAudience getOfflineAudienceByUuid(final @NonNull UUID uuid);
|
/**
|
||||||
|
* Get a ProfileAudience from a source. The source should be a platform-specific player instance
|
||||||
|
* when the player is online, and the username / uuid of the requested player when offline.
|
||||||
|
*
|
||||||
|
* @param source source to create a ProfileAudience from
|
||||||
|
* @param allowOffline if offline players are allowed
|
||||||
|
* @return a ProfileAudience unless allowOffline is false and the player isn't online
|
||||||
|
*/
|
||||||
|
public @Nullable ProfileAudience getProfileAudience(
|
||||||
|
@NonNull Object source,
|
||||||
|
boolean allowOffline) {
|
||||||
|
Objects.requireNonNull(source);
|
||||||
|
|
||||||
@Nullable UserAudience getAudienceByUsername(final @NonNull String username);
|
if (source instanceof UUID) {
|
||||||
|
return allowOffline ? new ProfileAudience((UUID) source, null) : null;
|
||||||
|
} else if (source instanceof String) {
|
||||||
|
return allowOffline ? new ProfileAudience(null, (String) source) : null;
|
||||||
|
} else {
|
||||||
|
return new ProfileAudience(getUuidFromSource(source), getUsernameFromSource(source));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull UserAudience getOfflineAudienceByUsername(final @NonNull String username);
|
protected abstract String getUsernameFromSource(@NonNull Object source);
|
||||||
|
protected abstract UUID getUuidFromSource(@NonNull Object source);
|
||||||
|
|
||||||
@NonNull Collection<String> getOnlineUsernames(final @NonNull PlayerType limitTo);
|
protected abstract Collection<?> getOnlinePlayers();
|
||||||
|
|
||||||
|
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
|
||||||
|
Collection<?> players = getOnlinePlayers();
|
||||||
|
|
||||||
|
Collection<String> usernames = new ArrayList<>();
|
||||||
|
switch (limitTo) {
|
||||||
|
case ALL_PLAYERS:
|
||||||
|
for (Object player : players) {
|
||||||
|
usernames.add(getUsernameFromSource(player));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ONLY_JAVA:
|
||||||
|
for (Object player : players) {
|
||||||
|
if (!api.isFloodgatePlayer(getUuidFromSource(player))) {
|
||||||
|
usernames.add(getUsernameFromSource(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ONLY_BEDROCK:
|
||||||
|
for (Object player : players) {
|
||||||
|
if (api.isFloodgatePlayer(getUuidFromSource(player))) {
|
||||||
|
usernames.add(getUsernameFromSource(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown PlayerType");
|
||||||
|
}
|
||||||
|
return usernames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param uuid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract Object getPlayerByUuid(@NonNull UUID uuid);
|
||||||
|
|
||||||
|
public Object getPlayerByUuid(@NonNull UUID uuid, PlayerType limitTo) {
|
||||||
|
return applyPlayerTypeFilter(getPlayerByUuid(uuid), limitTo, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Object getPlayerByUsername(@NonNull String username);
|
||||||
|
|
||||||
|
public Object getPlayerByUsername(@NonNull String username, PlayerType limitTo) {
|
||||||
|
return applyPlayerTypeFilter(getPlayerByUsername(username), limitTo, username);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object applyPlayerTypeFilter(Object player, PlayerType filter, Object fallback) {
|
||||||
|
if (filter == ALL_PLAYERS || player instanceof String || player instanceof UUID) {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
return (filter == ONLY_BEDROCK) == api.isFloodgatePlayer(getUuidFromSource(player))
|
||||||
|
? player
|
||||||
|
: fallback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the given player has the given permission.
|
* Checks if the given player has the given permission.
|
||||||
@@ -57,7 +145,7 @@ public interface CommandUtil {
|
|||||||
* @param permission the permission to check
|
* @param permission the permission to check
|
||||||
* @return true or false depending on if the player has the permission
|
* @return true or false depending on if the player has the permission
|
||||||
*/
|
*/
|
||||||
boolean hasPermission(Object player, String permission);
|
public abstract boolean hasPermission(Object player, String permission);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all online players with the given permission.
|
* Get all online players with the given permission.
|
||||||
@@ -65,17 +153,15 @@ public interface CommandUtil {
|
|||||||
* @param permission the permission to check
|
* @param permission the permission to check
|
||||||
* @return a list of online players that have the given permission
|
* @return a list of online players that have the given permission
|
||||||
*/
|
*/
|
||||||
Collection<Object> getOnlinePlayersWithPermission(String permission);
|
public Collection<Object> getOnlinePlayersWithPermission(String permission) {
|
||||||
|
List<Object> players = new ArrayList<>();
|
||||||
/**
|
for (Object player : getOnlinePlayers()) {
|
||||||
* Send a message to the specified target, no matter what platform Floodgate is running on.
|
if (hasPermission(player, permission)) {
|
||||||
*
|
players.add(player);
|
||||||
* @param target the player that should receive the message
|
}
|
||||||
* @param message the command message
|
}
|
||||||
* @param locale the locale of the player
|
return players;
|
||||||
* @param args the arguments
|
}
|
||||||
*/
|
|
||||||
void sendMessage(Object target, String locale, TranslatableMessage message, Object... args);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a raw message to the specified target, no matter what platform Floodgate is running
|
* Sends a raw message to the specified target, no matter what platform Floodgate is running
|
||||||
@@ -84,18 +170,19 @@ public interface CommandUtil {
|
|||||||
* @param target the player that should receive the message
|
* @param target the player that should receive the message
|
||||||
* @param message the message
|
* @param message the message
|
||||||
*/
|
*/
|
||||||
void sendMessage(Object target, String message);
|
public abstract void sendMessage(Object target, String message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link CommandUtil#sendMessage(Object, String, TranslatableMessage, Object...)}
|
* Kicks the given player using the given message as the kick reason.
|
||||||
* except it kicks the player using the given message as the kick reason.
|
|
||||||
*
|
*
|
||||||
* @param player the player that should be kicked
|
* @param player the player that should be kicked
|
||||||
* @param message the command message
|
* @param message the command message
|
||||||
* @param locale the locale of the player
|
|
||||||
* @param args the arguments
|
|
||||||
*/
|
*/
|
||||||
void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args);
|
public abstract void kickPlayer(Object player, String message);
|
||||||
|
|
||||||
|
public String translateMessage(String locale, TranslatableMessage message, Object... args) {
|
||||||
|
return message.translateMessage(manager, locale, args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whitelist the given Bedrock player.
|
* Whitelist the given Bedrock player.
|
||||||
@@ -105,7 +192,7 @@ public interface CommandUtil {
|
|||||||
* @return true if the player has been whitelisted, false if the player was already whitelisted.
|
* @return true if the player has been whitelisted, false if the player was already whitelisted.
|
||||||
* Defaults to false when this platform doesn't support whitelisting.
|
* Defaults to false when this platform doesn't support whitelisting.
|
||||||
*/
|
*/
|
||||||
default boolean whitelistPlayer(String xuid, String username) {
|
public boolean whitelistPlayer(String xuid, String username) {
|
||||||
UUID uuid = Utils.getJavaUuid(xuid);
|
UUID uuid = Utils.getJavaUuid(xuid);
|
||||||
return whitelistPlayer(uuid, username);
|
return whitelistPlayer(uuid, username);
|
||||||
}
|
}
|
||||||
@@ -118,7 +205,7 @@ public interface CommandUtil {
|
|||||||
* @return true if the player has been whitelisted, false if the player was already whitelisted.
|
* @return true if the player has been whitelisted, false if the player was already whitelisted.
|
||||||
* Defaults to false when this platform doesn't support whitelisting.
|
* Defaults to false when this platform doesn't support whitelisting.
|
||||||
*/
|
*/
|
||||||
default boolean whitelistPlayer(UUID uuid, String username) {
|
public boolean whitelistPlayer(UUID uuid, String username) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +217,7 @@ public interface CommandUtil {
|
|||||||
* @return true if the player has been removed from the whitelist, false if the player wasn't
|
* @return true if the player has been removed from the whitelist, false if the player wasn't
|
||||||
* whitelisted. Defaults to false when this platform doesn't support whitelisting.
|
* whitelisted. Defaults to false when this platform doesn't support whitelisting.
|
||||||
*/
|
*/
|
||||||
default boolean removePlayerFromWhitelist(String xuid, String username) {
|
public boolean removePlayerFromWhitelist(String xuid, String username) {
|
||||||
UUID uuid = Utils.getJavaUuid(xuid);
|
UUID uuid = Utils.getJavaUuid(xuid);
|
||||||
return removePlayerFromWhitelist(uuid, username);
|
return removePlayerFromWhitelist(uuid, username);
|
||||||
}
|
}
|
||||||
@@ -143,7 +230,7 @@ public interface CommandUtil {
|
|||||||
* @return true if the player has been removed from the whitelist, false if the player wasn't
|
* @return true if the player has been removed from the whitelist, false if the player wasn't
|
||||||
* whitelisted. Defaults to false when this platform doesn't support whitelisting.
|
* whitelisted. Defaults to false when this platform doesn't support whitelisting.
|
||||||
*/
|
*/
|
||||||
default boolean removePlayerFromWhitelist(UUID uuid, String username) {
|
public boolean removePlayerFromWhitelist(UUID uuid, String username) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ package org.geysermc.floodgate.platform.command;
|
|||||||
import org.geysermc.floodgate.util.LanguageManager;
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TranslatableMessage is the interface for a message that can be translated.
|
* TranslatableMessage is the interface for a message that can be translated. Messages are generally
|
||||||
* Messages are generally implemented using enums.
|
* implemented using enums.
|
||||||
*/
|
*/
|
||||||
public interface TranslatableMessage {
|
public interface TranslatableMessage {
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +52,7 @@ public interface TranslatableMessage {
|
|||||||
for (int i = 0; i < translateParts.length; i++) {
|
for (int i = 0; i < translateParts.length; i++) {
|
||||||
builder.append(manager.getString(translateParts[i], locale, args));
|
builder.append(manager.getString(translateParts[i], locale, args));
|
||||||
if (translateParts.length != i + 1) {
|
if (translateParts.length != i + 1) {
|
||||||
builder.append(" ");
|
builder.append(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
|
|||||||
@@ -25,37 +25,26 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.platform.util;
|
package org.geysermc.floodgate.platform.util;
|
||||||
|
|
||||||
import java.util.Collection;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|
||||||
|
|
||||||
public interface PlatformUtils {
|
@RequiredArgsConstructor
|
||||||
|
public abstract class PlatformUtils {
|
||||||
/**
|
/**
|
||||||
* Send a message to the specified player, no matter what platform Floodgate is running on.
|
* Returns the authentication type used on the platform
|
||||||
*
|
|
||||||
* @param player the player to send the message to
|
|
||||||
* @param message the command message
|
|
||||||
* @param locale the locale of the player
|
|
||||||
* @param args the arguments
|
|
||||||
*/
|
*/
|
||||||
void sendMessage(Object player, String locale, TranslatableMessage message, Object... args);
|
public abstract AuthType authType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link CommandUtil#sendMessage(Object, String, TranslatableMessage, Object...)} except it
|
* Returns the Minecraft version the server is based on (or the most recent supported version
|
||||||
* kicks the player.
|
* for proxy platforms)
|
||||||
*
|
|
||||||
* @param player the player to send the message to
|
|
||||||
* @param message the command message
|
|
||||||
* @param locale the locale of the player
|
|
||||||
* @param args the arguments
|
|
||||||
*/
|
*/
|
||||||
void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args);
|
public abstract String minecraftVersion();
|
||||||
|
|
||||||
Collection<String> getOnlineUsernames(PlayerType limitTo);
|
public abstract String serverImplementationName();
|
||||||
|
|
||||||
enum PlayerType {
|
public enum AuthType {
|
||||||
ALL_PLAYERS,
|
ONLINE,
|
||||||
ONLY_BEDROCK,
|
PROXIED,
|
||||||
ONLY_JAVA
|
OFFLINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
public enum PlayerType {
|
||||||
|
ALL_PLAYERS,
|
||||||
|
ONLY_BEDROCK,
|
||||||
|
ONLY_JAVA
|
||||||
|
}
|
||||||
@@ -35,7 +35,6 @@ import io.netty.util.AttributeKey;
|
|||||||
import it.unimi.dsi.fastutil.Pair;
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair;
|
import it.unimi.dsi.fastutil.objects.ObjectObjectImmutablePair;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
@@ -96,7 +95,7 @@ public final class FloodgateHandshakeHandler {
|
|||||||
String floodgateData = null;
|
String floodgateData = null;
|
||||||
int dataVersion = -1;
|
int dataVersion = -1;
|
||||||
|
|
||||||
StringBuilder hostnameBuilder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
for (String value : hostnameItems) {
|
for (String value : hostnameItems) {
|
||||||
int version = FloodgateCipher.version(value);
|
int version = FloodgateCipher.version(value);
|
||||||
if (floodgateData == null && version != -1) {
|
if (floodgateData == null && version != -1) {
|
||||||
@@ -104,10 +103,14 @@ public final class FloodgateHandshakeHandler {
|
|||||||
dataVersion = version;
|
dataVersion = version;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
hostnameBuilder.append(value).append('\0');
|
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append('\0');
|
||||||
}
|
}
|
||||||
// hostname now doesn't have Floodgate data anymore if it had
|
builder.append(value);
|
||||||
return new HostnameSeparationResult(floodgateData, dataVersion, hostnameBuilder.toString());
|
}
|
||||||
|
// the new hostname doesn't have Floodgate data anymore, if it had Floodgate data.
|
||||||
|
return new HostnameSeparationResult(floodgateData, dataVersion, builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<HandshakeResult> handle(
|
public CompletableFuture<HandshakeResult> handle(
|
||||||
@@ -218,8 +221,6 @@ public final class FloodgateHandshakeHandler {
|
|||||||
bedrockData.getVerifyCode());
|
bedrockData.getVerifyCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
correctHostname(handshakeData);
|
|
||||||
|
|
||||||
FloodgatePlayer player = FloodgatePlayerImpl.from(bedrockData, handshakeData);
|
FloodgatePlayer player = FloodgatePlayerImpl.from(bedrockData, handshakeData);
|
||||||
|
|
||||||
api.addPlayer(player);
|
api.addPlayer(player);
|
||||||
@@ -247,30 +248,9 @@ public final class FloodgateHandshakeHandler {
|
|||||||
bedrockData, configHolder.get(), null, hostname);
|
bedrockData, configHolder.get(), null, hostname);
|
||||||
handshakeHandlers.callHandshakeHandlers(handshakeData);
|
handshakeHandlers.callHandshakeHandlers(handshakeData);
|
||||||
|
|
||||||
if (bedrockData != null) {
|
|
||||||
correctHostname(handshakeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HandshakeResult(resultType, handshakeData, bedrockData, null);
|
return new HandshakeResult(resultType, handshakeData, bedrockData, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void correctHostname(HandshakeData handshakeData) {
|
|
||||||
BedrockData bedrockData = handshakeData.getBedrockData();
|
|
||||||
UUID correctUuid = handshakeData.getCorrectUniqueId();
|
|
||||||
|
|
||||||
// replace the ip and uuid with the Bedrock client IP and an uuid based of the xuid
|
|
||||||
String[] split = handshakeData.getHostname().split("\0");
|
|
||||||
if (split.length >= 3) {
|
|
||||||
if (logger.isDebug()) {
|
|
||||||
logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'",
|
|
||||||
split[1], bedrockData.getIp(), split[2], correctUuid.toString());
|
|
||||||
}
|
|
||||||
split[1] = bedrockData.getIp();
|
|
||||||
split[2] = correctUuid.toString();
|
|
||||||
}
|
|
||||||
handshakeData.setHostname(String.join("\0", split));
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<Pair<BedrockData, LinkedPlayer>> fetchLinkedPlayer(BedrockData data) {
|
private CompletableFuture<Pair<BedrockData, LinkedPlayer>> fetchLinkedPlayer(BedrockData data) {
|
||||||
if (!api.getPlayerLink().isEnabled()) {
|
if (!api.getPlayerLink().isEnabled()) {
|
||||||
return CompletableFuture.completedFuture(new ObjectObjectImmutablePair<>(data, null));
|
return CompletableFuture.completedFuture(new ObjectObjectImmutablePair<>(data, null));
|
||||||
|
|||||||
@@ -25,52 +25,80 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.player;
|
package org.geysermc.floodgate.player;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import net.kyori.adventure.audience.Audience;
|
import lombok.Getter;
|
||||||
import net.kyori.adventure.audience.MessageType;
|
import lombok.experimental.Accessors;
|
||||||
import net.kyori.adventure.identity.Identified;
|
|
||||||
import net.kyori.adventure.identity.Identity;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
||||||
|
|
||||||
public interface UserAudience extends Identified, Identity, Audience {
|
@Getter @Accessors(fluent = true)
|
||||||
@Override
|
public class UserAudience {
|
||||||
@NonNull UUID uuid();
|
private final @NonNull UUID uuid;
|
||||||
|
private final @NonNull String username;
|
||||||
|
private final @NonNull String locale;
|
||||||
|
private final @NonNull Object source;
|
||||||
|
private final @NonNull CommandUtil commandUtil;
|
||||||
|
|
||||||
@NonNull String username();
|
public UserAudience(
|
||||||
|
@NonNull UUID uuid,
|
||||||
@NonNull String locale();
|
@NonNull String username,
|
||||||
|
@NonNull String locale,
|
||||||
@NonNull Object source();
|
@NonNull Object source,
|
||||||
|
@NonNull CommandUtil commandUtil) {
|
||||||
boolean hasPermission(@NonNull final String permission);
|
this.uuid = Objects.requireNonNull(uuid);
|
||||||
|
this.username = username;
|
||||||
@Override
|
this.locale = Objects.requireNonNull(locale);
|
||||||
void sendMessage(final @NonNull Identity source,
|
this.source = Objects.requireNonNull(source);
|
||||||
final @NonNull Component message,
|
this.commandUtil = Objects.requireNonNull(commandUtil);
|
||||||
final @NonNull MessageType type);
|
|
||||||
|
|
||||||
void sendMessage(TranslatableMessage message, Object... args);
|
|
||||||
|
|
||||||
default void sendMessage(String message) {
|
|
||||||
sendMessage(Component.text(message));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(@NonNull final Component reason);
|
public boolean hasPermission(@NonNull String permission) {
|
||||||
|
return commandUtil.hasPermission(source(), permission);
|
||||||
void disconnect(TranslatableMessage message, Object... args);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default @NonNull Identity identity() {
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlayerAudience extends UserAudience {
|
public void sendMessage(String message) {
|
||||||
boolean online();
|
commandUtil.sendMessage(source(), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConsoleAudience extends UserAudience {
|
public void sendMessage(TranslatableMessage message, Object... args) {
|
||||||
|
sendMessage(translateMessage(message, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect(@NonNull String reason) {
|
||||||
|
commandUtil.kickPlayer(source(), reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect(TranslatableMessage message, Object... args) {
|
||||||
|
disconnect(translateMessage(message, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translateMessage(TranslatableMessage message, Object... args) {
|
||||||
|
return commandUtil.translateMessage(locale(), message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter @Accessors(fluent = true)
|
||||||
|
public static class PlayerAudience extends UserAudience {
|
||||||
|
private final boolean online;
|
||||||
|
|
||||||
|
public PlayerAudience(
|
||||||
|
@NonNull UUID uuid,
|
||||||
|
@NonNull String username,
|
||||||
|
@NonNull String locale,
|
||||||
|
@NonNull Object source,
|
||||||
|
@NonNull CommandUtil commandUtil,
|
||||||
|
boolean online) {
|
||||||
|
super(uuid, username, locale, source, commandUtil);
|
||||||
|
|
||||||
|
this.online = online;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter @Accessors(fluent = true)
|
||||||
|
public static class ConsoleAudience extends UserAudience {
|
||||||
|
public ConsoleAudience(@NonNull Object source, @NonNull CommandUtil commandUtil) {
|
||||||
|
super(new UUID(0, 0), "CONSOLE", "en_us", source, commandUtil);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,23 +23,20 @@
|
|||||||
* @link https://github.com/GeyserMC/Floodgate
|
* @link https://github.com/GeyserMC/Floodgate
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.player.audience;
|
||||||
|
|
||||||
public enum Permissions {
|
import java.util.UUID;
|
||||||
COMMAND_MAIN("floodgate.command.floodgate"),
|
import lombok.Getter;
|
||||||
COMMAND_LINK("floodgate.command.linkaccount"),
|
import lombok.experimental.Accessors;
|
||||||
COMMAND_UNLINK("floodgate.command.unlinkaccount"),
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
COMMAND_WHITELIST("floodgate.command.fwhitelist"),
|
|
||||||
|
|
||||||
NEWS_RECEIVE("floodgate.news.receive");
|
@Getter @Accessors(fluent = true)
|
||||||
|
public final class ProfileAudience {
|
||||||
|
private final @Nullable UUID uuid;
|
||||||
|
private final @Nullable String username;
|
||||||
|
|
||||||
private final String permission;
|
public ProfileAudience(@Nullable UUID uuid, @Nullable String username) {
|
||||||
|
this.uuid = uuid;
|
||||||
Permissions(String permission) {
|
this.username = username;
|
||||||
this.permission = permission;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String get() {
|
|
||||||
return permission;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
* @link https://github.com/GeyserMC/Floodgate
|
* @link https://github.com/GeyserMC/Floodgate
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.floodgate.player;
|
package org.geysermc.floodgate.player.audience;
|
||||||
|
|
||||||
import cloud.commandframework.arguments.CommandArgument;
|
import cloud.commandframework.arguments.CommandArgument;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||||
@@ -37,62 +37,58 @@ import java.util.UUID;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlayerType;
|
||||||
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
|
|
||||||
public final class UserAudienceArgument extends CommandArgument<UserAudience, UserAudience> {
|
public class ProfileAudienceArgument extends CommandArgument<UserAudience, ProfileAudience> {
|
||||||
private UserAudienceArgument(final @NonNull String name, final UserAudienceParser parser) {
|
private ProfileAudienceArgument(@NonNull String name, ProfileAudienceParser parser) {
|
||||||
super(true, name, parser, UserAudience.class);
|
super(true, name, parser, ProfileAudience.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAudienceArgument of(
|
public static ProfileAudienceArgument of(
|
||||||
final String name,
|
String name,
|
||||||
final boolean allowUuid,
|
boolean allowUuid,
|
||||||
final boolean allowOffline,
|
boolean allowOffline,
|
||||||
final PlayerType limitTo) {
|
PlayerType limitTo) {
|
||||||
return new UserAudienceArgument(name,
|
return new ProfileAudienceArgument(name,
|
||||||
new UserAudienceParser(allowUuid, allowOffline, limitTo));
|
new ProfileAudienceParser(allowUuid, allowOffline, limitTo));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAudienceArgument of(
|
public static ProfileAudienceArgument of(
|
||||||
final String name,
|
String name,
|
||||||
final boolean allowOffline,
|
boolean allowOffline,
|
||||||
final PlayerType limitTo) {
|
PlayerType limitTo) {
|
||||||
return of(name, false, allowOffline, limitTo);
|
return of(name, false, allowOffline, limitTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAudienceArgument ofOnline(final String name, final PlayerType limitTo) {
|
public static ProfileAudienceArgument ofOnline(String name, PlayerType limitTo) {
|
||||||
return of(name, false, false, limitTo);
|
return of(name, false, false, limitTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAudienceArgument ofOnline(final String name, final boolean allowUuid) {
|
public static ProfileAudienceArgument ofOnline(String name, boolean allowUuid) {
|
||||||
return of(name, allowUuid, false, PlayerType.ALL_PLAYERS);
|
return of(name, allowUuid, false, PlayerType.ALL_PLAYERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CommandArgument<UserAudience, UserAudience> ofOnline(final String name) {
|
public static CommandArgument<UserAudience, ProfileAudience> ofOnline(String name) {
|
||||||
return of(name, false, false, PlayerType.ALL_PLAYERS);
|
return of(name, false, false, PlayerType.ALL_PLAYERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserAudienceArgument of(final String name, final boolean allowOffline) {
|
public static ProfileAudienceArgument of(String name, boolean allowOffline) {
|
||||||
return of(name, false, allowOffline, PlayerType.ALL_PLAYERS);
|
return of(name, false, allowOffline, PlayerType.ALL_PLAYERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PlayerType {
|
|
||||||
ALL_PLAYERS,
|
|
||||||
ONLY_BEDROCK,
|
|
||||||
ONLY_JAVA
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public static final class UserAudienceParser
|
public static final class ProfileAudienceParser
|
||||||
implements ArgumentParser<UserAudience, UserAudience> {
|
implements ArgumentParser<UserAudience, ProfileAudience> {
|
||||||
|
|
||||||
private final boolean allowUuid;
|
private final boolean allowUuid;
|
||||||
private final boolean allowOffline;
|
private final boolean allowOffline;
|
||||||
private final PlayerType limitTo;
|
private final PlayerType limitTo;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull ArgumentParseResult<UserAudience> parse(
|
public @NonNull ArgumentParseResult<ProfileAudience> parse(
|
||||||
final @NonNull CommandContext<@NonNull UserAudience> commandContext,
|
@NonNull CommandContext<@NonNull UserAudience> commandContext,
|
||||||
final @NonNull Queue<@NonNull String> inputQueue) {
|
@NonNull Queue<@NonNull String> inputQueue) {
|
||||||
CommandUtil commandUtil = commandContext.get("CommandUtil");
|
CommandUtil commandUtil = commandContext.get("CommandUtil");
|
||||||
|
|
||||||
String input = inputQueue.poll();
|
String input = inputQueue.poll();
|
||||||
@@ -111,7 +107,7 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
|
|||||||
StringBuilder builder = new StringBuilder(input);
|
StringBuilder builder = new StringBuilder(input);
|
||||||
while (!inputQueue.isEmpty()) {
|
while (!inputQueue.isEmpty()) {
|
||||||
String string = inputQueue.remove();
|
String string = inputQueue.remove();
|
||||||
builder.append(" ").append(string);
|
builder.append(' ').append(string);
|
||||||
if (string.endsWith("\"")) {
|
if (string.endsWith("\"")) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -129,7 +125,7 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAudience userAudience;
|
ProfileAudience profileAudience;
|
||||||
|
|
||||||
if (input.length() > 16) {
|
if (input.length() > 16) {
|
||||||
// This must be a UUID.
|
// This must be a UUID.
|
||||||
@@ -146,46 +142,39 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// We only want to make sure the UUID is valid here.
|
// We only want to make sure the UUID is valid here.
|
||||||
final UUID uuid = UUID.fromString(input);
|
Object player = commandUtil.getPlayerByUuid(UUID.fromString(input), limitTo);
|
||||||
userAudience = commandUtil.getAudienceByUuid(uuid);
|
profileAudience = commandUtil.getProfileAudience(player, allowOffline);
|
||||||
|
|
||||||
if (userAudience == null && allowOffline) {
|
|
||||||
userAudience = commandUtil.getOfflineAudienceByUuid(uuid);
|
|
||||||
}
|
|
||||||
} catch (final IllegalArgumentException ignored) {
|
} catch (final IllegalArgumentException ignored) {
|
||||||
return ArgumentParseResult.failure(
|
return ArgumentParseResult.failure(
|
||||||
new InvalidPlayerIdentifierException("Invalid UUID '" + input + "'"));
|
new InvalidPlayerIdentifierException("Invalid UUID '" + input + "'"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is a username.
|
// This is a username.
|
||||||
userAudience = commandUtil.getAudienceByUsername(input);
|
Object player = commandUtil.getPlayerByUsername(input, limitTo);
|
||||||
|
profileAudience = commandUtil.getProfileAudience(player, allowOffline);
|
||||||
if (userAudience == null && allowOffline) {
|
|
||||||
userAudience = commandUtil.getOfflineAudienceByUsername(input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userAudience == null) {
|
if (profileAudience == null) {
|
||||||
return ArgumentParseResult.failure(
|
return ArgumentParseResult.failure(
|
||||||
new InvalidPlayerIdentifierException("Invalid player '" + input + "'"));
|
new InvalidPlayerIdentifierException("Invalid player '" + input + "'"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ArgumentParseResult.success(userAudience);
|
return ArgumentParseResult.success(profileAudience);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull List<String> suggestions(
|
public @NonNull List<String> suggestions(
|
||||||
final @NonNull CommandContext<UserAudience> commandContext,
|
@NonNull CommandContext<UserAudience> commandContext,
|
||||||
final @NonNull String input) {
|
@NonNull String input) {
|
||||||
final CommandUtil commandUtil = commandContext.get("CommandUtil");
|
CommandUtil commandUtil = commandContext.get("CommandUtil");
|
||||||
final String trimmedInput = input.trim();
|
String trimmedInput = input.trim();
|
||||||
|
|
||||||
if (trimmedInput.isEmpty()) {
|
if (trimmedInput.isEmpty()) {
|
||||||
return ImmutableList.copyOf(commandUtil.getOnlineUsernames(limitTo));
|
return ImmutableList.copyOf(commandUtil.getOnlineUsernames(limitTo));
|
||||||
}
|
}
|
||||||
|
|
||||||
final String lowercaseInput = input.toLowerCase(Locale.ROOT);
|
String lowercaseInput = input.toLowerCase(Locale.ROOT);
|
||||||
final ImmutableList.Builder<String> builder = ImmutableList.builder();
|
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||||
|
|
||||||
for (final String player : commandUtil.getOnlineUsernames(limitTo)) {
|
for (final String player : commandUtil.getOnlineUsernames(limitTo)) {
|
||||||
if (player.toLowerCase(Locale.ROOT).startsWith(lowercaseInput)) {
|
if (player.toLowerCase(Locale.ROOT).startsWith(lowercaseInput)) {
|
||||||
@@ -205,7 +194,7 @@ public final class UserAudienceArgument extends CommandArgument<UserAudience, Us
|
|||||||
public static final class InvalidPlayerIdentifierException extends IllegalArgumentException {
|
public static final class InvalidPlayerIdentifierException extends IllegalArgumentException {
|
||||||
private static final long serialVersionUID = -6500019324607183855L;
|
private static final long serialVersionUID = -6500019324607183855L;
|
||||||
|
|
||||||
public InvalidPlayerIdentifierException(final @NonNull String message) {
|
public InvalidPlayerIdentifierException(@NonNull String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,8 +26,10 @@
|
|||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
public final class Constants {
|
public final class Constants {
|
||||||
|
public static final String VERSION = "${floodgateVersion}";
|
||||||
public static final int BUILD_NUMBER = Integer.parseInt("${buildNumber}");
|
public static final int BUILD_NUMBER = Integer.parseInt("${buildNumber}");
|
||||||
public static final String GIT_BRANCH = "${branch}";
|
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 = '§';
|
||||||
|
|
||||||
|
|||||||
147
core/src/main/java/org/geysermc/floodgate/util/Metrics.java
Normal file
147
core/src/main/java/org/geysermc/floodgate/util/Metrics.java
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.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.platform.util.PlatformUtils;
|
||||||
|
|
||||||
|
public final class Metrics {
|
||||||
|
private final MetricsBase metricsBase;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Metrics(FloodgateConfig config, PlatformUtils platformUtils, FloodgateApi api,
|
||||||
|
@Named("implementationName") String implementationName, FloodgateLogger logger) {
|
||||||
|
|
||||||
|
MetricsConfig metricsConfig = config.getMetrics();
|
||||||
|
|
||||||
|
metricsBase = new MetricsBase(
|
||||||
|
"server-implementation",
|
||||||
|
metricsConfig.getUuid(),
|
||||||
|
Constants.METRICS_ID,
|
||||||
|
metricsConfig.isEnabled(),
|
||||||
|
this::appendPlatformData,
|
||||||
|
jsonObjectBuilder -> { /* NOP */ },
|
||||||
|
null,
|
||||||
|
() -> true, // remove this if/when we add some form of reload support
|
||||||
|
logger::error,
|
||||||
|
logger::info,
|
||||||
|
Constants.DEBUG_MODE,
|
||||||
|
Constants.DEBUG_MODE,
|
||||||
|
Constants.DEBUG_MODE
|
||||||
|
);
|
||||||
|
|
||||||
|
metricsBase.addCustomChart(
|
||||||
|
new SingleLineChart("players", api::getPlayerCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
metricsBase.addCustomChart(
|
||||||
|
new DrilldownPie("player_count", () -> {
|
||||||
|
int playerCount = api.getPlayerCount();
|
||||||
|
// 0 = 0 - 4, 9 = 5 - 9, etc.
|
||||||
|
int category = playerCount / 5 * 5;
|
||||||
|
String categoryName = category + " - " + (category + 4);
|
||||||
|
|
||||||
|
return Collections.singletonMap(
|
||||||
|
implementationName,
|
||||||
|
Collections.singletonMap(categoryName, 1)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
metricsBase.addCustomChart(
|
||||||
|
new SimplePie("authentication",
|
||||||
|
() -> platformUtils.authType().name().toLowerCase(Locale.ROOT))
|
||||||
|
);
|
||||||
|
|
||||||
|
metricsBase.addCustomChart(
|
||||||
|
new SimplePie("floodgate_version", () -> Constants.VERSION)
|
||||||
|
);
|
||||||
|
|
||||||
|
metricsBase.addCustomChart(
|
||||||
|
new DrilldownPie("platform", () -> Collections.singletonMap(
|
||||||
|
implementationName,
|
||||||
|
Collections.singletonMap(platformUtils.serverImplementationName(), 1)
|
||||||
|
)));
|
||||||
|
|
||||||
|
metricsBase.addCustomChart(
|
||||||
|
new DrilldownPie("minecraft_version", () -> {
|
||||||
|
// e.g.: 1.16.5 => (Spigot, 1)
|
||||||
|
return Collections.singletonMap(
|
||||||
|
implementationName,
|
||||||
|
Collections.singletonMap(platformUtils.minecraftVersion(), 1)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Source: Geyser
|
||||||
|
metricsBase.addCustomChart(new DrilldownPie("java_version", () -> {
|
||||||
|
Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||||
|
String javaVersion = System.getProperty("java.version");
|
||||||
|
Map<String, Integer> entry = new HashMap<>();
|
||||||
|
entry.put(javaVersion, 1);
|
||||||
|
|
||||||
|
String majorVersion = javaVersion.split("\\.")[0];
|
||||||
|
String release;
|
||||||
|
|
||||||
|
int indexOf = javaVersion.lastIndexOf('.');
|
||||||
|
|
||||||
|
if (majorVersion.equals("1")) {
|
||||||
|
release = "Java " + javaVersion.substring(0, indexOf);
|
||||||
|
} else {
|
||||||
|
Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion);
|
||||||
|
if (versionMatcher.find()) {
|
||||||
|
majorVersion = versionMatcher.group(0);
|
||||||
|
}
|
||||||
|
release = "Java " + majorVersion;
|
||||||
|
}
|
||||||
|
map.put(release, entry);
|
||||||
|
return map;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||||
|
builder.appendField("osName", System.getProperty("os.name"));
|
||||||
|
builder.appendField("osArch", System.getProperty("os.arch"));
|
||||||
|
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||||
|
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -269,6 +269,11 @@ public final class ReflectionUtils {
|
|||||||
return (T) getValue(instance, getField(instance.getClass(), fieldName));
|
return (T) getValue(instance, getField(instance.getClass(), fieldName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static <T> T castedStaticValue(Field field) {
|
||||||
|
return getCastedValue(null, field);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the value of a field. This method make the field accessible and then sets the value.<br>
|
* Set the value of a field. This method make the field accessible and then sets the value.<br>
|
||||||
* This method doesn't throw an exception when failed, but it'll log the error to the console.
|
* This method doesn't throw an exception when failed, but it'll log the error to the console.
|
||||||
|
|||||||
@@ -58,5 +58,9 @@ player-link:
|
|||||||
# you have limited internet access.
|
# you have limited internet access.
|
||||||
enable-global-linking: true
|
enable-global-linking: true
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
enabled: true
|
||||||
|
uuid: ${metrics.uuid}
|
||||||
|
|
||||||
# Do not change this
|
# Do not change this
|
||||||
config-version: 2
|
config-version: 3
|
||||||
|
|||||||
Submodule core/src/main/resources/languages updated: d08abbfab6...38cb4a52df
11
database/mongo/build.gradle.kts
Normal file
11
database/mongo/build.gradle.kts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
val mongoClientVersion = "4.4.1"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
provided(projects.core)
|
||||||
|
implementation("org.mongodb", "mongodb-driver-sync" , mongoClientVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
description = "The Floodgate database extension for MongoDB"
|
||||||
|
|
||||||
|
relocate("com.mongodb")
|
||||||
|
relocate("org.bson")
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
/*
|
||||||
|
* 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.database;
|
||||||
|
|
||||||
|
import com.mongodb.ConnectionString;
|
||||||
|
import com.mongodb.MongoClientSettings;
|
||||||
|
import com.mongodb.MongoCredential;
|
||||||
|
import com.mongodb.client.MongoClient;
|
||||||
|
import com.mongodb.client.MongoClients;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoCursor;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import com.mongodb.client.model.Filters;
|
||||||
|
import com.mongodb.client.model.IndexOptions;
|
||||||
|
import com.mongodb.client.model.Indexes;
|
||||||
|
import com.mongodb.client.model.UpdateOptions;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.bson.conversions.Bson;
|
||||||
|
import org.bson.internal.Base64;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.floodgate.api.link.LinkRequest;
|
||||||
|
import org.geysermc.floodgate.api.link.LinkRequestResult;
|
||||||
|
import org.geysermc.floodgate.database.config.MongoConfig;
|
||||||
|
import org.geysermc.floodgate.link.CommonPlayerLink;
|
||||||
|
import org.geysermc.floodgate.link.LinkRequestImpl;
|
||||||
|
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||||
|
|
||||||
|
public class MongoDbDatabase extends CommonPlayerLink {
|
||||||
|
private MongoClient client;
|
||||||
|
private MongoDatabase database;
|
||||||
|
private MongoCollection<Document> linkedPlayer;
|
||||||
|
private MongoCollection<Document> linkedPlayerRequests;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load() {
|
||||||
|
getLogger().info("Connecting to MongoDB database...");
|
||||||
|
try {
|
||||||
|
MongoConfig databaseConfig = getConfig(MongoConfig.class);
|
||||||
|
|
||||||
|
MongoClientSettings.Builder settings = MongoClientSettings.builder();
|
||||||
|
settings.applyToConnectionPoolSettings(builder -> {
|
||||||
|
builder.maxSize(10);
|
||||||
|
builder.minSize(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (databaseConfig.getMongouri().isEmpty()) {
|
||||||
|
settings.credential(
|
||||||
|
MongoCredential.createCredential(
|
||||||
|
databaseConfig.getUsername(),
|
||||||
|
databaseConfig.getDatabase(),
|
||||||
|
databaseConfig.getPassword().toCharArray()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
settings.applyConnectionString(new ConnectionString(databaseConfig.getMongouri()));
|
||||||
|
}
|
||||||
|
|
||||||
|
client = MongoClients.create(settings.build());
|
||||||
|
|
||||||
|
database = client.getDatabase(databaseConfig.getDatabase());
|
||||||
|
|
||||||
|
linkedPlayer = database.getCollection("LinkedPlayers");
|
||||||
|
if (collectionNotExists("LinkedPlayers")) {
|
||||||
|
database.createCollection("LinkedPlayers");
|
||||||
|
|
||||||
|
linkedPlayer.createIndex(new Document("bedrockId", 1),
|
||||||
|
new IndexOptions().unique(true)); // primary key equivalent
|
||||||
|
linkedPlayer.createIndex(Indexes.ascending("javaUniqueId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
linkedPlayerRequests = database.getCollection("LinkedPlayerRequests");
|
||||||
|
if (collectionNotExists("LinkedPlayerRequests")) {
|
||||||
|
database.createCollection("LinkedPlayerRequests");
|
||||||
|
|
||||||
|
linkedPlayerRequests.createIndex(new Document("bedrockId", 1),
|
||||||
|
new IndexOptions().unique(true)); // primary key equivalent
|
||||||
|
linkedPlayerRequests.createIndex(Indexes.ascending("requestTime"));
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogger().info("Connected to MongoDB database.");
|
||||||
|
} catch (Exception exception) {
|
||||||
|
getLogger().error("Error while loading database", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
super.stop();
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public CompletableFuture<LinkedPlayer> getLinkedPlayer(@NonNull UUID bedrockId) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
Bson filter = Filters.eq("bedrockId", uuidToBytes(bedrockId));
|
||||||
|
|
||||||
|
try (MongoCursor<Document> cursor = linkedPlayer.find(filter).cursor()) {
|
||||||
|
if (cursor.hasNext()) {
|
||||||
|
Document document = cursor.next();
|
||||||
|
String javaUsername = document.getString("javaUsername");
|
||||||
|
UUID javaUniqueId = bytesToUUID(document.getString("javaUniqueId"));
|
||||||
|
|
||||||
|
return LinkedPlayer.of(javaUsername, javaUniqueId, bedrockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
getLogger().error("Error while getting LinkedPlayer", exception);
|
||||||
|
throw new CompletionException("Error while getting LinkedPlayer", exception);
|
||||||
|
}
|
||||||
|
}, getExecutorService());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public CompletableFuture<Boolean> isLinkedPlayer(@NonNull UUID playerId) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
String uuidBytes = uuidToBytes(playerId);
|
||||||
|
Bson filter = Filters.or(
|
||||||
|
Filters.eq("bedrockId", uuidBytes),
|
||||||
|
Filters.eq("javaUniqueId", uuidBytes)
|
||||||
|
);
|
||||||
|
try (MongoCursor<Document> cursor = linkedPlayer.find(filter).cursor()) {
|
||||||
|
return cursor.hasNext();
|
||||||
|
}
|
||||||
|
} catch (Exception 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 {
|
||||||
|
Bson filter = Filters.eq("javaUsername", javaUsername);
|
||||||
|
Document create = new Document("bedrockId", uuidToBytes(bedrockId))
|
||||||
|
.append("javaUniqueId", uuidToBytes(javaId))
|
||||||
|
.append("javaUsername", javaUsername);
|
||||||
|
Document update = new Document("$set", create);
|
||||||
|
|
||||||
|
linkedPlayer.updateOne(filter, update, new UpdateOptions().upsert(true));
|
||||||
|
// The upsert option will create a new document if the filter doesn't match anything.
|
||||||
|
// Or will update the document if it does match.
|
||||||
|
} catch (Exception 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 {
|
||||||
|
String uuidBytes = uuidToBytes(javaId);
|
||||||
|
|
||||||
|
Bson filter = Filters.and(
|
||||||
|
Filters.eq("javaUniqueId", uuidBytes),
|
||||||
|
Filters.eq("bedrockId", uuidBytes)
|
||||||
|
);
|
||||||
|
|
||||||
|
linkedPlayer.deleteMany(filter);
|
||||||
|
} catch (Exception 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 {
|
||||||
|
Bson filter = Filters.eq("javaUsername", javaUsername);
|
||||||
|
Document create = new Document("javaUsername", javaUsername)
|
||||||
|
.append("javaUniqueId", uuidToBytes(javaId))
|
||||||
|
.append("linkCode", linkCode)
|
||||||
|
.append("bedrockUsername", bedrockUsername)
|
||||||
|
.append("requestTime", Instant.now().getEpochSecond());
|
||||||
|
Document update = new Document("$set", create);
|
||||||
|
|
||||||
|
linkedPlayerRequests.updateOne(filter, update, new UpdateOptions().upsert(true));
|
||||||
|
// The upsert option will create a new document if the filter doesn't match anything.
|
||||||
|
// Or will update the document if it does match.
|
||||||
|
} catch (Exception exception) {
|
||||||
|
getLogger().error("Error while linking player", exception);
|
||||||
|
throw new CompletionException("Error while linking player", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeLinkRequest(String javaUsername) {
|
||||||
|
try {
|
||||||
|
Document filter = new Document("javaUsername", javaUsername);
|
||||||
|
linkedPlayerRequests.deleteMany(filter);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
getLogger().error("Error while cleaning up LinkRequest", 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 LinkRequest getLinkRequest0(String javaUsername) {
|
||||||
|
try {
|
||||||
|
Bson filter = Filters.eq("javaUsername", javaUsername);
|
||||||
|
try (MongoCursor<Document> cursor = linkedPlayerRequests.find(filter).cursor()) {
|
||||||
|
if (cursor.hasNext()) {
|
||||||
|
Document document = cursor.next();
|
||||||
|
UUID javaId = bytesToUUID(document.getString("javaUniqueId"));
|
||||||
|
String linkCode = document.getString("linkCode");
|
||||||
|
String bedrockUsername = document.getString("bedrockUsername");
|
||||||
|
long requestTime = document.getLong("requestTime");
|
||||||
|
return new LinkRequestImpl(javaUsername, javaId, linkCode, bedrockUsername,
|
||||||
|
requestTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
getLogger().error("Error while getLinkRequest", exception);
|
||||||
|
throw new CompletionException("Error while getLinkRequest", exception);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanLinkRequests() {
|
||||||
|
try {
|
||||||
|
Document filter = new Document("requestTime",
|
||||||
|
new Document("$lt", Instant.now().getEpochSecond() - getVerifyLinkTimeout()));
|
||||||
|
linkedPlayerRequests.deleteMany(filter);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
getLogger().error("Error while cleaning up link requests", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String uuidToBytes(UUID uuid) {
|
||||||
|
byte[] uuidBytes = new byte[16];
|
||||||
|
ByteBuffer.wrap(uuidBytes)
|
||||||
|
.order(ByteOrder.BIG_ENDIAN)
|
||||||
|
.putLong(uuid.getMostSignificantBits())
|
||||||
|
.putLong(uuid.getLeastSignificantBits());
|
||||||
|
return Base64.encode(uuidBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UUID bytesToUUID(String uuidBytes) {
|
||||||
|
ByteBuffer buf = ByteBuffer.wrap(Base64.decode(uuidBytes));
|
||||||
|
return new UUID(buf.getLong(), buf.getLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean collectionNotExists(final String collectionName) {
|
||||||
|
return !database.listCollectionNames().into(new ArrayList<>()).contains(collectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.database.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class MongoConfig implements DatabaseConfig {
|
||||||
|
private String hostname = "localhost";
|
||||||
|
private String database = "floodgate";
|
||||||
|
private String username = "floodgate";
|
||||||
|
private String password;
|
||||||
|
private String mongouri = "";
|
||||||
|
}
|
||||||
4
database/mongo/src/main/resources/init.json
Normal file
4
database/mongo/src/main/resources/init.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"mainClass": "org.geysermc.floodgate.database.MongoDbDatabase",
|
||||||
|
"config": "mongo.yml"
|
||||||
|
}
|
||||||
5
database/mongo/src/main/resources/mongo.yml
Normal file
5
database/mongo/src/main/resources/mongo.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
hostname: "localhost"
|
||||||
|
database: "floodgate"
|
||||||
|
username: "floodgate"
|
||||||
|
password: ""
|
||||||
|
mongouri: ""
|
||||||
@@ -10,10 +10,6 @@
|
|||||||
|
|
||||||
<!-- UnusedPrivateMethod -->
|
<!-- UnusedPrivateMethod -->
|
||||||
<exclude-pattern>.*/CommonPlayerLink.*</exclude-pattern>
|
<exclude-pattern>.*/CommonPlayerLink.*</exclude-pattern>
|
||||||
<!-- NullAssignment -->
|
|
||||||
<exclude-pattern>.*/DefaultConfigHandler.*</exclude-pattern>
|
|
||||||
<!-- NullAssignment -->
|
|
||||||
<exclude-pattern>.*/ConfigFileUpdater.*</exclude-pattern>
|
|
||||||
<!-- RedundantFieldInitializer -->
|
<!-- RedundantFieldInitializer -->
|
||||||
<exclude-pattern>.*/FloodgateConfig.*</exclude-pattern>
|
<exclude-pattern>.*/FloodgateConfig.*</exclude-pattern>
|
||||||
<!-- CloseResource, there is no shutdown event and it has to load classes on the fly -->
|
<!-- CloseResource, there is no shutdown event and it has to load classes on the fly -->
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
|||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
|
// mavenLocal()
|
||||||
|
|
||||||
// Geyser, Cumulus etc.
|
// Geyser, Cumulus etc.
|
||||||
maven("https://repo.opencollab.dev/maven-releases") {
|
maven("https://repo.opencollab.dev/maven-releases") {
|
||||||
mavenContent { releasesOnly() }
|
mavenContent { releasesOnly() }
|
||||||
@@ -42,8 +44,6 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
plugins {
|
plugins {
|
||||||
id("net.kyori.blossom") version "1.2.0"
|
id("net.kyori.blossom") version "1.2.0"
|
||||||
id("net.kyori.indra")
|
|
||||||
id("net.kyori.indra.git")
|
|
||||||
}
|
}
|
||||||
includeBuild("build-logic")
|
includeBuild("build-logic")
|
||||||
}
|
}
|
||||||
@@ -57,5 +57,7 @@ include(":spigot")
|
|||||||
include(":velocity")
|
include(":velocity")
|
||||||
include(":sqlite")
|
include(":sqlite")
|
||||||
include(":mysql")
|
include(":mysql")
|
||||||
|
include(":mongo")
|
||||||
project(":sqlite").projectDir = file("database/sqlite")
|
project(":sqlite").projectDir = file("database/sqlite")
|
||||||
project(":mysql").projectDir = file("database/mysql")
|
project(":mysql").projectDir = file("database/mysql")
|
||||||
|
project(":mongo").projectDir = file("database/mongo")
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ var gsonVersion = "2.8.5"
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
|
||||||
|
implementation("cloud.commandframework", "cloud-bukkit", Versions.cloudVersion)
|
||||||
// hack to make pre 1.12 work
|
// hack to make pre 1.12 work
|
||||||
implementation("com.google.guava", "guava", guavaVersion)
|
implementation("com.google.guava", "guava", guavaVersion)
|
||||||
|
|
||||||
implementation("cloud.commandframework", "cloud-bukkit", Versions.cloudVersion)
|
|
||||||
implementation("net.kyori", "adventure-text-serializer-legacy", Versions.adventureApiVersion)
|
|
||||||
implementation("net.kyori", "adventure-text-serializer-gson", Versions.adventureApiVersion)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
relocate("com.google.inject")
|
relocate("com.google.inject")
|
||||||
@@ -24,7 +21,7 @@ relocate("com.google.guava")
|
|||||||
relocate("it.unimi")
|
relocate("it.unimi")
|
||||||
|
|
||||||
// these dependencies are already present on the platform
|
// these dependencies are already present on the platform
|
||||||
provided("org.spigotmc", "spigot-api", Versions.spigotVersion)
|
provided("com.destroystokyo.paper", "paper-api", Versions.spigotVersion)
|
||||||
provided("com.mojang", "authlib", authlibVersion)
|
provided("com.mojang", "authlib", authlibVersion)
|
||||||
provided("io.netty", "netty-transport", Versions.nettyVersion)
|
provided("io.netty", "netty-transport", Versions.nettyVersion)
|
||||||
provided("io.netty", "netty-codec", Versions.nettyVersion)
|
provided("io.netty", "netty-codec", Versions.nettyVersion)
|
||||||
|
|||||||
@@ -28,13 +28,17 @@ package org.geysermc.floodgate;
|
|||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
|
import org.geysermc.floodgate.module.PaperListenerModule;
|
||||||
import org.geysermc.floodgate.module.PluginMessageModule;
|
import org.geysermc.floodgate.module.PluginMessageModule;
|
||||||
import org.geysermc.floodgate.module.ServerCommonModule;
|
import org.geysermc.floodgate.module.ServerCommonModule;
|
||||||
import org.geysermc.floodgate.module.SpigotAddonModule;
|
import org.geysermc.floodgate.module.SpigotAddonModule;
|
||||||
import org.geysermc.floodgate.module.SpigotCommandModule;
|
import org.geysermc.floodgate.module.SpigotCommandModule;
|
||||||
import org.geysermc.floodgate.module.SpigotListenerModule;
|
import org.geysermc.floodgate.module.SpigotListenerModule;
|
||||||
import org.geysermc.floodgate.module.SpigotPlatformModule;
|
import org.geysermc.floodgate.module.SpigotPlatformModule;
|
||||||
|
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||||
|
import org.geysermc.floodgate.util.SpigotHandshakeHandler;
|
||||||
import org.geysermc.floodgate.util.SpigotProtocolSupportHandler;
|
import org.geysermc.floodgate.util.SpigotProtocolSupportHandler;
|
||||||
import org.geysermc.floodgate.util.SpigotProtocolSupportListener;
|
import org.geysermc.floodgate.util.SpigotProtocolSupportListener;
|
||||||
|
|
||||||
@@ -59,13 +63,20 @@ public final class SpigotPlugin extends JavaPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
boolean usePaperListener = ReflectionUtils.getClassSilently(
|
||||||
|
"com.destroystokyo.paper.event.profile.PreFillProfileEvent") != null;
|
||||||
|
|
||||||
platform.enable(
|
platform.enable(
|
||||||
new SpigotCommandModule(this),
|
new SpigotCommandModule(this),
|
||||||
new SpigotListenerModule(),
|
|
||||||
new SpigotAddonModule(),
|
new SpigotAddonModule(),
|
||||||
new PluginMessageModule()
|
new PluginMessageModule(),
|
||||||
|
(usePaperListener ? new PaperListenerModule() : new SpigotListenerModule())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//todo add proper support for disabling things on shutdown and enabling this on enable
|
||||||
|
injector.getInstance(HandshakeHandlers.class)
|
||||||
|
.addHandshakeHandler(injector.getInstance(SpigotHandshakeHandler.class));
|
||||||
|
|
||||||
// add ProtocolSupport support (hack)
|
// add ProtocolSupport support (hack)
|
||||||
if (getServer().getPluginManager().getPlugin("ProtocolSupport") != null) {
|
if (getServer().getPluginManager().getPlugin("ProtocolSupport") != null) {
|
||||||
injector.getInstance(SpigotProtocolSupportHandler.class);
|
injector.getInstance(SpigotProtocolSupportHandler.class);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import org.geysermc.floodgate.util.ProxyUtils;
|
|||||||
public final class SpigotDataHandler extends CommonDataHandler {
|
public final class SpigotDataHandler extends CommonDataHandler {
|
||||||
private Object networkManager;
|
private Object networkManager;
|
||||||
private FloodgatePlayer player;
|
private FloodgatePlayer player;
|
||||||
|
private boolean proxyData;
|
||||||
|
|
||||||
public SpigotDataHandler(
|
public SpigotDataHandler(
|
||||||
FloodgateHandshakeHandler handshakeHandler,
|
FloodgateHandshakeHandler handshakeHandler,
|
||||||
@@ -52,7 +53,6 @@ public final class SpigotDataHandler extends CommonDataHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setNewIp(Channel channel, InetSocketAddress newIp) {
|
protected void setNewIp(Channel channel, InetSocketAddress newIp) {
|
||||||
//todo the socket address will be overridden when bungeeData is true
|
|
||||||
setValue(networkManager, ClassNames.SOCKET_ADDRESS, newIp);
|
setValue(networkManager, ClassNames.SOCKET_ADDRESS, newIp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,20 +76,22 @@ public final class SpigotDataHandler extends CommonDataHandler {
|
|||||||
|
|
||||||
// the server will do all the work if BungeeCord mode is enabled,
|
// the server will do all the work if BungeeCord mode is enabled,
|
||||||
// otherwise we have to help the server.
|
// otherwise we have to help the server.
|
||||||
boolean bungeeData = ProxyUtils.isProxyData();
|
proxyData = ProxyUtils.isProxyData();
|
||||||
|
|
||||||
if (!bungeeData) {
|
if (!proxyData) {
|
||||||
// Use a spoofedUUID for initUUID (just like Bungeecord)
|
// Use a spoofedUUID for initUUID (just like Bungeecord)
|
||||||
setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId());
|
setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return bungeeData;
|
// we can only remove the handler if the data is proxy data and username validation doesn't
|
||||||
|
// exist. Otherwise, we need to wait and disable it.
|
||||||
|
return proxyData && ClassNames.PAPER_DISABLE_USERNAME_VALIDATION == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldCallFireRead(Object queuedPacket) {
|
protected boolean shouldCallFireRead(Object queuedPacket) {
|
||||||
// we have to ignore the 'login start' packet if BungeeCord mode is disabled,
|
// we have to ignore the 'login start' packet,
|
||||||
// otherwise the server might ask the user to login
|
// otherwise the server will ask the user to login
|
||||||
try {
|
try {
|
||||||
if (checkAndHandleLogin(queuedPacket)) {
|
if (checkAndHandleLogin(queuedPacket)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -136,6 +138,16 @@ public final class SpigotDataHandler extends CommonDataHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ClassNames.PAPER_DISABLE_USERNAME_VALIDATION != null) {
|
||||||
|
// ensure that Paper will not be checking
|
||||||
|
setValue(packetListener, ClassNames.PAPER_DISABLE_USERNAME_VALIDATION, true);
|
||||||
|
if (proxyData) {
|
||||||
|
// the server will handle the rest if we have proxy data
|
||||||
|
ctx.pipeline().remove(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set the player his GameProfile, we can't change the username without this
|
// set the player his GameProfile, we can't change the username without this
|
||||||
GameProfile gameProfile = new GameProfile(
|
GameProfile gameProfile = new GameProfile(
|
||||||
player.getCorrectUniqueId(), player.getCorrectUsername()
|
player.getCorrectUniqueId(), player.getCorrectUsername()
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.listener;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.event.profile.PreFillProfileEvent;
|
||||||
|
import com.destroystokyo.paper.profile.ProfileProperty;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||||
|
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||||
|
|
||||||
|
public final class PaperProfileListener implements Listener {
|
||||||
|
@Inject private SimpleFloodgateApi api;
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onFill(PreFillProfileEvent event) {
|
||||||
|
UUID id = event.getPlayerProfile().getId();
|
||||||
|
if (id == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FloodgatePlayer player = api.getPlayer(id);
|
||||||
|
if (player == null || player.isLinked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// back when this event got added the PlayerProfile class didn't have the
|
||||||
|
// hasProperty / hasTextures methods
|
||||||
|
if (event.getPlayerProfile().getProperties().stream().anyMatch(
|
||||||
|
prop -> "textures".equals(prop.getName()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<ProfileProperty> properties = new HashSet<>(event.getPlayerProfile().getProperties());
|
||||||
|
properties.add(new ProfileProperty("textures", "", ""));
|
||||||
|
event.setProperties(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,9 +35,7 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
|||||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||||
import org.geysermc.floodgate.player.FloodgatePlayerImpl;
|
|
||||||
import org.geysermc.floodgate.util.LanguageManager;
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
import org.geysermc.floodgate.util.SpigotCommandUtil;
|
|
||||||
|
|
||||||
public final class SpigotListener implements Listener {
|
public final class SpigotListener implements Listener {
|
||||||
@Inject private SimpleFloodgateApi api;
|
@Inject private SimpleFloodgateApi api;
|
||||||
@@ -68,7 +66,5 @@ public final class SpigotListener implements Listener {
|
|||||||
@EventHandler(priority = EventPriority.MONITOR)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
api.playerRemoved(event.getPlayer().getUniqueId());
|
api.playerRemoved(event.getPlayer().getUniqueId());
|
||||||
|
|
||||||
SpigotCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.module;
|
||||||
|
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.multibindings.ProvidesIntoSet;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.geysermc.floodgate.listener.PaperProfileListener;
|
||||||
|
import org.geysermc.floodgate.register.ListenerRegister;
|
||||||
|
|
||||||
|
public class PaperListenerModule extends SpigotListenerModule {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(new TypeLiteral<ListenerRegister<Listener>>() {}).asEagerSingleton();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@ProvidesIntoSet
|
||||||
|
public Listener paperProfileListener() {
|
||||||
|
return new PaperProfileListener();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,8 +32,12 @@ import com.google.inject.Provides;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.permissions.PermissionDefault;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.geysermc.floodgate.SpigotPlugin;
|
import org.geysermc.floodgate.SpigotPlugin;
|
||||||
|
import org.geysermc.floodgate.command.util.Permission;
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.player.FloodgateCommandPreprocessor;
|
import org.geysermc.floodgate.player.FloodgateCommandPreprocessor;
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
@@ -42,6 +46,12 @@ import org.geysermc.floodgate.player.UserAudience;
|
|||||||
public final class SpigotCommandModule extends CommandModule {
|
public final class SpigotCommandModule extends CommandModule {
|
||||||
private final SpigotPlugin plugin;
|
private final SpigotPlugin plugin;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
super.configure();
|
||||||
|
registerPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -49,10 +59,26 @@ public final class SpigotCommandModule extends CommandModule {
|
|||||||
CommandManager<UserAudience> commandManager = new BukkitCommandManager<>(
|
CommandManager<UserAudience> commandManager = new BukkitCommandManager<>(
|
||||||
plugin,
|
plugin,
|
||||||
CommandExecutionCoordinator.simpleCoordinator(),
|
CommandExecutionCoordinator.simpleCoordinator(),
|
||||||
commandUtil::getAudience,
|
commandUtil::getUserAudience,
|
||||||
audience -> (CommandSender) audience.source()
|
audience -> (CommandSender) audience.source()
|
||||||
);
|
);
|
||||||
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
|
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
|
||||||
return commandManager;
|
return commandManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerPermissions() {
|
||||||
|
PluginManager manager = Bukkit.getPluginManager();
|
||||||
|
for (Permission permission : Permission.values()) {
|
||||||
|
if (manager.getPermission(permission.get()) != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionDefault defaultValue =
|
||||||
|
PermissionDefault.getByName(permission.defaultValue().name());
|
||||||
|
|
||||||
|
manager.addPermission(new org.bukkit.permissions.Permission(
|
||||||
|
permission.get(), defaultValue
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import org.bukkit.event.Listener;
|
|||||||
import org.geysermc.floodgate.listener.SpigotListener;
|
import org.geysermc.floodgate.listener.SpigotListener;
|
||||||
import org.geysermc.floodgate.register.ListenerRegister;
|
import org.geysermc.floodgate.register.ListenerRegister;
|
||||||
|
|
||||||
public final class SpigotListenerModule extends AbstractModule {
|
public class SpigotListenerModule extends AbstractModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(new TypeLiteral<ListenerRegister<Listener>>() {}).asEagerSingleton();
|
bind(new TypeLiteral<ListenerRegister<Listener>>() {}).asEagerSingleton();
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import org.geysermc.floodgate.logger.JavaUtilFloodgateLogger;
|
|||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
||||||
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
|
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlatformUtils;
|
||||||
import org.geysermc.floodgate.pluginmessage.PluginMessageRegistration;
|
import org.geysermc.floodgate.pluginmessage.PluginMessageRegistration;
|
||||||
import org.geysermc.floodgate.pluginmessage.SpigotPluginMessageRegistration;
|
import org.geysermc.floodgate.pluginmessage.SpigotPluginMessageRegistration;
|
||||||
import org.geysermc.floodgate.pluginmessage.SpigotPluginMessageUtils;
|
import org.geysermc.floodgate.pluginmessage.SpigotPluginMessageUtils;
|
||||||
@@ -49,12 +50,18 @@ import org.geysermc.floodgate.pluginmessage.SpigotSkinApplier;
|
|||||||
import org.geysermc.floodgate.skin.SkinApplier;
|
import org.geysermc.floodgate.skin.SkinApplier;
|
||||||
import org.geysermc.floodgate.util.LanguageManager;
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
import org.geysermc.floodgate.util.SpigotCommandUtil;
|
import org.geysermc.floodgate.util.SpigotCommandUtil;
|
||||||
|
import org.geysermc.floodgate.util.SpigotPlatformUtils;
|
||||||
import org.geysermc.floodgate.util.SpigotVersionSpecificMethods;
|
import org.geysermc.floodgate.util.SpigotVersionSpecificMethods;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public final class SpigotPlatformModule extends AbstractModule {
|
public final class SpigotPlatformModule extends AbstractModule {
|
||||||
private final SpigotPlugin plugin;
|
private final SpigotPlugin plugin;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(PlatformUtils.class).to(SpigotPlatformUtils.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
public JavaPlugin javaPlugin() {
|
public JavaPlugin javaPlugin() {
|
||||||
@@ -76,10 +83,9 @@ public final class SpigotPlatformModule extends AbstractModule {
|
|||||||
public CommandUtil commandUtil(
|
public CommandUtil commandUtil(
|
||||||
FloodgateApi api,
|
FloodgateApi api,
|
||||||
SpigotVersionSpecificMethods versionSpecificMethods,
|
SpigotVersionSpecificMethods versionSpecificMethods,
|
||||||
FloodgateLogger logger,
|
|
||||||
LanguageManager languageManager) {
|
LanguageManager languageManager) {
|
||||||
return new SpigotCommandUtil(plugin.getServer(), api, versionSpecificMethods, plugin,
|
return new SpigotCommandUtil(
|
||||||
logger, languageManager);
|
languageManager, plugin.getServer(), api, versionSpecificMethods, plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -43,8 +43,6 @@ public class SpigotPluginMessageRegistration implements PluginMessageRegistratio
|
|||||||
(channel1, player, message) ->
|
(channel1, player, message) ->
|
||||||
channel.handleServerCall(message, player.getUniqueId(), player.getName()));
|
channel.handleServerCall(message, player.getUniqueId(), player.getName()));
|
||||||
|
|
||||||
//todo actually do something with the result, lol
|
|
||||||
|
|
||||||
messenger.registerOutgoingPluginChannel(plugin, channel.getIdentifier());
|
messenger.registerOutgoingPluginChannel(plugin, channel.getIdentifier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
import static org.geysermc.floodgate.util.ReflectionUtils.getField;
|
||||||
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
|
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
|
||||||
import static org.geysermc.floodgate.util.ReflectionUtils.getMethod;
|
import static org.geysermc.floodgate.util.ReflectionUtils.getMethod;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ import java.lang.reflect.Method;
|
|||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
@SuppressWarnings("PMD.SystemPrintln")
|
@SuppressWarnings("PMD.SystemPrintln")
|
||||||
public class ClassNames {
|
public class ClassNames {
|
||||||
@@ -57,12 +59,17 @@ public class ClassNames {
|
|||||||
public static final Field LOGIN_PROFILE;
|
public static final Field LOGIN_PROFILE;
|
||||||
public static final Field PACKET_LISTENER;
|
public static final Field PACKET_LISTENER;
|
||||||
|
|
||||||
|
@Nullable public static final Field PAPER_DISABLE_USERNAME_VALIDATION;
|
||||||
|
@Nullable public static final Field PAPER_VELOCITY_SUPPORT;
|
||||||
|
|
||||||
public static final Method GET_PROFILE_METHOD;
|
public static final Method GET_PROFILE_METHOD;
|
||||||
public static final Method LOGIN_DISCONNECT;
|
public static final Method LOGIN_DISCONNECT;
|
||||||
public static final Method NETWORK_EXCEPTION_CAUGHT;
|
public static final Method NETWORK_EXCEPTION_CAUGHT;
|
||||||
public static final Method INIT_UUID;
|
public static final Method INIT_UUID;
|
||||||
public static final Method FIRE_LOGIN_EVENTS;
|
public static final Method FIRE_LOGIN_EVENTS;
|
||||||
|
|
||||||
|
public static final Field BUNGEE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||||
SPIGOT_MAPPING_PREFIX = "net.minecraft.server." + version;
|
SPIGOT_MAPPING_PREFIX = "net.minecraft.server." + version;
|
||||||
@@ -71,7 +78,7 @@ public class ClassNames {
|
|||||||
// SpigotSkinApplier
|
// SpigotSkinApplier
|
||||||
Class<?> craftPlayerClass = ReflectionUtils.getClass(
|
Class<?> craftPlayerClass = ReflectionUtils.getClass(
|
||||||
"org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
|
"org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
|
||||||
GET_PROFILE_METHOD = ReflectionUtils.getMethod(craftPlayerClass, "getProfile");
|
GET_PROFILE_METHOD = getMethod(craftPlayerClass, "getProfile");
|
||||||
checkNotNull(GET_PROFILE_METHOD, "Get profile method");
|
checkNotNull(GET_PROFILE_METHOD, "Get profile method");
|
||||||
|
|
||||||
String nmsPackage = SPIGOT_MAPPING_PREFIX + '.';
|
String nmsPackage = SPIGOT_MAPPING_PREFIX + '.';
|
||||||
@@ -157,6 +164,28 @@ public class ClassNames {
|
|||||||
|
|
||||||
FIRE_LOGIN_EVENTS = getMethod(LOGIN_HANDLER, "fireEvents");
|
FIRE_LOGIN_EVENTS = getMethod(LOGIN_HANDLER, "fireEvents");
|
||||||
checkNotNull(FIRE_LOGIN_EVENTS, "fireEvents from LoginHandler");
|
checkNotNull(FIRE_LOGIN_EVENTS, "fireEvents from LoginHandler");
|
||||||
|
|
||||||
|
|
||||||
|
PAPER_DISABLE_USERNAME_VALIDATION = getField(LOGIN_LISTENER,
|
||||||
|
"iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation");
|
||||||
|
|
||||||
|
if (Constants.DEBUG_MODE) {
|
||||||
|
System.out.println("Paper disable username validation field exists? " +
|
||||||
|
(PAPER_DISABLE_USERNAME_VALIDATION != null));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyUtils
|
||||||
|
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
|
||||||
|
checkNotNull(spigotConfig, "Spigot config");
|
||||||
|
|
||||||
|
BUNGEE = getField(spigotConfig, "bungee");
|
||||||
|
checkNotNull(BUNGEE, "Bungee field");
|
||||||
|
|
||||||
|
Class<?> paperConfig = ReflectionUtils.getClassSilently(
|
||||||
|
"com.destroystokyo.paper.PaperConfig");
|
||||||
|
|
||||||
|
PAPER_VELOCITY_SUPPORT =
|
||||||
|
paperConfig == null ? null : getField(paperConfig, "velocitySupport");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> getClassOrFallBack(String className, String fallbackName) {
|
private static Class<?> getClassOrFallBack(String className, String fallbackName) {
|
||||||
|
|||||||
@@ -25,45 +25,22 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static org.geysermc.floodgate.util.ReflectionUtils.getField;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
public final class ProxyUtils {
|
public final class ProxyUtils {
|
||||||
private static final Field IS_BUNGEE_DATA;
|
|
||||||
private static final Field IS_MODERN_FORWARDING;
|
|
||||||
|
|
||||||
static {
|
|
||||||
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
|
|
||||||
IS_BUNGEE_DATA = getField(spigotConfig, "bungee");
|
|
||||||
checkNotNull(IS_BUNGEE_DATA, "bungee field cannot be null. Are you using CraftBukkit?");
|
|
||||||
|
|
||||||
Field velocitySupport;
|
|
||||||
try {
|
|
||||||
Class<?> paperConfig = Class.forName("com.destroystokyo.paper.PaperConfig");
|
|
||||||
velocitySupport = getField(paperConfig, "velocitySupport");
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// We're not on a platform that has modern forwarding
|
|
||||||
velocitySupport = null; // NOPMD - there's really not a better way around this unless you want to use an optional
|
|
||||||
}
|
|
||||||
IS_MODERN_FORWARDING = velocitySupport;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isProxyData() {
|
public static boolean isProxyData() {
|
||||||
return isBungeeData() || isVelocitySupport();
|
return isBungeeData() || isVelocitySupport();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isBungeeData() {
|
private static boolean isBungeeData() {
|
||||||
return ReflectionUtils.getCastedValue(null, IS_BUNGEE_DATA);
|
return ReflectionUtils.castedStaticValue(ClassNames.BUNGEE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isVelocitySupport() {
|
private static boolean isVelocitySupport() {
|
||||||
if (IS_MODERN_FORWARDING == null) {
|
if (ClassNames.PAPER_VELOCITY_SUPPORT == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ReflectionUtils.getCastedValue(null, IS_MODERN_FORWARDING);
|
return ReflectionUtils.castedStaticValue(ClassNames.PAPER_VELOCITY_SUPPORT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,44 +25,40 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType;
|
import org.geysermc.floodgate.player.UserAudience.ConsoleAudience;
|
||||||
import org.geysermc.floodgate.util.SpigotUserAudience.SpigotConsoleAudience;
|
import org.geysermc.floodgate.player.UserAudience.PlayerAudience;
|
||||||
import org.geysermc.floodgate.util.SpigotUserAudience.SpigotPlayerAudience;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public final class SpigotCommandUtil implements CommandUtil {
|
|
||||||
public static final @NonNull Map<UUID, UserAudience> AUDIENCE_CACHE = new HashMap<>();
|
|
||||||
private static UserAudience console;
|
|
||||||
|
|
||||||
|
public final class SpigotCommandUtil extends CommandUtil {
|
||||||
private final Server server;
|
private final Server server;
|
||||||
private final FloodgateApi api;
|
|
||||||
private final SpigotVersionSpecificMethods versionSpecificMethods;
|
private final SpigotVersionSpecificMethods versionSpecificMethods;
|
||||||
|
|
||||||
private final JavaPlugin plugin;
|
private final JavaPlugin plugin;
|
||||||
private final FloodgateLogger logger;
|
private UserAudience console;
|
||||||
private final LanguageManager manager;
|
|
||||||
|
public SpigotCommandUtil(
|
||||||
|
LanguageManager manager,
|
||||||
|
Server server,
|
||||||
|
FloodgateApi api,
|
||||||
|
SpigotVersionSpecificMethods versionSpecificMethods,
|
||||||
|
JavaPlugin plugin) {
|
||||||
|
super(manager, api);
|
||||||
|
this.server = server;
|
||||||
|
this.versionSpecificMethods = versionSpecificMethods;
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getAudience(final @NonNull Object sourceObj) {
|
public @NonNull UserAudience getUserAudience(final @NonNull Object sourceObj) {
|
||||||
if (!(sourceObj instanceof CommandSender)) {
|
if (!(sourceObj instanceof CommandSender)) {
|
||||||
throw new IllegalArgumentException("Source has to be a CommandSender!");
|
throw new IllegalArgumentException("Source has to be a CommandSender!");
|
||||||
}
|
}
|
||||||
@@ -72,89 +68,47 @@ public final class SpigotCommandUtil implements CommandUtil {
|
|||||||
if (console != null) {
|
if (console != null) {
|
||||||
return console;
|
return console;
|
||||||
}
|
}
|
||||||
return console = new SpigotConsoleAudience(source, this);
|
return console = new ConsoleAudience(source, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Player player = (Player) source;
|
Player player = (Player) source;
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
|
String username = player.getName();
|
||||||
String locale = versionSpecificMethods.getLocale(player);
|
String locale = versionSpecificMethods.getLocale(player);
|
||||||
|
|
||||||
return AUDIENCE_CACHE.computeIfAbsent(uuid,
|
return new PlayerAudience(uuid, username, locale, source,this, true);
|
||||||
$ -> new SpigotPlayerAudience(uuid, locale, source, true, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UserAudience getAudienceByUsername(@NonNull String username) {
|
protected String getUsernameFromSource(@NonNull Object source) {
|
||||||
Player player = server.getPlayer(username);
|
return ((Player) source).getName();
|
||||||
return player != null ? getAudience(player) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getOfflineAudienceByUsername(@NonNull String username) {
|
protected UUID getUuidFromSource(@NonNull Object source) {
|
||||||
return new SpigotPlayerAudience(null, username, null, null, false, this);
|
return ((Player) source).getUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UserAudience getAudienceByUuid(@NonNull UUID uuid) {
|
protected Collection<?> getOnlinePlayers() {
|
||||||
|
return server.getOnlinePlayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPlayerByUuid(@NonNull UUID uuid) {
|
||||||
Player player = server.getPlayer(uuid);
|
Player player = server.getPlayer(uuid);
|
||||||
return player != null ? getAudience(player) : null;
|
return player != null ? player : uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) {
|
public Object getPlayerByUsername(@NonNull String username) {
|
||||||
return new SpigotPlayerAudience(uuid, null, null, null, false, this);
|
Player player = server.getPlayer(username);
|
||||||
}
|
return player != null ? player : username;
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
|
|
||||||
Collection<? extends Player> players = server.getOnlinePlayers();
|
|
||||||
|
|
||||||
Collection<String> usernames = new ArrayList<>();
|
|
||||||
switch (limitTo) {
|
|
||||||
case ALL_PLAYERS:
|
|
||||||
for (Player player : players) {
|
|
||||||
usernames.add(player.getName());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLY_JAVA:
|
|
||||||
for (Player player : players) {
|
|
||||||
if (!api.isFloodgatePlayer(player.getUniqueId())) {
|
|
||||||
usernames.add(player.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLY_BEDROCK:
|
|
||||||
for (Player player : players) {
|
|
||||||
if (api.isFloodgatePlayer(player.getUniqueId())) {
|
|
||||||
usernames.add(player.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown PlayerType");
|
|
||||||
}
|
|
||||||
return usernames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(Object player, String permission) {
|
public boolean hasPermission(Object player, String permission) {
|
||||||
return cast(player).hasPermission(permission);
|
return ((CommandSender) player).hasPermission(permission);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Object> getOnlinePlayersWithPermission(String permission) {
|
|
||||||
List<Object> players = new ArrayList<>();
|
|
||||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
|
||||||
if (hasPermission(player, permission)) {
|
|
||||||
players.add(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return players;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(Object target, String locale, TranslatableMessage message, Object... args) {
|
|
||||||
sendMessage(target, translateAndTransform(locale, message, args));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,10 +117,11 @@ public final class SpigotCommandUtil implements CommandUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args) {
|
public void kickPlayer(Object player, String message) {
|
||||||
// Have to run this in the main thread so we don't get a `Asynchronous player kick!` error
|
// can also be console
|
||||||
Bukkit.getScheduler().runTask(plugin,
|
if (player instanceof Player) {
|
||||||
() -> cast(player).kickPlayer(translateAndTransform(locale, message, args)));
|
Bukkit.getScheduler().runTask(plugin, () -> ((Player) player).kickPlayer(message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,18 +133,4 @@ public final class SpigotCommandUtil implements CommandUtil {
|
|||||||
public boolean removePlayerFromWhitelist(UUID uuid, String username) {
|
public boolean removePlayerFromWhitelist(UUID uuid, String username) {
|
||||||
return WhitelistUtils.removePlayer(uuid, username);
|
return WhitelistUtils.removePlayer(uuid, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String translateAndTransform(String locale, TranslatableMessage message, Object... args) {
|
|
||||||
// unlike others, Bukkit doesn't have to transform a message into another class.
|
|
||||||
return message.translateMessage(manager, locale, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Player cast(Object instance) {
|
|
||||||
try {
|
|
||||||
return (Player) instance;
|
|
||||||
} catch (ClassCastException exception) {
|
|
||||||
logger.error("Failed to cast {} to Player", instance.getClass().getName());
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.UUID;
|
||||||
|
import org.geysermc.floodgate.api.handshake.HandshakeData;
|
||||||
|
import org.geysermc.floodgate.api.handshake.HandshakeHandler;
|
||||||
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
|
|
||||||
|
public class SpigotHandshakeHandler implements HandshakeHandler {
|
||||||
|
@Inject
|
||||||
|
private FloodgateLogger logger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HandshakeData data) {
|
||||||
|
// we never have to do anything when BedrockData is null.
|
||||||
|
// BedrockData is null when something went wrong (e.g. invalid key / exception)
|
||||||
|
if (data.getBedrockData() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BedrockData bedrockData = data.getBedrockData();
|
||||||
|
UUID correctUuid = data.getCorrectUniqueId();
|
||||||
|
|
||||||
|
// replace the ip and uuid with the Bedrock client IP and an uuid based of the xuid
|
||||||
|
String[] split = data.getHostname().split("\0");
|
||||||
|
if (split.length >= 3) {
|
||||||
|
if (logger.isDebug()) {
|
||||||
|
logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'",
|
||||||
|
split[1], bedrockData.getIp(), split[2], correctUuid.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
split[1] = bedrockData.getIp();
|
||||||
|
split[2] = correctUuid.toString();
|
||||||
|
}
|
||||||
|
data.setHostname(String.join("\0", split));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,23 +23,27 @@
|
|||||||
* @link https://github.com/GeyserMC/Floodgate
|
* @link https://github.com/GeyserMC/Floodgate
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.floodgate.player;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
import java.util.UUID;
|
import org.bukkit.Bukkit;
|
||||||
import net.kyori.adventure.audience.Audience;
|
import org.geysermc.floodgate.platform.util.PlatformUtils;
|
||||||
import org.checkerframework.checker.index.qual.NonNegative;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
import org.geysermc.floodgate.player.UserAudience.ConsoleAudience;
|
|
||||||
|
|
||||||
public interface ServerAudience extends Audience {
|
public class SpigotPlatformUtils extends PlatformUtils {
|
||||||
@NonNull Iterable<? extends UserAudience> onlineAudiences();
|
@Override
|
||||||
|
public AuthType authType() {
|
||||||
@NonNull ConsoleAudience consoleAudience();
|
if (Bukkit.getOnlineMode()) {
|
||||||
|
return AuthType.ONLINE;
|
||||||
@NonNegative int onlineCount();
|
}
|
||||||
|
return ProxyUtils.isProxyData() ? AuthType.PROXIED : AuthType.OFFLINE;
|
||||||
@Nullable UserAudience audienceOf(final @NonNull UUID uuid);
|
}
|
||||||
|
|
||||||
@Nullable UserAudience audienceOf(final @NonNull String username);
|
@Override
|
||||||
|
public String minecraftVersion() {
|
||||||
|
return Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serverImplementationName() {
|
||||||
|
return Bukkit.getServer().getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,158 +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 java.util.UUID;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import net.kyori.adventure.audience.Audience;
|
|
||||||
import net.kyori.adventure.audience.ForwardingAudience;
|
|
||||||
import net.kyori.adventure.audience.MessageType;
|
|
||||||
import net.kyori.adventure.identity.Identity;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class SpigotUserAudience implements UserAudience, ForwardingAudience.Single {
|
|
||||||
private final UUID uuid;
|
|
||||||
private final String locale;
|
|
||||||
private final CommandSender source;
|
|
||||||
private final CommandUtil commandUtil;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull UUID uuid() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String username() {
|
|
||||||
return source.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String locale() {
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull CommandSender source() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(@NonNull String permission) {
|
|
||||||
return source.hasPermission(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(
|
|
||||||
@NonNull Identity source,
|
|
||||||
@NonNull Component message,
|
|
||||||
@NonNull MessageType type) {
|
|
||||||
this.source.sendMessage(GsonComponentSerializer.gson().serialize(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(TranslatableMessage message, Object... args) {
|
|
||||||
commandUtil.sendMessage(source(), locale(), message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnect(@NonNull Component reason) {
|
|
||||||
if (source instanceof Player) {
|
|
||||||
((Player) source).kickPlayer(GsonComponentSerializer.gson().serialize(reason));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnect(TranslatableMessage message, Object... args) {
|
|
||||||
commandUtil.kickPlayer(source(), locale(), message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Audience audience() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class SpigotConsoleAudience extends SpigotUserAudience
|
|
||||||
implements ConsoleAudience {
|
|
||||||
public SpigotConsoleAudience(CommandSender source, CommandUtil commandUtil) {
|
|
||||||
super(new UUID(0, 0), "en_us", source, commandUtil);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(
|
|
||||||
@NonNull Identity source,
|
|
||||||
@NonNull Component message,
|
|
||||||
@NonNull MessageType type) {
|
|
||||||
source().sendMessage(LegacyComponentSerializer.legacySection().serialize(message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class SpigotPlayerAudience extends SpigotUserAudience
|
|
||||||
implements PlayerAudience {
|
|
||||||
|
|
||||||
private final String username;
|
|
||||||
private final boolean online;
|
|
||||||
|
|
||||||
public SpigotPlayerAudience(
|
|
||||||
UUID uuid,
|
|
||||||
String username,
|
|
||||||
String locale,
|
|
||||||
CommandSender source,
|
|
||||||
boolean online,
|
|
||||||
CommandUtil commandUtil) {
|
|
||||||
super(uuid, locale, source, commandUtil);
|
|
||||||
this.username = username;
|
|
||||||
this.online = online;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SpigotPlayerAudience(
|
|
||||||
UUID uuid,
|
|
||||||
String locale,
|
|
||||||
CommandSender source,
|
|
||||||
boolean online,
|
|
||||||
CommandUtil commandUtil) {
|
|
||||||
this(uuid, source.getName(), locale, source, online, commandUtil);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String username() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean online() {
|
|
||||||
return online;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ var guavaVersion = "25.1-jre"
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
api("cloud.commandframework", "cloud-velocity", Versions.cloudVersion)
|
implementation("cloud.commandframework", "cloud-velocity", Versions.cloudVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
relocate("cloud.commandframework")
|
relocate("cloud.commandframework")
|
||||||
@@ -14,7 +14,6 @@ relocate("io.leangen.geantyref")
|
|||||||
|
|
||||||
|
|
||||||
// these dependencies are already present on the platform
|
// these dependencies are already present on the platform
|
||||||
provided("net.kyori", "adventure-api", Versions.adventureApiVersion, 0b100)
|
|
||||||
provided("com.google.code.gson", "gson", gsonVersion)
|
provided("com.google.code.gson", "gson", gsonVersion)
|
||||||
provided("com.google.guava", "guava", guavaVersion)
|
provided("com.google.guava", "guava", guavaVersion)
|
||||||
provided("com.google.inject", "guice", Versions.guiceVersion)
|
provided("com.google.inject", "guice", Versions.guiceVersion)
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public final class VelocityInjector extends CommonPlatformInjector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeInjection() {
|
public boolean removeInjection() {
|
||||||
logger.error("Floodgate cannot remove itself from Bungee without a reboot");
|
logger.error("Floodgate cannot remove itself from Velocity without a reboot");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,18 +44,19 @@ import com.velocitypowered.api.event.connection.PreLoginEvent;
|
|||||||
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
||||||
import com.velocitypowered.api.proxy.InboundConnection;
|
import com.velocitypowered.api.proxy.InboundConnection;
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
import com.velocitypowered.api.util.GameProfile;
|
||||||
|
import com.velocitypowered.api.util.GameProfile.Property;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.Collections;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||||
|
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||||
import org.geysermc.floodgate.util.LanguageManager;
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
import org.geysermc.floodgate.util.VelocityCommandUtil;
|
|
||||||
|
|
||||||
public final class VelocityListener {
|
public final class VelocityListener {
|
||||||
private static final Field INITIAL_MINECRAFT_CONNECTION;
|
private static final Field INITIAL_MINECRAFT_CONNECTION;
|
||||||
@@ -89,6 +90,7 @@ public final class VelocityListener {
|
|||||||
.expireAfterAccess(20, TimeUnit.SECONDS)
|
.expireAfterAccess(20, TimeUnit.SECONDS)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@Inject private ProxyFloodgateConfig config;
|
||||||
@Inject private ProxyFloodgateApi api;
|
@Inject private ProxyFloodgateApi api;
|
||||||
@Inject private LanguageManager languageManager;
|
@Inject private LanguageManager languageManager;
|
||||||
@Inject private FloodgateLogger logger;
|
@Inject private FloodgateLogger logger;
|
||||||
@@ -141,8 +143,17 @@ public final class VelocityListener {
|
|||||||
FloodgatePlayer player = playerCache.getIfPresent(event.getConnection());
|
FloodgatePlayer player = playerCache.getIfPresent(event.getConnection());
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
playerCache.invalidate(event.getConnection());
|
playerCache.invalidate(event.getConnection());
|
||||||
event.setGameProfile(new GameProfile(
|
|
||||||
player.getCorrectUniqueId(), player.getCorrectUsername(), new ArrayList<>()));
|
GameProfile profile = new GameProfile(
|
||||||
|
player.getCorrectUniqueId(),
|
||||||
|
player.getCorrectUsername(),
|
||||||
|
Collections.emptyList()
|
||||||
|
);
|
||||||
|
// The texture properties addition is to fix the February 2 2022 Mojang authentication changes
|
||||||
|
if (!config.isSendFloodgateData() && !player.isLinked()) {
|
||||||
|
profile = profile.addProperty(new Property("textures", "", ""));
|
||||||
|
}
|
||||||
|
event.setGameProfile(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +170,5 @@ public final class VelocityListener {
|
|||||||
@Subscribe(order = PostOrder.LAST)
|
@Subscribe(order = PostOrder.LAST)
|
||||||
public void onDisconnect(DisconnectEvent event) {
|
public void onDisconnect(DisconnectEvent event) {
|
||||||
api.playerRemoved(event.getPlayer().getUniqueId());
|
api.playerRemoved(event.getPlayer().getUniqueId());
|
||||||
|
|
||||||
VelocityCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import org.geysermc.floodgate.logger.Slf4jFloodgateLogger;
|
|||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
|
||||||
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
|
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlatformUtils;
|
||||||
import org.geysermc.floodgate.player.FloodgateCommandPreprocessor;
|
import org.geysermc.floodgate.player.FloodgateCommandPreprocessor;
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||||
@@ -57,6 +58,7 @@ import org.geysermc.floodgate.pluginmessage.VelocityPluginMessageUtils;
|
|||||||
import org.geysermc.floodgate.skin.SkinApplier;
|
import org.geysermc.floodgate.skin.SkinApplier;
|
||||||
import org.geysermc.floodgate.util.LanguageManager;
|
import org.geysermc.floodgate.util.LanguageManager;
|
||||||
import org.geysermc.floodgate.util.VelocityCommandUtil;
|
import org.geysermc.floodgate.util.VelocityCommandUtil;
|
||||||
|
import org.geysermc.floodgate.util.VelocityPlatformUtils;
|
||||||
import org.geysermc.floodgate.util.VelocitySkinApplier;
|
import org.geysermc.floodgate.util.VelocitySkinApplier;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
@@ -66,25 +68,25 @@ public final class VelocityPlatformModule extends AbstractModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
VelocityCommandUtil commandUtil = new VelocityCommandUtil();
|
|
||||||
requestInjection(commandUtil);
|
|
||||||
|
|
||||||
bind(CommandUtil.class).to(VelocityCommandUtil.class);
|
bind(CommandUtil.class).to(VelocityCommandUtil.class);
|
||||||
bind(VelocityCommandUtil.class).toInstance(commandUtil);
|
bind(PlatformUtils.class).to(VelocityPlatformUtils.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public CommandManager<UserAudience> commandManager(CommandUtil commandUtil) {
|
||||||
Injector child = guice.createChildInjector(new CloudInjectionModule<>(
|
Injector child = guice.createChildInjector(new CloudInjectionModule<>(
|
||||||
UserAudience.class,
|
UserAudience.class,
|
||||||
CommandExecutionCoordinator.simpleCoordinator(),
|
CommandExecutionCoordinator.simpleCoordinator(),
|
||||||
commandUtil::getAudience,
|
commandUtil::getUserAudience,
|
||||||
audience -> (CommandSource) audience.source()
|
audience -> (CommandSource) audience.source()
|
||||||
));
|
));
|
||||||
|
|
||||||
CommandManager<UserAudience> commandManager =
|
CommandManager<UserAudience> commandManager =
|
||||||
child.getInstance(new Key<VelocityCommandManager<UserAudience>>() {});
|
child.getInstance(new Key<VelocityCommandManager<UserAudience>>() {});
|
||||||
|
|
||||||
bind(new Key<CommandManager<UserAudience>>() {}).toInstance(commandManager);
|
|
||||||
|
|
||||||
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
|
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
|
||||||
|
return commandManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -99,7 +101,8 @@ public final class VelocityPlatformModule extends AbstractModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
public ListenerRegistration<Object> listenerRegistration(EventManager eventManager,
|
public ListenerRegistration<Object> listenerRegistration(
|
||||||
|
EventManager eventManager,
|
||||||
VelocityPlugin plugin) {
|
VelocityPlugin plugin) {
|
||||||
return new VelocityListenerRegistration(eventManager, plugin);
|
return new VelocityListenerRegistration(eventManager, plugin);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,134 +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.player;
|
|
||||||
|
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
|
||||||
import java.util.UUID;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import net.kyori.adventure.audience.Audience;
|
|
||||||
import net.kyori.adventure.audience.ForwardingAudience;
|
|
||||||
import net.kyori.adventure.audience.MessageType;
|
|
||||||
import net.kyori.adventure.identity.Identity;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public abstract class VelocityUserAudience implements UserAudience, ForwardingAudience.Single {
|
|
||||||
private final UUID uuid;
|
|
||||||
private final String username;
|
|
||||||
private final String locale;
|
|
||||||
private final CommandSource source;
|
|
||||||
private final CommandUtil commandUtil;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull UUID uuid() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String username() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String locale() {
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull CommandSource source() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPermission(@NonNull String permission) {
|
|
||||||
return source.hasPermission(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(
|
|
||||||
@NonNull Identity source,
|
|
||||||
@NonNull Component message,
|
|
||||||
@NonNull MessageType type) {
|
|
||||||
// apparently the console doesn't implement sendMessage with MessageType,
|
|
||||||
// so we'll just ignore it
|
|
||||||
this.source.sendMessage(source, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(TranslatableMessage message, Object... args) {
|
|
||||||
commandUtil.sendMessage(source(), locale(), message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnect(@NonNull Component reason) {
|
|
||||||
if (source instanceof Player) {
|
|
||||||
((Player) source).disconnect(reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnect(TranslatableMessage message, Object... args) {
|
|
||||||
commandUtil.kickPlayer(source(), locale(), message, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Audience audience() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class VelocityConsoleAudience extends VelocityUserAudience
|
|
||||||
implements ConsoleAudience {
|
|
||||||
|
|
||||||
public VelocityConsoleAudience(CommandSource source, CommandUtil commandUtil) {
|
|
||||||
super(new UUID(0, 0), "CONSOLE", "en_us", source, commandUtil);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class VelocityPlayerAudience extends VelocityUserAudience
|
|
||||||
implements PlayerAudience {
|
|
||||||
private final boolean online;
|
|
||||||
|
|
||||||
public VelocityPlayerAudience(
|
|
||||||
UUID uuid,
|
|
||||||
String username,
|
|
||||||
String locale,
|
|
||||||
CommandSource source,
|
|
||||||
boolean online,
|
|
||||||
CommandUtil commandUtil) {
|
|
||||||
super(uuid, username, locale, source, commandUtil);
|
|
||||||
this.online = online;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean online() {
|
|
||||||
return online;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,35 +29,29 @@ import com.google.inject.Inject;
|
|||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.Optional;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
|
||||||
import org.geysermc.floodgate.platform.command.CommandUtil;
|
import org.geysermc.floodgate.platform.command.CommandUtil;
|
||||||
import org.geysermc.floodgate.platform.command.TranslatableMessage;
|
|
||||||
import org.geysermc.floodgate.player.UserAudience;
|
import org.geysermc.floodgate.player.UserAudience;
|
||||||
import org.geysermc.floodgate.player.UserAudienceArgument.PlayerType;
|
import org.geysermc.floodgate.player.UserAudience.ConsoleAudience;
|
||||||
import org.geysermc.floodgate.player.VelocityUserAudience.VelocityConsoleAudience;
|
import org.geysermc.floodgate.player.UserAudience.PlayerAudience;
|
||||||
import org.geysermc.floodgate.player.VelocityUserAudience.VelocityPlayerAudience;
|
|
||||||
|
|
||||||
public final class VelocityCommandUtil implements CommandUtil {
|
public final class VelocityCommandUtil extends CommandUtil {
|
||||||
public static final @NonNull Map<UUID, UserAudience> AUDIENCE_CACHE = new HashMap<>();
|
|
||||||
private static UserAudience console;
|
private static UserAudience console;
|
||||||
|
|
||||||
@Inject private ProxyServer server;
|
@Inject private ProxyServer server;
|
||||||
@Inject private FloodgateApi api;
|
|
||||||
@Inject private FloodgateLogger logger;
|
@Inject
|
||||||
@Inject private LanguageManager manager;
|
public VelocityCommandUtil(LanguageManager manager, FloodgateApi api) {
|
||||||
|
super(manager, api);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getAudience(@NonNull Object sourceObj) {
|
public @NonNull UserAudience getUserAudience(@NonNull Object sourceObj) {
|
||||||
if (!(sourceObj instanceof CommandSource)) {
|
if (!(sourceObj instanceof CommandSource)) {
|
||||||
throw new IllegalArgumentException("Can only work with CommandSource!");
|
throw new IllegalArgumentException("Can only work with CommandSource!");
|
||||||
}
|
}
|
||||||
@@ -67,7 +61,7 @@ public final class VelocityCommandUtil implements CommandUtil {
|
|||||||
if (console != null) {
|
if (console != null) {
|
||||||
return console;
|
return console;
|
||||||
}
|
}
|
||||||
return console = new VelocityConsoleAudience(source, this);
|
return console = new ConsoleAudience(source, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Player player = (Player) source;
|
Player player = (Player) source;
|
||||||
@@ -75,84 +69,39 @@ public final class VelocityCommandUtil implements CommandUtil {
|
|||||||
String username = player.getUsername();
|
String username = player.getUsername();
|
||||||
String locale = Utils.getLocale(player.getPlayerSettings().getLocale());
|
String locale = Utils.getLocale(player.getPlayerSettings().getLocale());
|
||||||
|
|
||||||
return AUDIENCE_CACHE.computeIfAbsent(uuid,
|
return new PlayerAudience(uuid, username, locale, source, this, true);
|
||||||
$ -> new VelocityPlayerAudience(uuid, username, locale, source, true, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UserAudience getAudienceByUsername(@NonNull String username) {
|
protected String getUsernameFromSource(@NonNull Object source) {
|
||||||
return server.getPlayer(username)
|
return ((Player) source).getUsername();
|
||||||
.map(this::getAudience)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getOfflineAudienceByUsername(@NonNull String username) {
|
protected UUID getUuidFromSource(@NonNull Object source) {
|
||||||
return new VelocityPlayerAudience(null, username, null, null, false, this);
|
return ((Player) source).getUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UserAudience getAudienceByUuid(@NonNull UUID uuid) {
|
protected Collection<?> getOnlinePlayers() {
|
||||||
return server.getPlayer(uuid)
|
return server.getAllPlayers();
|
||||||
.map(this::getAudience)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull UserAudience getOfflineAudienceByUuid(@NonNull UUID uuid) {
|
public Object getPlayerByUuid(@NonNull UUID uuid) {
|
||||||
return new VelocityPlayerAudience(uuid, null, null, null, false, this);
|
Optional<Player> player = server.getPlayer(uuid);
|
||||||
|
return player.isPresent() ? player.get() : uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull Collection<String> getOnlineUsernames(@NonNull PlayerType limitTo) {
|
public Object getPlayerByUsername(@NonNull String username) {
|
||||||
Collection<Player> players = server.getAllPlayers();
|
Optional<Player> player = server.getPlayer(username);
|
||||||
|
return player.isPresent() ? player.get() : username;
|
||||||
Collection<String> usernames = new ArrayList<>();
|
|
||||||
switch (limitTo) {
|
|
||||||
case ALL_PLAYERS:
|
|
||||||
for (Player player : players) {
|
|
||||||
usernames.add(player.getUsername());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLY_JAVA:
|
|
||||||
for (Player player : players) {
|
|
||||||
if (!api.isFloodgatePlayer(player.getUniqueId())) {
|
|
||||||
usernames.add(player.getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLY_BEDROCK:
|
|
||||||
for (Player player : players) {
|
|
||||||
if (api.isFloodgatePlayer(player.getUniqueId())) {
|
|
||||||
usernames.add(player.getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unknown PlayerType");
|
|
||||||
}
|
|
||||||
return usernames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(Object player, String permission) {
|
public boolean hasPermission(Object player, String permission) {
|
||||||
return cast(player).hasPermission(permission);
|
return ((CommandSource) player).hasPermission(permission);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Object> getOnlinePlayersWithPermission(String permission) {
|
|
||||||
List<Object> players = new ArrayList<>();
|
|
||||||
for (Player player : server.getAllPlayers()) {
|
|
||||||
if (hasPermission(player, permission)) {
|
|
||||||
players.add(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return players;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(Object target, String locale, TranslatableMessage message, Object... args) {
|
|
||||||
((CommandSource) target).sendMessage(translateAndTransform(locale, message, args));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,23 +110,9 @@ public final class VelocityCommandUtil implements CommandUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kickPlayer(Object player, String locale, TranslatableMessage message, Object... args) {
|
public void kickPlayer(Object player, String message) {
|
||||||
cast(player).disconnect(translateAndTransform(locale, message, args));
|
if (player instanceof Player) {
|
||||||
}
|
((Player) player).disconnect(Component.text(message));
|
||||||
|
|
||||||
public Component translateAndTransform(
|
|
||||||
String locale,
|
|
||||||
TranslatableMessage message,
|
|
||||||
Object... args) {
|
|
||||||
return Component.text(message.translateMessage(manager, locale, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Player cast(Object instance) {
|
|
||||||
try {
|
|
||||||
return (Player) instance;
|
|
||||||
} catch (ClassCastException exception) {
|
|
||||||
logger.error("Failed to cast {} to Player", instance.getClass().getName());
|
|
||||||
throw exception;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.velocitypowered.api.network.ProtocolVersion;
|
||||||
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
|
import org.geysermc.floodgate.platform.util.PlatformUtils;
|
||||||
|
|
||||||
|
public final class VelocityPlatformUtils extends PlatformUtils {
|
||||||
|
@Inject
|
||||||
|
private ProxyServer server;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthType authType() {
|
||||||
|
return server.getConfiguration().isOnlineMode() ? AuthType.ONLINE : AuthType.OFFLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String minecraftVersion() {
|
||||||
|
return ProtocolVersion.MAXIMUM_VERSION.getMostRecentSupportedVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serverImplementationName() {
|
||||||
|
return server.getVersion().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user