Sponge API 8 port (#2119)

* Initial commit for Sponge API 8 port
* Change Sponge PAL version to include -sponge version suffix
* Comment out Nucleus w/ needs a update in ExtensionRegister
* Fix error with ArtifactVersion (from org.apache.maven) being relocated
* Correct todos related to getting values from ResourceKeys
* Cleanup sponge buildscript a bit
* Remove sout
* Update Extension-Sponge-Economy to 8.0.0-R0.3
* Convert to Sponge's new preferred way to load in jar resources
* Convert SpongeMessageBuilder and VelocityMessageBuilder into AdventureMessageBuilder
* Add back the command
* Update bStats to 3.0.0, re-enable bStats for Sponge
* Implement gamemode change mixin
* Remove unused import
* Update SpongeGMChangeListener's error logging as suggested

Co-authored-by: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com>

* PAL 5.1.0
* Add a check in SpongeCommand to update the ErrorLogger and Subcommand if the PlanSpongeComponent changes
* Combine try-with-resources in SpongePlanFiles#asStringResource

Co-authored-by: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com>
This commit is contained in:
Henri Schubin 2022-05-29 17:08:50 +03:00 committed by GitHub
parent 5066806752
commit bab0f273a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 609 additions and 536 deletions

View File

@ -62,12 +62,12 @@ subprojects {
ext {
daggerVersion = "2.42"
palVersion = "5.0.3"
palVersion = "5.1.0"
bukkitVersion = "1.13.2-R0.1-SNAPSHOT"
spigotVersion = "1.13.2-R0.1-SNAPSHOT"
paperVersion = "1.13.2-R0.1-SNAPSHOT"
spongeVersion = "7.4.0"
spongeVersion = "8.1.0"
nukkitVersion = "1.0-SNAPSHOT"
bungeeVersion = "1.16-R0.4"
velocityVersion = "3.0.0-SNAPSHOT"
@ -84,7 +84,7 @@ subprojects {
gsonVersion = "2.9.0"
dependencyDownloadVersion = "1.2.1"
bstatsVersion = "2.2.1"
bstatsVersion = "3.0.0"
placeholderapiVersion = "2.11.1"
nkPlaceholderapiVersion = "1.4-SNAPSHOT"

View File

@ -42,6 +42,7 @@ dependencies {
implementation "org.slf4j:slf4j-api:$slf4jVersion"
implementation "com.maxmind.geoip2:geoip2:$geoIpVersion"
implementation "com.google.code.gson:gson:$gsonVersion"
compileOnly "net.kyori:adventure-api:4.9.3"
implementation("dev.vankka:dependencydownload-runtime:$dependencyDownloadVersion") {
// Effectively disables relocating

View File

@ -150,8 +150,18 @@ public class PlanSystem implements SubSystem {
return webServerSystem.getAddresses().getMainAddress().orElse(webServerSystem.getAddresses().getFallbackLocalhostAddress());
}
@Override
public void enable() {
/**
* Enables only the systems that are required for {@link com.djrapitops.plan.commands.PlanCommand}.
* @see #enableOtherThanCommands()
*/
public void enableForCommands() {
enableSystems(configSystem);
}
/**
* Enables the rest of the systems that are not enabled in {@link #enableForCommands()}.
*/
public void enableOtherThanCommands() {
extensionService.register();
resolverService.register();
resourceService.register();
@ -162,7 +172,6 @@ public class PlanSystem implements SubSystem {
enableSystems(
files,
configSystem,
localeSystem,
versionChecker,
databaseSystem,
@ -196,6 +205,12 @@ public class PlanSystem implements SubSystem {
}
}
@Override
public void enable() {
enableForCommands();
enableOtherThanCommands();
}
private void enableSystems(SubSystem... systems) {
for (SubSystem system : systems) {
system.enable();
@ -315,4 +330,4 @@ public class PlanSystem implements SubSystem {
public static long getServerEnableTime() {
return SERVER_ENABLE_TIME;
}
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.commands.use;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
@ -24,16 +25,21 @@ import org.apache.commons.text.TextStringBuilder;
import java.util.Collection;
public class VelocityMessageBuilder implements MessageBuilder {
/**
* Shared Adventure {@link MessageBuilder} shared by Velocity and Sponge.
*/
public class AdventureMessageBuilder implements MessageBuilder {
private final VelocityCMDSender sender;
private final CMDSender sender;
private final Audience audience;
private final TextComponent.Builder builder;
// Store reference to previous component to properly add hover & click events
private Component previousComponent;
public VelocityMessageBuilder(VelocityCMDSender sender) {
AdventureMessageBuilder(CMDSender sender, Audience audience) {
this.sender = sender;
this.audience = audience;
builder = Component.text();
}
@ -112,6 +118,6 @@ public class VelocityMessageBuilder implements MessageBuilder {
if (previousComponent != null) {
builder.append(previousComponent);
}
sender.commandSource.sendMessage(builder.build());
audience.sendMessage(builder.build());
}
}

View File

@ -40,7 +40,7 @@ dependencies {
implementation 'com.djrapitops:Extension-Quests:4.0.5-R0.1'
implementation 'com.djrapitops:Extension-React:6.651-R0.1'
implementation 'com.djrapitops:Extension-RedProtect:7.7.3-R0.1'
implementation 'com.djrapitops:Extension-Sponge-Economy:7.1.0-R0.3'
implementation 'com.djrapitops:Extension-Sponge-Economy:8.0.0-R0.3'
implementation 'com.djrapitops:Extension-SuperbVote:0.5.4-R0.1'
implementation 'com.djrapitops:Extension-Tebex:R1.3'
implementation 'com.djrapitops:Extension-Towny:0.96.7.4-R0.2'
@ -52,4 +52,4 @@ shadowJar {
dependencies {
exclude(project(':api'))
}
}
}

View File

@ -75,7 +75,7 @@ public class ExtensionRegister {
register(new FloodgateExtensionFactory(), FloodgateExtensionFactory::createExtension, FloodgateExtensionFactory::registerListener);
register(new GriefDefenderExtensionFactory(), GriefDefenderExtensionFactory::createExtension);
register(new GriefPreventionExtensionFactory(), GriefPreventionExtensionFactory::createExtension);
register(new GriefPreventionSpongeExtensionFactory(), GriefPreventionSpongeExtensionFactory::createExtension);
//register(new GriefPreventionSpongeExtensionFactory(), GriefPreventionSpongeExtensionFactory::createExtension); // TODO: update for Sponge API 8
register(new HeroesExtensionFactory(), HeroesExtensionFactory::createExtension);
register(new KingdomsXExtensionFactory(), KingdomsXExtensionFactory::createExtension);
register(new JobsExtensionFactory(), JobsExtensionFactory::createExtension);
@ -88,7 +88,7 @@ public class ExtensionRegister {
register(new McMMOExtensionFactory(), McMMOExtensionFactory::createExtension, McMMOExtensionFactory::registerExpansion);
registerMany(new MinigameLibExtensionFactory(), MinigameLibExtensionFactory::createExtensions);
register(new MyPetExtensionFactory(), MyPetExtensionFactory::createExtension);
register(new NucleusExtensionFactory(), NucleusExtensionFactory::createExtension);
//register(new NucleusExtensionFactory(), NucleusExtensionFactory::createExtension); // TODO: update for Sponge API 8
register(new NuVotifierExtensionFactory(), NuVotifierExtensionFactory::createExtension);
register(new PlaceholderAPIExtensionFactory(), PlaceholderAPIExtensionFactory::createExtension);
register(new PlotSquaredExtensionFactory(), PlotSquaredExtensionFactory::createExtension);
@ -200,4 +200,4 @@ public class ExtensionRegister {
return extensionService.register(dataExtension);
}
}
}

View File

@ -13,9 +13,17 @@ dependencies {
testImplementation project(path: ":velocity", configuration: 'testArtifacts')
}
jar {
// Add the sponge mixin into the manifest
manifest.attributes([
'MixinConfigs': 'plan-sponge.mixins.json'
])
}
shadowJar {
relocate('org.apache', 'plan.org.apache') {
exclude 'org/apache/logging/**'
exclude 'org/apache/maven/**' // This needs to be unrelocated for Sponge
}
relocate 'dagger', 'plan.dagger'
// Don't relocate MySQL or SQLite since they are loaded with a isolated class loader

View File

@ -1,10 +1,9 @@
plugins {
id "net.kyori.blossom" version "1.3.0"
}
import org.spongepowered.gradle.plugin.config.PluginLoaders
import org.spongepowered.gradle.vanilla.repository.MinecraftPlatform
blossom {
replaceTokenIn('src/main/java/com/djrapitops/plan/PlanSponge.java')
replaceToken('@version@', '' + project.ext.fullVersion)
plugins {
id "org.spongepowered.gradle.plugin" version "2.0.0"
id "org.spongepowered.gradle.vanilla" version "0.2"
}
dependencies {
@ -15,14 +14,63 @@ dependencies {
implementation "net.playeranalytics:platform-abstraction-layer-sponge:$palVersion"
implementation "org.bstats:bstats-sponge:$bstatsVersion"
compileOnly "org.spongepowered:spongeapi:$spongeVersion"
testImplementation "org.spongepowered:spongeapi:$spongeVersion"
annotationProcessor "org.spongepowered:spongeapi:$spongeVersion"
implementation "org.spongepowered:mixin:0.7.11-SNAPSHOT"
testImplementation project(path: ":common", configuration: 'testArtifacts')
}
sponge {
apiVersion(spongeVersion)
license("LGPLv3")
loader {
name(PluginLoaders.JAVA_PLAIN)
version("1.0")
}
plugin("plan") {
displayName("Plan")
version(fullVersion)
entrypoint("com.djrapitops.plan.PlanSponge")
description("Player Analytics Plugin by AuroraLS3")
contributor("AuroraLS3") {
description("Author")
}
// Dependencies
// version is a *required* property (empty string works fine)
dependency("griefprevention") {
optional(true)
version("")
}
dependency("luckperms") {
optional(true)
version("")
}
dependency("nucleus") {
optional(true)
version("")
}
dependency("redprotect") {
optional(true)
version("")
}
dependency("nuvotifier") {
optional(true)
version("")
}
}
}
minecraft {
version("1.16.5")
platform(MinecraftPlatform.JOINED)
}
compileJava {
options.release = 11
}
shadowJar {
relocate 'org.bstats', 'com.djrapitops.plan.utilities.metrics'
}
relocate "org.bstats", "com.djrapitops.plan.utilities.metrics"
}

View File

@ -29,24 +29,24 @@ import net.playeranalytics.plugin.SpongePlatformLayer;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
import net.playeranalytics.plugin.server.PluginLogger;
import org.bstats.sponge.Metrics;
import org.slf4j.Logger;
import org.spongepowered.api.Game;
import org.spongepowered.api.Server;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandManager;
import org.spongepowered.api.command.CommandMapping;
import org.spongepowered.api.command.Command;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GamePreInitializationEvent;
import org.spongepowered.api.event.game.state.GameStartedServerEvent;
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
import org.spongepowered.api.plugin.Dependency;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.event.lifecycle.ConstructPluginEvent;
import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
import org.spongepowered.api.event.lifecycle.StartingEngineEvent;
import org.spongepowered.api.event.lifecycle.StoppingEngineEvent;
import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.builtin.jvm.Plugin;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@ -54,26 +54,14 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
@Plugin(
id = "plan",
name = "Plan",
version = "@version@",
description = "Player Analytics Plugin by AuroraLS3",
authors = {"AuroraLS3"},
dependencies = {
@Dependency(id = "griefprevention", optional = true),
@Dependency(id = "luckperms", optional = true),
@Dependency(id = "nucleus", optional = true),
@Dependency(id = "redprotect", optional = true),
@Dependency(id = "nuvotifier", optional = true)
}
)
@Plugin("plan")
public class PlanSponge implements PlanPlugin {
private final Metrics metrics;
private final Logger slf4jLogger;
private final PluginContainer plugin;
private final File dataFolder;
private PlanSpongeComponent component;
private PlanSystem system;
private Locale locale;
private ServerShutdownSave serverShutdownSave;
@ -83,57 +71,93 @@ public class PlanSponge implements PlanPlugin {
@com.google.inject.Inject
public PlanSponge(
Logger slf4jLogger,
@ConfigDir(sharedRoot = false) File dataFolder,
@ConfigDir(sharedRoot = false) Path dataFolder,
PluginContainer plugin,
Metrics.Factory metrics
) {
this.slf4jLogger = slf4jLogger;
this.dataFolder = dataFolder;
this.dataFolder = dataFolder.toFile();
this.plugin = plugin;
int pluginId = 3086;
this.metrics = metrics.make(pluginId);
}
private final Map<String, CommandMapping> commands = new HashMap<>();
@Listener
public void onServerLoad(GamePreInitializationEvent event) {
public void onServerLoad(ConstructPluginEvent event) {
onLoad();
}
@Listener
public void onServerStart(GameStartedServerEvent event) {
public void onServerStart(StartingEngineEvent<Server> event) {
onEnable();
}
@Listener
public void onServerStop(GameStoppingServerEvent event) {
public void onServerStop(StoppingEngineEvent<Server> event) {
onDisable();
}
private void onLoad() {
abstractionLayer = new SpongePlatformLayer(this, dataFolder, slf4jLogger);
abstractionLayer = new SpongePlatformLayer(plugin, dataFolder);
logger = abstractionLayer.getPluginLogger();
runnableFactory = abstractionLayer.getRunnableFactory();
catchStartupErrors(() -> {
component = makeComponent();
system = component.system();
system.enableForCommands();
});
}
public void onEnable() {
PlanSpongeComponent component = DaggerPlanSpongeComponent.builder()
.plan(this)
.abstractionLayer(abstractionLayer)
.game(Sponge.getGame())
.build();
try {
system = component.system();
catchStartupErrors(() -> {
boolean firstBoot = component != null;
if (firstBoot && system == null) {
// Already failed to load. Prevent throwing any more errors than necessary
return;
}
if (!firstBoot) {
// Reinitialize component & system
component = makeComponent();
system = component.system();
}
serverShutdownSave = component.serverShutdownSave();
locale = system.getLocaleSystem().getLocale();
system.enable();
if (firstBoot) {
// First boot, only enable what isn't already enabled
system.enableOtherThanCommands();
} else {
// Not first boot, enable everything normally
system.enable();
}
new BStatsSponge(
metrics, system.getDatabaseSystem().getDatabase()
).registerMetrics();
logger.info(locale.getString(PluginLang.ENABLED));
});
// Registering command is done through onRegisterCommand
if (system != null) {
system.getProcessing().submitNonCritical(() -> system.getListenerSystem().callEnableEvent(this));
}
}
private PlanSpongeComponent makeComponent() {
return DaggerPlanSpongeComponent.builder()
.plan(this)
.abstractionLayer(abstractionLayer)
.game(Sponge.game())
.build();
}
private void catchStartupErrors(Runnable task) {
try {
task.run();
} catch (AbstractMethodError e) {
logger.error("Plugin ran into AbstractMethodError - Server restart is required. Likely cause is updating the jar without a restart.");
} catch (EnableException e) {
@ -149,13 +173,10 @@ public class PlanSponge implements PlanPlugin {
logger.error("This error should be reported at https://github.com/plan-player-analytics/Plan/issues");
onDisable();
}
registerCommand(component.planCommand().build());
if (system != null) {
system.getProcessing().submitNonCritical(() -> system.getListenerSystem().callEnableEvent(this));
}
}
public void onDisable() {
component = null;
storeSessionsOnShutdown();
cancelAllTasks();
if (system != null) system.disable();
@ -182,9 +203,6 @@ public class PlanSponge implements PlanPlugin {
public void cancelAllTasks() {
runnableFactory.cancelAllKnownTasks();
for (Task task : Sponge.getScheduler().getScheduledTasks(this)) {
task.cancel();
}
}
@Override
@ -199,21 +217,23 @@ public class PlanSponge implements PlanPlugin {
@Override
public void registerCommand(Subcommand command) {
if (command == null) {
logger.warn("Attempted to register a null command!");
return;
}
for (String name : command.getAliases()) {
CommandManager commandManager = Sponge.getCommandManager();
// NOOP: Sponge commands are registered via the event below
}
CommandMapping registered = commands.get(name);
if (registered != null) {
commandManager.removeMapping(registered);
}
Optional<CommandMapping> register = commandManager.register(this, new SpongeCommand(runnableFactory, system.getErrorLogger(), command), name);
register.ifPresent(commandMapping -> commands.put(name, commandMapping));
}
@Listener
public void onRegisterCommand(RegisterCommandEvent<Command.Raw> event) {
Subcommand command = component.planCommand().build();
List<String> aliases = new ArrayList<>(command.getAliases());
event.register(
plugin,
new SpongeCommand(
runnableFactory,
() -> component,
command
),
aliases.remove(0),
aliases.toArray(new String[0])
);
}
@Override
@ -222,7 +242,11 @@ public class PlanSponge implements PlanPlugin {
}
public Game getGame() {
return Sponge.getGame();
return Sponge.game();
}
public PluginContainer getPlugin() {
return plugin;
}
@Override

View File

@ -68,4 +68,4 @@ public interface PlanSpongeComponent {
PlanSpongeComponent build();
}
}
}

View File

@ -21,10 +21,10 @@ import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import net.playeranalytics.plugin.server.PluginLogger;
import org.spongepowered.api.GameState;
import org.spongepowered.api.Server;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
import org.spongepowered.api.event.lifecycle.StoppingEngineEvent;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -55,11 +55,7 @@ public class SpongeServerShutdownSave extends ServerShutdownSave {
}
@Listener(order = Order.PRE)
public void onServerShutdown(GameStoppingServerEvent event) {
GameState state = event.getState();
shuttingDown = state == GameState.SERVER_STOPPING
|| state == GameState.GAME_STOPPING
|| state == GameState.SERVER_STOPPED
|| state == GameState.GAME_STOPPED;
public void onServerShutdown(StoppingEngineEvent<Server> event) {
shuttingDown = true;
}
}
}

View File

@ -17,8 +17,8 @@
package com.djrapitops.plan.api.events;
import com.djrapitops.plan.PlanSponge;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.event.cause.EventContext;
import org.spongepowered.api.event.Cause;
import org.spongepowered.api.event.EventContext;
import org.spongepowered.api.event.impl.AbstractEvent;
/**
@ -50,17 +50,17 @@ public class PlanSpongeEnableEvent extends AbstractEvent {
}
@Override
public Cause getCause() {
public Cause cause() {
return Cause.builder().append(plugin.getSystem()).build(EventContext.empty());
}
@Override
public Object getSource() {
public Object source() {
return plugin.getSystem();
}
@Override
public EventContext getContext() {
public EventContext context() {
return EventContext.empty();
}
}

View File

@ -16,8 +16,10 @@
*/
package com.djrapitops.plan.commands.use;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.text.Text;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.spongepowered.api.service.permission.Subject;
import java.util.Objects;
import java.util.Optional;
@ -25,15 +27,21 @@ import java.util.UUID;
public class SpongeCMDSender implements CMDSender {
final CommandSource source;
final Subject subject;
final Audience audience;
public SpongeCMDSender(CommandSource source) {
this.source = source;
public <T extends Subject & Audience> SpongeCMDSender(T source) {
this(source, source);
}
public SpongeCMDSender(Subject subject, Audience audience) {
this.subject = subject;
this.audience = audience;
}
@Override
public MessageBuilder buildMessage() {
return new SpongeMessageBuilder(this);
return new AdventureMessageBuilder(this, audience);
}
@Override
@ -43,7 +51,7 @@ public class SpongeCMDSender implements CMDSender {
@Override
public boolean hasPermission(String permission) {
return source.hasPermission(permission);
return subject.hasPermission(permission);
}
@Override
@ -53,7 +61,7 @@ public class SpongeCMDSender implements CMDSender {
@Override
public void send(String text) {
source.sendMessage(Text.of(text));
audience.sendMessage(Identity.nil(), LegacyComponentSerializer.legacySection().deserialize(text));
}
@Override
@ -66,11 +74,11 @@ public class SpongeCMDSender implements CMDSender {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SpongeCMDSender that = (SpongeCMDSender) o;
return source.equals(that.source);
return subject.identifier().equals(that.subject.identifier());
}
@Override
public int hashCode() {
return Objects.hash(source);
return Objects.hash(subject.identifier());
}
}

View File

@ -16,46 +16,85 @@
*/
package com.djrapitops.plan.commands.use;
import com.djrapitops.plan.PlanSpongeComponent;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
import org.spongepowered.api.command.CommandCallable;
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.CommandSource;
import org.spongepowered.api.command.source.RconSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;
import org.spongepowered.api.command.parameter.ArgumentReader;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.network.RconConnection;
import org.spongepowered.api.service.permission.Subject;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class SpongeCommand implements CommandCallable {
public class SpongeCommand implements Command.Raw {
private final RunnableFactory runnableFactory;
private final ErrorLogger errorLogger;
private final Subcommand command;
private final Supplier<PlanSpongeComponent> componentSupplier;
private PlanSpongeComponent commandComponent;
private ErrorLogger errorLogger;
private Subcommand subcommand;
public SpongeCommand(
RunnableFactory runnableFactory,
ErrorLogger errorLogger, Subcommand command
Supplier<PlanSpongeComponent> componentSupplier,
Subcommand initialCommand
) {
this.runnableFactory = runnableFactory;
this.errorLogger = errorLogger;
this.command = command;
this.componentSupplier = componentSupplier;
this.commandComponent = componentSupplier.get();
this.errorLogger = commandComponent.system().getErrorLogger();
this.subcommand = initialCommand;
}
private synchronized Subcommand getCommand() {
PlanSpongeComponent component = componentSupplier.get();
// Check if the component has changed, if it has, update the command and error logger.
if (commandComponent != component) {
errorLogger = component.system().getErrorLogger();
subcommand = component.planCommand().build();
commandComponent = component;
}
return subcommand;
}
private CMDSender getSender(CommandCause cause) {
return getSender(cause.subject(), cause.audience());
}
private CMDSender getSender(Subject subject, Audience audience) {
if (subject instanceof ServerPlayer || subject instanceof RconConnection) {
return new SpongePlayerCMDSender(subject, audience);
} else {
return new SpongeCMDSender(subject, audience);
}
}
private Component convertLegacy(String legacy) {
return LegacyComponentSerializer.legacySection().deserialize(legacy);
}
@Override
public CommandResult process(CommandSource source, String arguments) {
public CommandResult process(CommandCause cause, ArgumentReader.Mutable arguments) {
runnableFactory.create(() -> {
try {
command.getExecutor().accept(getSender(source), new Arguments(arguments));
getCommand().getExecutor().accept(getSender(cause), new Arguments(arguments.input()));
} catch (Exception e) {
errorLogger.error(e, ErrorContext.builder()
.related(source.getClass())
.related(cause.subject().getClass())
.related(arguments)
.build());
}
@ -63,22 +102,17 @@ public class SpongeCommand implements CommandCallable {
return CommandResult.success();
}
private CMDSender getSender(CommandSource source) {
if (source instanceof Player || source instanceof RconSource) {
return new SpongePlayerCMDSender(source);
} else {
return new SpongeCMDSender(source);
}
}
@Override
public List<String> getSuggestions(CommandSource source, String arguments, @Nullable Location<World> targetPosition) {
public List<CommandCompletion> complete(CommandCause cause, ArgumentReader.Mutable arguments) {
try {
return command.getArgumentResolver()
.apply(getSender(source), new Arguments(arguments));
return getCommand().getArgumentResolver()
.apply(getSender(cause), new Arguments(arguments.input()))
.stream()
.map(CommandCompletion::of)
.collect(Collectors.toList());
} catch (Exception e) {
errorLogger.error(e, ErrorContext.builder()
.related(source.getClass())
.related(cause.subject().getClass())
.related("tab completion")
.related(arguments)
.build());
@ -87,9 +121,9 @@ public class SpongeCommand implements CommandCallable {
}
@Override
public boolean testPermission(CommandSource source) {
for (String requiredPermission : command.getRequiredPermissions()) {
if (!source.hasPermission(requiredPermission)) {
public boolean canExecute(CommandCause cause) {
for (String requiredPermission : getCommand().getRequiredPermissions()) {
if (!cause.subject().hasPermission(requiredPermission)) {
return false;
}
}
@ -97,17 +131,26 @@ public class SpongeCommand implements CommandCallable {
}
@Override
public Optional<Text> getShortDescription(CommandSource source) {
return Optional.ofNullable(command.getDescription()).map(Text::of);
public Optional<Component> shortDescription(CommandCause cause) {
return Optional.ofNullable(getCommand().getDescription()).map(this::convertLegacy);
}
@Override
public Optional<Text> getHelp(CommandSource source) {
return Optional.ofNullable(command.getDescription()).map(Text::of);
public Optional<Component> extendedDescription(CommandCause cause) {
return Optional.ofNullable(getCommand().getDescription()).map(this::convertLegacy);
}
@Override
public Text getUsage(CommandSource source) {
return Text.of(command.getInDepthDescription());
public Component usage(CommandCause cause) {
TextComponent.Builder builder = Component.text();
List<String> lines = getCommand().getInDepthDescription();
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
builder.append(convertLegacy(line));
if (i < lines.size() - 1) {
builder.append(Component.newline());
}
}
return builder.build();
}
}

View File

@ -1,113 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.commands.use;
import org.apache.commons.text.TextStringBuilder;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.action.TextActions;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
public class SpongeMessageBuilder implements MessageBuilder {
private final SpongeCMDSender sender;
private final Text.Builder builder;
private final SpongeMessageBuilder previous;
public SpongeMessageBuilder(SpongeCMDSender sender) {
this(sender, null);
}
SpongeMessageBuilder(SpongeCMDSender sender, SpongeMessageBuilder previous) {
this.sender = sender;
this.builder = Text.builder();
this.previous = previous;
}
@Override
public MessageBuilder addPart(String s) {
SpongeMessageBuilder newBuilder = new SpongeMessageBuilder(sender, this);
newBuilder.builder.append(Text.of(s));
return newBuilder;
}
@Override
public MessageBuilder newLine() {
builder.append(Text.of('\n'));
return this;
}
@Override
public MessageBuilder link(String url) {
try {
builder.onClick(TextActions.openUrl(new URL(url)));
} catch (MalformedURLException e) {
throw new IllegalArgumentException("'" + url + "' is not a valid URL");
}
return this;
}
@Override
public MessageBuilder command(String command) {
builder.onClick(TextActions.runCommand(command.charAt(0) == '/' ? command : '/' + command));
return this;
}
@Override
public MessageBuilder hover(String message) {
builder.onHover(TextActions.showText(Text.of(message)));
return this;
}
@Override
public MessageBuilder hover(String... lines) {
builder.onHover(TextActions.showText(Text.of(new TextStringBuilder().appendWithSeparators(lines, "\n"))));
return this;
}
@Override
public MessageBuilder hover(Collection<String> lines) {
builder.onHover(TextActions.showText(Text.of(new TextStringBuilder().appendWithSeparators(lines, "\n"))));
return this;
}
@Override
public MessageBuilder indent(int amount) {
for (int i = 0; i < amount; i++) {
builder.append(Text.of(' '));
}
return this;
}
@Override
public MessageBuilder tabular(CharSequence charSequence) {
addPart(sender.getFormatter().table(charSequence.toString(), ":"));
return this;
}
@Override
public void send() {
if (previous == null) {
sender.source.sendMessage(builder.build());
} else {
previous.builder.append(builder.build());
previous.send();
}
}
}

View File

@ -16,27 +16,32 @@
*/
package com.djrapitops.plan.commands.use;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import net.kyori.adventure.audience.Audience;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.service.permission.Subject;
import java.util.Optional;
import java.util.UUID;
public class SpongePlayerCMDSender extends SpongeCMDSender {
public SpongePlayerCMDSender(CommandSource source) {
super(source);
public <T extends Subject & Audience> SpongePlayerCMDSender(T source) {
this(source, source);
}
public SpongePlayerCMDSender(Subject subject, Audience audience) {
super(subject, audience);
}
@Override
public Optional<String> getPlayerName() {
return source.getFriendlyIdentifier();
return subject.friendlyIdentifier();
}
@Override
public Optional<UUID> getUUID() {
if (source instanceof Player) {
return Optional.of(((Player) source).getUniqueId());
if (subject instanceof ServerPlayer) {
return Optional.of(((ServerPlayer) subject).uniqueId());
}
return Optional.empty();
}

View File

@ -17,15 +17,15 @@
package com.djrapitops.plan.gathering;
import org.spongepowered.api.Game;
import org.spongepowered.api.world.Chunk;
import org.spongepowered.api.world.World;
import org.spongepowered.api.world.chunk.WorldChunk;
import org.spongepowered.api.world.server.ServerWorld;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Iterator;
@Singleton
public class SpongeSensor implements ServerSensor<World> {
public class SpongeSensor implements ServerSensor<ServerWorld> {
private final Game game;
@ -41,26 +41,26 @@ public class SpongeSensor implements ServerSensor<World> {
@Override
public int getOnlinePlayerCount() {
return game.getServer().getOnlinePlayers().size();
return game.server().onlinePlayers().size();
}
@Override
public double getTPS() {
return game.getServer().getTicksPerSecond();
return game.server().ticksPerSecond();
}
@Override
public Iterable<World> getWorlds() {
return game.getServer().getWorlds();
public Iterable<ServerWorld> getWorlds() {
return game.server().worldManager().worlds();
}
@Override
public int getChunkCount(World world) {
public int getChunkCount(ServerWorld world) {
return -1;
}
private int getLaggyChunkCount(World world) {
Iterator<Chunk> chunks = world.getLoadedChunks().iterator();
private int getLaggyChunkCount(ServerWorld world) {
Iterator<WorldChunk> chunks = world.loadedChunks().iterator();
int count = 0;
while (chunks.hasNext()) {
chunks.next();
@ -70,7 +70,7 @@ public class SpongeSensor implements ServerSensor<World> {
}
@Override
public int getEntityCount(World world) {
return world.getEntities().size();
public int getEntityCount(ServerWorld world) {
return world.entities().size();
}
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.SpongeServerShutdownSave;
import com.djrapitops.plan.api.events.PlanSpongeEnableEvent;
import com.djrapitops.plan.capability.CapabilitySvc;
import com.djrapitops.plan.gathering.listeners.sponge.*;
import com.djrapitops.plan.gathering.listeners.sponge.SpongeGMChangeListener;
import net.playeranalytics.plugin.server.Listeners;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.event.Event;
@ -82,7 +83,7 @@ public class SpongeListenerSystem extends ListenerSystem {
public void callEnableEvent(PlanPlugin plugin) {
try {
Event event = new PlanSpongeEnableEvent((PlanSponge) plugin);
Sponge.getEventManager().post(event);
Sponge.eventManager().post(event);
} catch (IllegalStateException ignore) {
/* Ignore, Sponge is not initialized */
}

View File

@ -39,16 +39,18 @@ import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.transactions.events.*;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.data.key.Keys;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.entity.living.player.gamemode.GameMode;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.entity.living.humanoid.player.KickPlayerEvent;
import org.spongepowered.api.event.network.ClientConnectionEvent;
import org.spongepowered.api.event.entity.living.player.KickPlayerEvent;
import org.spongepowered.api.event.network.ServerSideConnectionEvent;
import org.spongepowered.api.profile.GameProfile;
import org.spongepowered.api.service.ProviderRegistration;
import org.spongepowered.api.registry.RegistryTypes;
import org.spongepowered.api.service.ban.Ban;
import org.spongepowered.api.service.ban.BanService;
import javax.inject.Inject;
@ -103,7 +105,7 @@ public class PlayerOnlineListener {
}
@Listener(order = Order.POST)
public void onLogin(ClientConnectionEvent.Login event) {
public void onLogin(ServerSideConnectionEvent.Login event) {
try {
actOnLoginEvent(event);
} catch (Exception e) {
@ -111,9 +113,9 @@ public class PlayerOnlineListener {
}
}
private void actOnLoginEvent(ClientConnectionEvent.Login event) {
GameProfile profile = event.getProfile();
UUID playerUUID = profile.getUniqueId();
private void actOnLoginEvent(ServerSideConnectionEvent.Login event) {
GameProfile profile = event.profile();
UUID playerUUID = profile.uniqueId();
ServerUUID serverUUID = serverInfo.getServerUUID();
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> isBanned(profile)));
}
@ -121,7 +123,7 @@ public class PlayerOnlineListener {
@Listener(order = Order.POST)
public void onKick(KickPlayerEvent event) {
try {
UUID playerUUID = event.getTargetEntity().getUniqueId();
UUID playerUUID = event.player().uniqueId();
if (status.areKicksNotCounted() || SpongeAFKListener.afkTracker.isAfk(playerUUID)) {
return;
}
@ -132,16 +134,13 @@ public class PlayerOnlineListener {
}
private boolean isBanned(GameProfile profile) {
Optional<ProviderRegistration<BanService>> banService = Sponge.getServiceManager().getRegistration(BanService.class);
boolean banned = false;
if (banService.isPresent()) {
banned = banService.get().getProvider().isBanned(profile);
}
return banned;
BanService banService = Sponge.server().serviceProvider().banService();
Optional<Ban.Profile> ban = banService.find(profile).join();
return ban.isPresent();
}
@Listener(order = Order.POST)
public void onJoin(ClientConnectionEvent.Join event) {
public void onJoin(ServerSideConnectionEvent.Join event) {
try {
actOnJoinEvent(event);
} catch (Exception e) {
@ -149,27 +148,28 @@ public class PlayerOnlineListener {
}
}
private void actOnJoinEvent(ClientConnectionEvent.Join event) {
Player player = event.getTargetEntity();
private void actOnJoinEvent(ServerSideConnectionEvent.Join event) {
ServerPlayer player = event.player();
UUID playerUUID = player.getUniqueId();
UUID playerUUID = player.uniqueId();
ServerUUID serverUUID = serverInfo.getServerUUID();
long time = System.currentTimeMillis();
SpongeAFKListener.afkTracker.performedAction(playerUUID, time);
String world = player.getWorld().getName();
Optional<GameMode> gameMode = player.getGameModeData().get(Keys.GAME_MODE);
String gm = gameMode.map(mode -> mode.getName().toUpperCase()).orElse("ADVENTURE");
String world = Sponge.game().server().worldManager().worldDirectory(player.world().key())
.map(path -> path.getFileName().toString()).orElse("Unknown");
GameMode gameMode = player.gameMode().get();
String gm = gameMode.key(RegistryTypes.GAME_MODE).value().toUpperCase();
Database database = dbSystem.getDatabase();
database.executeTransaction(new WorldNameStoreTransaction(serverUUID, world));
InetAddress address = player.getConnection().getAddress().getAddress();
Supplier<String> getHostName = () -> player.getConnection().getVirtualHost().getHostString();
InetAddress address = player.connection().address().getAddress();
Supplier<String> getHostName = () -> player.connection().virtualHost().getHostString();
String playerName = player.getName();
String displayName = player.getDisplayNameData().displayName().get().toPlain();
String playerName = player.name();
String displayName = LegacyComponentSerializer.legacyAmpersand().serialize(player.displayName().get());
database.executeTransaction(new PlayerServerRegisterTransaction(playerUUID, () -> time,
playerName, serverUUID, getHostName))
@ -203,15 +203,15 @@ public class PlayerOnlineListener {
}
@Listener(order = Order.DEFAULT)
public void beforeQuit(ClientConnectionEvent.Disconnect event) {
Player player = event.getTargetEntity();
UUID playerUUID = player.getUniqueId();
String playerName = player.getName();
public void beforeQuit(ServerSideConnectionEvent.Disconnect event) {
Player player = event.player();
UUID playerUUID = player.uniqueId();
String playerName = player.name();
processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_LEAVE));
}
@Listener(order = Order.POST)
public void onQuit(ClientConnectionEvent.Disconnect event) {
public void onQuit(ServerSideConnectionEvent.Disconnect event) {
try {
actOnQuitEvent(event);
} catch (Exception e) {
@ -219,18 +219,18 @@ public class PlayerOnlineListener {
}
}
private void actOnQuitEvent(ClientConnectionEvent.Disconnect event) {
private void actOnQuitEvent(ServerSideConnectionEvent.Disconnect event) {
long time = System.currentTimeMillis();
Player player = event.getTargetEntity();
String playerName = player.getName();
UUID playerUUID = player.getUniqueId();
Player player = event.player();
String playerName = player.name();
UUID playerUUID = player.uniqueId();
ServerUUID serverUUID = serverInfo.getServerUUID();
SpongeAFKListener.afkTracker.loggedOut(playerUUID, time);
nicknameCache.removeDisplayName(playerUUID);
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> isBanned(player.getProfile())));
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> isBanned(player.profile())));
sessionCache.endSession(playerUUID, time)
.ifPresent(endedSession -> dbSystem.getDatabase().executeTransaction(new StoreSessionTransaction(endedSession)));
@ -239,4 +239,4 @@ public class PlayerOnlineListener {
processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName));
}
}
}
}

View File

@ -21,16 +21,16 @@ import com.djrapitops.plan.settings.Permissions;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.event.Event;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.command.SendCommandEvent;
import org.spongepowered.api.event.command.ExecuteCommandEvent;
import org.spongepowered.api.event.entity.MoveEntityEvent;
import org.spongepowered.api.event.entity.living.humanoid.player.PlayerChangeClientSettingsEvent;
import org.spongepowered.api.event.entity.living.humanoid.player.TargetPlayerEvent;
import org.spongepowered.api.event.entity.living.player.PlayerChangeClientSettingsEvent;
import org.spongepowered.api.event.filter.cause.First;
import org.spongepowered.api.event.message.MessageChannelEvent;
import org.spongepowered.api.event.network.ClientConnectionEvent;
import org.spongepowered.api.event.message.PlayerChatEvent;
import org.spongepowered.api.event.network.ServerSideConnectionEvent;
import javax.inject.Inject;
import java.util.Map;
@ -67,26 +67,26 @@ public class SpongeAFKListener {
}
}
private void event(TargetPlayerEvent event) {
private void event(Event event, ServerPlayer player) {
try {
performedAction(event.getTargetEntity());
performedAction(player);
} catch (Exception e) {
errorLogger.error(e, ErrorContext.builder().related(event).build());
}
}
@Listener(order = Order.POST)
public void onMove(MoveEntityEvent event, @First Player player) {
public void onMove(MoveEntityEvent event, @First ServerPlayer player) {
performedAction(player);
}
@Listener(order = Order.POST)
public void onPlayerChat(MessageChannelEvent.Chat event, @First Player player) {
public void onPlayerChat(PlayerChatEvent event, @First ServerPlayer player) {
performedAction(player);
}
private void performedAction(Player player) {
UUID uuid = player.getUniqueId();
private void performedAction(ServerPlayer player) {
UUID uuid = player.uniqueId();
long time = System.currentTimeMillis();
boolean ignored = ignorePermissionInfo.computeIfAbsent(uuid, keyUUID -> player.hasPermission(Permissions.IGNORE_AFK.getPermission()));
@ -102,22 +102,22 @@ public class SpongeAFKListener {
}
@Listener(order = Order.POST)
public void onPlayerCommand(SendCommandEvent event, @First Player player) {
public void onPlayerCommand(ExecuteCommandEvent event, @First ServerPlayer player) {
performedAction(player);
boolean isAfkCommand = event.getCommand().toLowerCase().startsWith("afk");
boolean isAfkCommand = event.command().toLowerCase().startsWith("afk");
if (isAfkCommand) {
afkTracker.usedAfkCommand(player.getUniqueId(), System.currentTimeMillis());
afkTracker.usedAfkCommand(player.uniqueId(), System.currentTimeMillis());
}
}
@Listener(order = Order.POST)
public void onSettingsChange(PlayerChangeClientSettingsEvent event) {
event(event);
event(event, event.player());
}
@Listener(order = Order.POST)
public void onLeave(ClientConnectionEvent.Disconnect event) {
ignorePermissionInfo.remove(event.getTargetEntity().getUniqueId());
public void onLeave(ServerSideConnectionEvent.Disconnect event) {
ignorePermissionInfo.remove(event.player().uniqueId());
}
}
}

View File

@ -23,11 +23,12 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.NicknameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.filter.cause.First;
import org.spongepowered.api.event.message.MessageChannelEvent;
import org.spongepowered.api.event.message.PlayerChatEvent;
import javax.inject.Inject;
import java.util.UUID;
@ -58,11 +59,7 @@ public class SpongeChatListener {
}
@Listener(order = Order.POST)
public void onPlayerChat(MessageChannelEvent.Chat event, @First Player player) {
if (event.isCancelled()) {
return;
}
public void onPlayerChat(PlayerChatEvent event, @First Player player) {
try {
actOnChatEvent(player);
} catch (Exception e) {
@ -72,12 +69,12 @@ public class SpongeChatListener {
private void actOnChatEvent(@First Player player) {
long time = System.currentTimeMillis();
UUID uuid = player.getUniqueId();
String displayName = player.getDisplayNameData().displayName().get().toPlain();
UUID uuid = player.uniqueId();
String displayName = LegacyComponentSerializer.legacySection().serialize(player.displayName().get());
dbSystem.getDatabase().executeTransaction(new NicknameStoreTransaction(
uuid, new Nickname(displayName, time, serverInfo.getServerUUID()),
(playerUUID, name) -> nicknameCache.getDisplayName(playerUUID).map(name::equals).orElse(false)
));
}
}
}

View File

@ -27,27 +27,28 @@ import com.djrapitops.plan.processing.processors.player.MobKillProcessor;
import com.djrapitops.plan.processing.processors.player.PlayerKillProcessor;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import org.spongepowered.api.ResourceKey;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.data.key.Keys;
import org.spongepowered.api.data.type.HandTypes;
import org.spongepowered.api.entity.EnderCrystal;
import org.spongepowered.api.data.value.Value;
import org.spongepowered.api.entity.Entity;
import org.spongepowered.api.entity.explosive.EndCrystal;
import org.spongepowered.api.entity.living.Living;
import org.spongepowered.api.entity.living.animal.Wolf;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.entity.projectile.Projectile;
import org.spongepowered.api.entity.projectile.source.ProjectileSource;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.cause.entity.damage.source.EntityDamageSource;
import org.spongepowered.api.event.entity.DestructEntityEvent;
import org.spongepowered.api.item.ItemType;
import org.spongepowered.api.item.ItemTypes;
import org.spongepowered.api.item.inventory.ItemStack;
import org.spongepowered.api.projectile.source.ProjectileSource;
import org.spongepowered.api.registry.RegistryTypes;
import javax.inject.Inject;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Listener for Deaths and Kills on Sponge.
@ -74,24 +75,24 @@ public class SpongeDeathListener {
@Listener
public void onEntityDeath(DestructEntityEvent.Death event) {
long time = System.currentTimeMillis();
Living dead = event.getTargetEntity();
Living dead = event.entity();
if (dead instanceof Player) {
// Process Death
SessionCache.getCachedSession(dead.getUniqueId()).ifPresent(ActiveSession::addDeath);
SessionCache.getCachedSession(dead.uniqueId()).ifPresent(ActiveSession::addDeath);
}
try {
List<EntityDamageSource> causes = event.getCause().allOf(EntityDamageSource.class);
List<EntityDamageSource> causes = event.cause().allOf(EntityDamageSource.class);
Optional<Player> foundKiller = findKiller(causes, 0);
if (!foundKiller.isPresent()) {
if (foundKiller.isEmpty()) {
return;
}
Player killer = foundKiller.get();
Runnable processor = dead instanceof Player
? new PlayerKillProcessor(getKiller(killer), getVictim((Player) dead), serverInfo.getServerIdentifier(), findWeapon(event), time)
: new MobKillProcessor(killer.getUniqueId());
: new MobKillProcessor(killer.uniqueId());
processing.submitCritical(processor);
} catch (Exception e) {
errorLogger.error(e, ErrorContext.builder().related(event, dead).build());
@ -99,11 +100,11 @@ public class SpongeDeathListener {
}
private PlayerKill.Killer getKiller(Player killer) {
return new PlayerKill.Killer(killer.getUniqueId(), killer.getName());
return new PlayerKill.Killer(killer.uniqueId(), killer.name());
}
private PlayerKill.Victim getVictim(Player victim) {
return new PlayerKill.Victim(victim.getUniqueId(), victim.getName());
return new PlayerKill.Victim(victim.uniqueId(), victim.name());
}
public Optional<Player> findKiller(List<EntityDamageSource> causes, int depth) {
@ -112,40 +113,41 @@ public class SpongeDeathListener {
}
EntityDamageSource damageSource = causes.get(depth);
Entity killerEntity = damageSource.getSource();
Entity killerEntity = damageSource.source();
if (killerEntity instanceof Player) return Optional.of((Player) killerEntity);
if (killerEntity instanceof Wolf) return getOwner((Wolf) killerEntity);
if (killerEntity instanceof Projectile) return getShooter((Projectile) killerEntity);
if (killerEntity instanceof EnderCrystal) return findKiller(causes, depth + 1);
if (killerEntity instanceof EndCrystal) return findKiller(causes, depth + 1);
return Optional.empty();
}
public String findWeapon(DestructEntityEvent.Death death) {
Optional<EntityDamageSource> damagedBy = death.getCause().first(EntityDamageSource.class);
Optional<EntityDamageSource> damagedBy = death.cause().first(EntityDamageSource.class);
if (damagedBy.isPresent()) {
EntityDamageSource damageSource = damagedBy.get();
Entity killerEntity = damageSource.getSource();
Entity killerEntity = damageSource.source();
if (killerEntity instanceof Player) return getItemInHand((Player) killerEntity);
if (killerEntity instanceof Wolf) return "Wolf";
return new EntityNameFormatter().apply(killerEntity.getType().getName());
Optional<ResourceKey> entityType = killerEntity.type().findKey(RegistryTypes.ENTITY_TYPE);
if (entityType.isPresent()) {
return new EntityNameFormatter().apply(entityType.get().value());
}
}
return "Unknown";
}
private String getItemInHand(Player killer) {
Optional<ItemStack> inMainHand = killer.getItemInHand(HandTypes.MAIN_HAND);
ItemStack inHand = inMainHand.orElse(
killer.getItemInHand(HandTypes.OFF_HAND)
.orElse(ItemStack.empty()));
ItemType type = inHand.isEmpty() ? ItemTypes.AIR : inHand.getType();
return new ItemNameFormatter().apply(type.getName());
ItemStack inMainHand = killer.itemInHand(HandTypes.MAIN_HAND);
ItemStack inHand = inMainHand.isEmpty() ? killer.itemInHand(HandTypes.OFF_HAND) : inMainHand;
ItemType type = inHand.isEmpty() ? ItemTypes.AIR.get() : inHand.type();
return new ItemNameFormatter().apply(type.key(RegistryTypes.ITEM_TYPE).value());
}
private Optional<Player> getShooter(Projectile projectile) {
ProjectileSource source = projectile.getShooter();
ProjectileSource source = projectile.shooter().map(Value::get).orElse(null);
if (source instanceof Player) {
return Optional.of((Player) source);
}
@ -154,10 +156,6 @@ public class SpongeDeathListener {
}
private Optional<Player> getOwner(Wolf wolf) {
Optional<Optional<UUID>> isTamed = wolf.get(Keys.TAMED_OWNER);
if (!isTamed.isPresent()) return Optional.empty();
Optional<UUID> owner = isTamed.get();
return owner.flatMap(Sponge.getGame().getServer()::getPlayer);
return wolf.tamer().flatMap(uuid -> Sponge.game().server().player(uuid.get()));
}
}
}

View File

@ -24,14 +24,21 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.entity.living.humanoid.ChangeGameModeEvent;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameType;
import org.spongepowered.api.ResourceKey;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.entity.living.player.gamemode.GameMode;
import org.spongepowered.api.entity.living.player.gamemode.GameModes;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.registry.RegistryTypes;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
/**
* Listener for GameMode change on Sponge.
@ -40,6 +47,8 @@ import java.util.UUID;
*/
public class SpongeGMChangeListener {
public static final List<Consumer<Event>> EVENT_CONSUMERS = new ArrayList<>(); // Available to the mixin
private final WorldAliasSettings worldAliasSettings;
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
@ -56,34 +65,71 @@ public class SpongeGMChangeListener {
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.errorLogger = errorLogger;
EVENT_CONSUMERS.add(this::onMixin);
}
@Listener(order = Order.POST)
public void onGMChange(ChangeGameModeEvent.TargetPlayer event) {
if (event.isCancelled()) {
public static class Event {
private final Player player;
private final GameType gameType;
public Event(Player player, GameType gameType) {
this.player = player;
this.gameType = gameType;
}
}
private void onMixin(Event event) {
ServerPlayer serverPlayer = Sponge.game().server()
.player(event.player.getUUID())
.orElse(null);
if (serverPlayer == null) {
// uh oh
errorLogger.error(
new RuntimeException("GameMode changed for player but no ServerPlayer was found"),
ErrorContext.builder()
.related(event.player, event.player.getGameProfile().getName())
.whatToDo("Report this, the gamemode change mixin might be broken")
.build()
);
return;
}
try {
actOnGMChangeEvent(event);
} catch (Exception e) {
errorLogger.error(e, ErrorContext.builder().related(event, event.getGameMode()).build());
}
GameMode gameMode = GameModes.registry().value(ResourceKey.sponge(event.gameType.getName()));
actOnGMChangeEvent(serverPlayer, gameMode);
}
private void actOnGMChangeEvent(ChangeGameModeEvent.TargetPlayer event) {
Player player = event.getTargetEntity();
UUID uuid = player.getUniqueId();
// This listener can replace the mixin if this pr is merged:
// https://github.com/SpongePowered/Sponge/pull/3563
// @Listener(order = Order.POST)
// public void onGMChange(ChangeDataHolderEvent.ValueChange event) {
// ServerPlayer player = event.targetHolder() instanceof ServerPlayer ? (ServerPlayer) event.targetHolder() : null;
// if (player == null) {
// return;
// }
//
// DataTransactionResult result = event.endResult();
// Optional<Value.Immutable<GameMode>> gameModeValue = result.successfulValue(Keys.GAME_MODE);
// if (gameModeValue.isEmpty()) {
// return;
// }
//
// GameMode newMode = gameModeValue.get().get();
// actOnGMChangeEvent(player, newMode);
// }
private void actOnGMChangeEvent(ServerPlayer player, GameMode gameMode) {
UUID uuid = player.uniqueId();
long time = System.currentTimeMillis();
String gameMode = event.getGameMode().getName().toUpperCase();
String worldName = player.getWorld().getName();
String gameModeText = gameMode.key(RegistryTypes.GAME_MODE).value().toUpperCase();
String worldName = Sponge.game().server().worldManager().worldDirectory(player.world().key())
.map(path -> path.getFileName().toString()).orElse("Unknown");
dbSystem.getDatabase().executeTransaction(new WorldNameStoreTransaction(serverInfo.getServerUUID(), worldName));
worldAliasSettings.addWorld(worldName);
Optional<ActiveSession> cachedSession = SessionCache.getCachedSession(uuid);
cachedSession.ifPresent(session -> session.changeState(worldName, gameMode, time));
cachedSession.ifPresent(session -> session.changeState(worldName, gameModeText, time));
}
}
}

View File

@ -24,13 +24,14 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import org.spongepowered.api.data.key.Keys;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.entity.living.player.gamemode.GameMode;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.entity.MoveEntityEvent;
import org.spongepowered.api.event.entity.ChangeEntityWorldEvent;
import org.spongepowered.api.event.filter.cause.First;
import org.spongepowered.api.registry.RegistryTypes;
import javax.inject.Inject;
import java.util.Optional;
@ -62,11 +63,7 @@ public class SpongeWorldChangeListener {
}
@Listener(order = Order.POST)
public void onWorldChange(MoveEntityEvent.Teleport event, @First Player player) {
if (event.isCancelled()) {
return;
}
public void onWorldChange(ChangeEntityWorldEvent event, @First ServerPlayer player) {
try {
actOnEvent(event, player);
} catch (Exception e) {
@ -74,12 +71,13 @@ public class SpongeWorldChangeListener {
}
}
private void actOnEvent(MoveEntityEvent.Teleport event, Player player) {
private void actOnEvent(ChangeEntityWorldEvent event, ServerPlayer player) {
long time = System.currentTimeMillis();
UUID uuid = player.getUniqueId();
UUID uuid = player.uniqueId();
String worldName = event.getToTransform().getExtent().getName();
String worldName = Sponge.game().server().worldManager().worldDirectory(event.destinationWorld().key())
.map(path -> path.getFileName().toString()).orElse("Unknown");
String gameMode = getGameMode(player);
dbSystem.getDatabase().executeTransaction(new WorldNameStoreTransaction(serverInfo.getServerUUID(), worldName));
@ -89,9 +87,9 @@ public class SpongeWorldChangeListener {
cachedSession.ifPresent(session -> session.changeState(worldName, gameMode, time));
}
private String getGameMode(Player player) {
Optional<GameMode> gameMode = player.getGameModeData().get(Keys.GAME_MODE);
return gameMode.map(gm -> gm.getName().toUpperCase()).orElse("ADVENTURE");
private String getGameMode(ServerPlayer player) {
GameMode gameMode = player.gameMode().get();
return gameMode.key(RegistryTypes.GAME_MODE).value().toUpperCase();
}
}
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.mixin;
import com.djrapitops.plan.gathering.listeners.sponge.SpongeGMChangeListener;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayer.class)
public class GameModeChangeMixin {
@Inject(method = "setGameMode", at = @At("HEAD"))
private void onGameModeChange(GameType gameType, CallbackInfo ci) {
SpongeGMChangeListener.Event event = new SpongeGMChangeListener.Event((Player) (Object) this, gameType);
SpongeGMChangeListener.EVENT_CONSUMERS.forEach(consumer -> consumer.accept(event));
}
}

View File

@ -36,8 +36,9 @@ import net.playeranalytics.plugin.scheduling.TimeAmount;
import net.playeranalytics.plugin.server.Listeners;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.network.ClientConnectionEvent;
import org.spongepowered.api.event.network.ServerSideConnectionEvent;
import javax.inject.Inject;
import java.util.*;
@ -93,7 +94,7 @@ public class SpongePingCounter extends TaskSystem.Task {
Map.Entry<UUID, List<DateObj<Integer>>> entry = iterator.next();
UUID uuid = entry.getKey();
List<DateObj<Integer>> history = entry.getValue();
Optional<Player> player = Sponge.getServer().getPlayer(uuid);
Optional<ServerPlayer> player = Sponge.server().player(uuid);
if (player.isPresent()) {
int ping = getPing(player.get());
if (ping <= -1 || ping > TimeUnit.SECONDS.toMillis(8L)) {
@ -129,27 +130,27 @@ public class SpongePingCounter extends TaskSystem.Task {
}
public void removePlayer(Player player) {
playerHistory.remove(player.getUniqueId());
startRecording.remove(player.getUniqueId());
playerHistory.remove(player.uniqueId());
startRecording.remove(player.uniqueId());
}
private int getPing(Player player) {
return player.getConnection().getLatency();
private int getPing(ServerPlayer player) {
return player.connection().latency();
}
@Listener
public void onPlayerJoin(ClientConnectionEvent.Join joinEvent) {
Player player = joinEvent.getTargetEntity();
public void onPlayerJoin(ServerSideConnectionEvent.Join joinEvent) {
Player player = joinEvent.player();
Long pingDelayMs = config.get(TimeSettings.PING_PLAYER_LOGIN_DELAY);
if (pingDelayMs >= TimeUnit.HOURS.toMillis(2L)) {
return;
}
startRecording.put(player.getUniqueId(), System.currentTimeMillis() + pingDelayMs);
startRecording.put(player.uniqueId(), System.currentTimeMillis() + pingDelayMs);
}
@Listener
public void onPlayerQuit(ClientConnectionEvent.Disconnect quitEvent) {
removePlayer(quitEvent.getTargetEntity());
public void onPlayerQuit(ServerSideConnectionEvent.Disconnect quitEvent) {
removePlayer(quitEvent.player());
}
public void clear() {

View File

@ -32,15 +32,16 @@ public class SpongeServerProperties extends ServerProperties {
@Inject
public SpongeServerProperties(Game game) {
// Port and max players cannot be provided as Game#server will fail when this is called (ConstructPluginEvent)
super(
"Sponge",
game.getServer().getBoundAddress().orElseGet(() -> new InetSocketAddress(25565)).getPort(),
game.getPlatform().getMinecraftVersion().getName(),
game.getPlatform().getMinecraftVersion().getName(),
() -> game.getServer().getBoundAddress()
-1, //game.server().boundAddress().orElseGet(() -> new InetSocketAddress(25565)).getPort(),
game.platform().minecraftVersion().name(),
game.platform().minecraftVersion().name(),
() -> game.server().boundAddress()
.orElseGet(() -> new InetSocketAddress(25565))
.getAddress().getHostAddress(),
game.getServer().getMaxPlayers()
-1 //game.server().maxPlayers()
);
}
}
}

View File

@ -34,7 +34,7 @@ import com.djrapitops.plan.version.SpongeVersionChecker;
import com.djrapitops.plan.version.VersionChecker;
import dagger.Binds;
import dagger.Module;
import org.spongepowered.api.world.World;
import org.spongepowered.api.world.server.ServerWorld;
/**
* Module for binding Sponge specific classes as interface implementations.
@ -63,11 +63,11 @@ public interface SpongeSuperClassBindingModule {
ServerShutdownSave bindServerShutdownSave(SpongeServerShutdownSave shutdownSave);
@Binds
ServerSensor<World> bindServerSensor(SpongeSensor sensor);
ServerSensor<ServerWorld> bindServerSensor(SpongeSensor sensor);
@Binds
ServerSensor<?> bindGenericsServerSensor(ServerSensor<World> sensor);
ServerSensor<?> bindGenericsServerSensor(ServerSensor<ServerWorld> sensor);
@Binds
VersionChecker bindVersionChecker(SpongeVersionChecker versionChecker);
}
}

View File

@ -35,13 +35,14 @@ import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoSet;
import org.spongepowered.api.world.World;
import org.spongepowered.api.world.server.ServerWorld;
@Module
public interface SpongeTaskModule {
@Binds
@IntoSet
TaskSystem.Task bindTPSCounter(ServerTPSCounter<World> tpsCounter);
TaskSystem.Task bindTPSCounter(ServerTPSCounter<ServerWorld> tpsCounter);
@Binds
@IntoSet

View File

@ -1,76 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.file;
import org.spongepowered.api.asset.Asset;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* {@link Resource} implementation for Sponge Asset API.
*
* @author AuroraLS3
*/
public class SpongeAssetResource implements Resource {
private final String resourceName;
private final Asset asset;
public SpongeAssetResource(String resourceName, Asset asset) {
this.resourceName = resourceName;
this.asset = asset;
}
@Override
public String getResourceName() {
return resourceName;
}
private void nullCheck() throws FileNotFoundException {
if (asset == null) {
throw new FileNotFoundException("a Resource was not found inside the jar (/assets/plan/" + resourceName + "), " +
"Plan does not support /reload or updates using " +
"Plugin Managers, restart the server and see if the error persists.");
}
}
@Override
public InputStream asInputStream() throws IOException {
nullCheck();
return new ByteArrayInputStream(asset.readBytes());
}
@Override
public List<String> asLines() throws IOException {
return asset.readLines(StandardCharsets.UTF_8);
}
@Override
public String asString() throws IOException {
return asset.readString(StandardCharsets.UTF_8);
}
@Override
public byte[] asBytes() throws IOException {
return asset.readBytes();
}
}

View File

@ -17,14 +17,18 @@
package com.djrapitops.plan.storage.file;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.PlanSponge;
import com.djrapitops.plan.settings.config.PlanConfig;
import dagger.Lazy;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.resource.ResourcePath;
import org.spongepowered.plugin.PluginContainer;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* Implements jar resource fetching with Sponge Asset API.
@ -50,9 +54,26 @@ public class SpongePlanFiles extends PlanFiles {
@Override
public Resource getResourceFromJar(String resourceName) {
try {
return new SpongeAssetResource(resourceName, Sponge.getAssetManager().getAsset(plugin, resourceName).orElse(null));
} catch (IllegalStateException spongeNotEnabled) {
if (!(plugin instanceof PlanSponge)) {
throw new IllegalStateException("Not a Sponge plugin");
}
PluginContainer container = ((PlanSponge) plugin).getPlugin();
ResourcePath resourcePath = ResourcePath.of(container, resourceName);
org.spongepowered.api.resource.Resource resource = Sponge.server().resourceManager().load(resourcePath);
return asStringResource(resource, resourceName);
} catch (IllegalStateException | IOException ignored) {
return super.getResourceFromJar(resourceName);
}
}
}
private Resource asStringResource(org.spongepowered.api.resource.Resource resource, String resourceName) throws IOException {
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.inputStream(), StandardCharsets.UTF_8));
StringWriter writer = new StringWriter()
) {
reader.transferTo(writer);
return new StringResource(resourceName, writer.toString());
}
}
}

View File

@ -0,0 +1,8 @@
{
"required": true,
"package": "com.djrapitops.plan.gathering.mixin",
"minVersion": "0.7.11",
"mixins": [
"GameModeChangeMixin"
]
}

View File

@ -16,18 +16,18 @@
*/
package utilities.mocks;
import com.djrapitops.plan.DaggerPlanSpongeComponent;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.PlanSpongeComponent;
import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.DaggerPlanSpongeComponent;
import net.kyori.adventure.text.TextComponent;
import com.djrapitops.plan.storage.database.SQLDB;
import org.mockito.Mockito;
import org.spongepowered.api.Game;
import org.spongepowered.api.MinecraftVersion;
import org.spongepowered.api.Platform;
import org.spongepowered.api.Server;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
import java.net.InetSocketAddress;
import java.nio.file.Path;
@ -81,8 +81,8 @@ public class SpongeMockComponent {
Platform platform = mockPlatform();
Server server = mockServer();
doReturn(platform).when(game).getPlatform();
doReturn(server).when(game).getServer();
doReturn(platform).when(game).platform();
doReturn(server).when(game).server();
return game;
}
@ -91,8 +91,8 @@ public class SpongeMockComponent {
Platform platform = Mockito.mock(Platform.class);
MinecraftVersion version = Mockito.mock(MinecraftVersion.class);
doReturn("1.12").when(version).getName();
doReturn(version).when(platform).getMinecraftVersion();
doReturn("1.12").when(version).name();
doReturn(version).when(platform).minecraftVersion();
return platform;
}
@ -100,17 +100,17 @@ public class SpongeMockComponent {
private Server mockServer() {
Server server = Mockito.mock(Server.class);
Text motd = Mockito.mock(Text.class);
doReturn("Motd").when(motd).toPlain();
TextComponent motd = Mockito.mock(TextComponent.class);
doReturn("Motd").when(motd).content();
Optional<InetSocketAddress> ip = Optional.of(new InetSocketAddress(25565));
int maxPlayers = 20;
List<Player> online = new ArrayList<>();
List<ServerPlayer> online = new ArrayList<>();
doReturn(motd).when(server).getMotd();
doReturn(ip).when(server).getBoundAddress();
doReturn(maxPlayers).when(server).getMaxPlayers();
doReturn(online).when(server).getOnlinePlayers();
doReturn(motd).when(server).motd();
doReturn(ip).when(server).boundAddress();
doReturn(maxPlayers).when(server).maxPlayers();
doReturn(online).when(server).onlinePlayers();
return server;
}
}
}

View File

@ -33,7 +33,7 @@ public class VelocityCMDSender implements CMDSender {
@Override
public MessageBuilder buildMessage() {
return new VelocityMessageBuilder(this);
return new AdventureMessageBuilder(this, commandSource);
}
@Override