Adding client-side and server-side interactions. Also adds purpur/paper support
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
plugins {
|
||||
id 'dev.architectury.loom'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
version = rootProject.mod_version
|
||||
group = rootProject.maven_group
|
||||
|
||||
base {
|
||||
archivesName = "${rootProject.archives_base_name}-1.21.1"
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def sharedRoot = file('../..')
|
||||
def prepareMainSources = tasks.register('prepareMainSources', Sync) {
|
||||
from(sharedRoot.toPath().resolve('src/main/java')) {
|
||||
exclude 'dev/yawaflua/gominecraftbridge/compat/MinecraftVersionAdapter.java'
|
||||
exclude 'dev/yawaflua/gominecraftbridge/network/AdminRequestPayload.java'
|
||||
exclude 'dev/yawaflua/gominecraftbridge/network/AdminResponsePayload.java'
|
||||
exclude 'dev/yawaflua/gominecraftbridge/network/BridgeAdminNetworking.java'
|
||||
}
|
||||
from(sharedRoot.toPath().resolve('src/main/generated'))
|
||||
from(file('src/main/java'))
|
||||
into(layout.buildDirectory.dir('version-sources/main'))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.setSrcDirs([layout.buildDirectory.dir('version-sources/main')])
|
||||
resources.setSrcDirs([file('src/main/resources')])
|
||||
}
|
||||
client {
|
||||
java.setSrcDirs([sharedRoot.toPath().resolve('src/client/java')])
|
||||
}
|
||||
test {
|
||||
java.setSrcDirs([sharedRoot.toPath().resolve('src/test/java'), file('src/test/java')])
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft 'com.mojang:minecraft:1.21.1'
|
||||
mappings loom.officialMojangMappings()
|
||||
modImplementation 'net.fabricmc:fabric-loader:0.16.14'
|
||||
modImplementation 'net.fabricmc.fabric-api:fabric-api:0.116.14+1.21.1'
|
||||
|
||||
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'
|
||||
|
||||
modClientImplementation 'me.shedaniel.cloth:cloth-config-fabric:15.0.140'
|
||||
modClientImplementation 'maven.modrinth:modmenu:11.0.3'
|
||||
|
||||
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 = 21
|
||||
}
|
||||
|
||||
tasks.named('compileJava') {
|
||||
dependsOn prepareMainSources
|
||||
}
|
||||
|
||||
tasks.withType(Test).configureEach {
|
||||
useJUnitPlatform()
|
||||
jvmArgs '--enable-native-access=ALL-UNNAMED'
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
tasks.named('sourcesJar') {
|
||||
dependsOn prepareMainSources
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create('mavenJava', MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -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 1.21.1 implementation of the shared version adapter. */
|
||||
public final class MinecraftVersionAdapter {
|
||||
private MinecraftVersionAdapter() {
|
||||
}
|
||||
|
||||
public static String gameVersion() {
|
||||
return SharedConstants.getCurrentVersion().getName();
|
||||
}
|
||||
|
||||
public static String dimension(Level level) {
|
||||
return level.dimension().location().toString();
|
||||
}
|
||||
|
||||
public static long dayTime(ServerLevel level) {
|
||||
return level.getDayTime();
|
||||
}
|
||||
|
||||
public static boolean isOperator(MinecraftServer server, ServerPlayer player) {
|
||||
return server.getPlayerList().isOp(player.getGameProfile());
|
||||
}
|
||||
}
|
||||
+29
@@ -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.ResourceLocation;
|
||||
|
||||
public record AdminRequestPayload(String action, String pluginId) implements CustomPacketPayload {
|
||||
public static final Type<AdminRequestPayload> TYPE = new Type<>(
|
||||
ResourceLocation.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;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
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.ResourceLocation;
|
||||
|
||||
public record AdminResponsePayload(String json) implements CustomPacketPayload {
|
||||
// 1.21.1 rejects a custom payload at 1 MiB. Leave room for the channel,
|
||||
// packet framing, VarInts and UTF-8 expansion.
|
||||
public static final int MAX_JSON_CHARS = 900 * 1024;
|
||||
public static final Type<AdminResponsePayload> TYPE = new Type<>(
|
||||
ResourceLocation.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;
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
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.ManagedPluginSnapshot;
|
||||
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.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Minecraft 1.21.1 networking registration overlay. */
|
||||
public final class BridgeAdminNetworking {
|
||||
private BridgeAdminNetworking() {
|
||||
}
|
||||
|
||||
public static void register(GoPluginManager plugins) {
|
||||
PayloadTypeRegistry.playC2S().register(AdminRequestPayload.TYPE, AdminRequestPayload.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(AdminResponsePayload.TYPE, AdminResponsePayload.CODEC);
|
||||
|
||||
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) {
|
||||
context.responseSender().sendPacket(new AdminResponsePayload(boundedJson(snapshot)));
|
||||
}
|
||||
|
||||
/** Keeps the encoded 1.21.1 payload below its fixed 1 MiB protocol limit. */
|
||||
static String boundedJson(BridgeManagementSnapshot snapshot) {
|
||||
String json = ProtocolJson.GSON.toJson(snapshot);
|
||||
if (fits(json)) {
|
||||
return json;
|
||||
}
|
||||
|
||||
for (int retainedLogs : new int[]{64, 16, 4, 0}) {
|
||||
List<ManagedPluginSnapshot> plugins = new ArrayList<>(snapshot.plugins().size());
|
||||
for (ManagedPluginSnapshot plugin : snapshot.plugins()) {
|
||||
int from = Math.max(0, plugin.logs().size() - retainedLogs);
|
||||
plugins.add(new ManagedPluginSnapshot(
|
||||
plugin.metadata(), plugin.state(), plugin.backend(), plugin.origin(),
|
||||
plugin.logs().subList(from, plugin.logs().size())
|
||||
));
|
||||
}
|
||||
BridgeManagementSnapshot trimmed = new BridgeManagementSnapshot(
|
||||
snapshot.generatedAtUnixMilli(), snapshot.serverRunning(), snapshot.canReload(),
|
||||
"Response was shortened to fit the Minecraft 1.21.1 payload limit",
|
||||
snapshot.packages(), plugins
|
||||
);
|
||||
json = ProtocolJson.GSON.toJson(trimmed);
|
||||
if (fits(json)) {
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
return ProtocolJson.GSON.toJson(new BridgeManagementSnapshot(
|
||||
snapshot.generatedAtUnixMilli(), snapshot.serverRunning(), snapshot.canReload(),
|
||||
"Management data exceeded the Minecraft 1.21.1 payload limit; reload or inspect fewer packages",
|
||||
List.of(), List.of()
|
||||
));
|
||||
}
|
||||
|
||||
private static boolean fits(String json) {
|
||||
return json.length() <= AdminResponsePayload.MAX_JSON_CHARS
|
||||
&& json.getBytes(StandardCharsets.UTF_8).length <= AdminResponsePayload.MAX_JSON_CHARS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "go_minecraft_bridge",
|
||||
"version": "${version}",
|
||||
"name": "Go Minecraft Bridge",
|
||||
"description": "A native Go plugin host with server and client runtimes.",
|
||||
"authors": ["yawaflua"],
|
||||
"license": "All-Rights-Reserved",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": ["dev.yawaflua.gominecraftbridge.GoMinecraftBridgeMod"],
|
||||
"client": ["dev.yawaflua.gominecraftbridge.client.GoMinecraftBridgeClient"],
|
||||
"modmenu": ["dev.yawaflua.gominecraftbridge.client.GoBridgeModMenuIntegration"]
|
||||
},
|
||||
"depends": {
|
||||
"fabricloader": ">=0.16.14",
|
||||
"minecraft": "~1.21.1",
|
||||
"java": ">=21",
|
||||
"fabric-api": "*"
|
||||
},
|
||||
"suggests": {
|
||||
"cloth-config": ">=15.0.140",
|
||||
"modmenu": ">=11.0.3"
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package dev.yawaflua.gominecraftbridge.network;
|
||||
|
||||
import dev.yawaflua.gominecraftbridge.management.BridgeManagementSnapshot;
|
||||
import dev.yawaflua.gominecraftbridge.management.ManagedPluginSnapshot;
|
||||
import dev.yawaflua.gominecraftbridge.protocol.PluginEnvironment;
|
||||
import dev.yawaflua.gominecraftbridge.protocol.PluginLog;
|
||||
import dev.yawaflua.gominecraftbridge.protocol.PluginMetadata;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BridgeAdminNetworking1211Test {
|
||||
@Test
|
||||
void managementJsonIsTrimmedBelowTheLegacyPayloadLimit() {
|
||||
String hugeMessage = "Ж".repeat(8192);
|
||||
List<PluginLog> logs = java.util.stream.IntStream.range(0, 500)
|
||||
.mapToObj(index -> new PluginLog("sdk", "info", hugeMessage, index))
|
||||
.toList();
|
||||
PluginMetadata metadata = new PluginMetadata(
|
||||
"large", "Large", "1", "", List.of(), null, 2, null, PluginEnvironment.BOTH
|
||||
);
|
||||
BridgeManagementSnapshot snapshot = new BridgeManagementSnapshot(
|
||||
1, true, true, null, List.of(),
|
||||
List.of(new ManagedPluginSnapshot(metadata, "running", "native", "/plugin.so", logs))
|
||||
);
|
||||
|
||||
String json = BridgeAdminNetworking.boundedJson(snapshot);
|
||||
|
||||
assertTrue(json.length() <= AdminResponsePayload.MAX_JSON_CHARS);
|
||||
assertTrue(json.getBytes(StandardCharsets.UTF_8).length <= AdminResponsePayload.MAX_JSON_CHARS);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -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());
|
||||
}
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
}
|
||||
+68
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user