Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MinecraftForge
GitHub Repository: MinecraftForge/MinecraftForge
Path: blob/1.20.x/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java
15849 views
1
/*
2
* Copyright (c) Forge Development LLC and contributors
3
* SPDX-License-Identifier: LGPL-2.1-only
4
*/
5
6
package net.minecraftforge.common.world;
7
8
import java.util.EnumSet;
9
import java.util.List;
10
import java.util.Set;
11
import java.util.function.Function;
12
13
import com.mojang.datafixers.util.Either;
14
import com.mojang.serialization.Codec;
15
import com.mojang.serialization.MapCodec;
16
import com.mojang.serialization.codecs.RecordCodecBuilder;
17
18
import net.minecraft.core.Holder;
19
import net.minecraft.core.HolderSet;
20
import net.minecraft.core.RegistryCodecs;
21
import net.minecraft.world.entity.EntityType;
22
import net.minecraft.world.entity.MobCategory;
23
import net.minecraft.world.level.biome.Biome;
24
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
25
import net.minecraft.world.level.levelgen.GenerationStep.Decoration;
26
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
27
import net.minecraftforge.common.world.ModifiableBiomeInfo.BiomeInfo.Builder;
28
import net.minecraftforge.registries.ForgeRegistries;
29
30
public final class ForgeBiomeModifiers {
31
private ForgeBiomeModifiers() {} // Utility class.
32
33
/**
34
* <p>Stock biome modifier that adds features to biomes. Has the following json format:</p>
35
* <pre>
36
* {
37
* "type": "forge:add_features", // required
38
* "biomes": "#namespace:your_biome_tag" // accepts a biome id, [list of biome ids], or #namespace:biome_tag
39
* "features": "namespace:your_feature", // accepts a placed feature id, [list of placed feature ids], or #namespace:feature_tag
40
* "step": "underground_ores" // accepts a Decoration enum name
41
* }
42
* </pre>
43
* <p>Be wary of using this to add vanilla PlacedFeatures to biomes, as doing so may cause a feature cycle violation.</p>
44
*
45
* @param biomes Biomes to add features to.
46
* @param features PlacedFeatures to add to biomes.
47
* @param step Decoration step to run features in.
48
*/
49
public static record AddFeaturesBiomeModifier(HolderSet<Biome> biomes, HolderSet<PlacedFeature> features, Decoration step) implements BiomeModifier {
50
public static final MapCodec<AddFeaturesBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder -> builder.group(
51
Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddFeaturesBiomeModifier::biomes),
52
PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(AddFeaturesBiomeModifier::features),
53
Decoration.CODEC.fieldOf("step").forGetter(AddFeaturesBiomeModifier::step)
54
).apply(builder, AddFeaturesBiomeModifier::new));
55
56
@Override
57
public void modify(Holder<Biome> biome, Phase phase, Builder builder) {
58
if (phase == Phase.ADD && this.biomes.contains(biome)) {
59
BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings();
60
this.features.forEach(holder -> generationSettings.addFeature(this.step, holder));
61
}
62
}
63
64
@Override
65
public MapCodec<? extends BiomeModifier> codec() {
66
return CODEC;
67
}
68
}
69
70
/**
71
* <p>Stock biome modifier that removes features from biomes. Has the following json format:</p>
72
* <pre>
73
* {
74
* "type": "forge:removefeatures", // required
75
* "biomes": "#namespace:your_biome_tag", // accepts a biome id, [list of biome ids], or #namespace:biome_tag
76
* "features": "namespace:your_feature", // accepts a placed feature id, [list of placed feature ids], or #namespace:feature_tag
77
* "steps": "underground_ores" OR ["underground_ores", "vegetal_decoration"] // one or more decoration steps; optional field, defaults to all steps if not specified
78
* }
79
* </pre>
80
*
81
* @param biomes Biomes to remove features from.
82
* @param features PlacedFeatures to remove from biomes.
83
* @param steps Decoration steps to remove features from.
84
*/
85
public static record RemoveFeaturesBiomeModifier(HolderSet<Biome> biomes, HolderSet<PlacedFeature> features, Set<Decoration> steps) implements BiomeModifier {
86
public static final MapCodec<RemoveFeaturesBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder ->
87
builder.group(
88
Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveFeaturesBiomeModifier::biomes),
89
PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(RemoveFeaturesBiomeModifier::features),
90
Codec.either(Decoration.CODEC.listOf(), Decoration.CODEC).<Set<Decoration>>xmap(
91
either -> either.map(Set::copyOf, Set::of), // convert list/singleton to set when decoding
92
set -> set.size() == 1 ? Either.right(set.toArray(Decoration[]::new)[0]) : Either.left(List.copyOf(set))
93
).optionalFieldOf("steps", EnumSet.allOf(Decoration.class)).forGetter(RemoveFeaturesBiomeModifier::steps)
94
).apply(builder, RemoveFeaturesBiomeModifier::new));
95
96
/**
97
* Creates a modifier that removes the given features from all decoration steps in the given biomes.
98
* @param biomes Biomes to remove features from.
99
* @param features PlacedFeatures to remove from biomes.
100
*/
101
public static RemoveFeaturesBiomeModifier allSteps(HolderSet<Biome> biomes, HolderSet<PlacedFeature> features) {
102
return new RemoveFeaturesBiomeModifier(biomes, features, EnumSet.allOf(Decoration.class));
103
}
104
105
@Override
106
public void modify(Holder<Biome> biome, Phase phase, Builder builder) {
107
if (phase == Phase.REMOVE && this.biomes.contains(biome)) {
108
BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings();
109
for (Decoration step : this.steps)
110
generationSettings.getFeatures(step).removeIf(this.features::contains);
111
}
112
}
113
114
@Override
115
public MapCodec<? extends BiomeModifier> codec() {
116
return CODEC;
117
}
118
}
119
120
/**
121
* <p>Stock biome modifier that adds a mob spawn to a biome. Has the following json format:</p>
122
* <pre>
123
* {
124
* "type": "forge:add_spawns", // Required
125
* "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag
126
* "spawners":
127
* {
128
* "type": "namespace:entity_type", // Type of mob to spawn
129
* "weight": 100, // int, spawn weighting
130
* "minCount": 1, // int, minimum pack size
131
* "maxCount": 4, // int, maximum pack size
132
* }
133
* }
134
* </pre>
135
* <p>Optionally accepts a list of spawner objects instead of a single spawner:</p>
136
* <pre>
137
* {
138
* "type": "forge:add_spawns", // Required
139
* "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag
140
* "spawners":
141
* [
142
* {
143
* "type": "namespace:entity_type", // Type of mob to spawn
144
* "weight": 100, // int, spawn weighting
145
* "minCount": 1, // int, minimum pack size
146
* "maxCount": 4, // int, maximum pack size
147
* },
148
* {
149
* // additional spawner object
150
* }
151
* ]
152
* }
153
* </pre>
154
*
155
* @param biomes Biomes to add mob spawns to.
156
* @param spawners List of SpawnerDatas specifying EntityType, weight, and pack size.
157
*/
158
public record AddSpawnsBiomeModifier(HolderSet<Biome> biomes, List<SpawnerData> spawners) implements BiomeModifier {
159
public static final MapCodec<AddSpawnsBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder ->
160
builder.group(
161
Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddSpawnsBiomeModifier::biomes),
162
// Allow either a list or single spawner, attempting to decode the list format first.
163
Codec.either(SpawnerData.CODEC.listOf(), SpawnerData.CODEC).xmap(
164
either -> either.map(Function.identity(), List::of), // convert list/singleton to list when decoding
165
list -> list.size() == 1 ? Either.right(list.get(0)) : Either.left(list) // convert list to singleton/list when encoding
166
).fieldOf("spawners").forGetter(AddSpawnsBiomeModifier::spawners)
167
).apply(builder, AddSpawnsBiomeModifier::new));
168
169
/**
170
* Convenience method for using a single spawn data.
171
* @param biomes Biomes to add mob spawns to.
172
* @param spawner SpawnerData specifying EntityTYpe, weight, and pack size.
173
* @return AddSpawnsBiomeModifier that adds a single spawn entry to the specified biomes.
174
*/
175
public static AddSpawnsBiomeModifier singleSpawn(HolderSet<Biome> biomes, SpawnerData spawner) {
176
return new AddSpawnsBiomeModifier(biomes, List.of(spawner));
177
}
178
179
@Override
180
public void modify(Holder<Biome> biome, Phase phase, Builder builder) {
181
if (phase == Phase.ADD && this.biomes.contains(biome)) {
182
var spawns = builder.getMobSpawnSettings();
183
for (var spawner : this.spawners)
184
spawns.addSpawn(spawner.type.getCategory(), spawner);
185
}
186
}
187
188
@Override
189
public MapCodec<? extends BiomeModifier> codec() {
190
return CODEC;
191
}
192
}
193
194
/**
195
* <p>Stock biome modifier that removes mob spawns from a biome. Has the following json format:</p>
196
* <pre>
197
* {
198
* "type": "forge:add_spawns", // Required
199
* "biomes": "#namespace:biome_tag", // Accepts a biome id, [list of biome ids], or #namespace:biome_tag
200
* "entity_types": #namespace:entitytype_tag // Accepts an entity type, [list of entity types], or #namespace:entitytype_tag
201
* }
202
* </pre>
203
*
204
* @param biomes Biomes to add mob spawns to.
205
* @param entityTypes EntityTypes to remove from spawn lists.
206
*/
207
public record RemoveSpawnsBiomeModifier(HolderSet<Biome> biomes, HolderSet<EntityType<?>> entityTypes) implements BiomeModifier {
208
public static final MapCodec<RemoveSpawnsBiomeModifier> CODEC = RecordCodecBuilder.mapCodec(builder ->
209
builder.group(
210
Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveSpawnsBiomeModifier::biomes),
211
RegistryCodecs.homogeneousList(ForgeRegistries.Keys.ENTITY_TYPES).fieldOf("entity_types").forGetter(RemoveSpawnsBiomeModifier::entityTypes)
212
).apply(builder, RemoveSpawnsBiomeModifier::new));
213
214
@Override
215
public void modify(Holder<Biome> biome, Phase phase, Builder builder) {
216
if (phase == Phase.REMOVE && this.biomes.contains(biome)) {
217
var spawns = builder.getMobSpawnSettings();
218
for (var category : MobCategory.values())
219
spawns.getSpawner(category).removeIf(data -> this.entityTypes.contains(ForgeRegistries.ENTITY_TYPES.getHolder(data.type).get()));
220
}
221
}
222
223
@Override
224
public MapCodec<? extends BiomeModifier> codec() {
225
return CODEC;
226
}
227
}
228
}
229
230