Skip to content

Commit 727652d

Browse files
committed
v3.1.0 multi version support
1 parent 70a5d31 commit 727652d

File tree

10 files changed

+122
-36
lines changed

10 files changed

+122
-36
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
//file:noinspection GroovyAssignabilityCheck
22
plugins {
3-
id 'fabric-loom' version '1.10-SNAPSHOT'
3+
id 'fabric-loom' version '1.11-SNAPSHOT'
44
id 'maven-publish'
55
}
66

77
version = project.mod_version
88
group = project.maven_group
99

1010
base {
11-
archivesName = project.archives_base_name
11+
archivesName = project.archives_base_name + "-" + project.minecraft_version
1212
}
1313

1414
repositories {

gradle.properties

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ org.gradle.parallel=true
44

55
# Fabric Properties
66
# check these on https://fabricmc.net/develop
7-
minecraft_version=1.21.6
7+
minecraft_version=1.21.7
88
loader_version=0.16.14
99

1010
# Mod Properties
11-
mod_version=3.0.0
11+
mod_version=3.1.0
1212
maven_group=top.qwerty770.clientexport
1313
archives_base_name=client-export-helper
1414

1515
# Dependencies
16-
fabric_version=0.128.0+1.21.6
16+
fabric_version=0.129.0+1.21.7

src/client/java/top/qwerty770/clientexport/AdvancementTool.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,22 @@
1010
import net.minecraft.client.Minecraft;
1111
import net.minecraft.client.multiplayer.ClientAdvancements;
1212
import net.minecraft.core.Holder;
13+
import net.minecraft.core.component.DataComponentHolder;
14+
import net.minecraft.core.component.DataComponentType;
1315
import net.minecraft.core.component.DataComponents;
1416
import net.minecraft.resources.ResourceLocation;
1517
import net.minecraft.world.item.ItemStack;
1618
import net.minecraft.world.item.enchantment.Enchantment;
1719
import net.minecraft.world.item.enchantment.ItemEnchantments;
1820
import top.qwerty770.clientexport.mixin.ClientAdvancementsAccessor;
1921

22+
import java.lang.invoke.MethodHandles;
23+
import java.lang.invoke.MethodType;
2024
import java.util.HashMap;
2125
import java.util.List;
2226
import java.util.Map;
23-
import java.util.Objects;
27+
28+
import static top.qwerty770.clientexport.util.MappingResolverTool.mapIntermediaryMethodNames;
2429

2530
public class AdvancementTool {
2631
public static AdvancementTree getAdvancementTree() {
@@ -45,9 +50,19 @@ public static JsonElement getJson(AdvancementHolder holder, AdvancementTree tree
4550
}
4651
if (advancement.display().isPresent()) {
4752
ItemStack icon = advancement.display().get().getIcon();
53+
// Enchantments are only registered on server side
4854
if (icon.has(DataComponents.ENCHANTMENTS)) {
49-
// Enchantments are only registered on server side
50-
ItemEnchantments enchantmentsComponent = Objects.requireNonNull(icon.get(DataComponents.ENCHANTMENTS));
55+
ItemEnchantments enchantmentsComponent = ItemEnchantments.EMPTY;
56+
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
57+
String name = mapIntermediaryMethodNames("net.minecraft.class_9322", "(Lnet/minecraft/class_9331;)Ljava/lang/Object;",
58+
"method_57824", "get");
59+
try {
60+
var method = lookup.findVirtual(DataComponentHolder.class, name, MethodType.methodType(Object.class, DataComponentType.class));
61+
enchantmentsComponent = (ItemEnchantments) method.invoke((DataComponentHolder) icon, DataComponents.ENCHANTMENTS);
62+
} catch (Throwable e) {
63+
ClientExportHelper.LOGGER.error("Failed to get enchantments from ItemStack", e);
64+
icon.remove(DataComponents.ENCHANTMENTS);
65+
}
5166
if (!enchantmentsComponent.isEmpty()) {
5267
enchantments = new HashMap<>();
5368
for (Object2IntMap.Entry<Holder<Enchantment>> entry : enchantmentsComponent.entrySet()) {

src/client/java/top/qwerty770/clientexport/ClientExportHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
@SuppressWarnings("unused")
1616
public class ClientExportHelper implements ClientModInitializer {
1717
public static final Logger LOGGER = LoggerFactory.getLogger("clientexport");
18-
public static final String version = "3.0.0";
18+
public static final String version = "3.1.0";
1919
@Override
2020
public void onInitializeClient() {
2121
ClientCommandRegistrationCallback.EVENT.register(((dispatcher, registryAccess) -> register(dispatcher)));
Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,40 @@
11
package top.qwerty770.clientexport;
22

3+
import com.google.common.collect.Maps;
34
import com.google.gson.JsonObject;
45
import com.mojang.brigadier.context.CommandContext;
5-
import com.mojang.serialization.JsonOps;
66
import it.unimi.dsi.fastutil.objects.Object2IntMap;
77
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
88
import net.minecraft.SharedConstants;
9+
import net.minecraft.WorldVersion;
910
import net.minecraft.client.Minecraft;
11+
import net.minecraft.core.registries.BuiltInRegistries;
1012
import net.minecraft.network.chat.Component;
13+
import net.minecraft.resources.ResourceLocation;
1114
import 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;
1317
import top.qwerty770.clientexport.mixin.StatsCounterAccessor;
1418

1519
import java.io.FileWriter;
1620
import 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

1827
import static top.qwerty770.clientexport.util.FileUtil.*;
28+
import static top.qwerty770.clientexport.util.MappingResolverTool.*;
1929

2030
public 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
}

src/client/java/top/qwerty770/clientexport/mixin/ServerStatsCounterAccessor.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/client/java/top/qwerty770/clientexport/util/FileUtil.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package top.qwerty770.clientexport.util;
22

33
import com.google.gson.JsonObject;
4-
import com.google.gson.Strictness;
54
import com.google.gson.internal.Streams;
65
import com.google.gson.stream.JsonWriter;
76
import com.mojang.brigadier.context.CommandContext;
@@ -37,7 +36,6 @@ public static FileWriter createFile(String namespace, String type) throws IOExce
3736

3837
public static void writeJson(FileWriter fileWriter, JsonObject jsonObject) throws IOException {
3938
JsonWriter jsonWriter = new JsonWriter(fileWriter);
40-
jsonWriter.setStrictness(Strictness.LENIENT);
4139
jsonWriter.setIndent(" ");
4240
Streams.write(jsonObject, jsonWriter);
4341
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package top.qwerty770.clientexport.util;
2+
3+
import net.fabricmc.loader.api.FabricLoader;
4+
import net.fabricmc.loader.api.MappingResolver;
5+
6+
public class MappingResolverTool {
7+
private static final MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
8+
9+
public static String mapIntermediaryFieldName(String owner, String name, String descriptor) {
10+
return resolver.mapFieldName("intermediary", owner, name, descriptor);
11+
}
12+
13+
public static String mapIntermediaryMethodName(String owner, String name, String descriptor) {
14+
return resolver.mapMethodName("intermediary", owner, name, descriptor);
15+
}
16+
17+
public static String mapIntermediaryMethodNames(String owner, String descriptor, String... names) {
18+
try {
19+
for (String name : names) {
20+
String mappedName = mapIntermediaryMethodName(owner, name, descriptor);
21+
if (mappedName != null && !mappedName.equals(name)) {
22+
return mappedName;
23+
}
24+
}
25+
} catch (Exception ignored) {}
26+
// ClientExportHelper.LOGGER.error("Failed to map method names for owner: {}, descriptor: {}, names: {}", owner, descriptor, String.join(", ", names));
27+
return mapIntermediaryMethodName(owner, names[0], descriptor); // Fallback to the first name
28+
}
29+
}

src/client/resources/clientexport.mixins.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"ClientAdvancementsAccessor"
88
],
99
"mixins": [
10-
"ServerStatsCounterAccessor",
1110
"StatsCounterAccessor"
1211
],
1312
"injectors": {

src/client/resources/fabric.mod.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
],
2424
"depends": {
2525
"fabricloader": ">=0.16.0",
26-
"minecraft": ">=1.20.6",
26+
"minecraft": ">=1.21",
2727
"java": ">=21",
2828
"fabric-api": "*"
2929
},

0 commit comments

Comments
 (0)