mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2026-01-06 15:42:03 +00:00
Merge remote-tracking branch 'origin/development' into feature/universal-platform
# Conflicts: # core/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java # spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java
This commit is contained in:
59
.github/workflows/build.yml
vendored
Normal file
59
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Build
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository and submodules
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
|
||||
- name: Build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: build
|
||||
|
||||
- name: Publish to Maven Repository
|
||||
if: ${{ job.status == 'success' && github.repository == 'GeyserMC/Floodgate' }}
|
||||
uses: gradle/gradle-build-action@v2
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||
ORG_GRADLE_PROJECT_geysermcPassword: ${{ secrets.DEPLOY_PASS }}
|
||||
with:
|
||||
arguments: publish
|
||||
|
||||
- name: Publish to Downloads API
|
||||
if: ${{ github.ref_name == 'master' && job.status == 'success' && github.repository == 'GeyserMC/Floodgate' }}
|
||||
shell: bash
|
||||
run: |
|
||||
# Save the private key to a file
|
||||
echo "$DOWNLOADS_PRIVATE_KEY" > id_ecdsa
|
||||
chmod 600 id_ecdsa
|
||||
|
||||
# Get the version from gradle.properties
|
||||
version=$(cat gradle.properties | grep -o "version=[0-9\\.]*" | cut -d"=" -f2)
|
||||
|
||||
# Copy over artifacts
|
||||
scp -B -o StrictHostKeyChecking=no -i id_ecdsa **/build/libs/floodgate-*.jar $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/files/
|
||||
|
||||
# Remove un-needed artifacts
|
||||
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP rm ~/files/floodgate-parent-*.jar ~/files/floodgate-api.jar ~/files/floodgate-core.jar
|
||||
|
||||
# Run the build script
|
||||
ssh -o StrictHostKeyChecking=no -i id_ecdsa $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP ./handleBuild.sh floodgate $version $GITHUB_RUN_ID $GITHUB_SHA
|
||||
|
||||
- name: Notify Discord
|
||||
if: ${{ github.repository == 'GeyserMC/Floodgate' }}
|
||||
uses: Tim203/actions-git-discord-webhook@main
|
||||
with:
|
||||
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
status: ${{ job.status }}
|
||||
82
Jenkinsfile
vendored
82
Jenkinsfile
vendored
@@ -26,87 +26,5 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Deploy') {
|
||||
when {
|
||||
anyOf {
|
||||
branch "master"
|
||||
branch "development"
|
||||
}
|
||||
}
|
||||
|
||||
steps {
|
||||
rtGradleDeployer(
|
||||
id: "GRADLE_DEPLOYER",
|
||||
serverId: "opencollab-artifactory",
|
||||
releaseRepo: "maven-releases",
|
||||
snapshotRepo: "maven-snapshots"
|
||||
)
|
||||
rtGradleResolver(
|
||||
id: "GRADLE_RESOLVER",
|
||||
serverId: "opencollab-artifactory"
|
||||
)
|
||||
rtGradleRun(
|
||||
usesPlugin: true,
|
||||
tool: 'Gradle 7',
|
||||
rootDir: "",
|
||||
useWrapper: true,
|
||||
buildFile: 'build.gradle.kts',
|
||||
tasks: 'artifactoryPublish',
|
||||
deployerId: "GRADLE_DEPLOYER",
|
||||
resolverId: "GRADLE_RESOLVER"
|
||||
)
|
||||
rtPublishBuildInfo(
|
||||
serverId: "opencollab-artifactory"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
def changeLogSets = currentBuild.changeSets
|
||||
def message = "**Changes:**"
|
||||
|
||||
if (changeLogSets.size() == 0) {
|
||||
message += "\n*No changes.*"
|
||||
} else {
|
||||
def repositoryUrl = scm.userRemoteConfigs[0].url.replace(".git", "")
|
||||
def count = 0;
|
||||
def extra = 0;
|
||||
for (int i = 0; i < changeLogSets.size(); i++) {
|
||||
def entries = changeLogSets[i].items
|
||||
for (int j = 0; j < entries.length; j++) {
|
||||
if (count <= 10) {
|
||||
def entry = entries[j]
|
||||
def commitId = entry.commitId.substring(0, 6)
|
||||
message += "\n - [`${commitId}`](${repositoryUrl}/commit/${entry.commitId}) ${entry.msg}"
|
||||
count++
|
||||
} else {
|
||||
extra++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extra != 0) {
|
||||
message += "\n - ${extra} more commits"
|
||||
}
|
||||
}
|
||||
|
||||
env.changes = message
|
||||
}
|
||||
deleteDir()
|
||||
withCredentials([string(credentialsId: 'geyser-discord-webhook', variable: 'DISCORD_WEBHOOK')]) {
|
||||
discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.opencollab.dev/job/GeyserMC/job/Floodgate)", footer: 'Open Collaboration Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK
|
||||
}
|
||||
}
|
||||
success {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'master') {
|
||||
build propagate: false, wait: false, job: 'GeyserMC/Floodgate-Fabric/master', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.unsafe.Unsafe;
|
||||
@@ -148,6 +149,10 @@ public interface FloodgateApi {
|
||||
*/
|
||||
CompletableFuture<String> getGamertagFor(long xuid);
|
||||
|
||||
default FloodgateEventBus getEventBus() {
|
||||
return InstanceHolder.getEventBus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance that manages all the linking.
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,7 @@ package org.geysermc.floodgate.api;
|
||||
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
@@ -35,6 +36,7 @@ import org.geysermc.floodgate.api.packet.PacketHandlers;
|
||||
public final class InstanceHolder {
|
||||
@Getter private static FloodgateApi api;
|
||||
@Getter private static PlayerLink playerLink;
|
||||
@Getter private static FloodgateEventBus eventBus;
|
||||
|
||||
@Getter private static PlatformInjector injector;
|
||||
@Getter private static PacketHandlers packetHandlers;
|
||||
@@ -44,11 +46,12 @@ public final class InstanceHolder {
|
||||
public static boolean set(
|
||||
FloodgateApi floodgateApi,
|
||||
PlayerLink link,
|
||||
FloodgateEventBus floodgateEventBus,
|
||||
PlatformInjector platformInjector,
|
||||
PacketHandlers packetHandlers,
|
||||
HandshakeHandlers handshakeHandlers,
|
||||
UUID key) {
|
||||
|
||||
UUID key
|
||||
) {
|
||||
if (storedKey != null) {
|
||||
if (!storedKey.equals(key)) {
|
||||
return false;
|
||||
@@ -59,14 +62,10 @@ public final class InstanceHolder {
|
||||
|
||||
api = floodgateApi;
|
||||
playerLink = link;
|
||||
eventBus = floodgateEventBus;
|
||||
injector = platformInjector;
|
||||
InstanceHolder.packetHandlers = packetHandlers;
|
||||
InstanceHolder.handshakeHandlers = handshakeHandlers;
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends FloodgateApi> T castApi(Class<T> cast) {
|
||||
return (T) api;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.api.event;
|
||||
|
||||
import org.geysermc.event.bus.EventBus;
|
||||
|
||||
public interface FloodgateEventBus extends EventBus<Object, FloodgateSubscriber<?>> {
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.api.event;
|
||||
|
||||
import org.geysermc.event.subscribe.Subscriber;
|
||||
|
||||
public interface FloodgateSubscriber<T> extends Subscriber<T> {
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.api.event.skin;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.common.returnsreceiver.qual.This;
|
||||
import org.geysermc.event.Cancellable;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
/**
|
||||
* An event that's fired when Floodgate receives a player skin. The event will be cancelled by
|
||||
* default when hasSkin is true, as Floodgate by default only applies skins when the player has no
|
||||
* skin applied yet.
|
||||
*/
|
||||
public interface SkinApplyEvent extends Cancellable {
|
||||
/**
|
||||
* Returns the player that will receive the skin.
|
||||
*/
|
||||
@NonNull FloodgatePlayer player();
|
||||
|
||||
/**
|
||||
* Returns the skin texture currently applied to the player.
|
||||
*/
|
||||
@Nullable SkinData currentSkin();
|
||||
|
||||
/**
|
||||
* Returns the skin texture to be applied to the player.
|
||||
*/
|
||||
@NonNull SkinData newSkin();
|
||||
|
||||
/**
|
||||
* Sets the skin texture to be applied to the player
|
||||
*
|
||||
* @param skinData the skin to apply
|
||||
* @return this
|
||||
*/
|
||||
@This SkinApplyEvent newSkin(@NonNull SkinData skinData);
|
||||
|
||||
interface SkinData {
|
||||
/**
|
||||
* Returns the value of the skin texture.
|
||||
*/
|
||||
@NonNull String value();
|
||||
|
||||
/**
|
||||
* Returns the signature of the skin texture.
|
||||
*/
|
||||
@NonNull String signature();
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,10 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.kyori", "indra-common", "2.0.6")
|
||||
implementation("org.jfrog.buildinfo", "build-info-extractor-gradle", "4.26.1")
|
||||
implementation("net.kyori", "indra-common", "3.0.1")
|
||||
implementation("net.kyori", "indra-git", "3.0.1")
|
||||
implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.1")
|
||||
implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext", "gradle-idea-ext", "1.1.7")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
*/
|
||||
|
||||
object Versions {
|
||||
const val geyserVersion = "2.0.4-SNAPSHOT"
|
||||
const val cumulusVersion = "1.1"
|
||||
const val geyserVersion = "2.0.7-SNAPSHOT"
|
||||
const val cumulusVersion = "1.1.1"
|
||||
const val eventsVersion = "1.0-SNAPSHOT"
|
||||
const val configUtilsVersion = "1.0-SNAPSHOT"
|
||||
const val fastutilVersion = "8.5.3"
|
||||
@@ -33,7 +33,7 @@ object Versions {
|
||||
const val nettyVersion = "4.1.49.Final"
|
||||
const val snakeyamlVersion = "1.28"
|
||||
const val cloudVersion = "1.5.0"
|
||||
const val bstatsVersion = "3.0.0"
|
||||
const val bstatsVersion = "3.0.1"
|
||||
|
||||
const val javaWebsocketVersion = "1.5.2"
|
||||
|
||||
|
||||
@@ -28,9 +28,6 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.kotlin.dsl.the
|
||||
|
||||
fun Project.isSnapshot(): Boolean =
|
||||
version.toString().endsWith("-SNAPSHOT")
|
||||
|
||||
fun Project.fullVersion(): String {
|
||||
var version = version.toString()
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
@@ -42,14 +39,19 @@ fun Project.fullVersion(): String {
|
||||
fun Project.lastCommitHash(): String? =
|
||||
the<IndraGitExtension>().commit()?.name?.substring(0, 7)
|
||||
|
||||
// retrieved from https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
|
||||
// some properties might be specific to Jenkins
|
||||
fun Project.branchName(): String =
|
||||
System.getenv("GIT_BRANCH") ?: "local/dev"
|
||||
fun Project.buildNumber(): Int =
|
||||
Integer.parseInt(System.getenv("BUILD_NUMBER") ?: "-1")
|
||||
the<IndraGitExtension>().branchName() ?: System.getenv("BRANCH_NAME") ?: "local/dev"
|
||||
|
||||
fun Project.buildNumberAsString(): String =
|
||||
fun Project.shouldAddBranchName(): Boolean =
|
||||
System.getenv("IGNORE_BRANCH")?.toBoolean() ?: (branchName() !in arrayOf("master", "local/dev"))
|
||||
|
||||
fun Project.versionWithBranchName(): String =
|
||||
branchName().replace(Regex("[^0-9A-Za-z-_]"), "-") + '-' + version
|
||||
|
||||
fun buildNumber(): Int =
|
||||
System.getenv("BUILD_NUMBER")?.let { Integer.parseInt(it) } ?: -1
|
||||
|
||||
fun buildNumberAsString(): String =
|
||||
buildNumber().takeIf { it != -1 }?.toString() ?: "??"
|
||||
|
||||
val providedDependencies = mutableMapOf<String, MutableSet<Pair<String, Any>>>()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
// id("net.ltgt.errorprone")
|
||||
id("net.kyori.indra")
|
||||
id("net.kyori.indra.git")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,21 @@ dependencies {
|
||||
compileOnly("org.checkerframework", "checker-qual", Versions.checkerQual)
|
||||
}
|
||||
|
||||
indra {
|
||||
github("GeyserMC", "Floodgate") {
|
||||
ci(true)
|
||||
issues(true)
|
||||
scm(true)
|
||||
}
|
||||
mitLicense()
|
||||
|
||||
javaVersions {
|
||||
// without toolchain & strictVersion sun.misc.Unsafe won't be found
|
||||
minimumToolchain(8)
|
||||
strictVersions(true)
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json")) {
|
||||
@@ -22,14 +37,4 @@ tasks {
|
||||
)
|
||||
}
|
||||
}
|
||||
compileJava {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
withSourcesJar()
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseModel
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
import org.jetbrains.gradle.ext.ProjectSettings
|
||||
import org.jetbrains.gradle.ext.TaskTriggersConfig
|
||||
|
||||
plugins {
|
||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||
}
|
||||
|
||||
registerGenerateTemplateTasks()
|
||||
|
||||
fun Project.registerGenerateTemplateTasks() {
|
||||
// main and test
|
||||
extensions.getByType<SourceSetContainer>().all {
|
||||
val javaDestination = layout.buildDirectory.dir("generated/sources/templates/$name")
|
||||
val javaSrcDir = layout.projectDirectory.dir("src/$name/templates")
|
||||
val javaGenerateTask = tasks.register<GenerateSourceTemplates>(
|
||||
getTaskName("template", "sources")
|
||||
) {
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
from(javaSrcDir)
|
||||
into(javaDestination)
|
||||
filter<ReplaceTokens>("tokens" to replacements())
|
||||
}
|
||||
java.srcDir(javaGenerateTask.map { it.outputs })
|
||||
|
||||
val resourcesDestination = layout.buildDirectory.dir("generated/resources/templates/$name")
|
||||
val resourcesSrcDir = layout.projectDirectory.dir("src/$name/resourceTemplates")
|
||||
val resourcesGenerateTask = tasks.register<GenerateResourceTemplates>(
|
||||
getTaskName("template", "resources")
|
||||
) {
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
from(resourcesSrcDir)
|
||||
into(resourcesDestination)
|
||||
filter<ReplaceTokens>("tokens" to replacements())
|
||||
}
|
||||
resources.srcDir(resourcesGenerateTask.map { it.outputs })
|
||||
}
|
||||
|
||||
return configureIdeSync(
|
||||
tasks.register("allTemplateSources") {
|
||||
dependsOn(tasks.withType<GenerateSourceTemplates>())
|
||||
},
|
||||
tasks.register("allTemplateResources") {
|
||||
dependsOn(tasks.withType<GenerateResourceTemplates>())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun Project.configureIdeSync(vararg generateAllTasks: TaskProvider<Task>) {
|
||||
extensions.findByType<EclipseModel> {
|
||||
synchronizationTasks(generateAllTasks)
|
||||
}
|
||||
|
||||
extensions.findByType<IdeaModel> {
|
||||
if (project != null) {
|
||||
(project as ExtensionAware).extensions.configure<ProjectSettings> {
|
||||
(this as ExtensionAware).extensions.configure<TaskTriggersConfig> {
|
||||
afterSync(generateAllTasks)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//todo wasn't able to find something for VS(Code)
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> ExtensionContainer.findByType(noinline action: T.() -> Unit) {
|
||||
val extension = findByType(T::class)
|
||||
if (extension != null) {
|
||||
action.invoke(extension)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenerateAnyTemplates : Copy() {
|
||||
private val replacements = mutableMapOf<String, String>()
|
||||
|
||||
fun replaceToken(key: String, value: () -> Any) {
|
||||
replaceToken(key, value.invoke())
|
||||
}
|
||||
|
||||
fun replaceToken(key: String, value: Any) {
|
||||
replacements[key] = value.toString()
|
||||
}
|
||||
|
||||
fun replacements(): Map<String, String> {
|
||||
return replacements
|
||||
}
|
||||
}
|
||||
|
||||
open class GenerateResourceTemplates : GenerateAnyTemplates()
|
||||
open class GenerateSourceTemplates : GenerateAnyTemplates()
|
||||
@@ -1,34 +1,15 @@
|
||||
plugins {
|
||||
id("floodgate.shadow-conventions")
|
||||
id("com.jfrog.artifactory")
|
||||
id("maven-publish")
|
||||
id("net.kyori.indra.publishing")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
groupId = project.group as String
|
||||
artifactId = project.name
|
||||
version = project.version as String
|
||||
|
||||
artifact(tasks["shadowJar"])
|
||||
artifact(tasks["sourcesJar"])
|
||||
indra {
|
||||
configurePublications {
|
||||
if (shouldAddBranchName()) {
|
||||
version = versionWithBranchName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
artifactory {
|
||||
setContextUrl("https://repo.opencollab.dev/artifactory")
|
||||
publish {
|
||||
repository {
|
||||
setRepoKey(if (isSnapshot()) "maven-snapshots" else "maven-releases")
|
||||
setMavenCompatible(true)
|
||||
}
|
||||
defaults {
|
||||
publications("mavenJava")
|
||||
setPublishArtifacts(true)
|
||||
setPublishPom(true)
|
||||
setPublishIvy(false)
|
||||
}
|
||||
}
|
||||
publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/artifactory/maven-snapshots")
|
||||
publishReleasesTo("geysermc", "https://repo.opencollab.dev/artifactory/maven-releases")
|
||||
}
|
||||
@@ -6,7 +6,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group = "org.geysermc.floodgate"
|
||||
version = "2.2.0-SNAPSHOT"
|
||||
version = property("version")!!
|
||||
description = "Allows Bedrock players to join Java edition servers while keeping the server in online mode"
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@@ -130,8 +130,8 @@ public final class BungeeListener implements Listener {
|
||||
// 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.hasSkin(player)) {
|
||||
skinApplier.applySkin(player, new SkinData("", ""));
|
||||
if (player != null && !player.isLinked()) {
|
||||
skinApplier.applySkin(player, new SkinDataImpl("", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public final class BungeePlatformModule extends AbstractModule {
|
||||
bind(PlatformUtils.class).to(BungeePlatformUtils.class);
|
||||
bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(plugin.getLogger());
|
||||
bind(FloodgateLogger.class).to(JavaUtilFloodgateLogger.class);
|
||||
bind(SkinApplier.class).to(BungeeSkinApplier.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -120,12 +121,6 @@ public final class BungeePlatformModule extends AbstractModule {
|
||||
return new BungeePluginMessageRegistration();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SkinApplier skinApplier(FloodgateLogger logger) {
|
||||
return new BungeeSkinApplier(logger);
|
||||
}
|
||||
|
||||
/*
|
||||
DebugAddon / PlatformInjector
|
||||
*/
|
||||
|
||||
@@ -26,68 +26,55 @@
|
||||
package org.geysermc.floodgate.pluginmessage;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.geysermc.floodgate.util.ReflectionUtils.getConstructor;
|
||||
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
|
||||
import static org.geysermc.floodgate.util.ReflectionUtils.getMethodByName;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.skin.SkinApplyEventImpl;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public final class BungeeSkinApplier implements SkinApplier {
|
||||
private static final Constructor<?> LOGIN_RESULT_CONSTRUCTOR;
|
||||
private static final Field LOGIN_RESULT_FIELD;
|
||||
private static final Method SET_PROPERTIES_METHOD;
|
||||
|
||||
private static final Class<?> PROPERTY_CLASS;
|
||||
private static final Constructor<?> PROPERTY_CONSTRUCTOR;
|
||||
|
||||
static {
|
||||
PROPERTY_CLASS = ReflectionUtils.getClassOrFallbackPrefixed(
|
||||
"protocol.Property", "connection.LoginResult$Property"
|
||||
);
|
||||
|
||||
LOGIN_RESULT_CONSTRUCTOR = getConstructor(
|
||||
LoginResult.class, true,
|
||||
String.class, String.class, Array.newInstance(PROPERTY_CLASS, 0).getClass()
|
||||
);
|
||||
|
||||
LOGIN_RESULT_FIELD = getFieldOfType(InitialHandler.class, LoginResult.class);
|
||||
checkNotNull(LOGIN_RESULT_FIELD, "LoginResult field cannot be null");
|
||||
|
||||
SET_PROPERTIES_METHOD = getMethodByName(LoginResult.class, "setProperties", true);
|
||||
|
||||
PROPERTY_CONSTRUCTOR = ReflectionUtils.getConstructor(
|
||||
PROPERTY_CLASS, true,
|
||||
String.class, String.class, String.class
|
||||
);
|
||||
checkNotNull(PROPERTY_CONSTRUCTOR, "Property constructor cannot be null");
|
||||
}
|
||||
|
||||
private final FloodgateLogger logger;
|
||||
private final ProxyServer server = ProxyServer.getInstance();
|
||||
|
||||
@Inject private EventBus eventBus;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public void applySkin(FloodgatePlayer uuid, SkinData skinData) {
|
||||
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid.getCorrectUniqueId());
|
||||
public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData) {
|
||||
ProxiedPlayer player = server.getPlayer(floodgatePlayer.getCorrectUniqueId());
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
InitialHandler handler = getHandler(player);
|
||||
if (handler == null) {
|
||||
InitialHandler handler;
|
||||
try {
|
||||
handler = (InitialHandler) player.getPendingConnection();
|
||||
} catch (Exception exception) {
|
||||
logger.error("Incompatible Bungeecord fork detected", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -95,57 +82,46 @@ public final class BungeeSkinApplier implements SkinApplier {
|
||||
// expected to be null since LoginResult is the data from hasJoined,
|
||||
// which Floodgate players don't have
|
||||
if (loginResult == null) {
|
||||
// id and name are unused and properties will be overridden
|
||||
loginResult = (LoginResult) ReflectionUtils.newInstance(
|
||||
LOGIN_RESULT_CONSTRUCTOR, null, null, null
|
||||
);
|
||||
// id and name are unused
|
||||
loginResult = new LoginResult(null, null, new Property[0]);
|
||||
ReflectionUtils.setValue(handler, LOGIN_RESULT_FIELD, loginResult);
|
||||
}
|
||||
|
||||
Object property = ReflectionUtils.newInstance(
|
||||
PROPERTY_CONSTRUCTOR,
|
||||
"textures", skinData.getValue(), skinData.getSignature()
|
||||
);
|
||||
Property[] properties = loginResult.getProperties();
|
||||
|
||||
Object propertyArray = Array.newInstance(PROPERTY_CLASS, 1);
|
||||
Array.set(propertyArray, 0, property);
|
||||
SkinData currentSkin = currentSkin(properties);
|
||||
|
||||
ReflectionUtils.invoke(loginResult, SET_PROPERTIES_METHOD, propertyArray);
|
||||
SkinApplyEvent event = new SkinApplyEventImpl(floodgatePlayer, currentSkin, skinData);
|
||||
event.setCancelled(floodgatePlayer.isLinked());
|
||||
|
||||
eventBus.fire(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
loginResult.setProperties(replaceSkin(properties, event.newSkin()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkin(FloodgatePlayer fPlayer) {
|
||||
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(fPlayer.getCorrectUniqueId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InitialHandler handler = getHandler(player);
|
||||
if (handler == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LoginResult loginResult = handler.getLoginProfile();
|
||||
if (loginResult == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Property property : loginResult.getProperties()) {
|
||||
private SkinData currentSkin(Property[] properties) {
|
||||
for (Property property : properties) {
|
||||
if (property.getName().equals("textures")) {
|
||||
if (!property.getValue().isEmpty()) {
|
||||
return true;
|
||||
return new SkinDataImpl(property.getValue(), property.getSignature());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
private InitialHandler getHandler(ProxiedPlayer player) {
|
||||
try {
|
||||
return (InitialHandler) player.getPendingConnection();
|
||||
} catch (Exception exception) {
|
||||
logger.error("Incompatible Bungeecord fork detected", exception);
|
||||
return null;
|
||||
private Property[] replaceSkin(Property[] properties, SkinData skinData) {
|
||||
List<Property> list = new ArrayList<>();
|
||||
for (Property property : properties) {
|
||||
if (!property.getName().equals("textures")) {
|
||||
list.add(property);
|
||||
}
|
||||
}
|
||||
list.add(new Property("textures", skinData.value(), skinData.signature()));
|
||||
return list.toArray(new Property[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import net.kyori.blossom.BlossomExtension
|
||||
|
||||
plugins {
|
||||
id("net.kyori.blossom")
|
||||
id("floodgate.generate-templates")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -26,9 +24,10 @@ provided("io.netty", "netty-codec", Versions.nettyVersion)
|
||||
|
||||
relocate("org.bstats")
|
||||
|
||||
configure<BlossomExtension> {
|
||||
val constantsFile = "src/main/java/org/geysermc/floodgate/util/Constants.java"
|
||||
replaceToken("\${floodgateVersion}", fullVersion(), constantsFile)
|
||||
replaceToken("\${branch}", branchName(), constantsFile)
|
||||
replaceToken("\${buildNumber}", buildNumber(), constantsFile)
|
||||
tasks {
|
||||
templateSources {
|
||||
replaceToken("floodgateVersion", fullVersion())
|
||||
replaceToken("branch", branchName())
|
||||
replaceToken("buildNumber", buildNumber())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.InstanceHolder;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
@@ -41,8 +42,8 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.packet.PacketHandlers;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.PostEnableEvent;
|
||||
import org.geysermc.floodgate.event.ShutdownEvent;
|
||||
import org.geysermc.floodgate.event.lifecycle.PostEnableEvent;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.module.PostEnableModules;
|
||||
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
@@ -67,6 +68,7 @@ public abstract class FloodgatePlatform {
|
||||
InstanceHolder.set(
|
||||
guice.getInstance(FloodgateApi.class),
|
||||
guice.getInstance(PlayerLink.class),
|
||||
guice.getInstance(FloodgateEventBus.class),
|
||||
injector,
|
||||
guice.getInstance(PacketHandlers.class),
|
||||
guice.getInstance(HandshakeHandlers.class),
|
||||
|
||||
@@ -32,10 +32,10 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.geysermc.cumulus.form.Form;
|
||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||
@@ -51,7 +51,7 @@ import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
public class SimpleFloodgateApi implements FloodgateApi {
|
||||
private final Map<UUID, FloodgatePlayer> players = new HashMap<>();
|
||||
private final Map<UUID, FloodgatePlayer> players = new ConcurrentHashMap<>();
|
||||
private final Cache<UUID, FloodgatePlayer> pendingRemove =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.SECONDS)
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.geysermc.floodgate.player.UserAudience;
|
||||
public final class MainCommand extends SubCommands implements FloodgateCommand {
|
||||
public MainCommand() {
|
||||
defineSubCommand(FirewallCheckSubcommand.class);
|
||||
defineSubCommand(VersionSubcommand.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.command.main;
|
||||
|
||||
import static org.geysermc.floodgate.util.Constants.COLOR_CHAR;
|
||||
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.inject.Inject;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.command.WhitelistCommand.Message;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateSubCommand;
|
||||
import org.geysermc.floodgate.player.UserAudience;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
|
||||
public class VersionSubcommand extends FloodgateSubCommand {
|
||||
@Inject
|
||||
private HttpClient httpClient;
|
||||
|
||||
@Inject
|
||||
private FloodgateLogger logger;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "version";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Displays version information about Floodgate";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Permission permission() {
|
||||
return Permission.COMMAND_MAIN_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext<UserAudience> context) {
|
||||
UserAudience sender = context.getSender();
|
||||
sender.sendMessage(String.format(
|
||||
COLOR_CHAR + "7You're currently on " + COLOR_CHAR + "b%s" +
|
||||
COLOR_CHAR + "7 (branch: " + COLOR_CHAR + "b%s" + COLOR_CHAR + "7)\n" +
|
||||
COLOR_CHAR + "eFetching latest build info...",
|
||||
Constants.VERSION, Constants.GIT_BRANCH
|
||||
));
|
||||
|
||||
String baseUrl = String.format(
|
||||
"https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/%s/lastSuccessfulBuild/",
|
||||
Constants.GIT_BRANCH
|
||||
);
|
||||
|
||||
httpClient.asyncGet(
|
||||
baseUrl + "buildNumber",
|
||||
JsonElement.class
|
||||
).whenComplete((result, error) -> {
|
||||
if (error != null) {
|
||||
sender.sendMessage(COLOR_CHAR + "cCould not retrieve latest version info!");
|
||||
error.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
JsonElement response = result.getResponse();
|
||||
|
||||
logger.info(String.valueOf(response));
|
||||
logger.info("{}", result.getHttpCode());
|
||||
|
||||
if (result.getHttpCode() == 404) {
|
||||
sender.sendMessage(
|
||||
COLOR_CHAR + "cGot a 404 (not found) while requesting the latest version." +
|
||||
" Are you using a custom Floodgate version?"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.isCodeOk()) {
|
||||
//todo make it more generic instead of using a Whitelist command strings
|
||||
logger.error(
|
||||
"Got an error from requesting the latest Floodgate version: {}",
|
||||
response.toString()
|
||||
);
|
||||
sender.sendMessage(Message.UNEXPECTED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
int buildNumber = response.getAsInt();
|
||||
|
||||
if (buildNumber > Constants.BUILD_NUMBER) {
|
||||
sender.sendMessage(String.format(
|
||||
COLOR_CHAR + "7There is a newer version of Floodgate available!\n" +
|
||||
COLOR_CHAR + "7You are " + COLOR_CHAR + "e%s " + COLOR_CHAR + "7builds behind.\n" +
|
||||
COLOR_CHAR + "7Download the latest Floodgate version here: " + COLOR_CHAR + "b%s",
|
||||
buildNumber - Constants.BUILD_NUMBER, baseUrl
|
||||
));
|
||||
return;
|
||||
}
|
||||
if (buildNumber == Constants.BUILD_NUMBER) {
|
||||
sender.sendMessage(COLOR_CHAR + "aYou're running the latest version of Floodgate!");
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(COLOR_CHAR + "cCannot check version for custom Floodgate versions!");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import static org.geysermc.floodgate.command.util.PermissionDefault.TRUE;
|
||||
public enum Permission {
|
||||
COMMAND_MAIN("floodgate.command.floodgate", TRUE),
|
||||
COMMAND_MAIN_FIREWALL(COMMAND_MAIN, "firewall", OP),
|
||||
COMMAND_MAIN_VERSION(COMMAND_MAIN, "version", OP),
|
||||
COMMAND_LINK("floodgate.command.linkaccount", TRUE),
|
||||
COMMAND_UNLINK("floodgate.command.unlinkaccount", TRUE),
|
||||
COMMAND_WHITELIST("floodgate.command.fwhitelist", OP),
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.floodgate.event;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@@ -32,9 +33,13 @@ import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.event.bus.impl.EventBusImpl;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.event.subscribe.Subscriber;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.event.FloodgateSubscriber;
|
||||
|
||||
@Singleton
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class EventBus extends EventBusImpl<Object, EventSubscriber<?>> {
|
||||
public final class EventBus extends EventBusImpl<Object, FloodgateSubscriber<?>>
|
||||
implements FloodgateEventBus {
|
||||
@Override
|
||||
protected <H, T, B extends Subscriber<T>> B makeSubscription(
|
||||
@NonNull Class<T> eventClass,
|
||||
|
||||
@@ -30,8 +30,9 @@ import java.util.function.Consumer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.event.subscribe.impl.SubscriberImpl;
|
||||
import org.geysermc.floodgate.api.event.FloodgateSubscriber;
|
||||
|
||||
public final class EventSubscriber<E> extends SubscriberImpl<E> {
|
||||
public final class EventSubscriber<E> extends SubscriberImpl<E> implements FloodgateSubscriber<E> {
|
||||
EventSubscriber(
|
||||
@NonNull Class<E> eventClass,
|
||||
@NonNull Consumer<E> handler,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event;
|
||||
package org.geysermc.floodgate.event.lifecycle;
|
||||
|
||||
public class PostEnableEvent {
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event;
|
||||
package org.geysermc.floodgate.event.lifecycle;
|
||||
|
||||
public class ShutdownEvent {
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Floodgate
|
||||
*/
|
||||
|
||||
package org.geysermc.floodgate.event.skin;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.event.util.AbstractCancellable;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public class SkinApplyEventImpl extends AbstractCancellable implements SkinApplyEvent {
|
||||
private final FloodgatePlayer player;
|
||||
private final SkinData currentSkin;
|
||||
private SkinData newSkin;
|
||||
|
||||
public SkinApplyEventImpl(
|
||||
@NonNull FloodgatePlayer player,
|
||||
@Nullable SkinData currentSkin,
|
||||
@NonNull SkinData newSkin
|
||||
) {
|
||||
this.player = Objects.requireNonNull(player);
|
||||
this.currentSkin = currentSkin;
|
||||
this.newSkin = Objects.requireNonNull(newSkin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull FloodgatePlayer player() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public @Nullable SkinData currentSkin() {
|
||||
return currentSkin;
|
||||
}
|
||||
|
||||
public @NonNull SkinData newSkin() {
|
||||
return newSkin;
|
||||
}
|
||||
|
||||
public SkinApplyEventImpl newSkin(@NonNull SkinData skinData) {
|
||||
this.newSkin = Objects.requireNonNull(skinData);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -26,18 +26,17 @@
|
||||
package org.geysermc.floodgate.inject;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import java.util.WeakHashMap;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
|
||||
public abstract class CommonPlatformInjector implements PlatformInjector {
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final Set<Channel> injectedClients = new HashSet<>();
|
||||
private final Set<Channel> injectedClients =
|
||||
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
|
||||
|
||||
private final Map<Class<?>, InjectorAddon> addons = new HashMap<>();
|
||||
|
||||
@@ -49,6 +48,10 @@ public abstract class CommonPlatformInjector implements PlatformInjector {
|
||||
return injectedClients.remove(channel);
|
||||
}
|
||||
|
||||
public Set<Channel> injectedClients() {
|
||||
return injectedClients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAddon(InjectorAddon addon) {
|
||||
return addons.putIfAbsent(addon.getClass(), addon) == null;
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.database.config.DatabaseConfig;
|
||||
import org.geysermc.floodgate.database.config.DatabaseConfigLoader;
|
||||
import org.geysermc.floodgate.event.ShutdownEvent;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.util.InjectorHolder;
|
||||
|
||||
@Listener
|
||||
|
||||
@@ -51,7 +51,7 @@ import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig.PlayerLinkConfig;
|
||||
import org.geysermc.floodgate.event.ShutdownEvent;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.InjectorHolder;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@@ -38,11 +38,13 @@ import io.netty.util.AttributeKey;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.event.PostOrder;
|
||||
import org.geysermc.floodgate.addon.data.HandshakeHandlersImpl;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.event.FloodgateEventBus;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
import org.geysermc.floodgate.api.link.PlayerLink;
|
||||
@@ -57,17 +59,17 @@ import org.geysermc.floodgate.crypto.Base64Topping;
|
||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.crypto.KeyProducer;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.ShutdownEvent;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
import org.geysermc.floodgate.event.util.ListenerAnnotationMatcher;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
import org.geysermc.floodgate.link.PlayerLinkHolder;
|
||||
import org.geysermc.floodgate.packet.PacketHandlersImpl;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageManager;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinUploadManager;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CommonModule extends AbstractModule {
|
||||
@@ -77,6 +79,7 @@ public class CommonModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(EventBus.class).toInstance(eventBus);
|
||||
bind(FloodgateEventBus.class).to(EventBus.class);
|
||||
// register every class that has the Listener annotation
|
||||
bindListener(new ListenerAnnotationMatcher(), new TypeListener() {
|
||||
@Override
|
||||
@@ -86,8 +89,19 @@ public class CommonModule extends AbstractModule {
|
||||
});
|
||||
|
||||
ExecutorService commonPool = Executors.newCachedThreadPool();
|
||||
eventBus.subscribe(ShutdownEvent.class, ignored -> commonPool.shutdown(), PostOrder.LAST);
|
||||
bind(ExecutorService.class).annotatedWith(Names.named("commonPool")).toInstance(commonPool);
|
||||
ScheduledExecutorService commonScheduledPool = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
eventBus.subscribe(ShutdownEvent.class, ignored -> {
|
||||
commonPool.shutdown();
|
||||
commonScheduledPool.shutdown();
|
||||
}, PostOrder.LAST);
|
||||
|
||||
bind(ExecutorService.class)
|
||||
.annotatedWith(Names.named("commonPool"))
|
||||
.toInstance(commonPool);
|
||||
bind(ScheduledExecutorService.class)
|
||||
.annotatedWith(Names.named("commonScheduledPool"))
|
||||
.toInstance(commonScheduledPool);
|
||||
|
||||
bind(HttpClient.class).in(Singleton.class);
|
||||
|
||||
@@ -152,10 +166,11 @@ public class CommonModule extends AbstractModule {
|
||||
FloodgateConfig config,
|
||||
SkinUploadManager skinUploadManager,
|
||||
@Named("playerAttribute") AttributeKey<FloodgatePlayer> playerAttribute,
|
||||
FloodgateLogger logger) {
|
||||
FloodgateLogger logger,
|
||||
LanguageManager languageManager) {
|
||||
|
||||
return new FloodgateHandshakeHandler(handshakeHandlers, api, cipher, config,
|
||||
skinUploadManager, playerAttribute, logger);
|
||||
skinUploadManager, playerAttribute, logger, languageManager);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -164,15 +179,6 @@ public class CommonModule extends AbstractModule {
|
||||
return new PluginMessageManager();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SkinUploadManager skinUploadManager(
|
||||
FloodgateApi api,
|
||||
SkinApplier skinApplier,
|
||||
FloodgateLogger logger) {
|
||||
return new SkinUploadManager(api, skinApplier, logger);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("gitBranch")
|
||||
|
||||
@@ -34,14 +34,10 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.command.util.Permission;
|
||||
import org.geysermc.floodgate.event.ShutdownEvent;
|
||||
import org.geysermc.floodgate.news.data.AnnouncementData;
|
||||
import org.geysermc.floodgate.news.data.BuildSpecificData;
|
||||
import org.geysermc.floodgate.news.data.CheckAfterData;
|
||||
@@ -52,10 +48,11 @@ import org.geysermc.floodgate.util.HttpClient;
|
||||
import org.geysermc.floodgate.util.HttpClient.HttpResponse;
|
||||
|
||||
@AutoBind
|
||||
@Listener
|
||||
public class NewsChecker {
|
||||
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
|
||||
private final Map<Integer, NewsItem> activeNewsItems = new HashMap<>();
|
||||
@Inject
|
||||
@Named("commonScheduledPool")
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
@Inject
|
||||
private CommandUtil commandUtil;
|
||||
@@ -199,15 +196,6 @@ public class NewsChecker {
|
||||
activateNews(item);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onShutdown(ShutdownEvent ignored) {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
private void activateNews(NewsItem item) {
|
||||
for (NewsItemAction action : item.getActions()) {
|
||||
handleNewsItem(null, item, action);
|
||||
|
||||
@@ -52,7 +52,9 @@ import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.crypto.FloodgateCipher;
|
||||
import org.geysermc.floodgate.skin.SkinUploadManager;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
import org.geysermc.floodgate.util.InvalidFormatException;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
import org.geysermc.floodgate.util.LinkedPlayer;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@@ -64,6 +66,7 @@ public final class FloodgateHandshakeHandler {
|
||||
private final SkinUploadManager skinUploadManager;
|
||||
private final AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
private final FloodgateLogger logger;
|
||||
private final LanguageManager languageManager;
|
||||
|
||||
public FloodgateHandshakeHandler(
|
||||
HandshakeHandlersImpl handshakeHandlers,
|
||||
@@ -72,7 +75,8 @@ public final class FloodgateHandshakeHandler {
|
||||
FloodgateConfig config,
|
||||
SkinUploadManager skinUploadManager,
|
||||
AttributeKey<FloodgatePlayer> playerAttribute,
|
||||
FloodgateLogger logger) {
|
||||
FloodgateLogger logger,
|
||||
LanguageManager languageManager) {
|
||||
|
||||
this.handshakeHandlers = handshakeHandlers;
|
||||
this.api = api;
|
||||
@@ -81,6 +85,7 @@ public final class FloodgateHandshakeHandler {
|
||||
this.skinUploadManager = skinUploadManager;
|
||||
this.playerAttribute = playerAttribute;
|
||||
this.logger = logger;
|
||||
this.languageManager = languageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,7 +216,12 @@ public final class FloodgateHandshakeHandler {
|
||||
linkedPlayer != null ? linkedPlayer.clone() : null, hostname);
|
||||
|
||||
if (config.getPlayerLink().isRequireLink() && linkedPlayer == null) {
|
||||
handshakeData.setDisconnectReason("floodgate.core.not_linked");
|
||||
String reason = languageManager.getString(
|
||||
"floodgate.core.not_linked",
|
||||
bedrockData.getLanguageCode(),
|
||||
Constants.LINK_INFO_URL
|
||||
);
|
||||
handshakeData.setDisconnectReason(reason);
|
||||
}
|
||||
|
||||
handshakeHandlers.callHandshakeHandlers(handshakeData);
|
||||
|
||||
@@ -32,7 +32,6 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.InstanceHolder;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
@@ -72,7 +71,7 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
|
||||
private Map<String, PropertyKey> stringToPropertyKey;
|
||||
|
||||
static FloodgatePlayerImpl from(BedrockData data, HandshakeData handshakeData) {
|
||||
FloodgateApi api = InstanceHolder.getApi();
|
||||
FloodgateApi api = FloodgateApi.getInstance();
|
||||
|
||||
UUID javaUniqueId = Utils.getJavaUuid(data.getXuid());
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
package org.geysermc.floodgate.pluginmessage.channel;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.inject.Inject;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
@@ -36,7 +35,7 @@ import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageChannel;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
|
||||
public class SkinChannel implements PluginMessageChannel {
|
||||
@Inject private FloodgateApi api;
|
||||
@@ -89,18 +88,10 @@ public class SkinChannel implements PluginMessageChannel {
|
||||
return Result.kick("Got invalid skin data");
|
||||
}
|
||||
|
||||
if (floodgatePlayer.isLinked() || skinApplier.hasSkin(floodgatePlayer)) {
|
||||
return Result.handled();
|
||||
}
|
||||
|
||||
String value = split[0];
|
||||
String signature = split[1];
|
||||
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("value", value);
|
||||
result.addProperty("signature", signature);
|
||||
|
||||
SkinData skinData = new SkinData(value, signature);
|
||||
SkinDataImpl skinData = new SkinDataImpl(value, signature);
|
||||
|
||||
floodgatePlayer.addProperty(PropertyKey.SKIN_UPLOADED, skinData);
|
||||
skinApplier.applySkin(floodgatePlayer, skinData);
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.floodgate.skin;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public interface SkinApplier {
|
||||
@@ -34,14 +36,5 @@ public interface SkinApplier {
|
||||
* @param floodgatePlayer player to apply skin to
|
||||
* @param skinData data for skin to apply to player
|
||||
*/
|
||||
void applySkin(FloodgatePlayer floodgatePlayer, SkinData skinData);
|
||||
|
||||
/**
|
||||
* Check if a {@link FloodgatePlayer player} currently
|
||||
* has a skin applied.
|
||||
*
|
||||
* @param floodgatePlayer player to check skin of
|
||||
* @return if player has a skin
|
||||
*/
|
||||
boolean hasSkin(FloodgatePlayer floodgatePlayer);
|
||||
void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData);
|
||||
}
|
||||
|
||||
@@ -26,22 +26,33 @@
|
||||
package org.geysermc.floodgate.skin;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.Objects;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class SkinData {
|
||||
public class SkinDataImpl implements SkinData {
|
||||
private final String value;
|
||||
private final String signature;
|
||||
|
||||
public SkinDataImpl(@NonNull String value, @NonNull String signature) {
|
||||
this.value = Objects.requireNonNull(value);
|
||||
this.signature = Objects.requireNonNull(signature);
|
||||
}
|
||||
|
||||
public static SkinData from(JsonObject data) {
|
||||
if (data.has("signature") && !data.get("signature").isJsonNull()) {
|
||||
return new SkinData(
|
||||
data.get("value").getAsString(),
|
||||
data.get("signature").getAsString()
|
||||
);
|
||||
}
|
||||
return new SkinData(data.get("value").getAsString(), null);
|
||||
return new SkinDataImpl(
|
||||
data.get("value").getAsString(),
|
||||
data.get("signature").getAsString()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String signature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
@@ -25,21 +25,26 @@
|
||||
|
||||
package org.geysermc.floodgate.skin;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.event.lifecycle.ShutdownEvent;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Listener
|
||||
@Singleton
|
||||
public final class SkinUploadManager {
|
||||
private final Int2ObjectMap<SkinUploadSocket> connections =
|
||||
Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
|
||||
|
||||
private final FloodgateApi api;
|
||||
private final SkinApplier applier;
|
||||
private final FloodgateLogger logger;
|
||||
@Inject private FloodgateApi api;
|
||||
@Inject private SkinApplier applier;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
public void addConnectionIfNeeded(int id, String verifyCode) {
|
||||
connections.computeIfAbsent(id, (ignored) -> {
|
||||
@@ -53,4 +58,16 @@ public final class SkinUploadManager {
|
||||
public void removeConnection(int id, SkinUploadSocket socket) {
|
||||
connections.remove(id, socket);
|
||||
}
|
||||
|
||||
public void closeAllSockets() {
|
||||
for (SkinUploadSocket socket : connections.values()) {
|
||||
socket.close();
|
||||
}
|
||||
connections.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onShutdown(ShutdownEvent ignored) {
|
||||
closeAllSockets();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.net.URI;
|
||||
import javax.net.ssl.SSLException;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.player.PropertyKey;
|
||||
@@ -61,8 +62,8 @@ final class SkinUploadSocket extends WebSocketClient {
|
||||
SkinUploadManager uploadManager,
|
||||
FloodgateApi api,
|
||||
SkinApplier applier,
|
||||
FloodgateLogger logger) {
|
||||
|
||||
FloodgateLogger logger
|
||||
) {
|
||||
super(getWebsocketUri(id, verifyCode));
|
||||
this.id = id;
|
||||
this.verifyCode = verifyCode;
|
||||
@@ -83,7 +84,7 @@ final class SkinUploadSocket extends WebSocketClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ServerHandshake handshakedata) {
|
||||
public void onOpen(ServerHandshake ignored) {
|
||||
setConnectionLostTimeout(11);
|
||||
}
|
||||
|
||||
@@ -114,10 +115,14 @@ final class SkinUploadSocket extends WebSocketClient {
|
||||
player.getCorrectUsername());
|
||||
return;
|
||||
}
|
||||
if (!player.isLinked() && !applier.hasSkin(player)) {
|
||||
SkinData skinData = SkinData.from(message.getAsJsonObject("data"));
|
||||
|
||||
SkinData skinData = SkinDataImpl.from(message.getAsJsonObject("data"));
|
||||
applier.applySkin(player, skinData);
|
||||
|
||||
// legacy stuff,
|
||||
// will be removed shortly after or during the Floodgate-Geyser integration
|
||||
if (!player.isLinked()) {
|
||||
player.addProperty(PropertyKey.SKIN_UPLOADED, skinData);
|
||||
applier.applySkin(player, skinData);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -57,6 +57,10 @@ public class HttpClient {
|
||||
return CompletableFuture.supplyAsync(() -> get(urlString), executorService);
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<HttpResponse<T>> asyncGet(String urlString, Class<T> response) {
|
||||
return CompletableFuture.supplyAsync(() -> get(urlString, response), executorService);
|
||||
}
|
||||
|
||||
public DefaultHttpResponse get(String urlString) {
|
||||
return readDefaultResponse(request(urlString));
|
||||
}
|
||||
@@ -99,12 +103,14 @@ public class HttpClient {
|
||||
return new HttpResponse<>(-1, null);
|
||||
}
|
||||
|
||||
int responseCode = -1;
|
||||
try {
|
||||
int responseCode = connection.getResponseCode();
|
||||
responseCode = connection.getResponseCode();
|
||||
T response = gson.fromJson(streamReader, clazz);
|
||||
return new HttpResponse<>(responseCode, response);
|
||||
} catch (Exception ignored) {
|
||||
return new HttpResponse<>(-1, null);
|
||||
// e.g. when the response isn't JSON
|
||||
return new HttpResponse<>(responseCode, null);
|
||||
} finally {
|
||||
try {
|
||||
streamReader.close();
|
||||
@@ -149,11 +155,7 @@ public class HttpClient {
|
||||
try {
|
||||
stream = connection.getInputStream();
|
||||
} catch (Exception exception) {
|
||||
try {
|
||||
stream = connection.getErrorStream();
|
||||
} catch (Exception exception1) {
|
||||
throw new RuntimeException("Both the input and the error stream failed?!");
|
||||
}
|
||||
stream = connection.getErrorStream();
|
||||
}
|
||||
|
||||
// it's null for example when it couldn't connect to the server
|
||||
|
||||
@@ -26,13 +26,16 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.geysermc.event.Listener;
|
||||
import org.geysermc.event.subscribe.Subscribe;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.event.PostEnableEvent;
|
||||
import org.geysermc.floodgate.event.lifecycle.PostEnableEvent;
|
||||
|
||||
@AutoBind
|
||||
@Listener
|
||||
@@ -41,6 +44,9 @@ public final class PostEnableMessages {
|
||||
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private FloodgateLogger logger;
|
||||
@Inject
|
||||
@Named("commonScheduledPool")
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
public void add(String[] message, Object... args) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -93,14 +99,11 @@ public final class PostEnableMessages {
|
||||
|
||||
@Subscribe
|
||||
public void onPostEnable(PostEnableEvent ignored) {
|
||||
new Thread(() -> {
|
||||
// normally proxies don't have a lot of plugins, so proxies don't need to sleep as long
|
||||
try {
|
||||
Thread.sleep(config.isProxy() ? 2000 : 5000);
|
||||
} catch (InterruptedException ignored1) {
|
||||
}
|
||||
|
||||
messages.forEach(logger::warn);
|
||||
}).start();
|
||||
// normally proxies don't have a lot of plugins, so proxies don't need to sleep as long
|
||||
executorService.schedule(
|
||||
() -> messages.forEach(logger::warn),
|
||||
config.isProxy() ? 2 : 5,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ public final class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a field and cast it to <T>.
|
||||
* Get the value of a field and cast it to T.
|
||||
*
|
||||
* @param instance the instance to get the value from
|
||||
* @param field the field to get the value from
|
||||
@@ -301,7 +301,7 @@ public final class ReflectionUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a field and cast it to <T>.
|
||||
* Get the value of a field and cast it to T.
|
||||
*
|
||||
* @param instance the instance to get the value from
|
||||
* @param fieldName the field to get the value from
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
@@ -75,7 +76,7 @@ public class Utils {
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
properties.load(is);
|
||||
properties.load(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Failed to read properties file", e);
|
||||
}
|
||||
|
||||
Submodule core/src/main/resources/languages updated: 38cb4a52df...204f4fe492
@@ -26,12 +26,12 @@
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
public final class Constants {
|
||||
public static final String VERSION = "${floodgateVersion}";
|
||||
public static final int BUILD_NUMBER = Integer.parseInt("${buildNumber}");
|
||||
public static final String GIT_BRANCH = "${branch}";
|
||||
public static final String VERSION = "@floodgateVersion@";
|
||||
public static final int BUILD_NUMBER = Integer.parseInt("@buildNumber@");
|
||||
public static final String GIT_BRANCH = "@branch@";
|
||||
public static final int METRICS_ID = 14649;
|
||||
|
||||
public static final char COLOR_CHAR = '§';
|
||||
public static final char COLOR_CHAR = '\u00A7';
|
||||
|
||||
public static final boolean DEBUG_MODE = false;
|
||||
public static final boolean PRINT_ALL_PACKETS = false;
|
||||
@@ -55,7 +55,7 @@ public final class Constants {
|
||||
|
||||
public static final String NTP_SERVER = "time.cloudflare.com";
|
||||
public static final String INTERNAL_ERROR_MESSAGE =
|
||||
"An internal error happened while handling Floodgate data." +
|
||||
"An internal error happened while handling Floodgate data." +
|
||||
" Try logging in again or contact a server administrator if the issue persists.";
|
||||
public static final String UNSUPPORTED_DATA_VERSION =
|
||||
"Received an unsupported Floodgate data version." +
|
||||
@@ -1,3 +1,5 @@
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
version=2.2.2-SNAPSHOT
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
10
gradlew
vendored
10
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright <EFBFBD> 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -32,10 +32,10 @@
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
# * expansions <EFBFBD>$var<EFBFBD>, <EFBFBD>${var}<EFBFBD>, <EFBFBD>${var:-default}<EFBFBD>, <EFBFBD>${var+SET}<EFBFBD>,
|
||||
# <EFBFBD>${var#prefix}<EFBFBD>, <EFBFBD>${var%suffix}<EFBFBD>, and <EFBFBD>$( cmd )<EFBFBD>;
|
||||
# * compound commands having a testable exit status, especially <EFBFBD>case<EFBFBD>;
|
||||
# * various built-in commands including <EFBFBD>command<EFBFBD>, <EFBFBD>set<EFBFBD>, and <EFBFBD>ulimit<EFBFBD>.
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
@@ -55,7 +56,8 @@ pluginManagement {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
plugins {
|
||||
id("net.kyori.blossom") version "1.2.0"
|
||||
id("net.kyori.indra")
|
||||
id("net.kyori.indra.git")
|
||||
}
|
||||
includeBuild("build-logic")
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.floodgate.inject.spigot;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -36,13 +38,15 @@ import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
import org.geysermc.floodgate.util.ClassNames;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public final class SpigotInjector extends CommonPlatformInjector {
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
private Object serverConnection;
|
||||
private String injectedFieldName;
|
||||
|
||||
@@ -126,30 +130,43 @@ public final class SpigotInjector extends CommonPlatformInjector {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove injection from clients
|
||||
for (Channel channel : getInjectedClients()) {
|
||||
removeAddonsCall(channel);
|
||||
}
|
||||
getInjectedClients().clear();
|
||||
|
||||
// and change the list back to the original
|
||||
// let's change the list back to the original first
|
||||
// so that new connections are not handled through our custom list
|
||||
Object serverConnection = getServerConnection();
|
||||
if (serverConnection != null) {
|
||||
Field field = ReflectionUtils.getField(serverConnection.getClass(), injectedFieldName);
|
||||
List<?> list = (List<?>) ReflectionUtils.getValue(serverConnection, field);
|
||||
Object value = ReflectionUtils.getValue(serverConnection, field);
|
||||
|
||||
if (list instanceof CustomList) {
|
||||
CustomList customList = (CustomList) list;
|
||||
if (value instanceof CustomList) {
|
||||
// all we have to do is replace the list with the original list.
|
||||
// the original list is up-to-date, so we don't have to clear/add/whatever anything
|
||||
CustomList customList = (CustomList) value;
|
||||
ReflectionUtils.setValue(serverConnection, field, customList.getOriginalList());
|
||||
customList.clear();
|
||||
customList.addAll(list);
|
||||
return;
|
||||
}
|
||||
|
||||
// we could replace all references of CustomList that are directly in 'value', but that
|
||||
// only brings you so far. ProtocolLib for example stores the original value
|
||||
// (which would be our CustomList e.g.) in a separate object
|
||||
logger.debug(
|
||||
"Unable to remove all references of Floodgate due to {}! ",
|
||||
value.getClass().getName()
|
||||
);
|
||||
}
|
||||
|
||||
// remove injection from clients
|
||||
for (Channel channel : injectedClients()) {
|
||||
removeAddonsCall(channel);
|
||||
}
|
||||
|
||||
//todo make sure that all references are removed from the channels,
|
||||
// except from one AttributeKey with Floodgate player data which could be used
|
||||
// after reloading
|
||||
|
||||
injected = false;
|
||||
}
|
||||
|
||||
public Object getServerConnection() {
|
||||
private Object getServerConnection() {
|
||||
if (serverConnection != null) {
|
||||
return serverConnection;
|
||||
}
|
||||
|
||||
@@ -60,9 +60,12 @@ public final class SpigotPlatformModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SpigotPlugin.class).toInstance(plugin);
|
||||
bind(PlatformUtils.class).to(SpigotPlatformUtils.class);
|
||||
bind(CommonPlatformInjector.class).to(SpigotInjector.class);
|
||||
bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(plugin.getLogger());
|
||||
bind(FloodgateLogger.class).to(JavaUtilFloodgateLogger.class);
|
||||
bind(SkinApplier.class).to(SpigotSkinApplier.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -95,12 +98,6 @@ public final class SpigotPlatformModule extends AbstractModule {
|
||||
DebugAddon / PlatformInjector
|
||||
*/
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommonPlatformInjector platformInjector() {
|
||||
return new SpigotInjector();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("packetEncoder")
|
||||
public String packetEncoder() {
|
||||
@@ -141,12 +138,6 @@ public final class SpigotPlatformModule extends AbstractModule {
|
||||
return new SpigotPluginMessageRegistration(plugin);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SkinApplier skinApplier(SpigotVersionSpecificMethods versionSpecificMethods) {
|
||||
return new SpigotSkinApplier(versionSpecificMethods, plugin);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SpigotVersionSpecificMethods versionSpecificMethods() {
|
||||
|
||||
@@ -25,67 +25,48 @@
|
||||
|
||||
package org.geysermc.floodgate.pluginmessage;
|
||||
|
||||
import com.destroystokyo.paper.profile.ProfileProperty;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.skin.SkinApplyEventImpl;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
import org.geysermc.floodgate.util.ClassNames;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
import org.geysermc.floodgate.util.SpigotVersionSpecificMethods;
|
||||
|
||||
@Singleton
|
||||
public final class SpigotSkinApplier implements SkinApplier {
|
||||
private final SpigotVersionSpecificMethods versionSpecificMethods;
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public SpigotSkinApplier(
|
||||
SpigotVersionSpecificMethods versionSpecificMethods,
|
||||
JavaPlugin plugin) {
|
||||
this.versionSpecificMethods = versionSpecificMethods;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@Inject private SpigotVersionSpecificMethods versionSpecificMethods;
|
||||
@Inject private JavaPlugin plugin;
|
||||
@Inject private EventBus eventBus;
|
||||
|
||||
@Override
|
||||
public void applySkin(FloodgatePlayer floodgatePlayer, SkinData skinData) {
|
||||
public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData) {
|
||||
applySkin0(floodgatePlayer, skinData, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkin(FloodgatePlayer floodgatePlayer) {
|
||||
Player player = Bukkit.getPlayer(floodgatePlayer.getCorrectUniqueId());
|
||||
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ProfileProperty property : player.getPlayerProfile().getProperties()) {
|
||||
if (property.getName().equals("textures")) {
|
||||
if (!property.getValue().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void applySkin0(FloodgatePlayer floodgatePlayer, SkinData skinData, boolean firstTry) {
|
||||
Player player = Bukkit.getPlayer(floodgatePlayer.getCorrectUniqueId());
|
||||
|
||||
// player is probably not logged in yet
|
||||
if (player == null) {
|
||||
if (firstTry) {
|
||||
Bukkit.getScheduler().runTaskLater(plugin,
|
||||
() -> {
|
||||
if (hasSkin(floodgatePlayer)) {
|
||||
applySkin0(floodgatePlayer, skinData, false);
|
||||
}
|
||||
},
|
||||
10 * 20);
|
||||
Bukkit.getScheduler().runTaskLater(
|
||||
plugin,
|
||||
() -> applySkin0(floodgatePlayer, skinData, false),
|
||||
10 * 20
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -96,11 +77,22 @@ public final class SpigotSkinApplier implements SkinApplier {
|
||||
throw new IllegalStateException("The GameProfile cannot be null! " + player.getName());
|
||||
}
|
||||
|
||||
// Need to be careful here - getProperties() returns an authlib PropertyMap, which extends
|
||||
// MultiMap from Guava. Floodgate relocates Guava.
|
||||
PropertyMap properties = profile.getProperties();
|
||||
|
||||
properties.removeAll("textures");
|
||||
Property property = new Property("textures", skinData.getValue(), skinData.getSignature());
|
||||
properties.put("textures", property);
|
||||
SkinData currentSkin = currentSkin(properties);
|
||||
|
||||
SkinApplyEvent event = new SkinApplyEventImpl(floodgatePlayer, currentSkin, skinData);
|
||||
event.setCancelled(floodgatePlayer.isLinked());
|
||||
|
||||
eventBus.fire(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
replaceSkin(properties, event.newSkin());
|
||||
|
||||
// By running as a task, we don't run into async issues
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
@@ -112,4 +104,19 @@ public final class SpigotSkinApplier implements SkinApplier {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private SkinData currentSkin(PropertyMap properties) {
|
||||
for (Property texture : properties.get("textures")) {
|
||||
if (!texture.getValue().isEmpty()) {
|
||||
return new SkinDataImpl(texture.getValue(), texture.getSignature());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void replaceSkin(PropertyMap properties, SkinData skinData) {
|
||||
properties.removeAll("textures");
|
||||
Property property = new Property("textures", skinData.value(), skinData.signature());
|
||||
properties.put("textures", property);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,11 @@ import org.slf4j.Logger;
|
||||
@Singleton
|
||||
public final class Slf4jFloodgateLogger implements FloodgateLogger {
|
||||
@Inject private Logger logger;
|
||||
@Inject private LanguageManager languageManager;
|
||||
private LanguageManager languageManager;
|
||||
|
||||
@Inject
|
||||
private void init(FloodgateConfig config) {
|
||||
private void init(LanguageManager languageManager, FloodgateConfig config) {
|
||||
this.languageManager = languageManager;
|
||||
if (config.isDebug() && !logger.isDebugEnabled()) {
|
||||
Configurator.setLevel(logger.getName(), Level.DEBUG);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public final class VelocityPlatformModule extends AbstractModule {
|
||||
bind(CommandUtil.class).to(VelocityCommandUtil.class);
|
||||
bind(PlatformUtils.class).to(VelocityPlatformUtils.class);
|
||||
bind(FloodgateLogger.class).to(Slf4jFloodgateLogger.class);
|
||||
bind(SkinApplier.class).to(VelocitySkinApplier.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -112,12 +113,6 @@ public final class VelocityPlatformModule extends AbstractModule {
|
||||
return new VelocityPluginMessageRegistration(proxy);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public SkinApplier skinApplier(ProxyServer server) {
|
||||
return new VelocitySkinApplier(server);
|
||||
}
|
||||
|
||||
/*
|
||||
DebugAddon / PlatformInjector
|
||||
*/
|
||||
|
||||
@@ -25,43 +25,60 @@
|
||||
|
||||
package org.geysermc.floodgate.util;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.util.GameProfile.Property;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent;
|
||||
import org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.event.EventBus;
|
||||
import org.geysermc.floodgate.event.skin.SkinApplyEventImpl;
|
||||
import org.geysermc.floodgate.skin.SkinApplier;
|
||||
import org.geysermc.floodgate.skin.SkinData;
|
||||
import org.geysermc.floodgate.skin.SkinDataImpl;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Singleton
|
||||
public class VelocitySkinApplier implements SkinApplier {
|
||||
private final ProxyServer server;
|
||||
@Inject private ProxyServer server;
|
||||
@Inject private EventBus eventBus;
|
||||
|
||||
@Override
|
||||
public void applySkin(FloodgatePlayer floodgatePlayer, SkinData skinData) {
|
||||
public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData) {
|
||||
server.getPlayer(floodgatePlayer.getCorrectUniqueId()).ifPresent(player -> {
|
||||
List<Property> properties = new ArrayList<>(player.getGameProfileProperties());
|
||||
properties.add(new Property("textures", skinData.getValue(), skinData.getSignature()));
|
||||
|
||||
SkinData currentSkin = currentSkin(properties);
|
||||
|
||||
SkinApplyEvent event = new SkinApplyEventImpl(floodgatePlayer, currentSkin, skinData);
|
||||
event.setCancelled(floodgatePlayer.isLinked());
|
||||
|
||||
eventBus.fire(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
replaceSkin(properties, event.newSkin());
|
||||
player.setGameProfileProperties(properties);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkin(FloodgatePlayer floodgatePlayer) {
|
||||
Optional<Player> player = server.getPlayer(floodgatePlayer.getCorrectUniqueId());
|
||||
|
||||
if (player.isPresent()) {
|
||||
for (Property property : player.get().getGameProfileProperties()) {
|
||||
if (property.getName().equals("textures")) {
|
||||
if (!property.getValue().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
private SkinData currentSkin(List<Property> properties) {
|
||||
for (Property property : properties) {
|
||||
if (property.getName().equals("textures")) {
|
||||
if (!property.getValue().isEmpty()) {
|
||||
return new SkinDataImpl(property.getValue(), property.getSignature());
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void replaceSkin(List<Property> properties, SkinData skinData) {
|
||||
properties.removeIf(property -> property.getName().equals("textures"));
|
||||
properties.add(new Property("textures", skinData.value(), skinData.signature()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user