1313import java .util .concurrent .atomic .AtomicBoolean ;
1414import java .util .function .Function ;
1515import java .util .function .Supplier ;
16+ import java .util .stream .Collectors ;
1617
1718import com .google .gson .Gson ;
1819import com .google .gson .GsonBuilder ;
20+ import io .github .axolotlclient .AxolotlClientConfig .api .AxolotlClientConfig ;
21+ import io .github .axolotlclient .AxolotlClientConfig .api .options .OptionCategory ;
22+ import io .github .axolotlclient .AxolotlClientConfig .impl .managers .JsonConfigManager ;
23+ import io .github .axolotlclient .AxolotlClientConfig .impl .options .IntegerOption ;
24+ import io .github .axolotlclient .AxolotlClientConfig .impl .options .StringOption ;
1925import lombok .Getter ;
2026import net .fabricmc .api .ClientModInitializer ;
2127import net .fabricmc .fabric .api .resource .ResourceManagerHelper ;
2228import net .fabricmc .fabric .api .resource .SimpleResourceReloadListener ;
23- import net .minecraft .client .MinecraftClient ;
24- import net .minecraft .resource .ResourceManager ;
25- import net .minecraft .resource .ResourceType ;
26- import net .minecraft .util .Identifier ;
27- import net .minecraft .util .math .random .Random ;
29+ import net .fabricmc .loader .api .FabricLoader ;
30+ import net .minecraft .client .Minecraft ;
31+ import net .minecraft .resources .ResourceLocation ;
32+ import net .minecraft .server .packs .PackType ;
33+ import net .minecraft .server .packs .resources .ResourceManager ;
34+ import net .minecraft .util .RandomSource ;
2835import org .apache .commons .lang3 .ArrayUtils ;
2936import org .apache .commons .lang3 .StringUtils ;
3037import org .slf4j .Logger ;
3138import org .slf4j .LoggerFactory ;
3239
3340public class RandomWorldNames implements ClientModInitializer {
3441 private static final Logger log = LoggerFactory .getLogger ("RandomWorldNames" );
35- private static final Identifier NAME_LOCATION = Identifier .of ("random-world-names" , "names.json" );
42+ private static final String MODID = "random-world-names" ;
43+ private static final ResourceLocation BLACKLIST_LOCATION = rl ("blacklist.json" );
44+ private static final ResourceLocation NAME_LOCATION = rl ("names.json" );
3645 private static final Gson GSON = new GsonBuilder ().create ();
37- public static final Random random = Random .create ();
46+ public static final RandomSource random = RandomSource .create ();
3847 private static final List <String > worldNames = new ArrayList <>();
39- private static final int nameLength = 3 ;
40- private static double maxCombinations ;
41- private static final String delimiter = " " ;
48+ private static final IntegerOption nameLength = new IntegerOption ( optionName ( "name_length" ), 3 , 1 , 6 ) ;
49+ private static final StringOption delimiter = new StringOption ( optionName ( "delimiter" ), " " ) ;
50+ private static final IntegerOption timeout = new IntegerOption ( optionName ( "timeout" ), 2 , 1 , 10 ) ;
4251
4352 @ Getter
4453 private static RandomWorldNames instance ;
4554
55+ public static ResourceLocation rl (String path ) {
56+ return ResourceLocation .fromNamespaceAndPath (MODID , path );
57+ }
58+
59+ private static String optionName (String name ) {
60+ return MODID .replace ("-" , "_" ) + "." + name ;
61+ }
62+
4663 @ Override
4764 public void onInitializeClient () {
4865 instance = this ;
49- ResourceManagerHelper .get (ResourceType .CLIENT_RESOURCES )
66+ var category = OptionCategory .create (MODID );
67+ category .add (nameLength , delimiter , timeout );
68+ delimiter .setMaxLength (10 );
69+ var configManager = new JsonConfigManager (FabricLoader .getInstance ().getConfigDir ().resolve (MODID + ".json" ), category );
70+ AxolotlClientConfig .getInstance ().register (configManager );
71+ configManager .load ();
72+ ResourceManagerHelper .get (PackType .CLIENT_RESOURCES )
5073 .registerReloadListener (new SimpleResourceReloadListener <List <String >>() {
5174 @ Override
52- public Identifier getFabricId () {
53- return Identifier . of ( "random-world-names" , "name-reloader" );
75+ public ResourceLocation getFabricId () {
76+ return rl ( "name-reloader" );
5477 }
5578
5679 @ Override
5780 public CompletableFuture <List <String >> load (ResourceManager resourceManager , Executor executor ) {
58- return CompletableFuture .supplyAsync (() -> resourceManager .getAllResources (NAME_LOCATION )
59- .stream ().map (resource -> {
60- try {
61- return GSON .fromJson (resource .getReader (), String [].class );
62- } catch (IOException e ) {
63- log .warn ("Failed to load world names from {}: " , resource .getPackId (), e );
64- return null ;
65- }
66- }).filter (Objects ::nonNull )
67- .flatMap (Arrays ::stream )
68- .toList (), executor );
81+ return CompletableFuture .supplyAsync (() -> {
82+ var blacklist = resourceManager .getResourceStack (BLACKLIST_LOCATION )
83+ .stream ().map (resource -> {
84+ try {
85+ return GSON .fromJson (resource .openAsReader (), String [].class );
86+ } catch (IOException e ) {
87+ log .warn ("Failed to load world names from {}: " , resource .sourcePackId (), e );
88+ return null ;
89+ }
90+ }).filter (Objects ::nonNull )
91+ .flatMap (Arrays ::stream )
92+ .toList ();
93+ return resourceManager .getResourceStack (NAME_LOCATION )
94+ .stream ().map (resource -> {
95+ if (blacklist .contains (resource .sourcePackId ())) {
96+ log .info ("Skipping names from blacklisted pack: {}" , resource .sourcePackId ());
97+ return null ;
98+ }
99+ try {
100+ return GSON .fromJson (resource .openAsReader (), String [].class );
101+ } catch (IOException e ) {
102+ log .warn ("Failed to load world names from {}: " , resource .sourcePackId (), e );
103+ return null ;
104+ }
105+ }).filter (Objects ::nonNull )
106+ .flatMap (Arrays ::stream )
107+ .toList ();
108+ }, executor );
69109 }
70110
71111 @ Override
72112 public CompletableFuture <Void > apply (List <String > o , ResourceManager resourceManager , Executor executor ) {
73113 return CompletableFuture .runAsync (() -> {
74114 worldNames .addAll (o );
75- maxCombinations = Math .pow (nameLength , o .size ());
76115 log .info ("Loaded {} names for random world names!" , o .size ());
77116 }, executor );
78117 }
@@ -81,7 +120,10 @@ public CompletableFuture<Void> apply(List<String> o, ResourceManager resourceMan
81120
82121 public <T > T getRandomWorldName (Supplier <T > fallback , Function <String , T > converter ) {
83122 try {
84- return converter .apply (generateRandomName ());
123+ String name = generateRandomName ();
124+ if (name != null ) {
125+ return converter .apply (name );
126+ }
85127 } catch (TimeoutException e ) {
86128 log .info ("Using default world name as generation could not determine an unused name within the time limit!" );
87129 } catch (Exception e ) {
@@ -95,33 +137,43 @@ public String getRandomWorldName(Supplier<String> fallback) {
95137 }
96138
97139 private String generateRandomName () throws TimeoutException , LimitExceededException {
98- String [] names = new String [nameLength ];
99- AtomicBoolean timeout = new AtomicBoolean ();
140+ String [] names = new String [nameLength .get ()];
141+ AtomicBoolean timeoutReached = new AtomicBoolean ();
142+ int limit = worldNames .size () - 1 ;
143+ if (limit < 0 ) {
144+ return null ;
145+ }
100146 CompletableFuture .runAsync (() -> {
101147 try {
102- Thread .sleep (2000 );
148+ Thread .sleep (timeout . get ()* 1000 );
103149 } catch (InterruptedException ignored ) {
104150 }
105- timeout .set (true );
151+ timeoutReached .set (true );
106152 });
107- int limit = worldNames . size () - 1 ;
153+ double maxCombinations = Math . pow ( nameLength . get (), limit + 1 ) ;
108154 for (int total = 0 ; total < maxCombinations ; total ++) {
109- if (timeout .get ()) {
155+ if (timeoutReached .get ()) {
110156 throw new TimeoutException ("Generation timeout reached." );
111157 }
112158 for (int i = 0 ; i < names .length ; i ++) {
113159 String name ;
114160 do {
115- name = worldNames .get (random .nextBetween (0 , limit ));
161+ name = worldNames .get (random .nextInt (0 , limit ));
116162 } while (ArrayUtils .contains (names , name ));
117163 names [i ] = name ;
164+ // The world name text field has a (default) limit of 32 characters
165+ if (Arrays .stream (names ).filter (Objects ::nonNull ).collect (Collectors .joining (delimiter .get ())).length () > 32 ) {
166+ log .info ("Got {} but the string would be too long, stopping at {} out of {}" , name , i , names .length );
167+ Arrays .fill (names , i , names .length , "" );
168+ break ;
169+ }
118170 }
119171 for (int i = 0 , length = names .length ; i < length ; i ++) {
120172 names [i ] = StringUtils .capitalize (names [i ]);
121173 }
122174
123- String name = String . join ( delimiter , names );
124- if (Files .isDirectory (MinecraftClient .getInstance ().getLevelStorage ().getSavesDirectory ().resolve (name ))) {
175+ String name = Arrays . stream ( names ). filter ( Objects :: nonNull ). collect ( Collectors . joining ( delimiter . get ())). trim ( );
176+ if (Files .isDirectory (Minecraft .getInstance ().getLevelSource ().getBaseDir ().resolve (name ))) {
125177 continue ;
126178 }
127179 return name ;
0 commit comments