Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
524610f
sparkles: Begin working on Sponge support
Citymonstret Oct 14, 2020
314a332
sparkles: Improve the Sponge module
Citymonstret Oct 15, 2020
ae77d8f
sparkles: Initial work on Sponge v8
Citymonstret Oct 16, 2020
13bbc6e
sponge: Update after rebase
zml2008 Mar 29, 2021
45c0f3c
Map to Sponge API Command.Raw instead of directly to Brigadier
jpenilla Apr 19, 2021
8f33567
Lock registration after event
jpenilla Apr 19, 2021
e329c2f
sponge: Add createNative to CloudInjectionModule
jpenilla Apr 19, 2021
057a4a5
sponge: Add some argument types and an example/test plugin
jpenilla Apr 20, 2021
aa758aa
sponge: More work on Sponge API 8 implementation
jpenilla Apr 24, 2021
32f2cfd
sponge: Prioritize registered parser mappers to allow for overriding …
jpenilla Apr 25, 2021
d235dd6
sponge: Implement more parsers, random fixes and improvements
jpenilla Apr 26, 2021
d6b4ae6
sponge: Set fields accessible
jpenilla Apr 26, 2021
31a14e9
sponge: Add more argument parsers
jpenilla Apr 26, 2021
cc7db25
sponge: More Javadoc
jpenilla Apr 26, 2021
628e9cc
sponge: Properly translate node requirements and executable status
jpenilla Apr 26, 2021
8551196
sponge: Also set requirements on root nodes
jpenilla Apr 26, 2021
4c5fb94
sponge: More Javadoc
jpenilla Apr 27, 2021
99b9c91
sponge: Fix injection module types
jpenilla Apr 27, 2021
24e4aa8
Add macOS garbage to .gitignore
jpenilla Apr 27, 2021
ba98096
sponge: Fix license header violations
jpenilla Apr 27, 2021
882cdaa
sponge: Clean up number argument mapping, unwrap mapped parsers
jpenilla Apr 28, 2021
f4b2ad9
sponge: Map compound arguments
jpenilla Apr 30, 2021
82644b0
sponge: Update for sponge math changes
jpenilla Apr 30, 2021
7be6805
sponge: Implement Block and ItemStack predicate arguments
jpenilla May 2, 2021
ba783f4
sponge: Update for plugin-spi changes
jpenilla May 3, 2021
7c6aa31
sponge: Update for command api renames
jpenilla May 4, 2021
f5c54ad
sponge: Simplify canExecute check
jpenilla May 7, 2021
ac2c06c
build/sponge: Update VanillaGradle to 0.2
jpenilla Jul 5, 2021
8cb17c7
build/sponge: Update SpongeGradle to 1.1.1
jpenilla Jul 5, 2021
9d6cac7
sponge: Update for Sponge API changes
jpenilla Jul 5, 2021
5f93441
Update for Sponge API changes
jpenilla Jul 27, 2021
9c8d99c
Update for Sponge API and SpongeGradle changes
jpenilla Sep 19, 2021
c72b799
Update import order
jpenilla Jan 10, 2022
8e78de2
sponge: Catch late command manager creation
jpenilla Jan 10, 2022
8b61b2d
sponge: Use usage message for description
jpenilla Jan 12, 2022
4ed9d44
sponge: Improve WorldArgument suggestions
jpenilla Jan 12, 2022
812d522
Fixes for SpongeForge
jpenilla Jan 13, 2022
69eb6af
sponge: Update RegistryEntryArgument
jpenilla Jan 13, 2022
2e3134c
sponge: Make GameProfileCollection extend Collection<GameProfile>
jpenilla Jan 14, 2022
23675b6
more spongeforge fixes
jpenilla Jan 14, 2022
c9f15ce
address review comments
jpenilla Jan 17, 2022
72432fa
build updates
jpenilla Mar 10, 2022
a692c68
Update for 1.7.0 deprecations
jpenilla Jun 19, 2022
2336e0f
Update sponge
jpenilla Jun 19, 2022
c56da59
update sponge module
jpenilla Nov 6, 2022
ec9a540
Update headers
jpenilla Dec 2, 2022
9d45579
Update for build changes
jpenilla Dec 11, 2022
52f0132
Get sponge project to import
jpenilla Jan 21, 2024
4df4dea
Begin porting to cloud v2
jpenilla Jan 21, 2024
42020a0
Cloud number suggestions by default & check sender type in node requi…
jpenilla Jan 21, 2024
7d82ca4
Make it compile
jpenilla Jan 21, 2024
a03b393
spotlessApply
jpenilla Jan 21, 2024
5e3bcfc
Resolve non-javadoc style issues
jpenilla Jan 21, 2024
786b9f9
Javadoc fixes
jpenilla Jan 21, 2024
aee9f97
Begin updating for 1.20.2+
jpenilla Jan 21, 2024
18ebda5
Fix rebase typo
jpenilla Jan 21, 2024
aa64ceb
Fix build
jpenilla Jan 21, 2024
c87c4a3
More updating for 1.20.2
jpenilla Jan 21, 2024
6f1717a
Add missing check for aggregate arguments
jpenilla Jan 21, 2024
6b95fb9
Clean up & tooltip suggestion support
jpenilla Jan 21, 2024
e926a57
Update for cloud changes
jpenilla Jan 22, 2024
c39566d
Handle all aggregates not just compound
jpenilla Jan 22, 2024
bd014ed
Update for caption changes
jpenilla Jan 23, 2024
dbf8983
Update
jpenilla Jan 25, 2024
0d760ae
Repackage to org.incendo.cloud
jpenilla Jan 25, 2024
ebcc856
Update for cloud beta 3
jpenilla Feb 21, 2024
41a9b58
fix compile
jpenilla Mar 12, 2024
8d3bf6b
fix compound example compile
jpenilla Apr 17, 2024
44210bd
Fix sponge compile
jpenilla Apr 30, 2024
a10d108
Update sponge for access check changes
jpenilla May 3, 2024
619e40d
1.20.6
jpenilla May 30, 2024
dcd16a2
fix stack overflow in sponge registration
jpenilla Nov 30, 2024
5763da3
Fix build
jpenilla Oct 24, 2025
b6e857c
Update to API-12
Vilsu221 Oct 24, 2025
335ab5d
Update to API-14 & fix parsers
Vilsu221 Nov 6, 2025
f3a8b96
Fix style violations
jpenilla Jan 24, 2026
af7a0b7
Fix Sponge Error Prone warnings
jpenilla Oct 9, 2026
c2a5f4c
Update Sponge to Minecraft 26.1.2 and API 19
jpenilla Oct 9, 2026
1bf02b0
Initialize Sponge parser mappings before configuration
jpenilla Oct 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions cloud-sponge/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id("conventions.base")
id("conventions.publishing")
id("net.neoforged.moddev")
}

