-
Notifications
You must be signed in to change notification settings - Fork 506
Event handling for custom click actions #4740
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
base: 1.21.7
Are you sure you want to change the base?
Changes from all commits
a3cd3b7
f877689
70d352e
7159fa7
3db85ce
5e6b483
745831a
517bf0c
68fac72
65c8eba
6819567
519d68e
9d4734b
52caa7d
4049294
25cf3a7
66a1dc4
d2ca80f
2503992
4fc69f7
05cc21b
3f593f0
3f9130e
2ac4575
2c4a29f
991c6bd
e25b3f6
150a4d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package net.fabricmc.fabric.api.networking.v1; | ||
|
|
||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
|
|
||
| import net.minecraft.nbt.NbtElement; | ||
| import net.minecraft.util.Identifier; | ||
|
|
||
| import net.fabricmc.fabric.api.event.Event; | ||
| import net.fabricmc.fabric.impl.networking.CustomClickActionsRegistry; | ||
|
|
||
| /** | ||
| * Events for listening to {@linkplain net.minecraft.text.ClickEvent.Custom custom click actions}, such as those invoked | ||
| * from a custom dialog. | ||
| */ | ||
| public final class CustomClickActionEvents { | ||
| /** | ||
| * Gets an event that is invoked on the server when a custom click event is received. The returned event will only | ||
| * be invoked when a click event is received with the given ID. | ||
| * | ||
| * @param id The of the ID click event to listen to. | ||
| * @return Returns an event that will be invoked when a click event with the given ID is received during the PLAY | ||
| * phase. | ||
| */ | ||
| public static Event<CustomClickActionReceived> customClickActionReceivedEvent(Identifier id) { | ||
| Objects.requireNonNull(id, "ID cannot be null"); | ||
| return CustomClickActionsRegistry.getOrCreateActionEvent(id); | ||
| } | ||
|
|
||
| @FunctionalInterface | ||
| public interface CustomClickActionReceived { | ||
| /** | ||
| * Handles a custom click event on the server from a given context. | ||
| * | ||
| * @param context The context of the event, contains the handler responsible for the action and the payload. | ||
| * Will either be an instance of {@link CustomClickEventContext.Play} or | ||
| * {@link CustomClickEventContext.Configuration}, depending on when this event was invoked. This | ||
| * can be checked using switch-statement pattern matching (see testmod if unfamiliar with this | ||
| * syntax). | ||
| * @param payload The payload received with this event. If no payload is received, then this payload will be | ||
| * empty. | ||
| */ | ||
| void handleCustomClickAction(CustomClickEventContext context, Optional<NbtElement> payload); | ||
| } | ||
|
|
||
| private CustomClickActionEvents() { | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package net.fabricmc.fabric.api.networking.v1; | ||
|
|
||
| import org.jetbrains.annotations.ApiStatus; | ||
|
|
||
| import net.minecraft.server.network.ServerCommonNetworkHandler; | ||
| import net.minecraft.server.network.ServerConfigurationNetworkHandler; | ||
| import net.minecraft.server.network.ServerPlayNetworkHandler; | ||
| import net.minecraft.server.network.ServerPlayerEntity; | ||
|
|
||
| /** | ||
| * Contains data about a {@linkplain net.minecraft.text.ClickEvent.Custom custom click event} when one is received on | ||
| * the server. Custom click events may be received either during the PLAY or in CONFIGURATION phases. | ||
| */ | ||
| public sealed interface CustomClickEventContext permits CustomClickEventContext.Play, CustomClickEventContext.Configuration { | ||
| /** | ||
| * The handler responsible for the event. | ||
| */ | ||
| ServerCommonNetworkHandler handler(); | ||
|
|
||
| /** | ||
| * The context data when a custom click event is received during the PLAY phase on the server. | ||
| */ | ||
| @ApiStatus.NonExtendable | ||
| non-sealed interface Play extends CustomClickEventContext { | ||
TheDeathlyCow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * The play handler responsible for the event. | ||
| */ | ||
| @Override | ||
| ServerPlayNetworkHandler handler(); | ||
TheDeathlyCow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * The player responsible for the event. | ||
| * | ||
| * @return Returns exactly the same player as calling {@link ServerPlayNetworkHandler#getPlayer()} on the result | ||
| * of {@link #handler()}. | ||
| */ | ||
| default ServerPlayerEntity player() { | ||
| return handler().getPlayer(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * The context data when a custom click event is received during the CONFIGURATION phase on the server. | ||
| */ | ||
| @ApiStatus.NonExtendable | ||
| non-sealed interface Configuration extends CustomClickEventContext { | ||
| /** | ||
| * The configuration handler responsible for the event. | ||
| */ | ||
| @Override | ||
| ServerConfigurationNetworkHandler handler(); | ||
TheDeathlyCow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| import java.util.HashSet; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
| import java.util.Set; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| import java.util.concurrent.locks.Lock; | ||
|
|
@@ -30,6 +31,7 @@ | |
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import net.minecraft.nbt.NbtElement; | ||
| import net.minecraft.util.Identifier; | ||
|
|
||
| /** | ||
|
|
@@ -172,4 +174,7 @@ public final void handleDisconnect() { | |
| * @return whether the channel is reserved | ||
| */ | ||
| protected abstract boolean isReservedChannel(Identifier channelName); | ||
|
|
||
| public void invokeCustomClickActionEvent(Identifier id, Optional<NbtElement> payload) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the default impl for this throw? Something is wrong if this is called but not overriden.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some other methods in the addon that are just declared as abstract but have empty implementations (like |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package net.fabricmc.fabric.impl.networking; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Optional; | ||
|
|
||
| import net.minecraft.nbt.NbtElement; | ||
| import net.minecraft.util.Identifier; | ||
|
|
||
| import net.fabricmc.fabric.api.event.Event; | ||
| import net.fabricmc.fabric.api.event.EventFactory; | ||
| import net.fabricmc.fabric.api.networking.v1.CustomClickActionEvents; | ||
| import net.fabricmc.fabric.api.networking.v1.CustomClickEventContext; | ||
|
|
||
| public final class CustomClickActionsRegistry { | ||
| private static final Map<Identifier, Event<CustomClickActionEvents.CustomClickActionReceived>> REGISTRY = new HashMap<>(); | ||
|
|
||
| public static Event<CustomClickActionEvents.CustomClickActionReceived> getOrCreateActionEvent(Identifier id) { | ||
| return REGISTRY.computeIfAbsent(id, idx -> createNewEvent()); | ||
| } | ||
|
|
||
| public static void invokeListenerEvent(Identifier id, CustomClickEventContext context, Optional<NbtElement> payload) { | ||
| Event<CustomClickActionEvents.CustomClickActionReceived> event = REGISTRY.get(id); | ||
|
|
||
| if (event != null) { | ||
| event.invoker().handleCustomClickAction(context, payload); | ||
| } | ||
| } | ||
|
|
||
| private static Event<CustomClickActionEvents.CustomClickActionReceived> createNewEvent() { | ||
| return EventFactory.createArrayBacked( | ||
| CustomClickActionEvents.CustomClickActionReceived.class, | ||
| listeners -> (context, payload) -> { | ||
| for (CustomClickActionEvents.CustomClickActionReceived listener : listeners) { | ||
| listener.handleCustomClickAction(context, payload); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| private CustomClickActionsRegistry() { | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /* | ||
| * Copyright (c) 2016, 2017, 2018, 2019 FabricMC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package net.fabricmc.fabric.mixin.networking; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| import com.llamalad7.mixinextras.injector.wrapoperation.Operation; | ||
| import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; | ||
| import com.llamalad7.mixinextras.sugar.Local; | ||
| import org.spongepowered.asm.mixin.Mixin; | ||
| import org.spongepowered.asm.mixin.injection.At; | ||
|
|
||
| import net.minecraft.block.entity.SignBlockEntity; | ||
| import net.minecraft.entity.player.PlayerEntity; | ||
| import net.minecraft.nbt.NbtElement; | ||
| import net.minecraft.server.MinecraftServer; | ||
| import net.minecraft.server.network.ServerPlayerEntity; | ||
| import net.minecraft.util.Identifier; | ||
|
|
||
| import net.fabricmc.fabric.api.networking.v1.CustomClickEventContext; | ||
| import net.fabricmc.fabric.impl.networking.CustomClickActionsRegistry; | ||
| import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon; | ||
|
|
||
| @Mixin(SignBlockEntity.class) | ||
| public class SignBlockEntityMixin { | ||
| @WrapOperation( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why a wrap operation and not just an inject?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. an |
||
| method = "runCommandClickEvent", | ||
| at = @At( | ||
| value = "INVOKE", | ||
| target = "Lnet/minecraft/server/MinecraftServer;handleCustomClickAction(Lnet/minecraft/util/Identifier;Ljava/util/Optional;)V" | ||
| ) | ||
| ) | ||
| private void hookCustomClickActionListener(MinecraftServer instance, Identifier id, Optional<NbtElement> payload, Operation<Void> original, @Local(argsOnly = true) PlayerEntity player) { | ||
| original.call(instance, id, payload); | ||
|
|
||
| if (player instanceof ServerPlayerEntity serverPlayer) { | ||
| CustomClickEventContext context = new ServerPlayNetworkAddon.PlayContextImpl(serverPlayer.networkHandler); | ||
| CustomClickActionsRegistry.invokeListenerEvent(id, context, payload); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.