Add RegistryEntryAddedCallback.allEntries (#4235)

* Add RegistryEntryAddedCallback.allEntries

* Pass a RegistryEntry.Reference

* Remove some temp test code

* Add note about recursion.

(cherry picked from commit aa5b2ca19e)
This commit is contained in:
modmuss 2024-11-25 18:12:59 +00:00 committed by modmuss50
parent 54a41b1cc9
commit 2758bfbf66
2 changed files with 135 additions and 0 deletions

View file

@ -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 <T> the type of the entry within the registry
*/
@FunctionalInterface
public interface RegistryEntryAddedCallback<T> {
/**
* 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 <T> Event<RegistryEntryAddedCallback<T>> event(Registry<T> registry) {
return ListenableRegistry.get(registry).fabric_getAddObjectEvent();
}
/**
* Register a callback for all present and future entries in the registry.
*
* <p>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 <T> void allEntries(Registry<T> registry, Consumer<RegistryEntry.Reference<T>> 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);
}
}

View file

@ -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<RegistryEntry.Reference<String>> mockConsumer;
@Captor
private ArgumentCaptor<RegistryEntry.Reference<String>> captor;
@BeforeAll
static void beforeAll() {
SharedConstants.createGameVersion();
Bootstrap.initialize();
}
@BeforeEach
void beforeEach() {
MockitoAnnotations.openMocks(this);
}
@Test
void testEntryAddedCallback() {
RegistryKey<Registry<String>> testRegistryKey = RegistryKey.ofRegistry(id(UUID.randomUUID().toString()));
SimpleRegistry<String> 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<String> 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);
}
}