1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2026-01-06 15:42:03 +00:00

Couple more changes

This commit is contained in:
onebeastchris
2025-08-28 02:12:27 +02:00
parent 6660fca77e
commit bd3ad51798
27 changed files with 319 additions and 343 deletions

View File

@@ -1,9 +1,19 @@
plugins {
id("floodgate.base-conventions")
id("floodgate.shadow-conventions")
id("architectury-plugin")
id("dev.architectury.loom")
}
configurations {
create("includeTransitive").isTransitive = true
create("shadowBundle") {
isCanBeResolved = true
isCanBeConsumed = false
isTransitive = false
}
}
architectury {
minecraft = libs.versions.minecraft.version.get()
}
@@ -18,6 +28,16 @@ indra {
}
}
tasks {
shadowJar {
// Mirrors the example fabric project, otherwise tons of dependencies are shaded that shouldn't be
configurations = listOf(project.configurations.getByName("shadowBundle"))
// The remapped shadowJar is the final desired mod jar
archiveVersion.set(project.version.toString())
archiveClassifier.set("shaded")
}
}
dependencies {
minecraft(libs.minecraft)
mappings(loom.officialMojangMappings())

View File

@@ -0,0 +1,34 @@
plugins {
id("floodgate.modded-conventions")
}
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"))
// Bundle transformed classes of the common module for production mod jar
shadowBundle(project(path = ":mod", configuration = "transformProductionFabric"))
modImplementation(libs.cloud.fabric)
compileOnlyApi(projects.isolation)
}

View File

@@ -4,10 +4,6 @@ import net.fabricmc.loader.api.FabricLoader;
public class ModMixinConfigPluginImpl {
public static boolean isGeyserLoaded() {
return FabricLoader.getInstance().isModLoaded("geyser-fabric");
}
public static boolean applyProxyFix() {
return FabricLoader.getInstance().isModLoaded("fabricproxy-lite");
}

View File

@@ -1,45 +1,41 @@
package org.geysermc.floodgate.platform.fabric;
import io.micronaut.context.ApplicationContext;
import io.micronaut.inject.qualifiers.Qualifiers;
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 net.kyori.adventure.platform.modcommon.MinecraftServerAudiences;
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 org.geysermc.floodgate.isolation.library.LibraryManager;
import org.geysermc.floodgate.mod.ModPlatform;
import java.nio.file.Path;
public final class FabricFloodgateMod extends FloodgateMod implements ModInitializer {
public final class FabricFloodgateMod extends ModPlatform {
private ModContainer container;
private final ModContainer container;
public FabricFloodgateMod(LibraryManager manager, ModContainer container) {
super(manager);
this.container = 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()
protected void onContextCreated(ApplicationContext context) {
super.onContextCreated(context);
context.registerSingleton(container)
.registerSingleton(
Path.class,
FabricLoader.getInstance().getConfigDir().resolve("floodgate"),
Qualifiers.byName("dataDirectory")
);
ServerLifecycleEvents.SERVER_STARTED.register(this::enable);
if (isClient()) {
ClientLifecycleEvents.CLIENT_STOPPING.register($ -> this.disable());
} else {
ServerLifecycleEvents.SERVER_STOPPING.register($ -> this.disable());
}
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
context.registerSingleton(server, false);
context.registerSingleton(MinecraftServerAudiences.of(server), false);
});
}
@Override

View File

@@ -1,5 +1,6 @@
package org.geysermc.floodgate.platform.fabric.listener;
import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import org.geysermc.floodgate.core.platform.listener.ListenerRegistration;
import org.geysermc.floodgate.mod.listener.ModEventListener;

View File

@@ -1,7 +1,7 @@
package org.geysermc.floodgate.platform.fabric.module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import jakarta.inject.Singleton;
import lombok.SneakyThrows;
import net.minecraft.commands.CommandSourceStack;
import org.geysermc.floodgate.core.module.CommandModule;

View File

@@ -9,47 +9,53 @@ 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 {
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import java.util.function.Function;
public final class FabricPluginMessageRegistration implements PluginMessageRegistration {
@Override
public void register(PluginMessageChannel channel) {
switch (channel.getIdentifier()) {
case "floodgate:form" -> {
PayloadTypeRegistry.playC2S().register(FormPayload.TYPE, FormPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(FormPayload.TYPE, FormPayload.STREAM_CODEC);
ServerPlayNetworking.registerGlobalReceiver(FormPayload.TYPE,
((payload, context) -> channel.handleServerCall(
payload.data(),
context.player().getUUID(),
context.player().getGameProfile().getName())));
}
case "floodgate:packet" -> {
PayloadTypeRegistry.playC2S().register(PacketPayload.TYPE, PacketPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(PacketPayload.TYPE, PacketPayload.STREAM_CODEC);
ServerPlayNetworking.registerGlobalReceiver(PacketPayload.TYPE,
((payload, context) -> channel.handleServerCall(
payload.data(),
context.player().getUUID(),
context.player().getGameProfile().getName())));
}
case "floodgate:skin" -> {
PayloadTypeRegistry.playC2S().register(SkinPayload.TYPE, SkinPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(SkinPayload.TYPE, SkinPayload.STREAM_CODEC);
ServerPlayNetworking.registerGlobalReceiver(SkinPayload.TYPE,
((payload, context) -> channel.handleServerCall(
payload.data(),
context.player().getUUID(),
context.player().getGameProfile().getName())));
}
case "floodgate:transfer" -> {
PayloadTypeRegistry.playC2S().register(TransferPayload.TYPE, TransferPayload.STREAM_CODEC);
PayloadTypeRegistry.playS2C().register(TransferPayload.TYPE, TransferPayload.STREAM_CODEC);
ServerPlayNetworking.registerGlobalReceiver(TransferPayload.TYPE,
((payload, context) -> channel.handleServerCall(
payload.data(),
context.player().getUUID(),
context.player().getGameProfile().getName())));
}
default -> throw new IllegalArgumentException("unknown channel: " + channel);
final String id = channel.getIdentifier();
switch (id) {
case "floodgate:form" ->
registerBoth(channel, FormPayload.TYPE, FormPayload.STREAM_CODEC, FormPayload::data);
case "floodgate:packet" ->
registerBoth(channel, PacketPayload.TYPE, PacketPayload.STREAM_CODEC, PacketPayload::data);
case "floodgate:skin" ->
registerBoth(channel, SkinPayload.TYPE, SkinPayload.STREAM_CODEC, SkinPayload::data);
case "floodgate:transfer" ->
registerBoth(channel, TransferPayload.TYPE, TransferPayload.STREAM_CODEC, TransferPayload::data);
default -> throw new IllegalArgumentException("Unknown channel: " + id);
}
}
/**
* Registers payload type/codec for both directions and wires a global receiver that
* forwards to the PluginMessageChannel.
*/
private static <T extends CustomPacketPayload> void registerBoth(
PluginMessageChannel channel,
CustomPacketPayload.Type<T> type,
StreamCodec<? super RegistryFriendlyByteBuf, T> codec,
Function<T, byte[]> dataExtractor
) {
// Bidirectional registration
PayloadTypeRegistry.playC2S().register(type, codec);
PayloadTypeRegistry.playS2C().register(type, codec);
// Single handler that delegates to channel.handleServerCall(...)
ServerPlayNetworking.registerGlobalReceiver(
type,
(payload, context) -> channel.handleServerCall(
dataExtractor.apply(payload),
context.player().getUUID(),
context.player().getGameProfile().getName()
)
);
}
}

View File

@@ -1,10 +1,12 @@
package org.geysermc.floodgate.platform.fabric.pluginmessage;
import io.micronaut.context.BeanProvider;
import jakarta.inject.Inject;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
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;
@@ -15,10 +17,13 @@ import java.util.UUID;
public class FabricPluginMessageUtils extends PluginMessageUtils {
@Inject
private BeanProvider<MinecraftServer> server;
@Override
public boolean sendMessage(UUID uuid, String channel, byte[] data) {
try {
ServerPlayer player = MinecraftServerHolder.get().getPlayerList().getPlayer(uuid);
ServerPlayer player = server.get().getPlayerList().getPlayer(uuid);
final CustomPacketPayload payload;
switch (channel) {
case "floodgate:form" -> payload = new FormPayload(data);

View File

@@ -7,46 +7,35 @@ architectury {
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 {
// FIXME why does it break when this is set to api scope???
compileOnlyApi(projects.isolation)
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)
include(libs.fabric.permissions.api)
}
tasks {
remapJar {
archiveBaseName.set("floodgate-fabric")
jar {
dependsOn(":fabric-base:build", configurations.runtimeClasspath)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
archiveBaseName = "floodgate-${project.name}"
archiveVersion = ""
archiveClassifier = ""
val libsDir = project.projects
.fabricBase.dependencyProject
.layout.buildDirectory.dir("libs")
from(libsDir) {
include("floodgate-fabric-base.jar")
rename("floodgate-fabric-base.jar", "platform-base.jar")
into("bundled/")
}
}
// modrinth {
// loaders.add("fabric")
// }
}

View File

@@ -23,27 +23,34 @@
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.spigot;
package org.geysermc.floodgate.fabric;
import java.util.List;
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.geysermc.floodgate.isolation.loader.PlatformHolder;
import org.geysermc.floodgate.isolation.loader.PlatformLoader;
public final class IsolatedFabricMod implements ModInitializer {
private PlatformHolder holder;
private static final isServer = FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER);
private static final boolean IS_SERVER = FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER);
@Override
public void onInitialize() {
PlatformHolder holder;
try {
var libsDirectory = getDataFolder().toPath().resolve("libs");
ModContainer container = FabricLoader.getInstance().getModContainer("floodgate").orElseThrow();
var libsDirectory = FabricLoader.getInstance().getConfigDir().resolve("floodgate").resolve("libs");
holder = PlatformLoader.loadDefault(getClass().getClassLoader(), libsDirectory);
holder.init(List.of(ModContainer.class), List.of(this));
holder.init(List.of(ModContainer.class), List.of(container));
} catch (Exception exception) {
throw new RuntimeException("Failed to load Floodgate", exception);
}
if (isServer) {
if (IS_SERVER) {
ServerLifecycleEvents.SERVER_STARTED.register(($) -> {
holder.enable();
});
@@ -54,7 +61,7 @@ public final class IsolatedFabricMod implements ModInitializer {
}
ServerLifecycleEvents.SERVER_STOPPING.register(($) -> {
if (isServer) {
if (IS_SERVER) {
holder.shutdown();
} else {
holder.disable();

View File

@@ -9,13 +9,13 @@
],
"contact": {
"website": "$url",
"repo": "https://github.com/GeyserMC/Floodgate-Modded"
"repo": "https://github.com/GeyserMC/Floodgate"
},
"license": "MIT",
"environment": "*",
"entrypoints": {
"main": [
"org.geysermc.floodgate.platform.fabric.FabricFloodgateMod"
"org.geysermc.floodgate.fabric.IsolatedFabricMod"
]
},
"accessWidener": "floodgate.accesswidener",

View File

@@ -0,0 +1 @@
org.geysermc.floodgate.fabric.FabricPlatform

View File

@@ -1,9 +1,11 @@
org.gradle.configureondemand=true
org.gradle.caching=true
org.gradle.parallel=true
# TODO https://github.com/architectury/architectury-plugin/pull/56
org.gradle.configuration-cache=false
group=org.geysermc
id=floodgate
description="Allows Bedrock players to join Java edition servers while keeping the server in online mode"
version=3.0.0-SNAPSHOT
micronautVersion=4.6.0
micronautVersion=4.6.0

View File

@@ -17,6 +17,7 @@ snakeyaml = "2.0"
bstats = "3.0.3"
adventure = "4.17.0"
adventure-platform = "4.3.4"
adventure-platform-modded = "6.6.0"
avaje-http = "2.7"
avaje-jsonb = "2.1"
expiringmap = "0.5.11"
@@ -48,7 +49,7 @@ fabric-permissions-api = "0.4.1-SNAPSHOT"
neoforge-version = "21.6.11-beta"
# buildSrc
indra = "3.1.3"
indra = "3.2.0"
shadow = "8.3.0"
gradle-idea-ext = "1.1.7"
checkerframework = "3.42.0"
@@ -83,8 +84,9 @@ bstats = { module = "org.bstats:bstats-base", version.ref = "bstats" }
adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure" }
adventure-key = { module = "net.kyori:adventure-key", version.ref = "adventure" }
adventure-text-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
adventure-platform-bukkit = { module = "net.kyori:adventure-platform-bukkit", version.ref = "adventure-platform"}
adventure-platform-bungee = { module = "net.kyori:adventure-platform-bungeecord", version.ref = "adventure-platform"}
adventure-platform-bukkit = { module = "net.kyori:adventure-platform-bukkit", version.ref = "adventure-platform" }
adventure-platform-bungee = { module = "net.kyori:adventure-platform-bungeecord", version.ref = "adventure-platform" }
adventure-platform-modded = { module = "net.kyori:adventure-platform-mod-shared", version.ref = "adventure-platform-modded" }
micronaut-inject = { module = "io.micronaut:micronaut-inject" }
micronaut-inject-java = { module = "io.micronaut:micronaut-inject-java" }

View File

@@ -17,6 +17,8 @@ dependencies {
annotationProcessor(libs.micronaut.inject.java)
compileOnlyApi(projects.isolation)
modApi(libs.adventure.platform.modded)
compileOnly(libs.mixin)
compileOnly(libs.asm)

View File

@@ -1,17 +1,27 @@
package org.geysermc.floodgate.mod;
import io.micronaut.context.ApplicationContext;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.floodgate.core.FloodgatePlatform;
import java.nio.file.Path;
import org.geysermc.floodgate.isolation.library.LibraryManager;
import org.slf4j.LoggerFactory;
public abstract class ModPlatform extends FloodgatePlatform {
protected ApplicationContext context;
protected ModPlatform(LibraryManager manager) {
super(manager);
}
@Override
protected void onContextCreated(ApplicationContext context) {
context.registerSingleton(LoggerFactory.getLogger("floodgate"));
this.context = context;
}
public @Nullable abstract Path resourcePath(String file);
public abstract boolean isClient();

View File

@@ -17,9 +17,9 @@ import org.geysermc.floodgate.core.util.Utils;
@Singleton
public final class ModDataAddon implements InjectorAddon<Channel> {
@Inject
DataSeeker dataSeeker;
@Inject
FloodgateDataHandler handshakeHandler;
@@ -47,14 +47,6 @@ public final class ModDataAddon implements InjectorAddon<Channel> {
channel.pipeline().addBefore(packetHandlerName, "floodgate_data_handler", dataHandler);
}
@Override
public void onChannelClosed(Channel channel) {
FloodgatePlayer player = channel.attr(playerAttribute).get();
if (player != null && api.setPendingRemove(player)) {
logger.translatedInfo("floodgate.ingame.disconnect_name", player.getCorrectUsername());
}
}
@Override
public void onRemoveInject(Channel channel) {
Utils.removeHandler(channel.pipeline(), "floodgate_data_handler");

View File

@@ -3,24 +3,25 @@ package org.geysermc.floodgate.mod.data;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.logging.LogUtils;
import io.micronaut.context.BeanProvider;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import jakarta.inject.Inject;
import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences;
import net.minecraft.DefaultUncaughtExceptionHandler;
import net.minecraft.network.Connection;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.handshake.ClientIntentionPacket;
import net.minecraft.network.protocol.login.ServerboundHelloPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.core.addon.data.CommonDataHandler;
import org.geysermc.floodgate.core.addon.data.CommonNettyDataHandler;
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.core.connection.DataSeeker;
import org.geysermc.floodgate.core.connection.FloodgateDataHandler;
import org.geysermc.floodgate.core.logger.FloodgateLogger;
import org.geysermc.floodgate.mod.mixin.ClientIntentionPacketMixinInterface;
import org.geysermc.floodgate.mod.mixin.ConnectionMixin;
import org.slf4j.Logger;
@@ -29,17 +30,33 @@ import java.net.InetSocketAddress;
public final class ModDataHandler extends CommonNettyDataHandler {
private static final Logger LOGGER = LogUtils.getLogger();
private final FloodgateLogger logger;
private Connection networkManager;
private FloodgatePlayer player;
// stellar, but, won't change this now
private org.geysermc.api.connection.Connection connection;
private boolean proxyData;
@Inject
BeanProvider<MinecraftServer> server;
@Inject
BeanProvider<MinecraftServerAudiences> audience;
public ModDataHandler(
FloodgateHandshakeHandler handshakeHandler,
DataSeeker dataSeeker,
FloodgateDataHandler dataHandler,
FloodgateConfig config,
AttributeKey<String> kickMessageAttribute, FloodgateLogger logger) {
super(handshakeHandler, config, kickMessageAttribute, new PacketBlocker());
this.logger = logger;
FloodgateLogger logger,
AttributeKey<org.geysermc.api.connection.Connection> connectionAttribute,
AttributeKey<net.kyori.adventure.text.Component> kickMessageAttribute) {
super(
dataSeeker,
dataHandler,
config,
logger,
connectionAttribute,
kickMessageAttribute,
new PacketBlocker()
);
}
@Override
@@ -56,21 +73,18 @@ public final class ModDataHandler extends CommonNettyDataHandler {
}
@Override
protected boolean shouldRemoveHandler(HandshakeResult result) {
player = result.getFloodgatePlayer();
protected boolean shouldRemoveHandler(FloodgateDataHandler.HandleResult result) {
connection = result.joinResult() != null ? result.joinResult().connection() : null;
if (getKickMessage() != null) {
// we also have to keep this handler if we want to kick then with a disconnect message
return false;
} else if (player == null) {
} else if (connection == null) {
// player is not a Floodgate player
return true;
}
if (result.getResultType() == FloodgateHandshakeHandler.ResultType.SUCCESS) {
logger.info("Floodgate player who is logged in as {} {} joined",
player.getCorrectUsername(), player.getCorrectUniqueId());
}
// TODO proxy data handling...?
// Handler will be removed after the login hello packet is handled
return false;
@@ -89,9 +103,9 @@ public final class ModDataHandler extends CommonNettyDataHandler {
private boolean checkAndHandleLogin(Object packet) {
if (packet instanceof ServerboundHelloPacket) {
String kickMessage = getKickMessage();
var kickMessage = getKickMessage();
if (kickMessage != null) {
Component message = Component.nullToEmpty(kickMessage);
Component message = audience.get().asNative(kickMessage);
// If possible, disconnect using the "proper" packet listener; otherwise there's no proper disconnect message
if (networkManager.getPacketListener() instanceof ServerLoginPacketListenerImpl loginPacketListener) {
loginPacketListener.disconnect(message);
@@ -108,9 +122,9 @@ public final class ModDataHandler extends CommonNettyDataHandler {
return true;
}
GameProfile gameProfile = new GameProfile(player.getCorrectUniqueId(), player.getCorrectUsername());
GameProfile gameProfile = new GameProfile(connection.javaUuid(), connection.javaUsername());
if (player.isLinked() && player.getCorrectUniqueId().version() == 4) {
if (connection.isLinked() && connection.javaUuid().version() == 4) {
verifyLinkedPlayerAsync(packetListener, gameProfile);
} else {
packetListener.startClientVerification(gameProfile);
@@ -135,7 +149,7 @@ public final class ModDataHandler extends CommonNettyDataHandler {
public void run() {
GameProfile effectiveProfile = gameProfile;
try {
MinecraftSessionService service = MinecraftServerHolder.get().getSessionService();
MinecraftSessionService service = server.get().getSessionService();
effectiveProfile = service.fetchProfile(effectiveProfile.getId(), true).profile();
} catch (Exception e) {
LOGGER.error("Unable to get Bedrock linked player textures for " + effectiveProfile.getName(), e);
@@ -151,7 +165,7 @@ public final class ModDataHandler extends CommonNettyDataHandler {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
if (config.isDebug()) {
if (config.debug()) {
LOGGER.error("Exception caught in FabricDataHandler", cause);
}
}

View File

@@ -1,6 +1,5 @@
package org.geysermc.floodgate.mod.inject;
import com.google.inject.Inject;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
@@ -9,26 +8,22 @@ import io.netty.channel.ChannelInitializer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.core.inject.CommonPlatformInjector;
import org.geysermc.floodgate.core.inject.Netty4PlatformInjector;
@RequiredArgsConstructor
public final class ModInjector extends CommonPlatformInjector {
public final class ModInjector extends Netty4PlatformInjector {
public static ModInjector INSTANCE = new ModInjector();
@Getter private final boolean injected = true;
@Inject private FloodgateLogger logger;
@Override
public void inject() throws Exception {
//no-op
//no-op, mixins go brrrrrr
}
public void injectClient(ChannelFuture future) {
if (future.channel().pipeline().names().contains("floodgate-init")) {
logger.debug("Tried to inject twice!");
if (isInjected()) {
return;
}
@@ -57,7 +52,7 @@ public final class ModInjector extends CommonPlatformInjector {
}
@Override
public void removeInjection() throws Exception {
public void removeInjection() {
//no-op
}

View File

@@ -1,26 +1,32 @@
package org.geysermc.floodgate.mod.listener;
import com.google.inject.Inject;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.core.util.LanguageManager;
import jakarta.inject.Inject;
import org.geysermc.floodgate.core.connection.ConnectionManager;
import java.util.UUID;
import org.geysermc.floodgate.core.util.LanguageManager;
public final class ModEventListener {
@Inject private FloodgateApi api;
@Inject private FloodgateLogger logger;
@Inject private LanguageManager languageManager;
@Inject
LanguageManager languageManager;
@Inject
ConnectionManager connectionManager;
public void onPlayerJoin(UUID uuid) {
FloodgatePlayer player = api.getPlayer(uuid);
if (player != null) {
logger.translatedInfo(
"floodgate.ingame.login_name",
player.getCorrectUsername(), player.getCorrectUniqueId()
);
languageManager.loadLocale(player.getLanguageCode());
// TODO this might be called late on fabric
var connection = connectionManager.findPendingConnection(uuid);
if (connection == null) {
return;
}
languageManager.loadLocale(connection.languageCode());
connectionManager.addAcceptedConnection(connection);
}
public void onPlayerQuit(UUID uuid) {
connectionManager.removeConnection(uuid);
}
}

View File

@@ -1,69 +0,0 @@
package org.geysermc.floodgate.mod.logger;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
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.core.config.FloodgateConfig;
import org.geysermc.floodgate.core.util.LanguageManager;
import static org.geysermc.floodgate.core.util.MessageFormatter.format;
@Singleton
public final class Log4jFloodgateLogger implements FloodgateLogger {
@Inject
@Named("logger")
private Logger logger;
private LanguageManager languageManager;
@Inject
private void init(LanguageManager languageManager, FloodgateConfig config) {
this.languageManager = languageManager;
if (config.isDebug() && !logger.isDebugEnabled()) {
Configurator.setLevel(logger.getName(), Level.DEBUG);
}
}
@Override
public void error(String message, Object... args) {
logger.error(message, args);
}
@Override
public void error(String message, Throwable throwable, Object... args) {
logger.error(format(message, args), throwable);
}
@Override
public void warn(String message, Object... args) {
logger.warn(message, args);
}
@Override
public void info(String message, Object... args) {
logger.info(message, args);
}
@Override
public void translatedInfo(String message, Object... args) {
logger.info(languageManager.getLogString(message, args));
}
@Override
public void debug(String message, Object... args) {
logger.debug(message, args);
}
@Override
public void trace(String message, Object... args) {
logger.trace(message, args);
}
@Override
public boolean isDebug() {
return logger.isDebugEnabled();
}
}

View File

@@ -1,55 +1,11 @@
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 io.micronaut.context.annotation.Bean;
import jakarta.inject.Named;
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;
}
public abstract class ModPlatformModule {
@Provides
@Named("packetEncoder")
@@ -63,15 +19,11 @@ public abstract class ModPlatformModule extends AbstractModule {
return FloodgateMod.INSTANCE.isClient() ? "inbound_config" : "decoder" ;
}
@Provides
@Bean
@Named("packetHandler")
public String packetHandler() {
return "packet_handler";
}
@Provides
@Singleton
public SkinApplier skinApplier() {
return new ModSkinApplier();
}
// TODO implementation name
}

View File

@@ -2,15 +2,16 @@ package org.geysermc.floodgate.mod.pluginmessage;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import io.micronaut.context.BeanProvider;
import jakarta.inject.Inject;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.server.MinecraftServer;
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.api.player.FloodgatePlayer;
import org.geysermc.api.connection.Connection;
import org.geysermc.floodgate.core.skin.SkinApplier;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
import org.geysermc.floodgate.mod.mixin.ChunkMapMixin;
import java.util.Collections;
@@ -19,12 +20,16 @@ import static org.geysermc.floodgate.api.event.skin.SkinApplyEvent.SkinData;
public final class ModSkinApplier implements SkinApplier {
@Inject
BeanProvider<MinecraftServer> server;
@Override
public void applySkin(@NonNull FloodgatePlayer floodgatePlayer, @NonNull SkinData skinData) {
MinecraftServerHolder.get().execute(() -> {
ServerPlayer bedrockPlayer = MinecraftServerHolder.get().getPlayerList()
.getPlayer(floodgatePlayer.getCorrectUniqueId());
public void applySkin(@NonNull Connection connection, @NonNull SkinData skinData) {
server.get().execute(() -> {
ServerPlayer bedrockPlayer = server.get().getPlayerList()
.getPlayer(connection.javaUuid());
if (bedrockPlayer == null) {
// TODO apply skins with delay???
// Disconnected probably?
return;
}
@@ -35,10 +40,10 @@ public final class ModSkinApplier implements SkinApplier {
properties.removeAll("textures");
properties.put("textures", new Property("textures", skinData.value(), skinData.signature()));
ChunkMap tracker = ((ServerLevel) bedrockPlayer.level).getChunkSource().chunkMap;
ChunkMap tracker = bedrockPlayer.level().getChunkSource().chunkMap;
ChunkMap.TrackedEntity entry = ((ChunkMapMixin) tracker).getEntityMap().get(bedrockPlayer.getId());
// Skin is applied - now it's time to refresh the player for everyone.
for (ServerPlayer otherPlayer : MinecraftServerHolder.get().getPlayerList().getPlayers()) {
for (ServerPlayer otherPlayer : server.get().getPlayerList().getPlayers()) {
boolean samePlayer = otherPlayer == bedrockPlayer;
if (!samePlayer) {
// TrackedEntity#broadcastRemoved doesn't actually remove them from seenBy
@@ -51,7 +56,7 @@ public final class ModSkinApplier implements SkinApplier {
continue;
}
if (bedrockPlayer.level == otherPlayer.level) {
if (bedrockPlayer.level() == otherPlayer.level()) {
entry.updatePlayer(otherPlayer);
}
}

View File

@@ -1,38 +1,48 @@
package org.geysermc.floodgate.mod.util;
import com.mojang.authlib.GameProfile;
import io.micronaut.context.BeanProvider;
import jakarta.inject.Inject;
import lombok.Setter;
import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.UserWhiteListEntry;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.api.GeyserApiBase;
import org.geysermc.floodgate.core.connection.audience.UserAudience;
import org.geysermc.floodgate.core.logger.FloodgateLogger;
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.Collection;
import java.util.UUID;
public final class ModCommandUtil extends CommandUtil {
private final FloodgateLogger logger;
private UserAudience console;
@Setter
private CommandManager<UserAudience> commandManager;
public ModCommandUtil(LanguageManager manager, FloodgateApi api, FloodgateLogger logger) {
BeanProvider<MinecraftServer> server;
BeanProvider<MinecraftServerAudiences> audience;
@Inject
public ModCommandUtil(
LanguageManager manager,
GeyserApiBase api,
BeanProvider<MinecraftServer> server,
BeanProvider<MinecraftServerAudiences> audience) {
super(manager, api);
this.logger = logger;
this.server = server;
this.audience = audience;
}
@Override
public @NonNull UserAudience getUserAudience(final @NonNull Object sourceObj) {
if (!(sourceObj instanceof CommandSourceStack stack)) {
throw new IllegalArgumentException();
throw new IllegalArgumentException("Source has to be a CommandSourceStack");
}
if (stack.getEntity() == null) {
if (console != null) {
@@ -58,19 +68,19 @@ public final class ModCommandUtil extends CommandUtil {
@Override
public Object getPlayerByUuid(@NonNull UUID uuid) {
ServerPlayer player = MinecraftServerHolder.get().getPlayerList().getPlayer(uuid);
ServerPlayer player = server.get().getPlayerList().getPlayer(uuid);
return player != null ? player : uuid;
}
@Override
public Object getPlayerByUsername(@NonNull String username) {
ServerPlayer player = MinecraftServerHolder.get().getPlayerList().getPlayerByName(username);
ServerPlayer player = server.get().getPlayerList().getPlayerByName(username);
return player != null ? player : username;
}
@Override
protected Collection<?> getOnlinePlayers() {
return MinecraftServerHolder.get().getPlayerList().getPlayers();
return server.get().getPlayerList().getPlayers();
}
@Override
@@ -79,35 +89,32 @@ public final class ModCommandUtil extends CommandUtil {
}
@Override
public void sendMessage(Object target, String message) {
public void sendMessage(Object target, net.kyori.adventure.text.Component message) {
CommandSourceStack commandSource = (CommandSourceStack) target;
if (commandSource.getEntity() instanceof ServerPlayer) {
MinecraftServerHolder.get().execute(() -> ((ServerPlayer) commandSource.getEntity())
.displayClientMessage(Component.literal(message), false));
} else {
// Console?
logger.info(message);
server.get().execute(() -> ((ServerPlayer) commandSource.getEntity())
.displayClientMessage(audience.get().asNative(message), false));
}
}
@Override
public void kickPlayer(Object o, String message) {
public void kickPlayer(Object o, net.kyori.adventure.text.Component message) {
if (o instanceof ServerPlayer player) {
player.connection.disconnect(Component.literal(message));
player.connection.disconnect(audience.get().asNative(message));
}
}
@Override
public boolean whitelistPlayer(UUID uuid, String username) {
GameProfile profile = new GameProfile(uuid, username);
MinecraftServerHolder.get().getPlayerList().getWhiteList().add(new UserWhiteListEntry(profile));
server.get().getPlayerList().getWhiteList().add(new UserWhiteListEntry(profile));
return true;
}
@Override
public boolean removePlayerFromWhitelist(UUID uuid, String username) {
GameProfile profile = new GameProfile(uuid, username);
MinecraftServerHolder.get().getPlayerList().getWhiteList().remove(profile);
server.get().getPlayerList().getWhiteList().remove(profile);
return true;
}
}

View File

@@ -1,13 +1,20 @@
package org.geysermc.floodgate.mod.util;
import io.micronaut.context.BeanProvider;
import jakarta.inject.Inject;
import net.minecraft.SharedConstants;
import net.minecraft.server.MinecraftServer;
import org.geysermc.floodgate.core.platform.util.PlatformUtils;
import org.geysermc.floodgate.mod.MinecraftServerHolder;
public class ModPlatformUtils extends PlatformUtils {
@Inject
BeanProvider<MinecraftServer> minecraftServer;
@Override
public AuthType authType() {
return MinecraftServerHolder.get().usesAuthentication() ? AuthType.ONLINE : AuthType.OFFLINE;
// TODO proxied auth type
return minecraftServer.get().usesAuthentication() ? AuthType.ONLINE : AuthType.OFFLINE;
}
@Override
@@ -17,6 +24,6 @@ public class ModPlatformUtils extends PlatformUtils {
@Override
public String serverImplementationName() {
return MinecraftServerHolder.get().getServerModName();
return minecraftServer.get().getServerModName();
}
}

View File

@@ -29,7 +29,7 @@ dependencies {
neoForge(libs.neoforge)
// "namedElements" configuration should be used to depend on different loom projects
common(project(":mod", configuration = "namedElements")) { isTransitive = false }
common(project(":mod", configuration = "namedElements"))
// Bundle transformed classes of the common module for production mod jar
shadow(project(path = ":mod", configuration = "transformProductionNeoForge")) { isTransitive = false }
@@ -54,8 +54,4 @@ tasks {
atAccessWideners.add("floodgate.accesswidener")
archiveBaseName.set("floodgate-neoforge")
}
// modrinth {
// loaders.add("neoforge")
// }
}

View File

@@ -34,7 +34,7 @@ arrayOf("common", "netty4").forEach {
project(id).projectDir = file("core/$it")
}
arrayOf("bungee", "spigot", "velocity").forEach { platform ->
arrayOf("bungee", "spigot", "velocity", "fabric").forEach { platform ->
arrayOf("base", "isolated").forEach {
var id = ":$platform-$it"
// isolated is the new default