Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.21] Codec conversion adjustments #274

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.wispforest.owo.mixin;

import net.minecraft.registry.RegistryOps;
import net.minecraft.registry.RegistryWrapper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(RegistryOps.CachedRegistryInfoGetter.class)
public interface CachedRegistryInfoGetterAccessor {
@Accessor("registriesLookup") RegistryWrapper.WrapperLookup owo$getRegistriesLookup();
}
169 changes: 92 additions & 77 deletions src/main/java/io/wispforest/owo/serialization/CodecUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.*;
import io.netty.buffer.ByteBuf;
import io.wispforest.endec.Endec;
import io.wispforest.endec.SerializationContext;
import io.wispforest.endec.StructEndec;
import io.wispforest.endec.*;
import io.wispforest.endec.format.bytebuf.ByteBufDeserializer;
import io.wispforest.endec.format.bytebuf.ByteBufSerializer;
import io.wispforest.endec.format.edm.*;
Expand All @@ -15,15 +13,18 @@
import io.wispforest.owo.serialization.endec.EitherEndec;
import io.wispforest.owo.serialization.endec.MinecraftEndecs;
import io.wispforest.owo.serialization.format.edm.EdmOps;
import io.wispforest.owo.util.Scary;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.registry.RegistryOps;
import net.minecraft.util.dynamic.ForwardingDynamicOps;
import org.jetbrains.annotations.ApiStatus;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class CodecUtils {
Expand All @@ -47,18 +48,12 @@ public class CodecUtils {
public static <T> Endec<T> toEndec(Codec<T> codec) {
return Endec.of(
(ctx, serializer, value) -> {
DynamicOps<EdmElement<?>> ops = EdmOps.withContext(ctx);
if (ctx.hasAttribute(RegistriesAttribute.REGISTRIES)) {
ops = RegistryOps.of(ops, ctx.getAttributeValue(RegistriesAttribute.REGISTRIES).infoGetter());
}
var ops = createEdmOps(ctx);

EdmEndec.INSTANCE.encode(ctx, serializer, codec.encodeStart(ops, value).getOrThrow(IllegalStateException::new));
},
(ctx, deserializer) -> {
DynamicOps<EdmElement<?>> ops = EdmOps.withContext(ctx);
if (ctx.hasAttribute(RegistriesAttribute.REGISTRIES)) {
ops = RegistryOps.of(ops, ctx.getAttributeValue(RegistriesAttribute.REGISTRIES).infoGetter());
}
var ops = createEdmOps(ctx);

return codec.parse(ops, EdmEndec.INSTANCE.decode(ctx, deserializer)).getOrThrow(IllegalStateException::new);
}
Expand All @@ -76,10 +71,7 @@ public static <T> Endec<T> toEndec(Codec<T> codec, PacketCodec<ByteBuf, T> packe
return;
}

DynamicOps<EdmElement<?>> ops = EdmOps.withContext(ctx);
if (ctx.hasAttribute(RegistriesAttribute.REGISTRIES)) {
ops = RegistryOps.of(ops, ctx.getAttributeValue(RegistriesAttribute.REGISTRIES).infoGetter());
}
var ops = createEdmOps(ctx);

EdmEndec.INSTANCE.encode(ctx, serializer, codec.encodeStart(ops, value).getOrThrow(IllegalStateException::new));
},
Expand All @@ -89,10 +81,7 @@ public static <T> Endec<T> toEndec(Codec<T> codec, PacketCodec<ByteBuf, T> packe
return packetCodec.decode(buffer);
}

DynamicOps<EdmElement<?>> ops = EdmOps.withContext(ctx);
if (ctx.hasAttribute(RegistriesAttribute.REGISTRIES)) {
ops = RegistryOps.of(ops, ctx.getAttributeValue(RegistriesAttribute.REGISTRIES).infoGetter());
}
var ops = createEdmOps(ctx);

return codec.parse(ops, EdmEndec.INSTANCE.decode(ctx, deserializer)).getOrThrow(IllegalStateException::new);
}
Expand Down Expand Up @@ -136,43 +125,17 @@ public static <T> Codec<T> toCodec(Endec<T> endec, SerializationContext assumedC
return new Codec<>() {
@Override
public <D> DataResult<Pair<T, D>> decode(DynamicOps<D> ops, D input) {
try {
var rootOps = ops;
while (rootOps instanceof ForwardingDynamicOps<D>) rootOps = ((ForwardingDynamicOpsAccessor<D>) rootOps).owo$delegate();

var context = rootOps instanceof EdmOps edmOps
? edmOps.capturedContext().and(assumedContext)
: assumedContext;

if (ops instanceof RegistryOps<D> registryOps) {
context = context.withAttributes(RegistriesAttribute.infoGetterOnly(((RegistryOpsAccessor) registryOps).owo$infoGetter()));
}

return DataResult.success(new Pair<>(endec.decode(context, LenientEdmDeserializer.of(ops.convertTo(EdmOps.withoutContext(), input))), input));
} catch (Exception e) {
return DataResult.error(e::getMessage);
}
return captureThrows(() -> {
return new Pair<>(endec.decode(createContext(ops, assumedContext), LenientEdmDeserializer.of(ops.convertTo(EdmOps.withoutContext(), input))), input);
});
}

@Override
@SuppressWarnings("unchecked")
public <D> DataResult<D> encode(T input, DynamicOps<D> ops, D prefix) {
try {
var rootOps = ops;
while (rootOps instanceof ForwardingDynamicOps<D>) rootOps = ((ForwardingDynamicOpsAccessor<D>) rootOps).owo$delegate();

var context = rootOps instanceof EdmOps edmOps
? edmOps.capturedContext().and(assumedContext)
: assumedContext;

if (ops instanceof RegistryOps<D> registryOps) {
context = context.withAttributes(RegistriesAttribute.infoGetterOnly(((RegistryOpsAccessor) registryOps).owo$infoGetter()));
}

return DataResult.success(EdmOps.withoutContext().convertTo(ops, endec.encodeFully(context, EdmSerializer::of, input)));
} catch (Exception e) {
return DataResult.error(e::getMessage);
}
return captureThrows(() -> {
return EdmOps.withoutContext().convertTo(ops, endec.encodeFully(createContext(ops, assumedContext), EdmSerializer::of, input));
});
}
};
}
Expand All @@ -190,7 +153,7 @@ public <T1> Stream<T1> keys(DynamicOps<T1> ops) {

@Override
public <T1> DataResult<T> decode(DynamicOps<T1> ops, MapLike<T1> input) {
try {
return captureThrows(() -> {
var map = new HashMap<String, EdmElement<?>>();
input.entries().forEach(pair -> {
map.put(
Expand All @@ -200,36 +163,14 @@ public <T1> DataResult<T> decode(DynamicOps<T1> ops, MapLike<T1> input) {
);
});

var rootOps = ops;
while (rootOps instanceof ForwardingDynamicOps<T1>) rootOps = ((ForwardingDynamicOpsAccessor<T1>) ops).owo$delegate();

var context = rootOps instanceof EdmOps edmOps
? edmOps.capturedContext().and(assumedContext)
: assumedContext;

if (ops instanceof RegistryOps<T1> registryOps) {
context = context.withAttributes(RegistriesAttribute.infoGetterOnly(((RegistryOpsAccessor) registryOps).owo$infoGetter()));
}

return DataResult.success(structEndec.decode(context, LenientEdmDeserializer.of(EdmElement.wrapMap(map))));
} catch (Exception e) {
return DataResult.error(e::getMessage);
}
return structEndec.decode(createContext(ops, assumedContext), LenientEdmDeserializer.of(EdmElement.wrapMap(map)));
});
}

@Override
public <T1> RecordBuilder<T1> encode(T input, DynamicOps<T1> ops, RecordBuilder<T1> prefix) {
try {
var rootOps = ops;
while (rootOps instanceof ForwardingDynamicOps<T1>) rootOps = ((ForwardingDynamicOpsAccessor<T1>) ops).owo$delegate();

var context = rootOps instanceof EdmOps edmOps
? edmOps.capturedContext().and(assumedContext)
: assumedContext;

if (ops instanceof RegistryOps<T1> registryOps) {
context = context.withAttributes(RegistriesAttribute.infoGetterOnly(((RegistryOpsAccessor) registryOps).owo$infoGetter()));
}
var context = createContext(ops, assumedContext);

var element = structEndec.encodeFully(context, EdmSerializer::of, input).<Map<String, EdmElement<?>>>cast();

Expand All @@ -250,6 +191,45 @@ public static <T> MapCodec<T> toMapCodec(StructEndec<T> structEndec) {
return toMapCodec(structEndec, SerializationContext.empty());
}

/*
* This method overall should be fine but do not expect such tot work always as it could be a problem as
* it bypasses certain features about Deserializer API that may be an issue but is low chance for general
* cases within Minecraft.
*
* blodhgarm: 21.07.2024
*/
@Scary
@ApiStatus.Experimental
public static <T> StructEndec<T> toStructEndec(MapCodec<T> mapCodec) {
return new StructEndec<T>() {
@Override
public void encodeStruct(SerializationContext ctx, Serializer<?> serializer, Serializer.Struct struct, T value) {
var ops = createEdmOps(ctx);

var edmMap = mapCodec.encode(value, ops, ops.mapBuilder()).build(ops.emptyMap())
.getOrThrow(IllegalStateException::new)
.asMap();

if(serializer instanceof SelfDescribedSerializer<?>) {
edmMap.value().forEach((s, element) -> struct.field(s, ctx, EdmEndec.INSTANCE, element));
} else {
struct.field("element", ctx, EdmEndec.MAP, edmMap);
}
}

@Override
public T decodeStruct(SerializationContext ctx, Deserializer<?> deserializer, Deserializer.Struct struct) {
var edmMap = ((deserializer instanceof SelfDescribedDeserializer<?>)
? EdmEndec.MAP.decode(ctx, deserializer)
: struct.field("element", ctx, EdmEndec.MAP));

var ops = createEdmOps(ctx);

return mapCodec.decode(ops, ops.getMap(edmMap).getOrThrow(IllegalStateException::new)).getOrThrow(IllegalStateException::new);
}
};
}

// the fact that we lose context here is certainly far from ideal,
// but for the most part *shouldn't* matter. after all, ideally nobody
// should ever be nesting packet codecs into endecs - there's little
Expand Down Expand Up @@ -278,4 +258,39 @@ public void encode(B buf, T value) {
}
};
}

//--

public static SerializationContext createContext(DynamicOps<?> ops, SerializationContext assumedContext) {
var rootOps = ops;
while (rootOps instanceof ForwardingDynamicOps<?>) rootOps = ((ForwardingDynamicOpsAccessor<?>) rootOps).owo$delegate();

var context = rootOps instanceof EdmOps edmOps
? edmOps.capturedContext().and(assumedContext)
: assumedContext;

if (ops instanceof RegistryOps<?> registryOps) {
context = context.withAttributes(RegistriesAttribute.tryFromCachedInfoGetter(((RegistryOpsAccessor) registryOps).owo$infoGetter()));
}

return context;
}

public static DynamicOps<EdmElement<?>> createEdmOps(SerializationContext ctx) {
DynamicOps<EdmElement<?>> ops = EdmOps.withContext(ctx);

if (ctx.hasAttribute(RegistriesAttribute.REGISTRIES)) {
ops = RegistryOps.of(ops, ctx.getAttributeValue(RegistriesAttribute.REGISTRIES).infoGetter());
}

return ops;
}

private static <T> DataResult<T> captureThrows(Supplier<T> action) {
try {
return DataResult.success(action.get());
} catch (Exception e) {
return DataResult.error(e::getMessage);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.wispforest.owo.serialization;

import io.wispforest.endec.SerializationAttribute;
import io.wispforest.owo.mixin.CachedRegistryInfoGetterAccessor;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryOps;
import org.jetbrains.annotations.ApiStatus;
Expand All @@ -27,7 +28,23 @@ public static RegistriesAttribute of(DynamicRegistryManager registryManager) {
}

@ApiStatus.Internal
public static RegistriesAttribute infoGetterOnly(RegistryOps.RegistryInfoGetter lookup) {
public static RegistriesAttribute tryFromCachedInfoGetter(RegistryOps.RegistryInfoGetter lookup) {
return (lookup instanceof RegistryOps.CachedRegistryInfoGetter cachedGetter)
? fromCachedInfoGetter(cachedGetter)
: fromInfoGetter(lookup);
}

public static RegistriesAttribute fromCachedInfoGetter(RegistryOps.CachedRegistryInfoGetter cachedGetter) {
DynamicRegistryManager registryManager = null;

if(((CachedRegistryInfoGetterAccessor) (Object) cachedGetter).owo$getRegistriesLookup() instanceof DynamicRegistryManager drm) {
registryManager = drm;
}

return new RegistriesAttribute(cachedGetter, registryManager);
}

public static RegistriesAttribute fromInfoGetter(RegistryOps.RegistryInfoGetter lookup) {
return new RegistriesAttribute(lookup, null);
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/wispforest/owo/util/Scary.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.wispforest.owo.util;

/**
* Annotations used to indicate that a given whatever is design in some manor that may or may not
* cause you pain. Combined it might scare your skeleton out of your body having it run down the block.
*/
public @interface Scary {
}
3 changes: 2 additions & 1 deletion src/main/resources/owo.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"ui.SimpleRegistryAccessor",
"ui.SlotAccessor",
"ui.SlotMixin",
"ui.access.BlockEntityAccessor"
"ui.access.BlockEntityAccessor",
"CachedRegistryInfoGetterAccessor"
],
"client": [
"ClientConfigurationNetworkHandlerMixin",
Expand Down
Loading