From aa5b2ca19e535fb47029fb360d297b085b9a21f9 Mon Sep 17 00:00:00 2001 From: modmuss Date: Mon, 25 Nov 2024 18:12:59 +0000 Subject: [PATCH] Add RegistryEntryAddedCallback.allEntries (#4235) * Add RegistryEntryAddedCallback.allEntries * Pass a RegistryEntry.Reference * Remove some temp test code * Add note about recursion. --- .../registry/RegistryEntryAddedCallback.java | 37 +++++++ .../sync/RegistryEntryAddedCallbackTest.java | 98 +++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 fabric-registry-sync-v0/src/test/java/net/fabricmc/fabric/test/registry/sync/RegistryEntryAddedCallbackTest.java diff --git a/fabric-registry-sync-v0/src/main/java/net/fabricmc/fabric/api/event/registry/RegistryEntryAddedCallback.java b/fabric-registry-sync-v0/src/main/java/net/fabricmc/fabric/api/event/registry/RegistryEntryAddedCallback.java index dc6cf7056d..d4f7aba294 100644 --- a/fabric-registry-sync-v0/src/main/java/net/fabricmc/fabric/api/event/registry/RegistryEntryAddedCallback.java +++ b/fabric-registry-sync-v0/src/main/java/net/fabricmc/fabric/api/event/registry/RegistryEntryAddedCallback.java @@ -16,17 +16,54 @@ package net.fabricmc.fabric.api.event.registry; +import java.util.function.Consumer; + import net.minecraft.registry.Registry; +import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.util.Identifier; import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.impl.registry.sync.ListenableRegistry; +/** + * An event for when an entry is added to a registry. + * + * @param the type of the entry within the registry + */ @FunctionalInterface public interface RegistryEntryAddedCallback { + /** + * Called when a new entry is added to the registry. + * + * @param rawId the raw id of the entry + * @param id the identifier of the entry + * @param object the object that was added + */ void onEntryAdded(int rawId, Identifier id, T object); + /** + * Get the {@link Event} for the {@link RegistryEntryAddedCallback} for the given registry. + * + * @param registry the registry to get the event for + * @return the event + */ static Event> event(Registry registry) { return ListenableRegistry.get(registry).fabric_getAddObjectEvent(); } + + /** + * Register a callback for all present and future entries in the registry. + * + *

Note: The callback is recursive and will be invoked for anything registered within the callback itself. + * + * @param registry the registry to listen to + * @param consumer the callback that accepts a {@link RegistryEntry.Reference} + */ + static void allEntries(Registry registry, Consumer> consumer) { + event(registry).register((rawId, id, object) -> consumer.accept(registry.getEntry(id).orElseThrow())); + // Call the consumer for all existing entries, after registering the callback. + // This way if the callback registers a new entry, it will also be called for that entry. + // It is also important to take a copy of the registry with .toList() to avoid concurrent modification exceptions if the callback modifies the registry. + registry.streamEntries().toList().forEach(consumer); + } } diff --git a/fabric-registry-sync-v0/src/test/java/net/fabricmc/fabric/test/registry/sync/RegistryEntryAddedCallbackTest.java b/fabric-registry-sync-v0/src/test/java/net/fabricmc/fabric/test/registry/sync/RegistryEntryAddedCallbackTest.java new file mode 100644 index 0000000000..16f3bc0e14 --- /dev/null +++ b/fabric-registry-sync-v0/src/test/java/net/fabricmc/fabric/test/registry/sync/RegistryEntryAddedCallbackTest.java @@ -0,0 +1,98 @@ +/* + * 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.test.registry.sync; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import net.minecraft.Bootstrap; +import net.minecraft.SharedConstants; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.util.Identifier; + +import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; +import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback; + +public class RegistryEntryAddedCallbackTest { + @Mock + private Consumer> mockConsumer; + + @Captor + private ArgumentCaptor> captor; + + @BeforeAll + static void beforeAll() { + SharedConstants.createGameVersion(); + Bootstrap.initialize(); + } + + @BeforeEach + void beforeEach() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testEntryAddedCallback() { + RegistryKey> testRegistryKey = RegistryKey.ofRegistry(id(UUID.randomUUID().toString())); + SimpleRegistry testRegistry = FabricRegistryBuilder.createSimple(testRegistryKey) + .buildAndRegister(); + + Registry.register(testRegistry, id("before"), "before"); + RegistryEntryAddedCallback.allEntries(testRegistry, mockConsumer); + + // Test that the callback can register new entries. + RegistryEntryAddedCallback.allEntries(testRegistry, s -> { + if (s.value().equals("before")) { + Registry.register(testRegistry, id("during"), "during"); + } + }); + + Registry.register(testRegistry, id("after"), "after"); + + verify(mockConsumer, times(3)).accept(captor.capture()); + + List values = captor.getAllValues() + .stream() + .map(RegistryEntry.Reference::value) + .toList(); + + assertEquals(3, values.size()); + assertEquals("before", values.getFirst()); + assertEquals("during", values.get(1)); + assertEquals("after", values.get(2)); + } + + private static Identifier id(String path) { + return Identifier.of("registry_sync_test_entry_added_test", path); + } +}