diff options
author | lucko <git@lucko.me> | 2024-07-21 12:44:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-21 12:44:06 +0100 |
commit | b78afab85ee15d809ea4ff0ccb14c2b263ede860 (patch) | |
tree | d3d987473aee0b1e71ac6a24ea2f692be78bf14b | |
parent | 635800af986351f08dba33aa2e8ec5dd691bbffb (diff) | |
download | spark-b78afab85ee15d809ea4ff0ccb14c2b263ede860.tar.gz spark-b78afab85ee15d809ea4ff0ccb14c2b263ede860.tar.bz2 spark-b78afab85ee15d809ea4ff0ccb14c2b263ede860.zip |
Include gamerule settings in metadata (#425)
12 files changed, 318 insertions, 8 deletions
diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitWorldInfoProvider.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitWorldInfoProvider.java index 8d2ea51..6290391 100644 --- a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitWorldInfoProvider.java +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitWorldInfoProvider.java @@ -24,6 +24,7 @@ import me.lucko.spark.common.platform.world.AbstractChunkInfo; import me.lucko.spark.common.platform.world.CountMap; import me.lucko.spark.common.platform.world.WorldInfoProvider; import org.bukkit.Chunk; +import org.bukkit.GameRule; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.block.BlockState; @@ -32,6 +33,7 @@ import org.bukkit.entity.EntityType; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class BukkitWorldInfoProvider implements WorldInfoProvider { private static final boolean SUPPORTS_PAPER_COUNT_METHODS; @@ -110,6 +112,33 @@ public class BukkitWorldInfoProvider implements WorldInfoProvider { return data; } + @Override + public GameRulesResult pollGameRules() { + GameRulesResult data = new GameRulesResult(); + + boolean addDefaults = true; // add defaults in the first iteration + for (World world : this.server.getWorlds()) { + for (String gameRule : world.getGameRules()) { + GameRule<?> ruleObj = GameRule.getByName(gameRule); + if (ruleObj == null) { + continue; + } + + if (addDefaults) { + Object defaultValue = world.getGameRuleDefault(ruleObj); + data.putDefault(gameRule, Objects.toString(defaultValue)); + } + + Object value = world.getGameRuleValue(ruleObj); + data.put(gameRule, world.getName(), Objects.toString(value)); + } + + addDefaults = false; + } + + return data; + } + static final class BukkitChunkInfo extends AbstractChunkInfo<EntityType> { private final CountMap<EntityType> entityCounts; diff --git a/spark-common/src/main/java/me/lucko/spark/common/platform/world/AsyncWorldInfoProvider.java b/spark-common/src/main/java/me/lucko/spark/common/platform/world/AsyncWorldInfoProvider.java index 82cddef..0f48b2e 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/platform/world/AsyncWorldInfoProvider.java +++ b/spark-common/src/main/java/me/lucko/spark/common/platform/world/AsyncWorldInfoProvider.java @@ -80,6 +80,10 @@ public class AsyncWorldInfoProvider { return async(WorldInfoProvider::pollChunks); } + public CompletableFuture<WorldInfoProvider.GameRulesResult> pollGameRules() { + return async(WorldInfoProvider::pollGameRules); + } + public WorldInfoProvider.CountsResult getCounts() { return get(pollCounts()); } @@ -87,4 +91,8 @@ public class AsyncWorldInfoProvider { public WorldInfoProvider.ChunksResult<? extends ChunkInfo<?>> getChunks() { return get(pollChunks()); } + + public WorldInfoProvider.GameRulesResult getGameRules() { + return get(pollGameRules()); + } } diff --git a/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldInfoProvider.java b/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldInfoProvider.java index 7fb581d..e360ea4 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldInfoProvider.java +++ b/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldInfoProvider.java @@ -39,6 +39,11 @@ public interface WorldInfoProvider { public ChunksResult<? extends ChunkInfo<?>> pollChunks() { return null; } + + @Override + public GameRulesResult pollGameRules() { + return null; + } }; /** @@ -55,6 +60,13 @@ public interface WorldInfoProvider { */ ChunksResult<? extends ChunkInfo<?>> pollChunks(); + /** + * Polls for game rules. + * + * @return the game rules + */ + GameRulesResult pollGameRules(); + default boolean mustCallSync() { return true; } @@ -101,4 +113,37 @@ public interface WorldInfoProvider { } } + final class GameRulesResult { + private final Map<String, GameRule> rules = new HashMap<>(); + + private GameRule rule(String name) { + return this.rules.computeIfAbsent(name, k -> new GameRule()); + } + + public void put(String gameRuleName, String worldName, String value) { + rule(gameRuleName).worldValues.put(worldName, value); + } + + public void putDefault(String gameRuleName, String value) { + rule(gameRuleName).defaultValue = value; + } + + public Map<String, GameRule> getRules() { + return this.rules; + } + + public static final class GameRule { + Map<String, String> worldValues = new HashMap<>(); + String defaultValue = null; + + public String getDefaultValue() { + return this.defaultValue; + } + + public Map<String, String> getWorldValues() { + return this.worldValues; + } + } + } + } diff --git a/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldStatisticsProvider.java b/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldStatisticsProvider.java index 7e63222..5d00037 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldStatisticsProvider.java +++ b/spark-common/src/main/java/me/lucko/spark/common/platform/world/WorldStatisticsProvider.java @@ -38,7 +38,7 @@ public class WorldStatisticsProvider { } public WorldStatistics getWorldStatistics() { - WorldInfoProvider.ChunksResult<? extends ChunkInfo<?>> result = provider.getChunks(); + WorldInfoProvider.ChunksResult<? extends ChunkInfo<?>> result = this.provider.getChunks(); if (result == null) { return null; } @@ -70,6 +70,16 @@ public class WorldStatisticsProvider { stats.setTotalEntities(combinedTotal.get()); combined.asMap().forEach((key, value) -> stats.putEntityCounts(key, value.get())); + WorldInfoProvider.GameRulesResult gameRules = this.provider.getGameRules(); + if (gameRules != null) { + gameRules.getRules().forEach((ruleName, rule) -> stats.addGameRules(WorldStatistics.GameRule.newBuilder() + .setName(ruleName) + .setDefaultValue(rule.getDefaultValue()) + .putAllWorldValues(rule.getWorldValues()) + .build() + )); + } + return stats.build(); } diff --git a/spark-common/src/main/proto/spark/spark.proto b/spark-common/src/main/proto/spark/spark.proto index 53750d7..6ffd4e9 100644 --- a/spark-common/src/main/proto/spark/spark.proto +++ b/spark-common/src/main/proto/spark/spark.proto @@ -140,6 +140,7 @@ message WorldStatistics { int32 total_entities = 1; map<string, int32> entity_counts = 2; repeated World worlds = 3; + repeated GameRule game_rules = 4; message World { string name = 1; @@ -158,6 +159,12 @@ message WorldStatistics { int32 total_entities = 3; map<string, int32> entity_counts = 4; } + + message GameRule { + string name = 1; + string default_value = 2; + map<string, string> world_values = 3; + } } message WindowStatistics { diff --git a/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java b/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java index 926e8a3..d7dc7a7 100644 --- a/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java +++ b/spark-fabric/src/main/java/me/lucko/spark/fabric/FabricWorldInfoProvider.java @@ -37,6 +37,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.world.ServerEntityManager; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.GameRules; import net.minecraft.world.entity.ClientEntityManager; import net.minecraft.world.entity.EntityIndex; import net.minecraft.world.entity.EntityTrackingSection; @@ -101,6 +102,28 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { return data; } + + @Override + public GameRulesResult pollGameRules() { + GameRulesResult data = new GameRulesResult(); + Iterable<ServerWorld> worlds = this.server.getWorlds(); + + GameRules.accept(new GameRules.Visitor() { + @Override + public <T extends GameRules.Rule<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) { + String defaultValue = type.createRule().serialize(); + data.putDefault(key.getName(), defaultValue); + + for (ServerWorld world : worlds) { + String worldName = world.getRegistryKey().getValue().getPath(); + + String value = world.getGameRules().get(key).serialize(); + data.put(key.getName(), worldName, value); + } + } + }); + return data; + } } public static final class Client extends FabricWorldInfoProvider { @@ -128,13 +151,13 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { @Override public ChunksResult<FabricChunkInfo> pollChunks() { - ChunksResult<FabricChunkInfo> data = new ChunksResult<>(); - ClientWorld world = this.client.world; if (world == null) { return null; } + ChunksResult<FabricChunkInfo> data = new ChunksResult<>(); + ClientEntityManager<Entity> entityManager = ((ClientWorldAccessor) world).getEntityManager(); SectionedEntityCache<Entity> cache = ((ClientEntityManagerAccessor) entityManager).getCache(); @@ -143,6 +166,32 @@ public abstract class FabricWorldInfoProvider implements WorldInfoProvider { return data; } + + @Override + public GameRulesResult pollGameRules() { + ClientWorld world = this.client.world; + if (world == null) { + return null; + } + + GameRulesResult data = new GameRulesResult(); + + String worldName = world.getRegistryKey().getValue().getPath(); + GameRules worldRules = world.getGameRules(); + + GameRules.accept(new GameRules.Visitor() { + @Override + public <T extends GameRules.Rule<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) { + String defaultValue = type.createRule().serialize(); + data.putDefault(key.getName(), defaultValue); + + String value = worldRules.get(key).serialize(); + data.put(key.getName(), worldName, value); + } + }); + + return data; + } } static final class FabricChunkInfo extends AbstractChunkInfo<EntityType<?>> { diff --git a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java index e10a0c8..286605a 100644 --- a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java +++ b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeWorldInfoProvider.java @@ -32,6 +32,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.GameRules; import net.minecraft.world.level.entity.EntityLookup; import net.minecraft.world.level.entity.EntitySection; import net.minecraft.world.level.entity.EntitySectionStorage; @@ -97,6 +98,29 @@ public abstract class ForgeWorldInfoProvider implements WorldInfoProvider { return data; } + + @Override + public GameRulesResult pollGameRules() { + GameRulesResult data = new GameRulesResult(); + Iterable<ServerLevel> levels = this.server.getAllLevels(); + + GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() { + @Override + public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) { + String defaultValue = type.createRule().serialize(); + data.putDefault(key.getId(), defaultValue); + + for (ServerLevel level : levels) { + String levelName = level.dimension().location().getPath(); + + String value = level.getGameRules().getRule(key).serialize(); + data.put(key.getId(), levelName, value); + } + } + }); + + return data; + } } public static final class Client extends ForgeWorldInfoProvider { @@ -124,13 +148,13 @@ public abstract class ForgeWorldInfoProvider implements WorldInfoProvider { @Override public ChunksResult<ForgeChunkInfo> pollChunks() { - ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); - ClientLevel level = this.client.level; if (level == null) { return null; } + ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); + TransientEntitySectionManager<Entity> entityManager = level.entityStorage; EntitySectionStorage<Entity> cache = entityManager.sectionStorage; @@ -139,6 +163,32 @@ public abstract class ForgeWorldInfoProvider implements WorldInfoProvider { return data; } + + @Override + public GameRulesResult pollGameRules() { + ClientLevel level = this.client.level; + if (level == null) { + return null; + } + + GameRulesResult data = new GameRulesResult(); + + String levelName = level.dimension().location().getPath(); + GameRules levelRules = level.getGameRules(); + + GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() { + @Override + public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) { + String defaultValue = type.createRule().serialize(); + data.putDefault(key.getId(), defaultValue); + + String value = levelRules.getRule(key).serialize(); + data.put(key.getId(), levelName, value); + } + }); + + return data; + } } static final class ForgeChunkInfo extends AbstractChunkInfo<EntityType<?>> { diff --git a/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java b/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java index ef76646..fe6a6c8 100644 --- a/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java +++ b/spark-neoforge/src/main/java/me/lucko/spark/neoforge/NeoForgeWorldInfoProvider.java @@ -32,6 +32,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.GameRules; import net.minecraft.world.level.entity.EntityLookup; import net.minecraft.world.level.entity.EntitySection; import net.minecraft.world.level.entity.EntitySectionStorage; @@ -97,6 +98,29 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { return data; } + + @Override + public GameRulesResult pollGameRules() { + GameRulesResult data = new GameRulesResult(); + Iterable<ServerLevel> levels = this.server.getAllLevels(); + + GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() { + @Override + public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) { + String defaultValue = type.createRule().serialize(); + data.putDefault(key.getId(), defaultValue); + + for (ServerLevel level : levels) { + String levelName = level.dimension().location().getPath(); + + String value = level.getGameRules().getRule(key).serialize(); + data.put(key.getId(), levelName, value); + } + } + }); + + return data; + } } public static final class Client extends NeoForgeWorldInfoProvider { @@ -124,13 +148,13 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { @Override public ChunksResult<ForgeChunkInfo> pollChunks() { - ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); - ClientLevel level = this.client.level; if (level == null) { return null; } + ChunksResult<ForgeChunkInfo> data = new ChunksResult<>(); + TransientEntitySectionManager<Entity> entityManager = level.entityStorage; EntitySectionStorage<Entity> cache = entityManager.sectionStorage; @@ -139,6 +163,32 @@ public abstract class NeoForgeWorldInfoProvider implements WorldInfoProvider { return data; } + + @Override + public GameRulesResult pollGameRules() { + ClientLevel level = this.client.level; + if (level == null) { + return null; + } + + GameRulesResult data = new GameRulesResult(); + + String levelName = level.dimension().location().getPath(); + GameRules levelRules = level.getGameRules(); + + GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() { + @Override + public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) { + String defaultValue = type.createRule().serialize(); + data.putDefault(key.getId(), defaultValue); + + String value = levelRules.getRule(key).serialize(); + data.put(key.getId(), levelName, value); + } + }); + + return data; + } } public static final class ForgeChunkInfo extends AbstractChunkInfo<EntityType<?>> { diff --git a/spark-paper/src/main/java/me/lucko/spark/paper/PaperSparkPlugin.java b/spark-paper/src/main/java/me/lucko/spark/paper/PaperSparkPlugin.java index eb595cc..eb82f8e 100644 --- a/spark-paper/src/main/java/me/lucko/spark/paper/PaperSparkPlugin.java +++ b/spark-paper/src/main/java/me/lucko/spark/paper/PaperSparkPlugin.java @@ -39,7 +39,6 @@ import me.lucko.spark.paper.api.PaperSparkModule; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.ServicePriority; import java.nio.file.Path; import java.util.Arrays; diff --git a/spark-paper/src/main/java/me/lucko/spark/paper/PaperWorldInfoProvider.java b/spark-paper/src/main/java/me/lucko/spark/paper/PaperWorldInfoProvider.java index 29ab1ef..feda9a0 100644 --- a/spark-paper/src/main/java/me/lucko/spark/paper/PaperWorldInfoProvider.java +++ b/spark-paper/src/main/java/me/lucko/spark/paper/PaperWorldInfoProvider.java @@ -24,6 +24,7 @@ import me.lucko.spark.common.platform.world.AbstractChunkInfo; import me.lucko.spark.common.platform.world.CountMap; import me.lucko.spark.common.platform.world.WorldInfoProvider; import org.bukkit.Chunk; +import org.bukkit.GameRule; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.Entity; @@ -31,6 +32,7 @@ import org.bukkit.entity.EntityType; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class PaperWorldInfoProvider implements WorldInfoProvider { private final Server server; @@ -75,6 +77,33 @@ public class PaperWorldInfoProvider implements WorldInfoProvider { return data; } + @Override + public GameRulesResult pollGameRules() { + GameRulesResult data = new GameRulesResult(); + + boolean addDefaults = true; // add defaults in the first iteration + for (World world : this.server.getWorlds()) { + for (String gameRule : world.getGameRules()) { + GameRule<?> ruleObj = GameRule.getByName(gameRule); + if (ruleObj == null) { + continue; + } + + if (addDefaults) { + Object defaultValue = world.getGameRuleDefault(ruleObj); + data.putDefault(gameRule, Objects.toString(defaultValue)); + } + + Object value = world.getGameRuleValue(ruleObj); + data.put(gameRule, world.getName(), Objects.toString(value)); + } + + addDefaults = false; + } + + return data; + } + static final class PaperChunkInfo extends AbstractChunkInfo<EntityType> { private final CountMap<EntityType> entityCounts; diff --git a/spark-sponge7/src/main/java/me/lucko/spark/sponge/Sponge7WorldInfoProvider.java b/spark-sponge7/src/main/java/me/lucko/spark/sponge/Sponge7WorldInfoProvider.java index 8c2e412..426ad47 100644 --- a/spark-sponge7/src/main/java/me/lucko/spark/sponge/Sponge7WorldInfoProvider.java +++ b/spark-sponge7/src/main/java/me/lucko/spark/sponge/Sponge7WorldInfoProvider.java @@ -34,6 +34,7 @@ import org.spongepowered.api.world.World; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; public class Sponge7WorldInfoProvider implements WorldInfoProvider { private final Server server; @@ -76,6 +77,21 @@ public class Sponge7WorldInfoProvider implements WorldInfoProvider { return data; } + @Override + public GameRulesResult pollGameRules() { + // No way to get defaults info on sponge API 7 :( + + // GameRulesResult data = new GameRulesResult(); + // for (World world : this.server.getWorlds()) { + // for (Map.Entry<String, String> entry : world.getGameRules().entrySet()) { + // data.put(entry.getKey(), world.getName(), entry.getValue()); + // } + // } + // return data; + + return null; + } + static final class Sponge7ChunkInfo extends AbstractChunkInfo<EntityType> { private final CountMap<EntityType> entityCounts; diff --git a/spark-sponge8/src/main/java/me/lucko/spark/sponge/Sponge8WorldInfoProvider.java b/spark-sponge8/src/main/java/me/lucko/spark/sponge/Sponge8WorldInfoProvider.java index fadd629..58f9c33 100644 --- a/spark-sponge8/src/main/java/me/lucko/spark/sponge/Sponge8WorldInfoProvider.java +++ b/spark-sponge8/src/main/java/me/lucko/spark/sponge/Sponge8WorldInfoProvider.java @@ -30,11 +30,14 @@ import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.entity.EntityTypes; import org.spongepowered.api.world.chunk.WorldChunk; +import org.spongepowered.api.world.gamerule.GameRule; +import org.spongepowered.api.world.gamerule.GameRules; import org.spongepowered.api.world.server.ServerWorld; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.stream.Collectors; public class Sponge8WorldInfoProvider implements WorldInfoProvider { private final Server server; @@ -77,6 +80,21 @@ public class Sponge8WorldInfoProvider implements WorldInfoProvider { return data; } + @Override + public GameRulesResult pollGameRules() { + GameRulesResult data = new GameRulesResult(); + + List<GameRule<?>> rules = GameRules.registry().stream().collect(Collectors.toList()); + for (GameRule<?> rule : rules) { + data.putDefault(rule.name(), rule.defaultValue().toString()); + for (ServerWorld world : this.server.worldManager().worlds()) { + data.put(rule.name(), world.key().value(), world.properties().gameRule(rule).toString()); + } + } + + return data; + } + static final class Sponge7ChunkInfo extends AbstractChunkInfo<EntityType<?>> { private final CountMap<EntityType<?>> entityCounts; |