Config.java 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package me.lortseam.completeconfig.data;
  2. import lombok.EqualsAndHashCode;
  3. import lombok.Getter;
  4. import lombok.NonNull;
  5. import lombok.ToString;
  6. import lombok.extern.log4j.Log4j2;
  7. import me.lortseam.completeconfig.CompleteConfig;
  8. import me.lortseam.completeconfig.api.ConfigContainer;
  9. import me.lortseam.completeconfig.extension.BaseExtension;
  10. import me.lortseam.completeconfig.text.TranslationKey;
  11. import net.fabricmc.api.EnvType;
  12. import net.fabricmc.api.Environment;
  13. import net.fabricmc.loader.api.FabricLoader;
  14. import net.fabricmc.loader.api.metadata.ModMetadata;
  15. import org.apache.commons.lang3.ArrayUtils;
  16. import org.spongepowered.configurate.CommentedConfigurationNode;
  17. import org.spongepowered.configurate.ConfigurateException;
  18. import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
  19. import org.spongepowered.configurate.serialize.TypeSerializerCollection;
  20. import java.io.BufferedReader;
  21. import java.io.BufferedWriter;
  22. import java.nio.file.Path;
  23. import java.util.Arrays;
  24. import java.util.Objects;
  25. import java.util.concurrent.Callable;
  26. import java.util.function.Consumer;
  27. /**
  28. * The base config class. Instantiate or inherit this class to create a config for your mod.
  29. */
  30. @Log4j2(topic = "CompleteConfig")
  31. @EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
  32. @ToString(onlyExplicitlyIncluded = true)
  33. public class Config extends Parent {
  34. private static HoconConfigurationLoader createLoader(Consumer<HoconConfigurationLoader.Builder> builderConsumer) {
  35. HoconConfigurationLoader.Builder builder = HoconConfigurationLoader.builder()
  36. .defaultOptions(options -> options.serializers(typeSerializerCollection -> {
  37. for (TypeSerializerCollection typeSerializers : CompleteConfig.collectExtensions(BaseExtension.class, BaseExtension::getTypeSerializers)) {
  38. typeSerializerCollection.registerAll(typeSerializers);
  39. }
  40. }));
  41. builderConsumer.accept(builder);
  42. return builder.build();
  43. }
  44. @EqualsAndHashCode.Include
  45. @ToString.Include
  46. @Getter
  47. private final String modId;
  48. @EqualsAndHashCode.Include
  49. @ToString.Include
  50. @Getter
  51. private final String[] branch;
  52. private final HoconConfigurationLoader loader;
  53. private Runnable resolver;
  54. @Environment(EnvType.CLIENT)
  55. private TranslationKey translation;
  56. /**
  57. * Creates a config with the specified branch.
  58. *
  59. * <p>The branch determines the location of the config's save file and has to be mod-unique.
  60. *
  61. * @param modId the ID of the mod creating the config
  62. * @param branch the branch
  63. */
  64. public Config(@NonNull String modId, @NonNull String[] branch, @NonNull ConfigContainer... containers) {
  65. if (!FabricLoader.getInstance().isModLoaded(modId)) {
  66. throw new IllegalArgumentException("Mod " + modId + " is not loaded");
  67. }
  68. Arrays.stream(branch).forEach(Objects::requireNonNull);
  69. Arrays.stream(containers).forEach(Objects::requireNonNull);
  70. this.modId = modId;
  71. this.branch = branch;
  72. loader = createLoader(builder -> {
  73. Path path = FabricLoader.getInstance().getConfigDir();
  74. String[] subPath = ArrayUtils.add(branch, 0, modId);
  75. subPath[subPath.length - 1] = subPath[subPath.length - 1] + ".conf";
  76. for (String child : subPath) {
  77. path = path.resolve(child);
  78. }
  79. builder.path(path);
  80. });
  81. resolver = () -> {
  82. if (this instanceof ConfigContainer) {
  83. resolve((ConfigContainer) this);
  84. }
  85. resolve(containers);
  86. if (isEmpty()) {
  87. logger.warn(this + " is empty");
  88. }
  89. };
  90. ConfigRegistry.register(this);
  91. }
  92. /**
  93. * Creates a config with the default branch.
  94. *
  95. * @param modId the ID of the mod creating the config
  96. */
  97. public Config(String modId, ConfigContainer... containers) {
  98. this(modId, new String[0], containers);
  99. }
  100. public final ModMetadata getMod() {
  101. return FabricLoader.getInstance().getModContainer(modId).get().getMetadata();
  102. }
  103. @Override
  104. public final TranslationKey getTranslation() {
  105. return getTranslation(false);
  106. }
  107. @Environment(EnvType.CLIENT)
  108. public final TranslationKey getTranslation(boolean includeBranch) {
  109. if (translation == null) {
  110. translation = TranslationKey.from(this);
  111. }
  112. if (includeBranch) {
  113. return translation.append(branch);
  114. }
  115. return translation;
  116. }
  117. private void load(HoconConfigurationLoader loader) {
  118. if (resolver != null) {
  119. resolver.run();
  120. resolver = null;
  121. }
  122. if (isEmpty()) return;
  123. try {
  124. CommentedConfigurationNode root = loader.load();
  125. if (!root.isNull()) {
  126. apply(root);
  127. }
  128. } catch (ConfigurateException e) {
  129. logger.error("Failed to load config", e);
  130. }
  131. }
  132. public final void load(Callable<BufferedReader> source) {
  133. load(createLoader(builder -> builder.source(source)));
  134. }
  135. /**
  136. * Loads the config from the save file.
  137. */
  138. public final void load() {
  139. load(loader);
  140. save();
  141. }
  142. private void serialize(HoconConfigurationLoader loader) {
  143. if (resolver != null) {
  144. throw new IllegalStateException("Cannot serialize config before it was loaded");
  145. }
  146. if (isEmpty()) return;
  147. CommentedConfigurationNode root = loader.createNode();
  148. fetch(root);
  149. try {
  150. loader.save(root);
  151. } catch (ConfigurateException e) {
  152. logger.error("Failed to serialize config", e);
  153. }
  154. }
  155. public final void serialize(Callable<BufferedWriter> sink) {
  156. serialize(createLoader(builder -> builder.sink(sink)));
  157. }
  158. /**
  159. * Saves the config to the save file.
  160. */
  161. public final void save() {
  162. serialize(loader);
  163. }
  164. }