11package com .falsepattern .lib .config ;
22
33import com .falsepattern .lib .StableAPI ;
4- import com .falsepattern .lib .internal .FalsePatternLib ;
5- import com .falsepattern .lib .util .FileUtil ;
4+ import com .falsepattern .lib .internal .impl .config .ConfigurationManagerImpl ;
5+ import lombok .AccessLevel ;
6+ import lombok .NoArgsConstructor ;
7+
68import cpw .mods .fml .client .config .IConfigElement ;
7- import cpw .mods .fml .client .event .ConfigChangedEvent ;
8- import cpw .mods .fml .common .FMLCommonHandler ;
9- import cpw .mods .fml .common .eventhandler .SubscribeEvent ;
109
11- import java .lang .reflect .Field ;
12- import java .lang .reflect .InvocationTargetException ;
13- import java .nio .file .Path ;
14- import java .util .Arrays ;
15- import java .util .HashMap ;
16- import java .util .HashSet ;
1710import java .util .List ;
18- import java .util .Map ;
19- import java .util .Optional ;
20- import java .util .Set ;
21- import java .util .regex .Pattern ;
22- import java .util .stream .Collectors ;
23- import lombok .AccessLevel ;
24- import lombok .NoArgsConstructor ;
25- import lombok .SneakyThrows ;
26- import lombok .val ;
27- import lombok .var ;
28- import net .minecraft .launchwrapper .Launch ;
29- import net .minecraftforge .common .config .ConfigElement ;
30- import net .minecraftforge .common .config .Configuration ;
3111
3212/**
3313 * Class for controlling the loading of configuration files.
3414 */
3515@ NoArgsConstructor (access = AccessLevel .PRIVATE )
3616@ StableAPI (since = "0.6.0" )
3717public class ConfigurationManager {
38- private static final Map <String , Configuration > configs = new HashMap <>();
39-
40- private static final Map <Configuration , Set <Class <?>>> configToClassMap = new HashMap <>();
41-
42- private static final ConfigurationManager instance = new ConfigurationManager ();
43-
44- private static boolean initialized = false ;
45-
46- private static Path configDir ;
4718
4819 /**
4920 * Registers a configuration class to be loaded. This should be done in preInit.
5021 *
5122 * @param configClass The class to register.
5223 */
5324 public static void registerConfig (Class <?> configClass ) throws ConfigException {
54- init ();
55- val cfg = Optional .ofNullable (configClass .getAnnotation (Config .class ))
56- .orElseThrow (() -> new ConfigException (
57- "Class " + configClass .getName () + " does not have a @Config annotation!" ));
58- val category = Optional .of (cfg .category ().trim ())
59- .map ((cat ) -> cat .length () == 0 ? null : cat )
60- .orElseThrow (() -> new ConfigException (
61- "Config class " + configClass .getName () + " has an empty category!" ));
62- val rawConfig = configs .computeIfAbsent (cfg .modid (), (ignored ) -> {
63- val c = new Configuration (configDir .resolve (cfg .modid () + ".cfg" ).toFile ());
64- c .load ();
65- return c ;
66- });
67- configToClassMap .computeIfAbsent (rawConfig , (ignored ) -> new HashSet <>()).add (configClass );
68- try {
69- processConfigInternal (configClass , category , rawConfig );
70- rawConfig .save ();
71- } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException | NoSuchFieldException e ) {
72- throw new ConfigException (e );
73- }
74- }
75-
76- private static void processConfigInternal (Class <?> configClass , String category , Configuration rawConfig )
77- throws IllegalAccessException , NoSuchMethodException , InvocationTargetException , NoSuchFieldException ,
78- ConfigException {
79- val cat = rawConfig .getCategory (category );
80- for (val field : configClass .getDeclaredFields ()) {
81- if (field .getAnnotation (Config .Ignore .class ) != null ) {
82- continue ;
83- }
84- field .setAccessible (true );
85- val comment = Optional .ofNullable (field .getAnnotation (Config .Comment .class ))
86- .map (Config .Comment ::value )
87- .map ((lines ) -> String .join ("\n " , lines ))
88- .orElse ("" );
89- val name = Optional .ofNullable (field .getAnnotation (Config .Name .class ))
90- .map (Config .Name ::value )
91- .orElse (field .getName ());
92- val langKey = Optional .ofNullable (field .getAnnotation (Config .LangKey .class ))
93- .map (Config .LangKey ::value )
94- .orElse (name );
95- val fieldClass = field .getType ();
96- var boxed = false ;
97- if ((boxed = fieldClass .equals (Boolean .class )) || fieldClass .equals (boolean .class )) {
98- val defaultValue = Optional .ofNullable (field .getAnnotation (Config .DefaultBoolean .class ))
99- .map (Config .DefaultBoolean ::value )
100- .orElse (boxed ? (Boolean ) field .get (null ) : field .getBoolean (null ));
101- field .setBoolean (null , rawConfig .getBoolean (name , category , defaultValue , comment , langKey ));
102- } else if ((boxed = fieldClass .equals (Integer .class )) || fieldClass .equals (int .class )) {
103- val range = Optional .ofNullable (field .getAnnotation (Config .RangeInt .class ));
104- val min = range .map (Config .RangeInt ::min ).orElse (Integer .MIN_VALUE );
105- val max = range .map (Config .RangeInt ::max ).orElse (Integer .MAX_VALUE );
106- val defaultValue = Optional .ofNullable (field .getAnnotation (Config .DefaultInt .class ))
107- .map (Config .DefaultInt ::value )
108- .orElse (boxed ? (Integer ) field .get (null ) : field .getInt (null ));
109- field .setInt (null , rawConfig .getInt (name , category , defaultValue , min , max , comment , langKey ));
110- } else if ((boxed = fieldClass .equals (Float .class )) || fieldClass .equals (float .class )) {
111- val range = Optional .ofNullable (field .getAnnotation (Config .RangeFloat .class ));
112- val min = range .map (Config .RangeFloat ::min ).orElse (Float .MIN_VALUE );
113- val max = range .map (Config .RangeFloat ::max ).orElse (Float .MAX_VALUE );
114- val defaultValue = Optional .ofNullable (field .getAnnotation (Config .DefaultFloat .class ))
115- .map (Config .DefaultFloat ::value )
116- .orElse (boxed ? (Float ) field .get (null ) : field .getFloat (null ));
117- field .setFloat (null , rawConfig .getFloat (name , category , defaultValue , min , max , comment , langKey ));
118- } else if (fieldClass .equals (String .class )) {
119- val defaultValue = Optional .ofNullable (field .getAnnotation (Config .DefaultString .class ))
120- .map (Config .DefaultString ::value )
121- .orElse ((String ) field .get (null ));
122- val pattern = Optional .ofNullable (field .getAnnotation (Config .Pattern .class ))
123- .map (Config .Pattern ::value )
124- .map (Pattern ::compile )
125- .orElse (null );
126- field .set (null , rawConfig .getString (name , category , defaultValue , comment , langKey , pattern ));
127- } else if (fieldClass .isEnum ()) {
128- val enumValues = Arrays .stream ((Object []) fieldClass .getDeclaredMethod ("values" ).invoke (null ))
129- .map ((obj ) -> (Enum <?>) obj )
130- .collect (Collectors .toList ());
131- val defaultValue = (Enum <?>) Optional .ofNullable (field .getAnnotation (Config .DefaultEnum .class ))
132- .map (Config .DefaultEnum ::value )
133- .map ((defName ) -> extractField (fieldClass , defName ))
134- .map (ConfigurationManager ::extractValue )
135- .orElse (field .get (null ));
136- val possibleValues = enumValues .stream ().map (Enum ::name ).toArray (String []::new );
137- var value = rawConfig .getString (name ,
138- category ,
139- defaultValue .name (),
140- comment + "\n Possible values: " + Arrays .toString (possibleValues ) +
141- "\n " ,
142- possibleValues ,
143- langKey );
144-
145- try {
146- if (!Arrays .asList (possibleValues ).contains (value )) {
147- throw new NoSuchFieldException ();
148- }
149- val enumField = fieldClass .getDeclaredField (value );
150- if (!enumField .isEnumConstant ()) {
151- throw new NoSuchFieldException ();
152- }
153- field .set (null , enumField .get (null ));
154- } catch (NoSuchFieldException e ) {
155- FalsePatternLib .getLog ()
156- .warn ("Invalid value " + value + " for enum configuration field " + field .getName () +
157- " of type " + fieldClass .getName () + " in config class " +
158- configClass .getName () + "! Using default value of " + defaultValue + "!" );
159- field .set (null , defaultValue );
160- }
161- } else if (fieldClass .isArray () && fieldClass .getComponentType ().equals (String .class )) {
162- val defaultValue = Optional .ofNullable (field .getAnnotation (Config .DefaultStringList .class )).map (Config .DefaultStringList ::value ).orElse ((String [])field .get (null ));
163- var value = rawConfig .getStringList (name , category , defaultValue , comment , null , langKey );
164- field .set (null , value );
165- } else {
166- throw new ConfigException ("Illegal config field: " + field .getName () + " in " + configClass .getName () +
167- ": Unsupported type " + fieldClass .getName () +
168- "! Did you forget an @Ignore annotation?" );
169- }
170- if (field .isAnnotationPresent (Config .RequiresMcRestart .class )) {
171- cat .setRequiresMcRestart (true );
172- }
173- if (field .isAnnotationPresent (Config .RequiresWorldRestart .class )) {
174- cat .setRequiresWorldRestart (true );
175- }
176- }
177- }
178-
179- @ SneakyThrows
180- private static Field extractField (Class <?> clazz , String field ) {
181- return clazz .getDeclaredField (field );
182- }
183-
184- @ SneakyThrows
185- private static Object extractValue (Field field ) {
186- return field .get (null );
25+ ConfigurationManagerImpl .registerConfig (configClass );
18726 }
18827
18928 /**
@@ -193,72 +32,8 @@ private static Object extractValue(Field field) {
19332 *
19433 * @return The configuration elements.
19534 */
196- @ SuppressWarnings ({ "rawtypes" , "unchecked" } )
35+ @ SuppressWarnings ("rawtypes" )
19736 public static List <IConfigElement > getConfigElements (Class <?> configClass ) throws ConfigException {
198- init ();
199- val cfg = Optional .ofNullable (configClass .getAnnotation (Config .class ))
200- .orElseThrow (() -> new ConfigException (
201- "Class " + configClass .getName () + " does not have a @Config annotation!" ));
202- val rawConfig = Optional .ofNullable (configs .get (cfg .modid ()))
203- .map ((conf ) -> Optional .ofNullable (configToClassMap .get (conf ))
204- .map ((l ) -> l .contains (configClass ))
205- .orElse (false ) ? conf : null )
206- .orElseThrow (() -> new ConfigException (
207- "Tried to get config elements for non-registered config class!" ));
208- val category = cfg .category ();
209- val elements = new ConfigElement <>(rawConfig .getCategory (category )).getChildElements ();
210- return elements .stream ().map ((element ) -> new IConfigElementProxy (element , () -> {
211- try {
212- processConfigInternal (configClass , category , rawConfig );
213- rawConfig .save ();
214- } catch (IllegalAccessException |
215- NoSuchMethodException |
216- InvocationTargetException |
217- NoSuchFieldException |
218- ConfigException e ) {
219- e .printStackTrace ();
220- }
221- })).collect (Collectors .toList ());
222- }
223-
224- private static void init () {
225- if (initialized ) {
226- return ;
227- }
228- configDir = FileUtil .getMinecraftHome ().toPath ().resolve ("config" );
229- initialized = true ;
230- }
231-
232- /**
233- * Internal, do not use.
234- */
235- public static void registerBus () {
236- FMLCommonHandler .instance ().bus ().register (instance );
237- }
238-
239- /**
240- * Internal, do not use.
241- *
242- * @param event The event.
243- */
244- @ SubscribeEvent
245- public void onConfigChanged (ConfigChangedEvent .OnConfigChangedEvent event ) {
246- init ();
247- val config = configs .get (event .modID );
248- if (config == null ) {
249- return ;
250- }
251- val configClasses = configToClassMap .get (config );
252- configClasses .forEach ((configClass ) -> {
253- try {
254- val category = Optional .ofNullable (configClass .getAnnotation (Config .class ))
255- .map (Config ::category )
256- .orElseThrow (() -> new ConfigException (
257- "Failed to get config category for class " + configClass .getName ()));
258- processConfigInternal (configClass , category , config );
259- } catch (Exception e ) {
260- e .printStackTrace ();
261- }
262- });
37+ return ConfigurationManagerImpl .getConfigElements (configClass );
26338 }
26439}
0 commit comments