diff --git a/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java b/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java index 7fcebd6d..5faf1589 100644 --- a/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java +++ b/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java @@ -37,11 +37,12 @@ private StatsProviderIO() {} * still maintaining support for the older chunks. * @apiNote TLDR; Only increase if backwards compatibility is impossible. */ - public static final int FILE_VERSION = 2; + public static final int FILE_VERSION = 4; /* # File version history: * 1 - Since v3.0-alpha.1 - Initial version * 2 - Since v3.0-alpha.3 - Major changes to the player badge system - * 3 - Since v3.9 - Support for modded stat types + * 3 - Since v3.9 - [Reserved for special use-case] + * 4 - Since v3.9 - Support for modded stat types */ // ================================================== /** @@ -61,12 +62,13 @@ public static final void write(PacketByteBuf buffer, IStatsProvider statsProvide buffer.writeBytes("RIFF".getBytes(US_ASCII)); //write data - write_file(buffer, statsProvider); + write_file(buffer, statsProvider, FILE_VERSION); } - @SuppressWarnings("deprecation") - private static final void write_file(PacketByteBuf buffer, IStatsProvider statsProvider) + private static final void write_file(PacketByteBuf buffer, IStatsProvider statsProvider, int fileVersion) { + if(fileVersion < 1) throw new IllegalArgumentException("Attempting to write file version < 1."); + //create the buffer final var buffer_file = new PacketByteBuf(Unpooled.buffer()); @@ -76,12 +78,13 @@ private static final void write_file(PacketByteBuf buffer, IStatsProvider statsP buffer_file.writeBytes(FILE_EXTENSION.toUpperCase().getBytes(US_ASCII)); //write the file version - buffer_file.writeIntLE(FILE_VERSION); + buffer_file.writeIntLE(fileVersion); //write chunks - switch(FILE_VERSION) + switch(fileVersion) { case 2: StatsProviderIO_fv2.write_fileChunks(buffer_file, statsProvider); break; + case 4: StatsProviderIO_fv4.write_fileChunks(buffer_file, statsProvider); break; default: break; } @@ -130,7 +133,6 @@ public static final void read(PacketByteBuf buffer, IEditableStatsProvider stats catch(IllegalHeaderException | UnsupportedFileVersionException exc) { buffer.resetReaderIndex(); throw exc; } } // -------------------------------------------------- - @SuppressWarnings("deprecation") private static final void read_file(PacketByteBuf buffer_file, IEditableStatsProvider statsProvider) throws IllegalHeaderException, UnsupportedFileVersionException { @@ -146,6 +148,7 @@ private static final void read_file(PacketByteBuf buffer_file, IEditableStatsPro switch(fileVersion) { case 2: StatsProviderIO_fv2.read_fileChunks(buffer_file, statsProvider); break; + case 4: StatsProviderIO_fv4.read_fileChunks(buffer_file, statsProvider); break; default: throw new UnsupportedFileVersionException(Integer.toString(fileVersion)); } } diff --git a/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java b/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java index cc742fad..92f74ee2 100644 --- a/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java +++ b/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java @@ -5,6 +5,7 @@ import java.util.UUID; import java.util.stream.Collectors; +import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.Nullable; import com.google.common.collect.Lists; @@ -29,10 +30,9 @@ * A class containing {@link StatsProviderIO} logic for file version 2. * @apiNote Used for backwards-compatibility. Internal use only! */ -public final @Deprecated class StatsProviderIO_fv2 +@Internal +public final class StatsProviderIO_fv2 { - // ================================================== - public static final int FILE_VERSION = 2; //Must always be 2 // ================================================== static final void write_fileChunks(PacketByteBuf buffer_file, IStatsProvider statsProvider) { diff --git a/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java b/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java new file mode 100644 index 00000000..f5a54c18 --- /dev/null +++ b/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java @@ -0,0 +1,226 @@ +package io.github.thecsdev.betterstats.api.util.io; + +import static io.github.thecsdev.tcdcommons.api.util.TextUtils.literal; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +import io.github.thecsdev.betterstats.api.util.stats.SUPlayerBadgeStat; +import io.github.thecsdev.tcdcommons.api.util.exceptions.UnsupportedFileVersionException; +import io.netty.buffer.Unpooled; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.registry.Registries; +import net.minecraft.stat.StatType; +import net.minecraft.util.Identifier; + +public class StatsProviderIO_fv4 +{ + // ================================================== + static final void write_fileChunks(PacketByteBuf buffer_file, IStatsProvider statsProvider) + { + write_fileChunk("metadata", buffer_file, statsProvider); + write_fileChunk("stats", buffer_file, statsProvider); + write_fileChunk("tcdcommons:player_badges", buffer_file, statsProvider); + } + // -------------------------------------------------- + private static final void write_fileChunk(String chunkId, PacketByteBuf buffer_file, IStatsProvider statsProvider) + { + //create a buffer for the chunk, and write the chunk ID to it + PacketByteBuf buffer_chunk = new PacketByteBuf(Unpooled.buffer()); + buffer_chunk.writeString(chunkId); + + //obtain and write chunk data to the chunk buffer + switch(chunkId) + { + case "metadata": write_fileChunk_metadata(buffer_chunk, statsProvider); break; + case "stats": write_fileChunk_stats(buffer_chunk, statsProvider); break; + case "tcdcommons:player_badges": write_fileChunk_playerBadges(buffer_chunk, statsProvider); break; + default: break; + } + + //write the chunk data buffer to the file buffer + buffer_file.writeIntLE(buffer_chunk.readableBytes()); + buffer_file.writeBytes(buffer_chunk); + buffer_chunk.release(); + } + // -------------------------------------------------- + private static final void write_fileChunk_metadata(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //obtain the stats display name as string + final var statsNameText = statsProvider.getDisplayName(); + final var statsName = (statsNameText != null) ? statsNameText.getString() : "-"; + + //write name and game profile + buffer_chunk.writeString(statsName); + StatsProviderIO.writeGameProfile(buffer_chunk, statsProvider.getGameProfile()); + } + // -------------------------------------------------- + @SuppressWarnings("unchecked") + private static final void write_fileChunk_stats(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //iterate all stat types, and write their corresponding stat data one by one + for(final var statType : Registries.STAT_TYPE) + { + //create a buffer for the stat type chunk, and write the chunk ID to it + PacketByteBuf buffer_st = new PacketByteBuf(Unpooled.buffer()); + buffer_st.writeString(Objects.toString(Registries.STAT_TYPE.getId(statType))); //write chunk id + + //write the stats data for the given stats type, to the stats type buffer + write_fileChunk_stats_statType(buffer_st, statsProvider, (StatType)statType); //write chunk data + + //write the stat type buffer data to the chunk buffer + buffer_chunk.writeIntLE(buffer_st.readableBytes()); + buffer_chunk.writeBytes(buffer_st); + buffer_st.release(); + } + } + + private static final void write_fileChunk_stats_statType( + PacketByteBuf buffer_st, IStatsProvider statsProvider, StatType statType) + { + //obtain the registry, and iterate all of its items + final var registry = statType.getRegistry(); + for(final var registryItem : registry) + { + //obtain the stat value for the given registry item + //also skip "zero" stats, as they do not have a value + final int statValue = statsProvider.getStatValue(statType, registryItem); + if(statValue == 0) continue; + + //obtain the id of the registry item + final var registryItemId = registry.getId(registryItem); + + //write stat id and value + buffer_st.writeString(registryItemId.getNamespace().equals(Identifier.DEFAULT_NAMESPACE) ? + registryItemId.getPath() : Objects.toString(registryItemId)); //write stat id + buffer_st.writeIntLE(statValue); //write stat value + } + } + // -------------------------------------------------- + private static final void write_fileChunk_playerBadges(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //obtain a map of mod stats + final var stats = SUPlayerBadgeStat.getPlayerBadgeStatsByModGroups(statsProvider, stat -> !stat.isEmpty()); + + //iterate groups, and write their data + for(final var entry : stats.entrySet()) + { + //obtain the group id and its stats + final var groupModId = entry.getKey(); + final var groupStats = entry.getValue(); + + //write the group id and its length + buffer_chunk.writeString(groupModId); + buffer_chunk.writeVarInt(groupStats.size()); + + //write group entries + for(final var stat : groupStats) + { + buffer_chunk.writeString(stat.getStatID().getPath()); + buffer_chunk.writeVarInt(stat.value); + } + } + } + // ================================================== + static final void read_fileChunks(PacketByteBuf buffer_file, IEditableStatsProvider statsProvider) + throws IllegalHeaderException, UnsupportedFileVersionException + { + //read chunks + while(buffer_file.readableBytes() > 0) + { + //read next chunk's size, and check it + final int chunkSize = buffer_file.readIntLE(); + if(buffer_file.readableBytes() < chunkSize) + throw new IllegalHeaderException( + "chunk size >= " + chunkSize, + "chunk size == " + buffer_file.readableBytes()); + + //read the chunk data + //(creates a view of the original buffer, so it doesn't have to be released separately) + final var buffer_chunk = new PacketByteBuf(buffer_file.readSlice(chunkSize)); + final var chunkId = buffer_chunk.readString(); + switch(chunkId) + { + case "metadata": read_fileChunk_metadata(buffer_chunk, statsProvider); break; + case "stats": read_fileChunk_stats(buffer_chunk, statsProvider); break; + case "tcdcommons:player_badges": read_fileChunk_playerBadges(buffer_chunk, statsProvider); break; + default: break; + } + } + } + // -------------------------------------------------- + private static final void read_fileChunk_metadata(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + { + statsProvider.setDisplayName(literal(buffer_chunk.readString())); + statsProvider.setGameProfile(StatsProviderIO.readGameProfile(buffer_chunk)); + } + // -------------------------------------------------- + private static final void read_fileChunk_stats(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + throws IllegalHeaderException + { + //keep reading chunks as they come in + while(buffer_chunk.readableBytes() > 0) + { + //read next chunk's size, and check it + final int chunkSize = buffer_chunk.readIntLE(); //read stats type chunk size + if(buffer_chunk.readableBytes() < chunkSize) + throw new IllegalHeaderException( + "chunk size >= " + chunkSize, + "chunk size == " + buffer_chunk.readableBytes()); + + //read the chunk data + //(creates a view of the original buffer, so it doesn't have to be released separately) + final var buffer_st = new PacketByteBuf(buffer_chunk.readSlice(chunkSize)); //read stats type chunk data + read_fileChunk_stats_statType(buffer_st, statsProvider); + } + } + + @SuppressWarnings("unchecked") + private static final void read_fileChunk_stats_statType(PacketByteBuf buffer_st, IEditableStatsProvider statsProvider) + { + //read the stat type identifier + final var statTypeId = new Identifier(buffer_st.readString()); + + //obtain the stat type and check if it exists + final @Nullable var statType = Registries.STAT_TYPE.containsId(statTypeId) ? + (StatType)Registries.STAT_TYPE.getOrEmpty(statTypeId).get() : null; + if(statType == null) return; + final var statTypeRegistry = statType.getRegistry(); + + //read stats one by one + while(buffer_st.readableBytes() > 0) + { + //read stat id and stat value + final Identifier statId = new Identifier(buffer_st.readString()); + final int statValue = buffer_st.readIntLE(); + + //obtain the registry item, null check it, and store its value to the stats provider + final @Nullable var item = statTypeRegistry.getOrEmpty(statId).orElse(null); + if(item == null) continue; + statsProvider.setStatValue(statType, item, statValue); + } + } + // -------------------------------------------------- + private static final void read_fileChunk_playerBadges(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + { + while(buffer_chunk.readableBytes() > 0) + { + //read the next mod id and how many entries it has + final String modId = buffer_chunk.readString(); + final int entryCount = buffer_chunk.readVarInt(); + + //read all entries for the corresponding mod id + for(int i = 0; i < entryCount; i++) + { + //read player badge stat data + final String playerBadgeIdPath = buffer_chunk.readString(); + final int value = buffer_chunk.readVarInt(); + + //obtain mob, and store its stats + final Identifier playerBadgeId = new Identifier(modId, playerBadgeIdPath); + statsProvider.setPlayerBadgeValue(playerBadgeId, value); + } + } + } + // ================================================== +} \ No newline at end of file diff --git a/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt b/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt deleted file mode 100644 index 5eaabffa..00000000 --- a/betterstats-3-fabric-1.20.1/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt +++ /dev/null @@ -1,27 +0,0 @@ -//Note: Square brackets after a chunk refers to a chunk array; For example `IntLE-Chunk<...>[]` -// To read a chunk, first read the IntLE that is prefixed before that indicates the chunk length in bytes -// All subchunks start with `PBB-String` that indicates the chunk ID. -// To read a `PBB-String`, first read the VarInt String length, and then the String bytes. - -<"RIFF", IntLE-Chunk<"EXTENSION", IntLE-VERSION, IntLE-Chunk<...>[]> - -//The "EXTENSION" always has to be uppercase and be 4 bytes in length -//The `IntLE-Chunk<...>` refers to one of the 5 possible chunk types: 'metadata', 'general', 'item', 'mob', 'player_badge' -//`PBB` refers to `PacketByteBuf` and `PBB-String` refers to a String written by a PacketByteBuf - -// ------------------------------------------------ -// next up is the documentation on how to read the sub-chunks themselves -// ------------------------------------------------ - -//contains chunk ID and a Text for display name -IntLE-Chunk - -//contains chunk ID, and an array of "groups". each group starts with an Identifier-namespace String, -//indicating the "mod id" the following set of stats belong to, after which comes an array of stat id paths and their values -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> diff --git a/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java b/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java index 7fcebd6d..5faf1589 100644 --- a/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java +++ b/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java @@ -37,11 +37,12 @@ private StatsProviderIO() {} * still maintaining support for the older chunks. * @apiNote TLDR; Only increase if backwards compatibility is impossible. */ - public static final int FILE_VERSION = 2; + public static final int FILE_VERSION = 4; /* # File version history: * 1 - Since v3.0-alpha.1 - Initial version * 2 - Since v3.0-alpha.3 - Major changes to the player badge system - * 3 - Since v3.9 - Support for modded stat types + * 3 - Since v3.9 - [Reserved for special use-case] + * 4 - Since v3.9 - Support for modded stat types */ // ================================================== /** @@ -61,12 +62,13 @@ public static final void write(PacketByteBuf buffer, IStatsProvider statsProvide buffer.writeBytes("RIFF".getBytes(US_ASCII)); //write data - write_file(buffer, statsProvider); + write_file(buffer, statsProvider, FILE_VERSION); } - @SuppressWarnings("deprecation") - private static final void write_file(PacketByteBuf buffer, IStatsProvider statsProvider) + private static final void write_file(PacketByteBuf buffer, IStatsProvider statsProvider, int fileVersion) { + if(fileVersion < 1) throw new IllegalArgumentException("Attempting to write file version < 1."); + //create the buffer final var buffer_file = new PacketByteBuf(Unpooled.buffer()); @@ -76,12 +78,13 @@ private static final void write_file(PacketByteBuf buffer, IStatsProvider statsP buffer_file.writeBytes(FILE_EXTENSION.toUpperCase().getBytes(US_ASCII)); //write the file version - buffer_file.writeIntLE(FILE_VERSION); + buffer_file.writeIntLE(fileVersion); //write chunks - switch(FILE_VERSION) + switch(fileVersion) { case 2: StatsProviderIO_fv2.write_fileChunks(buffer_file, statsProvider); break; + case 4: StatsProviderIO_fv4.write_fileChunks(buffer_file, statsProvider); break; default: break; } @@ -130,7 +133,6 @@ public static final void read(PacketByteBuf buffer, IEditableStatsProvider stats catch(IllegalHeaderException | UnsupportedFileVersionException exc) { buffer.resetReaderIndex(); throw exc; } } // -------------------------------------------------- - @SuppressWarnings("deprecation") private static final void read_file(PacketByteBuf buffer_file, IEditableStatsProvider statsProvider) throws IllegalHeaderException, UnsupportedFileVersionException { @@ -146,6 +148,7 @@ private static final void read_file(PacketByteBuf buffer_file, IEditableStatsPro switch(fileVersion) { case 2: StatsProviderIO_fv2.read_fileChunks(buffer_file, statsProvider); break; + case 4: StatsProviderIO_fv4.read_fileChunks(buffer_file, statsProvider); break; default: throw new UnsupportedFileVersionException(Integer.toString(fileVersion)); } } diff --git a/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java b/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java index cc742fad..92f74ee2 100644 --- a/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java +++ b/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java @@ -5,6 +5,7 @@ import java.util.UUID; import java.util.stream.Collectors; +import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.Nullable; import com.google.common.collect.Lists; @@ -29,10 +30,9 @@ * A class containing {@link StatsProviderIO} logic for file version 2. * @apiNote Used for backwards-compatibility. Internal use only! */ -public final @Deprecated class StatsProviderIO_fv2 +@Internal +public final class StatsProviderIO_fv2 { - // ================================================== - public static final int FILE_VERSION = 2; //Must always be 2 // ================================================== static final void write_fileChunks(PacketByteBuf buffer_file, IStatsProvider statsProvider) { diff --git a/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java b/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java new file mode 100644 index 00000000..f5a54c18 --- /dev/null +++ b/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java @@ -0,0 +1,226 @@ +package io.github.thecsdev.betterstats.api.util.io; + +import static io.github.thecsdev.tcdcommons.api.util.TextUtils.literal; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +import io.github.thecsdev.betterstats.api.util.stats.SUPlayerBadgeStat; +import io.github.thecsdev.tcdcommons.api.util.exceptions.UnsupportedFileVersionException; +import io.netty.buffer.Unpooled; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.registry.Registries; +import net.minecraft.stat.StatType; +import net.minecraft.util.Identifier; + +public class StatsProviderIO_fv4 +{ + // ================================================== + static final void write_fileChunks(PacketByteBuf buffer_file, IStatsProvider statsProvider) + { + write_fileChunk("metadata", buffer_file, statsProvider); + write_fileChunk("stats", buffer_file, statsProvider); + write_fileChunk("tcdcommons:player_badges", buffer_file, statsProvider); + } + // -------------------------------------------------- + private static final void write_fileChunk(String chunkId, PacketByteBuf buffer_file, IStatsProvider statsProvider) + { + //create a buffer for the chunk, and write the chunk ID to it + PacketByteBuf buffer_chunk = new PacketByteBuf(Unpooled.buffer()); + buffer_chunk.writeString(chunkId); + + //obtain and write chunk data to the chunk buffer + switch(chunkId) + { + case "metadata": write_fileChunk_metadata(buffer_chunk, statsProvider); break; + case "stats": write_fileChunk_stats(buffer_chunk, statsProvider); break; + case "tcdcommons:player_badges": write_fileChunk_playerBadges(buffer_chunk, statsProvider); break; + default: break; + } + + //write the chunk data buffer to the file buffer + buffer_file.writeIntLE(buffer_chunk.readableBytes()); + buffer_file.writeBytes(buffer_chunk); + buffer_chunk.release(); + } + // -------------------------------------------------- + private static final void write_fileChunk_metadata(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //obtain the stats display name as string + final var statsNameText = statsProvider.getDisplayName(); + final var statsName = (statsNameText != null) ? statsNameText.getString() : "-"; + + //write name and game profile + buffer_chunk.writeString(statsName); + StatsProviderIO.writeGameProfile(buffer_chunk, statsProvider.getGameProfile()); + } + // -------------------------------------------------- + @SuppressWarnings("unchecked") + private static final void write_fileChunk_stats(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //iterate all stat types, and write their corresponding stat data one by one + for(final var statType : Registries.STAT_TYPE) + { + //create a buffer for the stat type chunk, and write the chunk ID to it + PacketByteBuf buffer_st = new PacketByteBuf(Unpooled.buffer()); + buffer_st.writeString(Objects.toString(Registries.STAT_TYPE.getId(statType))); //write chunk id + + //write the stats data for the given stats type, to the stats type buffer + write_fileChunk_stats_statType(buffer_st, statsProvider, (StatType)statType); //write chunk data + + //write the stat type buffer data to the chunk buffer + buffer_chunk.writeIntLE(buffer_st.readableBytes()); + buffer_chunk.writeBytes(buffer_st); + buffer_st.release(); + } + } + + private static final void write_fileChunk_stats_statType( + PacketByteBuf buffer_st, IStatsProvider statsProvider, StatType statType) + { + //obtain the registry, and iterate all of its items + final var registry = statType.getRegistry(); + for(final var registryItem : registry) + { + //obtain the stat value for the given registry item + //also skip "zero" stats, as they do not have a value + final int statValue = statsProvider.getStatValue(statType, registryItem); + if(statValue == 0) continue; + + //obtain the id of the registry item + final var registryItemId = registry.getId(registryItem); + + //write stat id and value + buffer_st.writeString(registryItemId.getNamespace().equals(Identifier.DEFAULT_NAMESPACE) ? + registryItemId.getPath() : Objects.toString(registryItemId)); //write stat id + buffer_st.writeIntLE(statValue); //write stat value + } + } + // -------------------------------------------------- + private static final void write_fileChunk_playerBadges(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //obtain a map of mod stats + final var stats = SUPlayerBadgeStat.getPlayerBadgeStatsByModGroups(statsProvider, stat -> !stat.isEmpty()); + + //iterate groups, and write their data + for(final var entry : stats.entrySet()) + { + //obtain the group id and its stats + final var groupModId = entry.getKey(); + final var groupStats = entry.getValue(); + + //write the group id and its length + buffer_chunk.writeString(groupModId); + buffer_chunk.writeVarInt(groupStats.size()); + + //write group entries + for(final var stat : groupStats) + { + buffer_chunk.writeString(stat.getStatID().getPath()); + buffer_chunk.writeVarInt(stat.value); + } + } + } + // ================================================== + static final void read_fileChunks(PacketByteBuf buffer_file, IEditableStatsProvider statsProvider) + throws IllegalHeaderException, UnsupportedFileVersionException + { + //read chunks + while(buffer_file.readableBytes() > 0) + { + //read next chunk's size, and check it + final int chunkSize = buffer_file.readIntLE(); + if(buffer_file.readableBytes() < chunkSize) + throw new IllegalHeaderException( + "chunk size >= " + chunkSize, + "chunk size == " + buffer_file.readableBytes()); + + //read the chunk data + //(creates a view of the original buffer, so it doesn't have to be released separately) + final var buffer_chunk = new PacketByteBuf(buffer_file.readSlice(chunkSize)); + final var chunkId = buffer_chunk.readString(); + switch(chunkId) + { + case "metadata": read_fileChunk_metadata(buffer_chunk, statsProvider); break; + case "stats": read_fileChunk_stats(buffer_chunk, statsProvider); break; + case "tcdcommons:player_badges": read_fileChunk_playerBadges(buffer_chunk, statsProvider); break; + default: break; + } + } + } + // -------------------------------------------------- + private static final void read_fileChunk_metadata(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + { + statsProvider.setDisplayName(literal(buffer_chunk.readString())); + statsProvider.setGameProfile(StatsProviderIO.readGameProfile(buffer_chunk)); + } + // -------------------------------------------------- + private static final void read_fileChunk_stats(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + throws IllegalHeaderException + { + //keep reading chunks as they come in + while(buffer_chunk.readableBytes() > 0) + { + //read next chunk's size, and check it + final int chunkSize = buffer_chunk.readIntLE(); //read stats type chunk size + if(buffer_chunk.readableBytes() < chunkSize) + throw new IllegalHeaderException( + "chunk size >= " + chunkSize, + "chunk size == " + buffer_chunk.readableBytes()); + + //read the chunk data + //(creates a view of the original buffer, so it doesn't have to be released separately) + final var buffer_st = new PacketByteBuf(buffer_chunk.readSlice(chunkSize)); //read stats type chunk data + read_fileChunk_stats_statType(buffer_st, statsProvider); + } + } + + @SuppressWarnings("unchecked") + private static final void read_fileChunk_stats_statType(PacketByteBuf buffer_st, IEditableStatsProvider statsProvider) + { + //read the stat type identifier + final var statTypeId = new Identifier(buffer_st.readString()); + + //obtain the stat type and check if it exists + final @Nullable var statType = Registries.STAT_TYPE.containsId(statTypeId) ? + (StatType)Registries.STAT_TYPE.getOrEmpty(statTypeId).get() : null; + if(statType == null) return; + final var statTypeRegistry = statType.getRegistry(); + + //read stats one by one + while(buffer_st.readableBytes() > 0) + { + //read stat id and stat value + final Identifier statId = new Identifier(buffer_st.readString()); + final int statValue = buffer_st.readIntLE(); + + //obtain the registry item, null check it, and store its value to the stats provider + final @Nullable var item = statTypeRegistry.getOrEmpty(statId).orElse(null); + if(item == null) continue; + statsProvider.setStatValue(statType, item, statValue); + } + } + // -------------------------------------------------- + private static final void read_fileChunk_playerBadges(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + { + while(buffer_chunk.readableBytes() > 0) + { + //read the next mod id and how many entries it has + final String modId = buffer_chunk.readString(); + final int entryCount = buffer_chunk.readVarInt(); + + //read all entries for the corresponding mod id + for(int i = 0; i < entryCount; i++) + { + //read player badge stat data + final String playerBadgeIdPath = buffer_chunk.readString(); + final int value = buffer_chunk.readVarInt(); + + //obtain mob, and store its stats + final Identifier playerBadgeId = new Identifier(modId, playerBadgeIdPath); + statsProvider.setPlayerBadgeValue(playerBadgeId, value); + } + } + } + // ================================================== +} \ No newline at end of file diff --git a/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt b/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt deleted file mode 100644 index 5eaabffa..00000000 --- a/betterstats-3-fabric-1.20.2/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt +++ /dev/null @@ -1,27 +0,0 @@ -//Note: Square brackets after a chunk refers to a chunk array; For example `IntLE-Chunk<...>[]` -// To read a chunk, first read the IntLE that is prefixed before that indicates the chunk length in bytes -// All subchunks start with `PBB-String` that indicates the chunk ID. -// To read a `PBB-String`, first read the VarInt String length, and then the String bytes. - -<"RIFF", IntLE-Chunk<"EXTENSION", IntLE-VERSION, IntLE-Chunk<...>[]> - -//The "EXTENSION" always has to be uppercase and be 4 bytes in length -//The `IntLE-Chunk<...>` refers to one of the 5 possible chunk types: 'metadata', 'general', 'item', 'mob', 'player_badge' -//`PBB` refers to `PacketByteBuf` and `PBB-String` refers to a String written by a PacketByteBuf - -// ------------------------------------------------ -// next up is the documentation on how to read the sub-chunks themselves -// ------------------------------------------------ - -//contains chunk ID and a Text for display name -IntLE-Chunk - -//contains chunk ID, and an array of "groups". each group starts with an Identifier-namespace String, -//indicating the "mod id" the following set of stats belong to, after which comes an array of stat id paths and their values -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> diff --git a/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java b/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java index 7fcebd6d..5faf1589 100644 --- a/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java +++ b/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO.java @@ -37,11 +37,12 @@ private StatsProviderIO() {} * still maintaining support for the older chunks. * @apiNote TLDR; Only increase if backwards compatibility is impossible. */ - public static final int FILE_VERSION = 2; + public static final int FILE_VERSION = 4; /* # File version history: * 1 - Since v3.0-alpha.1 - Initial version * 2 - Since v3.0-alpha.3 - Major changes to the player badge system - * 3 - Since v3.9 - Support for modded stat types + * 3 - Since v3.9 - [Reserved for special use-case] + * 4 - Since v3.9 - Support for modded stat types */ // ================================================== /** @@ -61,12 +62,13 @@ public static final void write(PacketByteBuf buffer, IStatsProvider statsProvide buffer.writeBytes("RIFF".getBytes(US_ASCII)); //write data - write_file(buffer, statsProvider); + write_file(buffer, statsProvider, FILE_VERSION); } - @SuppressWarnings("deprecation") - private static final void write_file(PacketByteBuf buffer, IStatsProvider statsProvider) + private static final void write_file(PacketByteBuf buffer, IStatsProvider statsProvider, int fileVersion) { + if(fileVersion < 1) throw new IllegalArgumentException("Attempting to write file version < 1."); + //create the buffer final var buffer_file = new PacketByteBuf(Unpooled.buffer()); @@ -76,12 +78,13 @@ private static final void write_file(PacketByteBuf buffer, IStatsProvider statsP buffer_file.writeBytes(FILE_EXTENSION.toUpperCase().getBytes(US_ASCII)); //write the file version - buffer_file.writeIntLE(FILE_VERSION); + buffer_file.writeIntLE(fileVersion); //write chunks - switch(FILE_VERSION) + switch(fileVersion) { case 2: StatsProviderIO_fv2.write_fileChunks(buffer_file, statsProvider); break; + case 4: StatsProviderIO_fv4.write_fileChunks(buffer_file, statsProvider); break; default: break; } @@ -130,7 +133,6 @@ public static final void read(PacketByteBuf buffer, IEditableStatsProvider stats catch(IllegalHeaderException | UnsupportedFileVersionException exc) { buffer.resetReaderIndex(); throw exc; } } // -------------------------------------------------- - @SuppressWarnings("deprecation") private static final void read_file(PacketByteBuf buffer_file, IEditableStatsProvider statsProvider) throws IllegalHeaderException, UnsupportedFileVersionException { @@ -146,6 +148,7 @@ private static final void read_file(PacketByteBuf buffer_file, IEditableStatsPro switch(fileVersion) { case 2: StatsProviderIO_fv2.read_fileChunks(buffer_file, statsProvider); break; + case 4: StatsProviderIO_fv4.read_fileChunks(buffer_file, statsProvider); break; default: throw new UnsupportedFileVersionException(Integer.toString(fileVersion)); } } diff --git a/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java b/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java index cc742fad..92f74ee2 100644 --- a/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java +++ b/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv2.java @@ -5,6 +5,7 @@ import java.util.UUID; import java.util.stream.Collectors; +import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.Nullable; import com.google.common.collect.Lists; @@ -29,10 +30,9 @@ * A class containing {@link StatsProviderIO} logic for file version 2. * @apiNote Used for backwards-compatibility. Internal use only! */ -public final @Deprecated class StatsProviderIO_fv2 +@Internal +public final class StatsProviderIO_fv2 { - // ================================================== - public static final int FILE_VERSION = 2; //Must always be 2 // ================================================== static final void write_fileChunks(PacketByteBuf buffer_file, IStatsProvider statsProvider) { diff --git a/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java b/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java new file mode 100644 index 00000000..f5a54c18 --- /dev/null +++ b/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/StatsProviderIO_fv4.java @@ -0,0 +1,226 @@ +package io.github.thecsdev.betterstats.api.util.io; + +import static io.github.thecsdev.tcdcommons.api.util.TextUtils.literal; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +import io.github.thecsdev.betterstats.api.util.stats.SUPlayerBadgeStat; +import io.github.thecsdev.tcdcommons.api.util.exceptions.UnsupportedFileVersionException; +import io.netty.buffer.Unpooled; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.registry.Registries; +import net.minecraft.stat.StatType; +import net.minecraft.util.Identifier; + +public class StatsProviderIO_fv4 +{ + // ================================================== + static final void write_fileChunks(PacketByteBuf buffer_file, IStatsProvider statsProvider) + { + write_fileChunk("metadata", buffer_file, statsProvider); + write_fileChunk("stats", buffer_file, statsProvider); + write_fileChunk("tcdcommons:player_badges", buffer_file, statsProvider); + } + // -------------------------------------------------- + private static final void write_fileChunk(String chunkId, PacketByteBuf buffer_file, IStatsProvider statsProvider) + { + //create a buffer for the chunk, and write the chunk ID to it + PacketByteBuf buffer_chunk = new PacketByteBuf(Unpooled.buffer()); + buffer_chunk.writeString(chunkId); + + //obtain and write chunk data to the chunk buffer + switch(chunkId) + { + case "metadata": write_fileChunk_metadata(buffer_chunk, statsProvider); break; + case "stats": write_fileChunk_stats(buffer_chunk, statsProvider); break; + case "tcdcommons:player_badges": write_fileChunk_playerBadges(buffer_chunk, statsProvider); break; + default: break; + } + + //write the chunk data buffer to the file buffer + buffer_file.writeIntLE(buffer_chunk.readableBytes()); + buffer_file.writeBytes(buffer_chunk); + buffer_chunk.release(); + } + // -------------------------------------------------- + private static final void write_fileChunk_metadata(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //obtain the stats display name as string + final var statsNameText = statsProvider.getDisplayName(); + final var statsName = (statsNameText != null) ? statsNameText.getString() : "-"; + + //write name and game profile + buffer_chunk.writeString(statsName); + StatsProviderIO.writeGameProfile(buffer_chunk, statsProvider.getGameProfile()); + } + // -------------------------------------------------- + @SuppressWarnings("unchecked") + private static final void write_fileChunk_stats(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //iterate all stat types, and write their corresponding stat data one by one + for(final var statType : Registries.STAT_TYPE) + { + //create a buffer for the stat type chunk, and write the chunk ID to it + PacketByteBuf buffer_st = new PacketByteBuf(Unpooled.buffer()); + buffer_st.writeString(Objects.toString(Registries.STAT_TYPE.getId(statType))); //write chunk id + + //write the stats data for the given stats type, to the stats type buffer + write_fileChunk_stats_statType(buffer_st, statsProvider, (StatType)statType); //write chunk data + + //write the stat type buffer data to the chunk buffer + buffer_chunk.writeIntLE(buffer_st.readableBytes()); + buffer_chunk.writeBytes(buffer_st); + buffer_st.release(); + } + } + + private static final void write_fileChunk_stats_statType( + PacketByteBuf buffer_st, IStatsProvider statsProvider, StatType statType) + { + //obtain the registry, and iterate all of its items + final var registry = statType.getRegistry(); + for(final var registryItem : registry) + { + //obtain the stat value for the given registry item + //also skip "zero" stats, as they do not have a value + final int statValue = statsProvider.getStatValue(statType, registryItem); + if(statValue == 0) continue; + + //obtain the id of the registry item + final var registryItemId = registry.getId(registryItem); + + //write stat id and value + buffer_st.writeString(registryItemId.getNamespace().equals(Identifier.DEFAULT_NAMESPACE) ? + registryItemId.getPath() : Objects.toString(registryItemId)); //write stat id + buffer_st.writeIntLE(statValue); //write stat value + } + } + // -------------------------------------------------- + private static final void write_fileChunk_playerBadges(PacketByteBuf buffer_chunk, IStatsProvider statsProvider) + { + //obtain a map of mod stats + final var stats = SUPlayerBadgeStat.getPlayerBadgeStatsByModGroups(statsProvider, stat -> !stat.isEmpty()); + + //iterate groups, and write their data + for(final var entry : stats.entrySet()) + { + //obtain the group id and its stats + final var groupModId = entry.getKey(); + final var groupStats = entry.getValue(); + + //write the group id and its length + buffer_chunk.writeString(groupModId); + buffer_chunk.writeVarInt(groupStats.size()); + + //write group entries + for(final var stat : groupStats) + { + buffer_chunk.writeString(stat.getStatID().getPath()); + buffer_chunk.writeVarInt(stat.value); + } + } + } + // ================================================== + static final void read_fileChunks(PacketByteBuf buffer_file, IEditableStatsProvider statsProvider) + throws IllegalHeaderException, UnsupportedFileVersionException + { + //read chunks + while(buffer_file.readableBytes() > 0) + { + //read next chunk's size, and check it + final int chunkSize = buffer_file.readIntLE(); + if(buffer_file.readableBytes() < chunkSize) + throw new IllegalHeaderException( + "chunk size >= " + chunkSize, + "chunk size == " + buffer_file.readableBytes()); + + //read the chunk data + //(creates a view of the original buffer, so it doesn't have to be released separately) + final var buffer_chunk = new PacketByteBuf(buffer_file.readSlice(chunkSize)); + final var chunkId = buffer_chunk.readString(); + switch(chunkId) + { + case "metadata": read_fileChunk_metadata(buffer_chunk, statsProvider); break; + case "stats": read_fileChunk_stats(buffer_chunk, statsProvider); break; + case "tcdcommons:player_badges": read_fileChunk_playerBadges(buffer_chunk, statsProvider); break; + default: break; + } + } + } + // -------------------------------------------------- + private static final void read_fileChunk_metadata(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + { + statsProvider.setDisplayName(literal(buffer_chunk.readString())); + statsProvider.setGameProfile(StatsProviderIO.readGameProfile(buffer_chunk)); + } + // -------------------------------------------------- + private static final void read_fileChunk_stats(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + throws IllegalHeaderException + { + //keep reading chunks as they come in + while(buffer_chunk.readableBytes() > 0) + { + //read next chunk's size, and check it + final int chunkSize = buffer_chunk.readIntLE(); //read stats type chunk size + if(buffer_chunk.readableBytes() < chunkSize) + throw new IllegalHeaderException( + "chunk size >= " + chunkSize, + "chunk size == " + buffer_chunk.readableBytes()); + + //read the chunk data + //(creates a view of the original buffer, so it doesn't have to be released separately) + final var buffer_st = new PacketByteBuf(buffer_chunk.readSlice(chunkSize)); //read stats type chunk data + read_fileChunk_stats_statType(buffer_st, statsProvider); + } + } + + @SuppressWarnings("unchecked") + private static final void read_fileChunk_stats_statType(PacketByteBuf buffer_st, IEditableStatsProvider statsProvider) + { + //read the stat type identifier + final var statTypeId = new Identifier(buffer_st.readString()); + + //obtain the stat type and check if it exists + final @Nullable var statType = Registries.STAT_TYPE.containsId(statTypeId) ? + (StatType)Registries.STAT_TYPE.getOrEmpty(statTypeId).get() : null; + if(statType == null) return; + final var statTypeRegistry = statType.getRegistry(); + + //read stats one by one + while(buffer_st.readableBytes() > 0) + { + //read stat id and stat value + final Identifier statId = new Identifier(buffer_st.readString()); + final int statValue = buffer_st.readIntLE(); + + //obtain the registry item, null check it, and store its value to the stats provider + final @Nullable var item = statTypeRegistry.getOrEmpty(statId).orElse(null); + if(item == null) continue; + statsProvider.setStatValue(statType, item, statValue); + } + } + // -------------------------------------------------- + private static final void read_fileChunk_playerBadges(PacketByteBuf buffer_chunk, IEditableStatsProvider statsProvider) + { + while(buffer_chunk.readableBytes() > 0) + { + //read the next mod id and how many entries it has + final String modId = buffer_chunk.readString(); + final int entryCount = buffer_chunk.readVarInt(); + + //read all entries for the corresponding mod id + for(int i = 0; i < entryCount; i++) + { + //read player badge stat data + final String playerBadgeIdPath = buffer_chunk.readString(); + final int value = buffer_chunk.readVarInt(); + + //obtain mob, and store its stats + final Identifier playerBadgeId = new Identifier(modId, playerBadgeIdPath); + statsProvider.setPlayerBadgeValue(playerBadgeId, value); + } + } + } + // ================================================== +} \ No newline at end of file diff --git a/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt b/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt deleted file mode 100644 index 5eaabffa..00000000 --- a/betterstats-3-fabric-1.20.4/src/main/java/io/github/thecsdev/betterstats/api/util/io/riff_format.txt +++ /dev/null @@ -1,27 +0,0 @@ -//Note: Square brackets after a chunk refers to a chunk array; For example `IntLE-Chunk<...>[]` -// To read a chunk, first read the IntLE that is prefixed before that indicates the chunk length in bytes -// All subchunks start with `PBB-String` that indicates the chunk ID. -// To read a `PBB-String`, first read the VarInt String length, and then the String bytes. - -<"RIFF", IntLE-Chunk<"EXTENSION", IntLE-VERSION, IntLE-Chunk<...>[]> - -//The "EXTENSION" always has to be uppercase and be 4 bytes in length -//The `IntLE-Chunk<...>` refers to one of the 5 possible chunk types: 'metadata', 'general', 'item', 'mob', 'player_badge' -//`PBB` refers to `PacketByteBuf` and `PBB-String` refers to a String written by a PacketByteBuf - -// ------------------------------------------------ -// next up is the documentation on how to read the sub-chunks themselves -// ------------------------------------------------ - -//contains chunk ID and a Text for display name -IntLE-Chunk - -//contains chunk ID, and an array of "groups". each group starts with an Identifier-namespace String, -//indicating the "mod id" the following set of stats belong to, after which comes an array of stat id paths and their values -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]> - -IntLE-Chunk[]>[]>