1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Support neoforge platform, move to using architectury (#125)

* rebase

* split fabric/neoforge specific code

* more progress

* dependency configuration

* exclude more junk

* neo, you make me go insane

* neoforge seems to work!

* some minor cleanup, add neoforge command module 

* mixin config plugin shenanigans

* fix language strings loading

* some cleanup, yeet Jenkinsfile

* proper repository declaration using plugin, target jitpack branch

* oops

* address reviews

* Update for 1.21

* some minor fixes

* Fix modrinth task, add floodgate version command mixin to disable version checking, update to loom 1.7, update cloud, update floodgate core to not use my branch

* oops

* what on earth is going on now

* neoforge works again!!!!!!!!!

* Address review, dont rely on locals in mixin

* modrinth version/name changes, similar to geyser

* Update README.md

Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com>

* Improve handling of PayloadRegistrar

this took years off my life

* address review

* Move blossom version declaration to libs.versions.toml

* remove unused versions from catalogue

* Only run modrinth task if successful & on Geyser repo

* cleanup & fix gh actions building/archiving

* run and uses are different steps

---------

Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com>
This commit is contained in:
chris
2024-09-11 02:00:55 +08:00
committed by GitHub
parent 4502fd20cd
commit 0ef06597f1
76 changed files with 1507 additions and 586 deletions

View File

@@ -5,23 +5,27 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@72f2cec99f417b1a1c5e2e88945068983b7965f9
- uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4
- uses: actions/setup-java@4075bfc1b51bf22876335ae1cd589602d60d8758
- name: Setup Gradle
uses: GeyserMC/actions/setup-gradle-composite@master
with:
distribution: 'temurin'
java-version: 21
setup-java_java-version: 21
- name: Build Floodgate-Modded
run: ./gradlew build
- name: Publish to Modrinth
if: ${{ success() && github.repository == 'GeyserMC/Floodgate-Modded' && github.ref_name == 'master' }}
uses: gradle/gradle-build-action@3bfe3a46584a206fb8361cdedd0647b0c4204232
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
with:
arguments: modrinth
gradle-home-cache-cleanup: true
- name: Archive Artifacts
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
uses: GeyserMC/actions/upload-multi-artifact@master
if: success()
with:
name: Floodgate Fabric
path: build/libs/floodgate-fabric.jar
if-no-files-found: error
artifacts: |
Floodgate-Fabric:fabric/build/libs/floodgate-fabric.jar
Floodgate-NeoForge:neoforge/build/libs/floodgate-neoforge.jar

View File

@@ -1,4 +1,11 @@
# Floodgate-Fabric
Fabric port of the hybrid mode plugin to allow for connections from Geyser to join online mode servers.
# Floodgate-Modded
Fabric and NeoForge ports of the hybrid mode plugin to allow for connections from Geyser to join online mode servers.
Download: https://ci.opencollab.dev/job/GeyserMC/job/Floodgate-Fabric/job/master/
Hybrid mode mod to allow for connections from Geyser to join online mode servers.
Geyser is an open collaboration project by CubeCraft Games.
See the 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.
Wiki: https://wiki.geysermc.org/floodgate/
Download: https://modrinth.com/mod/floodgate

View File

@@ -0,0 +1,23 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
mavenCentral()
maven("https://maven.architectury.dev/")
maven("https://maven.fabricmc.net/")
maven("https://maven.neoforged.net/releases/")
}
dependencies {
// Used to access version catalogue from the convention plugins
// this is OK as long as the same version catalog is used in the main build and build-logic
// see https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
implementation(libs.indra)
implementation(libs.shadow)
implementation(libs.architectury.plugin)
implementation(libs.architectury.loom)
implementation(libs.minotaur)
}

View File

@@ -0,0 +1,11 @@
@file:Suppress("UnstableApiUsage")
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
rootProject.name = "build-logic"

View File

@@ -0,0 +1,6 @@
import org.gradle.accessors.dm.LibrariesForLibs
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
val Project.libs: LibrariesForLibs
get() = rootProject.extensions.getByType()

View File

@@ -0,0 +1,36 @@
import org.gradle.api.Project
import org.gradle.api.artifacts.MinimalExternalModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.provider.Provider
val providedDependencies = mutableMapOf<String, MutableSet<String>>()
fun Project.provided(pattern: String, name: String, excludedOn: Int = 0b110) {
providedDependencies.getOrPut(project.name) { mutableSetOf() }
.add("${calcExclusion(pattern, 0b100, excludedOn)}:${calcExclusion(name, 0b10, excludedOn)}")
}
fun Project.provided(dependency: ProjectDependency) =
provided(dependency.group!!, dependency.name)
fun Project.provided(dependency: MinimalExternalModuleDependency) =
provided(dependency.module.group, dependency.module.name)
fun Project.provided(provider: Provider<MinimalExternalModuleDependency>) =
provided(provider.get())
fun getProvidedDependenciesForProject(projectName: String): MutableSet<String> {
return providedDependencies.getOrDefault(projectName, emptySet()).toMutableSet()
}
private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String =
if (excludedOn and bit > 0) section else ""
fun projectVersion(project: Project): String =
project.version.toString().replace("SNAPSHOT", "b" + buildNumber())
fun versionName(project: Project): String =
"Floodgate-" + project.name.replaceFirstChar { it.uppercase() } + "-" + projectVersion(project)
fun buildNumber(): Int =
(System.getenv("GITHUB_RUN_NUMBER"))?.let { Integer.parseInt(it) } ?: -1

View File

@@ -0,0 +1,37 @@
plugins {
`java-library`
id("net.kyori.indra")
}
dependencies {
compileOnly("org.checkerframework", "checker-qual", "3.19.0")
}
indra {
github("GeyserMC", "floodgate-modded") {
ci(true)
issues(true)
scm(true)
}
mitLicense()
javaVersions {
target(21)
}
}
tasks {
processResources {
filesMatching(listOf("fabric.mod.json", "META-INF/neoforge.mods.toml")) {
expand(
"id" to "floodgate",
"name" to "Floodgate",
"version" to project.version,
"description" to project.description,
"url" to "https://geysermc.org",
"author" to "GeyserMC",
"minecraft_version" to libs.versions.minecraft.version.get()
)
}
}
}

View File

@@ -0,0 +1,14 @@
repositories {
// mavenLocal()
mavenCentral()
maven("https://maven.fabricmc.net/")
maven("https://maven.neoforged.net/releases")
maven("https://repo.opencollab.dev/main/")
maven("https://jitpack.io") {
content {
includeGroupByRegex("com.github.*")
}
}
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}

View File

@@ -0,0 +1,134 @@
import net.fabricmc.loom.task.RemapJarTask
plugins {
id("floodgate-modded.publish-conventions")
id("architectury-plugin")
id("dev.architectury.loom")
id("com.modrinth.minotaur")
}
// These are all provided by Minecraft/server platforms
provided("com.google.code.gson", "gson")
provided("org.slf4j", ".*")
provided("com.google.guava", "guava")
provided("org.ow2.asm", "asm")
provided("com.nukkitx.fastutil", ".*")
// these we just don't want to include
provided("org.checkerframework", ".*")
provided("com.google.errorprone", ".*")
provided("com.github.spotbugs", "spotbugs-annotations")
provided("com.google.code.findbugs", ".*")
// cloud-fabric/cloud-neoforge jij's all cloud depends already
provided("org.incendo", ".*")
provided("io.leangen.geantyref", "geantyref")
architectury {
minecraft = libs.versions.minecraft.version.get()
}
loom {
silentMojangMappingsLicense()
}
configurations {
create("includeTransitive").isTransitive = true
}
dependencies {
minecraft(libs.minecraft)
mappings(loom.officialMojangMappings())
// These are under our own namespace
shadow(libs.floodgate.api) { isTransitive = false }
shadow(libs.floodgate.core) { isTransitive = false }
// Requires relocation
shadow(libs.bstats) { isTransitive = false }
// Shadow & relocate these since the (indirectly) depend on quite old dependencies
shadow(libs.guice) { isTransitive = false }
shadow(libs.configutils) {
exclude("org.checkerframework")
exclude("com.google.errorprone")
exclude("com.github.spotbugs")
exclude("com.nukkitx.fastutil")
}
}
tasks {
sourcesJar {
archiveClassifier.set("sources")
from(sourceSets.main.get().allSource)
}
shadowJar {
// Mirrors the example fabric project, otherwise tons of dependencies are shaded that shouldn't be
configurations = listOf(project.configurations.shadow.get())
// Relocate these
relocate("org.bstats", "org.geysermc.floodgate.shadow.bstats")
relocate("com.google.inject", "org.geysermc.floodgate.shadow.google.inject")
relocate("org.yaml", "org.geysermc.floodgate.shadow.org.yaml")
// The remapped shadowJar is the final desired mod jar
archiveVersion.set(project.version.toString())
archiveClassifier.set("shaded")
}
remapJar {
dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
archiveClassifier.set("")
archiveVersion.set("")
}
register("remapModrinthJar", RemapJarTask::class) {
dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
archiveVersion.set(versionName(project))
archiveClassifier.set("")
}
// Readme sync
modrinth.get().dependsOn(tasks.modrinthSyncBody)
}
afterEvaluate {
val providedDependencies = getProvidedDependenciesForProject(project.name)
// These are shaded, no need to JiJ them
configurations["shadow"].resolvedConfiguration.resolvedArtifacts.forEach {shadowed ->
val string = "${shadowed.moduleVersion.id.group}:${shadowed.moduleVersion.id.name}"
println("Not including shadowed dependency: $string")
providedDependencies.add(string)
}
configurations["includeTransitive"].resolvedConfiguration.resolvedArtifacts.forEach { dep ->
if (!providedDependencies.contains("${dep.moduleVersion.id.group}:${dep.moduleVersion.id.name}")
and !providedDependencies.contains("${dep.moduleVersion.id.group}:.*")) {
println("Including dependency via JiJ: ${dep.id}")
dependencies.add("include", dep.moduleVersion.id.toString())
} else {
println("Not including ${dep.id} for ${project.name}!")
}
}
}
modrinth {
token.set(System.getenv("MODRINTH_TOKEN")) // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
projectId.set("bWrNNfkb")
versionName.set(versionName(project))
versionNumber.set(projectVersion(project))
versionType.set("release")
changelog.set("A changelog can be found at https://github.com/GeyserMC/Floodgate-Modded/commits")
syncBodyFrom.set(rootProject.file("README.md").readText())
uploadFile.set(tasks.getByPath("remapModrinthJar"))
gameVersions.add(libs.minecraft.get().version as String)
gameVersions.add("1.21.1")
failSilently.set(false)
}

