55import com .mojang .brigadier .exceptions .Dynamic2CommandExceptionType ;
66import com .mojang .brigadier .exceptions .DynamicCommandExceptionType ;
77import com .mojang .brigadier .exceptions .SimpleCommandExceptionType ;
8+ import com .mojang .datafixers .util .Pair ;
89import com .mojang .serialization .Codec ;
10+ import com .mojang .serialization .DataResult ;
911import com .mojang .serialization .DynamicOps ;
12+ import dev .latvian .mods .kubejs .error .KubeRuntimeException ;
1013import dev .latvian .mods .kubejs .plugin .KubeJSPlugin ;
1114import dev .latvian .mods .kubejs .plugin .KubeJSPlugins ;
15+ import dev .latvian .mods .kubejs .script .SourceLine ;
1216import dev .latvian .mods .kubejs .util .Cast ;
1317import dev .latvian .mods .kubejs .util .ID ;
1418import dev .latvian .mods .kubejs .util .Lazy ;
19+ import dev .latvian .mods .kubejs .util .RegistryAccessContainer ;
20+ import dev .latvian .mods .rhino .BaseFunction ;
21+ import dev .latvian .mods .rhino .Context ;
22+ import dev .latvian .mods .rhino .EvaluatorException ;
1523import dev .latvian .mods .rhino .NativeJavaMap ;
24+ import dev .latvian .mods .rhino .Undefined ;
1625import dev .latvian .mods .rhino .type .TypeInfo ;
26+ import net .minecraft .Util ;
1727import net .minecraft .core .component .DataComponentMap ;
1828import net .minecraft .core .component .DataComponentPatch ;
1929import net .minecraft .core .component .DataComponentPredicate ;
2030import net .minecraft .core .component .DataComponentType ;
2131import net .minecraft .core .component .DataComponents ;
32+ import net .minecraft .core .component .PatchedDataComponentMap ;
2233import net .minecraft .core .registries .BuiltInRegistries ;
34+ import net .minecraft .core .registries .Registries ;
2335import net .minecraft .nbt .NbtOps ;
2436import net .minecraft .nbt .Tag ;
2537import net .minecraft .nbt .TagParser ;
3244import java .lang .reflect .ParameterizedType ;
3345import java .util .HashSet ;
3446import java .util .Map ;
47+ import java .util .Objects ;
3548import java .util .Set ;
49+ import java .util .function .Consumer ;
50+ import java .util .function .Function ;
51+ import java .util .stream .Collectors ;
52+ import java .util .stream .Stream ;
53+
54+ import static com .mojang .serialization .DataResult .error ;
55+ import static com .mojang .serialization .DataResult .success ;
3656
3757public interface DataComponentWrapper {
38- DynamicCommandExceptionType ERROR_UNKNOWN_COMPONENT = new DynamicCommandExceptionType (( object ) -> Component .translatableEscape ("arguments.item.component.unknown" , object ));
58+ DynamicCommandExceptionType ERROR_UNKNOWN_COMPONENT = new DynamicCommandExceptionType (object -> Component .translatableEscape ("arguments.item.component.unknown" , object ));
3959 Dynamic2CommandExceptionType ERROR_MALFORMED_COMPONENT = new Dynamic2CommandExceptionType ((object , object2 ) -> Component .translatableEscape ("arguments.item.component.malformed" , object , object2 ));
4060 SimpleCommandExceptionType ERROR_EXPECTED_COMPONENT = new SimpleCommandExceptionType (Component .translatable ("arguments.item.component.expected" ));
61+
62+ TypeInfo COMPONENT_TYPE = TypeInfo .of (DataComponentType .class );
63+
4164 Lazy <Map <DataComponentType <?>, TypeInfo >> TYPE_INFOS = Lazy .identityMap (map -> {
4265 try {
4366 for (var field : DataComponents .class .getDeclaredFields ()) {
@@ -46,7 +69,7 @@ public interface DataComponentWrapper {
4669 && Modifier .isStatic (field .getModifiers ())
4770 && field .getGenericType () instanceof ParameterizedType t
4871 ) {
49- var key = (DataComponentType ) field .get (null );
72+ @ SuppressWarnings ( "rawtypes" ) var key = (DataComponentType ) field .get (null );
5073 var typeInfo = TypeInfo .of (t .getActualTypeArguments ()[0 ]);
5174 map .put (key , typeInfo );
5275 }
@@ -122,7 +145,7 @@ static DataComponentMap readMap(@Nullable DynamicOps<Tag> registryOps, StringRea
122145 builder = DataComponentMap .builder ();
123146 }
124147
125- builder .set (dataComponentType , Cast .to (dataResult .getOrThrow (( string ) -> {
148+ builder .set (dataComponentType , Cast .to (dataResult .getOrThrow (string -> {
126149 reader .setCursor (i );
127150 return ERROR_MALFORMED_COMPONENT .createWithContext (reader , dataComponentType .toString (), string );
128151 })));
@@ -203,7 +226,7 @@ static DataComponentPatch readPatch(@Nullable DynamicOps<Tag> registryOps, Strin
203226 builder = DataComponentPatch .builder ();
204227 }
205228
206- builder .set (dataComponentType , Cast .to (dataResult .getOrThrow (( string ) -> {
229+ builder .set (dataComponentType , Cast .to (dataResult .getOrThrow (string -> {
207230 reader .setCursor (i );
208231 return ERROR_MALFORMED_COMPONENT .createWithContext (reader , dataComponentType .toString (), string );
209232 })));
@@ -261,6 +284,7 @@ static boolean filter(Object from, TypeInfo target) {
261284 return from == null || from instanceof DataComponentMap || from instanceof DataComponentPatch || from instanceof Map || from instanceof NativeJavaMap || from instanceof String s && (s .isEmpty () || s .charAt (0 ) == '[' );
262285 }
263286
287+ @ Deprecated (forRemoval = true )
264288 static DataComponentMap mapOf (@ Nullable DynamicOps <Tag > ops , Object o ) {
265289 try {
266290 return readMap (ops , new StringReader (o .toString ()));
@@ -269,6 +293,7 @@ static DataComponentMap mapOf(@Nullable DynamicOps<Tag> ops, Object o) {
269293 }
270294 }
271295
296+ @ Deprecated (forRemoval = true )
272297 static DataComponentMap mapOrEmptyOf (@ Nullable DynamicOps <Tag > ops , Object o ) {
273298 try {
274299 return readMap (ops , new StringReader (o .toString ()));
@@ -277,6 +302,7 @@ static DataComponentMap mapOrEmptyOf(@Nullable DynamicOps<Tag> ops, Object o) {
277302 }
278303 }
279304
305+ @ Deprecated (forRemoval = true )
280306 static DataComponentPatch patchOf (@ Nullable DynamicOps <Tag > ops , Object o ) {
281307 try {
282308 return readPatch (ops , new StringReader (o .toString ()));
@@ -285,6 +311,7 @@ static DataComponentPatch patchOf(@Nullable DynamicOps<Tag> ops, Object o) {
285311 }
286312 }
287313
314+ @ Deprecated (forRemoval = true )
288315 static DataComponentPatch patchOrEmptyOf (@ Nullable DynamicOps <Tag > ops , Object o ) {
289316 try {
290317 return readPatch (ops , new StringReader (o .toString ()));
@@ -293,6 +320,207 @@ static DataComponentPatch patchOrEmptyOf(@Nullable DynamicOps<Tag> ops, Object o
293320 }
294321 }
295322
323+ static DataComponentMap mapOf (Context cx , Object from ) {
324+ return tryMapOf (cx , from )
325+ .getOrThrow (error -> new KubeRuntimeException ("Failed to warp DataComponentMap: %s" .formatted (error ))
326+ .source (SourceLine .of (cx )));
327+ }
328+
329+ static DataComponentPatch patchOf (Context cx , Object from ) {
330+ return tryPatchOf (cx , from )
331+ .getOrThrow (error -> new KubeRuntimeException ("Failed to warp DataComponentMap: %s" .formatted (error ))
332+ .source (SourceLine .of (cx )));
333+ }
334+
335+ static DataComponentMap mapOrEmptyOf (Context cx , Object from ) {
336+ return tryMapOf (cx , from )
337+ .resultOrPartial ()
338+ .orElse (DataComponentMap .EMPTY );
339+ }
340+
341+ static DataComponentPatch patchOrEmptyOf (Context cx , Object from ) {
342+ return tryPatchOf (cx , from )
343+ .resultOrPartial ()
344+ .orElse (DataComponentPatch .EMPTY );
345+ }
346+
347+ static DataResult <DataComponentMap > tryMapOf (Context cx , @ Nullable Object o ) {
348+ return switch (o ) {
349+ case DataComponentMap map -> success (map );
350+ case DataComponentPatch patch -> success (PatchedDataComponentMap .fromPatch (DataComponentMap .EMPTY , patch ));
351+ case BaseFunction fn -> fnToBuilder (cx , MapBuilder .class , fn ,
352+ builder -> Util .make (DataComponentMap .builder (), builder ).build ());
353+ case Map <?, ?> map -> {
354+ var reg = RegistryAccessContainer .of (cx );
355+ var builder = DataComponentMap .builder ();
356+
357+ var failed = false ;
358+ Stream .Builder <Pair <DataComponentType <?>, String >> errors = Stream .builder ();
359+
360+ Map <DataComponentType <?>, ?> wrapped = Objects .requireNonNull (cx .optionalMapOf (map , COMPONENT_TYPE , TypeInfo .NONE ));
361+
362+ for (var entry : wrapped .entrySet ()) {
363+ var type = entry .getKey ();
364+ var valueType = getTypeInfo (type );
365+
366+ var value = entry .getValue ();
367+
368+ if (cx .canConvert (value , valueType )) {
369+ try {
370+ Object converted = cx .jsToJava (value , valueType );
371+ if (converted != null ) {
372+ //noinspection rawtypes, unchecked
373+ builder .set ((DataComponentType ) type , converted );
374+ continue ;
375+ }
376+ } catch (EvaluatorException e ) {
377+ failed = true ;
378+ errors .add (Pair .of (type , "Failed to parse data component from input '%s': %s" .formatted (value , e )));
379+ continue ;
380+ }
381+ }
382+
383+ var codec = type .codec ();
384+
385+ if (codec == null ) {
386+ failed = true ;
387+ errors .add (Pair .of (type , "Component has non-serializable type" ));
388+ continue ;
389+ }
390+
391+ switch (codec .parse (reg .java (), value )) {
392+ case DataResult .Success <?> success ->
393+ //noinspection rawtypes, unchecked
394+ builder .set ((DataComponentType ) type , success .value ());
395+ case DataResult .Error <?> error -> {
396+ failed = true ;
397+ errors .add (Pair .of (type , error .message ()));
398+ }
399+ }
400+ }
401+
402+ if (failed ) {
403+ var msg = errors .build ().map (pair -> {
404+ var type = pair .getFirst ();
405+ var error = pair .getSecond ();
406+
407+ var id = reg .access ().registryOrThrow (Registries .DATA_COMPONENT_TYPE ).getKeyOrNull (type );
408+
409+ return "'%s': %s" .formatted (id , error );
410+ }).collect (Collectors .joining ("; " ));
411+ yield error (() -> "Failed to parse DataComponentMap: " + msg , builder .build ());
412+ } else {
413+ yield success (builder .build ());
414+ }
415+ }
416+ case null -> success (DataComponentMap .EMPTY );
417+ case String s -> {
418+ try {
419+ var reg = RegistryAccessContainer .of (cx );
420+ yield success (readMap (reg .nbt (), new StringReader (s )));
421+ } catch (CommandSyntaxException ex ) {
422+ yield error (() -> "Error parsing DataComponentMap from %s: %s" .formatted (s , ex .getMessage ()));
423+ }
424+ }
425+ default -> error (() -> "Don't know how to convert %s to DataComponentMap!" .formatted (o ));
426+ };
427+ }
428+
429+ static DataResult <DataComponentPatch > tryPatchOf (Context cx , @ Nullable Object o ) {
430+ return switch (o ) {
431+ case DataComponentPatch patch -> success (patch );
432+ case BaseFunction fn -> fnToBuilder (cx , PatchBuilder .class , fn ,
433+ builder -> Util .make (DataComponentPatch .builder (), builder ).build ());
434+ case Map <?, ?> map -> {
435+ var reg = RegistryAccessContainer .of (cx );
436+ var builder = DataComponentPatch .builder ();
437+
438+ var failed = false ;
439+ Stream .Builder <Pair <DataComponentType <?>, String >> errors = Stream .builder ();
440+
441+ Map <DataComponentType <?>, ?> wrapped = Objects .requireNonNull (cx .optionalMapOf (map , COMPONENT_TYPE , TypeInfo .NONE ));
442+
443+ for (var entry : wrapped .entrySet ()) {
444+ var type = entry .getKey ();
445+ var valueType = getTypeInfo (type );
446+
447+ var value = entry .getValue ();
448+
449+ if (value == null || value instanceof Undefined ) {
450+ builder .remove (type );
451+ continue ;
452+ }
453+
454+ if (cx .canConvert (value , valueType )) {
455+ try {
456+ Object converted = cx .jsToJava (value , valueType );
457+ if (converted != null ) {
458+ //noinspection rawtypes, unchecked
459+ builder .set ((DataComponentType ) type , converted );
460+ continue ;
461+ }
462+ } catch (EvaluatorException e ) {
463+ failed = true ;
464+ errors .add (Pair .of (type , "Failed to parse data component from input '%s': %s" .formatted (value , e )));
465+ continue ;
466+ }
467+ }
468+
469+ var codec = type .codec ();
470+
471+ if (codec == null ) {
472+ failed = true ;
473+ errors .add (Pair .of (type , "Component has non-serializable type" ));
474+ continue ;
475+ }
476+
477+ switch (codec .parse (reg .java (), value )) {
478+ case DataResult .Success <?> success ->
479+ //noinspection rawtypes, unchecked
480+ builder .set ((DataComponentType ) type , success .value ());
481+ case DataResult .Error <?> error -> {
482+ failed = true ;
483+ errors .add (Pair .of (type , error .message ()));
484+ }
485+ }
486+ }
487+
488+ if (failed ) {
489+ var msg = errors .build ().map (pair -> {
490+ var type = pair .getFirst ();
491+ var error = pair .getSecond ();
492+
493+ var id = reg .access ().registryOrThrow (Registries .DATA_COMPONENT_TYPE ).getKeyOrNull (type );
494+
495+ return "'%s': %s" .formatted (id , error );
496+ }).collect (Collectors .joining ("; " ));
497+ yield error (() -> "Failed to parse DataComponentPatch: " + msg , builder .build ());
498+ } else {
499+ yield success (builder .build ());
500+ }
501+ }
502+ case null -> success (DataComponentPatch .EMPTY );
503+ case String s -> {
504+ try {
505+ var reg = RegistryAccessContainer .of (cx );
506+ yield success (readPatch (reg .nbt (), new StringReader (s )));
507+ } catch (CommandSyntaxException ex ) {
508+ yield error (() -> "Error parsing DataComponentPatch from %s: %s" .formatted (s , ex .getMessage ()));
509+ }
510+ }
511+ default -> error (() -> "Don't know how to convert %s to DataComponentPatch!" .formatted (o ));
512+ };
513+ }
514+
515+ private static <B , T > DataResult <T > fnToBuilder (Context cx , Class <B > builderType , BaseFunction fn , Function <B , T > build ) {
516+ try {
517+ B builder = Cast .to (cx .createInterfaceAdapter (TypeInfo .of (builderType ), fn ));
518+ return success (build .apply (builder ));
519+ } catch (Exception e ) {
520+ return error (() -> "Failed to create %s from builder: %s" .formatted (builderType .toString (), e ));
521+ }
522+ }
523+
296524 static StringBuilder mapToString (StringBuilder builder , @ Nullable DynamicOps <Tag > ops , DataComponentMap map ) {
297525 builder .append ('[' );
298526
@@ -381,4 +609,14 @@ static DataComponentPatch visualPatch(DataComponentPatch patch) {
381609
382610 return builder .build ();
383611 }
612+
613+ interface MapBuilder extends Consumer <DataComponentMap .Builder > {
614+ @ Override
615+ void accept (DataComponentMap .Builder builder );
616+ }
617+
618+ interface PatchBuilder extends Consumer <DataComponentPatch .Builder > {
619+ @ Override
620+ void accept (DataComponentPatch .Builder builder );
621+ }
384622}
0 commit comments