Skip to content

Commit

Permalink
Finish 1.20.6 update
Browse files Browse the repository at this point in the history
- Update versions in fabric.mod.json
- Fix Java version everywhere
- Migrate ScreenNetworking to use codecs instead of raw buffers
  • Loading branch information
Juuxel committed May 5, 2024
1 parent 73d08c0 commit cd1f114
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 122 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ on: [pull_request, push]

jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: setup jdk 17
uses: actions/setup-java@v2
uses: gradle/actions/wrapper-validation@v3
- name: setup jdk 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
java-version: 21
- name: make gradle wrapper executable
if: ${{ runner.os != 'Windows' }}
run: chmod +x ./gradlew
Expand Down
15 changes: 3 additions & 12 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,6 @@ loom {
}
}

// Work around https://github.com/FabricMC/fabric-loom/issues/890.
afterEvaluate {
for (def config : [configurations.apiElements, configurations.runtimeElements]) {
def parents = new HashSet<>(config.extendsFrom)
parents.removeIf { it.name.startsWith 'modTestMod' }
config.extendsFrom = parents
}
}

repositories {
maven {
url "https://server.bbkr.space/artifactory/libs-release"
Expand Down Expand Up @@ -117,16 +108,16 @@ java {
withSourcesJar()
withJavadocJar()

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

tasks.withType(JavaCompile) {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
options.encoding = "UTF-8"
options.release.set 17
options.release = 21
}

jar {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ org.gradle.jvmargs=-Xmx1G
# check these on https://fabricmc.net/develop
minecraft_version=1.20.6
yarn_mappings=1.20.6+build.1
loader_version=0.15.10
loader_version=0.15.11

# Mod Properties
mod_version = 9.2.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,14 @@ public void setTitlePos(Vec2i titlePos) {
this.titlePos = titlePos;
}

/**
* {@return the world of this GUI description's player}
* @since 10.0.0
*/
public World getWorld() {
return world;
}

/**
* Gets the network side this GUI description runs on.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,142 +1,149 @@
package io.github.cottonmc.cotton.gui.impl;

import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.Decoder;
import com.mojang.serialization.Encoder;
import com.mojang.serialization.Lifecycle;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.NbtSizeTracker;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryOps;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.util.Identifier;

import io.github.cottonmc.cotton.gui.SyncedGuiDescription;
import io.github.cottonmc.cotton.gui.networking.NetworkSide;
import io.github.cottonmc.cotton.gui.networking.ScreenNetworking;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

public class ScreenNetworkingImpl implements ScreenNetworking {
// Packet structure:
// syncId: int
// message: identifier
// rest: buf

public record ScreenMessageData(int syncId, Identifier message, PacketByteBuf buf) {
public static final PacketCodec<RegistryByteBuf, ScreenMessageData> PACKET_CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, ScreenMessageData::syncId,
Identifier.PACKET_CODEC, ScreenMessageData::message,
PacketCodec.of(PacketByteBuf::writeBytes, packetBuf -> packetBuf.readBytes(PacketByteBufs.create())), ScreenMessageData::buf,
ScreenMessageData::new
// Matches the one used in PacketCodecs.codec() etc
private static final long MAX_NBT_SIZE = 0x200000L;

public record ScreenMessage(int syncId, Identifier message, NbtElement nbt) implements CustomPayload {
public static final Id<ScreenMessage> ID = new Id<>(new Identifier(LibGuiCommon.MOD_ID, "screen_message"));
public static final PacketCodec<RegistryByteBuf, ScreenMessage> CODEC = PacketCodec.tuple(
PacketCodecs.INTEGER, ScreenMessage::syncId,
Identifier.PACKET_CODEC, ScreenMessage::message,
PacketCodecs.nbt(() -> NbtSizeTracker.of(MAX_NBT_SIZE)), ScreenMessage::nbt,
ScreenMessage::new
);
}

public record S2CScreenMessage(ScreenMessageData data) implements CustomPayload {
public static final Id<S2CScreenMessage> PACKET_ID = new Id<>(new Identifier(LibGuiCommon.MOD_ID, "screen_message_s2c"));
public static final PacketCodec<RegistryByteBuf, S2CScreenMessage> PACKET_CODEC = ScreenMessageData.PACKET_CODEC.xmap(S2CScreenMessage::new, S2CScreenMessage::data);

@Override
public Id<? extends CustomPayload> getId() {
return PACKET_ID;
}
}

public record C2SScreenMessage(ScreenMessageData data) implements CustomPayload {
public static final Id<C2SScreenMessage> PACKET_ID = new Id<>(new Identifier(LibGuiCommon.MOD_ID, "screen_message_c2s"));
public static final PacketCodec<RegistryByteBuf, C2SScreenMessage> PACKET_CODEC = ScreenMessageData.PACKET_CODEC.xmap(C2SScreenMessage::new, C2SScreenMessage::data);

@Override
public Id<? extends CustomPayload> getId() {
return PACKET_ID;
return ID;
}
}

private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOGGER = LoggerFactory.getLogger(ScreenNetworkingImpl.class);
private static final Map<SyncedGuiDescription, ScreenNetworkingImpl> instanceCache = new WeakHashMap<>();

private final Map<Identifier, MessageReceiver> messages = new HashMap<>();
private SyncedGuiDescription description;
private final Map<Identifier, ReceiverData<?>> receivers = new HashMap<>();
private final SyncedGuiDescription description;
private final NetworkSide side;

private ScreenNetworkingImpl(SyncedGuiDescription description, NetworkSide side) {
this.description = description;
this.side = side;
}

public void receive(Identifier message, MessageReceiver receiver) {
private static RegistryOps<NbtElement> getRegistryOps(DynamicRegistryManager registryManager) {
return registryManager.getOps(NbtOps.INSTANCE);
}

@Override
public <D> void receive(Identifier message, Decoder<D> decoder, MessageReceiver<D> receiver) {
Objects.requireNonNull(message, "message");
Objects.requireNonNull(decoder, "decoder");
Objects.requireNonNull(receiver, "receiver");

if (!messages.containsKey(message)) {
messages.put(message, receiver);
if (!receivers.containsKey(message)) {
receivers.put(message, new ReceiverData<>(decoder, receiver));
} else {
throw new IllegalStateException("Message " + message + " on side " + side + " already registered");
}
}

@Override
public void send(Identifier message, Consumer<PacketByteBuf> writer) {
public <D> void send(Identifier message, Encoder<D> encoder, D data) {
Objects.requireNonNull(message, "message");
Objects.requireNonNull(writer, "writer");
Objects.requireNonNull(encoder, "encoder");

PacketByteBuf buf = PacketByteBufs.create();
writer.accept(buf);
ScreenMessageData data = new ScreenMessageData(description.syncId, message, buf);
description.getPacketSender().sendPacket(side == NetworkSide.SERVER ? new S2CScreenMessage(data) : new C2SScreenMessage(data));
var ops = getRegistryOps(description.getWorld().getRegistryManager());
NbtElement encoded = encoder.encodeStart(ops, data).getOrThrow();
ScreenMessage packet = new ScreenMessage(description.syncId, message, encoded);
description.getPacketSender().sendPacket(packet);
}

public static void init() {
PayloadTypeRegistry.playS2C().register(S2CScreenMessage.PACKET_ID, S2CScreenMessage.PACKET_CODEC);
PayloadTypeRegistry.playC2S().register(C2SScreenMessage.PACKET_ID, C2SScreenMessage.PACKET_CODEC);
ServerPlayNetworking.registerGlobalReceiver(C2SScreenMessage.PACKET_ID, (payload, context) -> {
handle(context.player().server, context.player(), payload.data());
PayloadTypeRegistry.playS2C().register(ScreenMessage.ID, ScreenMessage.CODEC);
PayloadTypeRegistry.playC2S().register(ScreenMessage.ID, ScreenMessage.CODEC);
ServerPlayNetworking.registerGlobalReceiver(ScreenMessage.ID, (payload, context) -> {
handle(context.player().server, context.player(), payload);
});
}

public static void handle(Executor executor, PlayerEntity player, ScreenMessageData data) {
public static void handle(Executor executor, PlayerEntity player, ScreenMessage packet) {
ScreenHandler screenHandler = player.currentScreenHandler;

if (!(screenHandler instanceof SyncedGuiDescription)) {
LOGGER.error("Received message packet for screen handler {} which is not a SyncedGuiDescription", screenHandler);
return;
} else if (data.syncId() != screenHandler.syncId) {
LOGGER.error("Received message for sync ID {}, current sync ID: {}", data.syncId(), screenHandler.syncId);
} else if (packet.syncId() != screenHandler.syncId) {
LOGGER.error("Received message for sync ID {}, current sync ID: {}", packet.syncId(), screenHandler.syncId);
return;
}

ScreenNetworkingImpl networking = instanceCache.get(screenHandler);

if (networking != null) {
MessageReceiver receiver = networking.messages.get(data.message());

if (receiver != null) {
data.buf().retain();
executor.execute(() -> {
try {
receiver.onMessage(data.buf());
} catch (Exception e) {
LOGGER.error("Error handling screen message {} for {} on side {}", data.message(), screenHandler, networking.side, e);
} finally {
data.buf().release();
}
});
ReceiverData<?> receiverData = networking.receivers.get(packet.message());
if (receiverData != null) {
processMessage(executor, player, packet, screenHandler, receiverData);
} else {
LOGGER.warn("Message {} not registered for {} on side {}", data.message(), screenHandler, networking.side);
LOGGER.error("Message {} not registered for {} on side {}", packet.message(), screenHandler, networking.side);
}
} else {
LOGGER.warn("GUI description {} does not use networking", screenHandler);
}
}

private static <D> void processMessage(Executor executor, PlayerEntity player, ScreenMessage packet, ScreenHandler description, ReceiverData<D> receiverData) {
var ops = getRegistryOps(player.getRegistryManager());
var result = receiverData.decoder().parse(ops, packet.nbt());

switch (result) {
case DataResult.Success(D data, Lifecycle lifecycle) -> executor.execute(() -> {
try {
receiverData.receiver().onMessage(data);
} catch (Exception e) {
LOGGER.error("Error handling screen message {} for {}", packet.message(), description, e);
}
});

case DataResult.Error<D> error -> LOGGER.error(
"Could not parse screen message {}: {}",
packet.message(),
error.message()
);
}
}

public static ScreenNetworking of(SyncedGuiDescription description, NetworkSide networkSide) {
Objects.requireNonNull(description, "description");
Objects.requireNonNull(networkSide, "networkSide");
Expand All @@ -148,6 +155,9 @@ public static ScreenNetworking of(SyncedGuiDescription description, NetworkSide
}
}

private record ReceiverData<D>(Decoder<D> decoder, MessageReceiver<D> receiver) {
}

private static final class DummyNetworking extends ScreenNetworkingImpl {
static final DummyNetworking INSTANCE = new DummyNetworking();

Expand All @@ -156,12 +166,12 @@ private DummyNetworking() {
}

@Override
public void receive(Identifier message, MessageReceiver receiver) {
public <D> void receive(Identifier message, Decoder<D> decoder, MessageReceiver<D> receiver) {
// NO-OP
}

@Override
public void send(Identifier message, Consumer<PacketByteBuf> writer) {
public <D> void send(Identifier message, Encoder<D> encoder, D data) {
// NO-OP
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public class LibGuiClient implements ClientModInitializer {
public void onInitializeClient() {
config = loadConfig();

ClientPlayNetworking.registerGlobalReceiver(ScreenNetworkingImpl.S2CScreenMessage.PACKET_ID, (payload, context) -> {
ScreenNetworkingImpl.handle(context.client(), context.player(), payload.data());
ClientPlayNetworking.registerGlobalReceiver(ScreenNetworkingImpl.ScreenMessage.ID, (payload, context) -> {
ScreenNetworkingImpl.handle(context.client(), context.player(), payload);
});

LibGuiShaders.register();
Expand Down
Loading

0 comments on commit cd1f114

Please sign in to comment.