Skip to content

Commit

Permalink
Merge branch 'master' into expose-location-parse-exception
Browse files Browse the repository at this point in the history
  • Loading branch information
Grabsky authored Oct 9, 2024
2 parents 5cbe5a1 + ad0bcea commit 505eb68
Show file tree
Hide file tree
Showing 22 changed files with 500 additions and 27 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v3
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 21
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v4
- name: Build
run: ./gradlew build
- name: Determine Status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private void registerUUID() {
final @NonNull String registryName
) {
this.mapNMS(parserType, "resource_key", type -> (ArgumentType<?>) type.getDeclaredConstructors()[0]
.newInstance(RegistryReflection.registryKey(RegistryReflection.registryByName(registryName))));
.newInstance(RegistryReflection.registryKey(registryName)));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private static final class ArgumentTypeGetterImpl implements MinecraftArgumentTy
private final Map<?, ?> byClassMap;

private ArgumentTypeGetterImpl() {
this.argumentRegistry = Suppliers.memoize(() -> RegistryReflection.registryByName("command_argument_type"));
this.argumentRegistry = Suppliers.memoize(() -> RegistryReflection.builtInRegistryByName("command_argument_type"));
try {
final Field declaredField = CraftBukkitReflection.needMCClass("commands.synchronization.ArgumentTypeInfos")
.getDeclaredFields()[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ public final class RegistryReflection {
"net.minecraft.resources.MinecraftKey",
"net.minecraft.resources.ResourceLocation"
);
private static final Class<?> RESOURCE_KEY_CLASS = CraftBukkitReflection.needNMSClassOrElse(
"ResourceKey",
"net.minecraft.resources.ResourceKey"
);
private static final Executable NEW_RESOURCE_LOCATION;
private static final Executable CREATE_REGISTRY_RESOURCE_KEY;

private RegistryReflection() {
}
Expand All @@ -63,6 +68,7 @@ private RegistryReflection() {
REGISTRY_GET = null;
REGISTRY_KEY = null;
NEW_RESOURCE_LOCATION = null;
CREATE_REGISTRY_RESOURCE_KEY = null;
} else {
registryClass = CraftBukkitReflection.firstNonNullOrThrow(
() -> "Registry",
Expand Down Expand Up @@ -95,13 +101,20 @@ private RegistryReflection() {
CraftBukkitReflection.findMethod(RESOURCE_LOCATION_CLASS, "parse", String.class), // 1.21+
CraftBukkitReflection.findMethod(RESOURCE_LOCATION_CLASS, "a", String.class)
);

CREATE_REGISTRY_RESOURCE_KEY = CraftBukkitReflection.firstNonNullOrThrow(
() -> "Could not find ResourceKey#createRegistryKey(ResourceLocation)",
CraftBukkitReflection.findMethod(RESOURCE_KEY_CLASS, "createRegistryKey", RESOURCE_LOCATION_CLASS),
CraftBukkitReflection.findMethod(RESOURCE_KEY_CLASS, "a", RESOURCE_LOCATION_CLASS)
);
}
}

public static Object registryKey(final Object registry) {
Objects.requireNonNull(REGISTRY_KEY, "REGISTRY_KEY");
public static Object registryKey(final String registryName) {
Objects.requireNonNull(CREATE_REGISTRY_RESOURCE_KEY, "CREATE_REGISTRY_RESOURCE_KEY");
try {
return REGISTRY_KEY.invoke(registry);
final Object resourceLocation = createResourceLocation(registryName);
return CraftBukkitReflection.invokeConstructorOrStaticMethod(CREATE_REGISTRY_RESOURCE_KEY, resourceLocation);
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
Expand All @@ -116,7 +129,7 @@ public static Object get(final Object registry, final String resourceLocation) {
}
}

public static Object registryByName(final String name) {
public static Object builtInRegistryByName(final String name) {
Objects.requireNonNull(REGISTRY_REGISTRY, "REGISTRY_REGISTRY");
try {
return get(REGISTRY_REGISTRY.get(null), name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ private ArgumentParser<C, BlockPredicate> createParser() {
if (GET_TAG_REGISTRY_METHOD != null) {
obj = GET_TAG_REGISTRY_METHOD.invoke(server);
} else {
obj = RegistryReflection.registryByName("block");
obj = RegistryReflection.builtInRegistryByName("block");
}
Objects.requireNonNull(CREATE_PREDICATE_METHOD, "create on BlockPredicateArgument$Result");
final Predicate<Object> predicate = (Predicate<Object>) CREATE_PREDICATE_METHOD.invoke(result, obj);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,9 @@ private static <C> Component getMessage(final ComponentCaptionFormatter<C> forma
if (throwable instanceof ParserException) {
return ((ParserException) throwable).formatCaption(formatter);
}
final Component msg = ComponentMessageThrowable.getOrConvertMessage(throwable);
final Component msg = ComponentMessageThrowable.getOrConvertMessage(
ComponentMessageThrowableConverterHolder.instance.maybeConvert(throwable)
);
return msg == null ? NULL : msg;
}

Expand Down Expand Up @@ -472,4 +474,45 @@ public interface Decorator<C> {
@NonNull Component message
);
}

/**
* Converts Throwables to ComponentMessageThrowables when possible.
*/
@API(status = API.Status.INTERNAL)
@FunctionalInterface
public interface ComponentMessageThrowableConverter {
/**
* Converts a Throwable to a ComponentMessageThrowable if possible.
*
* @param thr throwable
* @return possibly converted throwable
*/
Throwable maybeConvert(Throwable thr);
}

/**
* Default implementation and holder for {@link ComponentMessageThrowableConverter}.
*/
@API(status = API.Status.INTERNAL)
public static final class ComponentMessageThrowableConverterHolder implements ComponentMessageThrowableConverter {
private static ComponentMessageThrowableConverter instance = new ComponentMessageThrowableConverterHolder();

/**
* Replaces the converter. Mainly useful for platforms that need custom logic to transform vanilla CommandSyntaxExceptions
* into an Adventure representation.
*
* @param converter converter
*/
public static void converter(final ComponentMessageThrowableConverter converter) {
instance = Objects.requireNonNull(converter, "converter");
}

private ComponentMessageThrowableConverterHolder() {
}

@Override
public Throwable maybeConvert(final Throwable thr) {
return thr;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// 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.paper.util.sender;

import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.ConsoleCommandSender;
import org.checkerframework.checker.nullness.qual.NonNull;

/**
* Specialized variant of {@link Source}, for when the {@link CommandSourceStack#getSender() sender} is a
* {@link ConsoleCommandSender}.
*/
@SuppressWarnings("UnstableApiUsage")
public final class ConsoleSource extends GenericSource {

ConsoleSource(final CommandSourceStack commandSourceStack) {
super(commandSourceStack);
}

@Override
public @NonNull ConsoleCommandSender source() {
return (ConsoleCommandSender) super.source();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// 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.paper.util.sender;

import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.entity.Entity;
import org.checkerframework.checker.nullness.qual.NonNull;

/**
* Specialized variant of {@link Source}, for when the {@link CommandSourceStack#getSender() sender} is
* an {@link Entity}.
*/
@SuppressWarnings("UnstableApiUsage")
public class EntitySource extends GenericSource {

EntitySource(final CommandSourceStack commandSourceStack) {
super(commandSourceStack);
}

/**
* {@inheritDoc}
*
* @return the source
*/
@Override
public @NonNull Entity source() {
return (Entity) super.source();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// 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.paper.util.sender;

import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.checkerframework.checker.nullness.qual.NonNull;

@SuppressWarnings("UnstableApiUsage")
class GenericSource implements Source {

private final CommandSourceStack commandSourceStack;

GenericSource(final @NonNull CommandSourceStack commandSourceStack) {
this.commandSourceStack = commandSourceStack;
}

@Override
public final @NonNull CommandSourceStack stack() {
return this.commandSourceStack;
}

/**
* {@inheritDoc}
*
* @return the source
*/
@Override
public @NonNull CommandSender source() {
return this.commandSourceStack.getSender();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// 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.paper.util.sender;

import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.Command;
import org.incendo.cloud.SenderMapper;
import org.incendo.cloud.paper.PaperCommandManager;

/**
* A simple {@link SenderMapper} implementation designed for use with {@link PaperCommandManager}.
* Allows for easily utilizing cloud's {@link Command.Builder#senderType(Class)} utilities despite the
* single {@link CommandSourceStack} implementation provided by Paper/Minecraft.
*
* <p>The {@link #map(CommandSourceStack)} implementation performs type dispatch based on the {@link CommandSourceStack#getSender() sender},
* for example it will create a {@link PlayerSource} when {@link CommandSourceStack#getSender()} is a {@link Player}, and similar for
* {@link ConsoleSource} and {@link EntitySource}. Any other specific sender types do not currently have special handling
* and will fall back to a generic {@link Source} implementation.</p>
*/
@SuppressWarnings("UnstableApiUsage")
public final class PaperSimpleSenderMapper implements SenderMapper<CommandSourceStack, Source> {

/**
* Create a new instance of {@link PaperSimpleSenderMapper}.
*
* @return a new instance of {@link PaperSimpleSenderMapper}
*/
public static @NonNull PaperSimpleSenderMapper simpleSenderMapper() {
return new PaperSimpleSenderMapper();
}

PaperSimpleSenderMapper() {
}

@Override
public @NonNull Source map(final @NonNull CommandSourceStack base) {
CommandSender commandSender = base.getSender();

if (commandSender instanceof ConsoleCommandSender) {
return new ConsoleSource(base);
}

if (commandSender instanceof Player) {
return new PlayerSource(base);
}

if (commandSender instanceof Entity) {
return new EntitySource(base);
}

return new GenericSource(base);
}

@Override
public @NonNull CommandSourceStack reverse(final @NonNull Source mapped) {
return mapped.stack();
}
}
Loading

0 comments on commit 505eb68

Please sign in to comment.