Skip to content

Commit 41d7d8c

Browse files
committed
Merge remote-tracking branch 'origin/feature/field-aware-diagnostics' into feature/field-aware-diagnostics
2 parents 0e62942 + 2c9a2b7 commit 41d7d8c

2 files changed

Lines changed: 62 additions & 52 deletions

File tree

aether-datafixers-api/src/main/java/de/splatgames/aether/datafixers/api/rewrite/Rules.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2552,7 +2552,13 @@ private static String[] splitPath(@NotNull final String path) {
25522552
}
25532553

25542554
/**
2555-
* Recursively sets a value at a path, creating intermediate objects.
2555+
* Recursive helper for setAtPath that navigates the path segments and sets the value at the end.
2556+
*
2557+
* @param dynamic the current dynamic object, must not be {@code null}
2558+
* @param parts the path segments, must not be {@code null}
2559+
* @param index the current index in the path segments
2560+
* @param value the value to set at the target path, must not be {@code null}
2561+
* @return the updated dynamic with the value set at the target path
25562562
*/
25572563
@NotNull
25582564
private static Dynamic<Object> setAtPathRecursive(@NotNull final Dynamic<Object> dynamic,

docs/getting-started/your-first-migration.md

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,10 @@ Define the data structure at version 100:
6969
```java
7070
package com.example.game.schema;
7171

72-
import de.splatgames.aether.datafixers.api.DataVersion;
7372
import de.splatgames.aether.datafixers.api.dsl.DSL;
7473
import de.splatgames.aether.datafixers.api.schema.Schema;
74+
import de.splatgames.aether.datafixers.api.type.TypeRegistry;
75+
import de.splatgames.aether.datafixers.api.type.template.TypeTemplate;
7576
import de.splatgames.aether.datafixers.core.type.SimpleTypeRegistry;
7677
import com.example.game.TypeReferences;
7778

@@ -87,20 +88,30 @@ import com.example.game.TypeReferences;
8788
public class Schema100 extends Schema {
8889

8990
public Schema100() {
90-
super(new DataVersion(100), null, SimpleTypeRegistry::new);
91+
super(100, null); // No parent - this is the first version
92+
}
93+
94+
@Override
95+
protected TypeRegistry createTypeRegistry() {
96+
return new SimpleTypeRegistry();
9197
}
9298

9399
@Override
94100
protected void registerTypes() {
95-
registerType(TypeReferences.PLAYER, DSL.and(
101+
registerType(TypeReferences.PLAYER, player());
102+
}
103+
104+
/** Player type template for v1.0.0 */
105+
public static TypeTemplate player() {
106+
return DSL.and(
96107
DSL.field("playerName", DSL.string()),
97108
DSL.field("xp", DSL.intType()),
98109
DSL.field("x", DSL.doubleType()),
99110
DSL.field("y", DSL.doubleType()),
100111
DSL.field("z", DSL.doubleType()),
101112
DSL.field("gameMode", DSL.intType()),
102113
DSL.remainder()
103-
));
114+
);
104115
}
105116
}
106117
```
@@ -114,9 +125,10 @@ Define the updated structure:
114125
```java
115126
package com.example.game.schema;
116127

117-
import de.splatgames.aether.datafixers.api.DataVersion;
118128
import de.splatgames.aether.datafixers.api.dsl.DSL;
119129
import de.splatgames.aether.datafixers.api.schema.Schema;
130+
import de.splatgames.aether.datafixers.api.type.TypeRegistry;
131+
import de.splatgames.aether.datafixers.api.type.template.TypeTemplate;
120132
import de.splatgames.aether.datafixers.core.type.SimpleTypeRegistry;
121133
import com.example.game.TypeReferences;
122134

@@ -131,23 +143,33 @@ import com.example.game.TypeReferences;
131143
*/
132144
public class Schema110 extends Schema {
133145

134-
public Schema110(Schema parent) {
135-
super(new DataVersion(110), parent, SimpleTypeRegistry::new);
146+
public Schema110() {
147+
super(110, new Schema100()); // Extends from v1.0.0
148+
}
149+
150+
@Override
151+
protected TypeRegistry createTypeRegistry() {
152+
return new SimpleTypeRegistry();
136153
}
137154

138155
@Override
139156
protected void registerTypes() {
140-
registerType(TypeReferences.PLAYER, DSL.and(
157+
registerType(TypeReferences.PLAYER, player());
158+
}
159+
160+
/** Player type template for v1.1.0 */
161+
public static TypeTemplate player() {
162+
return DSL.and(
141163
DSL.field("name", DSL.string()),
142164
DSL.field("experience", DSL.intType()),
143165
DSL.field("position", position()),
144166
DSL.field("gameMode", DSL.string()),
145167
DSL.remainder()
146-
));
168+
);
147169
}
148170

149171
/** Position type template */
150-
public static DSL.TypeTemplate position() {
172+
public static TypeTemplate position() {
151173
return DSL.and(
152174
DSL.field("x", DSL.doubleType()),
153175
DSL.field("y", DSL.doubleType()),
@@ -172,40 +194,45 @@ import de.splatgames.aether.datafixers.api.rewrite.Rules;
172194
import de.splatgames.aether.datafixers.api.rewrite.TypeRewriteRule;
173195
import de.splatgames.aether.datafixers.api.schema.Schema;
174196
import de.splatgames.aether.datafixers.api.schema.SchemaRegistry;
197+
import de.splatgames.aether.datafixers.codec.json.gson.GsonOps;
175198
import de.splatgames.aether.datafixers.core.fix.SchemaDataFix;
176-
import com.example.game.TypeReferences;
199+
import org.jetbrains.annotations.NotNull;
177200

178201
/**
179202
* Migrates player data from v1.0.0 (100) to v1.1.0 (110).
180203
*/
181-
public class PlayerV1ToV2Fix extends SchemaDataFix {
204+
public class PlayerV100ToV110Fix extends SchemaDataFix {
182205

183-
public PlayerV1ToV2Fix(SchemaRegistry schemas) {
206+
public PlayerV100ToV110Fix(SchemaRegistry schemas) {
184207
super(
185-
"player_v1_to_v2",
208+
"player_v100_to_v110",
186209
new DataVersion(100),
187210
new DataVersion(110),
188211
schemas
189212
);
190213
}
191214

192215
@Override
193-
protected TypeRewriteRule makeRule(Schema inputSchema, Schema outputSchema) {
216+
@NotNull
217+
protected TypeRewriteRule makeRule(@NotNull Schema inputSchema,
218+
@NotNull Schema outputSchema) {
194219
return Rules.seq(
195220
// 1. Rename fields
196-
Rules.renameField(TypeReferences.PLAYER, "playerName", "name"),
197-
Rules.renameField(TypeReferences.PLAYER, "xp", "experience"),
221+
Rules.renameField(GsonOps.INSTANCE, "playerName", "name"),
222+
Rules.renameField(GsonOps.INSTANCE, "xp", "experience"),
198223

199224
// 2. Transform gameMode from int to string
200-
Rules.transformField(TypeReferences.PLAYER, "gameMode", this::gameModeToString),
225+
Rules.transformField(GsonOps.INSTANCE, "gameMode",
226+
PlayerV100ToV110Fix::gameModeToString),
201227

202228
// 3. Group coordinates into position object
203-
Rules.transform(TypeReferences.PLAYER, this::groupPosition)
229+
Rules.groupFields(GsonOps.INSTANCE, "position", "x", "y", "z")
204230
);
205231
}
206232

207-
private Dynamic<?> gameModeToString(Dynamic<?> value) {
208-
int mode = value.asInt().orElse(0);
233+
@NotNull
234+
private static Dynamic<?> gameModeToString(@NotNull Dynamic<?> value) {
235+
int mode = value.asInt().result().orElse(0);
209236
String modeName = switch (mode) {
210237
case 0 -> "survival";
211238
case 1 -> "creative";
@@ -215,26 +242,6 @@ public class PlayerV1ToV2Fix extends SchemaDataFix {
215242
};
216243
return value.createString(modeName);
217244
}
218-
219-
private Dynamic<?> groupPosition(Dynamic<?> player) {
220-
// Extract coordinates
221-
double x = player.get("x").asDouble().orElse(0.0);
222-
double y = player.get("y").asDouble().orElse(0.0);
223-
double z = player.get("z").asDouble().orElse(0.0);
224-
225-
// Create position object
226-
Dynamic<?> position = player.emptyMap()
227-
.set("x", player.createDouble(x))
228-
.set("y", player.createDouble(y))
229-
.set("z", player.createDouble(z));
230-
231-
// Remove old fields and add position
232-
return player
233-
.remove("x")
234-
.remove("y")
235-
.remove("z")
236-
.set("position", position);
237-
}
238245
}
239246
```
240247

@@ -254,6 +261,7 @@ import de.splatgames.aether.datafixers.api.schema.SchemaRegistry;
254261
import com.example.game.fix.PlayerV1ToV2Fix;
255262
import com.example.game.schema.Schema100;
256263
import com.example.game.schema.Schema110;
264+
import org.jetbrains.annotations.NotNull;
257265

258266
/**
259267
* Bootstrap for the game data fixer.
@@ -266,21 +274,18 @@ public class GameDataBootstrap implements DataFixerBootstrap {
266274
private SchemaRegistry schemas;
267275

268276
@Override
269-
public void registerSchemas(SchemaRegistry schemas) {
277+
public void registerSchemas(@NotNull SchemaRegistry schemas) {
270278
this.schemas = schemas;
271279

272280
// Register schemas in version order
273-
Schema100 v100 = new Schema100();
274-
Schema110 v110 = new Schema110(v100);
275-
276-
schemas.register(v100);
277-
schemas.register(v110);
281+
schemas.register(new Schema100());
282+
schemas.register(new Schema110());
278283
}
279284

280285
@Override
281-
public void registerFixes(FixRegistrar fixes) {
286+
public void registerFixes(@NotNull FixRegistrar fixes) {
282287
// Register fixes
283-
fixes.register(TypeReferences.PLAYER, new PlayerV1ToV2Fix(schemas));
288+
fixes.register(TypeReferences.PLAYER, new PlayerV100ToV110Fix(schemas));
284289
}
285290
}
286291
```
@@ -337,7 +342,6 @@ public class GameExample {
337342

338343
// 5. Print result
339344
System.out.println("\n=== Migrated Data (v1.1.0) ===");
340-
@SuppressWarnings("unchecked")
341345
Dynamic<JsonElement> result = (Dynamic<JsonElement>) migrated.value();
342346
System.out.println(GSON.toJson(result.value()));
343347
}
@@ -398,7 +402,7 @@ Define all type references in one class for easy discovery.
398402

399403
### 4. Use Parent Schemas
400404

401-
Chain schemas: `Schema110(v100)` inherits from `Schema100`.
405+
Each schema creates its own parent internally: `Schema110` extends `Schema100` via `super(110, new Schema100())`.
402406

403407
### 5. Test Your Fixes
404408

0 commit comments

Comments
 (0)