minecraft-src/net/minecraft/stats/ServerStatsCounter.java
2025-07-04 01:41:11 +03:00

208 lines
7 KiB
Java

package net.minecraft.stats;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonReader;
import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Map.Entry;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.protocol.game.ClientboundAwardStatsPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.player.Player;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
/**
* Server-side implementation of {@link net.minecraft.stats.StatsCounter}; handles counting, serialising, and de-serialising statistics, as well as sending them to connected clients via the {@linkplain net.minecraft.network.protocol.game.ClientboundAwardStatsPacket award stats packet}.
*/
public class ServerStatsCounter extends StatsCounter {
private static final Logger LOGGER = LogUtils.getLogger();
private final MinecraftServer server;
private final File file;
private final Set<Stat<?>> dirty = Sets.<Stat<?>>newHashSet();
public ServerStatsCounter(MinecraftServer server, File file) {
this.server = server;
this.file = file;
if (file.isFile()) {
try {
this.parseLocal(server.getFixerUpper(), FileUtils.readFileToString(file));
} catch (IOException var4) {
LOGGER.error("Couldn't read statistics file {}", file, var4);
} catch (JsonParseException var5) {
LOGGER.error("Couldn't parse statistics file {}", file, var5);
}
}
}
public void save() {
try {
FileUtils.writeStringToFile(this.file, this.toJson());
} catch (IOException var2) {
LOGGER.error("Couldn't save stats", (Throwable)var2);
}
}
@Override
public void setValue(Player player, Stat<?> stat, int value) {
super.setValue(player, stat, value);
this.dirty.add(stat);
}
private Set<Stat<?>> getDirty() {
Set<Stat<?>> set = Sets.<Stat<?>>newHashSet(this.dirty);
this.dirty.clear();
return set;
}
public void parseLocal(DataFixer fixerUpper, String json) {
try {
JsonReader jsonReader = new JsonReader(new StringReader(json));
label47: {
try {
jsonReader.setLenient(false);
JsonElement jsonElement = Streams.parse(jsonReader);
if (!jsonElement.isJsonNull()) {
CompoundTag compoundTag = fromJson(jsonElement.getAsJsonObject());
compoundTag = DataFixTypes.STATS.updateToCurrentVersion(fixerUpper, compoundTag, NbtUtils.getDataVersion(compoundTag, 1343));
if (!compoundTag.contains("stats", 10)) {
break label47;
}
CompoundTag compoundTag2 = compoundTag.getCompound("stats");
Iterator var7 = compoundTag2.getAllKeys().iterator();
while (true) {
if (!var7.hasNext()) {
break label47;
}
String string = (String)var7.next();
if (compoundTag2.contains(string, 10)) {
Util.ifElse(
BuiltInRegistries.STAT_TYPE.getOptional(ResourceLocation.parse(string)),
statType -> {
CompoundTag compoundTag2x = compoundTag2.getCompound(string);
for (String string2 : compoundTag2x.getAllKeys()) {
if (compoundTag2x.contains(string2, 99)) {
Util.ifElse(
this.getStat(statType, string2),
stat -> this.stats.put(stat, compoundTag2x.getInt(string2)),
() -> LOGGER.warn("Invalid statistic in {}: Don't know what {} is", this.file, string2)
);
} else {
LOGGER.warn("Invalid statistic value in {}: Don't know what {} is for key {}", this.file, compoundTag2x.get(string2), string2);
}
}
},
() -> LOGGER.warn("Invalid statistic type in {}: Don't know what {} is", this.file, string)
);
}
}
}
LOGGER.error("Unable to parse Stat data from {}", this.file);
} catch (Throwable var10) {
try {
jsonReader.close();
} catch (Throwable var9) {
var10.addSuppressed(var9);
}
throw var10;
}
jsonReader.close();
return;
}
jsonReader.close();
} catch (IOException | JsonParseException var11) {
LOGGER.error("Unable to parse Stat data from {}", this.file, var11);
}
}
private <T> Optional<Stat<T>> getStat(StatType<T> type, String location) {
return Optional.ofNullable(ResourceLocation.tryParse(location)).flatMap(type.getRegistry()::getOptional).map(type::get);
}
private static CompoundTag fromJson(JsonObject json) {
CompoundTag compoundTag = new CompoundTag();
for (Entry<String, JsonElement> entry : json.entrySet()) {
JsonElement jsonElement = (JsonElement)entry.getValue();
if (jsonElement.isJsonObject()) {
compoundTag.put((String)entry.getKey(), fromJson(jsonElement.getAsJsonObject()));
} else if (jsonElement.isJsonPrimitive()) {
JsonPrimitive jsonPrimitive = jsonElement.getAsJsonPrimitive();
if (jsonPrimitive.isNumber()) {
compoundTag.putInt((String)entry.getKey(), jsonPrimitive.getAsInt());
}
}
}
return compoundTag;
}
protected String toJson() {
Map<StatType<?>, JsonObject> map = Maps.<StatType<?>, JsonObject>newHashMap();
for (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<Stat<?>> entry : this.stats.object2IntEntrySet()) {
Stat<?> stat = (Stat<?>)entry.getKey();
((JsonObject)map.computeIfAbsent(stat.getType(), statType -> new JsonObject())).addProperty(getKey(stat).toString(), entry.getIntValue());
}
JsonObject jsonObject = new JsonObject();
for (Entry<StatType<?>, JsonObject> entry2 : map.entrySet()) {
jsonObject.add(BuiltInRegistries.STAT_TYPE.getKey((StatType<?>)entry2.getKey()).toString(), (JsonElement)entry2.getValue());
}
JsonObject jsonObject2 = new JsonObject();
jsonObject2.add("stats", jsonObject);
jsonObject2.addProperty("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
return jsonObject2.toString();
}
private static <T> ResourceLocation getKey(Stat<T> stat) {
return stat.getType().getRegistry().getKey(stat.getValue());
}
public void markAllDirty() {
this.dirty.addAll(this.stats.keySet());
}
public void sendStats(ServerPlayer player) {
Object2IntMap<Stat<?>> object2IntMap = new Object2IntOpenHashMap<>();
for (Stat<?> stat : this.getDirty()) {
object2IntMap.put(stat, this.getValue(stat));
}
player.connection.send(new ClientboundAwardStatsPacket(object2IntMap));
}
}