Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions CODEC_CONVERSION_STRATEGY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Codec Conversion Strategy for Legacy JSON Parsing

This document outlines the systematic approach for converting legacy JSON parsing logic to use Minecraft codecs.

## Completed Conversions

### Recipe.java
- ✅ Full codec implementation with registry-based type dispatch
- ✅ All recipe types converted (Blasting, Shaped, Shapeless, Special types, etc.)
- ✅ Backward compatibility maintained with legacy `fromJson` methods
- ✅ Comprehensive tests to ensure identical behavior

### FloatProvider.java
- ✅ Full codec implementation with union type handling
- ✅ All provider types converted (Constant, Uniform, ClampedNormal, Trapezoid)
- ✅ Support for both direct float values and object syntax
- ✅ Comprehensive tests covering all scenarios

### JsonUtils.SingleOrList
- ✅ Enhanced with codec support for either/list patterns
- ✅ Generic codec method for reuse across types

## Conversion Pattern

The established pattern for converting legacy JSON parsing to codecs:

1. **Add Codec Imports**
```java
import net.minestom.server.codec.Codec;
import net.minestom.server.codec.StructCodec;
import net.minestom.server.registry.DynamicRegistry;
```

2. **Create Main Codec Field**
```java
@NotNull Codec<YourType> CODEC = makeCodec();

private static StructCodec<YourType> makeCodec() {
return Codec.RegistryTaggedUnion(registries -> {
class Holder {
static final @NotNull DynamicRegistry<StructCodec<? extends YourType>> CODEC = createDefaultRegistry();
}
return Holder.CODEC;
}, YourType::codec, "type");
}
```

3. **Implement Registry Setup**
```java
private static DynamicRegistry<StructCodec<? extends YourType>> createDefaultRegistry() {
var registry = DynamicRegistry.<StructCodec<? extends YourType>>create("your_type");
registry.register(Key.key("minecraft:type1"), Type1.CODEC);
registry.register(Key.key("minecraft:type2"), Type2.CODEC);
return registry;
}
```

4. **Add Codec Method to Interface**
```java
@NotNull StructCodec<? extends YourType> codec();
```

5. **Implement Codecs for Each Type**
```java
record Type1(String field1, int field2) implements YourType {
public static final @NotNull StructCodec<Type1> CODEC = StructCodec.struct(
"field1", Codec.STRING, Type1::field1,
"field2", Codec.INT, Type1::field2,
Type1::new
);

@Override
public @NotNull StructCodec<? extends YourType> codec() {
return CODEC;
}
}
```

6. **Maintain Backward Compatibility**
```java
// Keep the legacy method
static YourType fromJson(JsonReader reader) throws IOException {
// ... existing implementation
}
```

7. **Add Comprehensive Tests**
- Test codec structure compiles
- Test parsing produces identical results
- Test all type variants
- Test error cases

## Priority Conversion Targets

Based on analysis, these are good candidates for conversion:

1. **NumberProvider** - Similar pattern to FloatProvider, but with Int/Double variants
2. **HeightProvider** - Union types with VerticalAnchor dependency
3. **DensityFunction** - Complex type system with many variants
4. **Noise** - Relatively simple with a few types
5. **Biome** - Complex but well-structured

## Implementation Notes

- Always preserve backward compatibility during transition
- Use `orElse()` for fallback handling (e.g., raw numbers vs objects)
- Leverage existing codec patterns from loot-table module
- Test extensively to ensure identical behavior
- Document conversion strategy and patterns

## Next Steps

1. Continue converting remaining types using the established pattern
2. Implement actual codec-based parsing integration with Minestom's codec system
3. Gradually replace legacy `JsonUtils.unionStringTypeAdapted` calls
4. Update dependent classes to use new codec-based parsing
5. Remove legacy parsing code once transition is complete

## Testing Strategy

Each conversion should include:
- Codec structure compilation tests
- Behavior compatibility tests comparing legacy vs codec results
- Edge case handling tests
- Error condition tests
- Performance comparison tests (optional)
10 changes: 10 additions & 0 deletions datapack-loading/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,14 @@ dependencies {
compileOnly(project(":mojang-data"))
implementation("space.vectrix.flare:flare:2.0.1")
implementation("space.vectrix.flare:flare-fastutil:2.0.1")

// Test dependencies
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation(project(":core"))
testImplementation(project(":mojang-data"))
}

tasks.test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.squareup.moshi.JsonReader;
import net.kyori.adventure.key.Key;
import net.minestom.server.codec.Codec;
import net.minestom.vanilla.datapack.DatapackLoader;
import okio.Buffer;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -53,6 +54,13 @@ static <T> SingleOrList<T> fromJson(Type elementType, JsonReader reader) throws
return new List<>(builder.build().toList());
}

static <T> Codec<SingleOrList<T>> codec(Codec<T> elementCodec) {
return Codec.either(
elementCodec.transform(Single::new, Single::asObject),
elementCodec.list().transform(List::new, List::asList)
).cast();
}

record Single<O>(O object) implements SingleOrList<O> {
@Override
public boolean isObject() {
Expand Down Expand Up @@ -143,6 +151,39 @@ public static <T> T unionStringTypeMapAdapted(JsonReader reader, String key, Map
return unionStringTypeMap(reader, key, adaptedMap);
}

/**
* Bridge method to help transition from legacy JSON parsing to codec-based parsing.
* This method provides a way to use codec-based parsing while maintaining the same interface
* as the legacy unionStringTypeAdapted method.
*/
public static <T> T unionStringTypeCodecBased(JsonReader reader, String key, Codec<T> codec) throws IOException {
// Extract the JSON string from the reader for codec processing
try {
// For now, we'll read the entire JSON into a string and parse it with legacy method
// In the future, this would use the codec directly with the Minestom codec system
String json = reader.nextSource().readUtf8();

// This is a placeholder - in a full implementation, this would use the codec system
// to parse the JSON directly without going through the legacy JsonReader
throw new UnsupportedOperationException("Full codec integration not yet implemented. Use legacy methods for now.");

} catch (Exception e) {
throw new IOException("Failed to parse with codec", e);
}
}

/**
* Utility method to demonstrate how codec-based parsing could work alongside legacy parsing
* during the transition period.
*/
public static <T> IoFunction<JsonReader, T> codecAdapter(Codec<T> codec) {
return reader -> {
// This demonstrates how we could bridge between JsonReader and Codec
// In practice, this would need to integrate with Minestom's codec system
throw new UnsupportedOperationException("Codec adapter not yet fully implemented");
};
}

public static <V, T> T unionMapType(JsonReader reader, String key, IoFunction<JsonReader, V> read, Function<V, IoFunction<JsonReader, T>> findReader) throws IOException {
// Fetch the property
V property;
Expand Down
Loading