/* * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ package net.minecraftforge.registries; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.mojang.serialization.Lifecycle; import net.minecraft.core.WritableRegistry; import net.minecraft.data.BuiltinRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.core.Registry; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.network.HandshakeMessages; import net.minecraftforge.registries.ForgeRegistry.Snapshot; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class RegistryManager { private static final Logger LOGGER = LogManager.getLogger(); public static final RegistryManager ACTIVE = new RegistryManager("ACTIVE"); public static final RegistryManager VANILLA = new RegistryManager("VANILLA"); public static final RegistryManager FROZEN = new RegistryManager("FROZEN"); private static Set vanillaRegistryKeys = Set.of(); BiMap> registries = HashBiMap.create(); private Set persisted = Sets.newHashSet(); private Set synced = Sets.newHashSet(); private Map legacyNames = new HashMap<>(); private final String name; RegistryManager() { this("STAGING"); } public RegistryManager(String name) { this.name = name; } public String getName() { return this.name; } boolean isStaging() { return "STAGING".equals(this.name); } @SuppressWarnings("unchecked") public ForgeRegistry getRegistry(ResourceLocation key) { return (ForgeRegistry)this.registries.get(key); } public ForgeRegistry getRegistry(ResourceKey> key) { return getRegistry(key.location()); } public ResourceLocation getName(IForgeRegistry reg) { return this.registries.inverse().get(reg); } public ResourceLocation updateLegacyName(ResourceLocation legacyName) { ResourceLocation originalName = legacyName; while (getRegistry(legacyName) == null) { legacyName = legacyNames.get(legacyName); if (legacyName == null) { return originalName; } } return legacyName; } public ForgeRegistry getRegistry(ResourceLocation key, RegistryManager other) { if (!this.registries.containsKey(key)) { ForgeRegistry ot = other.getRegistry(key); if (ot == null) return null; this.registries.put(key, ot.copy(this)); if (other.persisted.contains(key)) this.persisted.add(key); if (other.synced.contains(key)) this.synced.add(key); other.legacyNames.entrySet().stream() .filter(e -> e.getValue().equals(key)) .forEach(e -> addLegacyName(e.getKey(), e.getValue())); } return getRegistry(key); } ForgeRegistry createRegistry(ResourceLocation name, RegistryBuilder builder) { if (registries.containsKey(name)) throw new IllegalArgumentException("Attempted to register a registry for " + name + " but it already exists"); ForgeRegistry reg = new ForgeRegistry(this, name, builder); registries.put(name, reg); if (builder.getSaveToDisc()) this.persisted.add(name); if (builder.getSync()) this.synced.add(name); for (ResourceLocation legacyName : builder.getLegacyNames()) addLegacyName(legacyName, name); return getRegistry(name); } static void registerToRootRegistry(ForgeRegistry forgeReg) { injectForgeRegistry(forgeReg, Registry.REGISTRY); } static void registerToBuiltinRegistry(ForgeRegistry forgeReg) { injectForgeRegistry(forgeReg, BuiltinRegistries.REGISTRY); } @SuppressWarnings("unchecked") private static void injectForgeRegistry(ForgeRegistry forgeReg, Registry> rootRegistry) { WritableRegistry> registry = (WritableRegistry>) rootRegistry; Registry wrapper = forgeReg.getWrapper(); if (wrapper != null) registry.register(forgeReg.getRegistryKey(), wrapper, Lifecycle.experimental()); } public static void postNewRegistryEvent() { NewRegistryEvent event = new NewRegistryEvent(); vanillaRegistryKeys = Set.copyOf(Registry.REGISTRY.keySet()); ModLoader.get().postEventWithWrapInModOrder(event, (mc, e) -> ModLoadingContext.get().setActiveContainer(mc), (mc, e) -> ModLoadingContext.get().setActiveContainer(null)); event.fill(); } private void addLegacyName(ResourceLocation legacyName, ResourceLocation name) { if (this.legacyNames.containsKey(legacyName)) { throw new IllegalArgumentException("Legacy name conflict for registry " + name + ", upgrade path must be linear: " + legacyName); } this.legacyNames.put(legacyName, name); } private void findSuperTypes(Class type, Set> types) { if (type == null || type == Object.class) { return; } types.add(type); for (Class interfac : type.getInterfaces()) { findSuperTypes(interfac, types); } findSuperTypes(type.getSuperclass(), types); } public Map takeSnapshot(boolean savingToDisc) { Map ret = Maps.newHashMap(); Set keys = savingToDisc ? this.persisted : this.synced; keys.forEach(name -> ret.put(name, getRegistry(name).makeSnapshot())); return ret; } //Public for testing only public void clean() { this.persisted.clear(); this.synced.clear(); this.registries.clear(); } public static List> generateRegistryPackets(boolean isLocal) { return !isLocal ? ACTIVE.takeSnapshot(false).entrySet().stream(). map(e->Pair.of("Registry " + e.getKey(), new HandshakeMessages.S2CRegistry(e.getKey(), e.getValue()))). collect(Collectors.toList()) : Collections.emptyList(); } public static List getRegistryNamesForSyncToClient() { return ACTIVE.registries.keySet().stream(). filter(resloc -> ACTIVE.synced.contains(resloc)). collect(Collectors.toList()); } public static Set getVanillaRegistryKeys() { return vanillaRegistryKeys; } }