Skip to content

Commit e05d2a9

Browse files
committed
add configuration & blacklist
- move to mojmap
1 parent 321596a commit e05d2a9

File tree

8 files changed

+191
-70
lines changed

8 files changed

+191
-70
lines changed

README.md

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,34 @@ Generates random world names for the world creation screen. Just `New World` get
44

55
The default list of names allows for over 10 billion different combinations.
66
There are only two cases the default name is used:
7-
- Either your computer is too slow to generate a new name within the pre-set time limit (2 seconds)
7+
- Either your computer is too slow to generate a new name within the configured time limit (default 2 seconds)
88
- Or you have so many worlds that no unique combinations could be found
99

10-
### Using your own names
10+
### Configuration (from version 1.1.0)
1111

12-
Just create a resourcepack and put a file at `assets/random-world-names/names.json`
13-
and fill your names into a json array:
12+
#### Config file
13+
14+
The config file (as of version 1.1.0) offers the following options:
15+
16+
| Name | Description | Default value |
17+
|---------------|------------------------------------------------------------|---------------|
18+
| `name_length` | The number of words (name entries) to generate a name from | `3` |
19+
| `delimiter` | The delimiter to use for joining the entries | ` ` (Space) |
20+
| `timeout` | The generation timeout (seconds) | 2 |
21+
22+
*In-Game configuration is possible if ModMenu is installed.*
23+
24+
#### Resource pack configuration
25+
26+
Additional names can be added using resource packs, and unwanted packs may be blacklisted.
27+
28+
##### Adding names
29+
30+
Location: `assets/random-world-names/names.json`
31+
32+
Structure: Json Array
33+
34+
Example:
1435
```json
1536
[
1637
"name1",
@@ -19,6 +40,33 @@ and fill your names into a json array:
1940
...
2041
]
2142
```
43+
44+
#### Blacklisting Packs
45+
46+
Location: `assets/random-world-names/blacklist.json`
47+
48+
Structure: Json Array
49+
50+
Example:
51+
```json
52+
[
53+
"pack-id",
54+
"other-pack-id",
55+
"third-pack-id",
56+
...
57+
]
58+
```
59+
60+
Note: The default names can be disabled by adding `random-world-names` to the blacklist.
61+
62+
<details>
63+
64+
<summary>Configuration for versions < 1.1.0 </summary>
65+
66+
Versions below 1.1.0 only contain the functionality to add names using the `names.json` file.
67+
68+
</details>
69+
2270
That's it!
2371

2472

build.gradle

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ base {
1313
}
1414

1515
repositories {
16-
16+
maven { url = "https://moehreag.duckdns.org/maven/releases" }
17+
maven { url = "https://maven.parchmentmc.org" }
18+
maven { url = "https://maven.terraformersmc.com/releases" }
1719
}
1820

