-
-
Notifications
You must be signed in to change notification settings - Fork 510
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Datagen for and loading of structure-specific Loonium configurations
(includes a generic reloadable config data manager, which currently only covers Loonium configurations)
- Loading branch information
1 parent
7f0f8e5
commit 3e3594d
Showing
7 changed files
with
199 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
Xplat/src/main/java/vazkii/botania/common/config/ConfigDataManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package vazkii.botania.common.config; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.JsonElement; | ||
import com.mojang.serialization.Codec; | ||
import com.mojang.serialization.JsonOps; | ||
|
||
import net.minecraft.resources.ResourceLocation; | ||
import net.minecraft.server.packs.PackType; | ||
import net.minecraft.server.packs.resources.PreparableReloadListener; | ||
import net.minecraft.server.packs.resources.ResourceManager; | ||
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; | ||
import net.minecraft.util.profiling.ProfilerFiller; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
|
||
import vazkii.botania.api.BotaniaAPI; | ||
import vazkii.botania.api.configdata.LooniumStructureConfiguration; | ||
import vazkii.botania.xplat.XplatAbstractions; | ||
|
||
import java.util.*; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.Executor; | ||
import java.util.function.BiConsumer; | ||
import java.util.function.Consumer; | ||
|
||
import static vazkii.botania.common.lib.ResourceLocationHelper.prefix; | ||
|
||
public class ConfigDataManager implements PreparableReloadListener { | ||
public static void registerListener() { | ||
XplatAbstractions.INSTANCE.registerReloadListener(PackType.SERVER_DATA, prefix("configdata"), new ConfigDataManager()); | ||
} | ||
|
||
private final Map<ResourceLocation, LooniumStructureConfiguration> looniumConfigs = new HashMap<>(); | ||
|
||
public LooniumStructureConfiguration getEffectiveLooniumStructureConfiguration(ResourceLocation id) { | ||
LooniumStructureConfiguration configuration = this.looniumConfigs.get(id); | ||
return configuration != null ? configuration.getEffectiveConfig(looniumConfigs::get) : null; | ||
} | ||
|
||
private static void validateLooniumConfig(Map<ResourceLocation, LooniumStructureConfiguration> map) { | ||
Set<ResourceLocation> errorEntries = new HashSet<>(); | ||
Set<ResourceLocation> visitedEntries = new LinkedHashSet<>(); | ||
do { | ||
errorEntries.clear(); | ||
for (var entry : map.entrySet()) { | ||
ResourceLocation id = entry.getKey(); | ||
ResourceLocation parent = entry.getValue().parent; | ||
if (id.equals(parent)) { | ||
BotaniaAPI.LOGGER.warn("Ignoring Loonium structure configuration, because it specified itself as parent: {}", id); | ||
errorEntries.add(id); | ||
} else { | ||
visitedEntries.clear(); | ||
if (!findTopmostParent(map, id, parent, visitedEntries)) { | ||
BotaniaAPI.LOGGER.warn("Ignoring Loonium structure configuration(s) without top-most parent: {}", visitedEntries); | ||
errorEntries.addAll(visitedEntries); | ||
break; | ||
} | ||
} | ||
} | ||
errorEntries.forEach(map::remove); | ||
} while (!errorEntries.isEmpty() && !map.isEmpty()); | ||
|
||
if (!map.containsKey(LooniumStructureConfiguration.DEFAULT_CONFIG_ID)) { | ||
BotaniaAPI.LOGGER.error("Default Loonium configuration not found!"); | ||
} | ||
} | ||
|
||
private static boolean findTopmostParent(Map<ResourceLocation, LooniumStructureConfiguration> map, | ||
ResourceLocation id, ResourceLocation parent, Set<ResourceLocation> visitedEntries) { | ||
if (!visitedEntries.add(id)) { | ||
BotaniaAPI.LOGGER.warn("Cyclic dependency between Loonium structure configurations detected: {}", visitedEntries); | ||
return false; | ||
} | ||
if (parent == null) { | ||
return true; | ||
} | ||
var parentConfig = map.get(parent); | ||
return parentConfig != null && findTopmostParent(map, parent, parentConfig.parent, visitedEntries); | ||
} | ||
|
||
private void applyLooniumConfig(Map<ResourceLocation, LooniumStructureConfiguration> looniumConfigs) { | ||
this.looniumConfigs.putAll(looniumConfigs); | ||
} | ||
|
||
@NotNull | ||
@Override | ||
public CompletableFuture<Void> reload(@NotNull PreparationBarrier barrier, @NotNull ResourceManager manager, | ||
@NotNull ProfilerFiller prepProfiler, @NotNull ProfilerFiller reloadProfiler, | ||
@NotNull Executor backgroundExecutor, @NotNull Executor gameExecutor) { | ||
var looniumTask = scheduleConfigParse(barrier, manager, backgroundExecutor, gameExecutor, ConfigDataType.LOONUIM); | ||
|
||
return CompletableFuture.allOf(looniumTask).thenRun(() -> BotaniaAPI.instance().setConfigData(this)); | ||
} | ||
|
||
private <T> CompletableFuture<Void> scheduleConfigParse(PreparationBarrier barrier, ResourceManager manager, | ||
Executor backgroundExecutor, Executor gameExecutor, ConfigDataType<T> type) { | ||
return CompletableFuture.supplyAsync(() -> { | ||
Map<ResourceLocation, JsonElement> resourceMap = new HashMap<>(); | ||
SimpleJsonResourceReloadListener.scanDirectory(manager, "config/" + type.directory, new Gson(), resourceMap); | ||
Map<ResourceLocation, T> configs = new HashMap<>(resourceMap.size()); | ||
resourceMap.forEach((id, jsonElement) -> type.codec.parse(JsonOps.INSTANCE, jsonElement).result().ifPresent(c -> configs.put(id, c))); | ||
type.validateFunction.accept(configs); | ||
return configs; | ||
}, backgroundExecutor) | ||
.thenCompose(barrier::wait) | ||
.thenAcceptAsync(c -> type.applyFunction.accept(this, c), gameExecutor); | ||
} | ||
|
||
private record ConfigDataType<T> (Codec<T> codec, String directory, | ||
Consumer<Map<ResourceLocation, T>> validateFunction, | ||
BiConsumer<ConfigDataManager, Map<ResourceLocation, T>> applyFunction) { | ||
private static final ConfigDataType<LooniumStructureConfiguration> LOONUIM = | ||
new ConfigDataType<>(LooniumStructureConfiguration.CODEC, "loonium", | ||
ConfigDataManager::validateLooniumConfig, ConfigDataManager::applyLooniumConfig); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters