Adding client-side and server-side interactions. Also adds purpur/paper support

This commit is contained in:
Dmitri Shimanski
2026-07-20 05:41:57 +03:00
parent fbe4d786ca
commit ce93e99962
71 changed files with 5068 additions and 279 deletions
+95
View File
@@ -0,0 +1,95 @@
plugins {
id 'net.fabricmc.fabric-loom'
id 'maven-publish'
}
version = rootProject.mod_version
group = rootProject.maven_group
base {
archivesName = "${rootProject.archives_base_name}-26.1.2"
}
repositories {
mavenCentral()
maven { url 'https://maven.shedaniel.me/' }
maven { url 'https://maven.terraformersmc.com/releases/' }
maven { url 'https://api.modrinth.com/maven/' }
}
loom {
splitEnvironmentSourceSets()
mods {
go_minecraft_bridge {
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}
runs {
configureEach {
vmArg '--enable-native-access=ALL-UNNAMED'
}
}
}
sourceSets {
main {
java.srcDirs rootProject.file('src/main/java'), rootProject.file('src/main/generated')
resources.srcDirs rootProject.file('src/main/resources')
}
client {
java.srcDirs rootProject.file('src/client/java')
}
test {
java.srcDirs rootProject.file('src/test/java')
}
}
dependencies {
minecraft 'com.mojang:minecraft:26.1.2'
implementation 'net.fabricmc:fabric-loader:0.19.3'
implementation 'net.fabricmc.fabric-api:fabric-api:0.149.1+26.1.2'
implementation 'com.google.code.gson:gson:2.13.2'
implementation include('com.google.flatbuffers:flatbuffers-java:25.2.10')
implementation 'net.java.dev.jna:jna:5.18.1'
clientImplementation 'me.shedaniel.cloth:cloth-config-fabric:26.1.154'
clientImplementation 'maven.modrinth:modmenu:18.0.0'
testImplementation platform('org.junit:junit-bom:5.13.4')
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
processResources {
inputs.property 'version', project.version
filesMatching('fabric.mod.json') {
expand 'version': project.version
}
}
tasks.withType(JavaCompile).configureEach {
options.release = 25
}
tasks.withType(Test).configureEach {
useJUnitPlatform()
jvmArgs '--enable-native-access=ALL-UNNAMED'
}
java {
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
}
publishing {
publications {
create('mavenJava', MavenPublication) {
from components.java
}
}
}
@@ -0,0 +1,29 @@
package dev.yawaflua.gominecraftbridge.compat;
import net.minecraft.SharedConstants;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
/** Minecraft-version-specific names kept behind one small source overlay. */
public final class MinecraftVersionAdapter {
private MinecraftVersionAdapter() {
}
public static String gameVersion() {
return SharedConstants.getCurrentVersion().name();
}
public static String dimension(Level level) {
return level.dimension().identifier().toString();
}
public static long dayTime(ServerLevel level) {
return level.getDefaultClockTime();
}
public static boolean isOperator(MinecraftServer server, ServerPlayer player) {
return server.getPlayerList().isOp(player.nameAndId());
}
}
@@ -0,0 +1,29 @@
package dev.yawaflua.gominecraftbridge.network;
import dev.yawaflua.gominecraftbridge.GoMinecraftBridgeMod;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.Identifier;
public record AdminRequestPayload(String action, String pluginId) implements CustomPacketPayload {
public static final Type<AdminRequestPayload> TYPE = new Type<>(
Identifier.fromNamespaceAndPath(GoMinecraftBridgeMod.MOD_ID, "admin_request")
);
public static final StreamCodec<RegistryFriendlyByteBuf, AdminRequestPayload> CODEC =
CustomPacketPayload.codec(AdminRequestPayload::write, AdminRequestPayload::new);
public AdminRequestPayload(RegistryFriendlyByteBuf buffer) {
this(buffer.readUtf(32), buffer.readUtf(64));
}
private void write(RegistryFriendlyByteBuf buffer) {
buffer.writeUtf(this.action, 32);
buffer.writeUtf(this.pluginId == null ? "" : this.pluginId, 64);
}
@Override
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}
@@ -0,0 +1,29 @@
package dev.yawaflua.gominecraftbridge.network;
import dev.yawaflua.gominecraftbridge.GoMinecraftBridgeMod;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.Identifier;
public record AdminResponsePayload(String json) implements CustomPacketPayload {
public static final int MAX_JSON_CHARS = 2 * 1024 * 1024;
public static final Type<AdminResponsePayload> TYPE = new Type<>(
Identifier.fromNamespaceAndPath(GoMinecraftBridgeMod.MOD_ID, "admin_response")
);
public static final StreamCodec<RegistryFriendlyByteBuf, AdminResponsePayload> CODEC =
CustomPacketPayload.codec(AdminResponsePayload::write, AdminResponsePayload::new);
public AdminResponsePayload(RegistryFriendlyByteBuf buffer) {
this(buffer.readUtf(MAX_JSON_CHARS));
}
private void write(RegistryFriendlyByteBuf buffer) {
buffer.writeUtf(this.json, MAX_JSON_CHARS);
}
@Override
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}
@@ -0,0 +1,68 @@
package dev.yawaflua.gominecraftbridge.network;
import dev.yawaflua.gominecraftbridge.compat.MinecraftVersionAdapter;
import dev.yawaflua.gominecraftbridge.host.GoPluginManager;
import dev.yawaflua.gominecraftbridge.management.BridgeManagementSnapshot;
import dev.yawaflua.gominecraftbridge.management.ReloadResult;
import dev.yawaflua.gominecraftbridge.protocol.ProtocolJson;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import java.util.List;
public final class BridgeAdminNetworking {
private BridgeAdminNetworking() {
}
public static void register(GoPluginManager plugins) {
PayloadTypeRegistry.serverboundPlay().register(AdminRequestPayload.TYPE, AdminRequestPayload.CODEC);
PayloadTypeRegistry.clientboundPlay().registerLarge(
AdminResponsePayload.TYPE,
AdminResponsePayload.CODEC,
AdminResponsePayload.MAX_JSON_CHARS * 4 + 256
);
ServerPlayNetworking.registerGlobalReceiver(AdminRequestPayload.TYPE, (payload, context) -> {
boolean allowed = MinecraftVersionAdapter.isOperator(context.server(), context.player());
if (!allowed) {
send(context, withoutDetails(plugins.managementSnapshot(
false,
"Administrator permission is required"
)));
return;
}
String message = null;
if ("reload".equals(payload.action())) {
ReloadResult result = plugins.reload(payload.pluginId(), context.server());
message = result.message();
} else if ("rescan".equals(payload.action())) {
ReloadResult result = plugins.rescan(context.server());
message = result.message();
} else if (!"refresh".equals(payload.action())) {
message = "Unknown admin action: " + payload.action();
}
send(context, plugins.managementSnapshot(true, message));
});
}
static BridgeManagementSnapshot withoutDetails(BridgeManagementSnapshot snapshot) {
return new BridgeManagementSnapshot(
snapshot.generatedAtUnixMilli(),
snapshot.serverRunning(),
false,
snapshot.message(),
List.of(),
List.of()
);
}
private static void send(
ServerPlayNetworking.Context context,
BridgeManagementSnapshot snapshot
) {
String json = ProtocolJson.GSON.toJson(snapshot);
context.responseSender().sendPacket(new AdminResponsePayload(json));
}
}