11package top .qwerty770 .clientexport ;
22
3+ import com .google .common .collect .Maps ;
34import com .google .gson .JsonObject ;
45import com .mojang .brigadier .context .CommandContext ;
5- import com .mojang .serialization .JsonOps ;
66import it .unimi .dsi .fastutil .objects .Object2IntMap ;
77import net .fabricmc .fabric .api .client .command .v2 .FabricClientCommandSource ;
88import net .minecraft .SharedConstants ;
9+ import net .minecraft .WorldVersion ;
910import net .minecraft .client .Minecraft ;
11+ import net .minecraft .core .registries .BuiltInRegistries ;
1012import net .minecraft .network .chat .Component ;
13+ import net .minecraft .resources .ResourceLocation ;
1114import net .minecraft .stats .Stat ;
12- import top .qwerty770 .clientexport .mixin .ServerStatsCounterAccessor ;
15+ import net .minecraft .stats .StatType ;
16+ import net .minecraft .world .level .storage .DataVersion ;
1317import top .qwerty770 .clientexport .mixin .StatsCounterAccessor ;
1418
1519import java .io .FileWriter ;
1620import java .io .IOException ;
21+ import java .lang .invoke .MethodHandle ;
22+ import java .lang .invoke .MethodHandles ;
23+ import java .lang .invoke .MethodType ;
24+ import java .util .Map ;
25+ import java .util .Objects ;
1726
1827import static top .qwerty770 .clientexport .util .FileUtil .*;
28+ import static top .qwerty770 .clientexport .util .MappingResolverTool .*;
1929
2030public class StatisticsCommand {
2131 public static void exportStatistics (CommandContext <FabricClientCommandSource > context ) {
2232 assert Minecraft .getInstance ().player != null ;
2333 Object2IntMap <Stat <?>> stats = ((StatsCounterAccessor ) Minecraft .getInstance ().player .getStats ()).getStats ();
2434 try {
25- JsonObject json = new JsonObject ();
26- json .add ("stats" , ServerStatsCounterAccessor .getStatsCodec ().encodeStart (JsonOps .INSTANCE , stats ).getOrThrow ());
27- json .addProperty ("DataVersion" , SharedConstants .getCurrentVersion ().dataVersion ().version ());
35+ JsonObject jsonObject = toJson (stats );
2836 FileWriter fileWriter = createFile ("statistics" );
29- writeJson (fileWriter , json );
37+ writeJson (fileWriter , jsonObject );
3038 fileWriter .close ();
3139 ClientExportHelper .LOGGER .info ("Client Export Helper exported {} lines of statistics" , stats .size ());
3240 context .getSource ().sendFeedback (Component .translatable ("commands.clientexport.statistics.success" , stats .size ()));
@@ -35,4 +43,58 @@ public static void exportStatistics(CommandContext<FabricClientCommandSource> co
3543 sendFailure (exception , context , "commands.clientexport.statistics.fail" );
3644 }
3745 }
46+
47+ @ SuppressWarnings ({"rawtypes" , "unchecked" })
48+ public static JsonObject toJson (Object2IntMap <Stat <?>> stats ) {
49+ Map <StatType <?>, JsonObject > map = Maps .newHashMap ();
50+ for (Object2IntMap .Entry <Stat <?>> statEntry : stats .object2IntEntrySet ()) {
51+ Stat stat = statEntry .getKey ();
52+ var registry = stat .getType ().getRegistry ();
53+ map .computeIfAbsent (stat .getType (), (statType ) -> new JsonObject ())
54+ .addProperty (Objects .requireNonNull (registry .getKey (stat .getValue ())).toString (), statEntry .getIntValue ());
55+ }
56+
57+ JsonObject jsonObject = new JsonObject ();
58+ for (Map .Entry <StatType <?>, JsonObject > entry2 : map .entrySet ()) {
59+ ResourceLocation location = BuiltInRegistries .STAT_TYPE .getKey (entry2 .getKey ());
60+ if (location == null ) {
61+ ClientExportHelper .LOGGER .warn ("StatType {} is not registered in BuiltInRegistries.STAT_TYPE" , entry2 .getKey ());
62+ continue ;
63+ }
64+ jsonObject .add (location .toString (), entry2 .getValue ());
65+ }
66+
67+ JsonObject jsonObject2 = new JsonObject ();
68+ jsonObject2 .add ("stats" , jsonObject );
69+ jsonObject2 .addProperty ("DataVersion" , getDataVersion ());
70+ return jsonObject2 ;
71+ }
72+
73+ public static int getDataVersion () {
74+ /// Returns the data version of Minecraft. Ensures the compatibility between Minecraft 1.21.5 and 1.21.6.
75+ try {
76+ // Implement this method via reflection
77+ WorldVersion worldVersion = SharedConstants .getCurrentVersion ();
78+ MethodHandles .Lookup lookup = MethodHandles .publicLookup ();
79+ // WorldVersion.getDataVersion() was renamed to WorldVersion.dataVersion() in 1.21.6
80+ String name = mapIntermediaryMethodNames ("net.minecraft.class_6489" , "()Lnet/minecraft/class_6595;" , "method_37912" , "comp_4026" );
81+ MethodHandle handle = lookup .findVirtual (WorldVersion .class , name , MethodType .methodType (DataVersion .class ));
82+ DataVersion dataVersion = (DataVersion ) handle .invokeExact (worldVersion );
83+ // DataVersion.getVersion() was renamed to DataVersion.version() in 1.21.6
84+ String name2 = mapIntermediaryMethodNames ("net.minecraft.class_6595" , "()I" , "method_38494" , "comp_4038" );
85+ MethodHandle handle2 = lookup .findVirtual (DataVersion .class , name2 , MethodType .methodType (int .class ));
86+ return (int ) handle2 .invokeExact (dataVersion );
87+ }
88+ catch (Throwable e ) {
89+ try {
90+ // Fallback to SharedConstants.WORLD_VERSION if reflection fails
91+ String name = mapIntermediaryFieldName ("net.minecraft.class_155" , "field_29732" , "I" );
92+ return SharedConstants .class .getField (name ).getInt (null );
93+ } catch (Exception e2 ) {
94+ // This should never happen, but if it does, return a default value
95+ ClientExportHelper .LOGGER .error ("Failed to get data version via reflection" , e2 );
96+ return 4438 ;
97+ }
98+ }
99+ }
38100}
0 commit comments