Registry event API + loot table fix ()

This commit is contained in:
Adrian Siekierka 2019-05-28 18:28:00 +02:00 committed by GitHub
parent 6263b147d3
commit 99245497ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 428 additions and 228 deletions

View file

@ -1,5 +1,5 @@
archivesBaseName = "fabric-registry-sync-v0"
version = getSubprojectVersion(project, "0.1.2")
version = getSubprojectVersion(project, "0.2.0")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')

View file

@ -14,11 +14,23 @@
* limitations under the License.
*/
package net.fabricmc.fabric.impl.registry.callbacks;
package net.fabricmc.fabric.api.event.registry;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
@FunctionalInterface
public interface RegistryPostRegisterCallback<T> extends RegistryCallback<T> {
void onPostRegister(int rawId, Identifier id, T object);
public interface RegistryEntryAddedCallback<T> {
void onEntryAdded(int rawId, Identifier id, T object);
static <T> Event<RegistryEntryAddedCallback<T>> event(Registry<T> registry) {
if (!(registry instanceof ListenableRegistry)) {
throw new IllegalArgumentException("Unsupported registry: " + registry.getClass().getName());
}
//noinspection unchecked
return (Event<RegistryEntryAddedCallback<T>>) ((ListenableRegistry) registry).fabric_getAddObjectEvent();
}
}

View file

@ -0,0 +1,36 @@
/*
* 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.event.registry;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
@FunctionalInterface
public interface RegistryEntryRemovedCallback<T> {
void onEntryRemoved(int rawId, Identifier id, T object);
static <T> Event<RegistryEntryRemovedCallback<T>> event(Registry<T> registry) {
if (!(registry instanceof ListenableRegistry)) {
throw new IllegalArgumentException("Unsupported registry: " + registry.getClass().getName());
}
//noinspection unchecked
return (Event<RegistryEntryRemovedCallback<T>>) ((ListenableRegistry) registry).fabric_getRemoveObjectEvent();
}
}

View file

@ -0,0 +1,57 @@
/*
* 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.event.registry;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
/**
* The remapping process functions as follows:
*
* - RegistryEntryRemovedCallbacks are called to remove any objects culled in the process, with the old numeric ID.
* - RegistryIdRemapCallback is emitted to allow remapping the IDs of objects still present.
* - RegistryEntryAddedCallbacks are called to add any objects added in the process, with the new numeric ID.
*
* RegistryIdRemapCallback is called on every remapping operation, if you want to do your own processing in one swoop
* (say, rebuild the ID map from scratch).
*
* Generally speaking, a remap can only cause object *removals*; object *additions* are necessary to reverse remaps.
*
* @param <T> The registry type.
*/
@FunctionalInterface
public interface RegistryIdRemapCallback<T> {
void onRemap(RemapState<T> state);
interface RemapState<T> {
Int2IntMap getRawIdChangeMap();
Identifier getIdFromOld(int oldRawId);
Identifier getIdFromNew(int newRawId);
}
static <T> Event<RegistryIdRemapCallback<T>> event(Registry<T> registry) {
if (!(registry instanceof ListenableRegistry)) {
throw new IllegalArgumentException("Unsupported registry: " + registry.getClass().getName());
}
//noinspection unchecked
return (Event<RegistryIdRemapCallback<T>>) ((ListenableRegistry) registry).fabric_getRemapEvent();
}
}

View file

@ -17,12 +17,12 @@
package net.fabricmc.fabric.impl.registry;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPostRegisterCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreRegisterCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryRemovedCallback;
public interface ListenableRegistry<T> {
Event<RegistryPreClearCallback<T>> getPreClearEvent();
Event<RegistryPreRegisterCallback<T>> getPreRegisterEvent();
Event<RegistryPostRegisterCallback<T>> getPostRegisterEvent();
Event<RegistryEntryAddedCallback<T>> fabric_getAddObjectEvent();
Event<RegistryEntryRemovedCallback<T>> fabric_getRemoveObjectEvent();
Event<RegistryIdRemapCallback<T>> fabric_getRemapEvent();
}

View file

@ -0,0 +1,54 @@
/*
* 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.registry;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class RemapStateImpl<T> implements RegistryIdRemapCallback.RemapState<T> {
private final Int2IntMap rawIdChangeMap;
private final Int2ObjectMap<Identifier> newIdMap;
public RemapStateImpl(Registry<T> registry, Int2IntMap rawIdChangeMap) {
this.rawIdChangeMap = rawIdChangeMap;
this.newIdMap = new Int2ObjectOpenHashMap<>();
for (Int2IntMap.Entry entry : rawIdChangeMap.int2IntEntrySet()) {
Identifier id = registry.getId(registry.get(entry.getIntValue()));
newIdMap.put(entry.getIntValue(), id);
}
}
@Override
public Int2IntMap getRawIdChangeMap() {
return rawIdChangeMap;
}
@Override
public Identifier getIdFromOld(int oldRawId) {
return newIdMap.get(rawIdChangeMap.getOrDefault(oldRawId, -1));
}
@Override
public Identifier getIdFromNew(int newRawId) {
return newIdMap.get(newRawId);
}
}

View file

@ -16,8 +16,12 @@
package net.fabricmc.fabric.impl.registry;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
public interface RemovableIdList<T> {
void clear();
void remove(T o);
void removeId(int i);
void fabric_clear();
void fabric_remove(T o);
void fabric_removeId(int i);
void fabric_remapId(int from, int to);
void fabric_remapIds(Int2IntMap map);
}

View file

@ -1,20 +0,0 @@
/*
* 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.registry.callbacks;
public interface RegistryCallback<T> {
}

View file

@ -1,22 +0,0 @@
/*
* 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.registry.callbacks;
@FunctionalInterface
public interface RegistryPreClearCallback<T> extends RegistryCallback<T> {
void onPreClear();
}

View file

@ -1,24 +0,0 @@
/*
* 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.registry.callbacks;
import net.minecraft.util.Identifier;
@FunctionalInterface
public interface RegistryPreRegisterCallback<T> extends RegistryCallback<T> {
void onPreRegister(int rawId, Identifier id, T object, boolean isNewToRegistry);
}

View file

@ -16,10 +16,10 @@
package net.fabricmc.fabric.impl.registry.trackers;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryRemovedCallback;
import net.fabricmc.fabric.impl.registry.RemovableIdList;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreRegisterCallback;
import net.minecraft.util.IdList;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
@ -27,40 +27,40 @@ import net.minecraft.util.registry.Registry;
import java.util.HashMap;
import java.util.Map;
public class IdListTracker<V, OV> implements RegistryPreClearCallback<V>, RegistryPreRegisterCallback<V> {
public class IdListTracker<V, OV> implements RegistryEntryAddedCallback<V>, RegistryIdRemapCallback<V>, RegistryEntryRemovedCallback<V> {
private final IdList<OV> mappers;
private final Registry<V> registry;
private Map<Identifier, OV> mapperCache = new HashMap<>();
private Map<Identifier, OV> removedMapperCache = new HashMap<>();
private IdListTracker(Registry<V> registry, IdList<OV> mappers) {
this.registry = registry;
private IdListTracker(IdList<OV> mappers) {
this.mappers = mappers;
}
public static <V, OV> void register(Registry<V> registry, IdList<OV> mappers) {
IdListTracker<V, OV> updater = new IdListTracker<>(registry, mappers);
((ListenableRegistry<V>) registry).getPreClearEvent().register(updater);
((ListenableRegistry<V>) registry).getPreRegisterEvent().register(updater);
IdListTracker<V, OV> updater = new IdListTracker<>(mappers);
RegistryEntryAddedCallback.event(registry).register(updater);
RegistryIdRemapCallback.event(registry).register(updater);
RegistryEntryRemovedCallback.event(registry).register(updater);
}
@Override
public void onPreClear() {
mapperCache.clear();
for (Identifier id : registry.getIds()) {
int rawId = registry.getRawId(registry.get(id));
OV mapper = mappers.get(rawId);
if (mapper != null) {
mapperCache.put(id, mapper);
}
public void onEntryAdded(int rawId, Identifier id, V object) {
if (removedMapperCache.containsKey(id)) {
mappers.set(removedMapperCache.get(id), rawId);
}
}
((RemovableIdList) mappers).clear();
@SuppressWarnings("unchecked")
@Override
public void onRemap(RemapState<V> state) {
((RemovableIdList<OV>) mappers).fabric_remapIds(state.getRawIdChangeMap());
}
@Override
public void onPreRegister(int id, Identifier identifier, V object, boolean isNewToRegistry) {
if (mapperCache.containsKey(identifier)) {
mappers.set(mapperCache.get(identifier), id);
public void onEntryRemoved(int rawId, Identifier id, V object) {
if (mappers.get(rawId) != null) {
removedMapperCache.put(id, mappers.get(rawId));
//noinspection unchecked
((RemovableIdList<OV>) mappers).fabric_removeId(rawId);
}
}
}

View file

@ -17,49 +17,57 @@
package net.fabricmc.fabric.impl.registry.trackers;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreRegisterCallback;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryRemovedCallback;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import java.util.HashMap;
import java.util.Map;
public class Int2ObjectMapTracker<V, OV> implements RegistryPreClearCallback<V>, RegistryPreRegisterCallback<V> {
public class Int2ObjectMapTracker<V, OV> implements RegistryEntryAddedCallback<V>, RegistryIdRemapCallback<V>, RegistryEntryRemovedCallback<V> {
private final Int2ObjectMap<OV> mappers;
private final Registry<V> registry;
private Map<Identifier, OV> mapperCache = new HashMap<>();
private Map<Identifier, OV> removedMapperCache = new HashMap<>();
private Int2ObjectMapTracker(Registry<V> registry, Int2ObjectMap<OV> mappers) {
this.registry = registry;
private Int2ObjectMapTracker(Int2ObjectMap<OV> mappers) {
this.mappers = mappers;
}
public static <V, OV> void register(Registry<V> registry, Int2ObjectMap<OV> mappers) {
Int2ObjectMapTracker<V, OV> updater = new Int2ObjectMapTracker<>(registry, mappers);
((ListenableRegistry<V>) registry).getPreClearEvent().register(updater);
((ListenableRegistry<V>) registry).getPreRegisterEvent().register(updater);
Int2ObjectMapTracker<V, OV> updater = new Int2ObjectMapTracker<>(mappers);
RegistryEntryAddedCallback.event(registry).register(updater);
RegistryIdRemapCallback.event(registry).register(updater);
RegistryEntryRemovedCallback.event(registry).register(updater);
}
@Override
public void onPreClear() {
mapperCache.clear();
for (Identifier id : registry.getIds()) {
int rawId = registry.getRawId(registry.get(id));
OV mapper = mappers.get(rawId);
if (mapper != null) {
mapperCache.put(id, mapper);
}
public void onEntryAdded(int rawId, Identifier id, V object) {
if (removedMapperCache.containsKey(id)) {
mappers.put(rawId, removedMapperCache.get(id));
}
}
@Override
public void onRemap(RemapState<V> state) {
Int2ObjectMap<OV> oldMappers = new Int2ObjectOpenHashMap<>(mappers);
mappers.clear();
for (int i : oldMappers.keySet()) {
int newI = state.getRawIdChangeMap().getOrDefault(i, i);
if (mappers.containsKey(newI)) {
throw new RuntimeException("Int2ObjectMap contained two equal IDs " + newI + " (" + state.getIdFromOld(i) + "/" + i + " -> " + state.getIdFromNew(newI) + "/" + newI + ")!");
}
mappers.put(newI, oldMappers.get(i));
}
}
@Override
public void onPreRegister(int id, Identifier identifier, V object, boolean isNewToRegistry) {
if (mapperCache.containsKey(identifier)) {
mappers.put(id, mapperCache.get(identifier));
public void onEntryRemoved(int rawId, Identifier id, V object) {
if (mappers.containsKey(rawId)) {
removedMapperCache.put(id, mappers.remove(rawId));
}
}
}

View file

@ -16,39 +16,39 @@
package net.fabricmc.fabric.impl.registry.trackers;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
import net.fabricmc.fabric.impl.registry.RemovableIdList;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPostRegisterCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.minecraft.util.IdList;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.SimpleRegistry;
import net.minecraft.util.registry.Registry;
import java.util.Collection;
import java.util.function.Function;
public final class StateIdTracker<T, S> implements RegistryPreClearCallback<T>, RegistryPostRegisterCallback<T> {
public final class StateIdTracker<T, S> implements RegistryIdRemapCallback<T> {
private final Registry<T> registry;
private final IdList<S> stateList;
private final Function<T, Collection<S>> stateGetter;
public static <T, S> void register(SimpleRegistry<T> registry, IdList<S> stateList, Function<T, Collection<S>> stateGetter) {
StateIdTracker<T, S> tracker = new StateIdTracker<>(stateList, stateGetter);
((ListenableRegistry<T>) registry).getPreClearEvent().register(tracker);
((ListenableRegistry<T>) registry).getPostRegisterEvent().register(tracker);
public static <T, S> void register(Registry<T> registry, IdList<S> stateList, Function<T, Collection<S>> stateGetter) {
RegistryIdRemapCallback.event(registry).register(new StateIdTracker<>(registry, stateList, stateGetter));
}
private StateIdTracker(IdList<S> stateList, Function<T, Collection<S>> stateGetter) {
private StateIdTracker(Registry<T> registry, IdList<S> stateList, Function<T, Collection<S>> stateGetter) {
this.registry = registry;
this.stateList = stateList;
this.stateGetter = stateGetter;
}
@Override
public void onPreClear() {
((RemovableIdList) stateList).clear();
}
public void onRemap(RemapState<T> state) {
((RemovableIdList) stateList).fabric_clear();
@Override
public void onPostRegister(int rawId, Identifier id, T object) {
stateGetter.apply(object).forEach(stateList::add);
Int2ObjectMap<T> sortedBlocks = new Int2ObjectRBTreeMap<>();
registry.forEach((t) -> sortedBlocks.put(registry.getRawId(t), t));
for (T b : sortedBlocks.values()) {
stateGetter.apply(b).forEach(stateList::add);
}
}
}

View file

@ -16,15 +16,18 @@
package net.fabricmc.fabric.impl.registry.trackers.vanilla;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryRemovedCallback;
import net.fabricmc.fabric.impl.registry.RemovableIdList;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPostRegisterCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
public final class BiomeParentTracker implements RegistryPreClearCallback<Biome>, RegistryPostRegisterCallback<Biome> {
import java.util.Objects;
public final class BiomeParentTracker implements RegistryEntryAddedCallback<Biome>, RegistryEntryRemovedCallback<Biome>, RegistryIdRemapCallback<Biome> {
private final Registry<Biome> registry;
private BiomeParentTracker(Registry<Biome> registry) {
@ -33,19 +36,32 @@ public final class BiomeParentTracker implements RegistryPreClearCallback<Biome>
public static void register(Registry<Biome> registry) {
BiomeParentTracker tracker = new BiomeParentTracker(registry);
((ListenableRegistry<Biome>) registry).getPreClearEvent().register(tracker);
((ListenableRegistry<Biome>) registry).getPostRegisterEvent().register(tracker);
RegistryEntryAddedCallback.event(registry).register(tracker);
RegistryIdRemapCallback.event(registry).register(tracker);
RegistryEntryRemovedCallback.event(registry).register(tracker);
}
@Override
public void onPostRegister(int rawId, Identifier id, Biome object) {
public void onEntryAdded(int rawId, Identifier id, Biome object) {
if (object.hasParent()) {
Biome.PARENT_BIOME_ID_MAP.set(object, registry.getRawId(registry.get(new Identifier(object.getParent()))));
Biome.PARENT_BIOME_ID_MAP.set(object, registry.getRawId(registry.get(new Identifier(Objects.requireNonNull(object.getParent())))));
}
}
@Override
public void onPreClear() {
((RemovableIdList) Biome.PARENT_BIOME_ID_MAP).clear();
public void onRemap(RemapState<Biome> state) {
for (Int2IntMap.Entry entry : state.getRawIdChangeMap().int2IntEntrySet()) {
if (Biome.PARENT_BIOME_ID_MAP.get(entry.getIntKey()) != null) {
//noinspection unchecked
((RemovableIdList<Biome>) Biome.PARENT_BIOME_ID_MAP).fabric_remapId(entry.getIntKey(), entry.getIntValue());
}
}
}
@SuppressWarnings("unchecked")
@Override
public void onEntryRemoved(int rawId, Identifier id, Biome object) {
((RemovableIdList<Biome>) Biome.PARENT_BIOME_ID_MAP).fabric_remove(object);
((RemovableIdList<Biome>) Biome.PARENT_BIOME_ID_MAP).fabric_removeId(rawId);
}
}

View file

@ -16,32 +16,31 @@
package net.fabricmc.fabric.impl.registry.trackers.vanilla;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPostRegisterCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreRegisterCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.SimpleRegistry;
import net.minecraft.util.registry.Registry;
public final class BlockInitTracker implements RegistryPreRegisterCallback<Block> {
private BlockInitTracker() {
public final class BlockInitTracker implements RegistryEntryAddedCallback<Block> {
private final Registry<Block> registry;
private BlockInitTracker(Registry<Block> registry) {
this.registry = registry;
}
public static void register(SimpleRegistry<Block> registry) {
BlockInitTracker tracker = new BlockInitTracker();
((ListenableRegistry<Block>) registry).getPreRegisterEvent().register(tracker);
public static void register(Registry<Block> registry) {
BlockInitTracker tracker = new BlockInitTracker(registry);
RegistryEntryAddedCallback.event(registry).register(tracker);
}
@Override
public void onPreRegister(int rawId, Identifier id, Block object, boolean isNewToRegistry) {
if (isNewToRegistry) {
object.getStateFactory().getStates().forEach(BlockState::initShapeCache);
object.getDropTableId();
}
public void onEntryAdded(int rawId, Identifier id, Block object) {
object.getStateFactory().getStates().forEach(BlockState::initShapeCache);
// if false, getDropTableId() will generate an invalid drop table ID
assert id.equals(registry.getId(object));
object.getDropTableId();
}
}

View file

@ -16,34 +16,26 @@
package net.fabricmc.fabric.impl.registry.trackers.vanilla;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPostRegisterCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.SimpleRegistry;
import net.minecraft.util.registry.Registry;
public final class BlockItemTracker implements RegistryPreClearCallback<Item>, RegistryPostRegisterCallback<Item> {
public final class BlockItemTracker implements RegistryEntryAddedCallback<Item> {
private BlockItemTracker() {
}
public static void register(SimpleRegistry<Item> registry) {
public static void register(Registry<Item> registry) {
BlockItemTracker tracker = new BlockItemTracker();
((ListenableRegistry<Item>) registry).getPreClearEvent().register(tracker);
((ListenableRegistry<Item>) registry).getPostRegisterEvent().register(tracker);
RegistryEntryAddedCallback.event(registry).register(tracker);
}
@Override
public void onPostRegister(int rawId, Identifier id, Item object) {
public void onEntryAdded(int rawId, Identifier id, Item object) {
if (object instanceof BlockItem) {
((BlockItem) object).registerBlockItemMap(Item.BLOCK_ITEM_MAP, object);
}
}
@Override
public void onPreClear() {
Item.BLOCK_ITEM_MAP.clear();
}
}

View file

@ -16,14 +16,19 @@
package net.fabricmc.fabric.mixin.registry;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntMaps;
import net.fabricmc.fabric.impl.registry.RemovableIdList;
import net.minecraft.util.IdList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;
@Mixin(IdList.class)
public class MixinIdList implements RemovableIdList<Object> {
@ -35,7 +40,7 @@ public class MixinIdList implements RemovableIdList<Object> {
private List<Object> list;
@Override
public void clear() {
public void fabric_clear() {
nextId = 0;
idMap.clear();
list.clear();
@ -51,17 +56,55 @@ public class MixinIdList implements RemovableIdList<Object> {
}
@Override
public void remove(Object o) {
public void fabric_remove(Object o) {
if (idMap.containsKey(o)) {
fabric_removeInner(o);
}
}
@Override
public void removeId(int i) {
Object obj = list.get(i);
if (obj != null) {
fabric_removeInner(obj);
public void fabric_removeId(int i) {
List<Object> removals = new ArrayList<>();
for (Object o : idMap.keySet()) {
int j = idMap.get(o);
if (i == j) {
removals.add(o);
}
}
removals.forEach(this::fabric_removeInner);
}
@Override
public void fabric_remapId(int from, int to) {
fabric_remapIds(Int2IntMaps.singleton(from, to));
}
@Override
public void fabric_remapIds(Int2IntMap map) {
// remap idMap
idMap.replaceAll((a, b) -> map.get(b));
// remap list
nextId = 0;
List<Object> oldList = new ArrayList<>(list);
list.clear();
for (int k = 0; k < oldList.size(); k++) {
Object o = oldList.get(k);
if (o != null) {
int i = map.getOrDefault(k, k);
while (list.size() <= i) {
list.add(null);
}
list.set(i, o);
if (nextId <= i) {
nextId = i + 1;
}
}
}
}
}

View file

@ -18,16 +18,19 @@ package net.fabricmc.fabric.mixin.registry;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.fabricmc.fabric.api.event.registry.RegistryIdRemapCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryRemovedCallback;
import net.fabricmc.fabric.impl.registry.ListenableRegistry;
import net.fabricmc.fabric.impl.registry.RemapStateImpl;
import net.fabricmc.fabric.impl.registry.RemapException;
import net.fabricmc.fabric.impl.registry.RemappableRegistry;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPostRegisterCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreClearCallback;
import net.fabricmc.fabric.impl.registry.callbacks.RegistryPreRegisterCallback;
import net.minecraft.util.Identifier;
import net.minecraft.util.Int2ObjectBiMap;
import net.minecraft.util.registry.SimpleRegistry;
@ -43,7 +46,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.*;
@Mixin(SimpleRegistry.class)
public abstract class MixinIdRegistry<T> implements RemappableRegistry, ListenableRegistry<T> {
public abstract class MixinIdRegistry<T> implements RemappableRegistry, ListenableRegistry {
@Shadow
protected Int2ObjectBiMap<T> indexedEntries;
@Shadow
@ -53,65 +56,86 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
@Unique
private static Logger FABRIC_LOGGER = LogManager.getLogger();
private final Event<RegistryPreClearCallback> fabric_preClearEvent = EventFactory.createArrayBacked(RegistryPreClearCallback.class,
(callbacks) -> () -> {
for (RegistryPreClearCallback callback : callbacks) {
callback.onPreClear();
}
}
);
private final Event<RegistryPreRegisterCallback> fabric_preRegisterEvent = EventFactory.createArrayBacked(RegistryPreRegisterCallback.class,
(callbacks) -> (a, b, c, d) -> {
for (RegistryPreRegisterCallback callback : callbacks) {
@Unique
private final Event<RegistryEntryAddedCallback> fabric_addObjectEvent = EventFactory.createArrayBacked(RegistryEntryAddedCallback.class,
(callbacks) -> (rawId, id, object) -> {
for (RegistryEntryAddedCallback callback : callbacks) {
//noinspection unchecked
callback.onPreRegister(a, b, c, d);
callback.onEntryAdded(rawId, id, object);
}
}
);
private final Event<RegistryPostRegisterCallback> fabric_postRegisterEvent = EventFactory.createArrayBacked(RegistryPostRegisterCallback.class,
(callbacks) -> (a, b, c) -> {
for (RegistryPostRegisterCallback callback : callbacks) {
@Unique
private final Event<RegistryEntryRemovedCallback> fabric_removeObjectEvent = EventFactory.createArrayBacked(RegistryEntryRemovedCallback.class,
(callbacks) -> (rawId, id, object) -> {
for (RegistryEntryRemovedCallback callback : callbacks) {
//noinspection unchecked
callback.onPostRegister(a, b, c);
callback.onEntryRemoved(rawId, id, object);
}
}
);
@Unique
private final Event<RegistryIdRemapCallback> fabric_postRemapEvent = EventFactory.createArrayBacked(RegistryIdRemapCallback.class,
(callbacks) -> (a) -> {
for (RegistryIdRemapCallback callback : callbacks) {
//noinspection unchecked
callback.onRemap(a);
}
}
);
@Unique
private Object2IntMap<Identifier> fabric_prevIndexedEntries;
@Unique
private BiMap<Identifier, T> fabric_prevEntries;
@Override
public Event<RegistryPreClearCallback<T>> getPreClearEvent() {
public Event<RegistryEntryAddedCallback<T>> fabric_getAddObjectEvent() {
//noinspection unchecked
return (Event<RegistryPreClearCallback<T>>) (Event) fabric_preClearEvent;
return (Event<RegistryEntryAddedCallback<T>>) (Event) fabric_addObjectEvent;
}
@Override
public Event<RegistryPreRegisterCallback<T>> getPreRegisterEvent() {
public Event<RegistryEntryRemovedCallback<T>> fabric_getRemoveObjectEvent() {
//noinspection unchecked
return (Event<RegistryPreRegisterCallback<T>>) (Event) fabric_preRegisterEvent;
return (Event<RegistryEntryRemovedCallback<T>>) (Event) fabric_removeObjectEvent;
}
@Override
public Event<RegistryPostRegisterCallback<T>> getPostRegisterEvent() {
public Event<RegistryIdRemapCallback<T>> fabric_getRemapEvent() {
//noinspection unchecked
return (Event<RegistryPostRegisterCallback<T>>) (Event) fabric_postRegisterEvent;
return (Event<RegistryIdRemapCallback<T>>) (Event) fabric_postRemapEvent;
}
// The rest of the registry isn't thread-safe, so this one need not be either.
@Unique
private boolean fabric_isObjectNew = false;
@SuppressWarnings({"unchecked", "ConstantConditions"})
@Inject(method = "set", at = @At("HEAD"))
public void setPre(int id, Identifier identifier, Object object, CallbackInfoReturnable info) {
boolean isNewToRegistry = !entries.containsKey(identifier);
fabric_preRegisterEvent.invoker().onPreRegister(id, identifier, object, isNewToRegistry);
if (!entries.containsKey(identifier)) {
fabric_isObjectNew = true;
} else {
T oldObject = entries.get(identifier);
int oldId = indexedEntries.getId(oldObject);
if (oldObject != object || oldId != id) {
fabric_removeObjectEvent.invoker().onEntryRemoved(oldId, identifier, oldObject);
fabric_isObjectNew = true;
} else {
fabric_isObjectNew = false;
}
}
}
@SuppressWarnings({"unchecked", "ConstantConditions"})
@SuppressWarnings("unchecked")
@Inject(method = "set", at = @At("RETURN"))
public void setPost(int id, Identifier identifier, Object object, CallbackInfoReturnable info) {
SimpleRegistry<Object> registry = (SimpleRegistry<Object>) (Object) this;
fabric_postRegisterEvent.invoker().onPostRegister(id, identifier, object);
if (fabric_isObjectNew) {
fabric_addObjectEvent.invoker().onEntryAdded(id, identifier, object);
}
}
@Override
@ -193,7 +217,7 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
for (Identifier id : registry.getIds()) {
if (!remoteIndexedEntries.containsKey(id)) {
FABRIC_LOGGER.warn("Adding " + id + " to registry.");
FABRIC_LOGGER.warn("Adding " + id + " to saved/remote registry.");
remoteIndexedEntries.put(id, ++maxValue);
}
}
@ -204,14 +228,22 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
for (Identifier id : registry.getIds()) {
if (!remoteIndexedEntries.containsKey(id)) {
droppedIds.add(id);
Object object = registry.get(id);
// Emit RemoveObject events for removed objects.
//noinspection unchecked
fabric_getRemoveObjectEvent().invoker().onEntryRemoved(registry.getRawId(object), id, (T) object);
}
}
entries.keySet().removeAll(droppedIds);
}
// Inform about registry clearing.
fabric_preClearEvent.invoker().onPreClear();
Int2IntMap idMap = new Int2IntOpenHashMap();
for (Object o : indexedEntries) {
Identifier id = registry.getId(o);
idMap.put(registry.getRawId(o), remoteIndexedEntries.getInt(id));
}
// entries was handled above, if it was necessary.
indexedEntries.clear();
@ -235,29 +267,40 @@ public abstract class MixinIdRegistry<T> implements RemappableRegistry, Listenab
}
continue;
}
//noinspection unchecked
fabric_preRegisterEvent.invoker().onPreRegister(id, identifier, object, false);
// Add the new object, increment nextId to match.
indexedEntries.put(object, id);
if (nextId <= id) {
nextId = id + 1;
}
//noinspection unchecked
fabric_postRegisterEvent.invoker().onPostRegister(id, identifier, object);
}
//noinspection unchecked
fabric_getRemapEvent().invoker().onRemap(new RemapStateImpl(registry, idMap));
}
@Override
public void unmap(String name) throws RemapException {
if (fabric_prevIndexedEntries != null) {
List<Identifier> addedIds = new ArrayList<>();
// Emit AddObject events for previously culled objects.
for (Identifier id : fabric_prevEntries.keySet()) {
if (!entries.containsKey(id)) {
assert fabric_prevIndexedEntries.containsKey(id);
addedIds.add(id);
}
}
entries.clear();
entries.putAll(fabric_prevEntries);
remap(name, fabric_prevIndexedEntries, RemapMode.AUTHORITATIVE);
for (Identifier id : addedIds) {
fabric_getAddObjectEvent().invoker().onEntryAdded(indexedEntries.getId(entries.get(id)), id, entries.get(id));
}
fabric_prevIndexedEntries = null;
fabric_prevEntries = null;
}

View file

@ -39,12 +39,14 @@ import java.io.IOException;
@Mixin(WorldSaveHandler.class)
public class MixinWorldSaveHandler {
@Unique
private static final int FABRIC_ID_REGISTRY_BACKUPS = 3;
@Unique
private static Logger FABRIC_LOGGER = LogManager.getLogger();
@Shadow
public File worldDir;
@Unique
private CompoundTag fabric_lastSavedIdMap = null;
private boolean fabric_readIdMapFile(File file) throws IOException, RemapException {