View File

@@ -0,0 +1,15 @@
plugins {
id("floodgate-modded.shadow-conventions")
id("net.kyori.indra.publishing")
}
indra {
publishSnapshotsTo("geysermc", "https://repo.opencollab.dev/maven-snapshots")
publishReleasesTo("geysermc", "https://repo.opencollab.dev/maven-releases")
}
publishing {
// skip shadow jar from publishing. Workaround for https://github.com/johnrengelman/shadow/issues/651
val javaComponent = project.components["java"] as AdhocComponentWithVariants
javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { skip() }
}

View File

@@ -0,0 +1,37 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
id("floodgate-modded.base-conventions")
id("com.github.johnrengelman.shadow")
}
tasks {
named<Jar>("jar") {
archiveClassifier.set("unshaded")
from(project.rootProject.file("LICENSE"))
}
val shadowJar = named<ShadowJar>("shadowJar") {
archiveBaseName.set(project.name)
archiveVersion.set("")
archiveClassifier.set("")
val sJar: ShadowJar = this
doFirst {
providedDependencies[project.name]?.forEach { string ->
sJar.dependencies {
println("Excluding $string from ${project.name}")
exclude(dependency(string))
}
}
sJar.dependencies {
exclude(dependency("org.checkerframework:checker-qual:.*"))
exclude(dependency("org.jetbrains:annotations:.*"))
}
}
}
named("build") {
dependsOn(shadowJar)
}
}

View File