dependencies {
api(libs.cloud.core)
implementation(libs.cloud.brigadier)
offlineLinkedJavadoc(project(":cloud-minecraft-modded-common"))
implementation(project(":cloud-minecraft-modded-common"))
compileOnly("org.spongepowered:spongeapi:19.0.0-SNAPSHOT")
compileOnly("org.spongepowered:sponge:26.1.2-19.0.0-SNAPSHOT")
}

neoForge {
enable {
neoFormVersion = "26.1.2-1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// 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.sponge;

import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.util.Types;
import java.lang.reflect.Type;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.SenderMapper;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.spongepowered.api.command.CommandCause;

/**
* Injection module that allows for {@link SpongeCommandManager} to be injectable.
*
* @param <C> Command sender type
*/
public final class CloudInjectionModule<C> extends AbstractModule {

private final Class<C> commandSenderType;
private final ExecutionCoordinator<C> executionCoordinator;
private final SenderMapper<@NonNull CommandCause, @NonNull C> senderMapper;

/**
* Create a new injection module.
*
* @param commandSenderType Your command sender type
* @param executionCoordinator Command execution coordinator
* @param senderMapper Function mapping the custom command sender type to a Sponge CommandCause
*/
public CloudInjectionModule(
final @NonNull Class<C> commandSenderType,
final @NonNull ExecutionCoordinator<C> executionCoordinator,
final @NonNull SenderMapper<@NonNull CommandCause, @NonNull C> senderMapper
) {
this.commandSenderType = commandSenderType;
this.executionCoordinator = executionCoordinator;
this.senderMapper = senderMapper;
}

/**
* Create a new injection module using Sponge's {@link CommandCause} as the sender type.
*
* @param executionCoordinator Command execution coordinator
* @return new injection module
*/
public static @NonNull CloudInjectionModule<@NonNull CommandCause> createNative(
final @NonNull ExecutionCoordinator<CommandCause> executionCoordinator
) {
return new CloudInjectionModule<>(
CommandCause.class,
executionCoordinator,
SenderMapper.identity()
);
}

@SuppressWarnings({"unchecked", "rawtypes"})
@Override
protected void configure() {
final Type commandExecutionCoordinatorType = Types.newParameterizedType(
ExecutionCoordinator.class, this.commandSenderType
);
final Key coordinatorKey = Key.get(commandExecutionCoordinatorType);
this.bind(coordinatorKey).toInstance(this.executionCoordinator);

final Type commandSenderMapperFunction = Types.newParameterizedType(
SenderMapper.class, CommandCause.class, this.commandSenderType
);
final Key senderMapperKey = Key.get(commandSenderMapperFunction);
this.bind(senderMapperKey).toInstance(this.senderMapper);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
//
// 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.sponge;

import io.leangen.geantyref.GenericTypeReflector;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.component.CommandComponent;
import org.incendo.cloud.internal.CommandNode;
import org.incendo.cloud.parser.aggregate.AggregateParser;
import org.incendo.cloud.parser.standard.LiteralParser;
import org.incendo.cloud.permission.Permission;
import org.incendo.cloud.type.tuple.Pair;
import org.spongepowered.api.command.Command;
import org.spongepowered.api.command.CommandCause;
import org.spongepowered.api.command.CommandCompletion;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.parameter.ArgumentReader;
import org.spongepowered.api.command.registrar.tree.CommandTreeNode;
import org.spongepowered.api.registry.RegistryHolder;

import static net.kyori.adventure.text.Component.text;

final class CloudSpongeCommand<C> implements Command.Raw {

private final SpongeCommandManager<C> commandManager;
private final String label;

CloudSpongeCommand(
final @NonNull String label,
final @NonNull SpongeCommandManager<C> commandManager
) {
this.label = label;
this.commandManager = commandManager;
}

@Override
public CommandResult process(final @NonNull CommandCause cause, final ArgumentReader.@NonNull Mutable arguments) {
final C cloudSender = this.commandManager.senderMapper().map(cause);
final String input = this.formatCommandForParsing(arguments.input());
this.commandManager.commandExecutor().executeCommand(cloudSender, input);
return CommandResult.success();
}

@Override
public List<CommandCompletion> complete(
final @NonNull CommandCause cause,
final ArgumentReader.@NonNull Mutable arguments
) {
return this.commandManager.suggestionFactory()
.suggestImmediately(this.commandManager.senderMapper().map(cause),
this.formatCommandForSuggestions(arguments.input()))
.list()
.stream()
.map(s -> CommandCompletion.of(s.suggestion(), s.tooltip()))
.collect(Collectors.toList());
}

@Override
public boolean canExecute(final @NonNull CommandCause cause) {
return this.checkAccess(
cause,
this.namedNode().nodeMeta()
.getOrDefault(CommandNode.META_KEY_ACCESS, Collections.emptyMap())
);
}

@Override
public Optional<Component> shortDescription(final CommandCause cause) {
return Optional.of(this.usage(cause));
}

@Override
public Optional<Component> extendedDescription(final CommandCause cause) {
return Optional.of(this.usage(cause));
}

@Override
public Optional<Component> help(final @NonNull CommandCause cause) {
return Optional.of(this.usage(cause));
}

@Override
public Component usage(final CommandCause cause) {
return text(this.commandManager.commandSyntaxFormatter()
.apply(this.commandManager.senderMapper().map(cause), Collections.emptyList(), this.namedNode()));
}

private CommandNode<C> namedNode() {
return this.commandManager.commandTree().getNamedNode(this.label);
}

@Override
public CommandTreeNode.Root commandTree(final RegistryHolder holder) {
final CommandTreeNode<CommandTreeNode.Root> root = CommandTreeNode.root();

final CommandNode<C> cloud = this.namedNode();

if (canExecute(cloud)) {
root.executable();
}

this.addRequirement(cloud, root);

this.addChildren(root, cloud, holder);
return (CommandTreeNode.Root) root;
}

private void addChildren(final CommandTreeNode<?> node, final CommandNode<C> cloud, final RegistryHolder holder) {
for (final CommandNode<C> child : cloud.children()) {
final CommandComponent<C> value = child.component();
final CommandTreeNode.Argument<? extends CommandTreeNode.Argument<?>> treeNode;
if (value.parser() instanceof LiteralParser) {
treeNode = (CommandTreeNode.Argument<? extends CommandTreeNode.Argument<?>>) CommandTreeNode.literal();
} else if (value.parser() instanceof AggregateParser<C, ?> aggregate) {
this.handleAggregate(node, child, aggregate, holder);
continue;
} else {
treeNode = this.commandManager.parserMapper().mapComponent(value, holder);
}
this.addRequirement(child, treeNode);
if (canExecute(child)) {
treeNode.executable();
}
this.addChildren(treeNode, child, holder);
node.child(value.name(), treeNode);
}
}

private void handleAggregate(
final CommandTreeNode<?> node,
final CommandNode<C> child,
final AggregateParser<C, ?> compound,
final RegistryHolder holder
) {
final CommandTreeNode.Argument<? extends CommandTreeNode.Argument<?>> treeNode;
final ArrayDeque<Pair<String, CommandTreeNode.Argument<? extends CommandTreeNode.Argument<?>>>> nodes = new ArrayDeque<>();
for (final CommandComponent<C> component : compound.components()) {
final String name = component.name();
nodes.add(Pair.of(name, this.commandManager.parserMapper().mapParser(component.parser(), holder)));
}
Pair<String, CommandTreeNode.Argument<? extends CommandTreeNode.Argument<?>>> argument = null;
while (!nodes.isEmpty()) {
final Pair<String, CommandTreeNode.Argument<? extends CommandTreeNode.Argument<?>>> prev = argument;
argument = nodes.removeLast();
if (prev != null) {
argument.second().child(prev.first(), prev.second());
} else {
// last node
if (canExecute(child)) {
argument.second().executable();
}
}
this.addRequirement(child, argument.second());
}
treeNode = argument.second();
this.addChildren(treeNode, child, holder);
node.child(compound.components().get(0).toString(), treeNode);
}

private static <C> boolean canExecute(final @NonNull CommandNode<C> node) {
return node.isLeaf()
|| !node.component().required()
|| node.command() != null
|| node.children().stream().noneMatch(c -> c.component().required());
}

private void addRequirement(
final @NonNull CommandNode<C> cloud,
final @NonNull CommandTreeNode<? extends CommandTreeNode<?>> node
) {
final Map<Type, Permission> accessMap =
cloud.nodeMeta().getOrDefault(CommandNode.META_KEY_ACCESS, Collections.emptyMap());
node.requires(cause -> this.checkAccess(cause, accessMap));
}

private boolean checkAccess(final CommandCause cause, final Map<Type, Permission> accessMap) {
final C cloudSender = this.commandManager.senderMapper().map(cause);
for (final Map.Entry<Type, Permission> entry : accessMap.entrySet()) {
if (GenericTypeReflector.isSuperType(entry.getKey(), cloudSender.getClass())) {
if (this.commandManager.testPermission(cloudSender, entry.getValue()).allowed()) {
return true;
}
}
}
return false;
}

private String formatCommandForParsing(final @NonNull String arguments) {
if (arguments.isEmpty()) {
return this.label;
}
return this.label + " " + arguments;
}

private String formatCommandForSuggestions(final @NonNull String arguments) {
return this.label + " " + arguments;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// 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.sponge;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.api.command.registrar.tree.CommandTreeNode;
import org.spongepowered.api.registry.RegistryHolder;

/**
* Implemented by {@link org.incendo.cloud.parser.ArgumentParser} which also supply a special {@link CommandTreeNode.Argument}.
*/
public interface NodeSource {

/**
* Get the node for this parser.
*
* @param registryHolder registry holder
* @return argument node
*/
CommandTreeNode.@NonNull Argument<? extends CommandTreeNode.Argument<?>> node(RegistryHolder registryHolder);

}
Loading
Loading