Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sponge 11 implementation #9

Draft
wants to merge 72 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
b338b2d
sparkles: Begin working on Sponge support
Citymonstret Oct 14, 2020
baab901
sparkles: Improve the Sponge module
Citymonstret Oct 15, 2020
4a34b7f
sparkles: Initial work on Sponge v8
Citymonstret Oct 16, 2020
642d00b
sponge: Update after rebase
zml2008 Mar 29, 2021
750b6cc
Map to Sponge API Command.Raw instead of directly to Brigadier
jpenilla Apr 19, 2021
50cd277
Lock registration after event
jpenilla Apr 19, 2021
698cfa1
sponge: Add createNative to CloudInjectionModule
jpenilla Apr 19, 2021
75eeb6b
sponge: Add some argument types and an example/test plugin
jpenilla Apr 20, 2021
a587834
sponge: More work on Sponge API 8 implementation
jpenilla Apr 24, 2021
6ec83cc
sponge: Prioritize registered parser mappers to allow for overriding …
jpenilla Apr 25, 2021
0761cae
sponge: Implement more parsers, random fixes and improvements
jpenilla Apr 26, 2021
d7d02b1
sponge: Set fields accessible
jpenilla Apr 26, 2021
86c06b8
sponge: Add more argument parsers
jpenilla Apr 26, 2021
1388e23
sponge: More Javadoc
jpenilla Apr 26, 2021
a63bcb8
sponge: Properly translate node requirements and executable status
jpenilla Apr 26, 2021
232cfed
sponge: Also set requirements on root nodes
jpenilla Apr 26, 2021
93528ae
sponge: More Javadoc
jpenilla Apr 27, 2021
124173b
sponge: Fix injection module types
jpenilla Apr 27, 2021
ced14f9
Add macOS garbage to .gitignore
jpenilla Apr 27, 2021
28bde90
sponge: Fix license header violations
jpenilla Apr 27, 2021
5085ab9
sponge: Clean up number argument mapping, unwrap mapped parsers
jpenilla Apr 28, 2021
26c39ba
sponge: Map compound arguments
jpenilla Apr 30, 2021
b206f8b
sponge: Update for sponge math changes
jpenilla Apr 30, 2021
c45f0a3
sponge: Implement Block and ItemStack predicate arguments
jpenilla May 2, 2021
2912c05
sponge: Update for plugin-spi changes
jpenilla May 3, 2021
bff10a8
sponge: Update for command api renames
jpenilla May 4, 2021
5ffc2dc
sponge: Simplify canExecute check
jpenilla May 7, 2021
9910dec
build/sponge: Update VanillaGradle to 0.2
jpenilla Jul 5, 2021
85932b3
build/sponge: Update SpongeGradle to 1.1.1
jpenilla Jul 5, 2021
85dbcee
sponge: Update for Sponge API changes
jpenilla Jul 5, 2021
50ec990
Update for Sponge API changes
jpenilla Jul 27, 2021
09f2093
Update for Sponge API and SpongeGradle changes
jpenilla Sep 19, 2021
0a48c4d
Update import order
jpenilla Jan 10, 2022
247dda4
sponge: Catch late command manager creation
jpenilla Jan 10, 2022
8616346
sponge: Use usage message for description
jpenilla Jan 12, 2022
54b3013
sponge: Improve WorldArgument suggestions
jpenilla Jan 12, 2022
6790b4d
Fixes for SpongeForge
jpenilla Jan 13, 2022
8cc76c6
sponge: Update RegistryEntryArgument
jpenilla Jan 13, 2022
644ad39
sponge: Make GameProfileCollection extend Collection<GameProfile>
jpenilla Jan 14, 2022
a425476
more spongeforge fixes
jpenilla Jan 14, 2022
24b7b2f
address review comments
jpenilla Jan 17, 2022
b7d75de
build updates
jpenilla Mar 10, 2022
9f0dcc5
Update for 1.7.0 deprecations
jpenilla Jun 19, 2022
a2b9da9
Update sponge
jpenilla Jun 19, 2022
384944a
update sponge module
jpenilla Nov 6, 2022
4e92951
Update headers
jpenilla Dec 2, 2022
4a9d907
Update for build changes
jpenilla Dec 11, 2022
aa4a68d
Get sponge project to import
jpenilla Jan 21, 2024
8024768
Begin porting to cloud v2
jpenilla Jan 21, 2024
10acd0d
Cloud number suggestions by default & check sender type in node requi…
jpenilla Jan 21, 2024
f73a798
Make it compile
jpenilla Jan 21, 2024
a215eae
spotlessApply
jpenilla Jan 21, 2024
9924762
Resolve non-javadoc style issues
jpenilla Jan 21, 2024
6884aa9
Javadoc fixes
jpenilla Jan 21, 2024
6d1f6aa
Begin updating for 1.20.2+
jpenilla Jan 21, 2024
4489016
Fix rebase typo
jpenilla Jan 21, 2024
f591bce
Fix build
jpenilla Jan 21, 2024
feebfee
More updating for 1.20.2
jpenilla Jan 21, 2024
c34e2f3
Add missing check for aggregate arguments
jpenilla Jan 21, 2024
2b969c0
Clean up & tooltip suggestion support
jpenilla Jan 21, 2024
ec874ba
Update for cloud changes
jpenilla Jan 22, 2024
dd3ad44
Handle all aggregates not just compound
jpenilla Jan 22, 2024
17302b6
Update for caption changes
jpenilla Jan 23, 2024
be90dc3
Update
jpenilla Jan 25, 2024
1e100bc
Repackage to org.incendo.cloud
jpenilla Jan 25, 2024
b654ee9
Update for cloud beta 3
jpenilla Feb 21, 2024
0181348
fix compile
jpenilla Mar 12, 2024
e2d8b66
fix compound example compile
jpenilla Apr 17, 2024
ecfa09f
Fix sponge compile
jpenilla Apr 30, 2024
7dd03f1
Update sponge for access check changes
jpenilla May 3, 2024
c64fcf6
1.20.6
jpenilla May 30, 2024
d19fd34
fix stack overflow in sponge registration
jpenilla Nov 30, 2024
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 @@
import org.spongepowered.gradle.vanilla.repository.MinecraftPlatform

plugins {
id("conventions.base")
id("conventions.publishing")
id("org.spongepowered.gradle.vanilla")
}

dependencies {
api(libs.cloud.core)
implementation(libs.cloud.brigadier)
implementation(project(":cloud-minecraft-modded-common", configuration = "namedElements"))
compileOnly("org.spongepowered:spongeapi:11.0.0-SNAPSHOT")
compileOnly("org.spongepowered:sponge:1.20.6-11.0.0-SNAPSHOT")
}

minecraft {
version("1.20.6")
platform(MinecraftPlatform.JOINED)
}
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,226 @@
//
// 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 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 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);
return (CommandTreeNode.Root) root;
}

private void addChildren(final CommandTreeNode<?> node, final CommandNode<C> cloud) {
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);
continue;
} else {
treeNode = this.commandManager.parserMapper().mapComponent(value);
}
this.addRequirement(child, treeNode);
if (canExecute(child)) {
treeNode.executable();
}
this.addChildren(treeNode, child);
node.child(value.name(), treeNode);
}
}

private void handleAggregate(
final CommandTreeNode<?> node,
final CommandNode<C> child,
final AggregateParser<C, ?> compound
) {
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())));
}
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);
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,41 @@
//
// 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;

/**
* 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.
*
* @return argument node
*/
CommandTreeNode.@NonNull Argument<? extends CommandTreeNode.Argument<?>> node();

}
Loading