1921
loom {
@@ -28,11 +30,21 @@ loom {
2830
dependencies {
2931
// To change the versions see the gradle.properties file
3032
minecraft "com.mojang:minecraft:${project.minecraft_version}"
31-
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
33+
mappings(loom.layered {
34+
officialMojangMappings {
35+
nameSyntheticMembers = true
36+
}
37+
parchment("org.parchmentmc.data:parchment-${project.parchment_minecraft}:${project.parchment}@zip")
38+
})
3239
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
3340

3441
// Fabric API. This is technically optional, but you probably want it anyway.
3542
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
43+
44+
modImplementation(include("io.github.axolotlclient:AxolotlClient-config:3.0.7+1.21.5"))
45+
modImplementation(include("io.github.axolotlclient.AxolotlClient-config:AxolotlClientConfig-common:3.0.7"))
46+
47+
modLocalRuntime("com.terraformersmc:modmenu:14.0.0-rc.2")
3648

3749
}
3850

gradle.properties

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ org.gradle.parallel=true
55
# Fabric Properties
66
# check these on https://fabricmc.net/develop
77
minecraft_version=1.21.5
8-
yarn_mappings=1.21.5+build.1
9-
loader_version=0.16.10
8+
parchment_minecraft=1.21.4
9+
parchment=2025.03.23
10+
loader_version=0.16.13
1011

1112
# Mod Properties
12-
mod_version=1.0.2
13+
mod_version=1.1.0
1314
maven_group=io.github.moehreag
1415
archives_base_name=random-world-names
1516

src/main/java/io/github/moehreag/randomworldnames/RandomWorldNames.java

Lines changed: 87 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,66 +13,105 @@
1313
import java.util.concurrent.atomic.AtomicBoolean;
1414
import java.util.function.Function;
1515
import java.util.function.Supplier;
16+
import java.util.stream.Collectors;
1617

1718
import com.google.gson.Gson;
1819
import 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;
1925
import lombok.Getter;
2026
import net.fabricmc.api.ClientModInitializer;
2127
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
2228
import 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;
2835
import org.apache.commons.lang3.ArrayUtils;
2936
import org.apache.commons.lang3.StringUtils;
3037
import org.slf4j.Logger;
3138
import org.slf4j.LoggerFactory;
3239

3340
public 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;

src/main/java/io/github/moehreag/randomworldnames/mixin/CreateWorldScreenMixin.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
import java.util.concurrent.CompletableFuture;
66

77
import io.github.moehreag.randomworldnames.RandomWorldNames;
8-
import net.minecraft.client.gui.screen.world.CreateWorldScreen;
9-
import net.minecraft.client.gui.tooltip.Tooltip;
10-
import net.minecraft.client.gui.widget.*;
11-
import net.minecraft.text.Text;
12-
import net.minecraft.util.Identifier;
8+
import net.minecraft.client.gui.components.Button;
9+
import net.minecraft.client.gui.components.EditBox;
10+
import net.minecraft.client.gui.components.SpriteIconButton;
11+
import net.minecraft.client.gui.components.Tooltip;
12+
import net.minecraft.client.gui.layouts.LayoutElement;
13+
import net.minecraft.client.gui.layouts.LinearLayout;
14+
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
15+
import net.minecraft.network.chat.Component;
16+
import net.minecraft.resources.ResourceLocation;
1317
import org.spongepowered.asm.mixin.Final;
1418
import org.spongepowered.asm.mixin.Mixin;
1519
import org.spongepowered.asm.mixin.Shadow;
@@ -20,26 +24,26 @@
2024
@Mixin(CreateWorldScreen.GameTab.class)
2125
public class CreateWorldScreenMixin {
2226
@Unique
23-
private static final Identifier REGENERATE_LOCATION = Identifier.of("random-world-names", "regenerate");
27+
private static final ResourceLocation REGENERATE_LOCATION = RandomWorldNames.rl("regenerate");
2428

2529
@Shadow
2630
@Final
27-
private TextFieldWidget worldNameField;
31+
private EditBox nameEdit;
2832

29-
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/LayoutWidgets;createLabeledWidget(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/client/gui/widget/Widget;Lnet/minecraft/text/Text;)Lnet/minecraft/client/gui/widget/LayoutWidget;"), index = 1)
30-
private Widget addGenerateNewNameButton(Widget widget) {
31-
DirectionalLayoutWidget horizontal = DirectionalLayoutWidget.horizontal().spacing(4);
32-
horizontal.add(widget);
33-
ButtonWidget regenerate = horizontal.add(TextIconButtonWidget.builder(
34-
Text.translatable("randomworldnames.regenerate"), b ->
33+
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/layouts/CommonLayouts;labeledElement(Lnet/minecraft/client/gui/Font;Lnet/minecraft/client/gui/layouts/LayoutElement;Lnet/minecraft/network/chat/Component;)Lnet/minecraft/client/gui/layouts/Layout;"), index = 1)
34+
private LayoutElement addGenerateNewNameButton(LayoutElement element) {
35+
LinearLayout horizontal = LinearLayout.horizontal().spacing(4);
36+
horizontal.addChild(element);
37+
Button regenerate = horizontal.addChild(SpriteIconButton.builder(
38+
Component.translatable("random_world_names.regenerate"), b ->
3539
CompletableFuture.supplyAsync(() ->
3640
RandomWorldNames.getInstance().getRandomWorldName(() ->
37-
Text.translatable("selectWorld.newWorld").getString()))
38-
.thenAccept(worldNameField::setText), true)
39-
.dimension(widget.getHeight(), widget.getHeight())
40-
.texture(REGENERATE_LOCATION, widget.getHeight(), widget.getHeight())
41+
Component.translatable("selectWorld.newWorld").getString()))
42+
.thenAccept(nameEdit::setValue), true)
43+
.size(element.getHeight(), element.getHeight())
44+
.sprite(REGENERATE_LOCATION, element.getHeight(), element.getHeight())
4145
.build());
42-
regenerate.setTooltip(Tooltip.of(Text.translatable("randomworldnames.regenerate")));
46+
regenerate.setTooltip(Tooltip.create(Component.translatable("random_world_names.regenerate")));
4347
regenerate.setTooltipDelay(Duration.of(500, ChronoUnit.MILLIS));
4448
return horizontal;
4549
}

0 commit comments

Comments
 (0)