-
Notifications
You must be signed in to change notification settings - Fork 508
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 20 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,86 @@ | ||
| /* | ||
| * 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 net.minecraft.util.Identifier; | ||
|
|
||
| import net.fabricmc.fabric.api.event.Event; | ||
| import net.fabricmc.fabric.api.event.EventFactory; | ||
| 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 { | ||
| /** | ||
| * Invoked when any custom click action is received. If you are only interested in listening to events with a | ||
| * specific ID, use {@link #customClickActionReceivedEvent(Identifier)}. | ||
| */ | ||
| public static final Event<CustomClickActionReceived> ON_ANY_CUSTOM_CLICK_ACTION_RECEIVED = EventFactory.createArrayBacked( | ||
| CustomClickActionReceived.class, | ||
| listeners -> (id, context) -> { | ||
| for (CustomClickActionReceived listener : listeners) { | ||
| listener.handleCustomClickAction(id, context); | ||
| } | ||
| } | ||
| ); | ||
|
|
||
| /** | ||
| * Gets an event that is invoked on the server when a custom click event is received during the PLAY phase. 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<NamedCustomClickActionReceived> customClickActionReceivedEvent(Identifier id) { | ||
| Objects.requireNonNull(id, "ID cannot be null"); | ||
| return CustomClickActionsRegistry.getOrCreateActionEvent(id); | ||
| } | ||
|
|
||
| @FunctionalInterface | ||
| public interface CustomClickActionReceived { | ||
| /** | ||
| * Handles any 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. | ||
| */ | ||
| void handleCustomClickAction(Identifier id, CustomClickEventContext context); | ||
| } | ||
|
|
||
| @FunctionalInterface | ||
| public interface NamedCustomClickActionReceived { | ||
| /** | ||
| * Handles a custom click event on the server from a given context. | ||
| * | ||
| * <p>This event only works for click actions with a single ID registered with {@link #customClickActionReceivedEvent(Identifier)}, | ||
| * for generic events see {@link #ON_ANY_CUSTOM_CLICK_ACTION_RECEIVED}. | ||
| * | ||
| * @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. | ||
| */ | ||
| void handleCustomClickAction(CustomClickEventContext context); | ||
| } | ||
|
|
||
| private CustomClickActionEvents() { | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| /* | ||
| * 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.Optional; | ||
|
|
||
| import org.jetbrains.annotations.ApiStatus; | ||
|
|
||
| import net.minecraft.nbt.NbtElement; | ||
| 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. If the event is | ||
| * received during PLAY, then a player entity will be provided. | ||
| */ | ||
| public sealed interface CustomClickEventContext permits CustomClickEventContext.Play, CustomClickEventContext.Configuration { | ||
modmuss50 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * The handler responsible for the event. | ||
| */ | ||
| ServerCommonNetworkHandler handler(); | ||
|
|
||
| /** | ||
| * The player entity responsible for the event, if in the play phase. | ||
| */ | ||
| Optional<ServerPlayerEntity> player(); | ||
TheDeathlyCow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * The payload received with this event. If no payload is received, then this payload will be {@code null}. | ||
TheDeathlyCow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| */ | ||
| Optional<NbtElement> payload(); | ||
TheDeathlyCow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * 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 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
|
||
|
|
||
| /** | ||
| * The configuration phase is too early for an entity to have been created, so an entity is never returned. | ||
| * | ||
| * @return Returns an empty optional. | ||
| */ | ||
| default Optional<ServerPlayerEntity> player() { | ||
TheDeathlyCow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return Optional.empty(); | ||
| } | ||
| } | ||
| } | ||
| 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,81 @@ | ||
| /* | ||
| * 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.server.network.ServerConfigurationNetworkHandler; | ||
| import net.minecraft.server.network.ServerPlayNetworkHandler; | ||
| import net.minecraft.server.network.ServerPlayerEntity; | ||
| 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.NamedCustomClickActionReceived>> REGISTRY = new HashMap<>(); | ||
|
|
||
| public static Event<CustomClickActionEvents.NamedCustomClickActionReceived> getOrCreateActionEvent(Identifier id) { | ||
| return REGISTRY.computeIfAbsent( | ||
| id, | ||
| idx -> { | ||
TheDeathlyCow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return EventFactory.createArrayBacked( | ||
| CustomClickActionEvents.NamedCustomClickActionReceived.class, | ||
| listeners -> context -> { | ||
| for (CustomClickActionEvents.NamedCustomClickActionReceived listener : listeners) { | ||
| listener.handleCustomClickAction(context); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| public static void invokeListenerEvent(Identifier id, CustomClickEventContext context) { | ||
| CustomClickActionEvents.ON_ANY_CUSTOM_CLICK_ACTION_RECEIVED.invoker().handleCustomClickAction(id, context); | ||
|
|
||
| Event<CustomClickActionEvents.NamedCustomClickActionReceived> event = REGISTRY.get(id); | ||
|
|
||
| if (event != null) { | ||
| event.invoker().handleCustomClickAction(context); | ||
| } | ||
| } | ||
|
|
||
| public record PlayContextImpl( | ||
TheDeathlyCow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ServerPlayNetworkHandler handler, | ||
| Optional<ServerPlayerEntity> player, | ||
| Optional<NbtElement> payload | ||
| ) implements CustomClickEventContext.Play { | ||
| public PlayContextImpl(ServerPlayNetworkHandler handler, ServerPlayerEntity player, Optional<NbtElement> payload) { | ||
| this(handler, Optional.of(player), payload); | ||
| } | ||
| } | ||
|
|
||
| public record ConfigurationContextImpl( | ||
| ServerConfigurationNetworkHandler handler, | ||
| Optional<NbtElement> payload | ||
| ) implements CustomClickEventContext.Configuration { | ||
| } | ||
|
|
||
| private CustomClickActionsRegistry() { | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the example use case for this? Generally it seems like you should only care about your own specific click actions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a usecase where I tracked where a user was in a particular flow using the ID of the event. But that is probably not a good way to handle that, adding to payload is better (eg with the
additionsfield). Will remove.