@@ -1,147 +1,24 @@
import net.fabricmc.loom.task.RemapJarTask
plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
id("fabric-loom") version "1.6-SNAPSHOT"
id("java")
id("maven-publish")
id("com.modrinth.minotaur") version "2.+"
`java-library`
id("floodgate-modded.build-logic")
alias(libs.plugins.lombok) apply false
}
loom {
accessWidenerPath = file("src/main/resources/floodgate.accesswidener")
}
val platforms = setOf(
projects.fabric,
projects.neoforge,
projects.mod
).map { it.dependencyProject }
dependencies {
//to change the versions see the gradle.properties file
minecraft("com.mojang:minecraft:1.21")
mappings(loom.officialMojangMappings())
modImplementation("net.fabricmc:fabric-loader:0.15.11")
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation("net.fabricmc.fabric-api:fabric-api:0.100.1+1.21")
// Base Floodgate
implementation("org.geysermc.floodgate:core:2.2.3-20240508.151752-4")
shadow("org.geysermc.floodgate:core:2.2.3-20240508.151752-4") { isTransitive = false }
shadow("org.geysermc.floodgate:api:2.2.3-20240508.151752-4") { isTransitive = false }
// Requires relocation
shadow("org.bstats:bstats-base:3.0.2")
// Shadow & relocate these since the (indirectly) depend on quite old dependencies
shadow("com.google.inject:guice:6.0.0") { isTransitive = false }
shadow("org.geysermc.configutils:configutils:1.0-SNAPSHOT") {
exclude("org.checkerframework")
exclude("com.google.errorprone")
exclude("com.github.spotbugs")
exclude("com.nukkitx.fastutil")
subprojects {
apply {
plugin("java-library")
plugin("io.freefair.lombok")
plugin("floodgate-modded.build-logic")
}
include("aopalliance:aopalliance:1.0")
include("javax.inject:javax.inject:1")
include("jakarta.inject:jakarta.inject-api:2.0.1")
include("org.java-websocket:Java-WebSocket:1.5.2")
// Just like Geyser, include these
include("org.geysermc.geyser", "common", "2.2.3-SNAPSHOT")
include("org.geysermc.cumulus", "cumulus", "1.1.2")
include("org.geysermc.event", "events", "1.1-SNAPSHOT")
include("org.lanternpowered", "lmbda", "2.0.0") // used in events
// Geyser dependency for the fun injector mixin :)))
modCompileOnly("org.geysermc.geyser:fabric:2.2.3-SNAPSHOT") { isTransitive = false }
// cloud
include("org.incendo:cloud-fabric:2.0.0-SNAPSHOT")
modImplementation("org.incendo:cloud-fabric:2.0.0-SNAPSHOT")
// Lombok
compileOnly("org.projectlombok:lombok:1.18.32")
annotationProcessor("org.projectlombok:lombok:1.18.32")
}
repositories {
// mavenLocal()
mavenCentral()
maven("https://maven.fabricmc.net/")
maven("https://repo.opencollab.dev/main/")
maven("https://jitpack.io") {
content {
includeGroupByRegex("com.github.*")
}
}
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
java {
withSourcesJar()
}
tasks {
shadowJar {
configurations = listOf(project.configurations.shadow.get())
relocate("org.bstats", "org.geysermc.floodgate.shadow.bstats")
relocate("com.google.inject", "org.geysermc.floodgate.shadow.google.inject")
relocate("org.yaml", "org.geysermc.floodgate.shadow.org.yaml")
}
processResources {
filesMatching("fabric.mod.json") {
expand("version" to project.version)
}
}
remapJar {
dependsOn(shadowJar)
mustRunAfter(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
addNestedDependencies = true // todo?
archiveFileName.set("floodgate-fabric.jar")
}
register("remapModrinthJar", RemapJarTask::class) {
dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
addNestedDependencies = true
archiveVersion.set(project.version.toString() + "+build." + System.getenv("GITHUB_RUN_NUMBER"))
archiveClassifier.set("")
}
}
publishing {
publications {
register("publish", MavenPublication::class) {
from(project.components["java"])
// skip shadow jar from publishing. Workaround for https://github.com/johnrengelman/shadow/issues/651
val javaComponent = project.components["java"] as AdhocComponentWithVariants
javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { skip() }
}
}
repositories {
mavenLocal()
}
}
modrinth {
token.set(System.getenv("MODRINTH_TOKEN")) // Prevent GitHub Actions from caching empty Modrinth token
projectId.set("bWrNNfkb")
versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
versionType.set("beta")
changelog.set("A changelog can be found at https://github.com/GeyserMC/Floodgate-Fabric/commits")
syncBodyFrom.set(rootProject.file("README.md").readText())
uploadFile.set(tasks.named("remapModrinthJar"))
gameVersions.addAll("1.21")
loaders.add("fabric")
dependencies {
required.project("fabric-api")
when (this) {
in platforms -> plugins.apply("floodgate-modded.platform-conventions")
else -> plugins.apply("floodgate-modded.base-conventions")
}
}

46
fabric/build.gradle.kts Normal file
View File

@@ -0,0 +1,46 @@
architectury {
platformSetupLoomIde()
fabric()
}
// Used to extend runtime/compile classpaths
val common: Configuration by configurations.creating
// Needed to read mixin config in the runServer task, and for the architectury transformer
// (e.g. the @ExpectPlatform annotation)
val developmentFabric: Configuration = configurations.getByName("developmentFabric")
// Our custom transitive include configuration
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
configurations {
compileClasspath.get().extendsFrom(configurations["common"])
runtimeClasspath.get().extendsFrom(configurations["common"])
developmentFabric.extendsFrom(configurations["common"])
}
dependencies {
modImplementation(libs.fabric.loader)
modApi(libs.fabric.api)
// "namedElements" configuration should be used to depend on different loom projects
common(project(":mod", configuration = "namedElements")) { isTransitive = false }
// Bundle transformed classes of the common module for production mod jar
shadow(project(path = ":mod", configuration = "transformProductionFabric")) {
isTransitive = false
}
includeTransitive(libs.floodgate.core)
implementation(libs.floodgate.core)
implementation(libs.guice)
modImplementation(libs.cloud.fabric)
include(libs.cloud.fabric)
}
tasks {
remapJar {
archiveBaseName.set("floodgate-fabric")
}
modrinth {
loaders.add("fabric")
}
}

1
fabric/gradle.properties Normal file
View File

@@ -0,0 +1 @@
loom.platform=fabric

View File

@@ -0,0 +1,14 @@
package org.geysermc.floodgate.mod.util.fabric;
import net.fabricmc.loader.api.FabricLoader;
public class MixinConfigPluginImpl {
public static boolean isGeyserLoaded() {
return FabricLoader.getInstance().isModLoaded("geyser-fabric");
}
public static boolean applyProxyFix() {
return FabricLoader.getInstance().isModLoaded("fabricproxy-lite");
}
}

View File

@@ -0,0 +1,54 @@
package org.geysermc.floodgate.platform.fabric;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.floodgate.core.module.PluginMessageModule;
import org.geysermc.floodgate.core.module.ServerCommonModule;
import org.geysermc.floodgate.mod.FloodgateMod;
import org.geysermc.floodgate.mod.util.ModTemplateReader;
import org.geysermc.floodgate.platform.fabric.module.FabricCommandModule;
import org.geysermc.floodgate.platform.fabric.module.FabricPlatformModule;
import java.nio.file.Path;
public final class FabricFloodgateMod extends FloodgateMod implements ModInitializer {
private ModContainer container;
@Override
public void onInitialize() {
container = FabricLoader.getInstance().getModContainer("floodgate").orElseThrow();
init(
new ServerCommonModule(
FabricLoader.getInstance().getConfigDir().resolve("floodgate"),
new ModTemplateReader()
),
new FabricPlatformModule(),
new FabricCommandModule(),
new PluginMessageModule()
);
ServerLifecycleEvents.SERVER_STARTED.register(this::enable);
if (isClient()) {
ClientLifecycleEvents.CLIENT_STOPPING.register($ -> this.disable());
} else {
ServerLifecycleEvents.SERVER_STOPPING.register($ -> this.disable());
}
}
@Override
public @Nullable Path resourcePath(String file) {
return container.findPath(file).orElse(null);
}
@Override
public boolean isClient() {
return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT;
}
}

View File

@@ -0,0 +1,14 @@
package org.geysermc.floodgate.platform.fabric.listener;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import org.geysermc.floodgate.core.platform.listener.ListenerRegistration;
import org.geysermc.floodgate.mod.listener.ModEventListener;
public final class FabricEventRegistration implements ListenerRegistration<ModEventListener> {
@Override
public void register(ModEventListener listener) {
ServerPlayConnectionEvents.JOIN.register(
(handler, sender, server) -> listener.onPlayerJoin(handler.getPlayer().getUUID())
);
}
}

View File

@@ -1,13 +1,15 @@
package org.geysermc.floodgate.module;
package org.geysermc.floodgate.platform.fabric.module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import lombok.SneakyThrows;
import net.minecraft.commands.CommandSourceStack;
import org.geysermc.floodgate.platform.command.CommandUtil;
import org.geysermc.floodgate.player.FloodgateCommandPreprocessor;
import org.geysermc.floodgate.player.UserAudience;
import org.geysermc.floodgate.player.audience.FloodgateSenderMapper;
import org.geysermc.floodgate.core.module.CommandModule;
import org.geysermc.floodgate.core.platform.command.CommandUtil;
import org.geysermc.floodgate.core.player.FloodgateCommandPreprocessor;
import org.geysermc.floodgate.core.player.UserAudience;
import org.geysermc.floodgate.core.player.audience.FloodgateSenderMapper;
import org.geysermc.floodgate.mod.util.ModCommandUtil;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.fabric.FabricCommandManager;
@@ -23,6 +25,7 @@ public final class FabricCommandModule extends CommandModule {
new FloodgateSenderMapper<>(commandUtil)
);
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
((ModCommandUtil) commandUtil).setCommandManager(commandManager);
return commandManager;
}

View File

@@ -0,0 +1,40 @@
package org.geysermc.floodgate.platform.fabric.module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.geysermc.floodgate.core.platform.listener.ListenerRegistration;
import org.geysermc.floodgate.core.platform.pluginmessage.PluginMessageUtils;
import org.geysermc.floodgate.core.pluginmessage.PluginMessageRegistration;
import org.geysermc.floodgate.mod.listener.ModEventListener;
import org.geysermc.floodgate.mod.module.ModPlatformModule;
import org.geysermc.floodgate.platform.fabric.listener.FabricEventRegistration;
import org.geysermc.floodgate.platform.fabric.pluginmessage.FabricPluginMessageRegistration;
import org.geysermc.floodgate.platform.fabric.pluginmessage.FabricPluginMessageUtils;
public class FabricPlatformModule extends ModPlatformModule {
@Provides
@Singleton
public ListenerRegistration<ModEventListener> listenerRegistration() {
return new FabricEventRegistration();
}
@Provides
@Singleton
public PluginMessageUtils pluginMessageUtils() {
return new FabricPluginMessageUtils();
}
@Provides
@Singleton
public PluginMessageRegistration pluginMessageRegister() {
return new FabricPluginMessageRegistration();
}
@Provides
@Named("implementationName")
public String implementationName() {
return "Fabric";
}
}

View File

@@ -1,11 +1,13 @@
package org.geysermc.floodgate.pluginmessage;
package org.geysermc.floodgate.platform.fabric.pluginmessage;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import org.geysermc.floodgate.pluginmessage.payloads.FormPayload;
import org.geysermc.floodgate.pluginmessage.payloads.PacketPayload;
import org.geysermc.floodgate.pluginmessage.payloads.SkinPayload;
import org.geysermc.floodgate.pluginmessage.payloads.TransferPayload;
import org.geysermc.floodgate.core.pluginmessage.PluginMessageChannel;
import org.geysermc.floodgate.core.pluginmessage.PluginMessageRegistration;
import org.geysermc.floodgate.mod.pluginmessage.payloads.FormPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.PacketPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.SkinPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.TransferPayload;
public class FabricPluginMessageRegistration implements PluginMessageRegistration {
@Override

View File

@@ -1,21 +1,14 @@
package org.geysermc.floodgate.pluginmessage;
package org.geysermc.floodgate.platform.fabric.pluginmessage;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.geysermc.floodgate.MinecraftServerHolder;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.InstanceHolder;
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
import org.geysermc.floodgate.pluginmessage.payloads.FormPayload;
import org.geysermc.floodgate.pluginmessage.payloads.PacketPayload;
import org.geysermc.floodgate.pluginmessage.payloads.SkinPayload;
import org.geysermc.floodgate.pluginmessage.payloads.TransferPayload;
import org.geysermc.floodgate.core.platform.pluginmessage.PluginMessageUtils;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
import org.geysermc.floodgate.mod.pluginmessage.payloads.FormPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.PacketPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.SkinPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.TransferPayload;
import java.util.Objects;
import java.util.UUID;

View File

@@ -0,0 +1,30 @@
{
"schemaVersion": 1,
"id": "$id",
"version": "$version",
"name": "$name",
"description": "$description",
"authors": [
"$author"
],
"contact": {
"website": "$url",
"repo": "https://github.com/GeyserMC/Floodgate-Modded"
},
"license": "MIT",
"environment": "*",
"entrypoints": {
"main": [
"org.geysermc.floodgate.platform.fabric.FabricFloodgateMod"
]
},
"accessWidener": "floodgate.accesswidener",
"mixins": [
"floodgate.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.10",
"fabric": "*",
"minecraft": ">=$minecraft_version"
}
}

View File

@@ -1,6 +1,9 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false
org.gradle.caching=true
org.gradle.vfs.watch=false
# Mod Properties
version=2.2.3-SNAPSHOT
version=2.2.4-SNAPSHOT
group=org.geysermc.floodgate
archives_base_name=floodgate-fabric
id=floodgate-modded

57
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,57 @@
[versions]
geyser = "2.2.3-SNAPSHOT"
blossom = "1.2.0"
indra = "3.1.3"
shadow = "8.1.1"
architectury-plugin = "3.4-SNAPSHOT"
architectury-loom = "1.7-SNAPSHOT"
minecraft-version = "1.21"
minotaur = "2.+"
guice = "6.0.0"
cloud = "2.0.0-beta.7"
lombok = "8.6"
bstats = "3.0.2"
configutils = "1.0-SNAPSHOT"
mixin = "0.8.5"
asm = "5.2"
floodgate = "core-repackage-2.2.3-SNAPSHOT"
# fabric
fabric-loader = "0.15.11"
fabric-api = "0.100.1+1.21"
# neoforge
neoforge-version = "21.0.87-beta"
[libraries]
floodgate-core = { group = "org.geysermc.floodgate", name = "core", version.ref = "floodgate" }
floodgate-api = { group = "org.geysermc.floodgate", name = "api", version.ref = "floodgate" }
geyser-fabric = { group = "org.geysermc.geyser", name = "fabric", version.ref = "geyser" }
geyser-mod = { group = "org.geysermc.geyser", name = "mod", version.ref = "geyser" }
geyser-core = { group = "org.geysermc.geyser", name = "core", version.ref = "geyser" }
indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }
shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
architectury-plugin = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" }
architectury-loom = { group = "dev.architectury.loom", name = "dev.architectury.loom.gradle.plugin", version.ref = "architectury-loom" }
guice = { group = "com.google.inject", name = "guice", version.ref = "guice" }
bstats = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" }
configutils = { group = "org.geysermc.configutils", name = "configutils", version.ref = "configutils" }
cloud-fabric = { group = "org.incendo", name = "cloud-fabric", version.ref = "cloud" }
cloud-neoforge = { group = "org.incendo", name = "cloud-neoforge", version.ref = "cloud" }
minotaur = { group = "com.modrinth.minotaur", name = "Minotaur", version.ref = "minotaur" }
mixin = { group = "org.spongepowered", name = "mixin", version.ref = "mixin" }
asm = { group = "org.ow2.asm", name = "asm-debug-all", version.ref = "asm" }
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft-version" }
# Fabric
fabric-loader = { group = "net.fabricmc", name = "fabric-loader", version.ref = "fabric-loader" }
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
# NeoForge
neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge-version" }
[plugins]
lombok = { id = "io.freefair.lombok", version.ref = "lombok" }
blossom = { id = "net.kyori.blossom", version.ref = "blossom"}
indra = { id = "net.kyori.indra", version.ref = "indra" }

Binary file not shown.

View File

@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

51
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright <EFBFBD> 2015-2021 the original authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -32,10 +32,10 @@
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * 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>.
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +80,11 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,22 +131,29 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -205,6 +214,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

35
gradlew.bat vendored
View File

@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

33
mod/build.gradle.kts Normal file
View File

@@ -0,0 +1,33 @@
architectury {
common("neoforge", "fabric")
}
loom {
accessWidenerPath = file("src/main/resources/floodgate.accesswidener")
mixin.defaultRefmapName.set("floodgate-refmap.json")
}
dependencies {
api(libs.floodgate.core)
api(libs.floodgate.api)
api(libs.guice)
compileOnly(libs.mixin)
compileOnly(libs.asm)
modCompileOnly(libs.geyser.mod) { isTransitive = false }
modCompileOnly(libs.geyser.core) { isTransitive = false }
// Only here to suppress "unknown enum constant EnvType.CLIENT" warnings.
compileOnly(libs.fabric.loader)
}
afterEvaluate {
// We don't need these
tasks.named("remapModrinthJar").configure {
enabled = false
}
tasks.named("modrinth").configure {
enabled = false
}
}

View File

@@ -0,0 +1,59 @@
package org.geysermc.floodgate.mod;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import net.minecraft.server.MinecraftServer;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.core.FloodgatePlatform;
import org.geysermc.floodgate.mod.module.ModAddonModule;
import org.geysermc.floodgate.mod.module.ModListenerModule;
import java.nio.file.Path;
public abstract class FloodgateMod {
public static FloodgateMod INSTANCE;
private boolean started;
private FloodgatePlatform platform;
protected Injector injector;
protected void init(Module... modules) {
INSTANCE = this;
injector = Guice.createInjector(modules);
platform = injector.getInstance(FloodgatePlatform.class);
}
protected void enable(MinecraftServer server) {
long ctm = System.currentTimeMillis();
// Stupid hack, see the class for more information
// This can probably be Guice-i-fied but that is beyond me
MinecraftServerHolder.set(server);
if (!started) {
platform.enable(
new ModAddonModule(),
new ModListenerModule()
);
started = true;
}
long endCtm = System.currentTimeMillis();
injector.getInstance(FloodgateLogger.class)
.translatedInfo("floodgate.core.finish", endCtm - ctm);
}
protected void disable() {
platform.disable();
}
protected void enable(Module... module) {
platform.enable(module);
}
public @Nullable abstract Path resourcePath(String file);
public abstract boolean isClient();
}

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate;
package org.geysermc.floodgate.mod;
import net.minecraft.server.MinecraftServer;

View File

@@ -1,18 +1,18 @@
package org.geysermc.floodgate.addon.data;
package org.geysermc.floodgate.mod.data;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import org.geysermc.floodgate.api.SimpleFloodgateApi;
import org.geysermc.floodgate.api.inject.InjectorAddon;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
import org.geysermc.floodgate.util.Utils;
import org.geysermc.floodgate.core.api.SimpleFloodgateApi;
import org.geysermc.floodgate.core.config.FloodgateConfig;
import org.geysermc.floodgate.core.player.FloodgateHandshakeHandler;
import org.geysermc.floodgate.core.util.Utils;
public final class FabricDataAddon implements InjectorAddon {
public final class ModDataAddon implements InjectorAddon {
@Inject private FloodgateHandshakeHandler handshakeHandler;
@Inject private FloodgateConfig config;
@Inject private SimpleFloodgateApi api;
@@ -34,7 +34,7 @@ public final class FabricDataAddon implements InjectorAddon {
public void onInject(Channel channel, boolean toServer) {
channel.pipeline().addBefore(
packetHandlerName, "floodgate_data_handler",
new FabricDataHandler(handshakeHandler, config, kickMessageAttribute, logger)
new ModDataHandler(handshakeHandler, config, kickMessageAttribute, logger)
);
}

View File

@@ -1,8 +1,10 @@
package org.geysermc.floodgate.addon.data;
package org.geysermc.floodgate.mod.data;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.logging.LogUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import net.minecraft.DefaultUncaughtExceptionHandler;
import net.minecraft.network.Connection;
@@ -10,28 +12,28 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.handshake.ClientIntentionPacket;
import net.minecraft.network.protocol.login.ServerboundHelloPacket;
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
import org.geysermc.floodgate.MinecraftServerHolder;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.mixin.ConnectionMixin;
import org.geysermc.floodgate.mixin.ClientIntentionPacketMixinInterface;
import com.mojang.authlib.GameProfile;
import io.netty.channel.ChannelHandlerContext;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult;
import org.geysermc.floodgate.core.addon.data.CommonDataHandler;
import org.geysermc.floodgate.core.addon.data.PacketBlocker;
import org.geysermc.floodgate.core.config.FloodgateConfig;
import org.geysermc.floodgate.core.player.FloodgateHandshakeHandler;
import org.geysermc.floodgate.core.player.FloodgateHandshakeHandler.HandshakeResult;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
import org.geysermc.floodgate.mod.mixin.ClientIntentionPacketMixinInterface;
import org.geysermc.floodgate.mod.mixin.ConnectionMixin;
import org.slf4j.Logger;
import java.net.InetSocketAddress;
public final class FabricDataHandler extends CommonDataHandler {
public final class ModDataHandler extends CommonDataHandler {
private static final Logger LOGGER = LogUtils.getLogger();
private final FloodgateLogger logger;
private Connection networkManager;
private FloodgatePlayer player;
public FabricDataHandler(
public ModDataHandler(
FloodgateHandshakeHandler handshakeHandler,
FloodgateConfig config,
AttributeKey<String> kickMessageAttribute, FloodgateLogger logger) {

View File

@@ -1,19 +1,21 @@
package org.geysermc.floodgate.inject.fabric;
package org.geysermc.floodgate.mod.inject;
import com.google.inject.Inject;
import io.netty.channel.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.inject.CommonPlatformInjector;
import org.geysermc.floodgate.core.inject.CommonPlatformInjector;
@RequiredArgsConstructor
public final class FabricInjector extends CommonPlatformInjector {
public final class ModInjector extends CommonPlatformInjector {
@Setter @Getter
private static FabricInjector instance;
public static ModInjector INSTANCE = new ModInjector();
@Getter private final boolean injected = true;

View File

@@ -1,21 +1,20 @@
package org.geysermc.floodgate.listener;
package org.geysermc.floodgate.mod.listener;
import com.google.inject.Inject;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.util.LanguageManager;
import org.geysermc.floodgate.core.util.LanguageManager;
public final class FabricEventListener {
import java.util.UUID;
public final class ModEventListener {
@Inject private FloodgateApi api;
@Inject private FloodgateLogger logger;
@Inject private LanguageManager languageManager;
public void onPlayerJoin(ServerGamePacketListenerImpl networkHandler, PacketSender packetSender, MinecraftServer server) {
FloodgatePlayer player = api.getPlayer(networkHandler.player.getUUID());
public void onPlayerJoin(UUID uuid) {
FloodgatePlayer player = api.getPlayer(uuid);
if (player != null) {
logger.translatedInfo(
"floodgate.ingame.login_name",

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.logger;
package org.geysermc.floodgate.mod.logger;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -7,10 +7,10 @@ import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.util.LanguageManager;
import org.geysermc.floodgate.core.config.FloodgateConfig;
import org.geysermc.floodgate.core.util.LanguageManager;
import static org.geysermc.floodgate.util.MessageFormatter.format;
import static org.geysermc.floodgate.core.util.MessageFormatter.format;
@Singleton
public final class Log4jFloodgateLogger implements FloodgateLogger {

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.mixin;
package org.geysermc.floodgate.mod.mixin;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.server.level.ChunkMap;

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.mixin;
package org.geysermc.floodgate.mod.mixin;
import net.minecraft.network.protocol.handshake.ClientIntentionPacket;
import org.spongepowered.asm.mixin.Mixin;

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.mixin;
package org.geysermc.floodgate.mod.mixin;
import net.minecraft.network.protocol.handshake.ClientIntentionPacket;
import org.spongepowered.asm.mixin.Mixin;

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.mixin;
package org.geysermc.floodgate.mod.mixin;
import net.minecraft.network.Connection;
import org.spongepowered.asm.mixin.Mixin;

View File

@@ -0,0 +1,49 @@
package org.geysermc.floodgate.mod.mixin;
import org.geysermc.floodgate.core.util.Utils;
import org.geysermc.floodgate.mod.FloodgateMod;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Mixins into Floodgate's {@link Utils} class to modify how resources are loaded from the jar.
* This must be done due to mod platforms sharing a classloader across mods - this leads to Floodgate
* loading Geyser's language files, as they're not prefixed to avoid conflicts.
* To resolve this, this mixin replaces those calls with the platform-appropriate methods to load files.
*/
@Mixin(value = Utils.class, remap = false)
public class FloodgateUtilMixin {
@Redirect(method = "readProperties",
at = @At(value = "INVOKE", target = "Ljava/lang/ClassLoader;getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;"))
private static InputStream floodgate$redirectInputStream(ClassLoader instance, String string) {
Path path = FloodgateMod.INSTANCE.resourcePath(string);
try {
return path == null ? null : Files.newInputStream(path);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Redirect(method = "getGeneratedClassesForAnnotation(Ljava/lang/String;)Ljava/util/Set;",
at = @At(value = "INVOKE", target = "Ljava/lang/ClassLoader;getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;"))
private static InputStream floodgate$redirectInputStreamAnnotation(ClassLoader instance, String string) {
Path path = FloodgateMod.INSTANCE.resourcePath(string);
if (path == null) {
throw new IllegalStateException("Unable to find annotation class! " + string);
}
try {
return Files.newInputStream(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,7 +1,7 @@
package org.geysermc.floodgate.mixin;
package org.geysermc.floodgate.mod.mixin;
import io.netty.channel.ChannelFuture;
import org.geysermc.floodgate.inject.fabric.FabricInjector;
import org.geysermc.floodgate.mod.inject.ModInjector;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.platform.mod.GeyserModInjector;
import org.spongepowered.asm.mixin.Mixin;
@@ -19,7 +19,7 @@ public class GeyserModInjectorMixin {
private List<ChannelFuture> allServerChannels;
@Inject(method = "initializeLocalChannel0", at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"))
public void onChannelAdd(GeyserBootstrap bootstrap, CallbackInfo ci) {
FabricInjector.getInstance().injectClient(this.allServerChannels.get(this.allServerChannels.size() - 1));
public void floodgate$onChannelAdd(GeyserBootstrap bootstrap, CallbackInfo ci) {
ModInjector.INSTANCE.injectClient(this.allServerChannels.get(this.allServerChannels.size() - 1));
}
}

View File

@@ -1,8 +1,8 @@
package org.geysermc.floodgate.mixin;
package org.geysermc.floodgate.mod.mixin;
import net.minecraft.server.network.ServerConnectionListener;
import org.geysermc.floodgate.inject.fabric.FabricInjector;
import io.netty.channel.ChannelFuture;
import net.minecraft.server.network.ServerConnectionListener;
import org.geysermc.floodgate.mod.inject.ModInjector;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -19,7 +19,7 @@ public abstract class ServerConnectionListenerMixin {
@Shadow @Final private List<ChannelFuture> channels;
@Inject(method = "startTcpServerListener", at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/List;add(Ljava/lang/Object;)Z"))
public void onChannelAdd(InetAddress address, int port, CallbackInfo ci) {
FabricInjector.getInstance().injectClient(this.channels.get(this.channels.size() - 1));
public void floodgate$onChannelAdd(InetAddress address, int port, CallbackInfo ci) {
ModInjector.INSTANCE.injectClient(this.channels.get(this.channels.size() - 1));
}
}

View File

@@ -1,15 +1,15 @@
package org.geysermc.floodgate.module;
package org.geysermc.floodgate.mod.module;
import org.geysermc.floodgate.addon.data.FabricDataAddon;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import com.google.inject.multibindings.ProvidesIntoSet;
import org.geysermc.floodgate.addon.AddonManagerAddon;
import org.geysermc.floodgate.addon.DebugAddon;
import org.geysermc.floodgate.api.inject.InjectorAddon;
import org.geysermc.floodgate.register.AddonRegister;
import org.geysermc.floodgate.core.addon.AddonManagerAddon;
import org.geysermc.floodgate.core.addon.DebugAddon;
import org.geysermc.floodgate.core.register.AddonRegister;
import org.geysermc.floodgate.mod.data.ModDataAddon;
public final class FabricAddonModule extends AbstractModule {
public final class ModAddonModule extends AbstractModule {
@Override
protected void configure() {
bind(AddonRegister.class).asEagerSingleton();
@@ -24,7 +24,7 @@ public final class FabricAddonModule extends AbstractModule {
@Singleton
@ProvidesIntoSet
public InjectorAddon dataAddon() {
return new FabricDataAddon();
return new ModDataAddon();
}
@Singleton

View File

@@ -0,0 +1,21 @@
package org.geysermc.floodgate.mod.module;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.ProvidesIntoSet;
import org.geysermc.floodgate.core.register.ListenerRegister;
import org.geysermc.floodgate.mod.listener.ModEventListener;
public final class ModListenerModule extends AbstractModule {
@Override
protected void configure() {
bind(new TypeLiteral<ListenerRegister<ModEventListener>>() {}).asEagerSingleton();
}
@Singleton
@ProvidesIntoSet
public ModEventListener modEventListener() {
return new ModEventListener();
}
}

View File

@@ -0,0 +1,77 @@
package org.geysermc.floodgate.mod.module;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.core.inject.CommonPlatformInjector;
import org.geysermc.floodgate.core.platform.command.CommandUtil;
import org.geysermc.floodgate.core.platform.util.PlatformUtils;
import org.geysermc.floodgate.core.skin.SkinApplier;
import org.geysermc.floodgate.core.util.LanguageManager;
import org.geysermc.floodgate.mod.FloodgateMod;
import org.geysermc.floodgate.mod.inject.ModInjector;
import org.geysermc.floodgate.mod.logger.Log4jFloodgateLogger;
import org.geysermc.floodgate.mod.pluginmessage.ModSkinApplier;
import org.geysermc.floodgate.mod.util.ModCommandUtil;
import org.geysermc.floodgate.mod.util.ModPlatformUtils;
@RequiredArgsConstructor
public abstract class ModPlatformModule extends AbstractModule {
@Provides
@Singleton
public CommandUtil commandUtil(
FloodgateApi api,
FloodgateLogger logger,
LanguageManager languageManager) {
return new ModCommandUtil(languageManager, api, logger);
}
@Override
protected void configure() {
bind(PlatformUtils.class).to(ModPlatformUtils.class);
bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(LogManager.getLogger("floodgate"));
bind(FloodgateLogger.class).to(Log4jFloodgateLogger.class);
}
/*
DebugAddon / PlatformInjector
*/
@Provides
@Singleton
public CommonPlatformInjector platformInjector() {
return ModInjector.INSTANCE;
}
@Provides
@Named("packetEncoder")
public String packetEncoder() {
return FloodgateMod.INSTANCE.isClient() ? "encoder" : "outbound_config";
}
@Provides
@Named("packetDecoder")
public String packetDecoder() {
return FloodgateMod.INSTANCE.isClient() ? "inbound_config" : "decoder" ;
}
@Provides
@Named("packetHandler")
public String packetHandler() {
return "packet_handler";
}
@Provides
@Singleton
public SkinApplier skinApplier() {
return new ModSkinApplier();
}
}

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.pluginmessage;
package org.geysermc.floodgate.mod.pluginmessage;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
@@ -8,16 +8,16 @@ import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.MinecraftServerHolder;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.mixin.ChunkMapMixin;
import org.geysermc.floodgate.skin.SkinApplier;
import static org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
import org.geysermc.floodgate.core.skin.SkinApplier;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
import org.geysermc.floodgate.mod.mixin.ChunkMapMixin;
import java.util.Collections;
public final class FabricSkinApplier implements SkinApplier {
import static org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
public final class ModSkinApplier implements SkinApplier {
@Override
public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData) {

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.pluginmessage.payloads;
package org.geysermc.floodgate.mod.pluginmessage.payloads;
import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf;

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.pluginmessage.payloads;
package org.geysermc.floodgate.mod.pluginmessage.payloads;
import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf;

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.pluginmessage.payloads;
package org.geysermc.floodgate.mod.pluginmessage.payloads;
import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf;

View File

@@ -1,4 +1,4 @@
package org.geysermc.floodgate.pluginmessage.payloads;
package org.geysermc.floodgate.mod.pluginmessage.payloads;
import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf;

View File

@@ -1,28 +1,30 @@
package org.geysermc.floodgate.util;
package org.geysermc.floodgate.mod.util;
import com.mojang.authlib.GameProfile;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.commands.CommandSource;
import lombok.Setter;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.UserWhiteListEntry;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.MinecraftServerHolder;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.platform.command.CommandUtil;
import org.geysermc.floodgate.player.UserAudience;
import org.geysermc.floodgate.core.platform.command.CommandUtil;
import org.geysermc.floodgate.core.player.UserAudience;
import org.geysermc.floodgate.core.util.LanguageManager;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
import org.incendo.cloud.CommandManager;
import java.util.*;
import java.util.logging.Logger;
import java.util.Collection;
import java.util.UUID;
public final class FabricCommandUtil extends CommandUtil {
public final class ModCommandUtil extends CommandUtil {
private final FloodgateLogger logger;
private UserAudience console;
@Setter
private CommandManager<UserAudience> commandManager;
public FabricCommandUtil(LanguageManager manager, FloodgateApi api, FloodgateLogger logger) {
public ModCommandUtil(LanguageManager manager, FloodgateApi api, FloodgateLogger logger) {
super(manager, api);
this.logger = logger;
}
@@ -73,8 +75,7 @@ public final class FabricCommandUtil extends CommandUtil {
@Override
public boolean hasPermission(Object source, String permission) {
return Permissions.check((SharedSuggestionProvider) source,
permission, MinecraftServerHolder.get().getOperatorUserPermissionLevel());
return commandManager.hasPermission(getUserAudience(source), permission);
}
@Override

View File

@@ -1,6 +1,6 @@
package org.geysermc.floodgate.util;
package org.geysermc.floodgate.mod.util;
import net.fabricmc.loader.api.FabricLoader;
import dev.architectury.injectables.annotations.ExpectPlatform;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
@@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.List;
import java.util.Set;
public class MixinConfigPlugin implements IMixinConfigPlugin {
public class ModMixinConfigPlugin implements IMixinConfigPlugin {
@Override
public void onLoad(String mixinPackage) {
@@ -21,12 +21,11 @@ public class MixinConfigPlugin implements IMixinConfigPlugin {
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
if (mixinClassName.equals("org.geysermc.floodgate.mixin.ClientIntentionPacketMixin")) {
//returns true if fabricproxy-lite is present, therefore loading the mixin. If not present, the mixin will not be loaded.
return FabricLoader.getInstance().isModLoaded("fabricproxy-lite");
if (mixinClassName.equals("org.geysermc.floodgate.mod.mixin.ClientIntentionPacketMixin")) {
return applyProxyFix();
}
if (mixinClassName.equals("org.geysermc.floodgate.mixin.GeyserModInjectorMixin")) {
return FabricLoader.getInstance().isModLoaded("geyser-fabric");
if (mixinClassName.equals("org.geysermc.floodgate.mod.mixin.GeyserModInjectorMixin")) {
return isGeyserLoaded();
}
return true;
}
@@ -47,4 +46,14 @@ public class MixinConfigPlugin implements IMixinConfigPlugin {
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
@ExpectPlatform
public static boolean isGeyserLoaded() {
throw new IllegalStateException("isGeyserLoaded is not implemented!");
}
@ExpectPlatform
public static boolean applyProxyFix() {
throw new IllegalStateException("applyProxyFix is not implemented!");
}
}

View File

@@ -1,10 +1,10 @@
package org.geysermc.floodgate.util;
package org.geysermc.floodgate.mod.util;
import net.minecraft.SharedConstants;
import org.geysermc.floodgate.MinecraftServerHolder;
import org.geysermc.floodgate.platform.util.PlatformUtils;
import org.geysermc.floodgate.core.platform.util.PlatformUtils;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
public class FabricPlatformUtils extends PlatformUtils {
public class ModPlatformUtils extends PlatformUtils {
@Override
public AuthType authType() {
return MinecraftServerHolder.get().usesAuthentication() ? AuthType.ONLINE : AuthType.OFFLINE;

View File

@@ -0,0 +1,25 @@
package org.geysermc.floodgate.mod.util;
import org.geysermc.configutils.file.template.TemplateReader;
import org.geysermc.floodgate.mod.FloodgateMod;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class ModTemplateReader implements TemplateReader {
@Override
public BufferedReader read(String configName) {
Path path = FloodgateMod.INSTANCE.resourcePath(configName);
if (path != null) {
try {
return Files.newBufferedReader(path);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return null;
}
}

View File

@@ -1,17 +1,18 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.geysermc.floodgate.mixin",
"package": "org.geysermc.floodgate.mod.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"ChunkMapMixin",
"ClientIntentionPacketMixin",
"ClientIntentionPacketMixinInterface",
"ConnectionMixin",
"FloodgateUtilMixin",
"GeyserModInjectorMixin",
"ServerConnectionListenerMixin"
],
"plugin": "org.geysermc.floodgate.util.MixinConfigPlugin",
"plugin": "org.geysermc.floodgate.mod.util.ModMixinConfigPlugin",
"injectors": {
"defaultRequire": 1
}

61
neoforge/build.gradle.kts Normal file
View File

@@ -0,0 +1,61 @@
architectury {
platformSetupLoomIde()
neoForge()
}
provided("com.google.guava", "failureaccess")
// Used to extend runtime/compile classpaths
val common: Configuration by configurations.creating
// Needed to read mixin config in the runServer task, and for the architectury transformer
// (e.g. the @ExpectPlatform annotation)
val developmentNeoForge: Configuration = configurations.getByName("developmentNeoForge")
// Our custom transitive include configuration
val includeTransitive: Configuration = configurations.getByName("includeTransitive")
configurations {
compileClasspath.get().extendsFrom(configurations["common"])
runtimeClasspath.get().extendsFrom(configurations["common"])
developmentNeoForge.extendsFrom(configurations["common"])
}
dependencies {
// See https://github.com/google/guava/issues/6618
modules {
module("com.google.guava:listenablefuture") {
replacedBy("com.google.guava:guava", "listenablefuture is part of guava")
}
}
neoForge(libs.neoforge)
// "namedElements" configuration should be used to depend on different loom projects
common(project(":mod", configuration = "namedElements")) { isTransitive = false }
// Bundle transformed classes of the common module for production mod jar
shadow(project(path = ":mod", configuration = "transformProductionNeoForge")) { isTransitive = false }
includeTransitive(libs.floodgate.core)
implementation(libs.floodgate.core)
implementation(libs.guice)
modImplementation(libs.cloud.neoforge)
include(libs.cloud.neoforge)
}
tasks {
processResources {
from(project(":mod").file("src/main/resources/floodgate.accesswidener")) {
into("/assets/")
}
}
remapJar {
dependsOn(processResources)
atAccessWideners.add("floodgate.accesswidener")
archiveBaseName.set("floodgate-neoforge")
}
modrinth {
loaders.add("neoforge")
}
}

View File

@@ -0,0 +1 @@
loom.platform=neoforge

View File

@@ -0,0 +1,13 @@
package org.geysermc.floodgate.mod.util.neoforge;
import net.neoforged.fml.loading.LoadingModList;
public class ModMixinConfigPluginImpl {
public static boolean isGeyserLoaded() {
return LoadingModList.get().getModFileById("geyser_neoforge") != null;
}
public static boolean applyProxyFix() {
return false;
}
}

View File

@@ -0,0 +1,101 @@
package org.geysermc.floodgate.platform.neoforge;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.fml.loading.FMLPaths;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.GameShuttingDownEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.core.module.PluginMessageModule;
import org.geysermc.floodgate.core.module.ServerCommonModule;
import org.geysermc.floodgate.mod.FloodgateMod;
import org.geysermc.floodgate.mod.util.ModTemplateReader;
import org.geysermc.floodgate.platform.neoforge.module.NeoForgeCommandModule;
import org.geysermc.floodgate.platform.neoforge.module.NeoForgePlatformModule;
import org.geysermc.floodgate.platform.neoforge.pluginmessage.NeoForgePluginMessageRegistration;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.nio.file.Path;
import java.util.Set;
import java.util.stream.Collectors;
@Mod("floodgate")
public final class NeoForgeFloodgateMod extends FloodgateMod {
private final ModContainer container;
public NeoForgeFloodgateMod(IEventBus modEventBus, ModContainer container) {
this.container = container;
init(
new ServerCommonModule(
FMLPaths.CONFIGDIR.get().resolve("floodgate"),
new ModTemplateReader()
),
new NeoForgePlatformModule(),
new NeoForgeCommandModule()
);
modEventBus.addListener(this::onRegisterPackets);
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
if (FMLLoader.getDist().isClient()) {
NeoForge.EVENT_BUS.addListener(this::onClientStop);
} else {
NeoForge.EVENT_BUS.addListener(this::onServerStop);
}
}
private void onServerStarted(ServerStartedEvent event) {
this.enable(event.getServer());
}
private void onClientStop(GameShuttingDownEvent ignored) {
this.disable();
}
private void onServerStop(ServerStoppingEvent ignored) {
this.disable();
}
private void onRegisterPackets(final RegisterPayloadHandlersEvent event) {
// Set the registrar once we're given it - NeoForgePluginMessageRegistration was created earlier in NeoForgePlatformModule
NeoForgePluginMessageRegistration pluginMessageRegistration = injector.getInstance(NeoForgePluginMessageRegistration.class);
pluginMessageRegistration.setRegistrar(event.registrar("floodgate").optional());
// We can now trigger the registering of our plugin message channels
enable(new PluginMessageModule());
}
@Override
public @Nullable Path resourcePath(String file) {
return container.getModInfo().getOwningFile().getFile().findResource(file);
}
@Override
public boolean isClient() {
return FMLLoader.getDist().isClient();
}
public Set<Class<?>> getAnnotatedClasses(Class<? extends Annotation> annotationClass) {
return container.getModInfo()
.getOwningFile()
.getFile()
.getScanResult()
.getAnnotatedBy(annotationClass, ElementType.TYPE)
.map(annotationData -> {
try {
return Class.forName(annotationData.clazz().getClassName());
} catch (Exception e) {
injector.getInstance(FloodgateLogger.class).error(e.getMessage(), e);
return null;
}
})
.collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,20 @@
package org.geysermc.floodgate.platform.neoforge.listener;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import org.geysermc.floodgate.core.platform.listener.ListenerRegistration;
import org.geysermc.floodgate.mod.listener.ModEventListener;
public final class NeoForgeEventRegistration implements ListenerRegistration<ModEventListener> {
private ModEventListener listener;
@Override
public void register(ModEventListener listener) {
NeoForge.EVENT_BUS.addListener(this::onPlayerJoin);
this.listener = listener;
}
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
listener.onPlayerJoin(event.getEntity().getUUID());
}
}

View File

@@ -0,0 +1,27 @@
package org.geysermc.floodgate.platform.neoforge.mixin;
import org.geysermc.floodgate.core.util.Utils;
import org.geysermc.floodgate.mod.FloodgateMod;
import org.geysermc.floodgate.platform.neoforge.NeoForgeFloodgateMod;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import java.lang.annotation.Annotation;
import java.util.Set;
/**
* Mixin into Floodgate's {@link Utils} class as NeoForge is really picky about how it allows scanning
* mod-owned classes.
*/
@Mixin(value = Utils.class, remap = false)
public class NeoForgeFloodgateUtilMixin {
/**
* @author geysermc
* @reason NeoForge is really picky about how it allows scanning mod-owned classes.
*/
@Overwrite(remap = false)
public static Set<Class<?>> getGeneratedClassesForAnnotation(Class<? extends Annotation> annotationClass) {
return ((NeoForgeFloodgateMod) FloodgateMod.INSTANCE).getAnnotatedClasses(annotationClass);
}
}

View File

@@ -0,0 +1,29 @@
package org.geysermc.floodgate.platform.neoforge.module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import lombok.SneakyThrows;
import org.geysermc.floodgate.core.module.CommandModule;
import org.geysermc.floodgate.core.platform.command.CommandUtil;
import org.geysermc.floodgate.core.player.FloodgateCommandPreprocessor;
import org.geysermc.floodgate.core.player.UserAudience;
import org.geysermc.floodgate.core.player.audience.FloodgateSenderMapper;
import org.geysermc.floodgate.mod.util.ModCommandUtil;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.neoforge.NeoForgeServerCommandManager;
public class NeoForgeCommandModule extends CommandModule {
@Provides
@Singleton
@SneakyThrows
public CommandManager<UserAudience> commandManager(CommandUtil commandUtil) {
CommandManager<UserAudience> commandManager = new NeoForgeServerCommandManager<>(
ExecutionCoordinator.simpleCoordinator(),
new FloodgateSenderMapper<>(commandUtil)
);
commandManager.registerCommandPreProcessor(new FloodgateCommandPreprocessor<>(commandUtil));
((ModCommandUtil) commandUtil).setCommandManager(commandManager);
return commandManager;
}
}

View File

@@ -0,0 +1,46 @@
package org.geysermc.floodgate.platform.neoforge.module;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.geysermc.floodgate.core.platform.listener.ListenerRegistration;
import org.geysermc.floodgate.core.platform.pluginmessage.PluginMessageUtils;
import org.geysermc.floodgate.core.pluginmessage.PluginMessageRegistration;
import org.geysermc.floodgate.mod.listener.ModEventListener;
import org.geysermc.floodgate.mod.module.ModPlatformModule;
import org.geysermc.floodgate.platform.neoforge.listener.NeoForgeEventRegistration;
import org.geysermc.floodgate.platform.neoforge.pluginmessage.NeoForgePluginMessageRegistration;
import org.geysermc.floodgate.platform.neoforge.pluginmessage.NeoForgePluginMessageUtils;
public class NeoForgePlatformModule extends ModPlatformModule {
@Override
protected void configure() {
super.configure();
// We retrieve using NeoForgePluginMessageRegistration.class from our the mod class.
// We do this to ensure that injector#getInstance with either class returns the same singleton
bind(PluginMessageRegistration.class).to(NeoForgePluginMessageRegistration.class).in(Scopes.SINGLETON);
bind(NeoForgePluginMessageRegistration.class).toInstance(new NeoForgePluginMessageRegistration());
}
@Provides
@Singleton
public ListenerRegistration<ModEventListener> listenerRegistration() {
return new NeoForgeEventRegistration();
}
@Provides
@Singleton
public PluginMessageUtils pluginMessageUtils() {
return new NeoForgePluginMessageUtils();
}
@Provides
@Named("implementationName")
public String implementationName() {
return "NeoForge";
}
}

View File

@@ -0,0 +1,39 @@
package org.geysermc.floodgate.platform.neoforge.pluginmessage;
import lombok.Setter;
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
import org.geysermc.floodgate.core.pluginmessage.PluginMessageChannel;
import org.geysermc.floodgate.core.pluginmessage.PluginMessageRegistration;
import org.geysermc.floodgate.mod.pluginmessage.payloads.FormPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.PacketPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.SkinPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.TransferPayload;
public class NeoForgePluginMessageRegistration implements PluginMessageRegistration {
@Setter
private PayloadRegistrar registrar;
@Override
public void register(PluginMessageChannel channel) {
switch (channel.getIdentifier()) {
case "floodgate:form" ->
registrar.playBidirectional(FormPayload.TYPE, FormPayload.STREAM_CODEC, (payload, context) ->
channel.handleServerCall(payload.data(), context.player().getUUID(),
context.player().getGameProfile().getName()));
case "floodgate:packet" ->
registrar.playBidirectional(PacketPayload.TYPE, PacketPayload.STREAM_CODEC, (payload, context) ->
channel.handleServerCall(payload.data(), context.player().getUUID(),
context.player().getGameProfile().getName()));
case "floodgate:skin" ->
registrar.playBidirectional(SkinPayload.TYPE, SkinPayload.STREAM_CODEC, (payload, context) ->
channel.handleServerCall(payload.data(), context.player().getUUID(),
context.player().getGameProfile().getName()));
case "floodgate:transfer" ->
registrar.playBidirectional(TransferPayload.TYPE, TransferPayload.STREAM_CODEC, (payload, context) ->
channel.handleServerCall(payload.data(), context.player().getUUID(),
context.player().getGameProfile().getName()));
default -> throw new IllegalArgumentException("unknown channel: " + channel);
}
}
}

View File

@@ -0,0 +1,37 @@
package org.geysermc.floodgate.platform.neoforge.pluginmessage;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.neoforge.network.PacketDistributor;
import org.geysermc.floodgate.core.platform.pluginmessage.PluginMessageUtils;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
import org.geysermc.floodgate.mod.pluginmessage.payloads.FormPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.PacketPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.SkinPayload;
import org.geysermc.floodgate.mod.pluginmessage.payloads.TransferPayload;
import java.util.Objects;
import java.util.UUID;
public class NeoForgePluginMessageUtils extends PluginMessageUtils {
public boolean sendMessage(UUID uuid, String channel, byte[] data) {
try {
ServerPlayer player = MinecraftServerHolder.get().getPlayerList().getPlayer(uuid);
final CustomPacketPayload payload;
switch (channel) {
case "floodgate:form" -> payload = new FormPayload(data);
case "floodgate:packet" -> payload = new PacketPayload(data);
case "floodgate:skin" -> payload = new SkinPayload(data);
case "floodgate:transfer" -> payload = new TransferPayload(data);
default -> throw new IllegalArgumentException("unknown channel: " + channel);
}
Objects.requireNonNull(player);
PacketDistributor.sendToPlayer(player, payload);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}

View File

@@ -0,0 +1,27 @@
modLoader="javafml"
loaderVersion="[1,)"
license="MIT"
[[mods]]
modId="$id"
version="$version"
displayName="$name"
displayURL="$url"
logoFile= "../assets/floodgate/icon.png"
authors="$author"
description="$description"
[[mixins]]
config = "floodgate.mixins.json"
[[mixins]]
config = "floodgate_neoforge.mixins.json"
[[dependencies.floodgate]]
modId="neoforge"
type="required"
versionRange="[21.0.0-beta,)"
ordering="NONE"
side="BOTH"
[[dependencies.floodgate]]
modId="minecraft"
type="required"
versionRange="[$minecraft_version,)"
ordering="NONE"
side="BOTH"

View File

@@ -0,0 +1,12 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.geysermc.floodgate.platform.neoforge.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"NeoForgeFloodgateUtilMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -1,8 +1,34 @@
@file:Suppress("UnstableApiUsage")
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
repositories {
//mavenLocal()
mavenCentral()
gradlePluginPortal()
maven("https://repo.opencollab.dev/main/")
maven("https://jitpack.io") {
content {
includeGroupByRegex("com\\.github\\..*")
}
}
maven("https://maven.architectury.dev/")
maven("https://maven.neoforged.net/releases")
maven("https://maven.fabricmc.net/")
}
plugins {
id("net.kyori.blossom")
id("net.kyori.indra")
id("net.kyori.indra.git")
id("floodgate-modded.build-logic")
}
includeBuild("build-logic")
}
rootProject.name = "floodgate-modded"
include(":mod")
include(":fabric")
include(":neoforge")

View File

@@ -1,63 +0,0 @@
package org.geysermc.floodgate;
import net.fabricmc.api.EnvType;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import org.geysermc.floodgate.inject.fabric.FabricInjector;
import com.google.inject.Guice;
import com.google.inject.Injector;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.module.*;
import org.geysermc.floodgate.util.FabricTemplateReader;
public class FabricMod implements ModInitializer {
private boolean started;
@Override
public void onInitialize() {
FabricInjector.setInstance(new FabricInjector());
Injector injector = Guice.createInjector(
new ServerCommonModule(FabricLoader.getInstance().getConfigDir().resolve("floodgate"), new FabricTemplateReader()),
new FabricPlatformModule()
);
FloodgatePlatform platform = injector.getInstance(FloodgatePlatform.class);
platform.enable(new FabricCommandModule());
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
long ctm = System.currentTimeMillis();
// Stupid hack, see the class for more information
// This can probably be Guice-i-fied but that is beyond me
MinecraftServerHolder.set(server);
if (!started) {
platform.enable(
new FabricAddonModule(),
new FabricListenerModule(),
new PluginMessageModule()
);
started = true;
}
long endCtm = System.currentTimeMillis();
injector.getInstance(FloodgateLogger.class)
.translatedInfo("floodgate.core.finish", endCtm - ctm);
});
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
ClientLifecycleEvents.CLIENT_STOPPING.register(($) -> {
platform.disable();
});
} else {
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> {
platform.disable();
});
}
}
}

View File

@@ -1,14 +0,0 @@
package org.geysermc.floodgate.listener;
import com.google.inject.Inject;
import lombok.RequiredArgsConstructor;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
@RequiredArgsConstructor(onConstructor = @__(@Inject))
public final class FabricEventRegistration implements ListenerRegistration<FabricEventListener> {
@Override
public void register(FabricEventListener listener) {
ServerPlayConnectionEvents.JOIN.register(listener::onPlayerJoin);
}
}

View File

@@ -1,21 +0,0 @@
package org.geysermc.floodgate.module;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.ProvidesIntoSet;
import org.geysermc.floodgate.listener.FabricEventListener;
import org.geysermc.floodgate.register.ListenerRegister;
public final class FabricListenerModule extends AbstractModule {
@Override
protected void configure() {
bind(new TypeLiteral<ListenerRegister<FabricEventListener>>() {}).asEagerSingleton();
}
@Singleton
@ProvidesIntoSet
public FabricEventListener fabricEventListener() {
return new FabricEventListener();
}
}

View File

@@ -1,107 +0,0 @@
package org.geysermc.floodgate.module;
import com.google.inject.name.Names;
import org.apache.logging.log4j.Logger;
import org.geysermc.floodgate.inject.fabric.FabricInjector;
import org.geysermc.floodgate.listener.FabricEventListener;
import org.geysermc.floodgate.listener.FabricEventRegistration;
import org.geysermc.floodgate.logger.Log4jFloodgateLogger;
import org.geysermc.floodgate.platform.listener.ListenerRegistration;
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils;
import org.geysermc.floodgate.platform.util.PlatformUtils;
import org.geysermc.floodgate.pluginmessage.FabricPluginMessageRegistration;
import org.geysermc.floodgate.pluginmessage.FabricPluginMessageUtils;
import org.geysermc.floodgate.pluginmessage.FabricSkinApplier;
import org.geysermc.floodgate.pluginmessage.PluginMessageRegistration;
import org.geysermc.floodgate.util.FabricCommandUtil;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.inject.CommonPlatformInjector;
import org.geysermc.floodgate.platform.command.CommandUtil;
import org.geysermc.floodgate.skin.SkinApplier;
import org.geysermc.floodgate.util.FabricPlatformUtils;
import org.geysermc.floodgate.util.LanguageManager;
@RequiredArgsConstructor
public final class FabricPlatformModule extends AbstractModule {
@Override
protected void configure() {
bind(PlatformUtils.class).to(FabricPlatformUtils.class);
bind(Logger.class).annotatedWith(Names.named("logger")).toInstance(LogManager.getLogger("floodgate"));
bind(FloodgateLogger.class).to(Log4jFloodgateLogger.class);
}
@Provides
@Singleton
public CommandUtil commandUtil(
FloodgateApi api,
FloodgateLogger logger,
LanguageManager languageManager) {
return new FabricCommandUtil(languageManager, api, logger);
}
@Provides
@Singleton
public ListenerRegistration<FabricEventListener> listenerRegistration() {
return new FabricEventRegistration();
}
/*
DebugAddon / PlatformInjector
*/
@Provides
@Singleton
public CommonPlatformInjector platformInjector() {
return FabricInjector.getInstance();
}
@Provides
@Named("packetEncoder")
public String packetEncoder() {
return "encoder";
}
@Provides
@Named("packetDecoder")
public String packetDecoder() {
return "decoder";
}
@Provides
@Named("packetHandler")
public String packetHandler() {
return "packet_handler";
}
@Provides
@Singleton
public PluginMessageUtils pluginMessageUtils() {
return new FabricPluginMessageUtils();
}
@Provides
@Named("implementationName")
public String implementationName() {
return "Fabric";
}
@Provides
@Singleton
public PluginMessageRegistration pluginMessageRegister() {
return new FabricPluginMessageRegistration();
}
@Provides
@Singleton
public SkinApplier skinApplier() {
return new FabricSkinApplier();
}
}

View File

@@ -1,38 +0,0 @@
package org.geysermc.floodgate.util;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import org.geysermc.configutils.file.template.TemplateReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Optional;
public class FabricTemplateReader implements TemplateReader {
private final ModContainer container;
public FabricTemplateReader() {
container = FabricLoader.getInstance().getModContainer("floodgate").orElseThrow();
}
@Override
public BufferedReader read(String configName) {
Optional<Path> optional = container.findPath(configName);
if (optional.isPresent()) {
try {
InputStream stream = optional.get().getFileSystem()
.provider()
.newInputStream(optional.get());
return new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return null;
}
}

View File

@@ -1,31 +0,0 @@
{
"schemaVersion": 1,
"id": "floodgate",
"version": "${version}",
"name": "Floodgate-Fabric",
"description": "",
"authors": [
"GeyserMC"
],
"contact": {
"website": "https://geysermc.org",
"repo": "https://github.com/GeyserMC/Floodgate-Fabric"
},
"license": "MIT",
"icon": "assets/floodgate/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"org.geysermc.floodgate.FabricMod"
]
},
"accessWidener": "floodgate.accesswidener",
"mixins": [
"floodgate.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.11",
"fabric": "*",
"minecraft": ">=1.21"
}
}