diff options
Diffstat (limited to 'spark-sponge/src/main/java')
7 files changed, 743 insertions, 0 deletions
diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeClassSourceLookup.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeClassSourceLookup.java new file mode 100644 index 0000000..0820ae3 --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeClassSourceLookup.java @@ -0,0 +1,82 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.sponge; + +import com.google.common.collect.ImmutableMap; +import me.lucko.spark.common.sampler.source.ClassSourceLookup; +import org.spongepowered.api.Game; +import org.spongepowered.plugin.PluginCandidate; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.JVMPluginContainer; +import org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource; + +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Map; + +public class SpongeClassSourceLookup extends ClassSourceLookup.ByCodeSource { + private final Path modsDirectory; + private final Map<Path, String> pathToPluginMap; + + public SpongeClassSourceLookup(Game game) { + this.modsDirectory = game.gameDirectory().resolve("mods").toAbsolutePath().normalize(); + this.pathToPluginMap = constructPathToPluginIdMap(game.pluginManager().plugins()); + } + + @Override + public String identifyFile(Path path) { + String id = this.pathToPluginMap.get(path); + if (id != null) { + return id; + } + + if (!path.startsWith(this.modsDirectory)) { + return null; + } + + return super.identifyFileName(this.modsDirectory.relativize(path).toString()); + } + + // pretty nasty, but if it fails it doesn't really matter + @SuppressWarnings("unchecked") + private static Map<Path, String> constructPathToPluginIdMap(Collection<PluginContainer> plugins) { + ImmutableMap.Builder<Path, String> builder = ImmutableMap.builder(); + + try { + Field candidateField = JVMPluginContainer.class.getDeclaredField("candidate"); + candidateField.setAccessible(true); + + for (PluginContainer plugin : plugins) { + if (plugin instanceof JVMPluginContainer) { + PluginCandidate<JVMPluginResource> candidate = (PluginCandidate<JVMPluginResource>) candidateField.get(plugin); + Path path = candidate.resource().path().toAbsolutePath().normalize(); + builder.put(path, plugin.metadata().id()); + } + } + } catch (Exception e) { + // ignore + } + + return builder.build(); + } + +} diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeCommandSender.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeCommandSender.java new file mode 100644 index 0000000..aa634cc --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeCommandSender.java @@ -0,0 +1,94 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.sponge; + +import me.lucko.spark.common.command.sender.AbstractCommandSender; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.text.Component; +import org.spongepowered.api.command.CommandCause; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.util.Identifiable; + +import java.util.UUID; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class SpongeCommandSender extends AbstractCommandSender<Subject> { + private final CommandCause cause; + private final Audience audience; + + public SpongeCommandSender(CommandCause cause) { + super(cause); + this.cause = cause; + this.audience = cause.audience(); + } + + public <T extends Subject & Audience> SpongeCommandSender(T cause) { + super(cause); + this.cause = null; + this.audience = cause; + } + + @Override + public String getName() { + return super.delegate.friendlyIdentifier().orElse(super.delegate.identifier()); + } + + @Override + public UUID getUniqueId() { + if (this.cause != null) { + Identifiable identifiable = this.cause.first(Identifiable.class).orElse(null); + if (identifiable != null) { + return identifiable.uniqueId(); + } + } + + try { + return UUID.fromString(super.delegate.identifier()); + } catch (Exception e) { + return UUID.nameUUIDFromBytes(super.delegate.identifier().getBytes(UTF_8)); + } + } + + @Override + public void sendMessage(Component message) { + this.audience.sendMessage(Identity.nil(), message); + } + + @Override + public boolean hasPermission(String permission) { + return super.delegate.hasPermission(permission); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SpongeCommandSender that = (SpongeCommandSender) o; + return this.getUniqueId().equals(that.getUniqueId()); + } + + @Override + public int hashCode() { + return getUniqueId().hashCode(); + } +} diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlatformInfo.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlatformInfo.java new file mode 100644 index 0000000..e5811cd --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlatformInfo.java @@ -0,0 +1,60 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.sponge; + +import me.lucko.spark.common.platform.PlatformInfo; +import org.spongepowered.api.Game; +import org.spongepowered.api.Platform; +import org.spongepowered.plugin.metadata.PluginMetadata; + +public class SpongePlatformInfo implements PlatformInfo { + private final Game game; + + public SpongePlatformInfo(Game game) { + this.game = game; + } + + @Override + public Type getType() { + return Type.SERVER; + } + + @Override + public String getName() { + return "Sponge"; + } + + @Override + public String getBrand() { + PluginMetadata brandMetadata = this.game.platform().container(Platform.Component.IMPLEMENTATION).metadata(); + return brandMetadata.name().orElseGet(brandMetadata::id); + } + + @Override + public String getVersion() { + return this.game.platform().container(Platform.Component.IMPLEMENTATION).metadata().version().toString(); + } + + @Override + public String getMinecraftVersion() { + return this.game.platform().minecraftVersion().name(); + } +} diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlayerPingProvider.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlayerPingProvider.java new file mode 100644 index 0000000..b327a0a --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlayerPingProvider.java @@ -0,0 +1,45 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.sponge; + +import com.google.common.collect.ImmutableMap; +import me.lucko.spark.common.monitor.ping.PlayerPingProvider; +import org.spongepowered.api.Server; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; + +import java.util.Map; + +public class SpongePlayerPingProvider implements PlayerPingProvider { + private final Server server; + + public SpongePlayerPingProvider(Server server) { + this.server = server; + } + + @Override + public Map<String, Integer> poll() { + ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder(); + for (ServerPlayer player : this.server.onlinePlayers()) { + builder.put(player.name(), player.connection().latency()); + } + return builder.build(); + } +} diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java new file mode 100644 index 0000000..3542dae --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java @@ -0,0 +1,267 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.sponge; + +import com.google.common.base.Suppliers; +import com.google.inject.Inject; +import me.lucko.spark.common.SparkPlatform; +import me.lucko.spark.common.SparkPlugin; +import me.lucko.spark.common.command.sender.CommandSender; +import me.lucko.spark.common.monitor.ping.PlayerPingProvider; +import me.lucko.spark.common.platform.PlatformInfo; +import me.lucko.spark.common.platform.world.WorldInfoProvider; +import me.lucko.spark.common.sampler.ThreadDumper; +import me.lucko.spark.common.sampler.source.ClassSourceLookup; +import me.lucko.spark.common.sampler.source.SourceMetadata; +import me.lucko.spark.common.tick.TickHook; +import net.kyori.adventure.text.Component; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.api.Game; +import org.spongepowered.api.Server; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.command.CommandCause; +import org.spongepowered.api.command.CommandCompletion; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.parameter.ArgumentReader; +import org.spongepowered.api.command.registrar.tree.CommandTreeNode; +import org.spongepowered.api.config.ConfigDir; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.event.lifecycle.StartedEngineEvent; +import org.spongepowered.api.event.lifecycle.StoppingEngineEvent; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; +import org.spongepowered.plugin.metadata.model.PluginContributor; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Plugin("spark") +public class SpongeSparkPlugin implements SparkPlugin { + + private final PluginContainer pluginContainer; + private final Logger logger; + private final Game game; + private final Path configDirectory; + private final ExecutorService asyncExecutor; + private final Supplier<ExecutorService> syncExecutor; + private final ThreadDumper.GameThread gameThreadDumper = new ThreadDumper.GameThread(); + + private SparkPlatform platform; + + @Inject + public SpongeSparkPlugin(PluginContainer pluginContainer, Logger logger, Game game, @ConfigDir(sharedRoot = false) Path configDirectory) { + this.pluginContainer = pluginContainer; + this.logger = logger; + this.game = game; + this.configDirectory = configDirectory; + this.asyncExecutor = game.asyncScheduler().executor(pluginContainer); + this.syncExecutor = Suppliers.memoize(() -> { + if (this.game.isServerAvailable()) { + return this.game.server().scheduler().executor(this.pluginContainer); + } else if (this.game.isClientAvailable()) { + return this.game.client().scheduler().executor(this.pluginContainer); + } else { + throw new IllegalStateException("Server and client both unavailable"); + } + }); + } + + + @Listener + public void onRegisterCommands(final RegisterCommandEvent<Command.Raw> event) { + event.register(this.pluginContainer, new SparkCommand(this), this.pluginContainer.metadata().id()); + } + + @Listener + public void onEnable(StartedEngineEvent<Server> event) { + this.gameThreadDumper.setThread(Thread.currentThread()); + + this.platform = new SparkPlatform(this); + this.platform.enable(); + } + + @Listener + public void onDisable(StoppingEngineEvent<Server> event) { + this.platform.disable(); + } + + @Override + public String getVersion() { + return this.pluginContainer.metadata().version().toString(); + } + + @Override + public Path getPluginDirectory() { + return this.configDirectory; + } + + @Override + public String getCommandName() { + return "spark"; + } + + @Override + public Stream<CommandSender> getCommandSenders() { + if (this.game.isServerAvailable()) { + return Stream.concat( + this.game.server().onlinePlayers().stream(), + Stream.of(this.game.systemSubject()) + ).map(SpongeCommandSender::new); + } else { + return Stream.of(this.game.systemSubject()).map(SpongeCommandSender::new); + } + } + + @Override + public void executeAsync(Runnable task) { + this.asyncExecutor.execute(task); + } + + @Override + public void executeSync(Runnable task) { + this.syncExecutor.get().execute(task); + } + + @Override + public void log(Level level, String msg) { + if (level == Level.INFO) { + this.logger.info(msg); + } else if (level == Level.WARNING) { + this.logger.warn(msg); + } else if (level == Level.SEVERE) { + this.logger.error(msg); + } else { + throw new IllegalArgumentException(level.getName()); + } + } + + @Override + public ThreadDumper getDefaultThreadDumper() { + return this.gameThreadDumper.get(); + } + + @Override + public TickHook createTickHook() { + return new SpongeTickHook(this.pluginContainer, this.game); + } + + @Override + public ClassSourceLookup createClassSourceLookup() { + return new SpongeClassSourceLookup(this.game); + } + + @Override + public Collection<SourceMetadata> getKnownSources() { + return SourceMetadata.gather( + this.game.pluginManager().plugins(), + plugin -> plugin.metadata().id(), + plugin -> plugin.metadata().version().toString(), + plugin -> plugin.metadata().contributors().stream() + .map(PluginContributor::name) + .collect(Collectors.joining(", ")), + plugin -> plugin.metadata().description().orElse(null) + ); + } + + @Override + public PlayerPingProvider createPlayerPingProvider() { + if (this.game.isServerAvailable()) { + return new SpongePlayerPingProvider(this.game.server()); + } else { + return null; + } + } + + @Override + public WorldInfoProvider createWorldInfoProvider() { + if (this.game.isServerAvailable()) { + return new SpongeWorldInfoProvider(this.game.server()); + } else { + return WorldInfoProvider.NO_OP; + } + } + + @Override + public PlatformInfo getPlatformInfo() { + return new SpongePlatformInfo(this.game); + } + + private static final class SparkCommand implements Command.Raw { + private final SpongeSparkPlugin plugin; + + public SparkCommand(SpongeSparkPlugin plugin) { + this.plugin = plugin; + } + + @Override + public CommandResult process(CommandCause cause, ArgumentReader.Mutable arguments) { + this.plugin.platform.executeCommand(new SpongeCommandSender(cause), arguments.input().split(" ")); + return CommandResult.success(); + } + + @Override + public List<CommandCompletion> complete(CommandCause cause, ArgumentReader.Mutable arguments) { + return this.plugin.platform.tabCompleteCommand(new SpongeCommandSender(cause), arguments.input().split(" ")) + .stream() + .map(CommandCompletion::of) + .collect(Collectors.toList()); + } + + @Override + public boolean canExecute(CommandCause cause) { + return this.plugin.platform.hasPermissionForAnyCommand(new SpongeCommandSender(cause)); + } + + @Override + public Optional<Component> shortDescription(CommandCause cause) { + return Optional.of(Component.text("Main spark plugin command")); + } + + @Override + public Optional<Component> extendedDescription(CommandCause cause) { + return Optional.empty(); + } + + @Override + public Component usage(CommandCause cause) { + return Component.text("Run '/spark' to view usage."); + } + + @Override + public CommandTreeNode.Root commandTree() { + return Command.Raw.super.commandTree(); + } + + @Override + public Optional<Component> help(@NonNull CommandCause cause) { + return Optional.of(Component.text("Run '/spark' to view usage.")); + } + } +} diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeTickHook.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeTickHook.java new file mode 100644 index 0000000..71b4541 --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeTickHook.java @@ -0,0 +1,61 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.sponge; + +import me.lucko.spark.common.tick.AbstractTickHook; +import me.lucko.spark.common.tick.TickHook; +import org.spongepowered.api.Game; +import org.spongepowered.api.scheduler.ScheduledTask; +import org.spongepowered.api.scheduler.Task; +import org.spongepowered.api.util.Ticks; +import org.spongepowered.plugin.PluginContainer; + +public class SpongeTickHook extends AbstractTickHook implements TickHook, Runnable { + private final PluginContainer plugin; + private final Game game; + private ScheduledTask task; + + public SpongeTickHook(PluginContainer plugin, Game game) { + this.plugin = plugin; + this.game = game; + } + + @Override + public void run() { + onTick(); + } + + @Override + public void start() { + Task task = Task.builder() + .interval(Ticks.of(1)) + .plugin(this.plugin) + .execute(this) + .build(); + this.task = this.game.server().scheduler().submit(task); + } + + @Override + public void close() { + this.task.cancel(); + } + +} diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeWorldInfoProvider.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeWorldInfoProvider.java new file mode 100644 index 0000000..15f15f8 --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeWorldInfoProvider.java @@ -0,0 +1,134 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.sponge; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +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 net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.spongepowered.api.Server; +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.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class SpongeWorldInfoProvider implements WorldInfoProvider { + private final Server server; + + public SpongeWorldInfoProvider(Server server) { + this.server = server; + } + + @Override + public CountsResult pollCounts() { + int players = this.server.onlinePlayers().size(); + int entities = 0; + int tileEntities = 0; + int chunks = 0; + + for (ServerWorld world : this.server.worldManager().worlds()) { + entities += world.entities().size(); + tileEntities += world.blockEntities().size(); + chunks += Iterables.size(world.loadedChunks()); + } + + return new CountsResult(players, entities, tileEntities, chunks); + } + + @Override + public ChunksResult<Sponge7ChunkInfo> pollChunks() { + ChunksResult<Sponge7ChunkInfo> data = new ChunksResult<>(); + + for (ServerWorld world : this.server.worldManager().worlds()) { + List<WorldChunk> chunks = Lists.newArrayList(world.loadedChunks()); + + List<Sponge7ChunkInfo> list = new ArrayList<>(chunks.size()); + for (WorldChunk chunk : chunks) { + list.add(new Sponge7ChunkInfo(chunk)); + } + + data.put(world.key().value(), list); + } + + 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; + } + + @Override + public Collection<DataPackInfo> pollDataPacks() { + return this.server.packRepository().enabled().stream() + .map(pack -> new DataPackInfo( + pack.id(), + PlainTextComponentSerializer.plainText().serialize(pack.description()), + "unknown" + )) + .collect(Collectors.toList()); + } + + static final class Sponge7ChunkInfo extends AbstractChunkInfo<EntityType<?>> { + private final CountMap<EntityType<?>> entityCounts; + + Sponge7ChunkInfo(WorldChunk chunk) { + super(chunk.chunkPosition().x(), chunk.chunkPosition().z()); + + this.entityCounts = new CountMap.Simple<>(new HashMap<>()); + for (Entity entity : chunk.entities()) { + this.entityCounts.increment(entity.type()); + } + } + + @Override + public CountMap<EntityType<?>> getEntityCounts() { + return this.entityCounts; + } + + @Override + public String entityTypeName(EntityType<?> type) { + return EntityTypes.registry().valueKey(type).value(); + } + + } +} |