Skip to content

Commit

Permalink
Include gamerule settings in metadata (#425)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucko authored Jul 21, 2024
1 parent 635800a commit b78afab
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;
Expand All @@ -32,6 +33,7 @@

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;
Expand Down Expand Up @@ -110,6 +112,33 @@ public ChunksResult<BukkitChunkInfo> pollChunks() {
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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,19 @@ public CompletableFuture<WorldInfoProvider.ChunksResult<? extends ChunkInfo<?>>>
return async(WorldInfoProvider::pollChunks);
}

public CompletableFuture<WorldInfoProvider.GameRulesResult> pollGameRules() {
return async(WorldInfoProvider::pollGameRules);
}

public WorldInfoProvider.CountsResult getCounts() {
return get(pollCounts());
}

public WorldInfoProvider.ChunksResult<? extends ChunkInfo<?>> getChunks() {
return get(pollChunks());
}

public WorldInfoProvider.GameRulesResult getGameRules() {
return get(pollGameRules());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public CountsResult pollCounts() {
public ChunksResult<? extends ChunkInfo<?>> pollChunks() {
return null;
}

@Override
public GameRulesResult pollGameRules() {
return null;
}
};

/**
Expand All @@ -55,6 +60,13 @@ public ChunksResult<? extends ChunkInfo<?>> pollChunks() {
*/
ChunksResult<? extends ChunkInfo<?>> pollChunks();

/**
* Polls for game rules.
*
* @return the game rules
*/
GameRulesResult pollGameRules();

default boolean mustCallSync() {
return true;
}
Expand Down Expand Up @@ -101,4 +113,37 @@ public int chunks() {
}
}

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;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public WorldStatisticsProvider(AsyncWorldInfoProvider provider) {
}

public WorldStatistics getWorldStatistics() {
WorldInfoProvider.ChunksResult<? extends ChunkInfo<?>> result = provider.getChunks();
WorldInfoProvider.ChunksResult<? extends ChunkInfo<?>> result = this.provider.getChunks();
if (result == null) {
return null;
}
Expand Down Expand Up @@ -70,6 +70,16 @@ public WorldStatistics getWorldStatistics() {
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();
}

Expand Down
7 changes: 7 additions & 0 deletions spark-common/src/main/proto/spark/spark.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
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;
Expand Down Expand Up @@ -101,6 +102,28 @@ public ChunksResult<FabricChunkInfo> pollChunks() {

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 {
Expand Down Expand Up @@ -128,13 +151,13 @@ public CountsResult pollCounts() {

@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();

Expand All @@ -143,6 +166,32 @@ public ChunksResult<FabricChunkInfo> pollChunks() {

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<?>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -97,6 +98,29 @@ public ChunksResult<ForgeChunkInfo> pollChunks() {

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 {
Expand Down Expand Up @@ -124,13 +148,13 @@ public CountsResult pollCounts() {

@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;

Expand All @@ -139,6 +163,32 @@ public ChunksResult<ForgeChunkInfo> pollChunks() {

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<?>> {
Expand Down
Loading

0 comments on commit b78afab

Please sign in to comment.