diff --git a/README.md b/README.md
index 685ad4ff..01f0f66b 100644
--- a/README.md
+++ b/README.md
@@ -24,5 +24,6 @@
- cloud-sponge7: integration for [Sponge API](https://spongepowered.org) v7
- cloud-bungee: integration for Bungeecord API
- cloud-cloudburst: integration for cloudburst
+- cloud-waterdog: integration for [WaterdogPE](https://waterdog.dev) API
- cloud-minecraft-extras: optional extras using [adventure](https://github.com/KyoriPowered/adventure) API
- cloud-minecraft-bom: [bill of materials](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies) for cloud-minecraft dependencies
diff --git a/cloud-waterdog/build.gradle.kts b/cloud-waterdog/build.gradle.kts
new file mode 100644
index 00000000..f9e5e289
--- /dev/null
+++ b/cloud-waterdog/build.gradle.kts
@@ -0,0 +1,11 @@
+plugins {
+ id("conventions.base")
+ id("conventions.publishing")
+}
+
+dependencies {
+ api(libs.cloud.core)
+ compileOnly(libs.waterdog) {
+ isTransitive = false
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCaptionKeys.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCaptionKeys.java
new file mode 100644
index 00000000..e12dad42
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCaptionKeys.java
@@ -0,0 +1,68 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.incendo.cloud.caption.Caption;
+
+/**
+ * WaterdogPE specific {@link Caption caption keys}
+ *
+ * @since 2.0.0
+ */
+public final class WaterdogCaptionKeys {
+
+ private static final Collection
RECOGNIZED_CAPTIONS = new LinkedList<>();
+
+ /**
+ * Variables: {@code }
+ */
+ public static final Caption ARGUMENT_PARSE_FAILURE_PLAYER = of("argument.parse.failure.player");
+
+ /**
+ * Variables: {@code }
+ */
+ public static final Caption ARGUMENT_PARSE_FAILURE_SERVER = of("argument.parse.failure.server");
+
+ private WaterdogCaptionKeys() {
+ }
+
+ private static @NonNull Caption of(final @NonNull String key) {
+ final Caption caption = Caption.of(key);
+ RECOGNIZED_CAPTIONS.add(caption);
+ return caption;
+ }
+
+ /**
+ * Returns an immutable collection containing all standard caption keys.
+ *
+ * @return immutable collection of keys
+ */
+ public static @NonNull Collection<@NonNull Caption> waterdogCaptionKeys() {
+ return Collections.unmodifiableCollection(RECOGNIZED_CAPTIONS);
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommand.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommand.java
new file mode 100644
index 00000000..794a8e34
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommand.java
@@ -0,0 +1,66 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog;
+
+import dev.waterdog.waterdogpe.command.Command;
+import dev.waterdog.waterdogpe.command.CommandSender;
+import dev.waterdog.waterdogpe.command.CommandSettings;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.incendo.cloud.component.CommandComponent;
+
+final class WaterdogCommand extends Command {
+
+ private final WaterdogCommandManager manager;
+ private final CommandComponent command;
+
+ WaterdogCommand(
+ final org.incendo.cloud.@NonNull Command cloudCommand,
+ final @NonNull CommandComponent command,
+ final @NonNull WaterdogCommandManager manager
+ ) {
+ super(
+ command.name(),
+ CommandSettings.builder()
+ .setAliases(command.alternativeAliases().toArray(new String[0]))
+ .setDescription(cloudCommand.commandDescription().description().textDescription())
+ .setPermission(cloudCommand.commandPermission().toString())
+ .build()
+ );
+ this.command = command;
+ this.manager = manager;
+ }
+
+ @Override
+ public boolean onExecute(final CommandSender sender, final @Nullable String alias, final String[] args) {
+ /* Join input */
+ final StringBuilder builder = new StringBuilder(this.command.name());
+ for (final String arg : args) {
+ builder.append(" ").append(arg);
+ }
+ final C cloudSender = this.manager.senderMapper().map(sender);
+ this.manager.commandExecutor().executeCommand(cloudSender, builder.toString());
+ return true;
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommandManager.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommandManager.java
new file mode 100644
index 00000000..46c96360
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommandManager.java
@@ -0,0 +1,134 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog;
+
+import dev.waterdog.waterdogpe.command.CommandSender;
+import dev.waterdog.waterdogpe.plugin.Plugin;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.incendo.cloud.CommandManager;
+import org.incendo.cloud.SenderMapper;
+import org.incendo.cloud.SenderMapperHolder;
+import org.incendo.cloud.caption.CaptionProvider;
+import org.incendo.cloud.execution.ExecutionCoordinator;
+import org.incendo.cloud.waterdog.parser.PlayerParser;
+import org.incendo.cloud.waterdog.parser.ServerParser;
+
+/**
+ * Command manager for the WaterdogPE platform
+ *
+ * @param Command sender type
+ * @since 2.0.0
+ */
+public class WaterdogCommandManager extends CommandManager implements SenderMapperHolder {
+
+ /**
+ * Default caption for {@link WaterdogCaptionKeys#ARGUMENT_PARSE_FAILURE_PLAYER}
+ */
+ public static final String ARGUMENT_PARSE_FAILURE_PLAYER = "'' is not a valid player";
+
+ /**
+ * Default caption for {@link WaterdogCaptionKeys#ARGUMENT_PARSE_FAILURE_SERVER}
+ */
+ public static final String ARGUMENT_PARSE_FAILURE_SERVER = "'' is not a valid server";
+
+ /**
+ * WaterdogPE color code used to highlight error messages sent to command senders.
+ */
+ private static final String RED_COLOR_CODE = "§c";
+
+ private final Plugin owningPlugin;
+ private final SenderMapper senderMapper;
+
+ /**
+ * Construct a new WaterdogPE command manager
+ *
+ * @param owningPlugin Plugin that is constructing the manager
+ * @param commandExecutionCoordinator Coordinator provider
+ * @param senderMapper Function that maps {@link CommandSender} to the command sender type
+ */
+ @SuppressWarnings("this-escape")
+ public WaterdogCommandManager(
+ final @NonNull Plugin owningPlugin,
+ final @NonNull ExecutionCoordinator commandExecutionCoordinator,
+ final @NonNull SenderMapper senderMapper
+ ) {
+ super(commandExecutionCoordinator, new WaterdogPluginRegistrationHandler<>());
+ ((WaterdogPluginRegistrationHandler) this.commandRegistrationHandler()).initialize(this);
+ this.owningPlugin = owningPlugin;
+ this.senderMapper = senderMapper;
+
+ /* Register WaterdogPE Preprocessor */
+ this.registerCommandPreProcessor(new WaterdogCommandPreprocessor<>(this));
+
+ /* Register WaterdogPE Parsers */
+ this.parserRegistry()
+ .registerParser(PlayerParser.playerParser())
+ .registerParser(ServerParser.serverParser());
+
+ /* Register default captions */
+ this.captionRegistry()
+ .registerProvider(CaptionProvider.constantProvider()
+ .putCaption(WaterdogCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER, ARGUMENT_PARSE_FAILURE_PLAYER)
+ .putCaption(WaterdogCaptionKeys.ARGUMENT_PARSE_FAILURE_SERVER, ARGUMENT_PARSE_FAILURE_SERVER)
+ .build());
+
+ this.registerDefaultExceptionHandlers();
+ }
+
+ @Override
+ public final boolean hasPermission(
+ final @NonNull C sender,
+ final @NonNull String permission
+ ) {
+ if (permission.isEmpty()) {
+ return true;
+ }
+ return this.senderMapper.reverse(sender).hasPermission(permission);
+ }
+
+ /**
+ * Returns the owning plugin.
+ *
+ * @return owning plugin
+ */
+ public @NonNull Plugin owningPlugin() {
+ return this.owningPlugin;
+ }
+
+ private void registerDefaultExceptionHandlers() {
+ this.registerDefaultExceptionHandlers(
+ triplet -> {
+ final CommandSender commandSender = this.senderMapper.reverse(triplet.first().sender());
+ final String message = triplet.first().formatCaption(triplet.second(), triplet.third());
+ commandSender.sendMessage(RED_COLOR_CODE + message);
+ },
+ pair -> this.owningPlugin.getLogger().error(pair.first(), pair.second())
+ );
+ }
+
+ @Override
+ public final @NonNull SenderMapper senderMapper() {
+ return this.senderMapper;
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommandPreprocessor.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommandPreprocessor.java
new file mode 100644
index 00000000..ba833fdf
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogCommandPreprocessor.java
@@ -0,0 +1,54 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.incendo.cloud.execution.preprocessor.CommandPreprocessingContext;
+import org.incendo.cloud.execution.preprocessor.CommandPreprocessor;
+
+/**
+ * Command preprocessor which decorates incoming {@link org.incendo.cloud.context.CommandContext}
+ * with WaterdogPE specific objects
+ *
+ * @param Command sender type
+ * @since 2.0.0
+ */
+final class WaterdogCommandPreprocessor implements CommandPreprocessor {
+
+ private final WaterdogCommandManager mgr;
+
+ /**
+ * The WaterdogPE Command Preprocessor for storing WaterdogPE-specific contexts in the command contexts
+ *
+ * @param mgr The WaterdogCommandManager
+ */
+ WaterdogCommandPreprocessor(final @NonNull WaterdogCommandManager mgr) {
+ this.mgr = mgr;
+ }
+
+ @Override
+ public void accept(final @NonNull CommandPreprocessingContext context) {
+ context.commandContext().store(WaterdogContextKeys.PROXY_SERVER_KEY, this.mgr.owningPlugin().getProxy());
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogContextKeys.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogContextKeys.java
new file mode 100644
index 00000000..3c1b83dd
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogContextKeys.java
@@ -0,0 +1,48 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog;
+
+import dev.waterdog.waterdogpe.ProxyServer;
+import io.leangen.geantyref.TypeToken;
+import org.incendo.cloud.key.CloudKey;
+
+/**
+ * WaterdogPE related {@link org.incendo.cloud.context.CommandContext} keys
+ *
+ * @since 2.0.0
+ */
+public final class WaterdogContextKeys {
+
+ /**
+ * The {@link ProxyServer} instance is stored in the {@link org.incendo.cloud.context.CommandContext}
+ * in {@link WaterdogCommandPreprocessor}
+ */
+ public static final CloudKey PROXY_SERVER_KEY = CloudKey.of(
+ "ProxyServer",
+ TypeToken.get(ProxyServer.class)
+ );
+
+ private WaterdogContextKeys() {
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogPluginRegistrationHandler.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogPluginRegistrationHandler.java
new file mode 100644
index 00000000..37b45c4b
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/WaterdogPluginRegistrationHandler.java
@@ -0,0 +1,65 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.incendo.cloud.Command;
+import org.incendo.cloud.component.CommandComponent;
+import org.incendo.cloud.internal.CommandRegistrationHandler;
+
+final class WaterdogPluginRegistrationHandler implements CommandRegistrationHandler {
+
+ private final Map, dev.waterdog.waterdogpe.command.Command> registeredCommands = new HashMap<>();
+
+ private WaterdogCommandManager waterdogCommandManager;
+
+ WaterdogPluginRegistrationHandler() {
+ }
+
+ void initialize(final @NonNull WaterdogCommandManager waterdogCommandManager) {
+ this.waterdogCommandManager = waterdogCommandManager;
+ }
+
+ @Override
+ public boolean registerCommand(final @NonNull Command command) {
+ /* We only care about the root command argument */
+ final CommandComponent component = command.rootComponent();
+ if (this.registeredCommands.containsKey(component)) {
+ return false;
+ }
+ final WaterdogCommand waterdogCommand = new WaterdogCommand<>(
+ command,
+ component,
+ this.waterdogCommandManager
+ );
+ final boolean registered = this.waterdogCommandManager.owningPlugin().getProxy()
+ .getCommandMap().registerCommand(waterdogCommand);
+ if (registered) {
+ this.registeredCommands.put(component, waterdogCommand);
+ }
+ return registered;
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/package-info.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/package-info.java
new file mode 100644
index 00000000..9478bd2f
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * WaterdogPE implementation of cloud
+ */
+package org.incendo.cloud.waterdog;
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/PlayerParser.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/PlayerParser.java
new file mode 100644
index 00000000..d43849e4
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/PlayerParser.java
@@ -0,0 +1,120 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog.parser;
+
+import dev.waterdog.waterdogpe.player.ProxiedPlayer;
+import java.util.stream.Collectors;
+import org.apiguardian.api.API;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.incendo.cloud.caption.CaptionVariable;
+import org.incendo.cloud.component.CommandComponent;
+import org.incendo.cloud.context.CommandContext;
+import org.incendo.cloud.context.CommandInput;
+import org.incendo.cloud.exception.parsing.ParserException;
+import org.incendo.cloud.parser.ArgumentParseResult;
+import org.incendo.cloud.parser.ArgumentParser;
+import org.incendo.cloud.parser.ParserDescriptor;
+import org.incendo.cloud.suggestion.BlockingSuggestionProvider;
+import org.incendo.cloud.waterdog.WaterdogCaptionKeys;
+import org.incendo.cloud.waterdog.WaterdogContextKeys;
+
+/**
+ * Argument parser for {@link ProxiedPlayer players}
+ *
+ * @param Command sender type
+ * @since 2.0.0
+ */
+public final class PlayerParser implements ArgumentParser, BlockingSuggestionProvider.Strings {
+
+ /**
+ * Creates a new player parser.
+ *
+ * @param command sender type
+ * @return the created parser
+ * @since 2.0.0
+ */
+ @API(status = API.Status.STABLE, since = "2.0.0")
+ public static @NonNull ParserDescriptor playerParser() {
+ return ParserDescriptor.of(new PlayerParser<>(), ProxiedPlayer.class);
+ }
+
+ /**
+ * Returns a {@link CommandComponent.Builder} using {@link #playerParser()} as the parser.
+ *
+ * @param the command sender type
+ * @return the component builder
+ * @since 2.0.0
+ */
+ @API(status = API.Status.STABLE, since = "2.0.0")
+ public static CommandComponent.@NonNull Builder playerComponent() {
+ return CommandComponent.builder().parser(playerParser());
+ }
+
+ @Override
+ public @NonNull ArgumentParseResult<@NonNull ProxiedPlayer> parse(
+ final @NonNull CommandContext<@NonNull C> commandContext,
+ final @NonNull CommandInput commandInput
+ ) {
+ final String input = commandInput.readString();
+ final ProxiedPlayer player = commandContext.get(WaterdogContextKeys.PROXY_SERVER_KEY).getPlayer(input);
+ if (player == null) {
+ return ArgumentParseResult.failure(
+ new PlayerParseException(
+ input,
+ commandContext
+ )
+ );
+ }
+ return ArgumentParseResult.success(player);
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull String> stringSuggestions(
+ final @NonNull CommandContext commandContext,
+ final @NonNull CommandInput input
+ ) {
+ return commandContext.get(WaterdogContextKeys.PROXY_SERVER_KEY)
+ .getPlayers()
+ .values()
+ .stream()
+ .map(ProxiedPlayer::getName)
+ .collect(Collectors.toList());
+ }
+
+ public static final class PlayerParseException extends ParserException {
+
+
+ private PlayerParseException(
+ final @NonNull String input,
+ final @NonNull CommandContext> context
+ ) {
+ super(
+ PlayerParser.class,
+ context,
+ WaterdogCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER,
+ CaptionVariable.of("input", input)
+ );
+ }
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/ServerParser.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/ServerParser.java
new file mode 100644
index 00000000..51438612
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/ServerParser.java
@@ -0,0 +1,119 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+package org.incendo.cloud.waterdog.parser;
+
+import dev.waterdog.waterdogpe.network.serverinfo.ServerInfo;
+import java.util.stream.Collectors;
+import org.apiguardian.api.API;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.incendo.cloud.caption.CaptionVariable;
+import org.incendo.cloud.component.CommandComponent;
+import org.incendo.cloud.context.CommandContext;
+import org.incendo.cloud.context.CommandInput;
+import org.incendo.cloud.exception.parsing.ParserException;
+import org.incendo.cloud.parser.ArgumentParseResult;
+import org.incendo.cloud.parser.ArgumentParser;
+import org.incendo.cloud.parser.ParserDescriptor;
+import org.incendo.cloud.suggestion.BlockingSuggestionProvider;
+import org.incendo.cloud.waterdog.WaterdogCaptionKeys;
+import org.incendo.cloud.waterdog.WaterdogContextKeys;
+
+/**
+ * Argument parser for {@link ServerInfo servers}
+ *
+ * @param Command sender type
+ * @since 2.0.0
+ */
+public final class ServerParser implements ArgumentParser, BlockingSuggestionProvider.Strings {
+
+ /**
+ * Creates a new server parser.
+ *
+ * @param command sender type
+ * @return the created parser
+ * @since 2.0.0
+ */
+ @API(status = API.Status.STABLE, since = "2.0.0")
+ public static @NonNull ParserDescriptor serverParser() {
+ return ParserDescriptor.of(new ServerParser<>(), ServerInfo.class);
+ }
+
+ /**
+ * Returns a {@link CommandComponent.Builder} using {@link #serverParser()} as the parser.
+ *
+ * @param the command sender type
+ * @return the component builder
+ * @since 2.0.0
+ */
+ @API(status = API.Status.STABLE, since = "2.0.0")
+ public static CommandComponent.@NonNull Builder serverComponent() {
+ return CommandComponent.builder().parser(serverParser());
+ }
+
+ @Override
+ public @NonNull ArgumentParseResult<@NonNull ServerInfo> parse(
+ final @NonNull CommandContext<@NonNull C> commandContext,
+ final @NonNull CommandInput commandInput
+ ) {
+ final String input = commandInput.readString();
+ final ServerInfo server = commandContext.get(WaterdogContextKeys.PROXY_SERVER_KEY).getServerInfo(input);
+ if (server == null) {
+ return ArgumentParseResult.failure(
+ new ServerParseException(
+ input,
+ commandContext
+ )
+ );
+ }
+ return ArgumentParseResult.success(server);
+ }
+
+ @Override
+ public @NonNull Iterable<@NonNull String> stringSuggestions(
+ final @NonNull CommandContext commandContext,
+ final @NonNull CommandInput input
+ ) {
+ return commandContext.get(WaterdogContextKeys.PROXY_SERVER_KEY)
+ .getServers()
+ .stream()
+ .map(ServerInfo::getServerName)
+ .collect(Collectors.toList());
+ }
+
+ public static final class ServerParseException extends ParserException {
+
+
+ private ServerParseException(
+ final @NonNull String input,
+ final @NonNull CommandContext> context
+ ) {
+ super(
+ ServerParser.class,
+ context,
+ WaterdogCaptionKeys.ARGUMENT_PARSE_FAILURE_SERVER,
+ CaptionVariable.of("input", input)
+ );
+ }
+ }
+}
diff --git a/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/package-info.java b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/package-info.java
new file mode 100644
index 00000000..4d50210c
--- /dev/null
+++ b/cloud-waterdog/src/main/java/org/incendo/cloud/waterdog/parser/package-info.java
@@ -0,0 +1,27 @@
+//
+// MIT License
+//
+// Copyright (c) 2024 Incendo
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+/**
+ * WaterdogPE specific argument types
+ */
+package org.incendo.cloud.waterdog.parser;
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 680c6534..2a69cfbc 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -16,6 +16,7 @@ bukkit = "1.13.2-R0.1-SNAPSHOT"
commodore = "2.2"
bungeecord = "1.20-R0.2"
cloudburst = "1.0.0-SNAPSHOT"
+waterdog = "2.0.4-SNAPSHOT"
adventureApi = "4.15.0"
adventurePlatform = "4.4.1"
paperApi = "1.20.6-R0.1-SNAPSHOT"
@@ -47,6 +48,7 @@ reflectionRemapper = "xyz.jpenilla:reflection-remapper:0.1.3"
commodore = { group = "me.lucko", name = "commodore", version.ref = "commodore" }
bungeecord = { group = "net.md-5", name = "bungeecord-api", version.ref = "bungeecord" }
cloudburst = { group = "org.cloudburstmc", name = "cloudburst-server", version.ref = "cloudburst" }
+waterdog = { group = "dev.waterdog.waterdogpe", name = "waterdog", version.ref = "waterdog" }
adventureApi = { group = "net.kyori", name = "adventure-api", version.ref = "adventureApi" }
minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventureApi" }
adventurePlatformBukkit = { group = "net.kyori", name = "adventure-platform-bukkit", version.ref = "adventurePlatform" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 169a2052..698bda79 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -45,6 +45,14 @@ dependencyResolutionManagement {
name = "sponge"
mavenContent { includeGroup("org.spongepowered") }
}
+ /* The WaterdogPE repository, used for cloud-waterdog */
+ maven("https://repo.waterdog.dev/main") {
+ name = "waterdog"
+ mavenContent {
+ snapshotsOnly()
+ includeGroup("dev.waterdog.waterdogpe")
+ }
+ }
}
}
@@ -62,6 +70,7 @@ include("cloud-paper")
include("cloud-paper-signed-arguments")
include("cloud-sponge7")
include("cloud-velocity")
+include("cloud-waterdog")
include("examples/example-bukkit")
findProject(":examples/example-bukkit")?.name = "example-bukkit"