Skip to content

Commit 1d50067

Browse files
authored
feat: SignedGreedyStringParser (#50)
* Initial signed arguments implementation Still needs platform implementations of SignedStringMapper. On Fabric/NeoForge this is simple, since we have access to game types. For Paper, I have an implementation using `reflection-remapper`, so we need to decide whether we want to expose this dependency to all cloud-paper consumers. * fix comment * add static factory * Implement signed mapper on Paper * Use parser/mapper contributor system * Move Paper signed arguments impl to own project
1 parent 8d34f45 commit 1d50067

File tree

21 files changed

+961
-3
lines changed

21 files changed

+961
-3
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
plugins {
2+
id("conventions.base")
3+
id("conventions.publishing")
4+
}
5+
6+
dependencies {
7+
api(libs.cloud.core)
8+
compileOnly(libs.brigadier)
9+
compileOnlyApi(libs.adventureApi)
10+
api(projects.cloudBrigadier)
11+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2024 Incendo
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package org.incendo.cloud.minecraft.signed;
25+
26+
import com.mojang.brigadier.arguments.StringArgumentType;
27+
import io.leangen.geantyref.TypeToken;
28+
import org.apiguardian.api.API;
29+
import org.incendo.cloud.brigadier.BrigadierManagerHolder;
30+
import org.incendo.cloud.brigadier.CloudBrigadierManager;
31+
32+
@API(status = API.Status.INTERNAL)
33+
public final class SignedArguments {
34+
private SignedArguments() {
35+
}
36+
37+
/**
38+
* Returns whether Adventure is present.
39+
*
40+
* @return whether Adventure is present
41+
*/
42+
public static boolean adventurePresent() {
43+
try {
44+
Class.forName("net.kyori.adventure.chat.SignedMessage");
45+
return true;
46+
} catch (final ClassNotFoundException ignore) {
47+
return false;
48+
}
49+
}
50+
51+
/**
52+
* Registers the default Brigadier mapping for {@link SignedGreedyStringParser} if the manager
53+
* is a {@link BrigadierManagerHolder}.
54+
*
55+
* @param brigadierManager command manager
56+
* @param <C> command sender type
57+
*/
58+
@SuppressWarnings("unchecked")
59+
public static <C> void registerDefaultBrigadierMapping(final Object brigadierManager) {
60+
((CloudBrigadierManager<C, ?>) brigadierManager).registerMapping(
61+
new TypeToken<SignedGreedyStringParser<C>>() {},
62+
builder -> builder.toConstant(StringArgumentType.greedyString()).cloudSuggestions()
63+
);
64+
}
65+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2024 Incendo
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package org.incendo.cloud.minecraft.signed;
25+
26+
import org.apiguardian.api.API;
27+
import org.incendo.cloud.CommandManager;
28+
import org.incendo.cloud.brigadier.CloudBrigadierManager;
29+
import org.incendo.cloud.brigadier.argument.BrigadierMappingContributor;
30+
31+
@API(status = API.Status.INTERNAL)
32+
public final class SignedBrigadierContributor implements BrigadierMappingContributor {
33+
@Override
34+
public <C, S> void contribute(
35+
final CommandManager<C> commandManager,
36+
final CloudBrigadierManager<C, S> brigadierManager
37+
) {
38+
if (SignedArguments.adventurePresent()) {
39+
SignedStringMapper.get().registerBrigadier(commandManager, brigadierManager);
40+
}
41+
}
42+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2024 Incendo
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package org.incendo.cloud.minecraft.signed;
25+
26+
import io.leangen.geantyref.TypeToken;
27+
import java.util.concurrent.CompletableFuture;
28+
import java.util.function.BiFunction;
29+
import org.checkerframework.checker.nullness.qual.NonNull;
30+
import org.checkerframework.framework.qual.DefaultQualifier;
31+
import org.incendo.cloud.context.CommandContext;
32+
import org.incendo.cloud.context.CommandInput;
33+
import org.incendo.cloud.parser.ArgumentParseResult;
34+
import org.incendo.cloud.parser.ArgumentParser;
35+
import org.incendo.cloud.parser.ParserDescriptor;
36+
import org.incendo.cloud.parser.standard.StringParser;
37+
38+
/**
39+
* Parser for signed greedy strings.
40+
*
41+
* @param <C> command sender type
42+
*/
43+
@DefaultQualifier(NonNull.class)
44+
public final class SignedGreedyStringParser<C> implements
45+
ArgumentParser.FutureArgumentParser<C, SignedString>,
46+
ParserDescriptor<C, SignedString> {
47+
48+
private final ArgumentParser<C, SignedString> wrapped;
49+
50+
/**
51+
* Creates a {@link SignedGreedyStringParser} with the specified {@link SignedStringMapper mapper}.
52+
*
53+
* @param <C> command sender type
54+
* @param mapper signed string mapper
55+
* @return parser
56+
*/
57+
public static <C> SignedGreedyStringParser<C> signedGreedyStringParser(final SignedStringMapper mapper) {
58+
return new SignedGreedyStringParser<>(mapper);
59+
}
60+
61+
/**
62+
* Creates a {@link SignedGreedyStringParser} with the default {@link SignedStringMapper mapper}.
63+
*
64+
* @param <C> command sender type
65+
* @return parser
66+
*/
67+
public static <C> SignedGreedyStringParser<C> signedGreedyStringParser() {
68+
return signedGreedyStringParser(SignedStringMapper.get());
69+
}
70+
71+
@SuppressWarnings({"unchecked", "rawtypes"})
72+
private SignedGreedyStringParser(final SignedStringMapper mapper) {
73+
this.wrapped = StringParser.<C>greedyStringParser().parser().flatMapSuccess((BiFunction) mapper);
74+
}
75+
76+
@Override
77+
public CompletableFuture<ArgumentParseResult<SignedString>> parseFuture(
78+
final CommandContext<C> commandContext,
79+
final CommandInput commandInput
80+
) {
81+
return this.wrapped.parseFuture(commandContext, commandInput);
82+
}
83+
84+
@Override
85+
public ArgumentParser<C, SignedString> parser() {
86+
return this;
87+
}
88+
89+
@Override
90+
public TypeToken<SignedString> valueType() {
91+
return TypeToken.get(SignedString.class);
92+
}
93+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2024 Incendo
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package org.incendo.cloud.minecraft.signed;
25+
26+
import org.apiguardian.api.API;
27+
import org.incendo.cloud.parser.ParserContributor;
28+
import org.incendo.cloud.parser.ParserRegistry;
29+
30+
@API(status = API.Status.INTERNAL)
31+
public final class SignedParserContributor implements ParserContributor {
32+
@Override
33+
public <C> void contribute(final ParserRegistry<C> registry) {
34+
if (SignedArguments.adventurePresent()) {
35+
SignedStringMapper.get().registerParser(registry);
36+
}
37+
}
38+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2024 Incendo
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//
24+
package org.incendo.cloud.minecraft.signed;
25+
26+
import java.util.Objects;
27+
import net.kyori.adventure.audience.Audience;
28+
import net.kyori.adventure.chat.ChatType;
29+
import net.kyori.adventure.chat.SignedMessage;
30+
import net.kyori.adventure.text.Component;
31+
import org.checkerframework.checker.nullness.qual.NonNull;
32+
import org.checkerframework.checker.nullness.qual.Nullable;
33+
import org.checkerframework.framework.qual.DefaultQualifier;
34+
35+
@DefaultQualifier(NonNull.class)
36+
public interface SignedString {
37+
/**
38+
* Returns the raw string message.
39+
*
40+
* @return raw string message
41+
*/
42+
String string();
43+
44+
/**
45+
* Returns the signed message if the argument was actually signed by the client, otherwise {@code null}.
46+
*
47+
* @return signed message
48+
*/
49+
@Nullable SignedMessage signedMessage();
50+
51+
/**
52+
* Sends the signed message with the provided unsigned content.
53+
*
54+
* <p>If the message is not signed, it will be sent as a system message.</p>
55+
*
56+
* @param audience audience
57+
* @param chatType chat type, for if the message is signed
58+
* @param unsigned unsigned content
59+
*/
60+
void sendMessage(Audience audience, ChatType.Bound chatType, Component unsigned);
61+
62+
/**
63+
* Sends the signed message with the provided unsigned content.
64+
*
65+
* <p>If the message is not signed, it will be sent as a system message.</p>
66+
*
67+
* @param audience audience
68+
* @param chatType chat type, for if the message is signed
69+
* @param unsigned unsigned content
70+
*/
71+
default void sendMessage(final Audience audience, final ChatType chatType, final Component unsigned) {
72+
this.sendMessage(audience, chatType.bind(unsigned), unsigned);
73+
}
74+
75+
/**
76+
* Creates a new unsigned string wrapping {@code message}.
77+
*
78+
* @param message raw string message
79+
* @return unsigned string
80+
*/
81+
static SignedString unsigned(final String message) {
82+
return new Unsigned(message);
83+
}
84+
85+
final class Unsigned implements SignedString {
86+
private final String string;
87+
88+
private Unsigned(final String string) {
89+
this.string = Objects.requireNonNull(string, "string");
90+
}
91+
92+
@Override
93+
public @Nullable SignedMessage signedMessage() {
94+
return null;
95+
}
96+
97+
@Override
98+
public void sendMessage(final Audience audience, final ChatType.Bound chatType, final Component unsigned) {
99+
audience.sendMessage(unsigned);
100+
}
101+
102+
@Override
103+
public String string() {
104+
return this.string;
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)