aboutsummaryrefslogtreecommitdiff
path: root/spark-sponge/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'spark-sponge/src/main/java')
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeClassSourceLookup.java82
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeCommandSender.java94
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlatformInfo.java60
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongePlayerPingProvider.java45
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeSparkPlugin.java267
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeTickHook.java61
-rw-r--r--spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeWorldInfoProvider.java134
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();
+ }
+
+ }
+}