Skip to content

Commit

Permalink
feat: Implement SignedStringMapper (#28)
Browse files Browse the repository at this point in the history
* Implement signed string mapper on fabric

* fix service file name

* Fix dependencies

* Update for contributor api

* fabric: Add signed argument test

* remove mavenLocal
  • Loading branch information
jpenilla authored Mar 13, 2024
1 parent 859adb0 commit a0255f0
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 1 deletion.
9 changes: 9 additions & 0 deletions cloud-fabric/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ dependencies {

api(platform(libs.cloud.bom))
api(libs.cloud.core)
api(platform(libs.cloud.minecraft.bom))
api(libs.cloud.brigadier)

compileOnly(libs.cloud.minecraft.signed.arguments)
modCompileOnly(libs.adventureFabric)

offlineLinkedJavadoc(project(":cloud-minecraft-modded-common"))
localRuntime(project(":cloud-minecraft-modded-common", configuration = "namedElements"))
compileOnly(project(":cloud-minecraft-modded-common", configuration = "namedElements"))
Expand Down Expand Up @@ -88,6 +92,11 @@ val testmod: SourceSet by sourceSets.creating {
dependencies.add(implementationConfigurationName, main.output)
}

dependencies {
localRuntime(libs.cloud.minecraft.signed.arguments)
modLocalRuntime(libs.adventureFabric)
}

val testmodJar by tasks.registering(Jar::class) {
archiveClassifier.set("testmod-dev")
group = LifecycleBasePlugin.BUILD_GROUP
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// 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.fabric.internal;

import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.chat.ChatType;
import net.kyori.adventure.chat.SignedMessage;
import net.kyori.adventure.platform.fabric.impl.NonWrappingComponentSerializer;
import net.kyori.adventure.text.Component;
import net.minecraft.network.chat.PlayerChatMessage;
import org.apiguardian.api.API;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.incendo.cloud.minecraft.modded.internal.ModdedSignedStringMapper;
import org.incendo.cloud.minecraft.signed.SignedString;

@API(status = API.Status.INTERNAL)
public final class FabricSignedStringFactory implements ModdedSignedStringMapper.SignedStringFactory {
@Override
public SignedString create(final String str, final PlayerChatMessage signedMessage) {
return new SignedStringImpl(str, signedMessage);
}

private record SignedStringImpl(String string, PlayerChatMessage rawSignedMessage) implements SignedString {

@Override
public @Nullable SignedMessage signedMessage() {
return cast(this.rawSignedMessage);
}

@SuppressWarnings("DataFlowIssue")
private static SignedMessage cast(final PlayerChatMessage message) {
return (SignedMessage) ((Object) message);
}

@Override
public void sendMessage(final Audience audience, final ChatType.Bound chatType, final Component unsigned) {
final net.minecraft.network.chat.Component nativeComponent = NonWrappingComponentSerializer.INSTANCE.serialize(unsigned);
final PlayerChatMessage playerChatMessage = this.rawSignedMessage.withUnsignedContent(nativeComponent);
audience.sendMessage(cast(playerChatMessage), chatType);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.incendo.cloud.fabric.internal.FabricSignedStringFactory
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.api.metadata.Person;
import net.kyori.adventure.chat.ChatType;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.platform.fabric.AdventureCommandSourceStack;
import net.kyori.adventure.platform.fabric.FabricServerAudiences;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.item.ItemInput;
Expand Down Expand Up @@ -60,6 +65,7 @@
import org.incendo.cloud.minecraft.modded.data.MultiplePlayerSelector;
import org.incendo.cloud.minecraft.modded.parser.NamedColorParser;
import org.incendo.cloud.minecraft.modded.parser.RegistryEntryParser;
import org.incendo.cloud.minecraft.signed.SignedString;
import org.incendo.cloud.parser.ArgumentParseResult;
import org.incendo.cloud.parser.ParserDescriptor;
import org.incendo.cloud.suggestion.Suggestion;
Expand All @@ -69,6 +75,7 @@
import static org.incendo.cloud.minecraft.modded.parser.VanillaArgumentParsers.itemInput;
import static org.incendo.cloud.minecraft.modded.parser.VanillaArgumentParsers.multiplePlayerSelectorParser;
import static org.incendo.cloud.minecraft.modded.parser.VanillaArgumentParsers.vec3Parser;
import static org.incendo.cloud.minecraft.signed.SignedGreedyStringParser.signedGreedyStringParser;
import static org.incendo.cloud.parser.standard.IntegerParser.integerParser;
import static org.incendo.cloud.parser.standard.StringParser.stringParser;

Expand Down Expand Up @@ -266,6 +273,29 @@ public void onInitialize() {
target.teleportToWithTicket(location.x(), location.y(), location.z()));
}));

manager.command(base.literal("signed")
.required("message", signedGreedyStringParser())
.handler(ctx -> {
final AdventureCommandSourceStack audience =
FabricServerAudiences.of(ctx.sender().getServer()).audience(ctx.sender());

final SignedString message = ctx.get("message");

final net.kyori.adventure.text.Component mini =
MiniMessage.miniMessage().deserialize(message.string());

message.sendMessage(
audience,
ChatType.SAY_COMMAND.bind(
audience.get(Identity.DISPLAY_NAME).orElseGet(() ->
audience.get(Identity.NAME).map(net.kyori.adventure.text.Component::text)
.orElseGet(() -> net.kyori.adventure.text.Component.text("Command sender"))),
mini
),
mini
);
}));

manager.command(base.literal("gotochunk")
.permission("cloud.gotochunk")
.required("chunk_position", columnPosParser())
Expand Down
3 changes: 3 additions & 0 deletions cloud-minecraft-modded-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ dependencies {

api(platform(libs.cloud.bom))
api(libs.cloud.core)
api(platform(libs.cloud.minecraft.bom))
api(libs.cloud.brigadier)
compileOnly(libs.cloud.minecraft.signed.arguments)
compileOnly(libs.adventureApi)
}

tasks.withType(AbstractRemapJarTask::class).configureEach {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// 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.minecraft.modded.internal;

import io.leangen.geantyref.TypeToken;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import net.kyori.adventure.util.Services;
import net.minecraft.commands.CommandSigningContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.MessageArgument;
import net.minecraft.network.chat.PlayerChatMessage;
import org.apiguardian.api.API;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.brigadier.CloudBrigadierManager;
import org.incendo.cloud.brigadier.parser.WrappedBrigadierParser;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.minecraft.signed.SignedGreedyStringParser;
import org.incendo.cloud.minecraft.signed.SignedString;
import org.incendo.cloud.minecraft.signed.SignedStringMapper;
import org.incendo.cloud.parser.ArgumentParseResult;

@API(status = API.Status.INTERNAL)
public final class ModdedSignedStringMapper implements SignedStringMapper {
private final SignedStringFactory factory;

/**
* Creates a new mapper.
*/
public ModdedSignedStringMapper() {
this.factory = Services.service(SignedStringFactory.class)
.orElseThrow(() -> new IllegalStateException("Could not locate " + SignedStringFactory.class));
}

@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void registerBrigadier(final CommandManager<?> commandManager, final Object brigManager) {
registerBrigadier((CloudBrigadierManager) brigManager);
}

private static <C> void registerBrigadier(final CloudBrigadierManager<C, ?> manager) {
manager.registerMapping(
new TypeToken<SignedGreedyStringParser<C>>() {},
builder -> builder.toConstant(MessageArgument.message()).cloudSuggestions()
);
}

@Override
public CompletableFuture<ArgumentParseResult<SignedString>> apply(
final CommandContext<?> ctx,
final String str
) {
final CommandSourceStack stack = ctx.get(WrappedBrigadierParser.COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER);
final Map<String, PlayerChatMessage> signedArgs;
if (stack.getSigningContext() instanceof CommandSigningContext.SignedArguments) {
signedArgs = ((CommandSigningContext.SignedArguments) stack.getSigningContext()).arguments();
} else {
return ArgumentParseResult.successFuture(SignedString.unsigned(str));
}
if (signedArgs.size() != 1) {
throw new IllegalStateException();
}

return ArgumentParseResult.successFuture(
this.factory.create(
str,
signedArgs.entrySet().iterator().next().getValue()
)
);
}

public interface SignedStringFactory {
/**
* Creates a signed string.
*
* @param str raw string
* @param signedMessage signed message
* @return new signed string
*/
SignedString create(String str, PlayerChatMessage signedMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.incendo.cloud.minecraft.modded.internal.ModdedSignedStringMapper
1 change: 1 addition & 0 deletions cloud-neoforge/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies {

api(platform(libs.cloud.bom))
api(libs.cloud.core)
api(platform(libs.cloud.minecraft.bom))
api(libs.cloud.brigadier)
offlineLinkedJavadoc(project(":cloud-minecraft-modded-common"))
api(project(":cloud-minecraft-modded-common", configuration = "namedElements"))
Expand Down
6 changes: 5 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ktlint = "0.50.0"
errorprone = "2.26.1"

cloud = "2.0.0-beta.4"
cloudMinecraft = "2.0.0-beta.5"
cloudMinecraft = "2.0.0-SNAPSHOT"

immutables = "2.10.1"

Expand All @@ -21,8 +21,12 @@ stylecheck = { module = "ca.stellardrift:stylecheck", version.ref = "stylecheck"
errorproneCore = { group = "com.google.errorprone", name = "error_prone_core", version.ref = "errorprone" }

cloud-bom = { module = "org.incendo:cloud-bom", version.ref = "cloud" }
cloud-minecraft-bom = { module = "org.incendo:cloud-minecraft-bom", version.ref = "cloudMinecraft" }
cloud-core = { module = "org.incendo:cloud-core", version.ref = "cloud" }
cloud-brigadier = { module = "org.incendo:cloud-brigadier", version.ref = "cloudMinecraft" }
cloud-minecraft-signed-arguments = { module = "org.incendo:cloud-minecraft-signed-arguments", version.ref = "cloudMinecraft" }
adventureApi = { group = "net.kyori", name = "adventure-api", version = "4.15.0" }
adventureFabric = "net.kyori:adventure-platform-fabric:5.12.0"

immutables = { group = "org.immutables", name = "value", version.ref = "immutables" }
immutablesAnnotate = { group = "org.immutables", name = "annotate", version.ref = "immutables" }
Expand Down

0 comments on commit a0255f0

Please sign in to comment.